From 04b08da9af0c450d645ab7389d1467308cfc2db8 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 4 Mar 2013 21:27:36 +0100 Subject: Imported Upstream version 1.1~hg20130304 --- src/all.bat | 5 +- src/all.rc | 16 + src/clean.rc | 14 + src/cmd/5a/a.y | 16 +- src/cmd/5a/doc.go | 6 +- src/cmd/5a/lex.c | 51 +- src/cmd/5a/y.tab.c | 1053 +-- src/cmd/5a/y.tab.h | 92 +- src/cmd/5c/cgen.c | 4 +- src/cmd/5c/doc.go | 6 +- src/cmd/5c/gc.h | 1 + src/cmd/5c/peep.c | 3 +- src/cmd/5c/reg.c | 24 +- src/cmd/5c/swt.c | 46 +- src/cmd/5c/txt.c | 18 +- src/cmd/5g/cgen.c | 922 ++- src/cmd/5g/cgen64.c | 82 +- src/cmd/5g/doc.go | 4 +- src/cmd/5g/galign.c | 1 + src/cmd/5g/gg.h | 40 +- src/cmd/5g/ggen.c | 552 +- src/cmd/5g/gobj.c | 143 +- src/cmd/5g/gsubr.c | 195 +- src/cmd/5g/list.c | 21 +- src/cmd/5g/opt.h | 22 +- src/cmd/5g/peep.c | 125 +- src/cmd/5g/reg.c | 110 +- src/cmd/5l/5.out.h | 31 +- src/cmd/5l/asm.c | 1055 +-- src/cmd/5l/doc.go | 4 +- src/cmd/5l/l.h | 57 +- src/cmd/5l/list.c | 8 + src/cmd/5l/noop.c | 50 +- src/cmd/5l/obj.c | 223 +- src/cmd/5l/optab.c | 41 +- src/cmd/5l/pass.c | 77 +- src/cmd/5l/softfloat.c | 3 + src/cmd/5l/span.c | 107 +- src/cmd/6a/a.y | 5 + src/cmd/6a/doc.go | 6 +- src/cmd/6a/lex.c | 61 +- src/cmd/6a/y.tab.c | 829 +-- src/cmd/6c/cgen.c | 14 +- src/cmd/6c/doc.go | 6 +- src/cmd/6c/gc.h | 3 +- src/cmd/6c/list.c | 55 +- src/cmd/6c/mul.c | 8 +- src/cmd/6c/peep.c | 40 +- src/cmd/6c/reg.c | 136 + src/cmd/6c/sgen.c | 5 +- src/cmd/6c/swt.c | 47 +- src/cmd/6c/txt.c | 90 +- src/cmd/6g/cgen.c | 746 ++- src/cmd/6g/doc.go | 4 +- src/cmd/6g/galign.c | 5 +- src/cmd/6g/gg.h | 36 +- src/cmd/6g/ggen.c | 804 +-- src/cmd/6g/gobj.c | 30 +- src/cmd/6g/gsubr.c | 297 +- src/cmd/6g/list.c | 13 +- src/cmd/6g/opt.h | 22 +- src/cmd/6g/peep.c | 368 +- src/cmd/6g/reg.c | 100 +- src/cmd/6l/6.out.h | 34 +- src/cmd/6l/asm.c | 643 +- src/cmd/6l/doc.go | 4 +- src/cmd/6l/l.h | 35 +- src/cmd/6l/obj.c | 131 +- src/cmd/6l/optab.c | 81 +- src/cmd/6l/pass.c | 91 +- src/cmd/6l/span.c | 80 +- src/cmd/8a/a.y | 25 +- src/cmd/8a/doc.go | 6 +- src/cmd/8a/lex.c | 156 + src/cmd/8a/y.tab.c | 1922 +++--- src/cmd/8a/y.tab.h | 100 +- src/cmd/8c/cgen.c | 35 +- src/cmd/8c/doc.go | 6 +- src/cmd/8c/gc.h | 1 + src/cmd/8c/list.c | 64 +- src/cmd/8c/mul.c | 8 +- src/cmd/8c/peep.c | 6 + src/cmd/8c/reg.c | 151 +- src/cmd/8c/swt.c | 47 +- src/cmd/8c/txt.c | 23 + src/cmd/8g/cgen.c | 703 +- src/cmd/8g/cgen64.c | 71 +- src/cmd/8g/doc.go | 4 +- src/cmd/8g/galign.c | 1 + src/cmd/8g/gg.h | 40 +- src/cmd/8g/ggen.c | 841 ++- src/cmd/8g/gobj.c | 14 +- src/cmd/8g/gsubr.c | 833 ++- src/cmd/8g/list.c | 20 +- src/cmd/8g/opt.h | 20 +- src/cmd/8g/peep.c | 228 +- src/cmd/8g/reg.c | 286 +- src/cmd/8l/8.out.h | 152 +- src/cmd/8l/asm.c | 648 +- src/cmd/8l/doc.go | 4 +- src/cmd/8l/l.h | 23 +- src/cmd/8l/list.c | 9 + src/cmd/8l/obj.c | 148 +- src/cmd/8l/optab.c | 208 +- src/cmd/8l/pass.c | 79 +- src/cmd/8l/span.c | 156 +- src/cmd/api/clone.go | 251 + src/cmd/api/goapi.go | 225 +- src/cmd/api/goapi_test.go | 66 + src/cmd/api/testdata/src/pkg/p1/golden.txt | 16 +- src/cmd/api/testdata/src/pkg/p1/p1.go | 50 + src/cmd/api/testdata/src/pkg/p3/golden.txt | 3 + src/cmd/api/testdata/src/pkg/p3/p3.go | 6 + src/cmd/cc/Makefile | 2 +- src/cmd/cc/cc.h | 28 +- src/cmd/cc/cc.y | 6 +- src/cmd/cc/dcl.c | 4 + src/cmd/cc/doc.go | 4 +- src/cmd/cc/dpchk.c | 130 +- src/cmd/cc/godefs.c | 4 +- src/cmd/cc/lex.c | 130 +- src/cmd/cc/lexbody | 17 +- src/cmd/cc/macbody | 8 +- src/cmd/cc/pgen.c | 5 + src/cmd/cc/sub.c | 8 +- src/cmd/cc/y.tab.c | 1536 ++--- src/cmd/cc/y.tab.h | 92 +- src/cmd/cgo/ast.go | 26 +- src/cmd/cgo/doc.go | 506 +- src/cmd/cgo/gcc.go | 143 +- src/cmd/cgo/godefs.go | 2 +- src/cmd/cgo/main.go | 19 +- src/cmd/cgo/out.go | 338 +- src/cmd/cgo/util.go | 53 +- src/cmd/cov/Makefile | 5 + src/cmd/cov/doc.go | 36 + src/cmd/cov/main.c | 484 ++ src/cmd/cov/tree.c | 245 + src/cmd/cov/tree.h | 47 + src/cmd/dist/README | 4 +- src/cmd/dist/a.h | 7 + src/cmd/dist/arg.h | 3 +- src/cmd/dist/arm.c | 79 + src/cmd/dist/build.c | 218 +- src/cmd/dist/buildgc.c | 1 - src/cmd/dist/buildruntime.c | 60 +- src/cmd/dist/goc2c.c | 63 +- src/cmd/dist/plan9.c | 764 +++ src/cmd/dist/unix.c | 95 +- src/cmd/dist/windows.c | 103 +- src/cmd/fix/doc.go | 2 +- src/cmd/fix/error.go | 353 - src/cmd/fix/error_test.go | 240 - src/cmd/fix/filepath.go | 56 - src/cmd/fix/filepath_test.go | 33 - src/cmd/fix/go1pkgrename.go | 146 - src/cmd/fix/go1pkgrename_test.go | 139 - src/cmd/fix/go1rename.go | 167 - src/cmd/fix/go1rename_test.go | 195 - src/cmd/fix/googlecode.go | 41 - src/cmd/fix/googlecode_test.go | 31 - src/cmd/fix/hashsum.go | 94 - src/cmd/fix/hashsum_test.go | 99 - src/cmd/fix/hmacnew.go | 61 - src/cmd/fix/hmacnew_test.go | 107 - src/cmd/fix/htmlerr.go | 47 - src/cmd/fix/htmlerr_test.go | 39 - src/cmd/fix/httpfinalurl.go | 57 - src/cmd/fix/httpfinalurl_test.go | 37 - src/cmd/fix/httpfs.go | 70 - src/cmd/fix/httpfs_test.go | 47 - src/cmd/fix/httpheaders.go | 67 - src/cmd/fix/httpheaders_test.go | 73 - src/cmd/fix/httpserver.go | 141 - src/cmd/fix/httpserver_test.go | 53 - src/cmd/fix/imagecolor.go | 85 - src/cmd/fix/imagecolor_test.go | 126 - src/cmd/fix/imagenew.go | 83 - src/cmd/fix/imagenew_test.go | 51 - src/cmd/fix/imageycbcr.go | 64 - src/cmd/fix/imageycbcr_test.go | 54 - src/cmd/fix/iocopyn.go | 41 - src/cmd/fix/iocopyn_test.go | 37 - src/cmd/fix/main.go | 21 +- src/cmd/fix/mapdelete.go | 89 - src/cmd/fix/mapdelete_test.go | 43 - src/cmd/fix/math.go | 51 - src/cmd/fix/math_test.go | 47 - src/cmd/fix/netdial.go | 117 - src/cmd/fix/netdial_test.go | 57 - src/cmd/fix/netipv6zone.go | 71 + src/cmd/fix/netipv6zone_test.go | 51 + src/cmd/fix/netudpgroup.go | 58 - src/cmd/fix/netudpgroup_test.go | 53 - src/cmd/fix/newwriter.go | 90 - src/cmd/fix/newwriter_test.go | 83 - src/cmd/fix/oserrorstring.go | 75 - src/cmd/fix/oserrorstring_test.go | 57 - src/cmd/fix/osopen.go | 124 - src/cmd/fix/osopen_test.go | 82 - src/cmd/fix/printerconfig.go | 61 + src/cmd/fix/printerconfig_test.go | 37 + src/cmd/fix/procattr.go | 62 - src/cmd/fix/procattr_test.go | 74 - src/cmd/fix/reflect.go | 862 --- src/cmd/fix/reflect_test.go | 35 - src/cmd/fix/signal.go | 50 - src/cmd/fix/signal_test.go | 94 - src/cmd/fix/sorthelpers.go | 49 - src/cmd/fix/sorthelpers_test.go | 45 - src/cmd/fix/sortslice.go | 52 - src/cmd/fix/sortslice_test.go | 35 - src/cmd/fix/strconv.go | 127 - src/cmd/fix/strconv_test.go | 93 - src/cmd/fix/stringssplit.go | 72 - src/cmd/fix/stringssplit_test.go | 51 - src/cmd/fix/template.go | 111 - src/cmd/fix/template_test.go | 55 - src/cmd/fix/testdata/reflect.encoder.go.in | 2 +- src/cmd/fix/testdata/reflect.encoder.go.out | 2 +- src/cmd/fix/testdata/reflect.export.go.in | 2 +- src/cmd/fix/testdata/reflect.export.go.out | 2 +- src/cmd/fix/testdata/reflect.print.go.in | 2 +- src/cmd/fix/testdata/reflect.print.go.out | 2 +- src/cmd/fix/timefileinfo.go | 298 - src/cmd/fix/timefileinfo_test.go | 187 - src/cmd/fix/typecheck.go | 6 +- src/cmd/fix/url.go | 101 - src/cmd/fix/url2.go | 46 - src/cmd/fix/url2_test.go | 31 - src/cmd/fix/url_test.go | 159 - src/cmd/fix/xmlapi.go | 111 - src/cmd/fix/xmlapi_test.go | 85 - src/cmd/gc/align.c | 27 +- src/cmd/gc/bisonerrors | 4 +- src/cmd/gc/builtin.c | 215 +- src/cmd/gc/closure.c | 165 +- src/cmd/gc/const.c | 359 +- src/cmd/gc/cplx.c | 4 +- src/cmd/gc/dcl.c | 136 +- src/cmd/gc/doc.go | 33 +- src/cmd/gc/esc.c | 780 ++- src/cmd/gc/export.c | 96 +- src/cmd/gc/fmt.c | 127 +- src/cmd/gc/gen.c | 228 +- src/cmd/gc/go.errors | 3 + src/cmd/gc/go.h | 343 +- src/cmd/gc/go.y | 141 +- src/cmd/gc/init.c | 4 + src/cmd/gc/inl.c | 259 +- src/cmd/gc/lex.c | 390 +- src/cmd/gc/mparith1.c | 176 +- src/cmd/gc/obj.c | 14 +- src/cmd/gc/order.c | 6 +- src/cmd/gc/pgen.c | 50 +- src/cmd/gc/racewalk.c | 579 ++ src/cmd/gc/range.c | 11 +- src/cmd/gc/reflect.c | 378 +- src/cmd/gc/runtime.go | 24 +- src/cmd/gc/select.c | 19 +- src/cmd/gc/sinit.c | 54 +- src/cmd/gc/subr.c | 148 +- src/cmd/gc/swt.c | 37 +- src/cmd/gc/typecheck.c | 420 +- src/cmd/gc/walk.c | 1189 +++- src/cmd/gc/y.tab.c | 3983 +++++------- src/cmd/gc/y.tab.h | 62 +- src/cmd/gc/yerr.h | 9 +- src/cmd/go/build.go | 679 +- src/cmd/go/clean.go | 42 +- src/cmd/go/doc.go | 143 +- src/cmd/go/env.go | 15 +- src/cmd/go/fix.go | 2 +- src/cmd/go/fmt.go | 2 +- src/cmd/go/get.go | 33 +- src/cmd/go/help.go | 2 +- src/cmd/go/http.go | 10 +- src/cmd/go/list.go | 78 +- src/cmd/go/main.go | 120 +- src/cmd/go/pkg.go | 183 +- src/cmd/go/run.go | 24 + src/cmd/go/signal.go | 31 + src/cmd/go/signal_notunix.go | 13 + src/cmd/go/signal_unix.go | 14 + src/cmd/go/test.bash | 208 + src/cmd/go/test.go | 161 +- src/cmd/go/testdata/example1_test.go | 23 + src/cmd/go/testdata/example2_test.go | 21 + src/cmd/go/testflag.go | 35 +- src/cmd/go/tool.go | 21 +- src/cmd/go/vcs.go | 13 +- src/cmd/go/version.go | 2 +- src/cmd/godoc/appinit.go | 9 +- src/cmd/godoc/codewalk.go | 13 +- src/cmd/godoc/dirtrees.go | 23 +- src/cmd/godoc/doc.go | 20 +- src/cmd/godoc/filesystem.go | 10 +- src/cmd/godoc/format.go | 8 +- src/cmd/godoc/godoc.go | 584 +- src/cmd/godoc/index.go | 84 +- src/cmd/godoc/main.go | 37 +- src/cmd/godoc/parser.go | 51 +- src/cmd/godoc/play-appengine.go | 35 + src/cmd/godoc/play-local.go | 41 + src/cmd/godoc/play.go | 52 + src/cmd/godoc/setup-godoc-app.bash | 63 +- src/cmd/godoc/template.go | 10 +- src/cmd/gofmt/doc.go | 2 +- src/cmd/gofmt/gofmt.go | 4 +- src/cmd/gofmt/gofmt_test.go | 33 +- src/cmd/gofmt/long_test.go | 2 +- src/cmd/gofmt/rewrite.go | 5 +- src/cmd/gofmt/simplify.go | 54 +- src/cmd/gofmt/testdata/crlf.golden | 12 + src/cmd/gofmt/testdata/crlf.input | 12 + src/cmd/gofmt/testdata/rewrite5.golden | 15 + src/cmd/gofmt/testdata/rewrite5.input | 15 + src/cmd/gofmt/testdata/slices1.golden | 58 + src/cmd/gofmt/testdata/slices1.input | 58 + src/cmd/gofmt/testdata/slices2.golden | 61 + src/cmd/gofmt/testdata/slices2.input | 61 + src/cmd/gofmt/testdata/typeswitch.golden | 60 + src/cmd/gofmt/testdata/typeswitch.input | 60 + src/cmd/ld/data.c | 620 +- src/cmd/ld/decodesym.c | 215 + src/cmd/ld/doc.go | 21 +- src/cmd/ld/dwarf.c | 252 +- src/cmd/ld/elf.c | 954 ++- src/cmd/ld/elf.h | 43 +- src/cmd/ld/go.c | 380 +- src/cmd/ld/ldelf.c | 237 +- src/cmd/ld/ldmacho.c | 66 +- src/cmd/ld/ldpe.c | 44 +- src/cmd/ld/lib.c | 252 +- src/cmd/ld/lib.h | 71 +- src/cmd/ld/macho.h | 2 +- src/cmd/ld/pe.c | 6 +- src/cmd/ld/symtab.c | 240 +- src/cmd/nm/doc.go | 6 +- src/cmd/nm/nm.c | 2 +- src/cmd/pack/ar.c | 67 +- src/cmd/pack/doc.go | 4 +- src/cmd/prof/Makefile | 5 + src/cmd/prof/doc.go | 49 + src/cmd/prof/main.c | 910 +++ src/cmd/vet/Makefile | 5 +- src/cmd/vet/atomic.go | 59 + src/cmd/vet/buildtag.go | 91 + src/cmd/vet/buildtag_bad.go | 11 + src/cmd/vet/doc.go | 20 +- src/cmd/vet/main.go | 231 +- src/cmd/vet/method.go | 7 +- src/cmd/vet/print.go | 382 +- src/cmd/vet/rangeloop.go | 65 + src/cmd/vet/structtag.go | 3 + src/cmd/vet/taglit.go | 31 +- src/cmd/vet/test_atomic.go | 43 + src/cmd/vet/test_buildtag.go | 15 + src/cmd/vet/test_method.go | 24 + src/cmd/vet/test_print.go | 153 + src/cmd/vet/test_rangeloop.go | 61 + src/cmd/vet/test_structtag.go | 15 + src/cmd/vet/test_taglit.go | 31 + src/cmd/yacc/doc.go | 2 +- src/cmd/yacc/units.txt | 104 +- src/cmd/yacc/units.y | 35 +- src/cmd/yacc/yacc.go | 257 +- src/lib9/argv0.c | 35 - src/lib9/flag.c | 307 + src/lib9/fmt/test.c | 2 + src/lib9/getuser.c | 43 - src/lib9/getwd.c | 3 +- src/lib9/goos.c | 12 + src/lib9/main.c | 20 + src/lib9/utf/Makefile | 4 +- src/lib9/utf/rune.c | 21 +- src/lib9/utf/runetype.c | 2 +- src/lib9/utf/runetypebody-6.0.0.h | 1565 ----- src/lib9/utf/runetypebody-6.2.0.h | 1639 +++++ src/lib9/windows.c | 12 +- src/libmach/5obj.c | 3 + src/libmach/8db.c | 1 + src/libmach/darwin.c | 2 +- src/libmach/executable.c | 42 +- src/libmach/obj.c | 2 + src/libmach/sym.c | 169 +- src/libmach/windows.c | 8 +- src/make.bash | 19 +- src/make.bat | 10 +- src/make.rc | 97 + src/pkg/archive/tar/common.go | 184 +- src/pkg/archive/tar/example_test.go | 79 + src/pkg/archive/tar/reader.go | 205 +- src/pkg/archive/tar/reader_test.go | 106 +- src/pkg/archive/tar/stat_atim.go | 20 + src/pkg/archive/tar/stat_atimespec.go | 20 + src/pkg/archive/tar/stat_unix.go | 32 + src/pkg/archive/tar/tar_test.go | 271 + src/pkg/archive/tar/testdata/pax.tar | Bin 0 -> 10240 bytes src/pkg/archive/tar/testdata/ustar.tar | Bin 0 -> 2048 bytes src/pkg/archive/tar/writer.go | 155 +- src/pkg/archive/tar/writer_test.go | 80 + src/pkg/archive/zip/reader.go | 121 +- src/pkg/archive/zip/reader_test.go | 11 + src/pkg/archive/zip/struct.go | 93 +- src/pkg/archive/zip/testdata/zip64.zip | Bin 0 -> 242 bytes src/pkg/archive/zip/writer.go | 136 +- src/pkg/archive/zip/zip_test.go | 198 +- src/pkg/bufio/bufio.go | 100 +- src/pkg/bufio/bufio_test.go | 310 +- src/pkg/bufio/example_test.go | 74 + src/pkg/bufio/export_test.go | 27 + src/pkg/bufio/scan.go | 338 + src/pkg/bufio/scan_test.go | 370 ++ src/pkg/builtin/builtin.go | 9 +- src/pkg/bytes/asm_386.s | 20 +- src/pkg/bytes/asm_amd64.s | 30 +- src/pkg/bytes/asm_arm.s | 53 +- src/pkg/bytes/buffer.go | 38 +- src/pkg/bytes/buffer_test.go | 117 +- src/pkg/bytes/bytes.go | 96 +- src/pkg/bytes/bytes_decl.go | 8 + src/pkg/bytes/bytes_test.go | 104 +- src/pkg/bytes/example_test.go | 65 +- src/pkg/bytes/reader.go | 21 +- src/pkg/bytes/reader_test.go | 49 + src/pkg/compress/bzip2/bit_reader.go | 13 +- src/pkg/compress/flate/copy.go | 17 + src/pkg/compress/flate/copy_test.go | 52 + src/pkg/compress/flate/deflate.go | 2 +- src/pkg/compress/flate/deflate_test.go | 5 +- src/pkg/compress/flate/fixedhuff.go | 74 + src/pkg/compress/flate/flate_test.go | 113 - src/pkg/compress/flate/gen.go | 165 + src/pkg/compress/flate/inflate.go | 255 +- src/pkg/compress/flate/reader_test.go | 95 + src/pkg/compress/flate/writer_test.go | 60 + src/pkg/compress/lzw/reader_test.go | 12 +- src/pkg/compress/lzw/writer.go | 13 +- src/pkg/compress/lzw/writer_test.go | 20 +- .../compress/testdata/Mark.Twain-Tom.Sawyer.txt | 2 +- src/pkg/compress/testdata/e.txt | 2 +- src/pkg/compress/testdata/pi.txt | 2 +- src/pkg/compress/zlib/example_test.go | 37 + src/pkg/compress/zlib/reader.go | 2 +- src/pkg/container/heap/example_intheap_test.go | 43 + src/pkg/container/heap/example_pq_test.go | 95 + src/pkg/container/heap/example_test.go | 105 - src/pkg/container/heap/heap.go | 10 +- src/pkg/container/heap/heap_test.go | 16 +- src/pkg/container/list/example_test.go | 30 + src/pkg/container/list/list.go | 238 +- src/pkg/container/list/list_test.go | 102 +- src/pkg/container/ring/ring.go | 2 +- src/pkg/crypto/aes/aes_test.go | 33 +- src/pkg/crypto/aes/asm_amd64.s | 287 + src/pkg/crypto/aes/block.go | 6 +- src/pkg/crypto/aes/cipher.go | 8 +- src/pkg/crypto/aes/cipher_asm.go | 46 + src/pkg/crypto/aes/cipher_generic.go | 19 + src/pkg/crypto/cipher/cbc.go | 18 + src/pkg/crypto/cipher/cfb.go | 6 + src/pkg/crypto/cipher/cipher_test.go | 36 + src/pkg/crypto/cipher/ctr.go | 2 +- src/pkg/crypto/cipher/example_test.go | 283 + src/pkg/crypto/cipher/io.go | 6 +- src/pkg/crypto/des/des_test.go | 18 + src/pkg/crypto/ecdsa/ecdsa.go | 6 +- src/pkg/crypto/ecdsa/ecdsa_test.go | 256 +- src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2 | Bin 0 -> 95485 bytes src/pkg/crypto/elliptic/elliptic.go | 92 +- src/pkg/crypto/elliptic/elliptic_test.go | 38 + src/pkg/crypto/elliptic/p224.go | 91 +- src/pkg/crypto/hmac/hmac.go | 32 +- src/pkg/crypto/hmac/hmac_test.go | 19 + src/pkg/crypto/md5/gen.go | 300 + src/pkg/crypto/md5/md5.go | 35 +- src/pkg/crypto/md5/md5_test.go | 62 +- src/pkg/crypto/md5/md5block.go | 364 +- src/pkg/crypto/rand/example_test.go | 29 + src/pkg/crypto/rand/rand_test.go | 16 +- src/pkg/crypto/rand/rand_unix.go | 20 +- src/pkg/crypto/rand/rand_windows.go | 4 + src/pkg/crypto/rand/util.go | 61 +- src/pkg/crypto/rc4/rc4.go | 11 - src/pkg/crypto/rc4/rc4_amd64.s | 53 + src/pkg/crypto/rc4/rc4_arm.s | 58 + src/pkg/crypto/rc4/rc4_asm.go | 18 + src/pkg/crypto/rc4/rc4_ref.go | 20 + src/pkg/crypto/rc4/rc4_test.go | 60 +- src/pkg/crypto/rsa/pkcs1v15.go | 42 +- src/pkg/crypto/rsa/pkcs1v15_test.go | 8 +- src/pkg/crypto/rsa/rsa.go | 92 +- src/pkg/crypto/rsa/rsa_test.go | 57 +- src/pkg/crypto/sha1/sha1.go | 39 +- src/pkg/crypto/sha1/sha1_test.go | 32 +- src/pkg/crypto/sha1/sha1block.go | 51 +- src/pkg/crypto/sha256/sha256.go | 83 +- src/pkg/crypto/sha256/sha256_test.go | 25 + src/pkg/crypto/sha256/sha256block.go | 17 +- src/pkg/crypto/sha512/sha512.go | 83 +- src/pkg/crypto/sha512/sha512_test.go | 25 + src/pkg/crypto/sha512/sha512block.go | 16 +- src/pkg/crypto/tls/cipher_suites.go | 4 + src/pkg/crypto/tls/common.go | 25 + src/pkg/crypto/tls/conn.go | 99 +- src/pkg/crypto/tls/generate_cert.go | 60 +- src/pkg/crypto/tls/handshake_client.go | 9 +- src/pkg/crypto/tls/handshake_client_test.go | 234 +- src/pkg/crypto/tls/handshake_messages.go | 108 +- src/pkg/crypto/tls/handshake_messages_test.go | 34 + src/pkg/crypto/tls/handshake_server.go | 534 +- src/pkg/crypto/tls/handshake_server_test.go | 2369 ++++--- src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py | 57 - src/pkg/crypto/tls/prf.go | 21 +- src/pkg/crypto/tls/prf_test.go | 15 +- src/pkg/crypto/tls/root_test.go | 61 - src/pkg/crypto/tls/ticket.go | 182 + src/pkg/crypto/tls/tls.go | 80 +- src/pkg/crypto/tls/tls_test.go | 107 + src/pkg/crypto/x509/cert_pool.go | 2 +- src/pkg/crypto/x509/pem_decrypt.go | 233 + src/pkg/crypto/x509/pem_decrypt_test.go | 223 + src/pkg/crypto/x509/pkcs8.go | 22 +- src/pkg/crypto/x509/pkcs8_test.go | 18 +- src/pkg/crypto/x509/root_darwin.go | 9 +- src/pkg/crypto/x509/root_plan9.go | 33 + src/pkg/crypto/x509/root_stub.go | 3 +- src/pkg/crypto/x509/root_unix.go | 6 +- src/pkg/crypto/x509/root_windows.go | 9 +- src/pkg/crypto/x509/sec1.go | 69 + src/pkg/crypto/x509/sec1_test.go | 22 + src/pkg/crypto/x509/verify.go | 155 +- src/pkg/crypto/x509/verify_test.go | 246 +- src/pkg/crypto/x509/x509.go | 475 +- src/pkg/crypto/x509/x509_test.go | 296 +- src/pkg/database/sql/convert.go | 83 +- src/pkg/database/sql/doc.txt | 2 +- src/pkg/database/sql/driver/driver.go | 32 +- src/pkg/database/sql/example_test.go | 45 + src/pkg/database/sql/fakedb_test.go | 79 +- src/pkg/database/sql/sql.go | 408 +- src/pkg/database/sql/sql_test.go | 108 +- src/pkg/debug/dwarf/const.go | 1 + src/pkg/debug/dwarf/entry.go | 4 + src/pkg/debug/elf/file.go | 53 +- src/pkg/debug/elf/file_test.go | 134 +- .../testdata/gcc-amd64-openbsd-debug-with-rela.obj | Bin 0 -> 6544 bytes src/pkg/debug/elf/testdata/hello-world-core.gz | Bin 0 -> 12678 bytes src/pkg/debug/gosym/pclntab_test.go | 14 +- src/pkg/debug/gosym/symtab.go | 127 +- src/pkg/debug/pe/file.go | 68 +- src/pkg/debug/pe/file_test.go | 30 +- src/pkg/debug/pe/pe.go | 11 + src/pkg/encoding/asn1/asn1.go | 33 +- src/pkg/encoding/asn1/asn1_test.go | 8 +- src/pkg/encoding/asn1/common.go | 2 + src/pkg/encoding/asn1/marshal.go | 43 +- src/pkg/encoding/asn1/marshal_test.go | 12 +- src/pkg/encoding/base32/base32.go | 3 +- src/pkg/encoding/base32/example_test.go | 45 + src/pkg/encoding/base64/base64.go | 3 +- src/pkg/encoding/base64/base64_test.go | 1 + src/pkg/encoding/base64/example_test.go | 45 + src/pkg/encoding/binary/binary.go | 102 +- src/pkg/encoding/binary/binary_test.go | 90 +- src/pkg/encoding/binary/varint.go | 2 +- src/pkg/encoding/csv/writer.go | 14 +- src/pkg/encoding/csv/writer_test.go | 28 + src/pkg/encoding/gob/codec_test.go | 15 +- src/pkg/encoding/gob/decode.go | 14 +- src/pkg/encoding/gob/decoder.go | 37 +- src/pkg/encoding/gob/doc.go | 16 +- src/pkg/encoding/gob/encode.go | 23 +- src/pkg/encoding/gob/encoder.go | 12 +- src/pkg/encoding/gob/encoder_test.go | 106 + src/pkg/encoding/gob/gobencdec_test.go | 71 +- src/pkg/encoding/gob/timing_test.go | 37 +- src/pkg/encoding/gob/type.go | 21 +- src/pkg/encoding/gob/type_test.go | 61 + src/pkg/encoding/hex/hex_test.go | 2 +- src/pkg/encoding/json/bench_test.go | 34 +- src/pkg/encoding/json/decode.go | 244 +- src/pkg/encoding/json/decode_test.go | 607 +- src/pkg/encoding/json/encode.go | 266 +- src/pkg/encoding/json/encode_test.go | 20 + src/pkg/encoding/json/scanner_test.go | 9 +- src/pkg/encoding/json/stream.go | 13 +- src/pkg/encoding/json/stream_test.go | 46 + src/pkg/encoding/json/tagkey_test.go | 10 + src/pkg/encoding/pem/pem.go | 63 +- src/pkg/encoding/pem/pem_test.go | 104 +- src/pkg/encoding/xml/example_test.go | 40 + src/pkg/encoding/xml/marshal.go | 102 +- src/pkg/encoding/xml/marshal_test.go | 208 +- src/pkg/encoding/xml/read.go | 74 +- src/pkg/encoding/xml/read_test.go | 44 + src/pkg/encoding/xml/typeinfo.go | 30 +- src/pkg/encoding/xml/xml.go | 290 +- src/pkg/encoding/xml/xml_test.go | 133 +- src/pkg/exp/norm/Makefile | 30 + src/pkg/exp/norm/composition.go | 382 ++ src/pkg/exp/norm/composition_test.go | 143 + src/pkg/exp/norm/example_iter_test.go | 81 + src/pkg/exp/norm/forminfo.go | 229 + src/pkg/exp/norm/input.go | 105 + src/pkg/exp/norm/iter.go | 401 ++ src/pkg/exp/norm/iter_test.go | 188 + src/pkg/exp/norm/maketables.go | 923 +++ src/pkg/exp/norm/maketesttables.go | 45 + src/pkg/exp/norm/norm_test.go | 14 + src/pkg/exp/norm/normalize.go | 478 ++ src/pkg/exp/norm/normalize_test.go | 750 +++ src/pkg/exp/norm/normregtest.go | 304 + src/pkg/exp/norm/readwriter.go | 126 + src/pkg/exp/norm/readwriter_test.go | 68 + src/pkg/exp/norm/tables.go | 6779 ++++++++++++++++++++ src/pkg/exp/norm/trie.go | 232 + src/pkg/exp/norm/trie_test.go | 152 + src/pkg/exp/norm/triedata_test.go | 85 + src/pkg/exp/norm/triegen.go | 317 + src/pkg/expvar/expvar_test.go | 5 + src/pkg/flag/flag.go | 19 +- src/pkg/flag/flag_test.go | 62 +- src/pkg/fmt/doc.go | 31 +- src/pkg/fmt/fmt_test.go | 107 +- src/pkg/fmt/format.go | 82 +- src/pkg/fmt/print.go | 106 +- src/pkg/fmt/scan.go | 21 +- src/pkg/fmt/scan_test.go | 43 +- src/pkg/go/ast/ast.go | 3 +- src/pkg/go/ast/commentmap.go | 332 + src/pkg/go/ast/commentmap_test.go | 143 + src/pkg/go/ast/filter.go | 2 +- src/pkg/go/ast/import.go | 2 +- src/pkg/go/ast/print.go | 79 +- src/pkg/go/ast/print_test.go | 21 + src/pkg/go/ast/resolve.go | 4 +- src/pkg/go/ast/scope.go | 14 +- src/pkg/go/ast/walk.go | 7 +- src/pkg/go/build/build.go | 144 +- src/pkg/go/build/build_test.go | 42 + src/pkg/go/build/deps_test.go | 28 +- src/pkg/go/build/doc.go | 28 +- src/pkg/go/build/read.go | 238 + src/pkg/go/build/read_test.go | 226 + src/pkg/go/doc/comment.go | 5 +- src/pkg/go/doc/doc.go | 12 +- src/pkg/go/doc/doc_test.go | 2 +- src/pkg/go/doc/example.go | 270 +- src/pkg/go/doc/example_test.go | 111 + src/pkg/go/doc/exports.go | 2 +- src/pkg/go/doc/reader.go | 113 +- src/pkg/go/doc/synopsis.go | 41 +- src/pkg/go/doc/synopsis_test.go | 5 + src/pkg/go/doc/testdata/a.0.golden | 11 + src/pkg/go/doc/testdata/a.1.golden | 11 + src/pkg/go/doc/testdata/a.2.golden | 11 + src/pkg/go/doc/testdata/a0.go | 9 + src/pkg/go/doc/testdata/a1.go | 4 + src/pkg/go/doc/testdata/benchmark.go | 4 +- src/pkg/go/doc/testdata/e.go | 2 +- src/pkg/go/doc/testdata/error2.1.golden | 2 +- src/pkg/go/doc/testdata/error2.go | 2 +- src/pkg/go/doc/testdata/template.txt | 7 +- src/pkg/go/doc/testdata/testing.1.golden | 2 +- src/pkg/go/doc/testdata/testing.go | 4 +- src/pkg/go/format/format.go | 200 + src/pkg/go/format/format_test.go | 125 + src/pkg/go/parser/error_test.go | 10 +- src/pkg/go/parser/interface.go | 48 +- src/pkg/go/parser/parser.go | 249 +- src/pkg/go/parser/parser_test.go | 47 + src/pkg/go/parser/performance_test.go | 30 + src/pkg/go/parser/short_test.go | 13 +- src/pkg/go/printer/nodes.go | 164 +- src/pkg/go/printer/performance_test.go | 4 +- src/pkg/go/printer/printer.go | 92 +- src/pkg/go/printer/printer_test.go | 271 +- src/pkg/go/printer/testdata/comments.golden | 11 +- src/pkg/go/printer/testdata/comments.input | 10 +- src/pkg/go/printer/testdata/comments2.golden | 79 + src/pkg/go/printer/testdata/comments2.input | 79 + src/pkg/go/printer/testdata/declarations.golden | 54 + src/pkg/go/printer/testdata/declarations.input | 54 + src/pkg/go/printer/testdata/expressions.golden | 15 + src/pkg/go/printer/testdata/expressions.input | 15 + src/pkg/go/printer/testdata/expressions.raw | 15 + src/pkg/go/printer/testdata/statements.golden | 108 +- src/pkg/go/printer/testdata/statements.input | 86 +- src/pkg/go/scanner/errors.go | 2 +- src/pkg/go/scanner/scanner.go | 39 +- src/pkg/go/scanner/scanner_test.go | 116 +- src/pkg/go/token/position.go | 22 +- src/pkg/go/token/position_test.go | 51 + src/pkg/go/types/api.go | 105 + src/pkg/go/types/builtins.go | 455 ++ src/pkg/go/types/check.go | 507 ++ src/pkg/go/types/check_test.go | 259 + src/pkg/go/types/const.go | 718 +++ src/pkg/go/types/conversions.go | 129 + src/pkg/go/types/errors.go | 335 + src/pkg/go/types/exportdata.go | 111 + src/pkg/go/types/expr.go | 1520 +++++ src/pkg/go/types/gcimporter.go | 950 +++ src/pkg/go/types/gcimporter_test.go | 180 + src/pkg/go/types/objects.go | 186 + src/pkg/go/types/operand.go | 411 ++ src/pkg/go/types/predicates.go | 303 + src/pkg/go/types/resolve.go | 197 + src/pkg/go/types/resolver_test.go | 167 + src/pkg/go/types/scope.go | 78 + src/pkg/go/types/sizes.go | 162 + src/pkg/go/types/stmt.go | 743 +++ src/pkg/go/types/testdata/builtins.src | 401 ++ src/pkg/go/types/testdata/const0.src | 215 + src/pkg/go/types/testdata/conversions.src | 18 + src/pkg/go/types/testdata/decls0.src | 187 + src/pkg/go/types/testdata/decls1.src | 132 + src/pkg/go/types/testdata/decls2a.src | 67 + src/pkg/go/types/testdata/decls2b.src | 28 + src/pkg/go/types/testdata/decls3.src | 253 + src/pkg/go/types/testdata/exports.go | 89 + src/pkg/go/types/testdata/expr0.src | 161 + src/pkg/go/types/testdata/expr1.src | 7 + src/pkg/go/types/testdata/expr2.src | 23 + src/pkg/go/types/testdata/expr3.src | 463 ++ src/pkg/go/types/testdata/stmt0.src | 288 + src/pkg/go/types/types.go | 236 + src/pkg/go/types/types_test.go | 171 + src/pkg/go/types/universe.go | 146 + src/pkg/hash/adler32/adler32.go | 68 +- src/pkg/hash/adler32/adler32_test.go | 90 +- src/pkg/hash/crc32/crc32.go | 6 +- src/pkg/hash/crc32/crc32_amd64.go | 2 +- src/pkg/hash/crc32/crc32_amd64.s | 14 +- src/pkg/hash/crc32/crc32_test.go | 18 +- src/pkg/hash/crc64/crc64.go | 10 +- src/pkg/hash/crc64/crc64_test.go | 17 +- src/pkg/hash/fnv/fnv.go | 32 +- src/pkg/hash/fnv/fnv_test.go | 34 +- src/pkg/html/entity.go | 4154 ++++++------ src/pkg/html/escape.go | 4 +- src/pkg/html/template/content.go | 4 +- src/pkg/html/template/doc.go | 2 +- src/pkg/html/template/error.go | 4 +- src/pkg/html/template/escape.go | 14 +- src/pkg/html/template/escape_test.go | 16 +- src/pkg/html/template/html.go | 6 +- src/pkg/html/template/js.go | 2 +- src/pkg/html/template/template.go | 12 +- src/pkg/html/template/url.go | 2 +- src/pkg/image/decode_test.go | 1 + src/pkg/image/draw/draw.go | 33 +- src/pkg/image/format.go | 2 +- src/pkg/image/jpeg/dct_test.go | 299 + src/pkg/image/jpeg/huffman.go | 58 +- src/pkg/image/jpeg/idct.go | 80 +- src/pkg/image/jpeg/reader.go | 223 +- src/pkg/image/jpeg/reader_test.go | 157 + src/pkg/image/jpeg/scan.go | 432 ++ src/pkg/image/jpeg/writer.go | 100 +- src/pkg/image/jpeg/writer_test.go | 93 +- src/pkg/image/names.go | 2 +- src/pkg/image/png/paeth.go | 70 + src/pkg/image/png/paeth_test.go | 91 + src/pkg/image/png/reader.go | 99 +- src/pkg/image/png/reader_test.go | 113 +- src/pkg/image/png/testdata/benchGray.png | Bin 0 -> 14709 bytes src/pkg/image/png/testdata/benchNRGBA-gradient.png | Bin 0 -> 58831 bytes src/pkg/image/png/testdata/benchNRGBA-opaque.png | Bin 0 -> 44237 bytes src/pkg/image/png/testdata/benchPaletted.png | Bin 0 -> 13397 bytes src/pkg/image/png/testdata/benchRGB.png | Bin 0 -> 39571 bytes src/pkg/image/png/writer.go | 105 +- src/pkg/image/png/writer_test.go | 45 +- src/pkg/image/testdata/video-001.progressive.jpeg | Bin 0 -> 20732 bytes src/pkg/image/testdata/video-001.q50.420.jpeg | Bin 0 -> 3407 bytes .../testdata/video-001.q50.420.progressive.jpeg | Bin 0 -> 3279 bytes src/pkg/image/testdata/video-001.q50.422.jpeg | Bin 0 -> 3608 bytes .../testdata/video-001.q50.422.progressive.jpeg | Bin 0 -> 3506 bytes src/pkg/image/testdata/video-001.q50.440.jpeg | Bin 0 -> 3662 bytes .../testdata/video-001.q50.440.progressive.jpeg | Bin 0 -> 3529 bytes src/pkg/image/testdata/video-001.q50.444.jpeg | Bin 0 -> 4032 bytes .../testdata/video-001.q50.444.progressive.jpeg | Bin 0 -> 3935 bytes src/pkg/image/testdata/video-005.gray.q50.2x2.jpeg | Bin 0 -> 2782 bytes .../video-005.gray.q50.2x2.progressive.jpeg | Bin 0 -> 2699 bytes src/pkg/image/testdata/video-005.gray.q50.jpeg | Bin 0 -> 2782 bytes .../testdata/video-005.gray.q50.progressive.jpeg | Bin 0 -> 2699 bytes src/pkg/image/ycbcr.go | 9 + src/pkg/image/ycbcr_test.go | 1 + src/pkg/io/io.go | 81 +- src/pkg/io/io_test.go | 76 +- src/pkg/io/ioutil/blackhole.go | 23 + src/pkg/io/ioutil/ioutil.go | 6 +- src/pkg/io/ioutil/ioutil_test.go | 3 +- src/pkg/io/ioutil/tempfile.go | 10 +- src/pkg/io/ioutil/tempfile_test.go | 3 +- src/pkg/log/syslog/syslog.go | 239 +- src/pkg/log/syslog/syslog_plan9.go | 8 + src/pkg/log/syslog/syslog_test.go | 339 +- src/pkg/log/syslog/syslog_unix.go | 8 +- src/pkg/math/abs_arm.s | 7 +- src/pkg/math/acosh.go | 2 +- src/pkg/math/all_test.go | 22 +- src/pkg/math/asinh.go | 2 +- src/pkg/math/atan.go | 103 +- src/pkg/math/atanh.go | 2 +- src/pkg/math/big/arith_386.s | 18 +- src/pkg/math/big/arith_amd64.s | 331 +- src/pkg/math/big/arith_arm.s | 18 +- src/pkg/math/big/arith_test.go | 86 +- src/pkg/math/big/calibrate_test.go | 42 +- src/pkg/math/big/gcd_test.go | 47 + src/pkg/math/big/int.go | 124 +- src/pkg/math/big/int_test.go | 205 +- src/pkg/math/big/nat.go | 309 +- src/pkg/math/big/nat_test.go | 135 +- src/pkg/math/big/rat.go | 269 +- src/pkg/math/big/rat_test.go | 499 +- src/pkg/math/bits.go | 2 +- src/pkg/math/cbrt.go | 2 +- src/pkg/math/copysign.go | 2 +- src/pkg/math/dim_amd64.s | 2 +- src/pkg/math/erf.go | 4 +- src/pkg/math/floor_amd64.s | 70 +- src/pkg/math/frexp_386.s | 12 +- src/pkg/math/gamma.go | 27 +- src/pkg/math/hypot.go | 2 +- src/pkg/math/hypot_386.s | 46 +- src/pkg/math/hypot_amd64.s | 14 +- src/pkg/math/ldexp_386.s | 6 +- src/pkg/math/log10.go | 3 +- src/pkg/math/log_amd64.s | 8 +- src/pkg/math/logb.go | 4 +- src/pkg/math/modf_386.s | 14 +- src/pkg/math/rand/example_test.go | 69 + src/pkg/math/rand/rand_test.go | 13 +- src/pkg/math/remainder.go | 2 +- src/pkg/math/sincos.go | 2 +- src/pkg/math/sincos_386.s | 8 +- src/pkg/math/sincos_amd64.s | 2 +- src/pkg/math/tanh.go | 89 +- src/pkg/mime/grammar.go | 2 +- src/pkg/mime/multipart/multipart.go | 24 +- src/pkg/mime/multipart/multipart_test.go | 28 +- src/pkg/mime/multipart/quotedprintable.go | 130 + src/pkg/mime/multipart/quotedprintable_test.go | 199 + src/pkg/mime/multipart/testdata/nested-mime | 0 src/pkg/mime/multipart/writer.go | 29 +- src/pkg/mime/multipart/writer_test.go | 35 + src/pkg/mime/test.types | 8 - src/pkg/mime/testdata/test.types | 8 + src/pkg/mime/type_unix.go | 17 +- src/pkg/mime/type_windows.go | 10 +- src/pkg/net/cgo_bsd.go | 4 +- src/pkg/net/cgo_linux.go | 8 +- src/pkg/net/cgo_netbsd.go | 14 + src/pkg/net/cgo_openbsd.go | 14 + src/pkg/net/cgo_unix.go | 10 +- src/pkg/net/conn_test.go | 114 + src/pkg/net/dial.go | 243 +- src/pkg/net/dial_test.go | 114 +- src/pkg/net/dialgoogle_test.go | 24 +- src/pkg/net/dnsclient.go | 7 +- src/pkg/net/dnsclient_unix.go | 28 +- src/pkg/net/dnsconfig.go | 106 - src/pkg/net/dnsconfig_unix.go | 106 + src/pkg/net/dnsmsg.go | 2 +- src/pkg/net/doc.go | 59 - src/pkg/net/example_test.go | 2 +- src/pkg/net/fd.go | 663 -- src/pkg/net/fd_bsd.go | 123 + src/pkg/net/fd_darwin.go | 7 +- src/pkg/net/fd_freebsd.go | 116 - src/pkg/net/fd_linux.go | 7 +- src/pkg/net/fd_netbsd.go | 116 - src/pkg/net/fd_openbsd.go | 116 - src/pkg/net/fd_plan9.go | 129 + src/pkg/net/fd_posix_test.go | 57 + src/pkg/net/fd_unix.go | 672 ++ src/pkg/net/fd_unix_test.go | 58 + src/pkg/net/fd_windows.go | 293 +- src/pkg/net/file.go | 126 - src/pkg/net/file_plan9.go | 131 +- src/pkg/net/file_test.go | 8 +- src/pkg/net/file_unix.go | 136 + src/pkg/net/http/cgi/child.go | 12 +- src/pkg/net/http/cgi/child_test.go | 24 +- src/pkg/net/http/cgi/host_test.go | 86 +- src/pkg/net/http/cgi/plan9_test.go | 18 + src/pkg/net/http/cgi/posix_test.go | 21 + src/pkg/net/http/cgi/testdata/test.cgi | 55 +- src/pkg/net/http/chunked.go | 59 +- src/pkg/net/http/chunked_test.go | 54 + src/pkg/net/http/client.go | 178 +- src/pkg/net/http/client_test.go | 250 +- src/pkg/net/http/cookie.go | 9 +- src/pkg/net/http/cookie_test.go | 2 +- src/pkg/net/http/cookiejar/jar.go | 494 ++ src/pkg/net/http/cookiejar/jar_test.go | 1267 ++++ src/pkg/net/http/cookiejar/punycode.go | 159 + src/pkg/net/http/cookiejar/punycode_test.go | 161 + src/pkg/net/http/example_test.go | 2 +- src/pkg/net/http/export_test.go | 23 +- src/pkg/net/http/filetransport_test.go | 10 +- src/pkg/net/http/fs.go | 196 +- src/pkg/net/http/fs_test.go | 439 +- src/pkg/net/http/header.go | 135 +- src/pkg/net/http/header_test.go | 123 + src/pkg/net/http/httptest/example_test.go | 50 + src/pkg/net/http/httptest/recorder.go | 24 +- src/pkg/net/http/httptest/recorder_test.go | 90 + src/pkg/net/http/httptest/server.go | 73 +- src/pkg/net/http/httputil/chunked.go | 59 +- src/pkg/net/http/httputil/chunked_test.go | 54 + src/pkg/net/http/httputil/dump.go | 4 +- src/pkg/net/http/httputil/reverseproxy.go | 58 +- src/pkg/net/http/httputil/reverseproxy_test.go | 84 + src/pkg/net/http/jar.go | 19 +- src/pkg/net/http/lex.go | 206 +- src/pkg/net/http/lex_test.go | 65 +- src/pkg/net/http/npn_test.go | 118 + src/pkg/net/http/pprof/pprof.go | 17 +- src/pkg/net/http/proxy_test.go | 4 +- src/pkg/net/http/range_test.go | 22 + src/pkg/net/http/readrequest_test.go | 48 + src/pkg/net/http/request.go | 247 +- src/pkg/net/http/request_test.go | 189 +- src/pkg/net/http/requestwrite_test.go | 63 + src/pkg/net/http/response.go | 14 +- src/pkg/net/http/response_test.go | 162 +- src/pkg/net/http/responsewrite_test.go | 138 +- src/pkg/net/http/serve_test.go | 578 +- src/pkg/net/http/server.go | 873 ++- src/pkg/net/http/server_test.go | 95 + src/pkg/net/http/transfer.go | 100 +- src/pkg/net/http/transfer_test.go | 37 + src/pkg/net/http/transport.go | 392 +- src/pkg/net/http/transport_test.go | 577 +- src/pkg/net/http/z_last_test.go | 60 + src/pkg/net/interface.go | 16 +- src/pkg/net/interface_bsd.go | 104 +- src/pkg/net/interface_bsd_test.go | 52 + src/pkg/net/interface_darwin.go | 35 +- src/pkg/net/interface_freebsd.go | 35 +- src/pkg/net/interface_linux.go | 109 +- src/pkg/net/interface_linux_test.go | 50 +- src/pkg/net/interface_netbsd.go | 10 +- src/pkg/net/interface_openbsd.go | 10 +- src/pkg/net/interface_stub.go | 17 +- src/pkg/net/interface_test.go | 116 +- src/pkg/net/interface_unix_test.go | 145 + src/pkg/net/interface_windows.go | 28 +- src/pkg/net/ip.go | 11 +- src/pkg/net/ip_test.go | 123 +- src/pkg/net/ipraw_test.go | 413 +- src/pkg/net/iprawsock.go | 57 +- src/pkg/net/iprawsock_plan9.go | 97 +- src/pkg/net/iprawsock_posix.go | 165 +- src/pkg/net/ipsock.go | 180 +- src/pkg/net/ipsock_plan9.go | 214 +- src/pkg/net/ipsock_posix.go | 35 +- src/pkg/net/lookup.go | 105 + src/pkg/net/lookup_plan9.go | 15 + src/pkg/net/lookup_test.go | 36 +- src/pkg/net/lookup_unix.go | 13 + src/pkg/net/lookup_windows.go | 155 +- src/pkg/net/mail/message.go | 15 +- src/pkg/net/mail/message_test.go | 17 +- src/pkg/net/multicast_posix_test.go | 180 + src/pkg/net/multicast_test.go | 234 - src/pkg/net/net.go | 169 +- src/pkg/net/net_test.go | 126 +- src/pkg/net/newpollserver.go | 48 - src/pkg/net/newpollserver_unix.go | 46 + src/pkg/net/packetconn_test.go | 200 + src/pkg/net/parse_test.go | 3 +- src/pkg/net/port.go | 73 +- src/pkg/net/port_test.go | 2 +- src/pkg/net/port_unix.go | 69 + src/pkg/net/protoconn_test.go | 358 ++ src/pkg/net/rpc/client.go | 43 +- src/pkg/net/rpc/jsonrpc/all_test.go | 61 +- src/pkg/net/rpc/jsonrpc/server.go | 13 +- src/pkg/net/rpc/server.go | 84 +- src/pkg/net/rpc/server_test.go | 72 +- src/pkg/net/sendfile_freebsd.go | 105 + src/pkg/net/sendfile_linux.go | 4 +- src/pkg/net/sendfile_stub.go | 2 +- src/pkg/net/sendfile_windows.go | 4 +- src/pkg/net/server_test.go | 51 +- src/pkg/net/smtp/smtp.go | 65 +- src/pkg/net/smtp/smtp_test.go | 229 +- src/pkg/net/sock.go | 87 - src/pkg/net/sock_bsd.go | 31 - src/pkg/net/sock_cloexec.go | 69 + src/pkg/net/sock_linux.go | 31 - src/pkg/net/sock_posix.go | 67 + src/pkg/net/sock_unix.go | 36 + src/pkg/net/sock_windows.go | 29 +- src/pkg/net/sockopt.go | 193 - src/pkg/net/sockopt_posix.go | 170 + src/pkg/net/sockoptip.go | 219 - src/pkg/net/sockoptip_bsd.go | 36 +- src/pkg/net/sockoptip_darwin.go | 90 - src/pkg/net/sockoptip_freebsd.go | 92 - src/pkg/net/sockoptip_linux.go | 101 - src/pkg/net/sockoptip_netbsd.go | 39 - src/pkg/net/sockoptip_openbsd.go | 90 - src/pkg/net/sockoptip_posix.go | 73 + src/pkg/net/sockoptip_windows.go | 61 +- src/pkg/net/sys_cloexec.go | 54 + src/pkg/net/tcp_test.go | 206 + src/pkg/net/tcpsock.go | 12 +- src/pkg/net/tcpsock_plan9.go | 161 +- src/pkg/net/tcpsock_posix.go | 189 +- src/pkg/net/textproto/reader.go | 129 +- src/pkg/net/textproto/reader_test.go | 94 + src/pkg/net/textproto/textproto.go | 31 + src/pkg/net/timeout_test.go | 540 +- src/pkg/net/udp_test.go | 63 +- src/pkg/net/udpsock.go | 16 +- src/pkg/net/udpsock_plan9.go | 134 +- src/pkg/net/udpsock_posix.go | 229 +- src/pkg/net/unicast_posix_test.go | 516 ++ src/pkg/net/unicast_test.go | 538 -- src/pkg/net/unix_test.go | 144 + src/pkg/net/unixsock_plan9.go | 145 +- src/pkg/net/unixsock_posix.go | 286 +- src/pkg/net/url/url.go | 92 +- src/pkg/net/url/url_test.go | 107 +- src/pkg/os/dir_plan9.go | 259 +- src/pkg/os/doc.go | 4 +- src/pkg/os/env.go | 2 +- src/pkg/os/env_unix_test.go | 30 + src/pkg/os/error.go | 2 +- src/pkg/os/error_plan9.go | 21 +- src/pkg/os/error_posix.go | 21 +- src/pkg/os/error_test.go | 51 + src/pkg/os/error_windows.go | 21 +- src/pkg/os/error_windows_test.go | 47 + src/pkg/os/exec.go | 19 +- src/pkg/os/exec/exec.go | 10 +- src/pkg/os/exec/exec_test.go | 134 +- src/pkg/os/exec/lp_plan9.go | 3 +- src/pkg/os/exec/lp_unix.go | 3 + src/pkg/os/exec/lp_unix_test.go | 55 + src/pkg/os/exec/lp_windows.go | 35 +- src/pkg/os/exec_plan9.go | 58 +- src/pkg/os/exec_posix.go | 17 +- src/pkg/os/exec_unix.go | 4 +- src/pkg/os/exec_windows.go | 4 +- src/pkg/os/file.go | 4 +- src/pkg/os/file_plan9.go | 144 +- src/pkg/os/file_posix.go | 21 +- src/pkg/os/file_unix.go | 32 +- src/pkg/os/file_windows.go | 188 +- src/pkg/os/getwd.go | 23 + src/pkg/os/os_test.go | 78 +- src/pkg/os/path_plan9.go | 4 +- src/pkg/os/path_test.go | 15 +- src/pkg/os/pipe_bsd.go | 28 + src/pkg/os/pipe_linux.go | 33 + src/pkg/os/proc.go | 2 + src/pkg/os/signal/example_test.go | 19 + src/pkg/os/signal/signal_test.go | 41 + src/pkg/os/signal/signal_windows_test.go | 77 +- src/pkg/os/stat_darwin.go | 6 +- src/pkg/os/stat_freebsd.go | 6 +- src/pkg/os/stat_linux.go | 6 +- src/pkg/os/stat_netbsd.go | 10 +- src/pkg/os/stat_openbsd.go | 6 +- src/pkg/os/stat_plan9.go | 52 +- src/pkg/os/stat_windows.go | 149 +- src/pkg/os/types.go | 27 +- src/pkg/os/types_notwin.go | 25 + src/pkg/os/types_windows.go | 104 + src/pkg/os/user/lookup.go | 22 + src/pkg/os/user/lookup_stubs.go | 6 +- src/pkg/os/user/lookup_unix.go | 21 +- src/pkg/os/user/lookup_windows.go | 25 +- src/pkg/os/user/user_test.go | 36 +- src/pkg/path/filepath/example_unix_test.go | 39 + src/pkg/path/filepath/match_test.go | 3 +- src/pkg/path/filepath/path.go | 130 +- src/pkg/path/filepath/path_plan9.go | 15 +- src/pkg/path/filepath/path_test.go | 52 +- src/pkg/path/filepath/path_unix.go | 15 +- src/pkg/path/filepath/path_windows.go | 55 +- src/pkg/path/filepath/path_windows_test.go | 89 + src/pkg/path/filepath/symlink_windows.go | 10 +- src/pkg/path/path.go | 83 +- src/pkg/path/path_test.go | 11 + src/pkg/reflect/all_test.go | 1279 +++- src/pkg/reflect/asm_386.s | 13 + src/pkg/reflect/asm_amd64.s | 13 + src/pkg/reflect/asm_arm.s | 13 + src/pkg/reflect/deepequal.go | 9 +- src/pkg/reflect/example_test.go | 52 + src/pkg/reflect/export_test.go | 18 + src/pkg/reflect/makefunc.go | 67 + src/pkg/reflect/set_test.go | 8 +- src/pkg/reflect/type.go | 808 ++- src/pkg/reflect/value.go | 716 ++- src/pkg/regexp/all_test.go | 105 +- src/pkg/regexp/example_test.go | 144 + src/pkg/regexp/exec_test.go | 50 +- src/pkg/regexp/regexp.go | 74 +- src/pkg/regexp/syntax/compile.go | 4 +- src/pkg/regexp/syntax/doc.go | 127 + src/pkg/regexp/syntax/parse.go | 14 +- src/pkg/runtime/alg.c | 21 +- src/pkg/runtime/append_test.go | 71 + src/pkg/runtime/arch_386.h | 4 +- src/pkg/runtime/arch_amd64.h | 4 +- src/pkg/runtime/arch_arm.h | 4 +- src/pkg/runtime/asm_386.s | 188 +- src/pkg/runtime/asm_amd64.s | 178 +- src/pkg/runtime/asm_arm.s | 265 +- src/pkg/runtime/atomic_386.c | 13 + src/pkg/runtime/atomic_amd64.c | 7 + src/pkg/runtime/atomic_arm.c | 62 +- src/pkg/runtime/callback_windows_386.c | 3 +- src/pkg/runtime/callback_windows_amd64.c | 3 +- src/pkg/runtime/cgo/asm_386.s | 29 + src/pkg/runtime/cgo/asm_amd64.s | 45 + src/pkg/runtime/cgo/asm_arm.s | 23 + src/pkg/runtime/cgo/callbacks.c | 22 + src/pkg/runtime/cgo/cgo.go | 2 + src/pkg/runtime/cgo/cgo_arm.c | 12 + src/pkg/runtime/cgo/gcc_386.S | 25 - src/pkg/runtime/cgo/gcc_amd64.S | 48 - src/pkg/runtime/cgo/gcc_arm.S | 37 +- src/pkg/runtime/cgo/gcc_darwin_386.c | 13 +- src/pkg/runtime/cgo/gcc_darwin_amd64.c | 11 +- src/pkg/runtime/cgo/gcc_freebsd_386.c | 10 +- src/pkg/runtime/cgo/gcc_freebsd_amd64.c | 10 +- src/pkg/runtime/cgo/gcc_freebsd_arm.c | 114 + src/pkg/runtime/cgo/gcc_linux_386.c | 9 +- src/pkg/runtime/cgo/gcc_linux_amd64.c | 7 +- src/pkg/runtime/cgo/gcc_linux_arm.c | 108 +- src/pkg/runtime/cgo/gcc_netbsd_386.c | 80 + src/pkg/runtime/cgo/gcc_netbsd_amd64.c | 80 + src/pkg/runtime/cgo/gcc_netbsd_arm.c | 122 + src/pkg/runtime/cgo/gcc_openbsd_386.c | 169 + src/pkg/runtime/cgo/gcc_openbsd_amd64.c | 169 + src/pkg/runtime/cgo/gcc_setenv.c | 8 +- src/pkg/runtime/cgo/gcc_util.c | 16 +- src/pkg/runtime/cgo/gcc_windows_386.c | 13 +- src/pkg/runtime/cgo/gcc_windows_amd64.c | 13 +- src/pkg/runtime/cgo/libcgo.h | 8 +- src/pkg/runtime/cgo/netbsd.c | 13 + src/pkg/runtime/cgo/openbsd.c | 21 + src/pkg/runtime/cgo/setenv.c | 10 + src/pkg/runtime/cgocall.c | 109 +- src/pkg/runtime/chan.c | 316 +- src/pkg/runtime/closure_386.c | 105 - src/pkg/runtime/closure_amd64.c | 123 - src/pkg/runtime/closure_arm.c | 129 - src/pkg/runtime/complex.c | 30 +- src/pkg/runtime/complex_test.go | 67 + src/pkg/runtime/cpuprof.c | 22 +- src/pkg/runtime/crash_cgo_test.go | 88 + src/pkg/runtime/crash_test.go | 177 + src/pkg/runtime/debug.go | 29 +- src/pkg/runtime/debug/debug.c | 9 + src/pkg/runtime/debug/garbage.go | 101 + src/pkg/runtime/debug/garbage_test.go | 100 + src/pkg/runtime/debug/stack.go | 2 + src/pkg/runtime/debug/stack_test.go | 2 +- src/pkg/runtime/defs_freebsd.go | 8 +- src/pkg/runtime/defs_freebsd_386.h | 317 +- src/pkg/runtime/defs_freebsd_amd64.h | 339 +- src/pkg/runtime/defs_freebsd_arm.h | 163 + src/pkg/runtime/defs_linux_386.h | 2 +- src/pkg/runtime/defs_linux_arm.h | 2 +- src/pkg/runtime/defs_netbsd.go | 21 +- src/pkg/runtime/defs_netbsd_386.go | 42 + src/pkg/runtime/defs_netbsd_386.h | 261 +- src/pkg/runtime/defs_netbsd_amd64.go | 49 + src/pkg/runtime/defs_netbsd_amd64.h | 284 +- src/pkg/runtime/defs_netbsd_arm.h | 140 + src/pkg/runtime/defs_openbsd.go | 17 +- src/pkg/runtime/defs_openbsd_386.h | 248 +- src/pkg/runtime/defs_openbsd_amd64.h | 272 +- src/pkg/runtime/defs_plan9_386.h | 31 +- src/pkg/runtime/defs_plan9_amd64.h | 34 + src/pkg/runtime/env_plan9.c | 33 + src/pkg/runtime/env_posix.c | 61 + src/pkg/runtime/export_test.go | 42 + src/pkg/runtime/extern.go | 6 +- src/pkg/runtime/float.c | 171 +- src/pkg/runtime/gc_test.go | 55 +- src/pkg/runtime/hashmap.c | 151 +- src/pkg/runtime/hashmap.h | 29 +- src/pkg/runtime/iface.c | 134 +- src/pkg/runtime/iface_test.go | 138 + src/pkg/runtime/lfstack.c | 65 + src/pkg/runtime/lfstack_test.go | 130 + src/pkg/runtime/lock_futex.c | 3 +- src/pkg/runtime/malloc.goc | 424 +- src/pkg/runtime/malloc.h | 132 +- src/pkg/runtime/mallocrep1.go | 1 + src/pkg/runtime/mcache.c | 9 +- src/pkg/runtime/mcentral.c | 97 +- src/pkg/runtime/mem.go | 5 +- src/pkg/runtime/mem_darwin.c | 4 +- src/pkg/runtime/mem_freebsd.c | 10 +- src/pkg/runtime/mem_linux.c | 11 +- src/pkg/runtime/mem_netbsd.c | 10 +- src/pkg/runtime/mem_openbsd.c | 10 +- src/pkg/runtime/mem_plan9.c | 5 +- src/pkg/runtime/memclr_arm.s | 91 + src/pkg/runtime/memmove_arm.s | 96 +- src/pkg/runtime/memset_arm.s | 94 - src/pkg/runtime/mfinal.c | 10 +- src/pkg/runtime/mgc0.c | 1891 ++++-- src/pkg/runtime/mgc0.go | 15 + src/pkg/runtime/mgc0.h | 43 + src/pkg/runtime/mheap.c | 145 +- src/pkg/runtime/mprof.goc | 272 +- src/pkg/runtime/os_darwin.h | 4 +- src/pkg/runtime/os_freebsd.h | 4 +- src/pkg/runtime/os_linux.h | 4 +- src/pkg/runtime/os_netbsd.h | 21 +- src/pkg/runtime/os_openbsd.h | 17 +- src/pkg/runtime/os_plan9.h | 23 +- src/pkg/runtime/os_windows.h | 3 + src/pkg/runtime/panic.c | 487 ++ src/pkg/runtime/parfor.c | 215 + src/pkg/runtime/parfor_test.go | 144 + src/pkg/runtime/pprof/pprof.go | 98 +- src/pkg/runtime/pprof/pprof_test.go | 19 +- src/pkg/runtime/print.c | 36 +- src/pkg/runtime/proc.c | 2936 +++++---- src/pkg/runtime/proc_test.go | 212 +- src/pkg/runtime/race.c | 350 + src/pkg/runtime/race.go | 29 + src/pkg/runtime/race.h | 33 + src/pkg/runtime/race/README | 11 + src/pkg/runtime/race/race.go | 122 + src/pkg/runtime/race/race_darwin_amd64.syso | Bin 0 -> 146952 bytes src/pkg/runtime/race/race_linux_amd64.syso | Bin 0 -> 142848 bytes src/pkg/runtime/race/race_test.go | 157 + src/pkg/runtime/race/race_windows_amd64.syso | Bin 0 -> 121376 bytes src/pkg/runtime/race/testdata/atomic_test.go | 271 + src/pkg/runtime/race/testdata/cgo_test.go | 20 + src/pkg/runtime/race/testdata/cgo_test_main.go | 30 + src/pkg/runtime/race/testdata/chan_test.go | 457 ++ src/pkg/runtime/race/testdata/comp_test.go | 132 + src/pkg/runtime/race/testdata/finalizer_test.go | 67 + src/pkg/runtime/race/testdata/io_test.go | 69 + src/pkg/runtime/race/testdata/map_test.go | 163 + src/pkg/runtime/race/testdata/mop_test.go | 1384 ++++ src/pkg/runtime/race/testdata/mutex_test.go | 138 + src/pkg/runtime/race/testdata/regression_test.go | 150 + src/pkg/runtime/race/testdata/rwmutex_test.go | 134 + src/pkg/runtime/race/testdata/select_test.go | 286 + src/pkg/runtime/race/testdata/slice_test.go | 465 ++ src/pkg/runtime/race/testdata/sync_test.go | 197 + src/pkg/runtime/race/testdata/waitgroup_test.go | 232 + src/pkg/runtime/race0.c | 133 + src/pkg/runtime/race_amd64.s | 14 + src/pkg/runtime/rt0_freebsd_arm.s | 8 + src/pkg/runtime/rt0_linux_arm.s | 18 +- src/pkg/runtime/rt0_netbsd_arm.s | 8 + src/pkg/runtime/rt0_plan9_386.s | 1 + src/pkg/runtime/rt0_plan9_amd64.s | 11 + src/pkg/runtime/rt0_windows_386.s | 5 - src/pkg/runtime/rune.c | 9 +- src/pkg/runtime/runtime-gdb.py | 17 +- src/pkg/runtime/runtime.c | 216 +- src/pkg/runtime/runtime.h | 360 +- src/pkg/runtime/runtime1.goc | 4 +- src/pkg/runtime/runtime_test.go | 41 + src/pkg/runtime/sema.goc | 61 +- src/pkg/runtime/signal_darwin_386.c | 19 +- src/pkg/runtime/signal_darwin_amd64.c | 19 +- src/pkg/runtime/signal_freebsd_386.c | 24 +- src/pkg/runtime/signal_freebsd_amd64.c | 24 +- src/pkg/runtime/signal_freebsd_arm.c | 193 + src/pkg/runtime/signal_linux_386.c | 21 +- src/pkg/runtime/signal_linux_amd64.c | 23 +- src/pkg/runtime/signal_linux_arm.c | 115 +- src/pkg/runtime/signal_netbsd_386.c | 122 +- src/pkg/runtime/signal_netbsd_amd64.c | 143 +- src/pkg/runtime/signal_netbsd_arm.c | 208 + src/pkg/runtime/signal_openbsd_386.c | 19 +- src/pkg/runtime/signal_openbsd_amd64.c | 27 +- src/pkg/runtime/signal_plan9_386.c | 101 + src/pkg/runtime/signal_plan9_amd64.c | 127 + src/pkg/runtime/signal_windows_386.c | 8 +- src/pkg/runtime/signal_windows_amd64.c | 4 + src/pkg/runtime/signals_linux.h | 2 +- src/pkg/runtime/signals_netbsd.h | 20 +- src/pkg/runtime/signals_openbsd.h | 20 +- src/pkg/runtime/signals_plan9.h | 25 +- src/pkg/runtime/sigqueue.goc | 104 +- src/pkg/runtime/slice.c | 292 +- src/pkg/runtime/softfloat_arm.c | 17 + src/pkg/runtime/stack.c | 282 + src/pkg/runtime/stack.h | 12 +- src/pkg/runtime/stack_test.go | 51 +- src/pkg/runtime/string.goc | 98 +- src/pkg/runtime/string_test.go | 45 + src/pkg/runtime/symtab.c | 256 +- src/pkg/runtime/sys_darwin_386.s | 151 +- src/pkg/runtime/sys_darwin_amd64.s | 94 +- src/pkg/runtime/sys_freebsd_386.s | 38 +- src/pkg/runtime/sys_freebsd_amd64.s | 40 +- src/pkg/runtime/sys_freebsd_arm.s | 260 + src/pkg/runtime/sys_linux_386.s | 45 +- src/pkg/runtime/sys_linux_amd64.s | 81 +- src/pkg/runtime/sys_linux_arm.s | 62 +- src/pkg/runtime/sys_netbsd_386.s | 220 +- src/pkg/runtime/sys_netbsd_amd64.s | 181 +- src/pkg/runtime/sys_netbsd_arm.s | 280 + src/pkg/runtime/sys_openbsd_386.s | 135 +- src/pkg/runtime/sys_openbsd_amd64.s | 113 +- src/pkg/runtime/sys_plan9_386.s | 85 +- src/pkg/runtime/sys_plan9_amd64.s | 208 + src/pkg/runtime/sys_windows_386.s | 42 +- src/pkg/runtime/sys_windows_amd64.s | 16 +- src/pkg/runtime/syscall_windows_test.go | 3 +- src/pkg/runtime/thread_darwin.c | 73 +- src/pkg/runtime/thread_freebsd.c | 62 +- src/pkg/runtime/thread_linux.c | 79 +- src/pkg/runtime/thread_netbsd.c | 113 +- src/pkg/runtime/thread_openbsd.c | 76 +- src/pkg/runtime/thread_plan9.c | 222 +- src/pkg/runtime/thread_windows.c | 26 +- src/pkg/runtime/time.goc | 45 +- src/pkg/runtime/time_plan9_386.c | 34 + src/pkg/runtime/traceback_arm.c | 103 +- src/pkg/runtime/traceback_x86.c | 162 +- src/pkg/runtime/type.go | 15 +- src/pkg/runtime/type.h | 56 +- src/pkg/runtime/typekind.h | 41 + src/pkg/runtime/vdso_linux_amd64.c | 331 + src/pkg/runtime/vlop_arm.s | 316 +- src/pkg/runtime/vlop_arm_test.go | 70 + src/pkg/sort/example_keys_test.go | 96 + src/pkg/sort/example_reverse_test.go | 30 - src/pkg/sort/example_test.go | 7 + src/pkg/sort/search.go | 22 +- src/pkg/sort/search_test.go | 22 + src/pkg/sort/sort.go | 57 +- src/pkg/sort/sort_test.go | 41 +- src/pkg/strconv/atof.go | 303 +- src/pkg/strconv/atof_test.go | 129 + src/pkg/strconv/atoi.go | 24 +- src/pkg/strconv/atoi_test.go | 24 + src/pkg/strconv/decimal.go | 2 +- src/pkg/strconv/extfloat.go | 358 +- src/pkg/strconv/fp_test.go | 31 +- src/pkg/strconv/ftoa.go | 161 +- src/pkg/strconv/ftoa_test.go | 70 +- src/pkg/strconv/isprint.go | 125 +- src/pkg/strconv/itoa_test.go | 30 - src/pkg/strconv/quote.go | 5 +- src/pkg/strconv/strconv_test.go | 52 + src/pkg/strconv/testdata/testfp.txt | 181 + src/pkg/strconv/testfp.txt | 181 - src/pkg/strings/example_test.go | 16 + src/pkg/strings/export_test.go | 36 + src/pkg/strings/reader.go | 21 +- src/pkg/strings/reader_test.go | 23 + src/pkg/strings/replace.go | 416 +- src/pkg/strings/replace_test.go | 437 +- src/pkg/strings/search.go | 124 + src/pkg/strings/search_test.go | 90 + src/pkg/strings/strings.go | 116 +- src/pkg/strings/strings_test.go | 121 +- src/pkg/sync/atomic/64bit_arm.go | 36 + src/pkg/sync/atomic/asm_386.s | 52 +- src/pkg/sync/atomic/asm_amd64.s | 36 +- src/pkg/sync/atomic/asm_arm.s | 14 +- src/pkg/sync/atomic/asm_freebsd_arm.s | 89 + src/pkg/sync/atomic/asm_linux_arm.s | 87 +- src/pkg/sync/atomic/asm_netbsd_arm.s | 89 + src/pkg/sync/atomic/atomic_test.go | 189 +- src/pkg/sync/atomic/doc.go | 55 +- src/pkg/sync/atomic/race.go | 213 + src/pkg/sync/cond.go | 18 + src/pkg/sync/example_test.go | 6 +- src/pkg/sync/mutex.go | 16 +- src/pkg/sync/once.go | 2 +- src/pkg/sync/once_test.go | 29 +- src/pkg/sync/race.go | 34 + src/pkg/sync/race0.go | 28 + src/pkg/sync/rwmutex.go | 36 +- src/pkg/sync/waitgroup.go | 39 +- src/pkg/sync/waitgroup_test.go | 2 +- src/pkg/syscall/asm_freebsd_amd64.s | 34 + src/pkg/syscall/asm_freebsd_arm.s | 124 + src/pkg/syscall/asm_linux_amd64.s | 4 +- src/pkg/syscall/asm_netbsd_arm.s | 124 + src/pkg/syscall/asm_plan9_386.s | 8 + src/pkg/syscall/asm_plan9_amd64.s | 165 + src/pkg/syscall/asm_windows_386.s | 2 +- src/pkg/syscall/asm_windows_amd64.s | 2 +- src/pkg/syscall/creds_test.go | 113 + src/pkg/syscall/dir_plan9.go | 205 + src/pkg/syscall/dll_windows.go | 54 +- src/pkg/syscall/env_plan9.go | 56 +- src/pkg/syscall/env_unix.go | 10 + src/pkg/syscall/env_windows.go | 20 +- src/pkg/syscall/exec_bsd.go | 14 + src/pkg/syscall/exec_linux.go | 26 +- src/pkg/syscall/exec_plan9.go | 260 +- src/pkg/syscall/exec_unix.go | 74 +- src/pkg/syscall/exec_windows.go | 28 +- src/pkg/syscall/mkall.sh | 16 +- src/pkg/syscall/mkerrors.sh | 28 +- src/pkg/syscall/mksyscall.pl | 24 +- src/pkg/syscall/mksyscall_windows.pl | 27 +- src/pkg/syscall/netlink_linux.go | 193 +- src/pkg/syscall/passfd_test.go | 201 + src/pkg/syscall/race.go | 22 + src/pkg/syscall/race0.go | 19 + src/pkg/syscall/route_bsd.go | 41 +- src/pkg/syscall/route_darwin.go | 34 +- src/pkg/syscall/route_freebsd.go | 45 +- src/pkg/syscall/route_netbsd.go | 32 +- src/pkg/syscall/route_openbsd.go | 32 +- src/pkg/syscall/security_windows.go | 32 +- src/pkg/syscall/sockcmsg_linux.go | 26 +- src/pkg/syscall/sockcmsg_unix.go | 82 +- src/pkg/syscall/syscall.go | 52 +- src/pkg/syscall/syscall_bsd.go | 48 +- src/pkg/syscall/syscall_darwin.go | 17 +- src/pkg/syscall/syscall_freebsd.go | 19 +- src/pkg/syscall/syscall_freebsd_386.go | 14 + src/pkg/syscall/syscall_freebsd_amd64.go | 16 + src/pkg/syscall/syscall_freebsd_arm.go | 58 + src/pkg/syscall/syscall_linux.go | 118 +- src/pkg/syscall/syscall_linux_386.go | 84 +- src/pkg/syscall/syscall_linux_amd64.go | 7 +- src/pkg/syscall/syscall_linux_arm.go | 97 +- src/pkg/syscall/syscall_netbsd.go | 79 +- src/pkg/syscall/syscall_netbsd_386.go | 8 +- src/pkg/syscall/syscall_netbsd_amd64.go | 10 +- src/pkg/syscall/syscall_netbsd_arm.go | 42 + src/pkg/syscall/syscall_openbsd.go | 10 +- src/pkg/syscall/syscall_plan9.go | 98 +- src/pkg/syscall/syscall_plan9_386.go | 27 + src/pkg/syscall/syscall_plan9_amd64.go | 14 + src/pkg/syscall/syscall_unix.go | 24 + src/pkg/syscall/syscall_windows.go | 319 +- src/pkg/syscall/syscall_windows_test.go | 24 +- src/pkg/syscall/types_darwin.go | 8 + src/pkg/syscall/types_freebsd.go | 15 +- src/pkg/syscall/types_linux.go | 175 +- src/pkg/syscall/types_netbsd.go | 35 +- src/pkg/syscall/types_openbsd.go | 13 +- src/pkg/syscall/zerrors_darwin_386.go | 93 + src/pkg/syscall/zerrors_darwin_amd64.go | 93 + src/pkg/syscall/zerrors_freebsd_386.go | 2 + src/pkg/syscall/zerrors_freebsd_amd64.go | 2 + src/pkg/syscall/zerrors_freebsd_arm.go | 1652 +++++ src/pkg/syscall/zerrors_linux_386.go | 1 + src/pkg/syscall/zerrors_linux_amd64.go | 1 + src/pkg/syscall/zerrors_linux_arm.go | 1 + src/pkg/syscall/zerrors_netbsd_386.go | 907 ++- src/pkg/syscall/zerrors_netbsd_amd64.go | 899 ++- src/pkg/syscall/zerrors_netbsd_arm.go | 1644 +++++ src/pkg/syscall/zerrors_openbsd_386.go | 109 +- src/pkg/syscall/zerrors_openbsd_amd64.go | 107 +- src/pkg/syscall/zerrors_plan9_386.go | 32 +- src/pkg/syscall/zerrors_plan9_amd64.go | 48 + src/pkg/syscall/zsyscall_darwin_386.go | 239 +- src/pkg/syscall/zsyscall_darwin_amd64.go | 239 +- src/pkg/syscall/zsyscall_freebsd_386.go | 227 +- src/pkg/syscall/zsyscall_freebsd_amd64.go | 227 +- src/pkg/syscall/zsyscall_freebsd_arm.go | 1308 ++++ src/pkg/syscall/zsyscall_linux_386.go | 464 +- src/pkg/syscall/zsyscall_linux_amd64.go | 484 +- src/pkg/syscall/zsyscall_linux_arm.go | 495 +- src/pkg/syscall/zsyscall_netbsd_386.go | 214 +- src/pkg/syscall/zsyscall_netbsd_amd64.go | 214 +- src/pkg/syscall/zsyscall_netbsd_arm.go | 1228 ++++ src/pkg/syscall/zsyscall_openbsd_386.go | 220 +- src/pkg/syscall/zsyscall_openbsd_amd64.go | 220 +- src/pkg/syscall/zsyscall_plan9_386.go | 85 +- src/pkg/syscall/zsyscall_plan9_amd64.go | 282 + src/pkg/syscall/zsyscall_windows_386.go | 281 +- src/pkg/syscall/zsyscall_windows_amd64.go | 281 +- src/pkg/syscall/zsysctl_openbsd.go | 2 + src/pkg/syscall/zsysnum_freebsd_386.go | 2 +- src/pkg/syscall/zsysnum_freebsd_amd64.go | 2 +- src/pkg/syscall/zsysnum_freebsd_arm.go | 340 + src/pkg/syscall/zsysnum_linux_arm.go | 2 +- src/pkg/syscall/zsysnum_netbsd_386.go | 13 +- src/pkg/syscall/zsysnum_netbsd_amd64.go | 13 +- src/pkg/syscall/zsysnum_netbsd_arm.go | 271 + src/pkg/syscall/zsysnum_openbsd_386.go | 11 +- src/pkg/syscall/zsysnum_openbsd_amd64.go | 11 +- src/pkg/syscall/zsysnum_plan9_amd64.go | 48 + src/pkg/syscall/ztypes_darwin_386.go | 17 + src/pkg/syscall/ztypes_darwin_amd64.go | 18 + src/pkg/syscall/ztypes_freebsd_386.go | 22 +- src/pkg/syscall/ztypes_freebsd_amd64.go | 22 +- src/pkg/syscall/ztypes_freebsd_arm.go | 436 ++ src/pkg/syscall/ztypes_linux_386.go | 209 +- src/pkg/syscall/ztypes_linux_amd64.go | 209 +- src/pkg/syscall/ztypes_linux_arm.go | 205 +- src/pkg/syscall/ztypes_netbsd_386.go | 295 +- src/pkg/syscall/ztypes_netbsd_amd64.go | 280 +- src/pkg/syscall/ztypes_netbsd_arm.go | 373 ++ src/pkg/syscall/ztypes_openbsd_386.go | 64 +- src/pkg/syscall/ztypes_openbsd_amd64.go | 67 +- src/pkg/syscall/ztypes_plan9_amd64.go | 75 + src/pkg/syscall/ztypes_windows.go | 65 +- src/pkg/testing/allocs.go | 41 + src/pkg/testing/benchmark.go | 84 +- src/pkg/testing/example.go | 80 +- src/pkg/testing/testing.go | 212 +- src/pkg/text/scanner/scanner.go | 23 +- src/pkg/text/scanner/scanner_test.go | 10 +- src/pkg/text/tabwriter/tabwriter.go | 2 +- src/pkg/text/template/doc.go | 17 +- src/pkg/text/template/example_test.go | 16 +- src/pkg/text/template/exec.go | 192 +- src/pkg/text/template/exec_test.go | 88 +- src/pkg/text/template/funcs.go | 18 +- src/pkg/text/template/parse/lex.go | 241 +- src/pkg/text/template/parse/lex_test.go | 384 +- src/pkg/text/template/parse/node.go | 242 +- src/pkg/text/template/parse/parse.go | 289 +- src/pkg/text/template/parse/parse_test.go | 87 +- src/pkg/text/template/template.go | 3 + src/pkg/time/example_test.go | 97 + src/pkg/time/export_test.go | 19 + src/pkg/time/format.go | 485 +- src/pkg/time/internal_test.go | 2 +- src/pkg/time/sleep.go | 38 +- src/pkg/time/sleep_test.go | 74 +- src/pkg/time/tick.go | 4 +- src/pkg/time/time.go | 173 +- src/pkg/time/time_test.go | 424 +- src/pkg/time/zoneinfo.go | 39 +- src/pkg/time/zoneinfo_read.go | 12 +- src/pkg/time/zoneinfo_windows.go | 4 +- src/pkg/unicode/digit.go | 2 +- src/pkg/unicode/graphic.go | 32 +- src/pkg/unicode/letter.go | 80 +- src/pkg/unicode/letter_test.go | 118 + src/pkg/unicode/maketables.go | 75 +- src/pkg/unicode/script_test.go | 11 +- src/pkg/unicode/tables.go | 783 ++- src/pkg/unicode/utf8/example_test.go | 192 + src/pkg/unicode/utf8/utf8.go | 56 +- src/pkg/unicode/utf8/utf8_test.go | 101 +- src/pkg/unsafe/unsafe.go | 4 +- src/race.bash | 40 + src/race.bat | 52 + src/run.bash | 24 +- src/run.bat | 61 +- src/run.rc | 63 + src/sudo.bash | 6 + 1560 files changed, 143182 insertions(+), 49004 deletions(-) create mode 100755 src/all.rc create mode 100755 src/clean.rc create mode 100644 src/cmd/api/clone.go create mode 100644 src/cmd/api/testdata/src/pkg/p3/golden.txt create mode 100644 src/cmd/api/testdata/src/pkg/p3/p3.go create mode 100644 src/cmd/cov/Makefile create mode 100644 src/cmd/cov/doc.go create mode 100644 src/cmd/cov/main.c create mode 100644 src/cmd/cov/tree.c create mode 100644 src/cmd/cov/tree.h create mode 100644 src/cmd/dist/arm.c create mode 100644 src/cmd/dist/plan9.c delete mode 100644 src/cmd/fix/error.go delete mode 100644 src/cmd/fix/error_test.go delete mode 100644 src/cmd/fix/filepath.go delete mode 100644 src/cmd/fix/filepath_test.go delete mode 100644 src/cmd/fix/go1pkgrename.go delete mode 100644 src/cmd/fix/go1pkgrename_test.go delete mode 100644 src/cmd/fix/go1rename.go delete mode 100644 src/cmd/fix/go1rename_test.go delete mode 100644 src/cmd/fix/googlecode.go delete mode 100644 src/cmd/fix/googlecode_test.go delete mode 100644 src/cmd/fix/hashsum.go delete mode 100644 src/cmd/fix/hashsum_test.go delete mode 100644 src/cmd/fix/hmacnew.go delete mode 100644 src/cmd/fix/hmacnew_test.go delete mode 100644 src/cmd/fix/htmlerr.go delete mode 100644 src/cmd/fix/htmlerr_test.go delete mode 100644 src/cmd/fix/httpfinalurl.go delete mode 100644 src/cmd/fix/httpfinalurl_test.go delete mode 100644 src/cmd/fix/httpfs.go delete mode 100644 src/cmd/fix/httpfs_test.go delete mode 100644 src/cmd/fix/httpheaders.go delete mode 100644 src/cmd/fix/httpheaders_test.go delete mode 100644 src/cmd/fix/httpserver.go delete mode 100644 src/cmd/fix/httpserver_test.go delete mode 100644 src/cmd/fix/imagecolor.go delete mode 100644 src/cmd/fix/imagecolor_test.go delete mode 100644 src/cmd/fix/imagenew.go delete mode 100644 src/cmd/fix/imagenew_test.go delete mode 100644 src/cmd/fix/imageycbcr.go delete mode 100644 src/cmd/fix/imageycbcr_test.go delete mode 100644 src/cmd/fix/iocopyn.go delete mode 100644 src/cmd/fix/iocopyn_test.go delete mode 100644 src/cmd/fix/mapdelete.go delete mode 100644 src/cmd/fix/mapdelete_test.go delete mode 100644 src/cmd/fix/math.go delete mode 100644 src/cmd/fix/math_test.go delete mode 100644 src/cmd/fix/netdial.go delete mode 100644 src/cmd/fix/netdial_test.go create mode 100644 src/cmd/fix/netipv6zone.go create mode 100644 src/cmd/fix/netipv6zone_test.go delete mode 100644 src/cmd/fix/netudpgroup.go delete mode 100644 src/cmd/fix/netudpgroup_test.go delete mode 100644 src/cmd/fix/newwriter.go delete mode 100644 src/cmd/fix/newwriter_test.go delete mode 100644 src/cmd/fix/oserrorstring.go delete mode 100644 src/cmd/fix/oserrorstring_test.go delete mode 100644 src/cmd/fix/osopen.go delete mode 100644 src/cmd/fix/osopen_test.go create mode 100644 src/cmd/fix/printerconfig.go create mode 100644 src/cmd/fix/printerconfig_test.go delete mode 100644 src/cmd/fix/procattr.go delete mode 100644 src/cmd/fix/procattr_test.go delete mode 100644 src/cmd/fix/reflect.go delete mode 100644 src/cmd/fix/reflect_test.go delete mode 100644 src/cmd/fix/signal.go delete mode 100644 src/cmd/fix/signal_test.go delete mode 100644 src/cmd/fix/sorthelpers.go delete mode 100644 src/cmd/fix/sorthelpers_test.go delete mode 100644 src/cmd/fix/sortslice.go delete mode 100644 src/cmd/fix/sortslice_test.go delete mode 100644 src/cmd/fix/strconv.go delete mode 100644 src/cmd/fix/strconv_test.go delete mode 100644 src/cmd/fix/stringssplit.go delete mode 100644 src/cmd/fix/stringssplit_test.go delete mode 100644 src/cmd/fix/template.go delete mode 100644 src/cmd/fix/template_test.go delete mode 100644 src/cmd/fix/timefileinfo.go delete mode 100644 src/cmd/fix/timefileinfo_test.go delete mode 100644 src/cmd/fix/url.go delete mode 100644 src/cmd/fix/url2.go delete mode 100644 src/cmd/fix/url2_test.go delete mode 100644 src/cmd/fix/url_test.go delete mode 100644 src/cmd/fix/xmlapi.go delete mode 100644 src/cmd/fix/xmlapi_test.go create mode 100644 src/cmd/gc/racewalk.c create mode 100644 src/cmd/go/signal.go create mode 100644 src/cmd/go/signal_notunix.go create mode 100644 src/cmd/go/signal_unix.go create mode 100644 src/cmd/go/testdata/example1_test.go create mode 100644 src/cmd/go/testdata/example2_test.go create mode 100644 src/cmd/godoc/play-appengine.go create mode 100644 src/cmd/godoc/play-local.go create mode 100644 src/cmd/godoc/play.go mode change 100644 => 100755 src/cmd/godoc/setup-godoc-app.bash create mode 100644 src/cmd/gofmt/testdata/crlf.golden create mode 100644 src/cmd/gofmt/testdata/crlf.input create mode 100644 src/cmd/gofmt/testdata/rewrite5.golden create mode 100644 src/cmd/gofmt/testdata/rewrite5.input create mode 100644 src/cmd/gofmt/testdata/slices1.golden create mode 100644 src/cmd/gofmt/testdata/slices1.input create mode 100644 src/cmd/gofmt/testdata/slices2.golden create mode 100644 src/cmd/gofmt/testdata/slices2.input create mode 100644 src/cmd/gofmt/testdata/typeswitch.golden create mode 100644 src/cmd/gofmt/testdata/typeswitch.input create mode 100644 src/cmd/ld/decodesym.c create mode 100644 src/cmd/prof/Makefile create mode 100644 src/cmd/prof/doc.go create mode 100644 src/cmd/prof/main.c create mode 100644 src/cmd/vet/atomic.go create mode 100644 src/cmd/vet/buildtag.go create mode 100644 src/cmd/vet/buildtag_bad.go create mode 100644 src/cmd/vet/rangeloop.go create mode 100644 src/cmd/vet/test_atomic.go create mode 100644 src/cmd/vet/test_buildtag.go create mode 100644 src/cmd/vet/test_method.go create mode 100644 src/cmd/vet/test_print.go create mode 100644 src/cmd/vet/test_rangeloop.go create mode 100644 src/cmd/vet/test_structtag.go create mode 100644 src/cmd/vet/test_taglit.go delete mode 100644 src/lib9/argv0.c create mode 100644 src/lib9/flag.c delete mode 100644 src/lib9/getuser.c delete mode 100644 src/lib9/utf/runetypebody-6.0.0.h create mode 100644 src/lib9/utf/runetypebody-6.2.0.h create mode 100755 src/make.rc create mode 100644 src/pkg/archive/tar/example_test.go create mode 100644 src/pkg/archive/tar/stat_atim.go create mode 100644 src/pkg/archive/tar/stat_atimespec.go create mode 100644 src/pkg/archive/tar/stat_unix.go create mode 100644 src/pkg/archive/tar/tar_test.go create mode 100644 src/pkg/archive/tar/testdata/pax.tar create mode 100644 src/pkg/archive/tar/testdata/ustar.tar create mode 100644 src/pkg/archive/zip/testdata/zip64.zip create mode 100644 src/pkg/bufio/example_test.go create mode 100644 src/pkg/bufio/export_test.go create mode 100644 src/pkg/bufio/scan.go create mode 100644 src/pkg/bufio/scan_test.go create mode 100644 src/pkg/compress/flate/copy.go create mode 100644 src/pkg/compress/flate/copy_test.go create mode 100644 src/pkg/compress/flate/fixedhuff.go create mode 100644 src/pkg/compress/flate/gen.go create mode 100644 src/pkg/compress/flate/reader_test.go create mode 100644 src/pkg/compress/flate/writer_test.go create mode 100644 src/pkg/compress/zlib/example_test.go create mode 100644 src/pkg/container/heap/example_intheap_test.go create mode 100644 src/pkg/container/heap/example_pq_test.go delete mode 100644 src/pkg/container/heap/example_test.go create mode 100644 src/pkg/container/list/example_test.go create mode 100644 src/pkg/crypto/aes/asm_amd64.s create mode 100644 src/pkg/crypto/aes/cipher_asm.go create mode 100644 src/pkg/crypto/aes/cipher_generic.go create mode 100644 src/pkg/crypto/cipher/cipher_test.go create mode 100644 src/pkg/crypto/cipher/example_test.go create mode 100644 src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2 create mode 100644 src/pkg/crypto/md5/gen.go create mode 100644 src/pkg/crypto/rand/example_test.go create mode 100644 src/pkg/crypto/rc4/rc4_amd64.s create mode 100644 src/pkg/crypto/rc4/rc4_arm.s create mode 100644 src/pkg/crypto/rc4/rc4_asm.go create mode 100644 src/pkg/crypto/rc4/rc4_ref.go delete mode 100644 src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py delete mode 100644 src/pkg/crypto/tls/root_test.go create mode 100644 src/pkg/crypto/tls/ticket.go create mode 100644 src/pkg/crypto/tls/tls_test.go create mode 100644 src/pkg/crypto/x509/pem_decrypt.go create mode 100644 src/pkg/crypto/x509/pem_decrypt_test.go create mode 100644 src/pkg/crypto/x509/root_plan9.go create mode 100644 src/pkg/crypto/x509/sec1.go create mode 100644 src/pkg/crypto/x509/sec1_test.go create mode 100644 src/pkg/database/sql/example_test.go create mode 100644 src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj create mode 100644 src/pkg/debug/elf/testdata/hello-world-core.gz create mode 100644 src/pkg/encoding/base32/example_test.go create mode 100644 src/pkg/encoding/base64/example_test.go create mode 100644 src/pkg/exp/norm/Makefile create mode 100644 src/pkg/exp/norm/composition.go create mode 100644 src/pkg/exp/norm/composition_test.go create mode 100644 src/pkg/exp/norm/example_iter_test.go create mode 100644 src/pkg/exp/norm/forminfo.go create mode 100644 src/pkg/exp/norm/input.go create mode 100644 src/pkg/exp/norm/iter.go create mode 100644 src/pkg/exp/norm/iter_test.go create mode 100644 src/pkg/exp/norm/maketables.go create mode 100644 src/pkg/exp/norm/maketesttables.go create mode 100644 src/pkg/exp/norm/norm_test.go create mode 100644 src/pkg/exp/norm/normalize.go create mode 100644 src/pkg/exp/norm/normalize_test.go create mode 100644 src/pkg/exp/norm/normregtest.go create mode 100644 src/pkg/exp/norm/readwriter.go create mode 100644 src/pkg/exp/norm/readwriter_test.go create mode 100644 src/pkg/exp/norm/tables.go create mode 100644 src/pkg/exp/norm/trie.go create mode 100644 src/pkg/exp/norm/trie_test.go create mode 100644 src/pkg/exp/norm/triedata_test.go create mode 100644 src/pkg/exp/norm/triegen.go create mode 100644 src/pkg/go/ast/commentmap.go create mode 100644 src/pkg/go/ast/commentmap_test.go create mode 100644 src/pkg/go/build/read.go create mode 100644 src/pkg/go/build/read_test.go create mode 100644 src/pkg/go/doc/example_test.go create mode 100644 src/pkg/go/format/format.go create mode 100644 src/pkg/go/format/format_test.go create mode 100644 src/pkg/go/parser/performance_test.go create mode 100644 src/pkg/go/printer/testdata/comments2.golden create mode 100644 src/pkg/go/printer/testdata/comments2.input create mode 100644 src/pkg/go/types/api.go create mode 100644 src/pkg/go/types/builtins.go create mode 100644 src/pkg/go/types/check.go create mode 100644 src/pkg/go/types/check_test.go create mode 100644 src/pkg/go/types/const.go create mode 100644 src/pkg/go/types/conversions.go create mode 100644 src/pkg/go/types/errors.go create mode 100644 src/pkg/go/types/exportdata.go create mode 100644 src/pkg/go/types/expr.go create mode 100644 src/pkg/go/types/gcimporter.go create mode 100644 src/pkg/go/types/gcimporter_test.go create mode 100644 src/pkg/go/types/objects.go create mode 100644 src/pkg/go/types/operand.go create mode 100644 src/pkg/go/types/predicates.go create mode 100644 src/pkg/go/types/resolve.go create mode 100644 src/pkg/go/types/resolver_test.go create mode 100644 src/pkg/go/types/scope.go create mode 100644 src/pkg/go/types/sizes.go create mode 100644 src/pkg/go/types/stmt.go create mode 100644 src/pkg/go/types/testdata/builtins.src create mode 100644 src/pkg/go/types/testdata/const0.src create mode 100644 src/pkg/go/types/testdata/conversions.src create mode 100644 src/pkg/go/types/testdata/decls0.src create mode 100644 src/pkg/go/types/testdata/decls1.src create mode 100644 src/pkg/go/types/testdata/decls2a.src create mode 100644 src/pkg/go/types/testdata/decls2b.src create mode 100644 src/pkg/go/types/testdata/decls3.src create mode 100644 src/pkg/go/types/testdata/exports.go create mode 100644 src/pkg/go/types/testdata/expr0.src create mode 100644 src/pkg/go/types/testdata/expr1.src create mode 100644 src/pkg/go/types/testdata/expr2.src create mode 100644 src/pkg/go/types/testdata/expr3.src create mode 100644 src/pkg/go/types/testdata/stmt0.src create mode 100644 src/pkg/go/types/types.go create mode 100644 src/pkg/go/types/types_test.go create mode 100644 src/pkg/go/types/universe.go create mode 100644 src/pkg/image/jpeg/dct_test.go create mode 100644 src/pkg/image/jpeg/reader_test.go create mode 100644 src/pkg/image/jpeg/scan.go create mode 100644 src/pkg/image/png/paeth.go create mode 100644 src/pkg/image/png/paeth_test.go create mode 100644 src/pkg/image/png/testdata/benchGray.png create mode 100644 src/pkg/image/png/testdata/benchNRGBA-gradient.png create mode 100644 src/pkg/image/png/testdata/benchNRGBA-opaque.png create mode 100644 src/pkg/image/png/testdata/benchPaletted.png create mode 100644 src/pkg/image/png/testdata/benchRGB.png create mode 100644 src/pkg/image/testdata/video-001.progressive.jpeg create mode 100644 src/pkg/image/testdata/video-001.q50.420.jpeg create mode 100644 src/pkg/image/testdata/video-001.q50.420.progressive.jpeg create mode 100644 src/pkg/image/testdata/video-001.q50.422.jpeg create mode 100644 src/pkg/image/testdata/video-001.q50.422.progressive.jpeg create mode 100644 src/pkg/image/testdata/video-001.q50.440.jpeg create mode 100644 src/pkg/image/testdata/video-001.q50.440.progressive.jpeg create mode 100644 src/pkg/image/testdata/video-001.q50.444.jpeg create mode 100644 src/pkg/image/testdata/video-001.q50.444.progressive.jpeg create mode 100644 src/pkg/image/testdata/video-005.gray.q50.2x2.jpeg create mode 100644 src/pkg/image/testdata/video-005.gray.q50.2x2.progressive.jpeg create mode 100644 src/pkg/image/testdata/video-005.gray.q50.jpeg create mode 100644 src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg create mode 100644 src/pkg/io/ioutil/blackhole.go create mode 100644 src/pkg/log/syslog/syslog_plan9.go create mode 100644 src/pkg/math/big/gcd_test.go create mode 100644 src/pkg/math/rand/example_test.go create mode 100644 src/pkg/mime/multipart/quotedprintable.go create mode 100644 src/pkg/mime/multipart/quotedprintable_test.go mode change 100755 => 100644 src/pkg/mime/multipart/testdata/nested-mime delete mode 100644 src/pkg/mime/test.types create mode 100644 src/pkg/mime/testdata/test.types create mode 100644 src/pkg/net/cgo_netbsd.go create mode 100644 src/pkg/net/cgo_openbsd.go create mode 100644 src/pkg/net/conn_test.go delete mode 100644 src/pkg/net/dnsconfig.go create mode 100644 src/pkg/net/dnsconfig_unix.go delete mode 100644 src/pkg/net/doc.go delete mode 100644 src/pkg/net/fd.go create mode 100644 src/pkg/net/fd_bsd.go delete mode 100644 src/pkg/net/fd_freebsd.go delete mode 100644 src/pkg/net/fd_netbsd.go delete mode 100644 src/pkg/net/fd_openbsd.go create mode 100644 src/pkg/net/fd_plan9.go create mode 100644 src/pkg/net/fd_posix_test.go create mode 100644 src/pkg/net/fd_unix.go create mode 100644 src/pkg/net/fd_unix_test.go delete mode 100644 src/pkg/net/file.go create mode 100644 src/pkg/net/file_unix.go create mode 100644 src/pkg/net/http/cgi/plan9_test.go create mode 100644 src/pkg/net/http/cgi/posix_test.go create mode 100644 src/pkg/net/http/cookiejar/jar.go create mode 100644 src/pkg/net/http/cookiejar/jar_test.go create mode 100644 src/pkg/net/http/cookiejar/punycode.go create mode 100644 src/pkg/net/http/cookiejar/punycode_test.go create mode 100644 src/pkg/net/http/httptest/example_test.go create mode 100644 src/pkg/net/http/httptest/recorder_test.go create mode 100644 src/pkg/net/http/npn_test.go create mode 100644 src/pkg/net/http/server_test.go create mode 100644 src/pkg/net/http/transfer_test.go create mode 100644 src/pkg/net/http/z_last_test.go create mode 100644 src/pkg/net/interface_bsd_test.go create mode 100644 src/pkg/net/interface_unix_test.go create mode 100644 src/pkg/net/lookup.go create mode 100644 src/pkg/net/multicast_posix_test.go delete mode 100644 src/pkg/net/multicast_test.go delete mode 100644 src/pkg/net/newpollserver.go create mode 100644 src/pkg/net/newpollserver_unix.go create mode 100644 src/pkg/net/packetconn_test.go create mode 100644 src/pkg/net/port_unix.go create mode 100644 src/pkg/net/protoconn_test.go create mode 100644 src/pkg/net/sendfile_freebsd.go delete mode 100644 src/pkg/net/sock.go create mode 100644 src/pkg/net/sock_cloexec.go create mode 100644 src/pkg/net/sock_posix.go create mode 100644 src/pkg/net/sock_unix.go delete mode 100644 src/pkg/net/sockopt.go create mode 100644 src/pkg/net/sockopt_posix.go delete mode 100644 src/pkg/net/sockoptip.go delete mode 100644 src/pkg/net/sockoptip_darwin.go delete mode 100644 src/pkg/net/sockoptip_freebsd.go delete mode 100644 src/pkg/net/sockoptip_netbsd.go delete mode 100644 src/pkg/net/sockoptip_openbsd.go create mode 100644 src/pkg/net/sockoptip_posix.go create mode 100644 src/pkg/net/sys_cloexec.go create mode 100644 src/pkg/net/tcp_test.go create mode 100644 src/pkg/net/unicast_posix_test.go delete mode 100644 src/pkg/net/unicast_test.go create mode 100644 src/pkg/net/unix_test.go create mode 100644 src/pkg/os/env_unix_test.go create mode 100644 src/pkg/os/error_windows_test.go create mode 100644 src/pkg/os/exec/lp_unix_test.go create mode 100644 src/pkg/os/pipe_bsd.go create mode 100644 src/pkg/os/pipe_linux.go create mode 100644 src/pkg/os/signal/example_test.go create mode 100644 src/pkg/os/types_notwin.go create mode 100644 src/pkg/os/types_windows.go create mode 100644 src/pkg/os/user/lookup.go create mode 100644 src/pkg/path/filepath/example_unix_test.go create mode 100644 src/pkg/path/filepath/path_windows_test.go create mode 100644 src/pkg/reflect/asm_386.s create mode 100644 src/pkg/reflect/asm_amd64.s create mode 100644 src/pkg/reflect/asm_arm.s create mode 100644 src/pkg/reflect/example_test.go create mode 100644 src/pkg/reflect/export_test.go create mode 100644 src/pkg/reflect/makefunc.go create mode 100644 src/pkg/regexp/example_test.go create mode 100644 src/pkg/regexp/syntax/doc.go create mode 100644 src/pkg/runtime/cgo/asm_386.s create mode 100644 src/pkg/runtime/cgo/asm_amd64.s create mode 100644 src/pkg/runtime/cgo/asm_arm.s create mode 100644 src/pkg/runtime/cgo/cgo_arm.c create mode 100644 src/pkg/runtime/cgo/gcc_freebsd_arm.c create mode 100644 src/pkg/runtime/cgo/gcc_netbsd_386.c create mode 100644 src/pkg/runtime/cgo/gcc_netbsd_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_netbsd_arm.c create mode 100644 src/pkg/runtime/cgo/gcc_openbsd_386.c create mode 100644 src/pkg/runtime/cgo/gcc_openbsd_amd64.c create mode 100644 src/pkg/runtime/cgo/netbsd.c create mode 100644 src/pkg/runtime/cgo/openbsd.c create mode 100644 src/pkg/runtime/cgo/setenv.c delete mode 100644 src/pkg/runtime/closure_386.c delete mode 100644 src/pkg/runtime/closure_amd64.c delete mode 100644 src/pkg/runtime/closure_arm.c create mode 100644 src/pkg/runtime/complex_test.go create mode 100644 src/pkg/runtime/crash_cgo_test.go create mode 100644 src/pkg/runtime/crash_test.go create mode 100644 src/pkg/runtime/debug/debug.c create mode 100644 src/pkg/runtime/debug/garbage.go create mode 100644 src/pkg/runtime/debug/garbage_test.go create mode 100644 src/pkg/runtime/defs_freebsd_arm.h create mode 100644 src/pkg/runtime/defs_netbsd_386.go create mode 100644 src/pkg/runtime/defs_netbsd_amd64.go create mode 100644 src/pkg/runtime/defs_netbsd_arm.h create mode 100644 src/pkg/runtime/defs_plan9_amd64.h create mode 100644 src/pkg/runtime/env_plan9.c create mode 100644 src/pkg/runtime/env_posix.c create mode 100644 src/pkg/runtime/iface_test.go create mode 100644 src/pkg/runtime/lfstack.c create mode 100644 src/pkg/runtime/lfstack_test.go create mode 100644 src/pkg/runtime/memclr_arm.s delete mode 100644 src/pkg/runtime/memset_arm.s create mode 100644 src/pkg/runtime/mgc0.go create mode 100644 src/pkg/runtime/mgc0.h create mode 100644 src/pkg/runtime/panic.c create mode 100644 src/pkg/runtime/parfor.c create mode 100644 src/pkg/runtime/parfor_test.go create mode 100644 src/pkg/runtime/race.c create mode 100644 src/pkg/runtime/race.go create mode 100644 src/pkg/runtime/race.h create mode 100644 src/pkg/runtime/race/README create mode 100644 src/pkg/runtime/race/race.go create mode 100644 src/pkg/runtime/race/race_darwin_amd64.syso create mode 100644 src/pkg/runtime/race/race_linux_amd64.syso create mode 100644 src/pkg/runtime/race/race_test.go create mode 100644 src/pkg/runtime/race/race_windows_amd64.syso create mode 100644 src/pkg/runtime/race/testdata/atomic_test.go create mode 100644 src/pkg/runtime/race/testdata/cgo_test.go create mode 100644 src/pkg/runtime/race/testdata/cgo_test_main.go create mode 100644 src/pkg/runtime/race/testdata/chan_test.go create mode 100644 src/pkg/runtime/race/testdata/comp_test.go create mode 100644 src/pkg/runtime/race/testdata/finalizer_test.go create mode 100644 src/pkg/runtime/race/testdata/io_test.go create mode 100644 src/pkg/runtime/race/testdata/map_test.go create mode 100644 src/pkg/runtime/race/testdata/mop_test.go create mode 100644 src/pkg/runtime/race/testdata/mutex_test.go create mode 100644 src/pkg/runtime/race/testdata/regression_test.go create mode 100644 src/pkg/runtime/race/testdata/rwmutex_test.go create mode 100644 src/pkg/runtime/race/testdata/select_test.go create mode 100644 src/pkg/runtime/race/testdata/slice_test.go create mode 100644 src/pkg/runtime/race/testdata/sync_test.go create mode 100644 src/pkg/runtime/race/testdata/waitgroup_test.go create mode 100644 src/pkg/runtime/race0.c create mode 100644 src/pkg/runtime/race_amd64.s create mode 100644 src/pkg/runtime/rt0_freebsd_arm.s create mode 100644 src/pkg/runtime/rt0_netbsd_arm.s create mode 100644 src/pkg/runtime/rt0_plan9_amd64.s create mode 100644 src/pkg/runtime/signal_freebsd_arm.c create mode 100644 src/pkg/runtime/signal_netbsd_arm.c create mode 100644 src/pkg/runtime/signal_plan9_amd64.c create mode 100644 src/pkg/runtime/stack.c create mode 100644 src/pkg/runtime/string_test.go create mode 100644 src/pkg/runtime/sys_freebsd_arm.s create mode 100644 src/pkg/runtime/sys_netbsd_arm.s create mode 100644 src/pkg/runtime/sys_plan9_amd64.s create mode 100644 src/pkg/runtime/time_plan9_386.c create mode 100644 src/pkg/runtime/typekind.h create mode 100644 src/pkg/runtime/vdso_linux_amd64.c create mode 100644 src/pkg/runtime/vlop_arm_test.go create mode 100644 src/pkg/sort/example_keys_test.go delete mode 100644 src/pkg/sort/example_reverse_test.go create mode 100644 src/pkg/strconv/strconv_test.go create mode 100644 src/pkg/strconv/testdata/testfp.txt delete mode 100644 src/pkg/strconv/testfp.txt create mode 100644 src/pkg/strings/search.go create mode 100644 src/pkg/strings/search_test.go create mode 100644 src/pkg/sync/atomic/64bit_arm.go create mode 100644 src/pkg/sync/atomic/asm_freebsd_arm.s create mode 100644 src/pkg/sync/atomic/asm_netbsd_arm.s create mode 100644 src/pkg/sync/atomic/race.go create mode 100644 src/pkg/sync/race.go create mode 100644 src/pkg/sync/race0.go create mode 100644 src/pkg/syscall/asm_freebsd_arm.s create mode 100644 src/pkg/syscall/asm_netbsd_arm.s create mode 100644 src/pkg/syscall/asm_plan9_amd64.s create mode 100644 src/pkg/syscall/creds_test.go create mode 100644 src/pkg/syscall/dir_plan9.go create mode 100644 src/pkg/syscall/passfd_test.go create mode 100644 src/pkg/syscall/race.go create mode 100644 src/pkg/syscall/race0.go create mode 100644 src/pkg/syscall/syscall_freebsd_arm.go create mode 100644 src/pkg/syscall/syscall_netbsd_arm.go create mode 100644 src/pkg/syscall/syscall_plan9_amd64.go create mode 100644 src/pkg/syscall/zerrors_freebsd_arm.go create mode 100644 src/pkg/syscall/zerrors_netbsd_arm.go create mode 100644 src/pkg/syscall/zerrors_plan9_amd64.go create mode 100644 src/pkg/syscall/zsyscall_freebsd_arm.go create mode 100644 src/pkg/syscall/zsyscall_netbsd_arm.go create mode 100644 src/pkg/syscall/zsyscall_plan9_amd64.go create mode 100644 src/pkg/syscall/zsysnum_freebsd_arm.go create mode 100644 src/pkg/syscall/zsysnum_netbsd_arm.go create mode 100644 src/pkg/syscall/zsysnum_plan9_amd64.go create mode 100644 src/pkg/syscall/ztypes_freebsd_arm.go create mode 100644 src/pkg/syscall/ztypes_netbsd_arm.go create mode 100644 src/pkg/syscall/ztypes_plan9_amd64.go create mode 100644 src/pkg/testing/allocs.go create mode 100644 src/pkg/time/export_test.go create mode 100644 src/pkg/unicode/utf8/example_test.go create mode 100755 src/race.bash create mode 100644 src/race.bat create mode 100755 src/run.rc (limited to 'src') diff --git a/src/all.bat b/src/all.bat index 8edfd7f43..8929f7727 100644 --- a/src/all.bat +++ b/src/all.bat @@ -16,8 +16,11 @@ call make.bat --no-banner --no-local if %GOBUILDFAIL%==1 goto end call run.bat --no-rebuild --no-local if %GOBUILDFAIL%==1 goto end +:: we must restore %PATH% before running "dist banner" so that the latter +:: can get the original %PATH% and give suggestion to add %GOROOT%/bin +:: to %PATH% if necessary. set PATH=%OLDPATH% -go tool dist banner +%GOTOOLDIR%/dist banner :end if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL% diff --git a/src/all.rc b/src/all.rc new file mode 100755 index 000000000..14e2f5565 --- /dev/null +++ b/src/all.rc @@ -0,0 +1,16 @@ +#!/bin/rc -e +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +rfork n + +if(! test -f make.rc){ + echo 'all.rc must be run from $GOROOT/src' >[1=2] + exit wrongdir +} + +. ./make.rc --no-banner +bind -b $GOROOT/bin /bin +./run.rc --no-rebuild +$GOTOOLDIR/dist banner # print build info diff --git a/src/clean.rc b/src/clean.rc new file mode 100755 index 000000000..41cab613e --- /dev/null +++ b/src/clean.rc @@ -0,0 +1,14 @@ +#!/bin/rc -e +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +eval `{go tool dist env -9} + +if(! test -x $GOTOOLDIR/dist){ + echo 'cannot find $GOTOOLDIR/dist; nothing to clean' >[1=2] + exit noclean +} + +$GOBIN/go clean -i std +$GOTOOLDIR/dist clean diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y index 512fb5a95..c9fdf05d5 100644 --- a/src/cmd/5a/a.y +++ b/src/cmd/5a/a.y @@ -52,7 +52,7 @@ %token LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA %token LTYPEB LTYPEC LTYPED LTYPEE LTYPEF %token LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK -%token LTYPEL LTYPEM LTYPEN LTYPEBX +%token LTYPEL LTYPEM LTYPEN LTYPEBX LTYPEPLD %token LCONST LSP LSB LFP LPC %token LTYPEX LR LREG LF LFREG LC LCREG LPSR LFCR %token LCOND LS LAT @@ -286,21 +286,29 @@ inst: outcode(AWORD, Always, &nullgen, NREG, &g); } /* - * MULL hi,lo,r1,r2 + * MULL r1,r2,(hi,lo) */ | LTYPEM cond reg ',' reg ',' regreg { outcode($1, $2, &$3, $5.reg, &$7); } /* - * MULA hi,lo,r1,r2 + * MULA r1,r2,r3,r4: (r1*r2+r3) & 0xffffffff -> r4 + * MULAW{T,B} r1,r2,r3,r4 */ | LTYPEN cond reg ',' reg ',' reg ',' spreg { - $7.type = D_REGREG; + $7.type = D_REGREG2; $7.offset = $9; outcode($1, $2, &$3, $5.reg, &$7); } +/* + * PLD + */ +| LTYPEPLD oreg + { + outcode($1, Always, &$2, NREG, &nullgen); + } /* * END */ diff --git a/src/cmd/5a/doc.go b/src/cmd/5a/doc.go index a0d2c4c64..29725db04 100644 --- a/src/cmd/5a/doc.go +++ b/src/cmd/5a/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 5a is a version of the Plan 9 assembler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2a + http://plan9.bell-labs.com/magic/man2html/1/8a Its target architecture is the ARM, referred to by these tools as arm. */ -package documentation +package main diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index 4bef0219a..a77e3050d 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -329,8 +329,6 @@ struct "STREXD", LTYPE9, ASTREXD, /* - "ABSF", LTYPEI, AABSF, - "ABSD", LTYPEI, AABSD, "NEGF", LTYPEI, ANEGF, "NEGD", LTYPEI, ANEGD, "SQTF", LTYPEI, ASQTF, @@ -343,6 +341,8 @@ struct "NRMD", LTYPEI, ANRMD, */ + "ABSF", LTYPEI, AABSF, + "ABSD", LTYPEI, AABSD, "SQRTF", LTYPEI, ASQRTF, "SQRTD", LTYPEI, ASQRTD, "CMPF", LTYPEL, ACMPF, @@ -403,6 +403,18 @@ struct "MCR", LTYPEJ, 0, "MRC", LTYPEJ, 1, + + "PLD", LTYPEPLD, APLD, + "UNDEF", LTYPEE, AUNDEF, + "CLZ", LTYPE2, ACLZ, + + "MULWT", LTYPE1, AMULWT, + "MULWB", LTYPE1, AMULWB, + "MULAWT", LTYPEN, AMULAWT, + "MULAWB", LTYPEN, AMULAWB, + + "USEFIELD", LTYPEN, AUSEFIELD, + 0 }; @@ -493,6 +505,7 @@ zaddr(Gen *a, int s) Bputc(&obuf, a->reg); Bputc(&obuf, s); Bputc(&obuf, a->name); + Bputc(&obuf, 0); switch(a->type) { default: print("unknown type %d\n", a->type); @@ -506,6 +519,7 @@ zaddr(Gen *a, int s) break; case D_REGREG: + case D_REGREG2: Bputc(&obuf, a->offset); break; @@ -641,11 +655,37 @@ outhist(void) Hist *h; char *p, *q, *op, c; int n; - + char *tofree; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + + tofree = nil; g = nullgen; c = '/'; for(h = hist; h != H; h = h->link) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -697,6 +737,11 @@ outhist(void) Bputc(&obuf, h->line>>24); zaddr(&nullgen, 0); zaddr(&g, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } diff --git a/src/cmd/5a/y.tab.c b/src/cmd/5a/y.tab.c index a2fba60b3..ce97ee315 100644 --- a/src/cmd/5a/y.tab.c +++ b/src/cmd/5a/y.tab.c @@ -128,28 +128,29 @@ LTYPEM = 279, LTYPEN = 280, LTYPEBX = 281, - LCONST = 282, - LSP = 283, - LSB = 284, - LFP = 285, - LPC = 286, - LTYPEX = 287, - LR = 288, - LREG = 289, - LF = 290, - LFREG = 291, - LC = 292, - LCREG = 293, - LPSR = 294, - LFCR = 295, - LCOND = 296, - LS = 297, - LAT = 298, - LFCONST = 299, - LSCONST = 300, - LNAME = 301, - LLAB = 302, - LVAR = 303 + LTYPEPLD = 282, + LCONST = 283, + LSP = 284, + LSB = 285, + LFP = 286, + LPC = 287, + LTYPEX = 288, + LR = 289, + LREG = 290, + LF = 291, + LFREG = 292, + LC = 293, + LCREG = 294, + LPSR = 295, + LFCR = 296, + LCOND = 297, + LS = 298, + LAT = 299, + LFCONST = 300, + LSCONST = 301, + LNAME = 302, + LLAB = 303, + LVAR = 304 }; #endif /* Tokens. */ @@ -177,28 +178,29 @@ #define LTYPEM 279 #define LTYPEN 280 #define LTYPEBX 281 -#define LCONST 282 -#define LSP 283 -#define LSB 284 -#define LFP 285 -#define LPC 286 -#define LTYPEX 287 -#define LR 288 -#define LREG 289 -#define LF 290 -#define LFREG 291 -#define LC 292 -#define LCREG 293 -#define LPSR 294 -#define LFCR 295 -#define LCOND 296 -#define LS 297 -#define LAT 298 -#define LFCONST 299 -#define LSCONST 300 -#define LNAME 301 -#define LLAB 302 -#define LVAR 303 +#define LTYPEPLD 282 +#define LCONST 283 +#define LSP 284 +#define LSB 285 +#define LFP 286 +#define LPC 287 +#define LTYPEX 288 +#define LR 289 +#define LREG 290 +#define LF 291 +#define LFREG 292 +#define LC 293 +#define LCREG 294 +#define LPSR 295 +#define LFCR 296 +#define LCOND 297 +#define LS 298 +#define LAT 299 +#define LFCONST 300 +#define LSCONST 301 +#define LNAME 302 +#define LLAB 303 +#define LVAR 304 @@ -219,7 +221,7 @@ typedef union YYSTYPE /* Line 214 of yacc.c */ -#line 223 "y.tab.c" +#line 225 "y.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ @@ -231,7 +233,7 @@ typedef union YYSTYPE /* Line 264 of yacc.c */ -#line 235 "y.tab.c" +#line 237 "y.tab.c" #ifdef short # undef short @@ -446,20 +448,20 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 643 +#define YYLAST 603 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 69 +#define YYNTOKENS 70 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 35 /* YYNRULES -- Number of rules. */ -#define YYNRULES 129 +#define YYNRULES 130 /* YYNRULES -- Number of states. */ -#define YYNSTATES 327 +#define YYNSTATES 329 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 303 +#define YYMAXUTOK 304 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -470,16 +472,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 67, 12, 5, 2, - 65, 66, 10, 8, 62, 9, 2, 11, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 59, 61, - 6, 60, 7, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 68, 12, 5, 2, + 66, 67, 10, 8, 63, 9, 2, 11, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 60, 62, + 6, 61, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 63, 2, 64, 4, 2, 2, 2, 2, 2, + 2, 64, 2, 65, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 2, 68, 2, 2, 2, + 2, 2, 2, 2, 3, 2, 69, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -497,7 +499,7 @@ static const yytype_uint8 yytranslate[] = 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 58 + 55, 56, 57, 58, 59 }; #if YYDEBUG @@ -509,71 +511,72 @@ static const yytype_uint16 yyprhs[] = 26, 31, 33, 36, 39, 47, 54, 60, 66, 72, 77, 82, 86, 90, 95, 102, 110, 118, 126, 133, 140, 144, 149, 156, 163, 168, 172, 178, 184, 192, - 199, 212, 220, 230, 233, 234, 237, 240, 241, 244, - 249, 252, 255, 258, 261, 266, 269, 271, 274, 278, - 280, 284, 288, 290, 292, 294, 299, 301, 303, 305, - 307, 309, 311, 313, 317, 319, 324, 326, 331, 333, - 335, 337, 339, 342, 344, 350, 355, 360, 365, 370, - 372, 374, 376, 378, 383, 385, 387, 389, 394, 396, - 398, 400, 405, 410, 416, 424, 425, 428, 431, 433, - 435, 437, 439, 441, 444, 447, 450, 454, 455, 458, - 460, 464, 468, 472, 476, 480, 485, 490, 494, 498 + 199, 212, 220, 230, 233, 236, 237, 240, 243, 244, + 247, 252, 255, 258, 261, 264, 269, 272, 274, 277, + 281, 283, 287, 291, 293, 295, 297, 302, 304, 306, + 308, 310, 312, 314, 316, 320, 322, 327, 329, 334, + 336, 338, 340, 342, 345, 347, 353, 358, 363, 368, + 373, 375, 377, 379, 381, 386, 388, 390, 392, 397, + 399, 401, 403, 408, 413, 419, 427, 428, 431, 434, + 436, 438, 440, 442, 444, 447, 450, 453, 457, 458, + 461, 463, 467, 471, 475, 479, 483, 488, 493, 497, + 501 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 70, 0, -1, -1, -1, 70, 71, 72, -1, -1, - 57, 59, 73, 72, -1, -1, 56, 59, 74, 72, - -1, 56, 60, 103, 61, -1, 58, 60, 103, 61, - -1, 61, -1, 75, 61, -1, 1, 61, -1, 13, - 76, 87, 62, 94, 62, 89, -1, 13, 76, 87, - 62, 94, 62, -1, 13, 76, 87, 62, 89, -1, - 14, 76, 87, 62, 89, -1, 15, 76, 82, 62, - 82, -1, 16, 76, 77, 78, -1, 16, 76, 77, - 83, -1, 36, 77, 84, -1, 17, 77, 78, -1, - 18, 76, 77, 82, -1, 19, 76, 87, 62, 94, - 77, -1, 20, 76, 85, 62, 63, 81, 64, -1, - 20, 76, 63, 81, 64, 62, 85, -1, 21, 76, - 89, 62, 84, 62, 89, -1, 21, 76, 89, 62, - 84, 77, -1, 21, 76, 77, 84, 62, 89, -1, - 22, 76, 77, -1, 23, 98, 62, 88, -1, 23, - 98, 62, 101, 62, 88, -1, 24, 98, 11, 101, - 62, 79, -1, 25, 76, 89, 77, -1, 29, 77, - 79, -1, 30, 76, 97, 62, 97, -1, 32, 76, - 96, 62, 97, -1, 32, 76, 96, 62, 46, 62, - 97, -1, 33, 76, 97, 62, 97, 77, -1, 31, - 76, 101, 62, 103, 62, 94, 62, 95, 62, 95, - 102, -1, 34, 76, 89, 62, 89, 62, 90, -1, - 35, 76, 89, 62, 89, 62, 89, 62, 94, -1, - 26, 77, -1, -1, 76, 51, -1, 76, 52, -1, - -1, 62, 77, -1, 101, 65, 41, 66, -1, 56, - 99, -1, 57, 99, -1, 67, 101, -1, 67, 86, - -1, 67, 10, 67, 86, -1, 67, 55, -1, 80, - -1, 67, 54, -1, 67, 9, 54, -1, 94, -1, - 94, 9, 94, -1, 94, 77, 81, -1, 89, -1, - 79, -1, 91, -1, 91, 65, 94, 66, -1, 49, - -1, 50, -1, 101, -1, 86, -1, 97, -1, 84, - -1, 98, -1, 65, 94, 66, -1, 84, -1, 101, - 65, 93, 66, -1, 98, -1, 98, 65, 93, 66, - -1, 85, -1, 89, -1, 88, -1, 91, -1, 67, - 101, -1, 94, -1, 65, 94, 62, 94, 66, -1, - 94, 6, 6, 92, -1, 94, 7, 7, 92, -1, - 94, 9, 7, 92, -1, 94, 53, 7, 92, -1, - 94, -1, 101, -1, 44, -1, 41, -1, 43, 65, - 103, 66, -1, 93, -1, 38, -1, 48, -1, 47, - 65, 103, 66, -1, 97, -1, 80, -1, 46, -1, - 45, 65, 101, 66, -1, 101, 65, 100, 66, -1, - 56, 99, 65, 100, 66, -1, 56, 6, 7, 99, - 65, 39, 66, -1, -1, 8, 101, -1, 9, 101, - -1, 39, -1, 38, -1, 40, -1, 37, -1, 58, - -1, 9, 101, -1, 8, 101, -1, 68, 101, -1, - 65, 103, 66, -1, -1, 62, 103, -1, 101, -1, - 103, 8, 103, -1, 103, 9, 103, -1, 103, 10, - 103, -1, 103, 11, 103, -1, 103, 12, 103, -1, - 103, 6, 6, 103, -1, 103, 7, 7, 103, -1, - 103, 5, 103, -1, 103, 4, 103, -1, 103, 3, - 103, -1 + 71, 0, -1, -1, -1, 71, 72, 73, -1, -1, + 58, 60, 74, 73, -1, -1, 57, 60, 75, 73, + -1, 57, 61, 104, 62, -1, 59, 61, 104, 62, + -1, 62, -1, 76, 62, -1, 1, 62, -1, 13, + 77, 88, 63, 95, 63, 90, -1, 13, 77, 88, + 63, 95, 63, -1, 13, 77, 88, 63, 90, -1, + 14, 77, 88, 63, 90, -1, 15, 77, 83, 63, + 83, -1, 16, 77, 78, 79, -1, 16, 77, 78, + 84, -1, 36, 78, 85, -1, 17, 78, 79, -1, + 18, 77, 78, 83, -1, 19, 77, 88, 63, 95, + 78, -1, 20, 77, 86, 63, 64, 82, 65, -1, + 20, 77, 64, 82, 65, 63, 86, -1, 21, 77, + 90, 63, 85, 63, 90, -1, 21, 77, 90, 63, + 85, 78, -1, 21, 77, 78, 85, 63, 90, -1, + 22, 77, 78, -1, 23, 99, 63, 89, -1, 23, + 99, 63, 102, 63, 89, -1, 24, 99, 11, 102, + 63, 80, -1, 25, 77, 90, 78, -1, 29, 78, + 80, -1, 30, 77, 98, 63, 98, -1, 32, 77, + 97, 63, 98, -1, 32, 77, 97, 63, 47, 63, + 98, -1, 33, 77, 98, 63, 98, 78, -1, 31, + 77, 102, 63, 104, 63, 95, 63, 96, 63, 96, + 103, -1, 34, 77, 90, 63, 90, 63, 91, -1, + 35, 77, 90, 63, 90, 63, 90, 63, 95, -1, + 37, 87, -1, 26, 78, -1, -1, 77, 52, -1, + 77, 53, -1, -1, 63, 78, -1, 102, 66, 42, + 67, -1, 57, 100, -1, 58, 100, -1, 68, 102, + -1, 68, 87, -1, 68, 10, 68, 87, -1, 68, + 56, -1, 81, -1, 68, 55, -1, 68, 9, 55, + -1, 95, -1, 95, 9, 95, -1, 95, 78, 82, + -1, 90, -1, 80, -1, 92, -1, 92, 66, 95, + 67, -1, 50, -1, 51, -1, 102, -1, 87, -1, + 98, -1, 85, -1, 99, -1, 66, 95, 67, -1, + 85, -1, 102, 66, 94, 67, -1, 99, -1, 99, + 66, 94, 67, -1, 86, -1, 90, -1, 89, -1, + 92, -1, 68, 102, -1, 95, -1, 66, 95, 63, + 95, 67, -1, 95, 6, 6, 93, -1, 95, 7, + 7, 93, -1, 95, 9, 7, 93, -1, 95, 54, + 7, 93, -1, 95, -1, 102, -1, 45, -1, 42, + -1, 44, 66, 104, 67, -1, 94, -1, 39, -1, + 49, -1, 48, 66, 104, 67, -1, 98, -1, 81, + -1, 47, -1, 46, 66, 102, 67, -1, 102, 66, + 101, 67, -1, 57, 100, 66, 101, 67, -1, 57, + 6, 7, 100, 66, 40, 67, -1, -1, 8, 102, + -1, 9, 102, -1, 40, -1, 39, -1, 41, -1, + 38, -1, 59, -1, 9, 102, -1, 8, 102, -1, + 69, 102, -1, 66, 104, 67, -1, -1, 63, 104, + -1, 102, -1, 104, 8, 104, -1, 104, 9, 104, + -1, 104, 10, 104, -1, 104, 11, 104, -1, 104, + 12, 104, -1, 104, 6, 6, 104, -1, 104, 7, + 7, 104, -1, 104, 5, 104, -1, 104, 4, 104, + -1, 104, 3, 104, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -583,15 +586,16 @@ static const yytype_uint16 yyrline[] = 93, 99, 100, 101, 107, 111, 115, 122, 129, 136, 140, 147, 154, 161, 168, 175, 184, 196, 200, 204, 211, 218, 222, 229, 236, 243, 250, 254, 258, 262, - 269, 291, 298, 307, 313, 316, 320, 325, 326, 329, - 335, 344, 352, 358, 363, 368, 374, 377, 383, 391, - 395, 404, 410, 411, 412, 413, 418, 424, 430, 436, - 437, 440, 441, 449, 458, 459, 468, 469, 475, 478, - 479, 480, 482, 490, 498, 507, 513, 519, 525, 533, - 539, 547, 548, 552, 560, 561, 567, 568, 576, 577, - 580, 586, 594, 602, 610, 620, 623, 627, 633, 634, - 635, 638, 639, 643, 647, 651, 655, 661, 664, 670, - 671, 675, 679, 683, 687, 691, 695, 699, 703, 707 + 269, 291, 299, 308, 315, 321, 324, 328, 333, 334, + 337, 343, 352, 360, 366, 371, 376, 382, 385, 391, + 399, 403, 412, 418, 419, 420, 421, 426, 432, 438, + 444, 445, 448, 449, 457, 466, 467, 476, 477, 483, + 486, 487, 488, 490, 498, 506, 515, 521, 527, 533, + 541, 547, 555, 556, 560, 568, 569, 575, 576, 584, + 585, 588, 594, 602, 610, 618, 628, 631, 635, 641, + 642, 643, 646, 647, 651, 655, 659, 663, 669, 672, + 678, 679, 683, 687, 691, 695, 699, 703, 707, 711, + 715 }; #endif @@ -604,13 +608,13 @@ static const char *const yytname[] = "'-'", "'*'", "'/'", "'%'", "LTYPE1", "LTYPE2", "LTYPE3", "LTYPE4", "LTYPE5", "LTYPE6", "LTYPE7", "LTYPE8", "LTYPE9", "LTYPEA", "LTYPEB", "LTYPEC", "LTYPED", "LTYPEE", "LTYPEF", "LTYPEG", "LTYPEH", "LTYPEI", - "LTYPEJ", "LTYPEK", "LTYPEL", "LTYPEM", "LTYPEN", "LTYPEBX", "LCONST", - "LSP", "LSB", "LFP", "LPC", "LTYPEX", "LR", "LREG", "LF", "LFREG", "LC", - "LCREG", "LPSR", "LFCR", "LCOND", "LS", "LAT", "LFCONST", "LSCONST", - "LNAME", "LLAB", "LVAR", "':'", "'='", "';'", "','", "'['", "']'", "'('", - "')'", "'$'", "'~'", "$accept", "prog", "$@1", "line", "$@2", "$@3", - "inst", "cond", "comma", "rel", "ximm", "fcon", "reglist", "gen", - "nireg", "ireg", "ioreg", "oreg", "imsr", "imm", "reg", "regreg", + "LTYPEJ", "LTYPEK", "LTYPEL", "LTYPEM", "LTYPEN", "LTYPEBX", "LTYPEPLD", + "LCONST", "LSP", "LSB", "LFP", "LPC", "LTYPEX", "LR", "LREG", "LF", + "LFREG", "LC", "LCREG", "LPSR", "LFCR", "LCOND", "LS", "LAT", "LFCONST", + "LSCONST", "LNAME", "LLAB", "LVAR", "':'", "'='", "';'", "','", "'['", + "']'", "'('", "')'", "'$'", "'~'", "$accept", "prog", "$@1", "line", + "$@2", "$@3", "inst", "cond", "comma", "rel", "ximm", "fcon", "reglist", + "gen", "nireg", "ireg", "ioreg", "oreg", "imsr", "imm", "reg", "regreg", "shift", "rcon", "sreg", "spreg", "creg", "frcon", "freg", "name", "offset", "pointer", "con", "oexpr", "expr", 0 }; @@ -626,27 +630,28 @@ static const yytype_uint16 yytoknum[] = 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, - 295, 296, 297, 298, 299, 300, 301, 302, 303, 58, - 61, 59, 44, 91, 93, 40, 41, 36, 126 + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 58, 61, 59, 44, 91, 93, 40, 41, 36, 126 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 69, 70, 71, 70, 73, 72, 74, 72, 72, - 72, 72, 72, 72, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 76, 76, 76, 77, 77, 78, - 78, 78, 79, 79, 79, 79, 79, 80, 80, 81, - 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, - 82, 83, 83, 84, 85, 85, 86, 86, 86, 87, - 87, 87, 88, 89, 90, 91, 91, 91, 91, 92, - 92, 93, 93, 93, 94, 94, 95, 95, 96, 96, - 97, 97, 98, 98, 98, 99, 99, 99, 100, 100, - 100, 101, 101, 101, 101, 101, 101, 102, 102, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103 + 0, 70, 71, 72, 71, 74, 73, 75, 73, 73, + 73, 73, 73, 73, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 77, 77, 77, 78, 78, + 79, 79, 79, 80, 80, 80, 80, 80, 81, 81, + 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 84, 84, 85, 86, 86, 87, 87, 87, + 88, 88, 88, 89, 90, 91, 92, 92, 92, 92, + 93, 93, 94, 94, 94, 95, 95, 96, 96, 97, + 97, 98, 98, 99, 99, 99, 100, 100, 100, 101, + 101, 101, 102, 102, 102, 102, 102, 102, 103, 103, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -656,15 +661,16 @@ static const yytype_uint8 yyr2[] = 4, 1, 2, 2, 7, 6, 5, 5, 5, 4, 4, 3, 3, 4, 6, 7, 7, 7, 6, 6, 3, 4, 6, 6, 4, 3, 5, 5, 7, 6, - 12, 7, 9, 2, 0, 2, 2, 0, 2, 4, - 2, 2, 2, 2, 4, 2, 1, 2, 3, 1, - 3, 3, 1, 1, 1, 4, 1, 1, 1, 1, - 1, 1, 1, 3, 1, 4, 1, 4, 1, 1, - 1, 1, 2, 1, 5, 4, 4, 4, 4, 1, - 1, 1, 1, 4, 1, 1, 1, 4, 1, 1, - 1, 4, 4, 5, 7, 0, 2, 2, 1, 1, - 1, 1, 1, 2, 2, 2, 3, 0, 2, 1, - 3, 3, 3, 3, 3, 4, 4, 3, 3, 3 + 12, 7, 9, 2, 2, 0, 2, 2, 0, 2, + 4, 2, 2, 2, 2, 4, 2, 1, 2, 3, + 1, 3, 3, 1, 1, 1, 4, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 4, 1, 4, 1, + 1, 1, 1, 2, 1, 5, 4, 4, 4, 4, + 1, 1, 1, 1, 4, 1, 1, 1, 4, 1, + 1, 1, 4, 4, 5, 7, 0, 2, 2, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 0, 2, + 1, 3, 3, 3, 3, 3, 4, 4, 3, 3, + 3 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state @@ -672,279 +678,271 @@ static const yytype_uint8 yyr2[] = means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 3, 1, 0, 0, 44, 44, 44, 44, 47, - 44, 44, 44, 44, 44, 0, 0, 44, 47, 47, - 44, 44, 44, 44, 44, 44, 47, 0, 0, 0, - 11, 4, 0, 13, 0, 0, 0, 47, 47, 0, - 47, 0, 0, 47, 47, 0, 0, 111, 105, 112, - 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 0, 5, 0, 12, - 95, 92, 0, 91, 45, 46, 0, 0, 80, 79, - 81, 94, 83, 0, 0, 100, 66, 67, 0, 0, - 63, 56, 0, 74, 78, 69, 62, 64, 70, 76, - 68, 0, 48, 105, 105, 22, 0, 0, 0, 0, - 0, 0, 0, 0, 83, 30, 114, 113, 0, 0, - 0, 0, 119, 0, 115, 0, 0, 0, 47, 35, - 0, 0, 0, 99, 0, 98, 0, 0, 0, 0, - 21, 0, 0, 0, 0, 0, 82, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 57, 55, 53, - 52, 0, 0, 0, 0, 105, 19, 20, 71, 72, - 0, 50, 51, 0, 23, 0, 0, 47, 0, 0, - 0, 0, 105, 106, 107, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 116, 31, 0, 109, - 108, 110, 0, 0, 34, 0, 0, 0, 0, 0, - 0, 0, 8, 9, 6, 10, 0, 16, 83, 0, - 0, 0, 0, 17, 0, 73, 58, 0, 18, 0, - 0, 0, 50, 0, 0, 47, 0, 0, 0, 0, - 0, 47, 0, 0, 129, 128, 127, 0, 0, 120, - 121, 122, 123, 124, 0, 102, 0, 36, 0, 100, - 37, 47, 0, 0, 93, 15, 85, 89, 90, 86, - 87, 88, 101, 54, 0, 65, 77, 75, 49, 24, - 0, 60, 61, 0, 29, 47, 28, 0, 103, 125, - 126, 32, 33, 0, 0, 39, 0, 0, 14, 26, - 25, 27, 0, 0, 38, 0, 41, 0, 104, 0, - 0, 0, 0, 96, 0, 0, 42, 0, 0, 0, - 0, 117, 84, 97, 0, 40, 118 + 2, 3, 1, 0, 0, 45, 45, 45, 45, 48, + 45, 45, 45, 45, 45, 0, 0, 45, 48, 48, + 45, 45, 45, 45, 45, 45, 48, 0, 0, 0, + 0, 11, 4, 0, 13, 0, 0, 0, 48, 48, + 0, 48, 0, 0, 48, 48, 0, 0, 112, 106, + 113, 0, 0, 0, 0, 0, 0, 44, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 75, 79, 43, + 77, 0, 7, 0, 5, 0, 12, 96, 93, 0, + 92, 46, 47, 0, 0, 81, 80, 82, 95, 84, + 0, 0, 101, 67, 68, 0, 64, 57, 0, 70, + 63, 65, 71, 69, 0, 49, 106, 106, 22, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 30, 115, + 114, 0, 0, 0, 0, 120, 0, 116, 0, 0, + 0, 48, 35, 0, 0, 0, 100, 0, 99, 0, + 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, + 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 58, 56, 54, 53, 0, 0, 106, 19, + 20, 72, 73, 0, 51, 52, 0, 23, 0, 0, + 48, 0, 0, 0, 0, 106, 107, 108, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, + 31, 0, 110, 109, 111, 0, 0, 34, 0, 0, + 0, 0, 0, 0, 0, 74, 0, 0, 8, 9, + 6, 10, 0, 16, 84, 0, 0, 0, 0, 17, + 0, 59, 0, 18, 0, 51, 0, 0, 48, 0, + 0, 0, 0, 0, 48, 0, 0, 130, 129, 128, + 0, 0, 121, 122, 123, 124, 125, 0, 103, 0, + 36, 0, 101, 37, 48, 0, 0, 78, 76, 94, + 15, 86, 90, 91, 87, 88, 89, 102, 55, 66, + 50, 24, 0, 61, 62, 0, 29, 48, 28, 0, + 104, 126, 127, 32, 33, 0, 0, 39, 0, 0, + 14, 26, 25, 27, 0, 0, 38, 0, 41, 0, + 105, 0, 0, 0, 0, 97, 0, 0, 42, 0, + 0, 0, 0, 118, 85, 98, 0, 40, 119 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 31, 143, 141, 32, 34, 102, 105, - 90, 91, 176, 92, 167, 93, 94, 95, 77, 78, - 79, 306, 80, 266, 81, 114, 314, 134, 98, 99, - 121, 202, 122, 325, 123 + -1, 1, 3, 32, 149, 147, 33, 35, 105, 108, + 96, 97, 179, 98, 170, 67, 68, 99, 84, 85, + 86, 308, 87, 271, 88, 117, 316, 137, 102, 70, + 124, 205, 125, 327, 126 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -147 +#define YYPACT_NINF -114 static const yytype_int16 yypact[] = { - -147, 19, -147, 305, -37, -147, -147, -147, -147, -40, - -147, -147, -147, -147, -147, 448, 448, -147, -40, -40, - -147, -147, -147, -147, -147, -147, -40, -8, -22, -21, - -147, -147, -20, -147, 102, 102, 334, 108, -40, 416, - 108, 102, 397, 48, 108, 482, 482, -147, 95, -147, - 482, 482, -19, 18, 34, 223, -147, 5, 174, 424, - 132, 174, 223, 223, 20, -147, 482, -147, 482, -147, - -147, -147, 30, -147, -147, -147, 482, 35, -147, -147, - -147, -147, 49, 76, 84, -147, -147, -147, 205, 339, - -147, -147, 93, -147, -147, -147, -147, 110, -147, 124, - 125, 442, -147, 98, 98, -147, 128, 373, 134, 36, - 144, 147, 20, 153, -147, -147, -147, -147, 60, 482, - 482, 175, -147, 114, -147, 249, 21, 482, -40, -147, - 182, 183, 12, -147, 185, -147, 188, 189, 198, 36, - -147, 305, 562, 305, 572, 482, -147, 36, 256, 258, - 261, 269, 36, 482, 212, 460, 216, -147, -147, -147, - 125, 373, 36, 138, 586, 95, -147, -147, -147, -147, - 217, -147, -147, 247, -147, 36, 226, 7, 229, 138, - 227, 20, 98, -147, -147, 21, 482, 482, 482, 287, - 301, 482, 482, 482, 482, 482, -147, -147, 248, -147, - -147, -147, 243, 250, -147, 66, 482, 257, 106, 66, - 36, 36, -147, -147, -147, -147, 225, -147, 251, 205, - 205, 205, 205, -147, 266, -147, -147, 478, -147, 267, - 278, 279, 175, 215, 280, -40, 253, 36, 36, 36, - 36, 297, 295, 298, 601, 345, 609, 482, 482, 190, - 190, -147, -147, -147, 300, -147, 5, -147, 552, 303, - -147, -40, 306, 307, -147, 36, -147, -147, -147, -147, - -147, -147, -147, -147, 125, -147, -147, -147, -147, -147, - 486, -147, -147, 309, -147, 130, -147, 331, -147, 121, - 121, -147, -147, 36, 66, -147, 322, 36, -147, -147, - -147, -147, 308, 326, -147, 36, -147, 327, -147, 119, - 329, 36, 333, -147, 338, 36, -147, 482, 119, 330, - 292, 341, -147, -147, 482, -147, 631 + -114, 19, -114, 311, -20, -114, -114, -114, -114, -36, + -114, -114, -114, -114, -114, 413, 413, -114, -36, -36, + -114, -114, -114, -114, -114, -114, -36, 427, -22, -9, + -7, -114, -114, 11, -114, 480, 480, 341, 45, -36, + 229, 45, 480, 210, 436, 45, 435, 435, -114, 152, + -114, 435, 435, 25, 15, 79, 517, -114, 24, 134, + 393, 197, 134, 517, 517, 28, 71, -114, -114, -114, + 35, 58, -114, 435, -114, 435, -114, -114, -114, 69, + -114, -114, -114, 435, 51, -114, -114, -114, -114, 46, + 57, 75, -114, -114, -114, 119, -114, -114, 82, -114, + -114, 88, -114, 58, 368, -114, 159, 159, -114, 113, + 373, 114, 411, 120, 123, 28, 145, -114, -114, -114, + -114, 193, 435, 435, 173, -114, 54, -114, 395, 111, + 435, -36, -114, 178, 182, 21, -114, 198, -114, 201, + 207, 212, 411, -114, 206, 168, 558, 311, 310, 311, + 506, 435, -114, 411, 271, 274, 285, 286, 411, 435, + 87, 228, -114, -114, -114, 58, 373, 411, 152, -114, + -114, -114, -114, 231, -114, -114, 257, -114, 411, 235, + 5, 259, 168, 275, 28, 159, -114, -114, 111, 435, + 435, 435, 333, 344, 435, 435, 435, 435, 435, -114, + -114, 289, -114, -114, -114, 300, 308, -114, 151, 435, + 319, 176, 151, 411, 411, -114, 317, 322, -114, -114, + -114, -114, 224, -114, 312, 71, 71, 71, 71, -114, + 323, -114, 427, -114, 328, 173, 64, 329, -36, 315, + 411, 411, 411, 411, 334, 339, 332, 577, 247, 584, + 435, 435, 273, 273, -114, -114, -114, 340, -114, 24, + -114, 350, 351, -114, -36, 353, 365, -114, -114, -114, + 411, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, 439, -114, -114, 364, -114, 157, -114, 398, + -114, 518, 518, -114, -114, 411, 151, -114, 374, 411, + -114, -114, -114, -114, 382, 394, -114, 411, -114, 397, + -114, 155, 403, 411, 392, -114, 404, 411, -114, 435, + 155, 401, 299, 406, -114, -114, 435, -114, 568 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -147, -147, -147, -114, -147, -147, -147, 579, 44, 311, - -49, 348, 0, -97, -147, -44, -39, -80, -9, -119, - -13, -147, -25, 2, -146, -34, 91, -147, -14, -3, - -89, 228, -11, -147, -30 + -114, -114, -114, -78, -114, -114, -114, 529, 2, 367, + -50, 415, 48, -88, -114, -48, -40, -21, 47, -113, + -19, -114, -28, 137, -59, -35, 154, -114, -49, 18, + -83, 295, -11, -114, -25 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -60 +#define YYTABLE_NINF -61 static const yytype_int16 yytable[] = { - 82, 82, 82, 110, 53, 53, 197, 82, 129, 159, - 174, 97, 52, 54, 171, 172, 237, 230, 231, 2, - 140, 207, 38, 96, 33, 100, 83, 212, 106, 214, - 113, 111, 108, 231, 116, 117, 142, 67, 144, 68, - 124, 69, 128, 125, 130, 127, 135, 136, 131, 137, - 138, 65, 66, 39, 154, 148, 149, 168, 150, 199, - 200, 201, 56, 57, 228, 146, 157, 182, 180, 38, - 64, -59, 89, 82, 70, 177, 232, 71, 160, 72, - 73, 101, 97, 126, 107, 139, 70, 112, 115, 71, - 170, 72, 73, 242, 96, 145, 100, 147, 169, 74, - 75, 118, 151, 119, 120, 154, 119, 120, 183, 184, - 38, 84, 85, 218, 198, 216, 203, 186, 187, 188, - 189, 190, 191, 192, 193, 194, 195, 82, 229, 191, - 192, 193, 194, 195, 217, 291, 97, 241, 152, 223, - 70, 235, 224, 71, 117, 72, 73, 273, 96, 153, - 100, 84, 259, 74, 75, 161, 244, 245, 246, 74, - 75, 249, 250, 251, 252, 253, 312, 313, 70, 76, - 38, 71, 204, 72, 73, 162, 258, 84, 85, 71, - 196, 72, 73, 74, 75, 267, 267, 267, 267, 163, - 164, 257, 38, 173, 260, 261, 175, 262, 263, 132, - 193, 194, 195, 281, 177, 177, 178, 292, 268, 268, - 268, 268, 179, 45, 46, 181, 274, 289, 290, 84, - 85, 238, 269, 270, 271, 74, 75, 284, 186, 187, - 188, 189, 190, 191, 192, 193, 194, 195, 282, 283, - 185, 299, 47, 70, 205, 206, 71, 208, 72, 73, - 209, 210, 298, 199, 200, 201, 234, 45, 46, 303, - 211, 70, 219, 49, 71, 220, 72, 73, 221, 111, - 50, 310, 301, 51, 74, 75, 222, 316, 225, 279, - 304, 319, 233, 227, 307, 286, 47, 320, 234, 240, - 236, 264, 239, 247, 326, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 295, 4, 49, 248, 255, - 254, 226, 256, 265, 50, 280, 76, 51, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 272, 275, 19, 20, 21, 22, 23, 24, - 25, 26, 45, 46, 276, 277, 278, 45, 155, 156, - 188, 189, 190, 191, 192, 193, 194, 195, 323, 285, - 287, 27, 28, 29, 288, 294, 30, 76, 296, 297, - 302, 47, 70, 300, 308, 71, 47, 72, 73, 84, - 85, 45, 46, 86, 87, 74, 75, 305, 309, 311, - 48, 315, 49, 157, 158, 48, 322, 49, 317, 88, - 318, 89, 51, 324, 88, 45, 46, 51, 133, 321, - 47, 70, 166, 243, 71, 0, 72, 73, 84, 85, - 0, 0, 86, 87, 45, 46, 0, 0, 0, 48, - 0, 49, 45, 46, 47, 0, 0, 0, 88, 0, - 89, 51, 0, 0, 0, 0, 0, 0, 74, 75, - 45, 46, 0, 47, 0, 49, 45, 46, 0, 0, - 109, 47, 88, 0, 0, 51, 0, 0, 45, 46, - 0, 0, 103, 104, 49, 74, 75, 0, 0, 47, - 0, 50, 49, 0, 51, 47, 45, 46, 0, 50, - 45, 46, 51, 0, 45, 46, 0, 47, 165, 104, - 49, 0, 0, 0, 48, 0, 49, 88, 0, 0, - 51, 0, 0, 50, 226, 47, 51, 0, 49, 47, - 0, 0, 0, 47, 0, 50, 0, 0, 51, 0, - 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, - 49, 0, 0, 88, 49, 0, 51, 50, 0, 0, - 51, 88, 0, 0, 51, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 35, 36, 37, 0, 40, - 41, 42, 43, 44, 0, 0, 55, 0, 0, 58, - 59, 60, 61, 62, 63, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 293, 189, 190, 191, 192, 193, - 194, 195, 0, 213, 199, 200, 201, 71, 0, 72, - 73, 0, 0, 215, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195 + 89, 89, 89, 113, 54, 54, 69, 89, 132, 101, + 133, 40, 138, 139, 240, 200, 71, 143, 100, 2, + 57, 58, 177, 174, 175, 116, 103, 39, 65, 109, + 210, 144, 114, 53, 55, 119, 120, 131, 72, 73, + 104, 127, 34, 110, 140, 141, 115, 118, 148, 134, + 150, 74, 154, 155, 75, 156, 171, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 183, 39, 218, + -60, 220, 152, 76, 164, 89, 162, 180, 233, 46, + 47, 129, 101, 90, 165, 235, 216, 217, 128, 111, + 130, 100, 95, 173, 142, 46, 47, 81, 82, 103, + 157, 145, 245, 202, 203, 204, 237, 144, 39, 48, + 77, 186, 187, 78, 153, 79, 80, 201, 224, 206, + 158, 199, 172, 217, 146, 48, 222, 46, 160, 161, + 50, 89, 234, 207, 223, 151, 244, 51, 101, 229, + 52, 159, 231, 238, 293, 166, 50, 100, 230, 120, + 202, 203, 204, 51, 167, 103, 52, 48, 121, 260, + 122, 123, 263, 264, 247, 248, 249, 122, 123, 252, + 253, 254, 255, 256, 162, 163, 49, 178, 50, 176, + 91, 92, 241, 181, 261, 66, 81, 82, 52, 182, + 272, 272, 272, 272, 265, 266, 77, 91, 92, 78, + 185, 79, 80, 314, 315, 283, 180, 180, 184, 294, + 78, 278, 79, 80, 273, 273, 273, 273, 46, 47, + 39, 71, 91, 262, 286, 291, 292, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 46, 47, 188, + 281, 208, 301, 91, 92, 209, 288, 306, 48, 81, + 82, 300, 191, 192, 193, 194, 195, 196, 197, 198, + 305, 211, 81, 82, 212, 135, 297, 48, 303, 50, + 213, 114, 312, 215, 112, 214, 66, 225, 318, 52, + 309, 226, 321, 196, 197, 198, 106, 107, 50, 284, + 285, 269, 227, 228, 322, 51, 232, 236, 52, 237, + 239, 328, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 4, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 242, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 243, 250, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 46, + 47, 251, 257, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 274, 275, 276, 325, 258, 28, 29, + 30, 259, 219, 31, 231, 270, 46, 47, 282, 48, + 77, 46, 47, 78, 267, 79, 80, 91, 92, 268, + 277, 93, 94, 81, 82, 279, 280, 287, 49, 290, + 50, 46, 47, 46, 47, 289, 48, 66, 83, 95, + 52, 48, 77, 295, 296, 78, 298, 79, 80, 91, + 92, 46, 47, 93, 94, 168, 107, 50, 299, 302, + 49, 48, 50, 48, 66, 46, 47, 52, 304, 66, + 307, 95, 52, 46, 47, 81, 82, 46, 47, 310, + 77, 48, 50, 78, 50, 79, 80, 311, 319, 51, + 313, 51, 52, 83, 52, 48, 317, 320, 324, 326, + 49, 169, 50, 48, 323, 77, 136, 48, 78, 51, + 79, 80, 52, 246, 49, 0, 50, 0, 81, 82, + 0, 0, 0, 66, 50, 0, 52, 0, 50, 39, + 0, 51, 0, 0, 52, 66, 0, 0, 52, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 77, + 0, 0, 78, 0, 79, 80, 194, 195, 196, 197, + 198, 0, 81, 82, 0, 36, 37, 38, 0, 41, + 42, 43, 44, 45, 0, 0, 56, 0, 83, 59, + 60, 61, 62, 63, 64, 0, 77, 0, 0, 78, + 0, 79, 80, 0, 0, 0, 0, 0, 221, 81, + 82, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 192, 193, 194, 195, 196, 197, 198, 202, 203, 204, + 78, 0, 79, 80 }; static const yytype_int16 yycheck[] = { - 34, 35, 36, 42, 15, 16, 125, 41, 57, 89, - 107, 36, 15, 16, 103, 104, 9, 163, 164, 0, - 64, 9, 62, 36, 61, 36, 35, 141, 39, 143, - 43, 42, 41, 179, 45, 46, 66, 59, 68, 60, - 51, 61, 55, 62, 58, 11, 60, 61, 59, 62, - 63, 59, 60, 9, 88, 6, 7, 101, 9, 38, - 39, 40, 18, 19, 161, 76, 54, 7, 112, 62, - 26, 64, 67, 107, 38, 109, 165, 41, 89, 43, - 44, 37, 107, 65, 40, 65, 38, 43, 44, 41, - 101, 43, 44, 182, 107, 65, 107, 62, 101, 51, - 52, 6, 53, 8, 9, 139, 8, 9, 119, 120, - 62, 45, 46, 147, 125, 145, 127, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 161, 162, 8, - 9, 10, 11, 12, 147, 254, 161, 181, 62, 152, - 38, 175, 153, 41, 155, 43, 44, 227, 161, 65, - 161, 45, 46, 51, 52, 62, 186, 187, 188, 51, - 52, 191, 192, 193, 194, 195, 47, 48, 38, 67, - 62, 41, 128, 43, 44, 65, 206, 45, 46, 41, - 66, 43, 44, 51, 52, 219, 220, 221, 222, 65, - 65, 205, 62, 65, 208, 209, 62, 210, 211, 67, - 10, 11, 12, 237, 238, 239, 62, 256, 219, 220, - 221, 222, 65, 8, 9, 62, 227, 247, 248, 45, - 46, 177, 220, 221, 222, 51, 52, 240, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 238, 239, - 65, 280, 37, 38, 62, 62, 41, 62, 43, 44, - 62, 62, 265, 38, 39, 40, 41, 8, 9, 293, - 62, 38, 6, 58, 41, 7, 43, 44, 7, 280, - 65, 305, 285, 68, 51, 52, 7, 311, 66, 235, - 294, 315, 65, 67, 297, 241, 37, 317, 41, 62, - 64, 66, 63, 6, 324, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 261, 1, 58, 7, 66, - 62, 54, 62, 62, 65, 62, 67, 68, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 66, 66, 29, 30, 31, 32, 33, 34, - 35, 36, 8, 9, 66, 66, 66, 8, 9, 10, - 5, 6, 7, 8, 9, 10, 11, 12, 66, 62, - 65, 56, 57, 58, 66, 62, 61, 67, 62, 62, - 39, 37, 38, 64, 66, 41, 37, 43, 44, 45, - 46, 8, 9, 49, 50, 51, 52, 65, 62, 62, - 56, 62, 58, 54, 55, 56, 66, 58, 65, 65, - 62, 67, 68, 62, 65, 8, 9, 68, 60, 318, - 37, 38, 101, 185, 41, -1, 43, 44, 45, 46, - -1, -1, 49, 50, 8, 9, -1, -1, -1, 56, - -1, 58, 8, 9, 37, -1, -1, -1, 65, -1, - 67, 68, -1, -1, -1, -1, -1, -1, 51, 52, - 8, 9, -1, 37, -1, 58, 8, 9, -1, -1, - 63, 37, 65, -1, -1, 68, -1, -1, 8, 9, - -1, -1, 56, 57, 58, 51, 52, -1, -1, 37, - -1, 65, 58, -1, 68, 37, 8, 9, -1, 65, - 8, 9, 68, -1, 8, 9, -1, 37, 56, 57, - 58, -1, -1, -1, 56, -1, 58, 65, -1, -1, - 68, -1, -1, 65, 54, 37, 68, -1, 58, 37, - -1, -1, -1, 37, -1, 65, -1, -1, 68, -1, - -1, -1, -1, -1, 56, -1, 58, -1, -1, -1, - 58, -1, -1, 65, 58, -1, 68, 65, -1, -1, - 68, 65, -1, -1, 68, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 6, 7, 8, -1, 10, - 11, 12, 13, 14, -1, -1, 17, -1, -1, 20, - 21, 22, 23, 24, 25, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 62, 6, 7, 8, 9, 10, - 11, 12, -1, 61, 38, 39, 40, 41, -1, 43, - 44, -1, -1, 61, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12 + 35, 36, 37, 43, 15, 16, 27, 42, 58, 37, + 59, 9, 61, 62, 9, 128, 27, 65, 37, 0, + 18, 19, 110, 106, 107, 44, 37, 63, 26, 40, + 9, 66, 43, 15, 16, 46, 47, 56, 60, 61, + 38, 52, 62, 41, 63, 64, 44, 45, 73, 60, + 75, 60, 6, 7, 61, 9, 104, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 115, 63, 147, + 65, 149, 83, 62, 95, 110, 55, 112, 166, 8, + 9, 66, 110, 36, 95, 168, 145, 146, 63, 42, + 11, 110, 68, 104, 66, 8, 9, 52, 53, 110, + 54, 66, 185, 39, 40, 41, 42, 142, 63, 38, + 39, 122, 123, 42, 63, 44, 45, 128, 153, 130, + 63, 67, 104, 182, 66, 38, 151, 8, 9, 10, + 59, 166, 167, 131, 153, 66, 184, 66, 166, 158, + 69, 66, 55, 178, 257, 63, 59, 166, 159, 160, + 39, 40, 41, 66, 66, 166, 69, 38, 6, 208, + 8, 9, 211, 212, 189, 190, 191, 8, 9, 194, + 195, 196, 197, 198, 55, 56, 57, 63, 59, 66, + 46, 47, 180, 63, 209, 66, 52, 53, 69, 66, + 225, 226, 227, 228, 213, 214, 39, 46, 47, 42, + 7, 44, 45, 48, 49, 240, 241, 242, 63, 259, + 42, 232, 44, 45, 225, 226, 227, 228, 8, 9, + 63, 232, 46, 47, 243, 250, 251, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 8, 9, 66, + 238, 63, 282, 46, 47, 63, 244, 296, 38, 52, + 53, 270, 5, 6, 7, 8, 9, 10, 11, 12, + 295, 63, 52, 53, 63, 68, 264, 38, 287, 59, + 63, 282, 307, 67, 64, 63, 66, 6, 313, 69, + 299, 7, 317, 10, 11, 12, 57, 58, 59, 241, + 242, 67, 7, 7, 319, 66, 68, 66, 69, 42, + 65, 326, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 1, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 64, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 63, 6, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 8, + 9, 7, 63, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 226, 227, 228, 67, 67, 57, 58, + 59, 63, 62, 62, 55, 63, 8, 9, 63, 38, + 39, 8, 9, 42, 67, 44, 45, 46, 47, 67, + 67, 50, 51, 52, 53, 67, 67, 63, 57, 67, + 59, 8, 9, 8, 9, 66, 38, 66, 68, 68, + 69, 38, 39, 63, 63, 42, 63, 44, 45, 46, + 47, 8, 9, 50, 51, 57, 58, 59, 63, 65, + 57, 38, 59, 38, 66, 8, 9, 69, 40, 66, + 66, 68, 69, 8, 9, 52, 53, 8, 9, 67, + 39, 38, 59, 42, 59, 44, 45, 63, 66, 66, + 63, 66, 69, 68, 69, 38, 63, 63, 67, 63, + 57, 104, 59, 38, 320, 39, 61, 38, 42, 66, + 44, 45, 69, 188, 57, -1, 59, -1, 52, 53, + -1, -1, -1, 66, 59, -1, 69, -1, 59, 63, + -1, 66, -1, -1, 69, 66, -1, -1, 69, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 39, + -1, -1, 42, -1, 44, 45, 8, 9, 10, 11, + 12, -1, 52, 53, -1, 6, 7, 8, -1, 10, + 11, 12, 13, 14, -1, -1, 17, -1, 68, 20, + 21, 22, 23, 24, 25, -1, 39, -1, -1, 42, + -1, 44, 45, -1, -1, -1, -1, -1, 62, 52, + 53, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 6, 7, 8, 9, 10, 11, 12, 39, 40, 41, + 42, -1, 44, 45 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 70, 0, 71, 1, 13, 14, 15, 16, 17, + 0, 71, 0, 72, 1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 29, - 30, 31, 32, 33, 34, 35, 36, 56, 57, 58, - 61, 72, 75, 61, 76, 76, 76, 76, 62, 77, - 76, 76, 76, 76, 76, 8, 9, 37, 56, 58, - 65, 68, 98, 101, 98, 76, 77, 77, 76, 76, - 76, 76, 76, 76, 77, 59, 60, 59, 60, 61, - 38, 41, 43, 44, 51, 52, 67, 87, 88, 89, - 91, 93, 94, 87, 45, 46, 49, 50, 65, 67, - 79, 80, 82, 84, 85, 86, 89, 91, 97, 98, - 101, 77, 77, 56, 57, 78, 101, 77, 87, 63, - 85, 101, 77, 89, 94, 77, 101, 101, 6, 8, - 9, 99, 101, 103, 101, 62, 65, 11, 89, 79, - 97, 101, 67, 80, 96, 97, 97, 89, 89, 65, - 84, 74, 103, 73, 103, 65, 101, 62, 6, 7, - 9, 53, 62, 65, 94, 9, 10, 54, 55, 86, - 101, 62, 65, 65, 65, 56, 78, 83, 84, 98, - 101, 99, 99, 65, 82, 62, 81, 94, 62, 65, - 84, 62, 7, 101, 101, 65, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 66, 88, 101, 38, - 39, 40, 100, 101, 77, 62, 62, 9, 62, 62, - 62, 62, 72, 61, 72, 61, 103, 89, 94, 6, - 7, 7, 7, 89, 101, 66, 54, 67, 82, 94, - 93, 93, 99, 65, 41, 94, 64, 9, 77, 63, - 62, 84, 99, 100, 103, 103, 103, 6, 7, 103, - 103, 103, 103, 103, 62, 66, 62, 97, 103, 46, - 97, 97, 89, 89, 66, 62, 92, 94, 101, 92, - 92, 92, 66, 86, 101, 66, 66, 66, 66, 77, - 62, 94, 81, 81, 89, 62, 77, 65, 66, 103, - 103, 88, 79, 62, 62, 77, 62, 62, 89, 85, - 64, 89, 39, 94, 97, 65, 90, 89, 66, 62, - 94, 62, 47, 48, 95, 62, 94, 65, 62, 94, - 103, 95, 66, 66, 62, 102, 103 + 30, 31, 32, 33, 34, 35, 36, 37, 57, 58, + 59, 62, 73, 76, 62, 77, 77, 77, 77, 63, + 78, 77, 77, 77, 77, 77, 8, 9, 38, 57, + 59, 66, 69, 99, 102, 99, 77, 78, 78, 77, + 77, 77, 77, 77, 77, 78, 66, 85, 86, 87, + 99, 102, 60, 61, 60, 61, 62, 39, 42, 44, + 45, 52, 53, 68, 88, 89, 90, 92, 94, 95, + 88, 46, 47, 50, 51, 68, 80, 81, 83, 87, + 90, 92, 98, 102, 78, 78, 57, 58, 79, 102, + 78, 88, 64, 86, 102, 78, 90, 95, 78, 102, + 102, 6, 8, 9, 100, 102, 104, 102, 63, 66, + 11, 90, 80, 98, 102, 68, 81, 97, 98, 98, + 90, 90, 66, 85, 95, 66, 66, 75, 104, 74, + 104, 66, 102, 63, 6, 7, 9, 54, 63, 66, + 9, 10, 55, 56, 87, 102, 63, 66, 57, 79, + 84, 85, 99, 102, 100, 100, 66, 83, 63, 82, + 95, 63, 66, 85, 63, 7, 102, 102, 66, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 67, + 89, 102, 39, 40, 41, 101, 102, 78, 63, 63, + 9, 63, 63, 63, 63, 67, 94, 94, 73, 62, + 73, 62, 104, 90, 95, 6, 7, 7, 7, 90, + 102, 55, 68, 83, 95, 100, 66, 42, 95, 65, + 9, 78, 64, 63, 85, 100, 101, 104, 104, 104, + 6, 7, 104, 104, 104, 104, 104, 63, 67, 63, + 98, 104, 47, 98, 98, 90, 90, 67, 67, 67, + 63, 93, 95, 102, 93, 93, 93, 67, 87, 67, + 67, 78, 63, 95, 82, 82, 90, 63, 78, 66, + 67, 104, 104, 89, 80, 63, 63, 78, 63, 63, + 90, 86, 65, 90, 40, 95, 98, 66, 91, 90, + 67, 63, 95, 63, 48, 49, 96, 63, 95, 66, + 63, 95, 104, 96, 67, 67, 63, 103, 104 }; #define yyerrok (yyerrstatus = 0) @@ -1974,7 +1972,7 @@ yyreduce: /* Line 1455 of yacc.c */ #line 219 "a.y" { - outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), NREG, &(yyvsp[(4) - (4)].gen)); + outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), 0, &(yyvsp[(4) - (4)].gen)); } break; @@ -2086,9 +2084,9 @@ yyreduce: case 42: /* Line 1455 of yacc.c */ -#line 299 "a.y" +#line 300 "a.y" { - (yyvsp[(7) - (9)].gen).type = D_REGREG; + (yyvsp[(7) - (9)].gen).type = D_REGREG2; (yyvsp[(7) - (9)].gen).offset = (yyvsp[(9) - (9)].lval); outcode((yyvsp[(1) - (9)].lval), (yyvsp[(2) - (9)].lval), &(yyvsp[(3) - (9)].gen), (yyvsp[(5) - (9)].gen).reg, &(yyvsp[(7) - (9)].gen)); } @@ -2097,43 +2095,52 @@ yyreduce: case 43: /* Line 1455 of yacc.c */ -#line 308 "a.y" +#line 309 "a.y" { - outcode((yyvsp[(1) - (2)].lval), Always, &nullgen, NREG, &nullgen); + outcode((yyvsp[(1) - (2)].lval), Always, &(yyvsp[(2) - (2)].gen), NREG, &nullgen); } break; case 44: /* Line 1455 of yacc.c */ -#line 313 "a.y" +#line 316 "a.y" { - (yyval.lval) = Always; + outcode((yyvsp[(1) - (2)].lval), Always, &nullgen, NREG, &nullgen); } break; case 45: /* Line 1455 of yacc.c */ -#line 317 "a.y" +#line 321 "a.y" { - (yyval.lval) = ((yyvsp[(1) - (2)].lval) & ~C_SCOND) | (yyvsp[(2) - (2)].lval); + (yyval.lval) = Always; } break; case 46: /* Line 1455 of yacc.c */ -#line 321 "a.y" +#line 325 "a.y" + { + (yyval.lval) = ((yyvsp[(1) - (2)].lval) & ~C_SCOND) | (yyvsp[(2) - (2)].lval); + } + break; + + case 47: + +/* Line 1455 of yacc.c */ +#line 329 "a.y" { (yyval.lval) = (yyvsp[(1) - (2)].lval) | (yyvsp[(2) - (2)].lval); } break; - case 49: + case 50: /* Line 1455 of yacc.c */ -#line 330 "a.y" +#line 338 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2141,10 +2148,10 @@ yyreduce: } break; - case 50: + case 51: /* Line 1455 of yacc.c */ -#line 336 "a.y" +#line 344 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2155,10 +2162,10 @@ yyreduce: } break; - case 51: + case 52: /* Line 1455 of yacc.c */ -#line 345 "a.y" +#line 353 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2167,10 +2174,10 @@ yyreduce: } break; - case 52: + case 53: /* Line 1455 of yacc.c */ -#line 353 "a.y" +#line 361 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2178,30 +2185,30 @@ yyreduce: } break; - case 53: + case 54: /* Line 1455 of yacc.c */ -#line 359 "a.y" +#line 367 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).type = D_CONST; } break; - case 54: + case 55: /* Line 1455 of yacc.c */ -#line 364 "a.y" +#line 372 "a.y" { (yyval.gen) = (yyvsp[(4) - (4)].gen); (yyval.gen).type = D_OCONST; } break; - case 55: + case 56: /* Line 1455 of yacc.c */ -#line 369 "a.y" +#line 377 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2209,10 +2216,10 @@ yyreduce: } break; - case 57: + case 58: /* Line 1455 of yacc.c */ -#line 378 "a.y" +#line 386 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2220,10 +2227,10 @@ yyreduce: } break; - case 58: + case 59: /* Line 1455 of yacc.c */ -#line 384 "a.y" +#line 392 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2231,19 +2238,19 @@ yyreduce: } break; - case 59: + case 60: /* Line 1455 of yacc.c */ -#line 392 "a.y" +#line 400 "a.y" { (yyval.lval) = 1 << (yyvsp[(1) - (1)].lval); } break; - case 60: + case 61: /* Line 1455 of yacc.c */ -#line 396 "a.y" +#line 404 "a.y" { int i; (yyval.lval)=0; @@ -2254,29 +2261,29 @@ yyreduce: } break; - case 61: + case 62: /* Line 1455 of yacc.c */ -#line 405 "a.y" +#line 413 "a.y" { (yyval.lval) = (1<<(yyvsp[(1) - (3)].lval)) | (yyvsp[(3) - (3)].lval); } break; - case 65: + case 66: /* Line 1455 of yacc.c */ -#line 414 "a.y" +#line 422 "a.y" { (yyval.gen) = (yyvsp[(1) - (4)].gen); (yyval.gen).reg = (yyvsp[(3) - (4)].lval); } break; - case 66: + case 67: /* Line 1455 of yacc.c */ -#line 419 "a.y" +#line 427 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_PSR; @@ -2284,10 +2291,10 @@ yyreduce: } break; - case 67: + case 68: /* Line 1455 of yacc.c */ -#line 425 "a.y" +#line 433 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FPCR; @@ -2295,10 +2302,10 @@ yyreduce: } break; - case 68: + case 69: /* Line 1455 of yacc.c */ -#line 431 "a.y" +#line 439 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2306,10 +2313,10 @@ yyreduce: } break; - case 72: + case 73: /* Line 1455 of yacc.c */ -#line 442 "a.y" +#line 450 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); if((yyvsp[(1) - (1)].gen).name != D_EXTERN && (yyvsp[(1) - (1)].gen).name != D_STATIC) { @@ -2317,10 +2324,10 @@ yyreduce: } break; - case 73: + case 74: /* Line 1455 of yacc.c */ -#line 450 "a.y" +#line 458 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2329,10 +2336,10 @@ yyreduce: } break; - case 75: + case 76: /* Line 1455 of yacc.c */ -#line 460 "a.y" +#line 468 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2341,10 +2348,10 @@ yyreduce: } break; - case 77: + case 78: /* Line 1455 of yacc.c */ -#line 470 "a.y" +#line 478 "a.y" { (yyval.gen) = (yyvsp[(1) - (4)].gen); (yyval.gen).type = D_OREG; @@ -2352,10 +2359,10 @@ yyreduce: } break; - case 82: + case 83: /* Line 1455 of yacc.c */ -#line 483 "a.y" +#line 491 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2363,10 +2370,10 @@ yyreduce: } break; - case 83: + case 84: /* Line 1455 of yacc.c */ -#line 491 "a.y" +#line 499 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_REG; @@ -2374,10 +2381,10 @@ yyreduce: } break; - case 84: + case 85: /* Line 1455 of yacc.c */ -#line 499 "a.y" +#line 507 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_REGREG; @@ -2386,10 +2393,10 @@ yyreduce: } break; - case 85: + case 86: /* Line 1455 of yacc.c */ -#line 508 "a.y" +#line 516 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2397,10 +2404,10 @@ yyreduce: } break; - case 86: + case 87: /* Line 1455 of yacc.c */ -#line 514 "a.y" +#line 522 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2408,10 +2415,10 @@ yyreduce: } break; - case 87: + case 88: /* Line 1455 of yacc.c */ -#line 520 "a.y" +#line 528 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2419,10 +2426,10 @@ yyreduce: } break; - case 88: + case 89: /* Line 1455 of yacc.c */ -#line 526 "a.y" +#line 534 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2430,10 +2437,10 @@ yyreduce: } break; - case 89: + case 90: /* Line 1455 of yacc.c */ -#line 534 "a.y" +#line 542 "a.y" { if((yyval.lval) < 0 || (yyval.lval) >= 16) print("register value out of range\n"); @@ -2441,10 +2448,10 @@ yyreduce: } break; - case 90: + case 91: /* Line 1455 of yacc.c */ -#line 540 "a.y" +#line 548 "a.y" { if((yyval.lval) < 0 || (yyval.lval) >= 32) print("shift value out of range\n"); @@ -2452,19 +2459,19 @@ yyreduce: } break; - case 92: + case 93: /* Line 1455 of yacc.c */ -#line 549 "a.y" +#line 557 "a.y" { (yyval.lval) = REGPC; } break; - case 93: + case 94: /* Line 1455 of yacc.c */ -#line 553 "a.y" +#line 561 "a.y" { if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) print("register value out of range\n"); @@ -2472,19 +2479,19 @@ yyreduce: } break; - case 95: + case 96: /* Line 1455 of yacc.c */ -#line 562 "a.y" +#line 570 "a.y" { (yyval.lval) = REGSP; } break; - case 97: + case 98: /* Line 1455 of yacc.c */ -#line 569 "a.y" +#line 577 "a.y" { if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) print("register value out of range\n"); @@ -2492,10 +2499,10 @@ yyreduce: } break; - case 100: + case 101: /* Line 1455 of yacc.c */ -#line 581 "a.y" +#line 589 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FREG; @@ -2503,10 +2510,10 @@ yyreduce: } break; - case 101: + case 102: /* Line 1455 of yacc.c */ -#line 587 "a.y" +#line 595 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FREG; @@ -2514,10 +2521,10 @@ yyreduce: } break; - case 102: + case 103: /* Line 1455 of yacc.c */ -#line 595 "a.y" +#line 603 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2527,10 +2534,10 @@ yyreduce: } break; - case 103: + case 104: /* Line 1455 of yacc.c */ -#line 603 "a.y" +#line 611 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2540,10 +2547,10 @@ yyreduce: } break; - case 104: + case 105: /* Line 1455 of yacc.c */ -#line 611 "a.y" +#line 619 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2553,181 +2560,181 @@ yyreduce: } break; - case 105: + case 106: /* Line 1455 of yacc.c */ -#line 620 "a.y" +#line 628 "a.y" { (yyval.lval) = 0; } break; - case 106: + case 107: /* Line 1455 of yacc.c */ -#line 624 "a.y" +#line 632 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 107: + case 108: /* Line 1455 of yacc.c */ -#line 628 "a.y" +#line 636 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 112: + case 113: /* Line 1455 of yacc.c */ -#line 640 "a.y" +#line 648 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 113: + case 114: /* Line 1455 of yacc.c */ -#line 644 "a.y" +#line 652 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 114: + case 115: /* Line 1455 of yacc.c */ -#line 648 "a.y" +#line 656 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 115: + case 116: /* Line 1455 of yacc.c */ -#line 652 "a.y" +#line 660 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 116: + case 117: /* Line 1455 of yacc.c */ -#line 656 "a.y" +#line 664 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 117: + case 118: /* Line 1455 of yacc.c */ -#line 661 "a.y" +#line 669 "a.y" { (yyval.lval) = 0; } break; - case 118: + case 119: /* Line 1455 of yacc.c */ -#line 665 "a.y" +#line 673 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 120: + case 121: /* Line 1455 of yacc.c */ -#line 672 "a.y" +#line 680 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 121: + case 122: /* Line 1455 of yacc.c */ -#line 676 "a.y" +#line 684 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 122: + case 123: /* Line 1455 of yacc.c */ -#line 680 "a.y" +#line 688 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 123: + case 124: /* Line 1455 of yacc.c */ -#line 684 "a.y" +#line 692 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 124: + case 125: /* Line 1455 of yacc.c */ -#line 688 "a.y" +#line 696 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 125: + case 126: /* Line 1455 of yacc.c */ -#line 692 "a.y" +#line 700 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 126: + case 127: /* Line 1455 of yacc.c */ -#line 696 "a.y" +#line 704 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 127: + case 128: /* Line 1455 of yacc.c */ -#line 700 "a.y" +#line 708 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 128: + case 129: /* Line 1455 of yacc.c */ -#line 704 "a.y" +#line 712 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 129: + case 130: /* Line 1455 of yacc.c */ -#line 708 "a.y" +#line 716 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } @@ -2736,7 +2743,7 @@ yyreduce: /* Line 1455 of yacc.c */ -#line 2740 "y.tab.c" +#line 2747 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); diff --git a/src/cmd/5a/y.tab.h b/src/cmd/5a/y.tab.h index 28039992c..1dd0cb08a 100644 --- a/src/cmd/5a/y.tab.h +++ b/src/cmd/5a/y.tab.h @@ -63,28 +63,29 @@ LTYPEM = 279, LTYPEN = 280, LTYPEBX = 281, - LCONST = 282, - LSP = 283, - LSB = 284, - LFP = 285, - LPC = 286, - LTYPEX = 287, - LR = 288, - LREG = 289, - LF = 290, - LFREG = 291, - LC = 292, - LCREG = 293, - LPSR = 294, - LFCR = 295, - LCOND = 296, - LS = 297, - LAT = 298, - LFCONST = 299, - LSCONST = 300, - LNAME = 301, - LLAB = 302, - LVAR = 303 + LTYPEPLD = 282, + LCONST = 283, + LSP = 284, + LSB = 285, + LFP = 286, + LPC = 287, + LTYPEX = 288, + LR = 289, + LREG = 290, + LF = 291, + LFREG = 292, + LC = 293, + LCREG = 294, + LPSR = 295, + LFCR = 296, + LCOND = 297, + LS = 298, + LAT = 299, + LFCONST = 300, + LSCONST = 301, + LNAME = 302, + LLAB = 303, + LVAR = 304 }; #endif /* Tokens. */ @@ -112,28 +113,29 @@ #define LTYPEM 279 #define LTYPEN 280 #define LTYPEBX 281 -#define LCONST 282 -#define LSP 283 -#define LSB 284 -#define LFP 285 -#define LPC 286 -#define LTYPEX 287 -#define LR 288 -#define LREG 289 -#define LF 290 -#define LFREG 291 -#define LC 292 -#define LCREG 293 -#define LPSR 294 -#define LFCR 295 -#define LCOND 296 -#define LS 297 -#define LAT 298 -#define LFCONST 299 -#define LSCONST 300 -#define LNAME 301 -#define LLAB 302 -#define LVAR 303 +#define LTYPEPLD 282 +#define LCONST 283 +#define LSP 284 +#define LSB 285 +#define LFP 286 +#define LPC 287 +#define LTYPEX 288 +#define LR 289 +#define LREG 290 +#define LF 291 +#define LFREG 292 +#define LC 293 +#define LCREG 294 +#define LPSR 295 +#define LFCR 296 +#define LCOND 297 +#define LS 298 +#define LAT 299 +#define LFCONST 300 +#define LSCONST 301 +#define LNAME 302 +#define LLAB 303 +#define LVAR 304 @@ -154,7 +156,7 @@ typedef union YYSTYPE /* Line 1676 of yacc.c */ -#line 158 "y.tab.h" +#line 160 "y.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ diff --git a/src/cmd/5c/cgen.c b/src/cmd/5c/cgen.c index 9e74f515b..5ff4f633d 100644 --- a/src/cmd/5c/cgen.c +++ b/src/cmd/5c/cgen.c @@ -950,9 +950,9 @@ sugen(Node *n, Node *nn, int32 w) case OSTRUCT: /* - * rewrite so lhs has no fn call + * rewrite so lhs has no side effect. */ - if(nn != Z && nn->complex >= FNX) { + if(nn != Z && side(nn)) { nod1 = *n; nod1.type = typ(TIND, n->type); regret(&nod2, &nod1); diff --git a/src/cmd/5c/doc.go b/src/cmd/5c/doc.go index 0874293bf..7291d45f4 100644 --- a/src/cmd/5c/doc.go +++ b/src/cmd/5c/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 5c is a version of the Plan 9 C compiler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2c + http://plan9.bell-labs.com/magic/man2html/1/8c Its target architecture is the ARM, referred to by these tools as arm. */ -package documentation +package main diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h index 20652682b..a0fc63c60 100644 --- a/src/cmd/5c/gc.h +++ b/src/cmd/5c/gc.h @@ -297,6 +297,7 @@ void patch(Prog*, int32); int sconst(Node*); int sval(int32); void gpseudo(int, Sym*, Node*); +void gprefetch(Node*); /* * swt.c diff --git a/src/cmd/5c/peep.c b/src/cmd/5c/peep.c index 3a905f099..2f902e02a 100644 --- a/src/cmd/5c/peep.c +++ b/src/cmd/5c/peep.c @@ -824,7 +824,7 @@ xtramodes(Reg *r, Adr *a) Adr v; p = r->prog; - if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */ + if(p->as == AMOVB && p->from.type == D_OREG) /* byte load */ return 0; v = *a; v.type = D_REG; @@ -1067,6 +1067,7 @@ copyu(Prog *p, Adr *v, Adr *s) case ABLT: case ABGT: case ABLE: + case APLD: if(s != A) { if(copysub(&p->from, v, s, 1)) return 1; diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c index 1ccf74a35..42c5193de 100644 --- a/src/cmd/5c/reg.c +++ b/src/cmd/5c/reg.c @@ -66,12 +66,6 @@ rcmp(const void *a1, const void *a2) void regopt(Prog *p) { - USED(p); - // TODO(kaib): optimizer disabled because it smashes R8 when running out of registers - // the disable is unconventionally here because the call is in common code shared by 5c/6c/8c - return; - -#ifdef NOTDEF Reg *r, *r1, *r2; Prog *p1; int i, z; @@ -190,6 +184,14 @@ regopt(Prog *p) r->set.b[z] |= bit.b[z]; break; + /* + * right side read + */ + case APLD: + for(z=0; zuse2.b[z] |= bit.b[z]; + break; + /* * funny */ @@ -492,7 +494,6 @@ brk: r1->link = freer; freer = firstr; } -#endif } void @@ -1149,12 +1150,13 @@ addreg(Adr *a, int rn) * 1 R1 * ... ... * 10 R10 + * 12 R12 */ int32 RtoB(int r) { - if(r < 2 || r >= REGTMP-2) // excluded R9 and R10 for m and g + if(r < 2 || (r >= REGTMP-2 && r != 12)) // excluded R9 and R10 for m and g, but not R12 return 0; return 1L << r; } @@ -1162,7 +1164,7 @@ RtoB(int r) int BtoR(int32 b) { - b &= 0x01fcL; // excluded R9 and R10 for m and g + b &= 0x11fcL; // excluded R9 and R10 for m and g, but not R12 if(b == 0) return 0; return bitno(b); @@ -1173,7 +1175,7 @@ BtoR(int32 b) * 18 F2 * 19 F3 * ... ... - * 23 F7 + * 31 F15 */ int32 FtoB(int f) @@ -1188,7 +1190,7 @@ int BtoF(int32 b) { - b &= 0xfc0000L; + b &= 0xfffc0000L; if(b == 0) return 0; return bitno(b) - 16; diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c index 7268f9af2..87b77518b 100644 --- a/src/cmd/5c/swt.c +++ b/src/cmd/5c/swt.c @@ -393,18 +393,12 @@ outcode(void) } Bprint(&outbuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); - if(ndynimp > 0 || ndynexp > 0) { - int i; - + if(pragcgobuf.to > pragcgobuf.start) { Bprint(&outbuf, "\n"); Bprint(&outbuf, "$$ // exports\n\n"); Bprint(&outbuf, "$$ // local types\n\n"); - Bprint(&outbuf, "$$ // dynimport\n"); - for(i=0; ilink) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -525,6 +545,11 @@ outhist(Biobuf *b) pg.to.type = D_CONST; zwrite(b, &pg, 0, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } @@ -566,7 +591,8 @@ zaddr(char *bp, Adr *a, int s) bp[1] = a->reg; bp[2] = s; bp[3] = a->name; - bp += 4; + bp[4] = 0; + bp += 5; switch(a->type) { default: diag(Z, "unknown type %d in zaddr", a->type); diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index 1a189e3af..b8675fe60 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -1176,17 +1176,15 @@ patch(Prog *op, int32 pc) void gpseudo(int a, Sym *s, Node *n) { - nextpc(); p->as = a; p->from.type = D_OREG; p->from.sym = s; p->from.name = D_EXTERN; - if(a == ATEXT) { + if(a == ATEXT || a == AGLOBL) { p->reg = textflag; textflag = 0; - } else if(a == AGLOBL) - p->reg = 0; + } if(s->class == CSTATIC) p->from.name = D_STATIC; naddr(n, &p->to); @@ -1194,6 +1192,18 @@ gpseudo(int a, Sym *s, Node *n) pc--; } +void +gprefetch(Node *n) +{ + Node n1; + + regalloc(&n1, n, Z); + gmove(n, &n1); + n1.op = OINDREG; + gins(APLD, &n1, Z); + regfree(&n1); +} + int sconst(Node *n) { diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index cccef94c9..9e35f9566 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -15,8 +15,8 @@ void cgen(Node *n, Node *res) { Node *nl, *nr, *r; - Node n1, n2, n3, f0, f1; - int a, w; + Node n1, n2, f0, f1; + int a, w, rg; Prog *p1, *p2, *p3; Addr addr; @@ -30,6 +30,27 @@ cgen(Node *n, Node *res) if(res == N || res->type == T) fatal("cgen: res nil"); + switch(n->op) { + case OSLICE: + case OSLICEARR: + case OSLICESTR: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_slice(n, &n1); + cgen(&n1, res); + } else + cgen_slice(n, res); + return; + case OEFACE: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_eface(n, &n1); + cgen(&n1, res); + } else + cgen_eface(n, res); + return; + } + while(n->op == OCONVNOP) n = n->left; @@ -156,6 +177,7 @@ cgen(Node *n, Node *res) case OADD: case OSUB: case OMUL: + case OLROT: case OLSH: case ORSH: case OAND: @@ -171,7 +193,7 @@ cgen(Node *n, Node *res) switch(n->op) { default: dump("cgen", n); - fatal("cgen: unknown op %N", n); + fatal("cgen: unknown op %+hN", n); break; case OREAL: @@ -190,12 +212,12 @@ cgen(Node *n, Node *res) case OGE: case OGT: case ONOT: - p1 = gbranch(AB, T); + p1 = gbranch(AB, T, 0); p2 = pc; gmove(nodbool(1), res); - p3 = gbranch(AB, T); + p3 = gbranch(AB, T, 0); patch(p1, pc); - bgen(n, 1, p2); + bgen(n, 1, 0, p2); gmove(nodbool(0), res); patch(p3, pc); goto ret; @@ -218,13 +240,10 @@ cgen(Node *n, Node *res) case OMINUS: regalloc(&n1, nl->type, N); cgen(nl, &n1); - nodconst(&n3, nl->type, 0); - regalloc(&n2, nl->type, res); - gmove(&n3, &n2); - gins(optoas(OSUB, nl->type), &n1, &n2); - gmove(&n2, res); + nodconst(&n2, nl->type, 0); + gins(optoas(OMINUS, nl->type), &n2, &n1); + gmove(&n1, res); regfree(&n1); - regfree(&n2); goto ret; // symmetric binary @@ -241,9 +260,14 @@ cgen(Node *n, Node *res) a = optoas(n->op, nl->type); goto abop; + case OHMUL: + cgen_hmul(nl, nr, res); + break; + + case OLROT: case OLSH: case ORSH: - cgen_shift(n->op, nl, nr, res); + cgen_shift(n->op, n->bounded, nl, nr, res); break; case OCONV: @@ -284,17 +308,11 @@ cgen(Node *n, Node *res) break; case OITAB: - // itable of interface value + // interface table is first word of interface value igen(nl, &n1, res); - n1.op = OREGISTER; // was OINDREG - regalloc(&n2, n->type, &n1); - n1.op = OINDREG; n1.type = n->type; - n1.xoffset = 0; - gmove(&n1, &n2); - gmove(&n2, res); + gmove(&n1, res); regfree(&n1); - regfree(&n2); break; case OLEN: @@ -305,11 +323,8 @@ cgen(Node *n, Node *res) cgen(nl, &n1); nodconst(&n2, types[tptr], 0); - regalloc(&n3, n2.type, N); - gmove(&n2, &n3); - gcmp(optoas(OCMP, types[tptr]), &n1, &n3); - regfree(&n3); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + gcmp(optoas(OCMP, types[tptr]), &n1, &n2); + p1 = gbranch(optoas(OEQ, types[tptr]), T, -1); n2 = n1; n2.op = OINDREG; @@ -325,15 +340,10 @@ cgen(Node *n, Node *res) if(istype(nl->type, TSTRING) || isslice(nl->type)) { // both slice and string have len one pointer into the struct. igen(nl, &n1, res); - n1.op = OREGISTER; // was OINDREG - regalloc(&n2, types[TUINT32], &n1); - n1.op = OINDREG; n1.type = types[TUINT32]; - n1.xoffset = Array_nel; - gmove(&n1, &n2); - gmove(&n2, res); + n1.xoffset += Array_nel; + gmove(&n1, res); regfree(&n1); - regfree(&n2); break; } fatal("cgen: OLEN: unknown type %lT", nl->type); @@ -347,11 +357,8 @@ cgen(Node *n, Node *res) cgen(nl, &n1); nodconst(&n2, types[tptr], 0); - regalloc(&n3, n2.type, N); - gmove(&n2, &n3); - gcmp(optoas(OCMP, types[tptr]), &n1, &n3); - regfree(&n3); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + gcmp(optoas(OCMP, types[tptr]), &n1, &n2); + p1 = gbranch(optoas(OEQ, types[tptr]), T, -1); n2 = n1; n2.op = OINDREG; @@ -366,11 +373,9 @@ cgen(Node *n, Node *res) break; } if(isslice(nl->type)) { - regalloc(&n1, types[tptr], res); - agen(nl, &n1); - n1.op = OINDREG; + igen(nl, &n1, res); n1.type = types[TUINT32]; - n1.xoffset = Array_cap; + n1.xoffset += Array_cap; gmove(&n1, res); regfree(&n1); break; @@ -383,7 +388,22 @@ cgen(Node *n, Node *res) break; case OCALLMETH: - cgen_callmeth(n, 0); + case OCALLFUNC: + // Release res so that it is available for cgen_call. + // Pick it up again after the call. + rg = -1; + if(n->ullman >= UINF) { + if(res != N && (res->op == OREGISTER || res->op == OINDREG)) { + rg = res->val.u.reg; + reg[rg]--; + } + } + if(n->op == OCALLMETH) + cgen_callmeth(n, 0); + else + cgen_call(n, 0); + if(rg >= 0) + reg[rg]++; cgen_callret(n, res); break; @@ -392,11 +412,6 @@ cgen(Node *n, Node *res) cgen_callret(n, res); break; - case OCALLFUNC: - cgen_call(n, 0); - cgen_callret(n, res); - break; - case OMOD: case ODIV: a = optoas(n->op, nl->type); @@ -416,18 +431,43 @@ abop: // asymmetric binary if(nl->ullman >= nr->ullman) { regalloc(&n1, nl->type, res); cgen(nl, &n1); - regalloc(&n2, nr->type, N); - cgen(nr, &n2); + switch(n->op) { + case OADD: + case OSUB: + case OAND: + case OOR: + case OXOR: + if(smallintconst(nr)) { + n2 = *nr; + break; + } + default: + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + } } else { - regalloc(&n2, nr->type, res); - cgen(nr, &n2); + switch(n->op) { + case OADD: + case OSUB: + case OAND: + case OOR: + case OXOR: + if(smallintconst(nr)) { + n2 = *nr; + break; + } + default: + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + } regalloc(&n1, nl->type, N); cgen(nl, &n1); } gins(a, &n2, &n1); gmove(&n1, res); regfree(&n1); - regfree(&n2); + if(n2.op != OLITERAL) + regfree(&n2); goto ret; flt: // floating-point. @@ -478,7 +518,7 @@ ret: * returns Prog* to patch to panic call. */ Prog* -cgenindex(Node *n, Node *res) +cgenindex(Node *n, Node *res, int bounded) { Node tmp, lo, hi, zero, n1, n2; @@ -491,7 +531,7 @@ cgenindex(Node *n, Node *res) cgen(n, &tmp); split64(&tmp, &lo, &hi); gmove(&lo, res); - if(debug['B']) { + if(bounded) { splitclean(); return nil; } @@ -504,7 +544,7 @@ cgenindex(Node *n, Node *res) regfree(&n2); regfree(&n1); splitclean(); - return gbranch(ABNE, T); + return gbranch(ABNE, T, -1); } /* @@ -514,11 +554,8 @@ cgenindex(Node *n, Node *res) void agen(Node *n, Node *res) { - Node *nl, *nr; - Node n1, n2, n3, n4, n5, tmp; - Prog *p1, *p2; - uint32 w; - uint64 v; + Node *nl; + Node n1, n2, n3; int r; if(debug['g']) { @@ -531,6 +568,21 @@ agen(Node *n, Node *res) while(n->op == OCONVNOP) n = n->left; + if(isconst(n, CTNIL) && n->type->width > widthptr) { + // Use of a nil interface or nil slice. + // Create a temporary we can take the address of and read. + // The generated code is just going to panic, so it need not + // be terribly efficient. See issue 3670. + tempname(&n1, n->type); + clearfat(&n1); + regalloc(&n2, types[tptr], res); + gins(AMOVW, &n1, &n2); + gmove(&n2, res); + regfree(&n2); + goto ret; + } + + if(n->addable) { memset(&n1, 0, sizeof n1); n1.op = OADDR; @@ -543,11 +595,10 @@ agen(Node *n, Node *res) } nl = n->left; - nr = n->right; switch(n->op) { default: - fatal("agen: unknown op %N", n); + fatal("agen: unknown op %+hN", n); break; case OCALLMETH: @@ -575,16 +626,305 @@ agen(Node *n, Node *res) cgen_aret(n, res); break; + case OSLICE: + case OSLICEARR: + case OSLICESTR: + tempname(&n1, n->type); + cgen_slice(n, &n1); + agen(&n1, res); + break; + + case OEFACE: + tempname(&n1, n->type); + cgen_eface(n, &n1); + agen(&n1, res); + break; + + case OINDEX: + agenr(n, &n1, res); + gmove(&n1, res); + regfree(&n1); + break; + + case ONAME: + // should only get here with names in this func. + if(n->funcdepth > 0 && n->funcdepth != funcdepth) { + dump("bad agen", n); + fatal("agen: bad ONAME funcdepth %d != %d", + n->funcdepth, funcdepth); + } + + // should only get here for heap vars or paramref + if(!(n->class & PHEAP) && n->class != PPARAMREF) { + dump("bad agen", n); + fatal("agen: bad ONAME class %#x", n->class); + } + cgen(n->heapaddr, res); + if(n->xoffset != 0) { + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[TINT32], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + + case OIND: + cgen(nl, res); + break; + + case ODOT: + agen(nl, res); + if(n->xoffset != 0) { + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[TINT32], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + + case ODOTPTR: + cgen(nl, res); + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(nl->type->type->width >= unmappedzero) { + regalloc(&n1, types[tptr], N); + gmove(res, &n1); + regalloc(&n2, types[TUINT8], &n1); + n1.op = OINDREG; + n1.type = types[TUINT8]; + n1.xoffset = 0; + gmove(&n1, &n2); + regfree(&n1); + regfree(&n2); + } + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[tptr], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + } + +ret: + ; +} + +/* + * generate: + * newreg = &n; + * res = newreg + * + * on exit, a has been changed to be *newreg. + * caller must regfree(a). + */ +void +igen(Node *n, Node *a, Node *res) +{ + Node n1, n2; + int r; + + if(debug['g']) { + dump("\nigen-n", n); + } + switch(n->op) { + case ONAME: + if((n->class&PHEAP) || n->class == PPARAMREF) + break; + *a = *n; + return; + + case OINDREG: + // Increase the refcount of the register so that igen's caller + // has to call regfree. + if(n->val.u.reg != REGSP) + reg[n->val.u.reg]++; + *a = *n; + return; + + case ODOT: + igen(n->left, a, res); + a->xoffset += n->xoffset; + a->type = n->type; + return; + + case ODOTPTR: + if(n->left->addable + || n->left->op == OCALLFUNC + || n->left->op == OCALLMETH + || n->left->op == OCALLINTER) { + // igen-able nodes. + igen(n->left, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + } else { + regalloc(a, types[tptr], res); + cgen(n->left, a); + } + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(n->left->type->type->width >= unmappedzero) { + regalloc(&n1, types[tptr], N); + gmove(a, &n1); + regalloc(&n2, types[TUINT8], &n1); + n1.op = OINDREG; + n1.type = types[TUINT8]; + n1.xoffset = 0; + gmove(&n1, &n2); + regfree(&n1); + regfree(&n2); + } + } + a->op = OINDREG; + a->xoffset = n->xoffset; + a->type = n->type; + return; + + case OCALLMETH: + case OCALLFUNC: + case OCALLINTER: + // Release res so that it is available for cgen_call. + // Pick it up again after the call. + r = -1; + if(n->ullman >= UINF) { + if(res != N && (res->op == OREGISTER || res->op == OINDREG)) { + r = res->val.u.reg; + reg[r]--; + } + } + switch(n->op) { + case OCALLMETH: + cgen_callmeth(n, 0); + break; + case OCALLFUNC: + cgen_call(n, 0); + break; + case OCALLINTER: + cgen_callinter(n, N, 0); + break; + } + if(r >= 0) + reg[r]++; + regalloc(a, types[tptr], res); + cgen_aret(n, a); + a->op = OINDREG; + a->type = n->type; + return; + } + + agenr(n, a, res); + a->op = OINDREG; + a->type = n->type; +} + +/* + * allocate a register in res and generate + * newreg = &n + * The caller must call regfree(a). + */ +void +cgenr(Node *n, Node *a, Node *res) +{ + Node n1; + + if(debug['g']) + dump("cgenr-n", n); + + if(isfat(n->type)) + fatal("cgenr on fat node"); + + if(n->addable) { + regalloc(a, types[tptr], res); + gmove(n, a); + return; + } + + switch(n->op) { + case ONAME: + case ODOT: + case ODOTPTR: + case OINDEX: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + break; + default: + regalloc(a, n->type, res); + cgen(n, a); + break; + } +} + +/* + * generate: + * newreg = &n; + * + * caller must regfree(a). + */ +void +agenr(Node *n, Node *a, Node *res) +{ + Node *nl, *nr; + Node n1, n2, n3, n4, tmp; + Prog *p1, *p2; + uint32 w; + uint64 v; + int bounded; + + if(debug['g']) + dump("agenr-n", n); + + nl = n->left; + nr = n->right; + + switch(n->op) { + case ODOT: + case ODOTPTR: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + agen(&n1, a); + regfree(&n1); + break; + + case OIND: + cgenr(n->left, a, res); + break; + case OINDEX: p2 = nil; // to be patched to panicindex. w = n->type->width; + bounded = debug['B'] || n->bounded; if(nr->addable) { if(!isconst(nr, CTINT)) tempname(&tmp, types[TINT32]); if(!isconst(nl, CTSTR)) agenr(nl, &n3, res); if(!isconst(nr, CTINT)) { - p2 = cgenindex(nr, &tmp); + p2 = cgenindex(nr, &tmp, bounded); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } @@ -592,17 +932,16 @@ agen(Node *n, Node *res) if(nl->addable) { if(!isconst(nr, CTINT)) { tempname(&tmp, types[TINT32]); - p2 = cgenindex(nr, &tmp); + p2 = cgenindex(nr, &tmp, bounded); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } if(!isconst(nl, CTSTR)) { - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + agenr(nl, &n3, res); } } else { tempname(&tmp, types[TINT32]); - p2 = cgenindex(nr, &tmp); + p2 = cgenindex(nr, &tmp, bounded); nr = &tmp; if(!isconst(nl, CTSTR)) agenr(nl, &n3, res); @@ -614,26 +953,37 @@ agen(Node *n, Node *res) // i is in &n1 (if not constant) // w is width + // explicit check for nil if array is large enough + // that we might derive too big a pointer. + if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { + regalloc(&n4, types[tptr], N); + gmove(&n3, &n4); + regalloc(&tmp, types[TUINT8], &n4); + n4.op = OINDREG; + n4.type = types[TUINT8]; + n4.xoffset = 0; + gmove(&n4, &tmp); + regfree(&n4); + regfree(&tmp); + } + // constant index if(isconst(nr, CTINT)) { if(isconst(nl, CTSTR)) fatal("constant string constant index"); v = mpgetfix(nr->val.u.xval); if(isslice(nl->type) || nl->type->etype == TSTRING) { - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; regalloc(&n4, n1.type, N); - cgen(&n1, &n4); + gmove(&n1, &n4); nodconst(&n2, types[TUINT32], v); - regalloc(&n5, n2.type, N); - gmove(&n2, &n5); - gcmp(optoas(OCMP, types[TUINT32]), &n4, &n5); + gcmp(optoas(OCMP, types[TUINT32]), &n4, &n2); regfree(&n4); - regfree(&n5); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); + p1 = gbranch(optoas(OGT, types[TUINT32]), T, +1); ginscall(panicindex, 0); patch(p1, pc); } @@ -646,13 +996,8 @@ agen(Node *n, Node *res) } nodconst(&n2, types[tptr], v*w); - regalloc(&n4, n2.type, N); - gmove(&n2, &n4); - gins(optoas(OADD, types[tptr]), &n4, &n3); - regfree(&n4); - - gmove(&n3, res); - regfree(&n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + *a = n3; break; } @@ -660,25 +1005,24 @@ agen(Node *n, Node *res) gmove(&n1, &n2); regfree(&n1); - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { // check bounds - regalloc(&n4, types[TUINT32], N); if(isconst(nl, CTSTR)) { - nodconst(&n1, types[TUINT32], nl->val.u.sval->len); - gmove(&n1, &n4); + nodconst(&n4, types[TUINT32], nl->val.u.sval->len); } else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; - cgen(&n1, &n4); - } else { - nodconst(&n1, types[TUINT32], nl->type->bound); + regalloc(&n4, types[TUINT32], N); gmove(&n1, &n4); + } else { + nodconst(&n4, types[TUINT32], nl->type->bound); } gcmp(optoas(OCMP, types[TUINT32]), &n2, &n4); - regfree(&n4); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(n4.op == OREGISTER) + regfree(&n4); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); if(p2) patch(p2, pc); ginscall(panicindex, 0); @@ -713,7 +1057,7 @@ agen(Node *n, Node *res) else if(w == 4) gshift(AADD, &n2, SHIFT_LL, 2, &n3); else if(w == 8) - gshift(AADD, &n2, SHIFT_LL, 3, &n3); + gshift(AADD, &n2, SHIFT_LL, 3, &n3); } else { regalloc(&n4, types[TUINT32], N); nodconst(&n1, types[TUINT32], w); @@ -721,122 +1065,21 @@ agen(Node *n, Node *res) gins(optoas(OMUL, types[TUINT32]), &n4, &n2); gins(optoas(OADD, types[tptr]), &n2, &n3); regfree(&n4); - gmove(&n3, res); } - gmove(&n3, res); + *a = n3; regfree(&n2); - regfree(&n3); break; - case ONAME: - // should only get here with names in this func. - if(n->funcdepth > 0 && n->funcdepth != funcdepth) { - dump("bad agen", n); - fatal("agen: bad ONAME funcdepth %d != %d", - n->funcdepth, funcdepth); - } - - // should only get here for heap vars or paramref - if(!(n->class & PHEAP) && n->class != PPARAMREF) { - dump("bad agen", n); - fatal("agen: bad ONAME class %#x", n->class); - } - cgen(n->heapaddr, res); - if(n->xoffset != 0) { - nodconst(&n1, types[TINT32], n->xoffset); - regalloc(&n2, n1.type, N); - regalloc(&n3, types[TINT32], N); - gmove(&n1, &n2); - gmove(res, &n3); - gins(optoas(OADD, types[tptr]), &n2, &n3); - gmove(&n3, res); - regfree(&n2); - regfree(&n3); - } - break; - - case OIND: - cgen(nl, res); - break; - - case ODOT: - agen(nl, res); - if(n->xoffset != 0) { - nodconst(&n1, types[TINT32], n->xoffset); - regalloc(&n2, n1.type, N); - regalloc(&n3, types[TINT32], N); - gmove(&n1, &n2); - gmove(res, &n3); - gins(optoas(OADD, types[tptr]), &n2, &n3); - gmove(&n3, res); - regfree(&n2); - regfree(&n3); - } - break; - - case ODOTPTR: - cgen(nl, res); - if(n->xoffset != 0) { - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. - if(nl->type->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], N); - gmove(res, &n1); - p1 = gins(AMOVW, &n1, &n1); - p1->from.type = D_OREG; - p1->from.offset = 0; - regfree(&n1); - } - nodconst(&n1, types[TINT32], n->xoffset); - regalloc(&n2, n1.type, N); - regalloc(&n3, types[tptr], N); - gmove(&n1, &n2); - gmove(res, &n3); - gins(optoas(OADD, types[tptr]), &n2, &n3); - gmove(&n3, res); - regfree(&n2); - regfree(&n3); - } + default: + regalloc(a, types[tptr], res); + agen(n, a); break; } - -ret: - ; } -/* - * generate: - * newreg = &n; - * res = newreg - * - * on exit, a has been changed to be *newreg. - * caller must regfree(a). - */ void -igen(Node *n, Node *a, Node *res) -{ - regalloc(a, types[tptr], res); - agen(n, a); - a->op = OINDREG; - a->type = n->type; -} - -/* - * generate: - * newreg = &n; - * - * caller must regfree(a). - */ -void -agenr(Node *n, Node *a, Node *res) -{ - regalloc(a, types[tptr], res); - agen(n, a); -} - -void -gencmp0(Node *n, Type *t, int o, Prog *to) +gencmp0(Node *n, Type *t, int o, int likely, Prog *to) { Node n1, n2, n3; int a; @@ -853,7 +1096,7 @@ gencmp0(Node *n, Type *t, int o, Prog *to) } else gins(ATST, &n1, N); a = optoas(o, t); - patch(gbranch(a, t), to); + patch(gbranch(a, t, likely), to); regfree(&n1); } @@ -862,7 +1105,7 @@ gencmp0(Node *n, Type *t, int o, Prog *to) * if(n == true) goto to; */ void -bgen(Node *n, int true, Prog *to) +bgen(Node *n, int true, int likely, Prog *to) { int et, a; Node *nl, *nr, *r; @@ -900,13 +1143,13 @@ bgen(Node *n, int true, Prog *to) a = ONE; if(!true) a = OEQ; - gencmp0(n, n->type, a, to); + gencmp0(n, n->type, a, likely, to); goto ret; case OLITERAL: // need to ask if it is bool? if(!true == !n->val.u.bval) - patch(gbranch(AB, T), to); + patch(gbranch(AB, T, 0), to); goto ret; case OANDAND: @@ -914,12 +1157,12 @@ bgen(Node *n, int true, Prog *to) goto caseor; caseand: - p1 = gbranch(AB, T); - p2 = gbranch(AB, T); + p1 = gbranch(AB, T, 0); + p2 = gbranch(AB, T, 0); patch(p1, pc); - bgen(n->left, !true, p2); - bgen(n->right, !true, p2); - p1 = gbranch(AB, T); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AB, T, 0); patch(p1, to); patch(p2, pc); goto ret; @@ -929,8 +1172,8 @@ bgen(Node *n, int true, Prog *to) goto caseand; caseor: - bgen(n->left, true, to); - bgen(n->right, true, to); + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); goto ret; case OEQ: @@ -952,7 +1195,7 @@ bgen(Node *n, int true, Prog *to) switch(n->op) { case ONOT: - bgen(nl, !true, to); + bgen(nl, !true, likely, to); goto ret; case OEQ: @@ -965,14 +1208,14 @@ bgen(Node *n, int true, Prog *to) if(!true) { if(isfloat[nl->type->etype]) { // brcom is not valid on floats when NaN is involved. - p1 = gbranch(AB, T); - p2 = gbranch(AB, T); + p1 = gbranch(AB, T, 0); + p2 = gbranch(AB, T, 0); patch(p1, pc); ll = n->ninit; n->ninit = nil; - bgen(n, 1, p2); + bgen(n, 1, -likely, p2); n->ninit = ll; - patch(gbranch(AB, T), to); + patch(gbranch(AB, T, 0), to); patch(p2, pc); goto ret; } @@ -995,34 +1238,12 @@ bgen(Node *n, int true, Prog *to) break; } - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = Array_array; - gencmp0(&n2, types[tptr], a, to); - regfree(&n1); - break; - -#ifdef NOTDEF - a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - regalloc(&n3, types[tptr], N); - regalloc(&n4, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = Array_array; - gmove(&n2, &n4); - nodconst(&tmp, types[tptr], 0); - gmove(&tmp, &n3); - gcmp(optoas(OCMP, types[tptr]), &n4, &n3); - patch(gbranch(a, types[tptr]), to); - regfree(&n4); - regfree(&n3); + igen(nl, &n1, N); + n1.xoffset += Array_array; + n1.type = types[tptr]; + gencmp0(&n1, types[tptr], a, likely, to); regfree(&n1); break; -#endif } if(isinter(nl->type)) { @@ -1032,38 +1253,16 @@ bgen(Node *n, int true, Prog *to) break; } - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = 0; - gencmp0(&n2, types[tptr], a, to); - regfree(&n1); - break; - -#ifdef NOTDEF - a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - regalloc(&n3, types[tptr], N); - regalloc(&n4, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = 0; - gmove(&n2, &n4); - nodconst(&tmp, types[tptr], 0); - gmove(&tmp, &n3); - gcmp(optoas(OCMP, types[tptr]), &n4, &n3); - patch(gbranch(a, types[tptr]), to); + igen(nl, &n1, N); + n1.type = types[tptr]; + n1.xoffset += 0; + gencmp0(&n1, types[tptr], a, likely, to); regfree(&n1); - regfree(&n3); - regfree(&n4); break; -#endif } if(iscomplex[nl->type->etype]) { - complexbool(a, nl, nr, true, to); + complexbool(a, nl, nr, true, likely, to); break; } @@ -1078,17 +1277,17 @@ bgen(Node *n, int true, Prog *to) cgen(nr, &n2); nr = &n2; } - cmp64(nl, nr, a, to); + cmp64(nl, nr, a, likely, to); break; } if(nr->op == OLITERAL) { if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) == 0) { - gencmp0(nl, nl->type, a, to); + gencmp0(nl, nl->type, a, likely, to); break; } if(nr->val.ctype == CTNIL) { - gencmp0(nl, nl->type, a, to); + gencmp0(nl, nl->type, a, likely, to); break; } } @@ -1110,7 +1309,7 @@ bgen(Node *n, int true, Prog *to) cgen(&tmp, &n1); gcmp(optoas(OCMP, nr->type), &n1, &n2); - patch(gbranch(a, nr->type), to); + patch(gbranch(a, nr->type, likely), to); regfree(&n1); regfree(&n2); @@ -1131,14 +1330,17 @@ bgen(Node *n, int true, Prog *to) gcmp(optoas(OCMP, nr->type), &n1, &n2); if(isfloat[nl->type->etype]) { - p1 = gbranch(ABVS, nr->type); - patch(gbranch(a, nr->type), to); - if(n->op == ONE) + if(n->op == ONE) { + p1 = gbranch(ABVS, nr->type, likely); + patch(gbranch(a, nr->type, likely), to); patch(p1, to); - else + } else { + p1 = gbranch(ABVS, nr->type, -likely); + patch(gbranch(a, nr->type, likely), to); patch(p1, pc); + } } else { - patch(gbranch(a, nr->type), to); + patch(gbranch(a, nr->type, likely), to); } regfree(&n1); regfree(&n2); @@ -1241,6 +1443,10 @@ sgen(Node *n, Node *res, int64 w) return; } + // Avoid taking the address for simple enough types. + if(componentgen(n, res)) + return; + // determine alignment. // want to avoid unaligned access, so have to use // smaller operations for less aligned types. @@ -1284,16 +1490,14 @@ sgen(Node *n, Node *res, int64 w) if(osrc < odst && odst < osrc+w) dir = -dir; - regalloc(&dst, types[tptr], res); if(n->ullman >= res->ullman) { - agen(n, &dst); // temporarily use dst + agenr(n, &dst, res); // temporarily use dst regalloc(&src, types[tptr], N); gins(AMOVW, &dst, &src); agen(res, &dst); } else { - agen(res, &dst); - regalloc(&src, types[tptr], N); - agen(n, &src); + agenr(res, &dst, res); + agenr(n, &src, N); } regalloc(&tmp, types[TUINT32], N); @@ -1338,7 +1542,7 @@ sgen(Node *n, Node *res, int64 w) p = gins(ACMP, &src, N); raddr(&nend, p); - patch(gbranch(ABNE, T), ploop); + patch(gbranch(ABNE, T, 0), ploop); regfree(&nend); } else { while(c-- > 0) { @@ -1358,3 +1562,157 @@ sgen(Node *n, Node *res, int64 w) regfree(&src); regfree(&tmp); } + +static int +cadable(Node *n) +{ + if(!n->addable) { + // dont know how it happens, + // but it does + return 0; + } + + switch(n->op) { + case ONAME: + return 1; + } + return 0; +} + +/* + * copy a composite value by moving its individual components. + * Slices, strings and interfaces are supported. + * nr is N when assigning a zero value. + * return 1 if can do, 0 if cant. + */ +int +componentgen(Node *nr, Node *nl) +{ + Node nodl, nodr, tmp; + int freel, freer; + + freel = 0; + freer = 0; + + switch(nl->type->etype) { + default: + goto no; + + case TARRAY: + if(!isslice(nl->type)) + goto no; + case TSTRING: + case TINTER: + break; + } + + nodl = *nl; + if(!cadable(nl)) { + if(nr == N || !cadable(nr)) + goto no; + igen(nl, &nodl, N); + freel = 1; + } + + if(nr != N) { + nodr = *nr; + if(!cadable(nr)) { + igen(nr, &nodr, N); + freer = 1; + } + } else { + // When zeroing, prepare a register containing zero. + nodconst(&tmp, nl->type, 0); + regalloc(&nodr, types[TUINT], N); + gmove(&tmp, &nodr); + freer = 1; + } + + switch(nl->type->etype) { + case TARRAY: + nodl.xoffset += Array_array; + nodl.type = ptrto(nl->type->type); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + nodl.xoffset += Array_cap-Array_nel; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_cap-Array_nel; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + goto yes; + + case TSTRING: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + goto yes; + + case TINTER: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + goto yes; + } + +no: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 0; + +yes: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 1; +} diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c index 1235d1ace..ef11e2adb 100644 --- a/src/cmd/5g/cgen64.c +++ b/src/cmd/5g/cgen64.c @@ -94,6 +94,7 @@ cgen64(Node *n, Node *res) case OAND: case OOR: case OXOR: + case OLROT: // binary operators. // common setup below. break; @@ -175,7 +176,7 @@ cgen64(Node *n, Node *res) p1->from.type = D_REG; p1->from.reg = bl.val.u.reg; p1->reg = ch.val.u.reg; - p1->to.type = D_REGREG; + p1->to.type = D_REGREG2; p1->to.reg = ah.val.u.reg; p1->to.offset = ah.val.u.reg; //print("%P\n", p1); @@ -185,7 +186,7 @@ cgen64(Node *n, Node *res) p1->from.type = D_REG; p1->from.reg = bh.val.u.reg; p1->reg = cl.val.u.reg; - p1->to.type = D_REGREG; + p1->to.type = D_REGREG2; p1->to.reg = ah.val.u.reg; p1->to.offset = ah.val.u.reg; //print("%P\n", p1); @@ -197,6 +198,47 @@ cgen64(Node *n, Node *res) break; + case OLROT: + // We only rotate by a constant c in [0,64). + // if c >= 32: + // lo, hi = hi, lo + // c -= 32 + // if c == 0: + // no-op + // else: + // t = hi + // shld hi:lo, c + // shld lo:t, c + v = mpgetfix(r->val.u.xval); + regalloc(&bl, lo1.type, N); + regalloc(&bh, hi1.type, N); + if(v >= 32) { + // reverse during load to do the first 32 bits of rotate + v -= 32; + gins(AMOVW, &hi1, &bl); + gins(AMOVW, &lo1, &bh); + } else { + gins(AMOVW, &hi1, &bh); + gins(AMOVW, &lo1, &bl); + } + if(v == 0) { + gins(AMOVW, &bh, &ah); + gins(AMOVW, &bl, &al); + } else { + // rotate by 1 <= v <= 31 + // MOVW bl<>(32-v), ah + // OR bh>>(32-v), al + gshift(AMOVW, &bl, SHIFT_LL, v, &al); + gshift(AMOVW, &bh, SHIFT_LL, v, &ah); + gshift(AORR, &bl, SHIFT_LR, 32-v, &ah); + gshift(AORR, &bh, SHIFT_LR, 32-v, &al); + } + regfree(&bl); + regfree(&bh); + break; + case OLSH: regalloc(&bl, lo1.type, N); regalloc(&bh, hi1.type, N); @@ -243,7 +285,7 @@ cgen64(Node *n, Node *res) split64(r, &cl, &ch); gmove(&ch, &s); gins(ATST, &s, N); - p6 = gbranch(ABNE, T); + p6 = gbranch(ABNE, T, 0); gmove(&cl, &s); splitclean(); } else { @@ -257,7 +299,7 @@ cgen64(Node *n, Node *res) p1->scond = C_SCOND_EQ; p1 = gins(AMOVW, &bh, &ah); p1->scond = C_SCOND_EQ; - p2 = gbranch(ABEQ, T); + p2 = gbranch(ABEQ, T, 0); // shift is < 32 nodconst(&n1, types[TUINT32], 32); @@ -281,14 +323,14 @@ cgen64(Node *n, Node *res) p1->scond = C_SCOND_LO; // BLO end - p3 = gbranch(ABLO, T); + p3 = gbranch(ABLO, T, 0); // shift == 32 p1 = gins(AEOR, &al, &al); p1->scond = C_SCOND_EQ; p1 = gins(AMOVW, &bl, &ah); p1->scond = C_SCOND_EQ; - p4 = gbranch(ABEQ, T); + p4 = gbranch(ABEQ, T, 0); // shift is < 64 nodconst(&n1, types[TUINT32], 64); @@ -311,7 +353,7 @@ cgen64(Node *n, Node *res) p1 = gregshift(AMOVW, &bl, SHIFT_LL, &s, &ah); p1->scond = C_SCOND_LO; - p5 = gbranch(ABLO, T); + p5 = gbranch(ABLO, T, 0); // shift >= 64 if (p6 != P) patch(p6, pc); @@ -406,7 +448,7 @@ olsh_break: else p1 = gins(AEOR, &ah, &ah); p1->scond = C_SCOND_NE; - p6 = gbranch(ABNE, T); + p6 = gbranch(ABNE, T, 0); gmove(&cl, &s); splitclean(); } else { @@ -420,7 +462,7 @@ olsh_break: p1->scond = C_SCOND_EQ; p1 = gins(AMOVW, &bh, &ah); p1->scond = C_SCOND_EQ; - p2 = gbranch(ABEQ, T); + p2 = gbranch(ABEQ, T, 0); // check if shift is < 32 nodconst(&n1, types[TUINT32], 32); @@ -449,7 +491,7 @@ olsh_break: p1->scond = C_SCOND_LO; // BLO end - p3 = gbranch(ABLO, T); + p3 = gbranch(ABLO, T, 0); // shift == 32 p1 = gins(AMOVW, &bh, &al); @@ -458,7 +500,7 @@ olsh_break: gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); else gins(AEOR, &ah, &ah); - p4 = gbranch(ABEQ, T); + p4 = gbranch(ABEQ, T, 0); // check if shift is < 64 nodconst(&n1, types[TUINT32], 64); @@ -484,7 +526,7 @@ olsh_break: } // BLO end - p5 = gbranch(ABLO, T); + p5 = gbranch(ABLO, T, 0); // s >= 64 if(p6 != P) @@ -633,7 +675,7 @@ orsh_break: * nl is memory; nr is constant or memory. */ void -cmp64(Node *nl, Node *nr, int op, Prog *to) +cmp64(Node *nl, Node *nr, int op, int likely, Prog *to) { Node lo1, hi1, lo2, hi2, r1, r2; Prog *br; @@ -663,14 +705,14 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // beq to // L: - br = gbranch(ABNE, T); + br = gbranch(ABNE, T, -likely); break; case ONE: // cmp hi // bne to // cmp lo // bne to - patch(gbranch(ABNE, T), to); + patch(gbranch(ABNE, T, likely), to); break; case OGE: case OGT: @@ -680,8 +722,8 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // bge to (or bgt to) // L: - patch(gbranch(optoas(OGT, t), T), to); - br = gbranch(optoas(OLT, t), T); + patch(gbranch(optoas(OGT, t), T, likely), to); + br = gbranch(optoas(OLT, t), T, -likely); break; case OLE: case OLT: @@ -691,8 +733,8 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // ble to (or jlt to) // L: - patch(gbranch(optoas(OLT, t), T), to); - br = gbranch(optoas(OGT, t), T); + patch(gbranch(optoas(OLT, t), T, likely), to); + br = gbranch(optoas(OGT, t), T, -likely); break; } @@ -707,7 +749,7 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) regfree(&r2); // jump again - patch(gbranch(optoas(op, t), T), to); + patch(gbranch(optoas(op, t), T, likely), to); // point first branch down here if appropriate if(br != P) diff --git a/src/cmd/5g/doc.go b/src/cmd/5g/doc.go index 5a4a772fb..aebdcab71 100644 --- a/src/cmd/5g/doc.go +++ b/src/cmd/5g/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 5g is the version of the gc compiler for the ARM. @@ -10,4 +12,4 @@ The $GOARCH for these tools is arm. It reads .go files and outputs .5 files. The flags are documented in ../gc/doc.go. */ -package documentation +package main diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c index 070804217..1fbf633f9 100644 --- a/src/cmd/5g/galign.c +++ b/src/cmd/5g/galign.c @@ -27,6 +27,7 @@ void betypeinit(void) { widthptr = 4; + widthint = 4; zprog.link = P; zprog.as = AGOK; diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index 7dbf3beec..45a9a887e 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -15,32 +15,36 @@ struct Addr { int32 offset; int32 offset2; - double dval; - Prog* branch; - char sval[NSNAME]; + + union { + double dval; + vlong vval; + Prog* branch; + char sval[NSNAME]; + } u; Sym* sym; + Sym* gotype; Node* node; int width; uchar type; char name; uchar reg; - char pun; uchar etype; }; #define A ((Addr*)0) struct Prog { - short as; // opcode uint32 loc; // pc offset in this func uint32 lineno; // source line that generated this - Addr from; // src address - Addr to; // dst address Prog* link; // next instruction in this func void* regp; // points to enclosing Reg struct + short as; // opcode uchar reg; // doubles as width in DATA op uchar scond; + Addr from; // src address + Addr to; // dst address }; #define TEXTFLAG reg @@ -78,22 +82,20 @@ void cgen_callinter(Node*, Node*, int); void cgen_proc(Node*, int); void cgen_callret(Node*, Node*); void cgen_dcl(Node*); -int cgen_inline(Node*, Node*); int needconvert(Type*, Type*); void genconv(Type*, Type*); void allocparams(void); -void checklabels(); +void checklabels(void); void ginscall(Node*, int); /* * cgen */ void agen(Node*, Node*); -Prog* cgenindex(Node *, Node *); +Prog* cgenindex(Node *, Node *, int); void igen(Node*, Node*, Node*); void agenr(Node *n, Node *a, Node *res); vlong fieldoffset(Type*, Node*); -void bgen(Node*, int, Prog*); void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); @@ -104,12 +106,14 @@ Prog* gshift(int as, Node *lhs, int32 stype, int32 sval, Node *rhs); Prog * gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs); void naddr(Node*, Addr*, int); void cgen_aret(Node*, Node*); -void cgen_shift(int, Node*, Node*, Node*); +void cgen_hmul(Node*, Node*, Node*); +void cgen_shift(int, int, Node*, Node*, Node*); +int componentgen(Node*, Node*); /* * cgen64.c */ -void cmp64(Node*, Node*, int, Prog*); +void cmp64(Node*, Node*, int, int, Prog*); void cgen64(Node*, Node*); /* @@ -117,16 +121,13 @@ void cgen64(Node*, Node*); */ void clearp(Prog*); void proglist(void); -Prog* gbranch(int, Type*); +Prog* gbranch(int, Type*, int); Prog* prog(int); -void gaddoffset(Node*); void gconv(int, int); int conv2pt(Type*); vlong convvtox(vlong, int); void fnparam(Type*, int, int); Prog* gop(int, Node*, Node*, Node*); -void setconst(Addr*, vlong); -void setaddr(Addr*, Node*); int optoas(int, Type*); void ginit(void); void gclean(void); @@ -141,11 +142,12 @@ int isfat(Type*); int dotaddable(Node*, Node*); void sudoclean(void); int sudoaddable(int, Node*, Addr*, int*); -void afunclit(Addr*); +void afunclit(Addr*, Node*); void datagostring(Strlit*, Addr*); void split64(Node*, Node*, Node*); void splitclean(void); Node* ncon(uint32 i); +void gtrack(Sym*); /* * obj.c @@ -164,7 +166,7 @@ int Rconv(Fmt*); int Yconv(Fmt*); void listinit(void); -void zaddr(Biobuf*, Addr*, int); +void zaddr(Biobuf*, Addr*, int, int); #pragma varargck type "D" Addr* #pragma varargck type "M" Addr* diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index de100620b..de1671bb6 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -28,6 +28,9 @@ void markautoused(Prog* p) { for (; p; p = p->link) { + if (p->as == ATYPE) + continue; + if (p->from.name == D_AUTO && p->from.node) p->from.node->used = 1; @@ -40,27 +43,38 @@ markautoused(Prog* p) void fixautoused(Prog* p) { - for (; p; p = p->link) { + Prog **lp; + + for (lp=&p; (p=*lp) != P; ) { + if (p->as == ATYPE && p->from.node && p->from.name == D_AUTO && !p->from.node->used) { + *lp = p->link; + continue; + } + if (p->from.name == D_AUTO && p->from.node) p->from.offset += p->from.node->stkdelta; if (p->to.name == D_AUTO && p->to.node) p->to.offset += p->to.node->stkdelta; + + lp = &p->link; } } /* * generate: * call f + * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack + * proc=3 normal call to C pointer (not Go func value) */ void ginscall(Node *f, int proc) { Prog *p; - Node n1, r, con; + Node n1, r, r1, con; switch(proc) { default: @@ -68,8 +82,26 @@ ginscall(Node *f, int proc) break; case 0: // normal call - p = gins(ABL, N, f); - afunclit(&p->to); + case -1: // normal call but no return + if(f->op == ONAME && f->class == PFUNC) { + p = gins(ABL, N, f); + afunclit(&p->to, f); + if(proc == -1 || noreturn(p)) + gins(AUNDEF, N, N); + break; + } + nodreg(&r, types[tptr], 7); + nodreg(&r1, types[tptr], 1); + gmove(f, &r); + r.op = OINDREG; + gmove(&r, &r1); + r.op = OREGISTER; + r1.op = OINDREG; + gins(ABL, &r, &r1); + break; + + case 3: // normal call of c function pointer + gins(ABL, N, f); break; case 1: // call in new proc (go) @@ -120,7 +152,7 @@ ginscall(Node *f, int proc) nodconst(&con, types[TINT32], 0); p = gins(ACMP, &con, N); p->reg = 0; - patch(gbranch(ABNE, T), retpc); + patch(gbranch(ABNE, T, -1), retpc); } break; } @@ -136,6 +168,7 @@ cgen_callinter(Node *n, Node *res, int proc) int r; Node *i, *f; Node tmpi, nodo, nodr, nodsp; + Prog *p; i = n->left; if(i->op != ODOTINTER) @@ -180,7 +213,17 @@ cgen_callinter(Node *n, Node *res, int proc) cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + + if(proc == 0) { + // plain call: use direct c function pointer - more efficient + cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + nodr.op = OINDREG; + proc = 3; + } else { + // go/defer. generate go func value. + p = gins(AMOVW, &nodo, &nodr); + p->from.type = D_CONST; // REG = &(20+offset(REG)) -- i.tab->fun[f] + } // BOTCH nodr.type = fntype; nodr.type = n->left->type; @@ -368,14 +411,19 @@ cgen_asop(Node *n) case OOR: a = optoas(n->etype, nl->type); if(nl->addable) { - regalloc(&n3, nr->type, N); - cgen(nr, &n3); + if(smallintconst(nr)) + n3 = *nr; + else { + regalloc(&n3, nr->type, N); + cgen(nr, &n3); + } regalloc(&n2, nl->type, N); cgen(nl, &n2); gins(a, &n3, &n2); cgen(&n2, nl); regfree(&n2); - regfree(&n3); + if(n3.op != OLITERAL) + regfree(&n3); goto ret; } if(nr->ullman < UINF) @@ -399,7 +447,9 @@ cgen_asop(Node *n) hard: n2.op = 0; n1.op = 0; - if(nr->ullman >= nl->ullman || nl->addable) { + if(nr->op == OLITERAL) { + // don't allocate a register for literals. + } else if(nr->ullman >= nl->ullman || nl->addable) { regalloc(&n2, nr->type, N); cgen(nr, &n2); nr = &n2; @@ -463,25 +513,100 @@ samereg(Node *a, Node *b) return 1; } +/* + * generate high multiply + * res = (nl * nr) >> wordsize + */ +void +cgen_hmul(Node *nl, Node *nr, Node *res) +{ + int w; + Node n1, n2, *tmp; + Type *t; + Prog *p; + + if(nl->ullman < nr->ullman) { + tmp = nl; + nl = nr; + nr = tmp; + } + t = nl->type; + w = t->width * 8; + regalloc(&n1, t, res); + cgen(nl, &n1); + regalloc(&n2, t, N); + cgen(nr, &n2); + switch(simtype[t->etype]) { + case TINT8: + case TINT16: + gins(optoas(OMUL, t), &n2, &n1); + gshift(AMOVW, &n1, SHIFT_AR, w, &n1); + break; + case TUINT8: + case TUINT16: + gins(optoas(OMUL, t), &n2, &n1); + gshift(AMOVW, &n1, SHIFT_LR, w, &n1); + break; + case TINT32: + case TUINT32: + // perform a long multiplication. + if(issigned[t->etype]) + p = gins(AMULL, &n2, N); + else + p = gins(AMULLU, &n2, N); + // n2 * n1 -> (n1 n2) + p->reg = n1.val.u.reg; + p->to.type = D_REGREG; + p->to.reg = n1.val.u.reg; + p->to.offset = n2.val.u.reg; + break; + default: + fatal("cgen_hmul %T", t); + break; + } + cgen(&n1, res); + regfree(&n1); + regfree(&n2); +} + /* * generate shift according to op, one of: * res = nl << nr * res = nl >> nr */ void -cgen_shift(int op, Node *nl, Node *nr, Node *res) +cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) { Node n1, n2, n3, nt, t, lo, hi; - int w; + int w, v; Prog *p1, *p2, *p3; Type *tr; uvlong sc; + USED(bounded); if(nl->type->width > 4) fatal("cgen_shift %T", nl->type); w = nl->type->width * 8; + if(op == OLROT) { + v = mpgetfix(nr->val.u.xval); + regalloc(&n1, nl->type, res); + if(w == 32) { + cgen(nl, &n1); + gshift(AMOVW, &n1, SHIFT_RR, w-v, &n1); + } else { + regalloc(&n2, nl->type, N); + cgen(nl, &n2); + gshift(AMOVW, &n2, SHIFT_LL, v, &n1); + gshift(AORR, &n2, SHIFT_LR, w-v, &n1); + regfree(&n2); + } + gmove(&n1, res); + regfree(&n1); + return; + } + if(nr->op == OLITERAL) { regalloc(&n1, nl->type, res); cgen(nl, &n1); @@ -524,6 +649,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) regalloc(&n3, types[TUINT32], N); gmove(&lo, &n1); gmove(&hi, &n3); + splitclean(); gins(ATST, &n3, N); nodconst(&t, types[TUINT32], w); p1 = gins(AMOVW, &t, &n1); @@ -546,9 +672,10 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) // test for shift being 0 gins(ATST, &n1, N); - p3 = gbranch(ABEQ, T); + p3 = gbranch(ABEQ, T, -1); // test and fix up large shifts + // TODO: if(!bounded), don't emit some of this. regalloc(&n3, tr, N); nodconst(&t, types[TUINT32], w); gmove(&t, &n3); @@ -589,7 +716,12 @@ clearfat(Node *nl) if(debug['g']) dump("\nclearfat", nl); + w = nl->type->width; + // Avoid taking the address for simple enough types. + if(componentgen(N, nl)) + return; + c = w % 4; // bytes q = w / 4; // quads @@ -613,7 +745,7 @@ clearfat(Node *nl) p = gins(ACMP, &dst, N); raddr(&end, p); - patch(gbranch(ABNE, T), pl); + patch(gbranch(ABNE, T, 0), pl); regfree(&end); } else @@ -637,395 +769,3 @@ clearfat(Node *nl) regfree(&dst); regfree(&nz); } - -static int -regcmp(const void *va, const void *vb) -{ - Node *ra, *rb; - - ra = (Node*)va; - rb = (Node*)vb; - return ra->local - rb->local; -} - -static Prog* throwpc; - -// We're only going to bother inlining if we can -// convert all the arguments to 32 bits safely. Can we? -static int -fix64(NodeList *nn, int n) -{ - NodeList *l; - Node *r; - int i; - - l = nn; - for(i=0; in->right; - if(is64(r->type) && !smallintconst(r)) { - if(r->op == OCONV) - r = r->left; - if(is64(r->type)) - return 0; - } - l = l->next; - } - return 1; -} - -void -getargs(NodeList *nn, Node *reg, int n) -{ - NodeList *l; - int i; - - throwpc = nil; - - l = nn; - for(i=0; in->right) && !isslice(l->n->right->type)) { - regalloc(reg+i, l->n->right->type, N); - cgen(l->n->right, reg+i); - } else - reg[i] = *l->n->right; - if(reg[i].local != 0) - yyerror("local used"); - reg[i].local = l->n->left->xoffset; - l = l->next; - } - qsort((void*)reg, n, sizeof(*reg), regcmp); - for(i=0; ival.u.xval); - if(cl == 0) - return; - if(smallintconst(nr)) - return; - - // put the constant on the right - op = brrev(op); - c = nl; - nl = nr; - nr = c; - } - - n1.op = OXXX; - if(nr->op != OREGISTER) { - regalloc(&n1, types[TUINT32], N); - gmove(nr, &n1); - nr = &n1; - } - n2.op = OXXX; - if(nl->op != OREGISTER) { - regalloc(&n2, types[TUINT32], N); - gmove(nl, &n2); - nl = &n2; - } - gcmp(optoas(OCMP, types[TUINT32]), nl, nr); - if(nr == &n1) - regfree(&n1); - if(nl == &n2) - regfree(&n2); - if(throwpc == nil) { - p1 = gbranch(optoas(op, types[TUINT32]), T); - throwpc = pc; - ginscall(panicslice, 0); - patch(p1, pc); - } else { - op = brcom(op); - p1 = gbranch(optoas(op, types[TUINT32]), T); - patch(p1, throwpc); - } -} - -int -sleasy(Node *n) -{ - if(n->op != ONAME) - return 0; - if(!n->addable) - return 0; - return 1; -} - -// generate inline code for -// slicearray -// sliceslice -// arraytoslice -int -cgen_inline(Node *n, Node *res) -{ - Node nodes[5]; - Node n1, n2, n3, nres, ntemp; - vlong v; - int i, narg; - - if(n->op != OCALLFUNC) - goto no; - if(!n->left->addable) - goto no; - if(n->left->sym == S) - goto no; - if(n->left->sym->pkg != runtimepkg) - goto no; - if(strcmp(n->left->sym->name, "slicearray") == 0) - goto slicearray; - if(strcmp(n->left->sym->name, "sliceslice") == 0) { - narg = 4; - goto sliceslice; - } - if(strcmp(n->left->sym->name, "sliceslice1") == 0) { - narg = 3; - goto sliceslice; - } - goto no; - -slicearray: - if(!sleasy(res)) - goto no; - if(!fix64(n->list, 5)) - goto no; - getargs(n->list, nodes, 5); - - // if(hb[3] > nel[1]) goto throw - cmpandthrow(&nodes[3], &nodes[1]); - - // if(lb[2] > hb[3]) goto throw - cmpandthrow(&nodes[2], &nodes[3]); - - // len = hb[3] - lb[2] (destroys hb) - n2 = *res; - n2.type = types[TUINT32]; - n2.xoffset += Array_nel; - - if(smallintconst(&nodes[3]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[3].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gmove(&n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[3]); - gmove(&nodes[3], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gmove(&n1, &n2); - regfree(&n1); - } - - // cap = nel[1] - lb[2] (destroys nel) - n2 = *res; - n2.type = types[TUINT32]; - n2.xoffset += Array_cap; - - if(smallintconst(&nodes[1]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[1].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gmove(&n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[1]); - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gmove(&n1, &n2); - regfree(&n1); - } - - // if slice could be too big, dereference to - // catch nil array pointer. - if(nodes[0].op == OREGISTER && nodes[0].type->type->width >= unmappedzero) { - n2 = nodes[0]; - n2.xoffset = 0; - n2.op = OINDREG; - n2.type = types[TUINT8]; - regalloc(&n1, types[TUINT32], N); - gins(AMOVB, &n2, &n1); - regfree(&n1); - } - - // ary = old[0] + (lb[2] * width[4]) (destroys old) - n2 = *res; - n2.type = types[tptr]; - n2.xoffset += Array_array; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { - v = mpgetfix(nodes[2].val.u.xval) * - mpgetfix(nodes[4].val.u.xval); - if(v != 0) { - nodconst(&n1, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - } - } else { - regalloc(&n1, types[tptr], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[4]) || mpgetfix(nodes[4].val.u.xval) != 1) { - regalloc(&n3, types[tptr], N); - gmove(&nodes[4], &n3); - gins(optoas(OMUL, types[tptr]), &n3, &n1); - regfree(&n3); - } - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - regfree(&n1); - } - gmove(&nodes[0], &n2); - - for(i=0; i<5; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - return 1; - -sliceslice: - if(!fix64(n->list, narg)) - goto no; - ntemp.op = OXXX; - if(!sleasy(n->list->n->right)) { - Node *n0; - - n0 = n->list->n->right; - tempname(&ntemp, res->type); - cgen(n0, &ntemp); - n->list->n->right = &ntemp; - getargs(n->list, nodes, narg); - n->list->n->right = n0; - } else - getargs(n->list, nodes, narg); - - nres = *res; // result - if(!sleasy(res)) { - if(ntemp.op == OXXX) - tempname(&ntemp, res->type); - nres = ntemp; - } - - if(narg == 3) { // old[lb:] - // move width to where it would be for old[lb:hb] - nodes[3] = nodes[2]; - nodes[2].op = OXXX; - - // if(lb[1] > old.nel[0]) goto throw; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - cmpandthrow(&nodes[1], &n2); - - // ret.nel = old.nel[0]-lb[1]; - n2 = nodes[0]; - n2.type = types[TUINT32]; - n2.xoffset += Array_nel; - - regalloc(&n1, types[TUINT32], N); - gmove(&n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.type = types[TUINT32]; - n2.xoffset += Array_nel; - gmove(&n1, &n2); - regfree(&n1); - } else { // old[lb:hb] - // if(hb[2] > old.cap[0]) goto throw; - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - cmpandthrow(&nodes[2], &n2); - - // if(lb[1] > hb[2]) goto throw; - cmpandthrow(&nodes[1], &nodes[2]); - - // ret.len = hb[2]-lb[1]; (destroys hb[2]) - n2 = nres; - n2.type = types[TUINT32]; - n2.xoffset += Array_nel; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[1])) { - v = mpgetfix(nodes[2].val.u.xval) - - mpgetfix(nodes[1].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gmove(&n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - gmove(&n1, &n2); - regfree(&n1); - } - } - - // ret.cap = old.cap[0]-lb[1]; (uses hb[2]) - n2 = nodes[0]; - n2.type = types[TUINT32]; - n2.xoffset += Array_cap; - - regalloc(&n1, types[TUINT32], &nodes[2]); - gmove(&n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.type = types[TUINT32]; - n2.xoffset += Array_cap; - gmove(&n1, &n2); - regfree(&n1); - - // ret.array = old.array[0]+lb[1]*width[3]; (uses lb[1]) - n2 = nodes[0]; - n2.type = types[tptr]; - n2.xoffset += Array_array; - regalloc(&n3, types[tptr], N); - gmove(&n2, &n3); - - regalloc(&n1, types[tptr], &nodes[1]); - if(smallintconst(&nodes[1]) && smallintconst(&nodes[3])) { - gmove(&n2, &n1); - v = mpgetfix(nodes[1].val.u.xval) * - mpgetfix(nodes[3].val.u.xval); - if(v != 0) { - nodconst(&n2, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n3, &n1); - } - } else { - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) { - regalloc(&n2, types[tptr], N); - gmove(&nodes[3], &n2); - gins(optoas(OMUL, types[tptr]), &n2, &n1); - regfree(&n2); - } - gins(optoas(OADD, types[tptr]), &n3, &n1); - } - regfree(&n3); - - n2 = nres; - n2.type = types[tptr]; - n2.xoffset += Array_array; - gmove(&n1, &n2); - regfree(&n1); - - for(i=0; i<4; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - - if(!sleasy(res)) { - cgen(&nres, res); - } - return 1; - -no: - return 0; -} diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c index 2763e7b16..9c5fb2a96 100644 --- a/src/cmd/5g/gobj.c +++ b/src/cmd/5g/gobj.c @@ -65,17 +65,17 @@ zhist(Biobuf *b, int line, vlong offset) Bputc(b, line>>8); Bputc(b, line>>16); Bputc(b, line>>24); - zaddr(b, &zprog.from, 0); + zaddr(b, &zprog.from, 0, 0); a = zprog.to; if(offset != 0) { a.offset = offset; a.type = D_CONST; } - zaddr(b, &a, 0); + zaddr(b, &a, 0, 0); } void -zaddr(Biobuf *b, Addr *a, int s) +zaddr(Biobuf *b, Addr *a, int s, int gotype) { int32 l; uint64 e; @@ -95,6 +95,7 @@ zaddr(Biobuf *b, Addr *a, int s) Bputc(b, a->reg); Bputc(b, s); Bputc(b, a->name); + Bputc(b, gotype); } switch(a->type) { @@ -128,9 +129,9 @@ zaddr(Biobuf *b, Addr *a, int s) break; case D_BRANCH: - if(a->branch == nil) + if(a->u.branch == nil) fatal("unpatched branch"); - a->offset = a->branch->loc; + a->offset = a->u.branch->loc; l = a->offset; Bputc(b, l); Bputc(b, l>>8); @@ -139,7 +140,7 @@ zaddr(Biobuf *b, Addr *a, int s) break; case D_SCONST: - n = a->sval; + n = a->u.sval; for(i=0; ioffset); break; case D_FCONST: - ieeedtod(&e, a->dval); + ieeedtod(&e, a->u.dval); l = e; Bputc(b, l); Bputc(b, l>>8); @@ -166,20 +168,66 @@ zaddr(Biobuf *b, Addr *a, int s) } } +static struct { + struct { Sym *sym; short type; } h[NSYM]; + int sym; +} z; + +static void +zsymreset(void) +{ + for(z.sym=0; z.symsym; + if(i < 0 || i >= NSYM) + i = 0; + if(z.h[i].type == t && z.h[i].sym == s) + return i; + i = z.sym; + s->sym = i; + zname(bout, s, t); + z.h[i].sym = s; + z.h[i].type = t; + if(++z.sym >= NSYM) + z.sym = 1; + *new = 1; + return i; +} + +static int +zsymaddr(Addr *a, int *new) +{ + int t; + + t = a->name; + if(t == D_ADDR) + t = a->name; + return zsym(a->sym, t, new); +} + void dumpfuncs(void) { Plist *pl; - int sf, st, t, sym; - struct { Sym *sym; short type; } h[NSYM]; + int sf, st, gf, gt, new; Sym *s; Prog *p; - for(sym=0; symfirstpc; p!=P; p=p->link) { - jackpot: - sf = 0; - s = p->from.sym; - while(s != S) { - sf = s->sym; - if(sf < 0 || sf >= NSYM) - sf = 0; - t = p->from.name; - if(t == D_ADDR) - t = p->from.name; - if(h[sf].type == t) - if(h[sf].sym == s) - break; - s->sym = sym; - zname(bout, s, t); - h[sym].sym = s; - h[sym].type = t; - sf = sym; - sym++; - if(sym >= NSYM) - sym = 1; - break; - } - st = 0; - s = p->to.sym; - while(s != S) { - st = s->sym; - if(st < 0 || st >= NSYM) - st = 0; - t = p->to.name; - if(t == D_ADDR) - t = p->to.name; - if(h[st].type == t) - if(h[st].sym == s) - break; - s->sym = sym; - zname(bout, s, t); - h[sym].sym = s; - h[sym].type = t; - st = sym; - sym++; - if(sym >= NSYM) - sym = 1; - if(st == sf) - goto jackpot; + for(;;) { + sf = zsymaddr(&p->from, &new); + gf = zsym(p->from.gotype, D_EXTERN, &new); + if(new && sf == gf) + continue; + st = zsymaddr(&p->to, &new); + if(new && (st == sf || st == gf)) + continue; + gt = zsym(p->to.gotype, D_EXTERN, &new); + if(new && (gt == sf || gt == gf || gt == st)) + continue; break; } + Bputc(bout, p->as); Bputc(bout, p->scond); Bputc(bout, p->reg); @@ -263,8 +278,8 @@ dumpfuncs(void) Bputc(bout, p->lineno>>8); Bputc(bout, p->lineno>>16); Bputc(bout, p->lineno>>24); - zaddr(bout, &p->from, sf); - zaddr(bout, &p->to, st); + zaddr(bout, &p->from, sf, gf); + zaddr(bout, &p->to, st, gt); } } } @@ -288,7 +303,7 @@ dsname(Sym *sym, int off, char *t, int n) p->to.name = D_NONE; p->to.reg = NREG; p->to.offset = 0; - memmove(p->to.sval, t, n); + memmove(p->to.u.sval, t, n); return off + n; } @@ -372,13 +387,13 @@ gdatacomplex(Node *nam, Mpcplx *cval) p = gins(ADATA, nam, N); p->reg = w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->real); + p->to.u.dval = mpgetflt(&cval->real); p = gins(ADATA, nam, N); p->reg = w; p->from.offset += w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->imag); + p->to.u.dval = mpgetflt(&cval->imag); } void diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 9acf93670..191c755b8 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -108,17 +108,22 @@ dumpdata(void) /* * generate a branch. * t is ignored. + * likely values are for branch prediction: + * -1 unlikely + * 0 no opinion + * +1 likely */ Prog* -gbranch(int as, Type *t) +gbranch(int as, Type *t, int likely) { Prog *p; USED(t); + USED(likely); // TODO: record this for linker p = prog(as); p->to.type = D_BRANCH; - p->to.branch = P; + p->to.u.branch = P; return p; } @@ -130,7 +135,7 @@ patch(Prog *p, Prog *to) { if(p->to.type != D_BRANCH) fatal("patch: not a branch"); - p->to.branch = to; + p->to.u.branch = to; p->to.offset = to->loc; } @@ -141,8 +146,8 @@ unpatch(Prog *p) if(p->to.type != D_BRANCH) fatal("unpatch: not a branch"); - q = p->to.branch; - p->to.branch = P; + q = p->to.u.branch; + p->to.u.branch = P; p->to.offset = 0; return q; } @@ -169,64 +174,6 @@ newplist(void) return pl; } -void -clearstk(void) -{ - Plist *pl; - Prog *p, *p1, *p2, *p3; - Node dst, end, zero, con; - - if(plast->firstpc->to.offset <= 0) - return; - - // reestablish context for inserting code - // at beginning of function. - pl = plast; - p1 = pl->firstpc; - p2 = p1->link; - pc = mal(sizeof(*pc)); - clearp(pc); - p1->link = pc; - - // zero stack frame - - // MOVW $4(SP), R1 - nodreg(&dst, types[tptr], 1); - p = gins(AMOVW, N, &dst); - p->from.type = D_CONST; - p->from.reg = REGSP; - p->from.offset = 4; - - // MOVW $n(R1), R2 - nodreg(&end, types[tptr], 2); - p = gins(AMOVW, N, &end); - p->from.type = D_CONST; - p->from.reg = 1; - p->from.offset = p1->to.offset; - - // MOVW $0, R3 - nodreg(&zero, types[TUINT32], 3); - nodconst(&con, types[TUINT32], 0); - gmove(&con, &zero); - - // L: - // MOVW.P R3, 0(R1) +4 - // CMP R1, R2 - // BNE L - p = gins(AMOVW, &zero, &dst); - p->to.type = D_OREG; - p->to.offset = 4; - p->scond |= C_PBIT; - p3 = p; - p = gins(ACMP, &dst, N); - raddr(&end, p); - patch(gbranch(ABNE, T), p3); - - // continue with original code. - gins(ANOP, N, N)->link = p2; - pc = P; -} - void gused(Node *n) { @@ -238,22 +185,23 @@ gjmp(Prog *to) { Prog *p; - p = gbranch(AB, T); + p = gbranch(AB, T, 0); if(to != P) patch(p, to); return p; } void -ggloblnod(Node *nam, int32 width) +ggloblnod(Node *nam) { Prog *p; p = gins(AGLOBL, nam, N); p->lineno = nam->lineno; + p->from.gotype = ngotype(nam); p->to.sym = S; p->to.type = D_CONST; - p->to.offset = width; + p->to.offset = nam->type->width; if(nam->readonly) p->reg = RODATA; if(nam->type != T && !haspointers(nam->type)) @@ -261,7 +209,7 @@ ggloblnod(Node *nam, int32 width) } void -ggloblsym(Sym *s, int32 width, int dupok) +ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -273,8 +221,20 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.name = D_NONE; p->to.offset = width; if(dupok) - p->reg = DUPOK; - p->reg |= RODATA; + p->reg |= DUPOK; + if(rodata) + p->reg |= RODATA; +} + +void +gtrack(Sym *s) +{ + Prog *p; + + p = gins(AUSEFIELD, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = s; } int @@ -298,17 +258,20 @@ isfat(Type *t) * also fix up direct register references to be D_OREG. */ void -afunclit(Addr *a) +afunclit(Addr *a, Node *n) { if(a->type == D_CONST && a->name == D_EXTERN || a->type == D_REG) { a->type = D_OREG; + if(n->op == ONAME) + a->sym = n->sym; } } static int resvd[] = { - 9, // reserved for m - 10, // reserved for g + 9, // reserved for m + 10, // reserved for g + REGSP, // reserved for SP }; void @@ -401,6 +364,7 @@ regalloc(Node *n, Type *t, Node *o) regpc[i] = (uintptr)getcallerpc(&n); goto out; } + print("registers allocated at\n"); for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) print("%d %p\n", i, regpc[i]); yyerror("out of fixed registers"); @@ -452,15 +416,17 @@ regfree(Node *n) print("regalloc fix %d float %d\n", fixfree, floatfree); } - if(n->op == ONAME && iscomplex[n->type->etype]) + if(n->op == ONAME) return; if(n->op != OREGISTER && n->op != OINDREG) fatal("regfree: not a register"); i = n->val.u.reg; + if(i == REGSP) + return; if(i < 0 || i >= nelem(reg) || i >= nelem(regpc)) fatal("regfree: reg out of range"); if(reg[i] <= 0) - fatal("regfree: reg not allocated"); + fatal("regfree: reg %R not allocated", i); reg[i]--; if(reg[i] == 0) regpc[i] = 0; @@ -597,9 +563,9 @@ split64(Node *n, Node *lo, Node *hi) if(!is64(n->type)) fatal("split64 %T", n->type); - sclean[nsclean].op = OEMPTY; if(nsclean >= nelem(sclean)) fatal("split64 clean"); + sclean[nsclean].op = OEMPTY; nsclean++; switch(n->op) { default: @@ -1155,7 +1121,7 @@ gcmp(int as, Node *lhs, Node *rhs) { Prog *p; - if(lhs->op != OREGISTER || rhs->op != OREGISTER) + if(lhs->op != OREGISTER) fatal("bad operands to gcmp: %O %O", lhs->op, rhs->op); p = gins(as, rhs, N); @@ -1194,6 +1160,27 @@ gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs) return p; } +// Generate an instruction referencing *n +// to force segv on nil pointer dereference. +void +checkref(Node *n) +{ + Node m1, m2; + + if(n->type->type->width < unmappedzero) + return; + + regalloc(&m1, types[TUINTPTR], n); + regalloc(&m2, types[TUINT8], n); + cgen(n, &m1); + m1.xoffset = 0; + m1.op = OINDREG; + m1.type = types[TUINT8]; + gins(AMOVBU, &m1, &m2); + regfree(&m2); + regfree(&m1); +} + static void checkoffset(Addr *a, int canemitcode) { @@ -1209,7 +1196,7 @@ checkoffset(Addr *a, int canemitcode) // reference with large offset. instead, emit explicit // test of 0(reg). regalloc(&n1, types[TUINTPTR], N); - p = gins(AMOVW, N, &n1); + p = gins(AMOVB, N, &n1); p->from = *a; p->from.offset = 0; regfree(&n1); @@ -1222,14 +1209,22 @@ checkoffset(Addr *a, int canemitcode) void naddr(Node *n, Addr *a, int canemitcode) { + Prog *p; + a->type = D_NONE; a->name = D_NONE; a->reg = NREG; + a->gotype = S; a->node = N; a->etype = 0; if(n == N) return; + if(n->type != T && n->type->etype != TIDEAL) { + dowidth(n->type); + a->width = n->type->width; + } + switch(n->op) { default: fatal("naddr: bad %O %D", n->op, a); @@ -1286,6 +1281,25 @@ naddr(Node *n, Addr *a, int canemitcode) a->name = D_PARAM; a->node = n->left->orig; break; + + case OCLOSUREVAR: + if(!canemitcode) + fatal("naddr OCLOSUREVAR cannot emit code"); + p = gins(AMOVW, N, N); + p->from.type = D_OREG; + p->from.reg = 7; + p->from.offset = n->xoffset; + p->to.type = D_REG; + p->to.reg = 1; + a->type = D_REG; + a->reg = 1; + a->sym = S; + break; + + case OCFUNC: + naddr(n->left, a, canemitcode); + a->sym = n->left->sym; + break; case ONAME: a->etype = 0; @@ -1326,6 +1340,7 @@ naddr(Node *n, Addr *a, int canemitcode) case PFUNC: a->name = D_EXTERN; a->type = D_CONST; + a->sym = funcsym(a->sym); break; } break; @@ -1337,7 +1352,7 @@ naddr(Node *n, Addr *a, int canemitcode) break; case CTFLT: a->type = D_FCONST; - a->dval = mpgetflt(n->val.u.fval); + a->u.dval = mpgetflt(n->val.u.fval); break; case CTINT: case CTRUNE: @@ -1409,6 +1424,9 @@ naddr(Node *n, Addr *a, int canemitcode) fatal("naddr: OADDR %d\n", a->type); } } + + if(a->width < 0) + fatal("naddr: bad width for %N -> %D", n, a); } /* @@ -1619,6 +1637,16 @@ optoas(int op, Type *t) a = ASUBD; break; + case CASE(OMINUS, TINT8): + case CASE(OMINUS, TUINT8): + case CASE(OMINUS, TINT16): + case CASE(OMINUS, TUINT16): + case CASE(OMINUS, TINT32): + case CASE(OMINUS, TUINT32): + case CASE(OMINUS, TPTR32): + a = ARSB; + break; + case CASE(OAND, TINT8): case CASE(OAND, TUINT8): case CASE(OAND, TINT16): @@ -1825,6 +1853,9 @@ sudoaddable(int as, Node *n, Addr *a, int *w) goto odot; case OINDEX: + return 0; + // disabled: OINDEX case is now covered by agenr + // for a more suitable register allocation pattern. if(n->left->type->etype == TSTRING) return 0; cleani += 2; @@ -1942,7 +1973,7 @@ oindex: t = types[TINT32]; regalloc(reg1, t, N); regalloc(&n3, types[TINT32], reg1); - p2 = cgenindex(r, &n3); + p2 = cgenindex(r, &n3, debug['B'] || n->bounded); gmove(&n3, reg1); regfree(&n3); @@ -1982,7 +2013,7 @@ oindex: cgen(&n2, &n3); gcmp(optoas(OCMP, types[TUINT32]), reg1, &n3); regfree(&n3); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); if(p2) patch(p2, pc); ginscall(panicindex, 0); @@ -2032,7 +2063,7 @@ oindex_const: v = mpgetfix(r->val.u.xval); if(o & ODynam) { - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { n1 = *reg; n1.op = OINDREG; n1.type = types[tptr]; @@ -2045,7 +2076,7 @@ oindex_const: gcmp(optoas(OCMP, types[TUINT32]), &n4, &n3); regfree(&n4); regfree(&n3); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); + p1 = gbranch(optoas(OGT, types[TUINT32]), T, +1); ginscall(panicindex, 0); patch(p1, pc); } diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c index f0da24742..6c3f1d744 100644 --- a/src/cmd/5g/list.c +++ b/src/cmd/5g/list.c @@ -153,6 +153,12 @@ Dconv(Fmt *fp) sprint(str, "%M(R%d)(REG)", a, a->reg); break; + case D_REGREG2: + sprint(str, "R%d,R%d", a->reg, (int)a->offset); + if(a->name != D_NONE || a->sym != S) + sprint(str, "%M(R%d)(REG)", a, a->reg); + break; + case D_FREG: sprint(str, "F%d", a->reg); if(a->name != D_NONE || a->sym != S) @@ -160,24 +166,24 @@ Dconv(Fmt *fp) break; case D_BRANCH: - if(a->branch == P || a->branch->loc == 0) { + if(a->u.branch == P || a->u.branch->loc == 0) { if(a->sym != S) sprint(str, "%s+%d(APC)", a->sym->name, a->offset); else sprint(str, "%d(APC)", a->offset); } else if(a->sym != S) - sprint(str, "%s+%d(APC)", a->sym->name, a->branch->loc); + sprint(str, "%s+%d(APC)", a->sym->name, a->u.branch->loc); else - sprint(str, "%d(APC)", a->branch->loc); + sprint(str, "%d(APC)", a->u.branch->loc); break; case D_FCONST: - snprint(str, sizeof(str), "$(%.17e)", a->dval); + snprint(str, sizeof(str), "$(%.17e)", a->u.dval); break; case D_SCONST: - snprint(str, sizeof(str), "$\"%Y\"", a->sval); + snprint(str, sizeof(str), "$\"%Y\"", a->u.sval); break; // TODO(kaib): Add back @@ -190,7 +196,10 @@ Dconv(Fmt *fp) // goto conv; } conv: - return fmtstrcpy(fp, str); + fmtstrcpy(fp, str); + if(a->gotype) + fmtprint(fp, "{%s}", a->gotype->name); + return 0; } int diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h index 7a0070fc9..af7d654de 100644 --- a/src/cmd/5g/opt.h +++ b/src/cmd/5g/opt.h @@ -34,8 +34,6 @@ #define D_HI D_NONE #define D_LO D_NONE -#define isregtype(t) ((t)>= D_AX && (t)<=D_R15) - #define BLOAD(r) band(bnot(r->refbehind), r->refahead) #define BSTORE(r) band(bnot(r->calbehind), r->calahead) #define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z]) @@ -49,12 +47,16 @@ typedef struct Reg Reg; typedef struct Rgn Rgn; +// A Reg is a wrapper around a single Prog (one instruction) that holds +// register optimization information while the optimizer runs. +// r->prog is the instruction. +// r->prog->regp points back to r. struct Reg { - Bits set; - Bits use1; - Bits use2; + Bits set; // variables written by this instruction. + Bits use1; // variables read by prog->from. + Bits use2; // variables read by prog->to. Bits refbehind; Bits refahead; @@ -70,13 +72,13 @@ struct Reg uint16 loop; // x5 for every loop uchar refset; // diagnostic generated - Reg* p1; - Reg* p2; + Reg* p1; // predecessors of this instruction: p1, + Reg* p2; // and then p2 linked though p2link. Reg* p2link; - Reg* s1; + Reg* s1; // successors of this instruction (at most two: s1 and s2). Reg* s2; - Reg* link; - Prog* prog; + Reg* link; // next instruction in function code + Prog* prog; // actual instruction }; #define R ((Reg*)0) diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c index e87f5d697..b6202a882 100644 --- a/src/cmd/5g/peep.c +++ b/src/cmd/5g/peep.c @@ -1,5 +1,5 @@ // Inferno utils/5c/peep.c -// http://code.google.com/p/inferno-os/source/browse/utils/5g/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/peep.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) @@ -49,7 +49,6 @@ peep(void) int t; p1 = nil; - USED(p1); // ... in unreachable code... /* * complete R structure */ @@ -77,6 +76,8 @@ peep(void) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: p = p->link; } } @@ -120,7 +121,7 @@ loop1: } break; -#ifdef NOTDEF +#ifdef NOTDEF if(p->scond == C_SCOND_NONE) if(regtyp(&p->to)) if(isdconst(&p->from)) { @@ -133,26 +134,24 @@ loop1: if(t) goto loop1; -return; -#ifdef NOTDEF for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { -// case AEOR: -// /* -// * EOR -1,x,y => MVN x,y -// */ -// if(isdconst(&p->from) && p->from.offset == -1) { -// p->as = AMVN; -// p->from.type = D_REG; -// if(p->reg != NREG) -// p->from.reg = p->reg; -// else -// p->from.reg = p->to.reg; -// p->reg = NREG; -// } -// break; + case AEOR: + /* + * EOR -1,x,y => MVN x,y + */ + if(isdconst(&p->from) && p->from.offset == -1) { + p->as = AMVN; + p->from.type = D_REG; + if(p->reg != NREG) + p->from.reg = p->reg; + else + p->from.reg = p->to.reg; + p->reg = NREG; + } + break; case AMOVH: case AMOVHU: @@ -161,6 +160,7 @@ return; /* * look for MOVB x,R; MOVB R,R */ + r1 = r->link; if(p->to.type != D_REG) break; if(r1 == R) @@ -175,23 +175,22 @@ return; excise(r1); break; } - r1 = r->link; } -// for(r=firstr; r!=R; r=r->link) { -// p = r->prog; -// switch(p->as) { -// case AMOVW: -// case AMOVB: -// case AMOVBU: -// if(p->from.type == D_OREG && p->from.offset == 0) -// xtramodes(r, &p->from); -// else -// if(p->to.type == D_OREG && p->to.offset == 0) -// xtramodes(r, &p->to); -// else -// continue; -// break; + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case AMOVW: + case AMOVB: + case AMOVBU: + if(p->from.type == D_OREG && p->from.offset == 0) + xtramodes(r, &p->from); + else + if(p->to.type == D_OREG && p->to.offset == 0) + xtramodes(r, &p->to); + else + continue; + break; // case ACMP: // /* // * elide CMP $0,x if calculation of x can set condition codes @@ -259,13 +258,17 @@ return; // r2->prog->as = t; // excise(r); // continue; -// } -// } + } + } - predicate(); -#endif +// predicate(); } +/* + * uniqp returns a "unique" predecessor to instruction r. + * If the instruction is the first one or has multiple + * predecessors due to jump, R is returned. + */ Reg* uniqp(Reg *r) { @@ -738,6 +741,11 @@ shiftprop(Reg *r) return 1; } +/* + * findpre returns the last instruction mentioning v + * before r. It must be a set, and there must be + * a unique path from that instruction to r. + */ Reg* findpre(Reg *r, Adr *v) { @@ -758,6 +766,10 @@ findpre(Reg *r, Adr *v) return R; } +/* + * findinc finds ADD instructions with a constant + * argument which falls within the immed_12 range. + */ Reg* findinc(Reg *r, Reg *r2, Adr *v) { @@ -848,6 +860,19 @@ finduse(Reg *r, Adr *v) return findu1(r, v); } +/* + * xtramodes enables the ARM post increment and + * shift offset addressing modes to transform + * MOVW 0(R3),R1 + * ADD $4,R3,R3 + * into + * MOVW.P 4(R3),R1 + * and + * ADD R0,R1 + * MOVBU 0(R1),R0 + * into + * MOVBU R0<<0(R1),R0 + */ int xtramodes(Reg *r, Adr *a) { @@ -856,8 +881,6 @@ xtramodes(Reg *r, Adr *a) Adr v; p = r->prog; - if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */ - return 0; v = *a; v.type = D_REG; r1 = findpre(r, &v); @@ -866,6 +889,9 @@ xtramodes(Reg *r, Adr *a) if(p1->to.type == D_REG && p1->to.reg == v.reg) switch(p1->as) { case AADD: + if(p1->scond & C_SBIT) + // avoid altering ADD.S/ADC sequences. + break; if(p1->from.type == D_REG || (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 && (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) || @@ -1032,6 +1058,7 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; case AMULLU: /* read, read, write, write */ + case AMULL: case AMULA: case AMVN: return 2; @@ -1135,12 +1162,9 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; case ARET: /* funny */ - if(v->type == D_REG) - if(v->reg == REGRET) - return 2; - if(v->type == D_FREG) - if(v->reg == FREGRET) - return 2; + if(s != A) + return 1; + return 3; case ABL: /* funny */ if(v->type == D_REG) { @@ -1152,6 +1176,8 @@ copyu(Prog *p, Adr *v, Adr *s) if(v->type == D_FREG) if(v->reg <= FREGEXT && v->reg > exfregoffset) return 2; + if(p->from.type == D_REG && v->type == D_REG && p->from.reg == v->reg) + return 2; if(s != A) { if(copysub(&p->to, v, s, 1)) @@ -1167,6 +1193,9 @@ copyu(Prog *p, Adr *v, Adr *s) if(v->reg == (uchar)REGARG) return 3; return 0; + + case ALOCALS: /* funny */ + return 0; } } @@ -1213,7 +1242,7 @@ copyau(Adr *a, Adr *v) if(a->reg == v->reg) return 1; } else - if(a->type == D_REGREG) { + if(a->type == D_REGREG || a->type == D_REGREG2) { if(a->reg == v->reg) return 1; if(a->offset == v->reg) @@ -1276,7 +1305,7 @@ copysub(Adr *a, Adr *v, Adr *s, int f) if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) a->offset = (a->offset&~(0xf<<8))|(s->reg<<8); } else - if(a->type == D_REGREG) { + if(a->type == D_REGREG || a->type == D_REGREG2) { if(a->offset == v->reg) a->offset = s->reg; if(a->reg == v->reg) diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 393266973..eaaaf9be3 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -1,5 +1,5 @@ // Inferno utils/5c/reg.c -// http://code.google.com/p/inferno-os/source/browse/utils/5g/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/reg.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) @@ -34,8 +34,8 @@ #include "gg.h" #include "opt.h" -#define NREGVAR 24 -#define REGBITS ((uint32)0xffffff) +#define NREGVAR 32 +#define REGBITS ((uint32)0xffffffff) #define P2R(p) (Reg*)(p->reg) void addsplits(void); @@ -95,7 +95,7 @@ setoutvar(void) ovar.b[z] |= bit.b[z]; t = structnext(&save); } -//if(bany(ovar)) +//if(bany(&ovar)) //print("ovar = %Q\n", ovar); } @@ -160,8 +160,18 @@ static char* regname[] = { ".F5", ".F6", ".F7", + ".F8", + ".F9", + ".F10", + ".F11", + ".F12", + ".F13", + ".F14", + ".F15", }; +static Node* regnodes[NREGVAR]; + void regopt(Prog *firstp) { @@ -197,7 +207,6 @@ regopt(Prog *firstp) return; } - r1 = R; firstr = R; lastr = R; @@ -208,8 +217,11 @@ regopt(Prog *firstp) */ nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; ias == ABL && p->to.type == D_EXTERN) + continue; + /* * left side always read */ @@ -408,10 +426,14 @@ regopt(Prog *firstp) addrs.b[z] |= bit.b[z]; } -// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n", -// i, v->addr, v->etype, v->width, v->sym, v->offset); + if(debug['R'] && debug['v']) + print("bit=%2d addr=%d et=%-6E w=%-2d s=%N + %lld\n", + i, v->addr, v->etype, v->width, v->node, v->offset); } + if(debug['R'] && debug['v']) + dumpit("pass1", firstr); + /* * pass 2 * turn branch references to pointers @@ -420,9 +442,9 @@ regopt(Prog *firstp) for(r=firstr; r!=R; r=r->link) { p = r->prog; if(p->to.type == D_BRANCH) { - if(p->to.branch == P) + if(p->to.u.branch == P) fatal("pnil %P", p); - r1 = p->to.branch->regp; + r1 = p->to.u.branch->regp; if(r1 == R) fatal("rnil %P", p); if(r1 == r) { @@ -440,6 +462,9 @@ regopt(Prog *firstp) print(" addr = %Q\n", addrs); } + if(debug['R'] && debug['v']) + dumpit("pass2", firstr); + /* * pass 2.5 * find looping structure @@ -449,6 +474,9 @@ regopt(Prog *firstp) change = 0; loopit(firstr, nr); + if(debug['R'] && debug['v']) + dumpit("pass2.5", firstr); + /* * pass 3 * iterate propagating usage @@ -476,6 +504,9 @@ loop11: if(change) goto loop1; + if(debug['R'] && debug['v']) + dumpit("pass3", firstr); + /* * pass 4 @@ -492,6 +523,9 @@ loop2: addsplits(); + if(debug['R'] && debug['v']) + dumpit("pass4", firstr); + if(debug['R'] > 1) { print("\nprop structure:\n"); for(r = firstr; r != R; r = r->link) { @@ -543,6 +577,9 @@ loop2: r->act.b[0] &= ~REGBITS; } + if(debug['R'] && debug['v']) + dumpit("pass4.5", firstr); + /* * pass 5 * isolate regions @@ -605,6 +642,9 @@ loop2: brk: qsort(region, nregion, sizeof(region[0]), rcmp); + if(debug['R'] && debug['v']) + dumpit("pass5", firstr); + /* * pass 6 * determine used registers (paint2) @@ -633,6 +673,10 @@ brk: paint3(rgp->enter, rgp->varno, vreg, rgp->regno); rgp++; } + + if(debug['R'] && debug['v']) + dumpit("pass6", firstr); + /* * pass 7 * peep-hole on basic block @@ -641,6 +685,9 @@ brk: peep(); } + if(debug['R'] && debug['v']) + dumpit("pass7", firstr); + /* * last pass * eliminate nops @@ -662,8 +709,8 @@ brk: while(p->link != P && p->link->as == ANOP) p->link = p->link->link; if(p->to.type == D_BRANCH) - while(p->to.branch != P && p->to.branch->as == ANOP) - p->to.branch = p->to.branch->link; + while(p->to.u.branch != P && p->to.u.branch->as == ANOP) + p->to.u.branch = p->to.u.branch->link; if(p->as == AMOVW && p->to.reg == 13) { if(p->scond & C_WBIT) { vreg = -p->to.offset; // in adjust region @@ -866,6 +913,7 @@ mkvar(Reg *r, Adr *a) goto onereg; case D_REGREG: + case D_REGREG2: bit = zbits; if(a->offset != NREG) bit.b[0] |= RtoB(a->offset); @@ -926,6 +974,8 @@ mkvar(Reg *r, Adr *a) et = a->etype; o = a->offset; w = a->width; + if(w < 0) + fatal("bad width %d for %D", w, a); for(i=0; ioffset = o; v->name = n; -// v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); + print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); bit = blsh(i); if(n == D_EXTERN || n == D_STATIC) @@ -1033,8 +1080,12 @@ prop(Reg *r, Bits ref, Bits cal) default: // Work around for issue 1304: // flush modified globals before each instruction. - for(z=0; z= REGTMP-2) // excluded R9 and R10 for m and g + if(r >= REGTMP-2 && r != 12) // excluded R9 and R10 for m and g, but not R12 return 0; return 1L << r; } @@ -1498,7 +1550,7 @@ RtoB(int r) int BtoR(int32 b) { - b &= 0x01fcL; // excluded R9 and R10 for m and g + b &= 0x11fcL; // excluded R9 and R10 for m and g, but not R12 if(b == 0) return 0; return bitno(b); @@ -1509,7 +1561,7 @@ BtoR(int32 b) * 18 F2 * 19 F3 * ... ... - * 23 F7 + * 31 F15 */ int32 FtoB(int f) @@ -1524,7 +1576,7 @@ int BtoF(int32 b) { - b &= 0xfc0000L; + b &= 0xfffc0000L; if(b == 0) return 0; return bitno(b) - 16; @@ -1643,7 +1695,7 @@ chasejmp(Prog *p, int *jmploop) *jmploop = 1; break; } - p = p->to.branch; + p = p->to.u.branch; } return p; } @@ -1665,8 +1717,8 @@ mark(Prog *firstp) if(p->regp != dead) break; p->regp = alive; - if(p->as != ABL && p->to.type == D_BRANCH && p->to.branch) - mark(p->to.branch); + if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch) + mark(p->to.u.branch); if(p->as == AB || p->as == ARET || (p->as == ABL && noreturn(p))) break; } @@ -1686,8 +1738,8 @@ fixjmp(Prog *firstp) for(p=firstp; p; p=p->link) { if(debug['R'] && debug['v']) print("%P\n", p); - if(p->as != ABL && p->to.type == D_BRANCH && p->to.branch && p->to.branch->as == AB) { - p->to.branch = chasejmp(p->to.branch, &jmploop); + if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AB) { + p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); if(debug['R'] && debug['v']) print("->%P\n", p); } @@ -1695,7 +1747,7 @@ fixjmp(Prog *firstp) } if(debug['R'] && debug['v']) print("\n"); - + // pass 2: mark all reachable code alive mark(firstp); @@ -1723,7 +1775,7 @@ fixjmp(Prog *firstp) if(!jmploop) { last = nil; for(p=firstp; p; p=p->link) { - if(p->as == AB && p->to.type == D_BRANCH && p->to.branch == p->link) { + if(p->as == AB && p->to.type == D_BRANCH && p->to.u.branch == p->link) { if(debug['R'] && debug['v']) print("del %P\n", p); continue; diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index 569536ebd..4aef8a27f 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -49,12 +49,11 @@ #define REGM (REGEXT-1) /* compiler allocates external registers R10 down */ #define REGTMP 11 -#define REGSB 12 #define REGSP 13 #define REGLINK 14 #define REGPC 15 -#define NFREG 8 +#define NFREG 16 #define FREGRET 0 #define FREGEXT 7 #define FREGTMP 15 @@ -127,6 +126,8 @@ enum as ADIVD, ASQRTF, ASQRTD, + AABSF, + AABSD, ASRL, ASRA, @@ -184,6 +185,21 @@ enum as ALDREXD, ASTREXD, + APLD, + + AUNDEF, + + ACLZ, + + AMULWT, + AMULWB, + AMULAWT, + AMULAWB, + + AUSEFIELD, + ALOCALS, + ATYPE, + ALAST, }; @@ -237,12 +253,14 @@ enum as #define D_SHIFT (D_NONE+19) #define D_FPCR (D_NONE+20) -#define D_REGREG (D_NONE+21) +#define D_REGREG (D_NONE+21) // (reg, reg) #define D_ADDR (D_NONE+22) #define D_SBIG (D_NONE+23) #define D_CONST2 (D_NONE+24) +#define D_REGREG2 (D_NONE+25) // reg, reg + /* name */ #define D_EXTERN (D_NONE+3) #define D_STATIC (D_NONE+4) @@ -252,11 +270,16 @@ enum as /* internal only */ #define D_SIZE (D_NONE+40) #define D_PCREL (D_NONE+41) +#define D_GOTOFF (D_NONE+42) // R_ARM_GOTOFF +#define D_PLT0 (D_NONE+43) // R_ARM_PLT32, 1st inst: add ip, pc, #0xNN00000 +#define D_PLT1 (D_NONE+44) // R_ARM_PLT32, 2nd inst: add ip, ip, #0xNN000 +#define D_PLT2 (D_NONE+45) // R_ARM_PLT32, 3rd inst: ldr pc, [ip, #0xNNN]! +#define D_CALL (D_NONE+46) // R_ARM_PLT32/R_ARM_CALL/R_ARM_JUMP24, bl xxxxx or b yyyyy /* * this is the ranlib header */ -#define SYMDEF "__.SYMDEF" +#define SYMDEF "__.GOSYMDEF" /* * this is the simulated IEEE floating point diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index b36a982d1..04f2a9c6c 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -33,10 +33,14 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" static Prog *PP; -char linuxdynld[] = "/lib/ld-linux.so.2"; +char linuxdynld[] = "/lib/ld-linux.so.3"; // 2 for OABI, 3 for EABI +char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; +char openbsddynld[] = "XXX"; +char netbsddynld[] = "/libexec/ld.elf_so"; int32 entryvalue(void) @@ -55,32 +59,6 @@ entryvalue(void) return s->value; } -enum { - ElfStrEmpty, - ElfStrInterp, - ElfStrHash, - ElfStrGot, - ElfStrGotPlt, - ElfStrDynamic, - ElfStrDynsym, - ElfStrDynstr, - ElfStrRel, - ElfStrText, - ElfStrData, - ElfStrBss, - ElfStrSymtab, - ElfStrStrtab, - ElfStrShstrtab, - ElfStrRelPlt, - ElfStrPlt, - ElfStrNoteNetbsdIdent, - ElfStrNoPtrData, - ElfStrNoPtrBss, - NElfStr -}; - -vlong elfstr[NElfStr]; - static int needlib(char *name) { @@ -103,164 +81,437 @@ needlib(char *name) int nelfsym = 1; +static void addpltsym(Sym*); +static void addgotsym(Sym*); +static void addgotsyminternal(Sym*); + +// Preserve highest 8 bits of a, and do addition to lower 24-bit +// of a and b; used to adjust ARM branch intruction's target +static int32 +braddoff(int32 a, int32 b) +{ + return (((uint32)a) & 0xff000000U) | (0x00ffffffU & (uint32)(a + b)); +} + +Sym * +lookuprel(void) +{ + return lookup(".rel", 0); +} + void -adddynrel(Sym *s, Reloc *r) +adddynrela(Sym *rel, Sym *s, Reloc *r) { - USED(s); - USED(r); - diag("adddynrel: unsupported binary format"); + addaddrplus(rel, s, r->off); + adduint32(rel, R_ARM_RELATIVE); } void -adddynsym(Sym *s) +adddynrel(Sym *s, Reloc *r) { - USED(s); - diag("adddynsym: not implemented"); + Sym *targ, *rel; + + targ = r->sym; + cursym = s; + + switch(r->type) { + default: + if(r->type >= 256) { + diag("unexpected relocation type %d", r->type); + return; + } + break; + + // Handle relocations found in ELF object files. + case 256 + R_ARM_PLT32: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + return; + + case 256 + R_ARM_THM_PC22: // R_ARM_THM_CALL + diag("R_ARM_THM_CALL, are you using -marm?"); + errorexit(); + return; + + case 256 + R_ARM_GOT32: // R_ARM_GOT_BREL + if(targ->dynimpname == nil || targ->dynexport) { + addgotsyminternal(targ); + } else { + addgotsym(targ); + } + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + r->add += targ->got; + return; + + case 256 + R_ARM_GOT_PREL: // GOT(S) + A - P + if(targ->dynimpname == nil || targ->dynexport) { + addgotsyminternal(targ); + } else { + addgotsym(targ); + } + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += targ->got + 4; + return; + + case 256 + R_ARM_GOTOFF: // R_ARM_GOTOFF32 + r->type = D_GOTOFF; + return; + + case 256 + R_ARM_GOTPC: // R_ARM_BASE_PREL + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += 4; + return; + + case 256 + R_ARM_CALL: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + return; + + case 256 + R_ARM_REL32: // R_ARM_REL32 + r->type = D_PCREL; + r->add += 4; + return; + + case 256 + R_ARM_ABS32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ->name); + r->type = D_ADDR; + return; + + case 256 + R_ARM_V4BX: + // we can just ignore this, because we are targeting ARM V5+ anyway + if(r->sym) { + // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it + r->sym->type = 0; + } + r->sym = S; + return; + + case 256 + R_ARM_PC24: + case 256 + R_ARM_JUMP24: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + return; + } + + // Handle references to ELF symbols from our own object files. + if(targ->dynimpname == nil || targ->dynexport) + return; + + switch(r->type) { + case D_PCREL: + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + return; + + case D_ADDR: + if(s->type != SDATA) + break; + if(iself) { + adddynsym(targ); + rel = lookup(".rel", 0); + addaddrplus(rel, s, r->off); + adduint32(rel, ELF32_R_INFO(targ->dynid, R_ARM_GLOB_DAT)); // we need a S + A dynmic reloc + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + return; + } + break; + } + + cursym = s; + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } -static void +int +elfreloc1(Reloc *r, vlong off, int32 elfsym, vlong add) +{ + USED(add); // written to obj file by ../ld/data.c's reloc + + LPUT(off); + + switch(r->type) { + default: + return -1; + + case D_ADDR: + if(r->siz == 4) + LPUT(R_ARM_ABS32 | elfsym<<8); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + LPUT(R_ARM_REL32 | elfsym<<8); + else + return -1; + break; + } + + return 0; +} + +void elfsetupplt(void) { - // TODO + Sym *plt, *got; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + if(plt->size == 0) { + // str lr, [sp, #-4]! + adduint32(plt, 0xe52de004); + // ldr lr, [pc, #4] + adduint32(plt, 0xe59fe004); + // add lr, pc, lr + adduint32(plt, 0xe08fe00e); + // ldr pc, [lr, #8]! + adduint32(plt, 0xe5bef008); + // .word &GLOBAL_OFFSET_TABLE[0] - . + addpcrelplus(plt, got, 4); + + // the first .plt entry requires 3 .plt.got entries + adduint32(got, 0); + adduint32(got, 0); + adduint32(got, 0); + } } int archreloc(Reloc *r, Sym *s, vlong *val) { - USED(r); - USED(s); - USED(val); - return -1; + switch(r->type) { + case D_CONST: + *val = r->add; + return 0; + case D_GOTOFF: + *val = symaddr(r->sym) + r->add - symaddr(lookup(".got", 0)); + return 0; + // The following three arch specific relocations are only for generation of + // Linux/ARM ELF's PLT entry (3 assembler instruction) + case D_PLT0: // add ip, pc, #0xXX00000 + if (symaddr(lookup(".got.plt", 0)) < symaddr(lookup(".plt", 0))) + diag(".got.plt should be placed after .plt section."); + *val = 0xe28fc600U + + (0xff & ((uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add) >> 20)); + return 0; + case D_PLT1: // add ip, ip, #0xYY000 + *val = 0xe28cca00U + + (0xff & ((uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add + 4) >> 12)); + return 0; + case D_PLT2: // ldr pc, [ip, #0xZZZ]! + *val = 0xe5bcf000U + + (0xfff & (uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add + 8)); + return 0; + case D_CALL: // bl XXXXXX or b YYYYYY + *val = braddoff((0xff000000U & (uint32)r->add), + (0xffffff & (uint32) + ((symaddr(r->sym) + ((uint32)r->add) * 4 - (s->value + r->off)) / 4))); + return 0; +} +return -1; } -void -adddynlib(char *lib) +static Reloc * +addpltreloc(Sym *plt, Sym *got, Sym *sym, int typ) { - Sym *s; +Reloc *r; + r = addrel(plt); + r->sym = got; + r->off = plt->size; + r->siz = 4; + r->type = typ; + r->add = sym->got - 8; + + plt->reachable = 1; + plt->size += 4; + symgrow(plt, plt->size); + + return r; +} + +static void +addpltsym(Sym *s) +{ + Sym *plt, *got, *rel; - if(!needlib(lib)) + if(s->plt >= 0) return; + + adddynsym(s); if(iself) { - s = lookup(".dynstr", 0); - if(s->size == 0) - addstring(s, ""); - elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + rel = lookup(".rel.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // .got entry + s->got = got->size; + // In theory, all GOT should point to the first PLT entry, + // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's + // dynamic linker won't, so we'd better do it ourselves. + addaddrplus(got, plt, 0); + + // .plt entry, this depends on the .got entry + s->plt = plt->size; + addpltreloc(plt, got, s, D_PLT0); // add lr, pc, #0xXX00000 + addpltreloc(plt, got, s, D_PLT1); // add lr, lr, #0xYY000 + addpltreloc(plt, got, s, D_PLT2); // ldr pc, [lr, #0xZZZ]! + + // rel + addaddrplus(rel, got, s->got); + adduint32(rel, ELF32_R_INFO(s->dynid, R_ARM_JUMP_SLOT)); } else { - diag("adddynlib: unsupported binary format"); + diag("addpltsym: unsupported binary format"); + } +} + +static void +addgotsyminternal(Sym *s) +{ + Sym *got; + + if(s->got >= 0) + return; + + got = lookup(".got", 0); + s->got = got->size; + + addaddrplus(got, s, 0); + + if(iself) { + ; + } else { + diag("addgotsyminternal: unsupported binary format"); + } +} + +static void +addgotsym(Sym *s) +{ + Sym *got, *rel; + + if(s->got >= 0) + return; + + adddynsym(s); + got = lookup(".got", 0); + s->got = got->size; + adduint32(got, 0); + + if(iself) { + rel = lookup(".rel", 0); + addaddrplus(rel, got, s->got); + adduint32(rel, ELF32_R_INFO(s->dynid, R_ARM_GLOB_DAT)); + } else { + diag("addgotsym: unsupported binary format"); } } void -doelf(void) +adddynsym(Sym *s) { - Sym *s, *shstrtab, *dynstr; + Sym *d; + int t; + char *name; - if(!iself) + if(s->dynid >= 0) return; - /* predefine strings we need for section headers */ - shstrtab = lookup(".shstrtab", 0); - shstrtab->type = SELFROSECT; - shstrtab->reachable = 1; - - elfstr[ElfStrEmpty] = addstring(shstrtab, ""); - elfstr[ElfStrText] = addstring(shstrtab, ".text"); - elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); - elfstr[ElfStrData] = addstring(shstrtab, ".data"); - elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); - elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss"); - if(HEADTYPE == Hnetbsd) - elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident"); - addstring(shstrtab, ".rodata"); - addstring(shstrtab, ".gosymtab"); - addstring(shstrtab, ".gopclntab"); - if(!debug['s']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); + if(s->dynimpname == nil) { + s->dynimpname = s->name; + //diag("adddynsym: no dynamic name for %s", s->name); } - elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); - - if(!debug['d']) { /* -d suppresses dynamic loader format */ - elfstr[ElfStrInterp] = addstring(shstrtab, ".interp"); - elfstr[ElfStrHash] = addstring(shstrtab, ".hash"); - elfstr[ElfStrGot] = addstring(shstrtab, ".got"); - elfstr[ElfStrGotPlt] = addstring(shstrtab, ".got.plt"); - elfstr[ElfStrDynamic] = addstring(shstrtab, ".dynamic"); - elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); - elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); - elfstr[ElfStrRel] = addstring(shstrtab, ".rel"); - elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); - elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); - - /* dynamic symbol table - first entry all zeros */ - s = lookup(".dynsym", 0); - s->type = SELFROSECT; - s->reachable = 1; - s->value += ELF32SYMSIZE; - - /* dynamic string table */ - s = lookup(".dynstr", 0); - s->type = SELFROSECT; - s->reachable = 1; - if(s->size == 0) - addstring(s, ""); - dynstr = s; - /* relocation table */ - s = lookup(".rel", 0); - s->reachable = 1; - s->type = SELFROSECT; + if(iself) { + s->dynid = nelfsym++; - /* global offset table */ - s = lookup(".got", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* hash */ - s = lookup(".hash", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* got.plt */ - s = lookup(".got.plt", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - s = lookup(".plt", 0); - s->reachable = 1; - s->type = SELFROSECT; + d = lookup(".dynsym", 0); - s = lookup(".rel.plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - elfsetupplt(); - - /* define dynamic elf table */ - s = lookup(".dynamic", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* - * .dynamic table - */ - elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); - elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); - elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); - elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); - elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); - elfwritedynentsym(s, DT_REL, lookup(".rel", 0)); - elfwritedynentsymsize(s, DT_RELSZ, lookup(".rel", 0)); - elfwritedynent(s, DT_RELENT, ELF32RELSIZE); - if(rpath) - elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); - elfwritedynent(s, DT_PLTREL, DT_REL); - elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); - elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); - elfwritedynent(s, DT_DEBUG, 0); - elfwritedynent(s, DT_NULL, 0); + /* name */ + name = s->dynimpname; + if(name == nil) + name = s->name; + adduint32(d, addstring(lookup(".dynstr", 0), name)); + + /* value */ + if(s->type == SDYNIMPORT) + adduint32(d, 0); + else + addaddr(d, s); + + /* size */ + adduint32(d, 0); + + /* type */ + t = STB_GLOBAL << 4; + if(s->dynexport && (s->type&SMASK) == STEXT) + t |= STT_FUNC; + else + t |= STT_OBJECT; + adduint8(d, t); + adduint8(d, 0); + + /* shndx */ + if(!s->dynexport && s->dynimpname != nil) + adduint16(d, SHN_UNDEF); + else { + switch(s->type) { + default: + case STEXT: + t = 11; + break; + case SRODATA: + t = 12; + break; + case SDATA: + t = 13; + break; + case SBSS: + t = 14; + break; + } + adduint16(d, t); + } + } else { + diag("adddynsym: unsupported binary format"); + } +} + +void +adddynlib(char *lib) +{ + Sym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = lookup(".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else { + diag("adddynlib: unsupported binary format"); } } @@ -275,49 +526,27 @@ datoff(vlong addr) return 0; } -void -shsym(Elf64_Shdr *sh, Sym *s) -{ - vlong addr; - addr = symaddr(s); - if(sh->flags&SHF_ALLOC) - sh->addr = addr; - sh->off = datoff(addr); - sh->size = s->size; -} - -void -phsh(Elf64_Phdr *ph, Elf64_Shdr *sh) -{ - ph->vaddr = sh->addr; - ph->paddr = ph->vaddr; - ph->off = sh->off; - ph->filesz = sh->size; - ph->memsz = sh->size; - ph->align = sh->addralign; -} - void asmb(void) { int32 t; - int a, dynsym; - uint32 fo, symo, startva, resoff; - ElfEhdr *eh; - ElfPhdr *ph, *pph; - ElfShdr *sh; + uint32 symo; Section *sect; - int o; + Sym *sym; + int i; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); + if(iself) + asmbelfsetup(); + sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); @@ -330,19 +559,6 @@ asmb(void) cseek(segdata.fileoff); datblk(segdata.vaddr, segdata.filelen); - if(iself) { - /* index of elf text section; needed by asmelfsym, double-checked below */ - /* !debug['d'] causes extra sections before the .text section */ - elftextsh = 2; - if(!debug['d']) { - elftextsh += 10; - if(elfverneed) - elftextsh += 2; - } - if(HEADTYPE == Hnetbsd) - elftextsh += 1; - } - /* output symbol table */ symsize = 0; lcsize = 0; @@ -365,28 +581,43 @@ asmb(void) case Hplan9x32: symo = HEADR+segtext.len+segdata.filelen; break; - case Hnetbsd: - symo = rnd(segdata.filelen, 4096); - break; ElfSym: symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; } cseek(symo); - if(iself) { - if(debug['v']) - Bprint(&bso, "%5.2f elfsym\n", cputime()); - asmelfsym(); + switch(HEADTYPE) { + default: + if(iself) { + if(debug['v']) + Bprint(&bso, "%5.2f elfsym\n", cputime()); + asmelfsym(); + cflush(); + cwrite(elfstrdat, elfstrsize); + + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); + } + break; + case Hplan9x32: + asmplan9sym(); cflush(); - cwrite(elfstrdat, elfstrsize); - // if(debug['v']) - // Bprint(&bso, "%5.2f dwarf\n", cputime()); - // dwarfemitdebugsections(); + sym = lookup("pclntab", 0); + if(sym != nil) { + lcsize = sym->np; + for(i=0; i < lcsize; i++) + cput(sym->p[i]); + + cflush(); + } + break; } - cflush(); - } cursym = nil; @@ -395,6 +626,7 @@ asmb(void) Bflush(&bso); cseek(0L); switch(HEADTYPE) { + default: case Hnoheader: /* no header */ break; case Hrisc: /* aif for risc os */ @@ -409,7 +641,7 @@ asmb(void) - 8) / 4); /* BL - entry code */ lputl(0xef000011); /* SWI - exit code */ - lputl(textsize+HEADR); /* text size */ + lputl(segtext.filelen+HEADR); /* text size */ lputl(segdata.filelen); /* data size */ lputl(0); /* sym size */ @@ -429,7 +661,7 @@ asmb(void) break; case Hplan9x32: /* plan 9 */ lput(0x647); /* magic */ - lput(textsize); /* sizes */ + lput(segtext.filelen); /* sizes */ lput(segdata.filelen); lput(segdata.len - segdata.filelen); lput(symsize); /* nsyms */ @@ -437,16 +669,6 @@ asmb(void) lput(0L); lput(lcsize); break; - case Hnetbsd: /* boot for NetBSD */ - lput((143<<16)|0413); /* magic */ - lputl(rnd(HEADR+textsize, 4096)); - lputl(rnd(segdata.filelen, 4096)); - lputl(segdata.len - segdata.filelen); - lputl(symsize); /* nsyms */ - lputl(entryvalue()); /* va of entry */ - lputl(0L); - lputl(0L); - break; case Hixp1200: /* boot for IXP1200 */ break; case Hipaq: /* boot for ipaq */ @@ -456,216 +678,20 @@ asmb(void) lputl(0xe3300000); /* nop */ break; case Hlinux: - /* elf arm */ - eh = getElfEhdr(); - fo = HEADR; - startva = INITTEXT - fo; /* va of byte 0 of file */ - resoff = ELFRESERVE; - - /* This null SHdr must appear before all others */ - newElfShdr(elfstr[ElfStrEmpty]); - - /* program header info */ - pph = newElfPhdr(); - pph->type = PT_PHDR; - pph->flags = PF_R + PF_X; - pph->off = eh->ehsize; - pph->vaddr = INITTEXT - HEADR + pph->off; - pph->paddr = INITTEXT - HEADR + pph->off; - pph->align = INITRND; - - /* - * PHDR must be in a loaded segment. Adjust the text - * segment boundaries downwards to include it. - */ - o = segtext.vaddr - pph->vaddr; - segtext.vaddr -= o; - segtext.len += o; - o = segtext.fileoff - pph->off; - segtext.fileoff -= o; - segtext.filelen += o; - - if(!debug['d']) { - /* interpreter for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrInterp]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - if(interpreter == nil) - interpreter = linuxdynld; - resoff -= elfinterp(sh, startva, resoff, interpreter); - - ph = newElfPhdr(); - ph->type = PT_INTERP; - ph->flags = PF_R; - phsh(ph, sh); - } - - if(HEADTYPE == Hnetbsd) { - sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]); - sh->type = SHT_NOTE; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - resoff -= elfnetbsdsig(sh, startva, resoff); - - ph = newElfPhdr(); - ph->type = PT_NOTE; - ph->flags = PF_R; - phsh(ph, sh); - } - - elfphload(&segtext); - elfphload(&segdata); - - /* Dynamic linking sections */ - if(!debug['d']) { /* -d suppresses dynamic loader format */ - /* S headers for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrGot]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".got", 0)); - - sh = newElfShdr(elfstr[ElfStrGotPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".got.plt", 0)); - - dynsym = eh->shnum; - sh = newElfShdr(elfstr[ElfStrDynsym]); - sh->type = SHT_DYNSYM; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32SYMSIZE; - sh->addralign = 4; - sh->link = dynsym+1; // dynstr - // sh->info = index of first non-local symbol (number of local symbols) - shsym(sh, lookup(".dynsym", 0)); - - sh = newElfShdr(elfstr[ElfStrDynstr]); - sh->type = SHT_STRTAB; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup(".dynstr", 0)); - - sh = newElfShdr(elfstr[ElfStrHash]); - sh->type = SHT_HASH; - sh->flags = SHF_ALLOC; - sh->entsize = 4; - sh->addralign = 4; - sh->link = dynsym; - shsym(sh, lookup(".hash", 0)); - - sh = newElfShdr(elfstr[ElfStrRel]); - sh->type = SHT_REL; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32RELSIZE; - sh->addralign = 4; - sh->link = dynsym; - shsym(sh, lookup(".rel", 0)); - - /* sh and PT_DYNAMIC for .dynamic section */ - sh = newElfShdr(elfstr[ElfStrDynamic]); - sh->type = SHT_DYNAMIC; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 8; - sh->addralign = 4; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".dynamic", 0)); - - ph = newElfPhdr(); - ph->type = PT_DYNAMIC; - ph->flags = PF_R + PF_W; - phsh(ph, sh); - - /* - * Thread-local storage segment (really just size). - if(tlsoffset != 0) { - ph = newElfPhdr(); - ph->type = PT_TLS; - ph->flags = PF_R; - ph->memsz = -tlsoffset; - ph->align = 4; - } - */ - } - - ph = newElfPhdr(); - ph->type = PT_GNU_STACK; - ph->flags = PF_W+PF_R; - ph->align = 4; - - sh = newElfShstrtab(elfstr[ElfStrShstrtab]); - sh->type = SHT_STRTAB; - sh->addralign = 1; - shsym(sh, lookup(".shstrtab", 0)); - - if(elftextsh != eh->shnum) - diag("elftextsh = %d, want %d", elftextsh, eh->shnum); - for(sect=segtext.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - for(sect=segdata.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - - if(!debug['s']) { - sh = newElfShdr(elfstr[ElfStrSymtab]); - sh->type = SHT_SYMTAB; - sh->off = symo; - sh->size = symsize; - sh->addralign = 4; - sh->entsize = 16; - sh->link = eh->shnum; // link to strtab - - sh = newElfShdr(elfstr[ElfStrStrtab]); - sh->type = SHT_STRTAB; - sh->off = symo+symsize; - sh->size = elfstrsize; - sh->addralign = 1; - - // dwarfaddelfheaders(); - } - - /* Main header */ - eh->ident[EI_MAG0] = '\177'; - eh->ident[EI_MAG1] = 'E'; - eh->ident[EI_MAG2] = 'L'; - eh->ident[EI_MAG3] = 'F'; - eh->ident[EI_CLASS] = ELFCLASS32; - eh->ident[EI_DATA] = ELFDATA2LSB; - eh->ident[EI_VERSION] = EV_CURRENT; - - eh->type = ET_EXEC; - eh->machine = EM_ARM; - eh->version = EV_CURRENT; - eh->entry = entryvalue(); - - if(pph != nil) { - pph->filesz = eh->phnum * eh->phentsize; - pph->memsz = pph->filesz; - } - - cseek(0); - a = 0; - a += elfwritehdr(); - a += elfwritephdrs(); - a += elfwriteshdrs(); - a += elfwriteinterp(elfstr[ElfStrInterp]); - if(HEADTYPE == Hnetbsd) - a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); - if(a > ELFRESERVE) - diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); + case Hfreebsd: + case Hnetbsd: + case Hopenbsd: + asmbelf(symo); break; } cflush(); if(debug['c']){ - print("textsize=%d\n", textsize); + print("textsize=%ulld\n", segtext.filelen); print("datsize=%ulld\n", segdata.filelen); print("bsssize=%ulld\n", segdata.len - segdata.filelen); print("symsize=%d\n", symsize); print("lcsize=%d\n", lcsize); - print("total=%lld\n", textsize+segdata.len+symsize+lcsize); + print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize); } } @@ -812,6 +838,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- case 5: /* bra s */ v = -8; + // TODO: Use addrel. if(p->cond != P) v = (p->cond->pc - pc) - 8; o1 = opbra(p->as, p->scond); @@ -875,15 +902,22 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- rel = addrel(cursym); rel->off = pc - cursym->value; rel->siz = 4; - rel->type = D_ADDR; rel->sym = p->to.sym; rel->add = p->to.offset; + if(flag_shared) { + rel->type = D_PCREL; + rel->add += pc - p->pcrel->pc - 8; + } else + rel->type = D_ADDR; o1 = 0; } break; case 12: /* movw $lcon, reg */ o1 = omvl(p, &p->from, p->to.reg); + if(o->flag & LPCREL) { + o2 = oprrr(AADD, p->scond) | p->to.reg | REGPC << 16 | p->to.reg << 12; + } break; case 13: /* op $lcon, [R], R */ @@ -1128,7 +1162,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- r = p->reg; if(r == NREG) { r = rt; - if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD) + if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD || p->as == AABSF || p->as == AABSD) r = 0; } o1 |= rf | (r<<16) | (rt<<12); @@ -1188,13 +1222,23 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- break; case 62: /* case R -> movw R<<2(PC),PC */ - o1 = olrr(p->from.reg, REGPC, REGPC, p->scond); - o1 |= 2<<7; + if(o->flag & LPCREL) { + o1 = oprrr(AADD, p->scond) | immrot(1) | p->from.reg << 16 | REGTMP << 12; + o2 = olrr(REGTMP, REGPC, REGTMP, p->scond); + o2 |= 2<<7; + o3 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGPC << 12; + } else { + o1 = olrr(p->from.reg, REGPC, REGPC, p->scond); + o1 |= 2<<7; + } break; case 63: /* bcase */ - if(p->cond != P) + if(p->cond != P) { o1 = p->cond->pc; + if(flag_shared) + o1 = o1 - p->pcrel->pc - 16; + } break; /* reloc ops */ @@ -1203,6 +1247,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 65: /* mov/movbu addr,R */ @@ -1212,6 +1260,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- o2 = olr(0, REGTMP, p->to.reg, p->scond); if(p->as == AMOVBU || p->as == AMOVB) o2 |= 1<<22; + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 68: /* floating point store -> ADDR */ @@ -1219,6 +1271,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 69: /* floating point load <- ADDR */ @@ -1226,6 +1282,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; /* ArmV4 ops: */ @@ -1422,12 +1482,57 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- o2 ^= (1<<5)|(1<<6); else if(p->as == AMOVH) o2 ^= (1<<6); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 94: /* movh/movhu R,addr -> strh */ o1 = omvl(p, &p->to, REGTMP); if(!o1) break; o2 = oshr(p->from.reg, 0, REGTMP, p->scond); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + case 95: /* PLD off(reg) */ + o1 = 0xf5d0f000; + o1 |= p->from.reg << 16; + if(p->from.offset < 0) { + o1 &= ~(1 << 23); + o1 |= (-p->from.offset) & 0xfff; + } else + o1 |= p->from.offset & 0xfff; + break; + case 96: /* UNDEF */ + // This is supposed to be something that stops execution. + // It's not supposed to be reached, ever, but if it is, we'd + // like to be able to tell how we got there. Assemble as + // BL $0 + // TODO: Use addrel. + v = (0 - pc) - 8; + o1 = opbra(ABL, C_SCOND_NONE); + o1 |= (v >> 2) & 0xffffff; + break; + case 97: /* CLZ Rm, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 12; + o1 |= p->from.reg; + break; + case 98: /* MULW{T,B} Rs, Rm, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 16; + o1 |= p->from.reg << 8; + o1 |= p->reg; + break; + case 99: /* MULAW{T,B} Rs, Rm, Rn, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 12; + o1 |= p->from.reg << 8; + o1 |= p->reg; + o1 |= p->to.offset << 16; break; } @@ -1547,6 +1652,8 @@ oprrr(int a, int sc) case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4); case ASQRTD: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xb<<8) | (0xc<<4); case ASQRTF: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xa<<8) | (0xc<<4); + case AABSD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (0xc<<4); + case AABSF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (0xc<<4); case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4); case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4); @@ -1586,6 +1693,19 @@ oprrr(int a, int sc) return o | (0xe<<24) | (0x1<<20) | (0xb<<8) | (1<<4); case ACMP+AEND: // cmp imm return o | (0x3<<24) | (0x5<<20); + + case ACLZ: + // CLZ doesn't support .S + return (o & (0xf<<28)) | (0x16f<<16) | (0xf1<<4); + + case AMULWT: + return (o & (0xf<<28)) | (0x12 << 20) | (0xe<<4); + case AMULWB: + return (o & (0xf<<28)) | (0x12 << 20) | (0xa<<4); + case AMULAWT: + return (o & (0xf<<28)) | (0x12 << 20) | (0xc<<4); + case AMULAWB: + return (o & (0xf<<28)) | (0x12 << 20) | (0x8<<4); } diag("bad rrr %d", a); prasm(curp); @@ -1789,7 +1909,8 @@ omvl(Prog *p, Adr *a, int dr) int chipzero(Ieee *e) { - if(e->l != 0 || e->h != 0) + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if(goarm < 7 || e->l != 0 || e->h != 0) return -1; return 0; } @@ -1800,6 +1921,10 @@ chipfloat(Ieee *e) int n; ulong h; + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if(goarm < 7) + goto no; + if(e->l != 0 || (e->h&0xffff) != 0) goto no; h = e->h & 0x7fc00000; @@ -1824,83 +1949,3 @@ chipfloat(Ieee *e) no: return -1; } - - -void -genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) -{ - Auto *a; - Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - put(s, s->name, 'T', s->value, s->size, s->version, 0); - - for(h=0; hhash) { - if(s->hide) - continue; - switch(s->type) { - case SCONST: - case SRODATA: - case SDATA: - case SELFROSECT: - case STYPE: - case SSTRING: - case SGOSTRING: - case SNOPTRDATA: - case SSYMTAB: - case SPCLNTAB: - if(!s->reachable) - continue; - put(s, s->name, 'D', s->value, s->size, s->version, s->gotype); - continue; - - case SBSS: - case SNOPTRBSS: - if(!s->reachable) - continue; - if(s->np > 0) - diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special); - put(s, s->name, 'B', s->value, s->size, s->version, s->gotype); - continue; - - case SFILE: - put(nil, s->name, 'f', s->value, 0, s->version, 0); - continue; - } - } - } - - for(s = textp; s != nil; s = s->next) { - /* filenames first */ - for(a=s->autom; a; a=a->link) - if(a->type == D_FILE) - put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); - - put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); - - /* frame, auto and param after */ - put(nil, ".frame", 'm', s->text->to.offset+4, 0, 0, 0); - - for(a=s->autom; a; a=a->link) - if(a->type == D_AUTO) - put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); - else - if(a->type == D_PARAM) - put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %ud\n", symsize); - Bflush(&bso); -} - -void -setpersrc(Sym *s) -{ - USED(s); -} diff --git a/src/cmd/5l/doc.go b/src/cmd/5l/doc.go index 969f502a7..a054a228b 100644 --- a/src/cmd/5l/doc.go +++ b/src/cmd/5l/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 5l is the linker for the ARM. @@ -10,4 +12,4 @@ The $GOARCH for these tools is arm. The flags are documented in ../ld/doc.go. */ -package documentation +package main diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index b1a48ded8..62dd8947f 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -36,7 +36,9 @@ enum { thechar = '5', - PtrSize = 4 + PtrSize = 4, + IntSize = 4, + FuncAlign = 4 // single-instruction alignment }; #ifndef EXTERN @@ -66,22 +68,24 @@ struct Adr { union { - int32 u0offset; + struct { + int32 u0offset; + int32 u0offset2; // argsize + } u0off; char* u0sval; Ieee u0ieee; char* u0sbig; } u0; Sym* sym; + Sym* gotype; char type; - uchar index; // not used on arm, required by ld/go.c char reg; char name; - int32 offset2; // argsize char class; - Sym* gotype; }; -#define offset u0.u0offset +#define offset u0.u0off.u0offset +#define offset2 u0.u0off.u0offset2 #define sval u0.u0sval #define scon sval #define ieee u0.u0ieee @@ -107,7 +111,7 @@ struct Prog } u0; Prog* cond; Prog* link; - Prog* dlink; + Prog* pcrel; int32 pc; int32 line; int32 spadj; @@ -116,7 +120,7 @@ struct Prog uchar as; uchar scond; uchar reg; - uchar align; + uchar align; // unused }; #define regused u0.u0regused @@ -135,8 +139,6 @@ struct Sym uchar reachable; uchar dynexport; uchar leaf; - uchar stkcheck; - uchar hide; int32 dynid; int32 plt; int32 got; @@ -144,18 +146,26 @@ struct Sym int32 sig; int32 size; int32 align; // if non-zero, required alignment in bytes + int32 elfsym; + int32 locals; // size of stack frame locals area + int32 args; // size of stack frame incoming arguments area uchar special; uchar fnptr; // used as fn ptr + uchar stkcheck; + uchar hide; Sym* hash; // in hash table Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in SSUB list Sym* outer; // container of sub Sym* gotype; + Sym* reachparent; + Sym* queue; char* file; char* dynimpname; char* dynimplib; char* dynimpvers; + struct Section* sect; // STEXT Auto* autom; @@ -168,6 +178,7 @@ struct Sym Reloc* r; int32 nr; int32 maxr; + int rel_ro; }; #define SIGNINTERN (1729*325*1729) @@ -190,6 +201,7 @@ struct Optab char size; char param; char flag; + uchar pcrelsiz; }; struct Oprang { @@ -207,10 +219,12 @@ enum LFROM = 1<<0, LTO = 1<<1, LPOOL = 1<<2, + LPCREL = 1<<3, C_NONE = 0, C_REG, C_REGREG, + C_REGREG2, C_SHIFT, C_FREG, C_PSR, @@ -220,6 +234,7 @@ enum C_NCON, /* ~RCON */ C_SCON, /* 0xffff */ C_LCON, + C_LCONADDR, C_ZFCON, C_SFCON, C_LFCON, @@ -273,6 +288,7 @@ EXTERN int32 INITDAT; /* data location */ EXTERN int32 INITRND; /* data round above text location */ EXTERN int32 INITTEXT; /* text location */ EXTERN char* INITENTRY; /* entry point */ +EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN int32 autosize; EXTERN Auto* curauto; EXTERN Auto* curhist; @@ -280,7 +296,7 @@ EXTERN Prog* curp; EXTERN Sym* cursym; EXTERN Sym* datap; EXTERN int32 elfdatsize; -EXTERN char debug[128]; +EXTERN int debug[128]; EXTERN Sym* etextp; EXTERN char* noname; EXTERN Prog* lastp; @@ -298,12 +314,15 @@ EXTERN char* rpath; EXTERN uint32 stroffset; EXTERN int32 symsize; EXTERN Sym* textp; -EXTERN int32 textsize; EXTERN int version; EXTERN char xcmp[C_GOK+1][C_GOK+1]; EXTERN Prog zprg; EXTERN int dtype; +EXTERN int tlsoffset; EXTERN int armsize; +EXTERN int goarm; +EXTERN Sym* adrgotype; // type symbol on last Adr read +EXTERN Sym* fromgotype; // type symbol on last p->from read extern char* anames[]; extern Optab optab[]; @@ -312,6 +331,8 @@ void addpool(Prog*, Adr*); EXTERN Prog* blitrl; EXTERN Prog* elitrl; +EXTERN int goarm; + void initdiv(void); EXTERN Prog* prog_div; EXTERN Prog* prog_divu; @@ -398,6 +419,9 @@ void span(void); void strnput(char*, int); int32 symaddr(Sym*); void undef(void); +void vputb(uint64); +void vputl(uint64); +void wputb(uint16); void wput(int32); void wputl(ushort w); void xdefine(char*, int, int32); @@ -407,8 +431,9 @@ int32 immaddr(int32); int32 opbra(int, int); int brextra(Prog*); int isbranch(Prog*); -void fnptrs(void); +void fnptrs(void); void doelf(void); +void dozerostk(void); // used by -Z vlong addaddr(Sym *s, Sym *t); vlong addsize(Sym *s, Sym *t); @@ -425,3 +450,9 @@ vlong adduintxx(Sym *s, uint64 v, int wid); #define VPUT(a) abort() #endif + +/* Used by ../ld/dwarf.c */ +enum +{ + DWARFREGSP = 13 +}; diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index 293fee3c6..a051774b4 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -225,6 +225,12 @@ Dconv(Fmt *fp) snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); break; + case D_REGREG2: + snprint(str, sizeof str, "R%d,R%d", a->reg, (int)a->offset); + if(a->name != D_NONE || a->sym != S) + snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); + break; + case D_FREG: snprint(str, sizeof str, "F%d", a->reg); if(a->name != D_NONE || a->sym != S) @@ -429,6 +435,7 @@ cnames[] = [C_LAUTO] = "C_LAUTO", [C_LBRA] = "C_LBRA", [C_LCON] = "C_LCON", + [C_LCONADDR] = "C_LCONADDR", [C_LOREG] = "C_LOREG", [C_NCON] = "C_NCON", [C_NONE] = "C_NONE", @@ -438,6 +445,7 @@ cnames[] = [C_RCON] = "C_RCON", [C_REG] = "C_REG", [C_REGREG] = "C_REGREG", + [C_REGREG2] = "C_REGREG2", [C_ROREG] = "C_ROREG", [C_SAUTO] = "C_SAUTO", [C_SBRA] = "C_SBRA", diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index 004f9f2fa..99a096a31 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -45,6 +45,20 @@ static Sym* sym_divu; static Sym* sym_mod; static Sym* sym_modu; +static void +linkcase(Prog *casep) +{ + Prog *p; + + for(p = casep; p != P; p = p->link){ + if(p->as == ABCASE) { + for(; p != P && p->as == ABCASE; p = p->link) + p->pcrel = casep; + break; + } + } +} + void noops(void) { @@ -76,6 +90,11 @@ noops(void) for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { switch(p->as) { + case ACASE: + if(flag_shared) + linkcase(p); + break; + case ATEXT: p->mark |= LEAF; break; @@ -365,11 +384,7 @@ noops(void) q1 = p; /* MOV a,4(SP) */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = AMOVW; p->line = q1->line; p->from.type = D_REG; @@ -379,11 +394,7 @@ noops(void) p->to.offset = 4; /* MOV b,REGTMP */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = AMOVW; p->line = q1->line; p->from.type = D_REG; @@ -395,11 +406,7 @@ noops(void) p->to.offset = 0; /* CALL appropriate */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = ABL; p->line = q1->line; p->to.type = D_BRANCH; @@ -424,11 +431,7 @@ noops(void) } /* MOV REGTMP, b */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = AMOVW; p->line = q1->line; p->from.type = D_REG; @@ -438,12 +441,9 @@ noops(void) p->to.reg = q1->to.reg; /* ADD $8,SP */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = AADD; + p->line = q1->line; p->from.type = D_CONST; p->from.reg = NREG; p->from.offset = 8; diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index a3f816160..6aa7fdd69 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -34,6 +34,7 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" #include #ifndef DEFAULT @@ -47,41 +48,29 @@ Header headers[] = { "noheader", Hnoheader, "risc", Hrisc, "plan9", Hplan9x32, - "netbsd", Hnetbsd, "ixp1200", Hixp1200, "ipaq", Hipaq, "linux", Hlinux, + "freebsd", Hfreebsd, + "netbsd", Hnetbsd, 0, 0 }; /* * -Hrisc -T0x10005000 -R4 is aif for risc os * -Hplan9 -T4128 -R4096 is plan9 format - * -Hnetbsd -T0xF0000020 -R4 is NetBSD format * -Hixp1200 is IXP1200 (raw) * -Hipaq -T0xC0008010 -R1024 is ipaq * -Hlinux -Tx -Rx is linux elf + * -Hfreebsd is freebsd elf + * -Hnetbsd is netbsd elf */ -static char* -linkername[] = -{ - "runtime.softfloat", - "math.sqrtGoC", -}; - -void -usage(void) -{ - fprint(2, "usage: 5l [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-D data] [-R rnd] [-r path] [-o out] main.5\n"); - errorexit(); -} - void main(int argc, char *argv[]) { - int c, i; - char *p, *name, *val; + char *p; + Sym *s; Binit(&bso, 1, OWRITE); listinit(); @@ -92,59 +81,53 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + LIBINITENTRY = 0; nuxiinit(); - p = getenv("GOARM"); - if(p != nil && strcmp(p, "5") == 0) + p = getgoarm(); + if(p != nil) + goarm = atoi(p); + else + goarm = 6; + if(goarm == 5) debug['F'] = 1; - ARGBEGIN { - default: - c = ARGC(); - if(c == 'l') - usage(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - case 'o': - outfile = EARGF(usage()); - break; - case 'E': - INITENTRY = EARGF(usage()); - break; - case 'I': - interpreter = EARGF(usage()); - break; - case 'L': - Lflag(EARGF(usage())); - break; - case 'T': - INITTEXT = atolwhex(EARGF(usage())); - break; - case 'D': - INITDAT = atolwhex(EARGF(usage())); - break; - case 'R': - INITRND = atolwhex(EARGF(usage())); - break; - case 'r': - rpath = EARGF(usage()); - break; - case 'H': - HEADTYPE = headtype(EARGF(usage())); - /* do something about setting INITTEXT */ - break; - case 'V': - print("%cl version %s\n", thechar, getgoversion()); - errorexit(); - case 'X': - name = EARGF(usage()); - val = EARGF(usage()); - addstrdata(name, val); - break; - } ARGEND - - USED(argc); + flagcount("1", "use alternate profiling code", &debug['1']); + flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); + flagstr("E", "sym: entry symbol", &INITENTRY); + flagint32("D", "addr: data address", &INITDAT); + flagcount("G", "debug pseudo-ops", &debug['G']); + flagfn1("I", "interp: set ELF interp", setinterp); + flagfn1("L", "dir: add dir to library path", Lflag); + flagfn1("H", "head: header type", setheadtype); + flagcount("K", "add stack underflow checks", &debug['K']); + flagcount("M", "disable software div/mod", &debug['M']); + flagcount("O", "print pc-line tables", &debug['O']); + flagcount("P", "debug code generation", &debug['P']); + flagint32("R", "rnd: address rounding", &INITRND); + flagint32("T", "addr: text address", &INITTEXT); + flagfn0("V", "print version and exit", doversion); + flagcount("W", "disassemble input", &debug['W']); + flagfn2("X", "name value: define string data", addstrdata); + flagcount("Z", "clear stack frame on entry", &debug['Z']); + flagcount("a", "disassemble output", &debug['a']); + flagcount("c", "dump call graph", &debug['c']); + flagcount("d", "disable dynamic executable", &debug['d']); + flagcount("f", "ignore version mismatch", &debug['f']); + flagcount("g", "disable go package data checks", &debug['g']); + flagstr("k", "sym: set field tracking symbol", &tracksym); + flagcount("n", "dump symbol table", &debug['n']); + flagstr("o", "outfile: set output file", &outfile); + flagcount("p", "insert profiling code", &debug['p']); + flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); + flagcount("race", "enable race detector", &flag_race); + flagcount("s", "disable symbol table", &debug['s']); + flagcount("u", "reject unsafe packages", &debug['u']); + flagcount("v", "print link trace", &debug['v']); + flagcount("w", "disable DWARF generation", &debug['w']); + flagcount("shared", "generate shared object", &flag_shared); + + flagparse(&argc, &argv, usage); if(argc != 1) usage(); @@ -152,7 +135,7 @@ main(int argc, char *argv[]) libinit(); if(HEADTYPE == -1) - HEADTYPE = Hlinux; + HEADTYPE = headtype(goos); switch(HEADTYPE) { default: diag("unknown -H option"); @@ -184,15 +167,6 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 4096; break; - case Hnetbsd: /* boot for NetBSD */ - HEADR = 32L; - if(INITTEXT == -1) - INITTEXT = 0xF0000020L; - if(INITDAT == -1) - INITDAT = 0; - if(INITRND == -1) - INITRND = 4096; - break; case Hixp1200: /* boot for IXP1200 */ HEADR = 0L; if(INITTEXT == -1) @@ -212,7 +186,11 @@ main(int argc, char *argv[]) INITRND = 1024; break; case Hlinux: /* arm elf */ - debug['d'] = 1; // no dynamic linking + case Hfreebsd: + case Hnetbsd: + debug['d'] = 0; // with dynamic linking + tlsoffset = -8; // hardcoded number, first 4-byte word for g, and then 4-byte word for m + // this number is known to ../../pkg/runtime/cgo/gcc_linux_arm.c elfinit(); HEADR = ELFRESERVE; if(INITTEXT == -1) @@ -246,13 +224,17 @@ main(int argc, char *argv[]) cbp = buf.cbuf; cbc = sizeof(buf.cbuf); + // embed goarm to runtime.goarm + s = lookup("runtime.goarm", 0); + s->dupok = 1; + adduint8(s, goarm); + addlibpath("command line", "command line", argv[0], "main"); loadlib(); // mark some functions that are only referenced after linker code editing - // TODO(kaib): this doesn't work, the prog can't be found in runtime - for(i=0; i= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + static void -zaddr(Biobuf *f, Adr *a, Sym *h[]) +zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) { int i, c; int32 l; Sym *s; Auto *u; - a->type = Bgetc(f); - a->reg = Bgetc(f); - c = Bgetc(f); + a->type = BGETC(f); + a->reg = BGETC(f); + c = BGETC(f); if(c < 0 || c > NSYM){ print("sym out of range: %d\n", c); Bputc(f, ALAST+1); return; } a->sym = h[c]; - a->name = Bgetc(f); + a->name = BGETC(f); + adrgotype = zsym(pn, f, h); if((schar)a->reg < 0 || a->reg > NREG) { print("register out of range %d\n", a->reg); @@ -341,7 +344,8 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) break; case D_REGREG: - a->offset = Bgetc(f); + case D_REGREG2: + a->offset = BGETC(f); break; case D_CONST2: @@ -368,8 +372,11 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) if(s == S) return; i = a->name; - if(i != D_AUTO && i != D_PARAM) + if(i != D_AUTO && i != D_PARAM) { + if(s && adrgotype) + s->gotype = adrgotype; return; + } l = a->offset; for(u=curauto; u; u=u->link) @@ -377,6 +384,8 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) if(u->type == i) { if(u->aoffset > l) u->aoffset = l; + if(adrgotype) + u->gotype = adrgotype; return; } @@ -386,6 +395,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) u->asym = s; u->aoffset = l; u->type = i; + u->gotype = adrgotype; } void @@ -425,7 +435,7 @@ newloop: loop: if(f->state == Bracteof || Boffset(f) >= eof) goto eof; - o = Bgetc(f); + o = BGETC(f); if(o == Beof) goto eof; @@ -438,8 +448,8 @@ loop: sig = 0; if(o == ASIGNAME) sig = Bget4(f); - v = Bgetc(f); /* type */ - o = Bgetc(f); /* sym */ + v = BGETC(f); /* type */ + o = BGETC(f); /* sym */ r = 0; if(v == D_STATIC) r = version; @@ -483,18 +493,20 @@ loop: histfrogp++; } else collapsefrog(s); + dwarfaddfrag(s->value, s->name); } goto loop; } p = mal(sizeof(Prog)); p->as = o; - p->scond = Bgetc(f); - p->reg = Bgetc(f); + p->scond = BGETC(f); + p->reg = BGETC(f); p->line = Bget4(f); - zaddr(f, &p->from, h); - zaddr(f, &p->to, h); + zaddr(pn, f, &p->from, h); + fromgotype = adrgotype; + zaddr(pn, f, &p->to, h); if(p->as != ATEXT && p->as != AGLOBL && p->reg > NREG) diag("register out of range %A %d", p->as, p->reg); @@ -582,6 +594,15 @@ loop: pc++; break; + case ALOCALS: + cursym->locals = p->to.offset; + pc++; + break; + + case ATYPE: + pc++; + goto loop; + case ATEXT: if(cursym != nil && cursym->text) { histtoauto(); @@ -611,6 +632,11 @@ loop: etextp->next = s; else textp = s; + if(fromgotype) { + if(s->gotype && s->gotype != fromgotype) + diag("%s: type mismatch for %s", pn, s->name); + s->gotype = fromgotype; + } etextp = s; p->align = 4; autosize = (p->to.offset+3L) & ~3L; @@ -619,6 +645,7 @@ loop: s->type = STEXT; s->text = p; s->value = pc; + s->args = p->to.offset2; lastp = p; p->pc = pc; pc++; @@ -672,7 +699,7 @@ loop: sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { - s->type = SBSS; + s->type = SRODATA; adduint32(s, ieeedtof(&p->from.ieee)); s->reachable = 0; } @@ -694,7 +721,7 @@ loop: p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { - s->type = SBSS; + s->type = SRODATA; adduint32(s, p->from.ieee.l); adduint32(s, p->from.ieee.h); s->reachable = 0; diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 514786f85..231071f20 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -63,6 +63,7 @@ Optab optab[] = { AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL }, { ABL, C_NONE, C_NONE, C_ROREG, 7, 8, 0 }, + { ABL, C_REG, C_NONE, C_ROREG, 7, 8, 0 }, { ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0 }, { ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0 }, @@ -77,10 +78,12 @@ Optab optab[] = { ASWI, C_NONE, C_NONE, C_LCON, 10, 4, 0 }, { AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0 }, + { AWORD, C_NONE, C_NONE, C_LCONADDR, 11, 4, 0 }, { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 }, { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 }, { AMOVW, C_LCON, C_NONE, C_REG, 12, 4, 0, LFROM }, + { AMOVW, C_LCONADDR, C_NONE, C_REG, 12, 4, 0, LFROM | LPCREL, 4}, { AADD, C_NCON, C_REG, C_REG, 13, 8, 0 }, { AADD, C_NCON, C_NONE, C_REG, 13, 8, 0 }, @@ -103,6 +106,7 @@ Optab optab[] = { ADIV, C_REG, C_NONE, C_REG, 16, 4, 0 }, { AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0 }, + { AMULA, C_REG, C_REG, C_REGREG2, 17, 4, 0 }, { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, @@ -118,20 +122,20 @@ Optab optab[] = { AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, - { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, + { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, { AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, + { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, { AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, + { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, { AMOVW, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, { AMOVW, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, - { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM }, + { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4 }, { AMOVBU, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, { AMOVBU, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, - { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM }, + { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4 }, { AMOVW, C_LACON,C_NONE, C_REG, 34, 8, REGSP, LFROM }, @@ -158,8 +162,8 @@ Optab optab[] = { AMOVF, C_LAUTO,C_NONE, C_FREG, 53, 12, REGSP, LFROM }, { AMOVF, C_LOREG,C_NONE, C_FREG, 53, 12, 0, LFROM }, - { AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO }, - { AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM }, + { AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO | LPCREL, 4 }, + { AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM | LPCREL, 4}, { AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, { AADDF, C_FREG, C_REG, C_FREG, 54, 4, 0 }, @@ -177,7 +181,7 @@ Optab optab[] = { AMOVB, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, { AMOVBU, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, - { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0 }, + { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0, LPCREL, 8 }, { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 }, { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, @@ -194,20 +198,20 @@ Optab optab[] = { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, - { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO }, + { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO }, + { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, - { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, + { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, - { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, + { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, - { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, + { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, @@ -232,5 +236,16 @@ Optab optab[] = { ALDREXD, C_SOREG,C_NONE, C_REG, 91, 4, 0 }, { ASTREXD, C_SOREG,C_REG, C_REG, 92, 4, 0 }, + { APLD, C_SOREG,C_NONE, C_NONE, 95, 4, 0 }, + + { AUNDEF, C_NONE, C_NONE, C_NONE, 96, 4, 0 }, + + { ACLZ, C_REG, C_NONE, C_REG, 97, 4, 0 }, + + { AMULWT, C_REG, C_REG, C_REG, 98, 4, 0 }, + { AMULAWT, C_REG, C_REG, C_REGREG2, 99, 4, 0 }, + + { AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0 }, + { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 }, }; diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c index 0f2afbd85..c22b86085 100644 --- a/src/cmd/5l/pass.c +++ b/src/cmd/5l/pass.c @@ -119,7 +119,7 @@ loop: i--; continue; } - if(a == AB || (a == ARET && q->scond == 14) || a == ARFE) + if(a == AB || (a == ARET && q->scond == 14) || a == ARFE || a == AUNDEF) goto copy; if(q->cond == P || (q->cond->mark&FOLL)) continue; @@ -140,7 +140,7 @@ loop: } (*last)->link = r; *last = r; - if(a == AB || (a == ARET && q->scond == 14) || a == ARFE) + if(a == AB || (a == ARET && q->scond == 14) || a == ARFE || a == AUNDEF) return; r->as = ABNE; if(a == ABNE) @@ -166,7 +166,7 @@ loop: p->mark |= FOLL; (*last)->link = p; *last = p; - if(a == AB || (a == ARET && p->scond == 14) || a == ARFE){ + if(a == AB || (a == ARET && p->scond == 14) || a == ARFE || a == AUNDEF){ return; } if(p->cond != P) @@ -215,7 +215,7 @@ patch(void) s = p->to.sym; if(s->text == nil) continue; - switch(s->type) { + switch(s->type&SMASK) { default: diag("undefined: %s", s->name); s->type = STEXT; @@ -231,7 +231,7 @@ patch(void) if(p->to.type != D_BRANCH) continue; c = p->to.offset; - for(q = textp->text; q != P;) { + for(q = cursym->text; q != P;) { if(c == q->pc) break; if(q->forwd != P && c >= q->forwd->pc) @@ -333,3 +333,70 @@ rnd(int32 v, int32 r) v -= c; return v; } + +void +dozerostk(void) +{ + Prog *p, *pl; + int32 autoffset; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->text->link == nil) + continue; + p = cursym->text; + autoffset = p->to.offset; + if(autoffset < 0) + autoffset = 0; + if(autoffset && !(p->reg&NOSPLIT)) { + // MOVW $4(R13), R1 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = 13; + p->from.offset = 4; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW $n(R13), R2 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = 13; + p->from.offset = 4 + autoffset; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW $0, R3 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_REG; + p->to.reg = 3; + + // L: + // MOVW.P R3, 0(R1) +4 + // CMP R1, R2 + // BNE L + p = pl = appendp(p); + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = 3; + p->to.type = D_OREG; + p->to.reg = 1; + p->to.offset = 4; + p->scond |= C_PBIT; + + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = 2; + + p = appendp(p); + p->as = ABNE; + p->to.type = D_BRANCH; + p->cond = pl; + } + } +} diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c index 401107178..de6481c71 100644 --- a/src/cmd/5l/softfloat.c +++ b/src/cmd/5l/softfloat.c @@ -55,6 +55,8 @@ softfloat(void) case ADIVD: case ASQRTF: case ASQRTD: + case AABSF: + case AABSD: goto soft; default: @@ -74,6 +76,7 @@ softfloat(void) p->to.type = D_BRANCH; p->to.sym = symsfloat; p->cond = psfloat; + p->line = next->line; p = next; wasfloat = 1; diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index 13e1848e1..a5afa02e7 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -90,16 +90,36 @@ span(void) int32 c, otxt, out[6]; Section *sect; uchar *bp; + Sym *sub; if(debug['v']) Bprint(&bso, "%5.2f span\n", cputime()); Bflush(&bso); + sect = addsection(&segtext, ".text", 05); + lookup("text", 0)->sect = sect; + lookup("etext", 0)->sect = sect; + bflag = 0; c = INITTEXT; otxt = c; for(cursym = textp; cursym != nil; cursym = cursym->next) { + cursym->sect = sect; p = cursym->text; + if(p == P || p->link == P) { // handle external functions and ELF section symbols + if(cursym->type & SSUB) + continue; + if(cursym->align != 0) + c = rnd(c, cursym->align); + cursym->value = 0; + for(sub = cursym; sub != S; sub = sub->sub) { + sub->value += c; + for(p = sub->text; p != P; p = p->link) + p->pc += sub->value; + } + c += cursym->size; + continue; + } p->pc = c; cursym->value = c; @@ -160,6 +180,8 @@ span(void) bflag = 0; c = INITTEXT; for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(!cursym->text || !cursym->text->link) + continue; cursym->value = c; for(p = cursym->text; p != P; p = p->link) { curp = p; @@ -217,6 +239,8 @@ span(void) */ for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; + if(p == P || p->link == P) + continue; autosize = p->to.offset + 4; symgrow(cursym, cursym->size); @@ -235,7 +259,6 @@ span(void) } } } - sect = addsection(&segtext, ".text", 05); sect->vaddr = INITTEXT; sect->len = c - INITTEXT; } @@ -269,12 +292,20 @@ flushpool(Prog *p, int skip, int force) q->to.type = D_BRANCH; q->cond = p->link; q->link = blitrl; + q->line = p->line; blitrl = q; } else if(!force && (p->pc+pool.size-pool.start < 2048)) return 0; elitrl->link = p->link; p->link = blitrl; + // BUG(minux): how to correctly handle line number for constant pool entries? + // for now, we set line number to the last instruction preceding them at least + // this won't bloat the .debug_line tables + while(blitrl) { + blitrl->line = p->line; + blitrl = blitrl->link; + } blitrl = 0; /* BUG: should refer back to values until out-of-range */ elitrl = 0; pool.size = 0; @@ -299,9 +330,11 @@ addpool(Prog *p, Adr *a) switch(c) { default: t.to = *a; + if(flag_shared && t.to.sym != S) + t.pcrel = p; break; - case C_SROREG: + case C_SROREG: case C_LOREG: case C_ROREG: case C_FOREG: @@ -316,11 +349,13 @@ addpool(Prog *p, Adr *a) break; } - for(q = blitrl; q != P; q = q->link) /* could hash on t.t0.offset */ - if(memcmp(&q->to, &t.to, sizeof(t.to)) == 0) { - p->cond = q; - return; - } + if(t.pcrel == P) { + for(q = blitrl; q != P; q = q->link) /* could hash on t.t0.offset */ + if(q->pcrel == P && memcmp(&q->to, &t.to, sizeof(t.to)) == 0) { + p->cond = q; + return; + } + } q = prg(); *q = t; @@ -407,25 +442,9 @@ immhalf(int32 v) int32 symaddr(Sym *s) { - int32 v; - - v = s->value; - switch(s->type) { - default: - diag("unexpected type %d in symaddr(%s)", s->type, s->name); - return 0; - - case STEXT: - case SELFROSECT: - case SRODATA: - case SDATA: - case SBSS: - case SCONST: - case SNOPTRDATA: - case SNOPTRBSS: - break; - } - return v; + if(!s->reachable) + diag("unreachable symbol in symaddr - %s", s->name); + return s->value; } int @@ -444,6 +463,9 @@ aclass(Adr *a) case D_REGREG: return C_REGREG; + case D_REGREG2: + return C_REGREG2; + case D_SHIFT: return C_SHIFT; @@ -552,7 +574,10 @@ aclass(Adr *a) if(s == S) break; instoffset = 0; // s.b. unused but just in case - return C_LCON; + if(flag_shared) + return C_LCONADDR; + else + return C_LCON; case D_AUTO: instoffset = autosize + a->offset; @@ -718,8 +743,14 @@ buildop(void) for(i=0; ilink) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -1306,21 +1348,12 @@ outhist(void) Bputc(&obuf, h->line>>24); zaddr(&nullgen, 0); zaddr(&g, 0); - } -} -void -pragbldicks(void) -{ - while(getnsc() != '\n') - ; -} - -void -praghjdicks(void) -{ - while(getnsc() != '\n') - ; + if(tofree) { + free(tofree); + tofree = nil; + } + } } #include "../cc/lexbody" diff --git a/src/cmd/6a/y.tab.c b/src/cmd/6a/y.tab.c index 2da8a1fac..75c4ad5ea 100644 --- a/src/cmd/6a/y.tab.c +++ b/src/cmd/6a/y.tab.c @@ -406,16 +406,16 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 548 +#define YYLAST 554 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 54 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 40 /* YYNRULES -- Number of rules. */ -#define YYNRULES 131 +#define YYNRULES 132 /* YYNRULES -- Number of states. */ -#define YYNSTATES 260 +#define YYNSTATES 263 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 @@ -468,16 +468,16 @@ static const yytype_uint16 yyprhs[] = 23, 26, 29, 33, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 89, 91, 95, 99, 102, 104, 107, 109, - 112, 114, 120, 124, 130, 133, 135, 137, 139, 143, - 149, 153, 159, 162, 164, 168, 174, 180, 181, 183, - 187, 193, 195, 197, 199, 201, 204, 207, 209, 211, - 213, 215, 220, 223, 226, 228, 230, 232, 234, 236, - 238, 240, 243, 246, 249, 252, 255, 260, 266, 270, - 272, 274, 276, 281, 286, 291, 298, 308, 312, 316, - 322, 331, 333, 340, 346, 354, 355, 358, 361, 363, - 365, 367, 369, 371, 374, 377, 380, 384, 386, 389, - 393, 398, 400, 404, 408, 412, 416, 420, 425, 430, - 434, 438 + 112, 114, 118, 124, 128, 134, 137, 139, 141, 143, + 147, 153, 157, 163, 166, 168, 172, 178, 184, 185, + 187, 191, 197, 199, 201, 203, 205, 208, 211, 213, + 215, 217, 219, 224, 227, 230, 232, 234, 236, 238, + 240, 242, 244, 247, 250, 253, 256, 259, 264, 270, + 274, 276, 278, 280, 285, 290, 295, 302, 312, 316, + 320, 326, 335, 337, 344, 350, 358, 359, 362, 365, + 367, 369, 371, 373, 375, 378, 381, 384, 388, 390, + 393, 397, 402, 404, 408, 412, 416, 420, 424, 429, + 434, 438, 442 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ @@ -494,40 +494,40 @@ static const yytype_int8 yyrhs[] = 75, -1, 29, 76, -1, 23, 77, -1, -1, 49, -1, 80, 49, 78, -1, 78, 49, 80, -1, 80, 49, -1, 80, -1, 49, 78, -1, 78, -1, 49, - 81, -1, 81, -1, 88, 11, 91, 49, 84, -1, - 85, 49, 83, -1, 85, 49, 91, 49, 83, -1, - 49, 79, -1, 79, -1, 61, -1, 65, -1, 80, - 49, 78, -1, 80, 49, 78, 46, 35, -1, 80, - 49, 78, -1, 80, 49, 78, 46, 36, -1, 80, - 49, -1, 80, -1, 80, 49, 78, -1, 82, 49, - 78, 49, 91, -1, 84, 49, 78, 49, 82, -1, - -1, 84, -1, 85, 49, 84, -1, 85, 49, 91, - 49, 84, -1, 82, -1, 85, -1, 81, -1, 87, - -1, 10, 82, -1, 10, 86, -1, 82, -1, 86, - -1, 78, -1, 84, -1, 91, 50, 32, 51, -1, - 43, 89, -1, 44, 89, -1, 34, -1, 37, -1, - 35, -1, 38, -1, 42, -1, 36, -1, 39, -1, - 52, 92, -1, 52, 91, -1, 52, 88, -1, 52, - 41, -1, 52, 40, -1, 52, 50, 40, 51, -1, - 52, 50, 9, 40, 51, -1, 52, 9, 40, -1, - 86, -1, 87, -1, 91, -1, 91, 50, 35, 51, - -1, 91, 50, 42, 51, -1, 91, 50, 36, 51, - -1, 91, 50, 35, 10, 91, 51, -1, 91, 50, - 35, 51, 50, 35, 10, 91, 51, -1, 50, 35, - 51, -1, 50, 42, 51, -1, 50, 35, 10, 91, - 51, -1, 50, 35, 51, 50, 35, 10, 91, 51, - -1, 88, -1, 88, 50, 35, 10, 91, 51, -1, - 43, 89, 50, 90, 51, -1, 43, 6, 7, 89, - 50, 33, 51, -1, -1, 8, 91, -1, 9, 91, - -1, 33, -1, 42, -1, 31, -1, 30, -1, 45, - -1, 9, 91, -1, 8, 91, -1, 53, 91, -1, - 50, 93, 51, -1, 30, -1, 9, 30, -1, 30, - 9, 30, -1, 9, 30, 9, 30, -1, 91, -1, - 93, 8, 93, -1, 93, 9, 93, -1, 93, 10, - 93, -1, 93, 11, 93, -1, 93, 12, 93, -1, - 93, 6, 6, 93, -1, 93, 7, 7, 93, -1, - 93, 5, 93, -1, 93, 4, 93, -1, 93, 3, - 93, -1 + 81, -1, 81, -1, 84, 49, 81, -1, 88, 11, + 91, 49, 84, -1, 85, 49, 83, -1, 85, 49, + 91, 49, 83, -1, 49, 79, -1, 79, -1, 61, + -1, 65, -1, 80, 49, 78, -1, 80, 49, 78, + 46, 35, -1, 80, 49, 78, -1, 80, 49, 78, + 46, 36, -1, 80, 49, -1, 80, -1, 80, 49, + 78, -1, 82, 49, 78, 49, 91, -1, 84, 49, + 78, 49, 82, -1, -1, 84, -1, 85, 49, 84, + -1, 85, 49, 91, 49, 84, -1, 82, -1, 85, + -1, 81, -1, 87, -1, 10, 82, -1, 10, 86, + -1, 82, -1, 86, -1, 78, -1, 84, -1, 91, + 50, 32, 51, -1, 43, 89, -1, 44, 89, -1, + 34, -1, 37, -1, 35, -1, 38, -1, 42, -1, + 36, -1, 39, -1, 52, 92, -1, 52, 91, -1, + 52, 88, -1, 52, 41, -1, 52, 40, -1, 52, + 50, 40, 51, -1, 52, 50, 9, 40, 51, -1, + 52, 9, 40, -1, 86, -1, 87, -1, 91, -1, + 91, 50, 35, 51, -1, 91, 50, 42, 51, -1, + 91, 50, 36, 51, -1, 91, 50, 35, 10, 91, + 51, -1, 91, 50, 35, 51, 50, 35, 10, 91, + 51, -1, 50, 35, 51, -1, 50, 42, 51, -1, + 50, 35, 10, 91, 51, -1, 50, 35, 51, 50, + 35, 10, 91, 51, -1, 88, -1, 88, 50, 35, + 10, 91, 51, -1, 43, 89, 50, 90, 51, -1, + 43, 6, 7, 89, 50, 33, 51, -1, -1, 8, + 91, -1, 9, 91, -1, 33, -1, 42, -1, 31, + -1, 30, -1, 45, -1, 9, 91, -1, 8, 91, + -1, 53, 91, -1, 50, 93, 51, -1, 30, -1, + 9, 30, -1, 30, 9, 30, -1, 9, 30, 9, + 30, -1, 91, -1, 93, 8, 93, -1, 93, 9, + 93, -1, 93, 10, 93, -1, 93, 11, 93, -1, + 93, 12, 93, -1, 93, 6, 6, 93, -1, 93, + 7, 7, 93, -1, 93, 5, 93, -1, 93, 4, + 93, -1, 93, 3, 93, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -537,16 +537,16 @@ static const yytype_uint16 yyrline[] = 86, 87, 90, 95, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 120, 124, 131, 138, 145, 150, 157, 162, 169, - 174, 181, 189, 194, 202, 207, 214, 215, 218, 223, - 233, 238, 248, 253, 258, 265, 273, 283, 287, 294, - 299, 307, 308, 311, 312, 313, 317, 321, 322, 325, - 326, 329, 335, 344, 353, 358, 363, 368, 373, 378, - 383, 389, 397, 403, 414, 420, 426, 432, 438, 446, - 447, 450, 456, 462, 468, 474, 483, 492, 497, 502, - 510, 520, 524, 533, 540, 549, 552, 556, 562, 563, - 567, 570, 571, 575, 579, 583, 587, 593, 597, 601, - 606, 613, 614, 618, 622, 626, 630, 634, 638, 642, - 646, 650 + 174, 179, 186, 194, 199, 207, 212, 219, 220, 223, + 228, 238, 243, 253, 258, 263, 270, 278, 288, 292, + 299, 304, 312, 313, 316, 317, 318, 322, 326, 327, + 330, 331, 334, 340, 349, 358, 363, 368, 373, 378, + 383, 388, 394, 402, 408, 419, 425, 431, 437, 443, + 451, 452, 455, 461, 467, 473, 479, 488, 497, 502, + 507, 515, 525, 529, 538, 545, 554, 557, 561, 567, + 568, 572, 575, 576, 580, 584, 588, 592, 598, 602, + 606, 611, 618, 619, 623, 627, 631, 635, 639, 643, + 647, 651, 655 }; #endif @@ -591,16 +591,16 @@ static const yytype_uint8 yyr1[] = 57, 57, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 62, 63, 64, 64, 65, 65, 66, - 66, 67, 68, 68, 69, 69, 70, 70, 71, 71, - 72, 72, 73, 73, 73, 74, 75, 76, 76, 77, - 77, 78, 78, 79, 79, 79, 79, 79, 79, 80, - 80, 81, 81, 81, 82, 82, 82, 82, 82, 82, - 82, 83, 84, 84, 84, 84, 84, 84, 84, 85, - 85, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 87, 87, 88, 88, 89, 89, 89, 90, 90, - 90, 91, 91, 91, 91, 91, 91, 92, 92, 92, - 92, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93 + 66, 66, 67, 68, 68, 69, 69, 70, 70, 71, + 71, 72, 72, 73, 73, 73, 74, 75, 76, 76, + 77, 77, 78, 78, 79, 79, 79, 79, 79, 79, + 80, 80, 81, 81, 81, 82, 82, 82, 82, 82, + 82, 82, 83, 84, 84, 84, 84, 84, 84, 84, + 85, 85, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 87, 87, 88, 88, 89, 89, 89, 90, + 90, 90, 91, 91, 91, 91, 91, 91, 92, 92, + 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -610,16 +610,16 @@ static const yytype_uint8 yyr2[] = 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 3, 3, 2, 1, 2, 1, 2, - 1, 5, 3, 5, 2, 1, 1, 1, 3, 5, - 3, 5, 2, 1, 3, 5, 5, 0, 1, 3, - 5, 1, 1, 1, 1, 2, 2, 1, 1, 1, - 1, 4, 2, 2, 1, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 4, 5, 3, 1, - 1, 1, 4, 4, 4, 6, 9, 3, 3, 5, - 8, 1, 6, 5, 7, 0, 2, 2, 1, 1, - 1, 1, 1, 2, 2, 2, 3, 1, 2, 3, - 4, 1, 3, 3, 3, 3, 3, 4, 4, 3, - 3, 3 + 1, 3, 5, 3, 5, 2, 1, 1, 1, 3, + 5, 3, 5, 2, 1, 3, 5, 5, 0, 1, + 3, 5, 1, 1, 1, 1, 2, 2, 1, 1, + 1, 1, 4, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 4, 5, 3, + 1, 1, 1, 4, 4, 4, 6, 9, 3, 3, + 5, 8, 1, 6, 5, 7, 0, 2, 2, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 1, 2, + 3, 4, 1, 3, 3, 3, 3, 3, 4, 4, + 3, 3, 3 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state @@ -629,39 +629,40 @@ static const yytype_uint8 yydefact[] = { 2, 3, 1, 0, 0, 31, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 57, 0, 0, 0, 9, 4, 0, 11, 32, - 14, 0, 0, 111, 74, 76, 79, 75, 77, 80, - 78, 105, 112, 0, 0, 0, 15, 38, 61, 62, - 89, 90, 101, 91, 0, 16, 69, 36, 70, 17, - 0, 18, 0, 0, 105, 105, 0, 22, 45, 63, - 67, 68, 64, 91, 20, 0, 32, 46, 47, 23, - 105, 0, 0, 19, 40, 0, 21, 0, 30, 0, - 24, 0, 25, 0, 26, 53, 27, 0, 28, 0, - 29, 58, 7, 0, 5, 0, 10, 114, 113, 0, - 0, 0, 0, 37, 0, 0, 121, 0, 115, 0, - 0, 0, 85, 84, 0, 83, 82, 35, 0, 0, - 65, 66, 72, 73, 44, 0, 0, 72, 39, 0, - 0, 0, 0, 0, 52, 0, 0, 0, 12, 0, - 13, 105, 106, 107, 0, 0, 97, 98, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 116, 0, - 0, 0, 0, 88, 0, 0, 33, 34, 0, 0, - 0, 42, 0, 59, 0, 48, 50, 54, 0, 0, - 8, 6, 0, 110, 108, 109, 0, 0, 0, 131, - 130, 129, 0, 0, 122, 123, 124, 125, 126, 0, - 0, 92, 94, 93, 0, 86, 71, 0, 0, 117, - 81, 0, 0, 0, 0, 0, 0, 0, 103, 99, - 0, 127, 128, 0, 0, 0, 87, 41, 118, 0, - 43, 60, 49, 51, 55, 56, 0, 0, 102, 95, - 0, 0, 119, 104, 0, 0, 120, 100, 0, 96 + 0, 58, 0, 0, 0, 9, 4, 0, 11, 32, + 14, 0, 0, 112, 75, 77, 80, 76, 78, 81, + 79, 106, 113, 0, 0, 0, 15, 38, 62, 63, + 90, 91, 102, 92, 0, 16, 70, 36, 71, 17, + 0, 18, 0, 0, 106, 106, 0, 22, 46, 64, + 68, 69, 65, 92, 20, 0, 32, 47, 48, 23, + 106, 0, 0, 19, 40, 0, 0, 21, 0, 30, + 0, 24, 0, 25, 0, 26, 54, 27, 0, 28, + 0, 29, 59, 7, 0, 5, 0, 10, 115, 114, + 0, 0, 0, 0, 37, 0, 0, 122, 0, 116, + 0, 0, 0, 86, 85, 0, 84, 83, 35, 0, + 0, 66, 67, 73, 74, 45, 0, 0, 73, 39, + 0, 0, 0, 0, 0, 0, 53, 0, 0, 0, + 12, 0, 13, 106, 107, 108, 0, 0, 98, 99, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 117, 0, 0, 0, 0, 89, 0, 0, 33, 34, + 0, 0, 41, 0, 43, 0, 60, 0, 49, 51, + 55, 0, 0, 8, 6, 0, 111, 109, 110, 0, + 0, 0, 132, 131, 130, 0, 0, 123, 124, 125, + 126, 127, 0, 0, 93, 95, 94, 0, 87, 72, + 0, 0, 118, 82, 0, 0, 0, 0, 0, 0, + 0, 104, 100, 0, 128, 129, 0, 0, 0, 88, + 42, 119, 0, 44, 61, 50, 52, 56, 57, 0, + 0, 103, 96, 0, 0, 120, 105, 0, 0, 121, + 101, 0, 97 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 26, 149, 147, 27, 30, 59, 61, - 55, 46, 83, 74, 86, 67, 79, 90, 92, 94, - 96, 98, 100, 88, 56, 68, 57, 69, 48, 181, - 58, 49, 50, 51, 52, 112, 196, 53, 220, 117 + -1, 1, 3, 26, 151, 149, 27, 30, 59, 61, + 55, 46, 83, 74, 87, 67, 79, 91, 93, 95, + 97, 99, 101, 89, 56, 68, 57, 69, 48, 184, + 58, 49, 50, 51, 52, 113, 199, 53, 223, 118 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing @@ -669,41 +670,42 @@ static const yytype_int16 yydefgoto[] = #define YYPACT_NINF -97 static const yytype_int16 yypact[] = { - -97, 29, -97, 206, -39, -36, 254, 278, 278, 326, - 230, 15, 302, 59, 412, 412, 278, 278, 278, 82, - -20, -20, -22, -6, 2, -97, -97, 12, -97, -97, - -97, 476, 476, -97, -97, -97, -97, -97, -97, -97, - -97, 88, -97, 326, 366, 476, -97, -97, -97, -97, - -97, -97, 14, 16, 364, -97, -97, 33, -97, -97, - 39, -97, 41, 350, 88, 11, 152, -97, -97, -97, - -97, -97, -97, 31, -97, 51, 326, -97, -97, -97, - 11, 382, 476, -97, -97, 42, -97, 50, -97, 56, - -97, 58, -97, 61, -97, 64, -97, 66, -97, 79, - -97, -97, -97, 476, -97, 476, -97, -97, -97, 84, - 476, 476, 92, -97, 18, 78, -97, 129, -97, 108, - -5, 414, -97, -97, 421, -97, -97, -97, 326, 278, - -97, -97, 92, -97, -97, 13, 476, -97, -97, 112, - 428, 438, 326, 326, 326, 326, 326, 206, 527, 206, - 527, 11, -97, -97, 10, 476, 96, -97, 476, 476, - 476, 141, 144, 476, 476, 476, 476, 476, -97, 142, - 23, 126, 127, -97, 467, 128, -97, -97, 130, 110, - 8, -97, 114, -97, 134, 138, 139, -97, 149, 154, - -97, -97, 163, -97, -97, -97, 153, 165, 179, 536, - 493, 159, 476, 476, 65, 65, -97, -97, -97, 476, - 476, 168, -97, -97, 185, -97, -97, -20, 211, 233, - -97, 191, -20, 209, 210, 476, 82, 214, -97, -97, - 238, 200, 200, 201, 203, 220, -97, -97, 247, 227, - -97, -97, -97, -97, -97, -97, 207, 476, -97, -97, - 249, 231, -97, -97, 219, 476, -97, -97, 225, -97 + -97, 40, -97, 208, 5, -4, 140, 295, 295, 343, + 233, 15, 319, 381, 91, 91, 295, 295, 295, 222, + 24, 24, -15, 22, 14, -97, -97, 30, -97, -97, + -97, 486, 486, -97, -97, -97, -97, -97, -97, -97, + -97, 42, -97, 343, 406, 486, -97, -97, -97, -97, + -97, -97, 45, 48, 399, -97, -97, 20, -97, -97, + 67, -97, 68, 367, 42, 19, 271, -97, -97, -97, + -97, -97, -97, 87, -97, 127, 343, -97, -97, -97, + 19, 437, 486, -97, -97, 90, 92, -97, 94, -97, + 96, -97, 104, -97, 105, -97, 111, -97, 118, -97, + 119, -97, -97, -97, 486, -97, 486, -97, -97, -97, + 133, 486, 486, 121, -97, 8, 122, -97, 80, -97, + 134, 78, 413, -97, -97, 446, -97, -97, -97, 343, + 295, -97, -97, 121, -97, -97, 7, 486, -97, -97, + 437, 148, 453, 462, 343, 343, 343, 343, 343, 208, + 284, 208, 284, 19, -97, -97, -1, 486, 131, -97, + 486, 486, 486, 166, 177, 486, 486, 486, 486, 486, + -97, 176, 10, 136, 137, -97, 480, 141, -97, -97, + 143, 142, -97, 16, -97, 149, -97, 150, 151, 154, + -97, 152, 155, -97, -97, 156, -97, -97, -97, 157, + 159, 170, 99, 535, 542, 486, 486, 26, 26, -97, + -97, -97, 486, 486, 161, -97, -97, 162, -97, -97, + 24, 184, 198, -97, 163, 24, 181, 183, 486, 222, + 205, -97, -97, 229, 114, 114, 193, 194, 211, -97, + -97, 238, 219, -97, -97, -97, -97, -97, -97, 199, + 486, -97, -97, 244, 232, -97, -97, 214, 486, -97, + -97, 215, -97 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -97, -97, -97, -96, -97, -97, -97, 259, -97, -97, - -97, 265, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, 30, 212, -2, -11, -9, 60, - -16, 32, -3, -1, 7, -53, -97, -10, -97, -80 + -97, -97, -97, -96, -97, -97, -97, 261, -97, -97, + -97, 262, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, 17, 218, -2, -11, -9, 61, + -8, 51, 1, -3, -7, -56, -97, -10, -97, -87 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If @@ -713,120 +715,122 @@ static const yytype_int16 yypgoto[] = #define YYTABLE_NINF -1 static const yytype_uint16 yytable[] = { - 73, 70, 84, 85, 99, 101, 60, 71, 28, 72, - 97, 132, 133, 29, 91, 93, 95, 218, 75, 110, - 111, 107, 108, 148, 102, 150, 103, 137, 155, 2, - 170, 171, 54, 210, 116, 118, 47, 172, 219, 62, - 104, 193, 47, 194, 126, 178, 87, 89, 170, 171, - 105, 190, 195, 191, 130, 172, 73, 70, 41, 106, - 131, 125, 136, 71, 119, 72, 120, 31, 32, 156, - 138, 85, 116, 113, 211, 165, 166, 167, 199, 200, - 201, 135, 127, 204, 205, 206, 207, 208, 128, 33, - 129, 151, 139, 116, 109, 116, 110, 111, 192, 140, - 152, 153, 80, 65, 42, 141, 113, 142, 81, 82, - 143, 108, 45, 144, 116, 145, 34, 35, 36, 37, - 38, 39, 231, 232, 40, 183, 179, 177, 146, 157, - 182, 184, 158, 159, 160, 161, 162, 163, 164, 165, - 166, 167, 154, 169, 178, 197, 198, 202, 116, 116, - 116, 203, 209, 116, 116, 116, 116, 116, 176, 217, - 31, 32, 63, 221, 108, 161, 162, 163, 164, 165, - 166, 167, 185, 186, 187, 188, 189, 212, 213, 215, - 168, 216, 33, 222, 223, 224, 34, 35, 36, 37, - 38, 39, 116, 116, 40, 64, 65, 42, 225, 233, - 234, 237, 44, 226, 228, 45, 241, 4, 163, 164, - 165, 166, 167, 227, 230, 244, 229, 245, 235, 5, - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 236, 254, 31, 32, - 63, 238, 239, 180, 242, 258, 243, 246, 247, 22, - 23, 24, 248, 25, 249, 250, 251, 252, 253, 255, - 33, 256, 31, 32, 34, 35, 36, 37, 38, 39, - 257, 77, 40, 64, 65, 42, 259, 78, 134, 66, - 44, 240, 0, 45, 33, 0, 31, 32, 34, 35, - 36, 37, 38, 39, 0, 0, 40, 41, 0, 42, - 0, 0, 0, 43, 44, 0, 0, 45, 33, 0, - 31, 32, 34, 35, 36, 37, 38, 39, 0, 0, - 40, 41, 0, 42, 0, 0, 0, 0, 44, 0, - 54, 45, 33, 0, 31, 32, 34, 35, 36, 37, - 38, 39, 0, 0, 40, 41, 0, 42, 0, 0, - 0, 76, 44, 0, 0, 45, 33, 0, 31, 32, - 34, 35, 36, 37, 38, 39, 0, 0, 40, 41, - 0, 42, 31, 121, 31, 32, 44, 0, 0, 45, - 33, 0, 0, 0, 34, 35, 36, 37, 38, 39, - 31, 32, 40, 0, 33, 42, 33, 0, 0, 0, - 44, 114, 0, 45, 122, 123, 0, 41, 115, 42, - 0, 42, 33, 0, 124, 0, 82, 45, 0, 45, - 31, 32, 31, 32, 0, 80, 65, 42, 0, 31, - 174, 0, 82, 0, 0, 45, 31, 32, 0, 0, - 0, 0, 33, 0, 33, 0, 31, 32, 0, 0, - 0, 33, 0, 0, 173, 41, 0, 42, 33, 42, - 0, 175, 44, 0, 82, 45, 42, 45, 33, 0, - 0, 82, 0, 42, 45, 31, 32, 0, 82, 0, - 180, 45, 0, 42, 31, 32, 0, 0, 82, 0, - 54, 45, 0, 0, 0, 0, 0, 33, 160, 161, - 162, 163, 164, 165, 166, 167, 33, 214, 0, 0, - 0, 0, 42, 0, 0, 0, 0, 82, 0, 0, - 45, 42, 0, 0, 0, 0, 82, 0, 0, 45, - 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, - 159, 160, 161, 162, 163, 164, 165, 166, 167 + 73, 70, 84, 86, 75, 85, 60, 72, 133, 134, + 98, 71, 100, 102, 92, 94, 96, 150, 157, 152, + 213, 108, 109, 47, 138, 221, 62, 111, 112, 47, + 196, 103, 197, 104, 117, 119, 167, 168, 169, 180, + 2, 198, 172, 173, 127, 29, 222, 126, 110, 174, + 111, 112, 28, 193, 131, 194, 73, 70, 41, 158, + 114, 214, 106, 72, 132, 88, 90, 71, 105, 128, + 139, 86, 117, 202, 203, 204, 54, 107, 207, 208, + 209, 210, 211, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 114, 117, 120, 117, 195, 121, 31, + 32, 154, 155, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 109, 172, 173, 117, 129, 130, 234, 235, + 174, 33, 165, 166, 167, 168, 169, 181, 179, 182, + 86, 170, 185, 187, 41, 186, 42, 136, 137, 140, + 153, 44, 141, 142, 45, 143, 178, 200, 31, 32, + 117, 117, 117, 144, 145, 117, 117, 117, 117, 117, + 146, 188, 189, 190, 191, 192, 109, 147, 148, 171, + 33, 156, 205, 159, 34, 35, 36, 37, 38, 39, + 180, 201, 40, 41, 206, 42, 212, 215, 216, 43, + 44, 220, 218, 45, 219, 117, 117, 226, 224, 225, + 227, 228, 236, 237, 229, 233, 230, 242, 231, 4, + 232, 238, 240, 239, 241, 183, 245, 244, 247, 246, + 248, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 249, 250, + 257, 31, 32, 63, 251, 252, 253, 254, 261, 255, + 256, 22, 23, 24, 258, 25, 34, 35, 36, 37, + 38, 39, 259, 33, 40, 260, 262, 34, 35, 36, + 37, 38, 39, 77, 78, 40, 64, 65, 42, 31, + 32, 63, 66, 44, 135, 243, 45, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 0, 0, 0, + 0, 33, 0, 31, 32, 34, 35, 36, 37, 38, + 39, 0, 0, 40, 64, 65, 42, 0, 0, 0, + 0, 44, 0, 0, 45, 33, 0, 31, 32, 34, + 35, 36, 37, 38, 39, 0, 0, 40, 41, 0, + 42, 0, 0, 0, 0, 44, 0, 54, 45, 33, + 0, 31, 32, 34, 35, 36, 37, 38, 39, 0, + 0, 40, 41, 0, 42, 0, 0, 0, 76, 44, + 0, 0, 45, 33, 0, 31, 32, 34, 35, 36, + 37, 38, 39, 0, 0, 40, 41, 0, 42, 31, + 32, 0, 0, 44, 0, 0, 45, 33, 0, 0, + 0, 34, 35, 36, 37, 38, 39, 31, 122, 40, + 0, 33, 42, 0, 31, 32, 0, 44, 0, 0, + 45, 31, 32, 0, 80, 65, 42, 0, 0, 33, + 81, 82, 0, 54, 45, 0, 33, 0, 0, 123, + 124, 115, 41, 33, 42, 31, 32, 0, 116, 125, + 0, 42, 45, 175, 31, 176, 82, 0, 42, 45, + 0, 31, 32, 82, 0, 0, 45, 33, 0, 0, + 31, 32, 0, 0, 0, 0, 33, 0, 0, 0, + 80, 65, 42, 33, 0, 0, 177, 82, 31, 32, + 45, 42, 33, 0, 31, 32, 82, 0, 42, 45, + 0, 0, 0, 82, 0, 183, 45, 42, 0, 0, + 33, 0, 82, 0, 54, 45, 33, 0, 0, 0, + 217, 0, 0, 0, 0, 42, 0, 0, 0, 0, + 82, 42, 0, 45, 0, 0, 82, 0, 0, 45, + 162, 163, 164, 165, 166, 167, 168, 169, 163, 164, + 165, 166, 167, 168, 169 }; static const yytype_int16 yycheck[] = { - 10, 10, 13, 13, 20, 21, 8, 10, 47, 10, - 19, 64, 65, 49, 16, 17, 18, 9, 11, 8, - 9, 31, 32, 103, 46, 105, 48, 80, 10, 0, - 35, 36, 52, 10, 44, 45, 6, 42, 30, 9, - 46, 31, 12, 33, 54, 32, 14, 15, 35, 36, - 48, 147, 42, 149, 63, 42, 66, 66, 43, 47, - 63, 54, 11, 66, 50, 66, 50, 8, 9, 51, - 81, 81, 82, 43, 51, 10, 11, 12, 158, 159, - 160, 50, 49, 163, 164, 165, 166, 167, 49, 30, - 49, 7, 50, 103, 6, 105, 8, 9, 151, 49, - 110, 111, 43, 44, 45, 49, 76, 49, 49, 50, - 49, 121, 53, 49, 124, 49, 34, 35, 36, 37, - 38, 39, 202, 203, 42, 141, 136, 129, 49, 51, - 140, 141, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 50, 35, 32, 155, 50, 6, 158, 159, - 160, 7, 10, 163, 164, 165, 166, 167, 128, 49, - 8, 9, 10, 49, 174, 6, 7, 8, 9, 10, - 11, 12, 142, 143, 144, 145, 146, 51, 51, 51, - 51, 51, 30, 49, 46, 46, 34, 35, 36, 37, - 38, 39, 202, 203, 42, 43, 44, 45, 49, 209, - 210, 217, 50, 49, 51, 53, 222, 1, 8, 9, - 10, 11, 12, 50, 35, 225, 51, 226, 50, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 51, 247, 8, 9, - 10, 30, 9, 52, 35, 255, 36, 33, 10, 43, - 44, 45, 51, 47, 51, 35, 9, 30, 51, 10, - 30, 30, 8, 9, 34, 35, 36, 37, 38, 39, - 51, 12, 42, 43, 44, 45, 51, 12, 66, 49, - 50, 221, -1, 53, 30, -1, 8, 9, 34, 35, - 36, 37, 38, 39, -1, -1, 42, 43, -1, 45, - -1, -1, -1, 49, 50, -1, -1, 53, 30, -1, - 8, 9, 34, 35, 36, 37, 38, 39, -1, -1, - 42, 43, -1, 45, -1, -1, -1, -1, 50, -1, - 52, 53, 30, -1, 8, 9, 34, 35, 36, 37, - 38, 39, -1, -1, 42, 43, -1, 45, -1, -1, - -1, 49, 50, -1, -1, 53, 30, -1, 8, 9, - 34, 35, 36, 37, 38, 39, -1, -1, 42, 43, - -1, 45, 8, 9, 8, 9, 50, -1, -1, 53, - 30, -1, -1, -1, 34, 35, 36, 37, 38, 39, - 8, 9, 42, -1, 30, 45, 30, -1, -1, -1, - 50, 35, -1, 53, 40, 41, -1, 43, 42, 45, - -1, 45, 30, -1, 50, -1, 50, 53, -1, 53, - 8, 9, 8, 9, -1, 43, 44, 45, -1, 8, - 9, -1, 50, -1, -1, 53, 8, 9, -1, -1, - -1, -1, 30, -1, 30, -1, 8, 9, -1, -1, - -1, 30, -1, -1, 40, 43, -1, 45, 30, 45, - -1, 40, 50, -1, 50, 53, 45, 53, 30, -1, - -1, 50, -1, 45, 53, 8, 9, -1, 50, -1, - 52, 53, -1, 45, 8, 9, -1, -1, 50, -1, - 52, 53, -1, -1, -1, -1, -1, 30, 5, 6, - 7, 8, 9, 10, 11, 12, 30, 40, -1, -1, - -1, -1, 45, -1, -1, -1, -1, 50, -1, -1, - 53, 45, -1, -1, -1, -1, 50, -1, -1, 53, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 4, 5, 6, 7, 8, 9, 10, 11, 12 + 10, 10, 13, 13, 11, 13, 8, 10, 64, 65, + 19, 10, 20, 21, 16, 17, 18, 104, 10, 106, + 10, 31, 32, 6, 80, 9, 9, 8, 9, 12, + 31, 46, 33, 48, 44, 45, 10, 11, 12, 32, + 0, 42, 35, 36, 54, 49, 30, 54, 6, 42, + 8, 9, 47, 149, 63, 151, 66, 66, 43, 51, + 43, 51, 48, 66, 63, 14, 15, 66, 46, 49, + 81, 81, 82, 160, 161, 162, 52, 47, 165, 166, + 167, 168, 169, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 76, 104, 50, 106, 153, 50, 8, + 9, 111, 112, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 122, 35, 36, 125, 49, 49, 205, 206, + 42, 30, 8, 9, 10, 11, 12, 137, 130, 140, + 140, 51, 142, 143, 43, 143, 45, 50, 11, 49, + 7, 50, 50, 49, 53, 49, 129, 157, 8, 9, + 160, 161, 162, 49, 49, 165, 166, 167, 168, 169, + 49, 144, 145, 146, 147, 148, 176, 49, 49, 35, + 30, 50, 6, 51, 34, 35, 36, 37, 38, 39, + 32, 50, 42, 43, 7, 45, 10, 51, 51, 49, + 50, 49, 51, 53, 51, 205, 206, 46, 49, 49, + 46, 49, 212, 213, 49, 35, 50, 9, 51, 1, + 51, 50, 220, 51, 30, 52, 35, 225, 228, 36, + 229, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 33, 10, + 250, 8, 9, 10, 51, 51, 35, 9, 258, 30, + 51, 43, 44, 45, 10, 47, 34, 35, 36, 37, + 38, 39, 30, 30, 42, 51, 51, 34, 35, 36, + 37, 38, 39, 12, 12, 42, 43, 44, 45, 8, + 9, 10, 49, 50, 66, 224, 53, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, + -1, 30, -1, 8, 9, 34, 35, 36, 37, 38, + 39, -1, -1, 42, 43, 44, 45, -1, -1, -1, + -1, 50, -1, -1, 53, 30, -1, 8, 9, 34, + 35, 36, 37, 38, 39, -1, -1, 42, 43, -1, + 45, -1, -1, -1, -1, 50, -1, 52, 53, 30, + -1, 8, 9, 34, 35, 36, 37, 38, 39, -1, + -1, 42, 43, -1, 45, -1, -1, -1, 49, 50, + -1, -1, 53, 30, -1, 8, 9, 34, 35, 36, + 37, 38, 39, -1, -1, 42, 43, -1, 45, 8, + 9, -1, -1, 50, -1, -1, 53, 30, -1, -1, + -1, 34, 35, 36, 37, 38, 39, 8, 9, 42, + -1, 30, 45, -1, 8, 9, -1, 50, -1, -1, + 53, 8, 9, -1, 43, 44, 45, -1, -1, 30, + 49, 50, -1, 52, 53, -1, 30, -1, -1, 40, + 41, 35, 43, 30, 45, 8, 9, -1, 42, 50, + -1, 45, 53, 40, 8, 9, 50, -1, 45, 53, + -1, 8, 9, 50, -1, -1, 53, 30, -1, -1, + 8, 9, -1, -1, -1, -1, 30, -1, -1, -1, + 43, 44, 45, 30, -1, -1, 40, 50, 8, 9, + 53, 45, 30, -1, 8, 9, 50, -1, 45, 53, + -1, -1, -1, 50, -1, 52, 53, 45, -1, -1, + 30, -1, 50, -1, 52, 53, 30, -1, -1, -1, + 40, -1, -1, -1, -1, 45, -1, -1, -1, -1, + 50, 45, -1, 53, -1, -1, 50, -1, -1, 53, + 5, 6, 7, 8, 9, 10, 11, 12, 6, 7, + 8, 9, 10, 11, 12 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing @@ -841,24 +845,25 @@ static const yytype_uint8 yystos[] = 86, 87, 88, 91, 52, 64, 78, 80, 84, 62, 80, 63, 78, 10, 43, 44, 49, 69, 79, 81, 82, 86, 87, 91, 67, 88, 49, 61, 65, 70, - 43, 49, 50, 66, 81, 91, 68, 85, 77, 85, - 71, 80, 72, 80, 73, 80, 74, 82, 75, 84, - 76, 84, 46, 48, 46, 48, 47, 91, 91, 6, - 8, 9, 89, 78, 35, 42, 91, 93, 91, 50, - 50, 9, 40, 41, 50, 88, 91, 49, 49, 49, - 82, 86, 89, 89, 79, 50, 11, 89, 81, 50, - 49, 49, 49, 49, 49, 49, 49, 59, 93, 58, - 93, 7, 91, 91, 50, 10, 51, 51, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 51, 35, - 35, 36, 42, 40, 9, 40, 78, 80, 32, 91, - 52, 83, 91, 84, 91, 78, 78, 78, 78, 78, - 57, 57, 89, 31, 33, 42, 90, 91, 50, 93, - 93, 93, 6, 7, 93, 93, 93, 93, 93, 10, - 10, 51, 51, 51, 40, 51, 51, 49, 9, 30, - 92, 49, 49, 46, 46, 49, 49, 50, 51, 51, - 35, 93, 93, 91, 91, 50, 51, 84, 30, 9, - 83, 84, 35, 36, 91, 82, 33, 10, 51, 51, - 35, 9, 30, 51, 91, 10, 30, 51, 91, 51 + 43, 49, 50, 66, 81, 84, 91, 68, 85, 77, + 85, 71, 80, 72, 80, 73, 80, 74, 82, 75, + 84, 76, 84, 46, 48, 46, 48, 47, 91, 91, + 6, 8, 9, 89, 78, 35, 42, 91, 93, 91, + 50, 50, 9, 40, 41, 50, 88, 91, 49, 49, + 49, 82, 86, 89, 89, 79, 50, 11, 89, 81, + 49, 50, 49, 49, 49, 49, 49, 49, 49, 59, + 93, 58, 93, 7, 91, 91, 50, 10, 51, 51, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 51, 35, 35, 36, 42, 40, 9, 40, 78, 80, + 32, 91, 81, 52, 83, 91, 84, 91, 78, 78, + 78, 78, 78, 57, 57, 89, 31, 33, 42, 90, + 91, 50, 93, 93, 93, 6, 7, 93, 93, 93, + 93, 93, 10, 10, 51, 51, 51, 40, 51, 51, + 49, 9, 30, 92, 49, 49, 46, 46, 49, 49, + 50, 51, 51, 35, 93, 93, 91, 91, 50, 51, + 84, 30, 9, 83, 84, 35, 36, 91, 82, 33, + 10, 51, 51, 35, 9, 30, 51, 91, 10, 30, + 51, 91, 51 }; #define yyerrok (yyerrstatus = 0) @@ -1879,7 +1884,15 @@ yyreduce: break; case 41: -#line 182 "a.y" +#line 180 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 42: +#line 187 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1887,16 +1900,16 @@ yyreduce: } break; - case 42: -#line 190 "a.y" + case 43: +#line 195 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 43: -#line 195 "a.y" + case 44: +#line 200 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1904,32 +1917,32 @@ yyreduce: } break; - case 44: -#line 203 "a.y" + case 45: +#line 208 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 45: -#line 208 "a.y" + case 46: +#line 213 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 48: -#line 219 "a.y" + case 49: +#line 224 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 49: -#line 224 "a.y" + case 50: +#line 229 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1939,16 +1952,16 @@ yyreduce: } break; - case 50: -#line 234 "a.y" + case 51: +#line 239 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 51: -#line 239 "a.y" + case 52: +#line 244 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1958,32 +1971,32 @@ yyreduce: } break; - case 52: -#line 249 "a.y" + case 53: +#line 254 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 53: -#line 254 "a.y" + case 54: +#line 259 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 54: -#line 259 "a.y" + case 55: +#line 264 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 55: -#line 266 "a.y" + case 56: +#line 271 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1991,8 +2004,8 @@ yyreduce: } break; - case 56: -#line 274 "a.y" + case 57: +#line 279 "a.y" { (yyval.gen2).from = (yyvsp[(3) - (5)].gen); (yyval.gen2).to = (yyvsp[(5) - (5)].gen); @@ -2002,32 +2015,32 @@ yyreduce: } break; - case 57: -#line 283 "a.y" + case 58: +#line 288 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 58: -#line 288 "a.y" + case 59: +#line 293 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 59: -#line 295 "a.y" + case 60: +#line 300 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 60: -#line 300 "a.y" + case 61: +#line 305 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -2035,22 +2048,22 @@ yyreduce: } break; - case 65: -#line 314 "a.y" + case 66: +#line 319 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 66: -#line 318 "a.y" + case 67: +#line 323 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 71: -#line 330 "a.y" + case 72: +#line 335 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2058,8 +2071,8 @@ yyreduce: } break; - case 72: -#line 336 "a.y" + case 73: +#line 341 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2070,8 +2083,8 @@ yyreduce: } break; - case 73: -#line 345 "a.y" + case 74: +#line 350 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2080,14 +2093,6 @@ yyreduce: } break; - case 74: -#line 354 "a.y" - { - (yyval.gen) = nullgen; - (yyval.gen).type = (yyvsp[(1) - (1)].lval); - } - break; - case 75: #line 359 "a.y" { @@ -2116,7 +2121,7 @@ yyreduce: #line 374 "a.y" { (yyval.gen) = nullgen; - (yyval.gen).type = D_SP; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; @@ -2124,7 +2129,7 @@ yyreduce: #line 379 "a.y" { (yyval.gen) = nullgen; - (yyval.gen).type = (yyvsp[(1) - (1)].lval); + (yyval.gen).type = D_SP; } break; @@ -2137,16 +2142,15 @@ yyreduce: break; case 81: -#line 390 "a.y" +#line 389 "a.y" { (yyval.gen) = nullgen; - (yyval.gen).type = D_CONST; - (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; case 82: -#line 398 "a.y" +#line 395 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2155,7 +2159,16 @@ yyreduce: break; case 83: -#line 404 "a.y" +#line 403 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_CONST; + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 84: +#line 409 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).index = (yyvsp[(2) - (2)].gen).type; @@ -2168,8 +2181,8 @@ yyreduce: } break; - case 84: -#line 415 "a.y" + case 85: +#line 420 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2177,8 +2190,8 @@ yyreduce: } break; - case 85: -#line 421 "a.y" + case 86: +#line 426 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2186,8 +2199,8 @@ yyreduce: } break; - case 86: -#line 427 "a.y" + case 87: +#line 432 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2195,8 +2208,8 @@ yyreduce: } break; - case 87: -#line 433 "a.y" + case 88: +#line 438 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2204,8 +2217,8 @@ yyreduce: } break; - case 88: -#line 439 "a.y" + case 89: +#line 444 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2213,8 +2226,8 @@ yyreduce: } break; - case 91: -#line 451 "a.y" + case 92: +#line 456 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2222,8 +2235,8 @@ yyreduce: } break; - case 92: -#line 457 "a.y" + case 93: +#line 462 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2231,8 +2244,8 @@ yyreduce: } break; - case 93: -#line 463 "a.y" + case 94: +#line 468 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; @@ -2240,8 +2253,8 @@ yyreduce: } break; - case 94: -#line 469 "a.y" + case 95: +#line 474 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2249,8 +2262,8 @@ yyreduce: } break; - case 95: -#line 475 "a.y" + case 96: +#line 480 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2261,8 +2274,8 @@ yyreduce: } break; - case 96: -#line 484 "a.y" + case 97: +#line 489 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); @@ -2273,24 +2286,24 @@ yyreduce: } break; - case 97: -#line 493 "a.y" + case 98: +#line 498 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval); } break; - case 98: -#line 498 "a.y" + case 99: +#line 503 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; } break; - case 99: -#line 503 "a.y" + case 100: +#line 508 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2300,8 +2313,8 @@ yyreduce: } break; - case 100: -#line 511 "a.y" + case 101: +#line 516 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval); @@ -2311,15 +2324,15 @@ yyreduce: } break; - case 101: -#line 521 "a.y" + case 102: +#line 526 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); } break; - case 102: -#line 525 "a.y" + case 103: +#line 530 "a.y" { (yyval.gen) = (yyvsp[(1) - (6)].gen); (yyval.gen).index = (yyvsp[(3) - (6)].lval); @@ -2328,8 +2341,8 @@ yyreduce: } break; - case 103: -#line 534 "a.y" + case 104: +#line 539 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(4) - (5)].lval); @@ -2338,8 +2351,8 @@ yyreduce: } break; - case 104: -#line 541 "a.y" + case 105: +#line 546 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_STATIC; @@ -2348,164 +2361,164 @@ yyreduce: } break; - case 105: -#line 549 "a.y" + case 106: +#line 554 "a.y" { (yyval.lval) = 0; } break; - case 106: -#line 553 "a.y" + case 107: +#line 558 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 107: -#line 557 "a.y" + case 108: +#line 562 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 109: -#line 564 "a.y" + case 110: +#line 569 "a.y" { (yyval.lval) = D_AUTO; } break; - case 112: -#line 572 "a.y" + case 113: +#line 577 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 113: -#line 576 "a.y" + case 114: +#line 581 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 114: -#line 580 "a.y" + case 115: +#line 585 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 115: -#line 584 "a.y" + case 116: +#line 589 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 116: -#line 588 "a.y" + case 117: +#line 593 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 117: -#line 594 "a.y" + case 118: +#line 599 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].lval) & 0xffffffffLL; } break; - case 118: -#line 598 "a.y" + case 119: +#line 603 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval) & 0xffffffffLL; } break; - case 119: -#line 602 "a.y" + case 120: +#line 607 "a.y" { (yyval.lval) = ((yyvsp[(1) - (3)].lval) & 0xffffffffLL) + (((yyvsp[(3) - (3)].lval) & 0xffffLL) << 32); } break; - case 120: -#line 607 "a.y" + case 121: +#line 612 "a.y" { (yyval.lval) = (-(yyvsp[(2) - (4)].lval) & 0xffffffffLL) + (((yyvsp[(4) - (4)].lval) & 0xffffLL) << 32); } break; - case 122: -#line 615 "a.y" + case 123: +#line 620 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 123: -#line 619 "a.y" + case 124: +#line 624 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 124: -#line 623 "a.y" + case 125: +#line 628 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 125: -#line 627 "a.y" + case 126: +#line 632 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 126: -#line 631 "a.y" + case 127: +#line 636 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 127: -#line 635 "a.y" + case 128: +#line 640 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 128: -#line 639 "a.y" + case 129: +#line 644 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 129: -#line 643 "a.y" + case 130: +#line 648 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 130: -#line 647 "a.y" + case 131: +#line 652 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 131: -#line 651 "a.y" + case 132: +#line 656 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } @@ -2513,7 +2526,7 @@ yyreduce: /* Line 1267 of yacc.c. */ -#line 2517 "y.tab.c" +#line 2530 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index 71822586c..95400c445 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -265,6 +265,18 @@ cgen(Node *n, Node *nn) break; } } + if(n->op == OOR && l->op == OASHL && r->op == OLSHR + && l->right->op == OCONST && r->right->op == OCONST + && l->left->op == ONAME && r->left->op == ONAME + && l->left->sym == r->left->sym + && l->right->vconst + r->right->vconst == 8 * l->left->type->width) { + regalloc(&nod, l->left, nn); + cgen(l->left, &nod); + gopcode(OROTL, n->type, l->right, &nod); + gmove(&nod, nn); + regfree(&nod); + break; + } if(n->op == OADD && l->op == OASHL && l->right->op == OCONST && (r->op != OCONST || r->vconst < -128 || r->vconst > 127)) { c = l->right->vconst; @@ -1672,7 +1684,7 @@ copy: regsalloc(&nod2, nn); nn->type = t; - gins(AMOVL, &nod1, &nod2); + gins(AMOVQ, &nod1, &nod2); regfree(&nod1); nod2.type = typ(TIND, t); diff --git a/src/cmd/6c/doc.go b/src/cmd/6c/doc.go index 249a8ed80..e0a22e78b 100644 --- a/src/cmd/6c/doc.go +++ b/src/cmd/6c/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 6c is a version of the Plan 9 C compiler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2c + http://plan9.bell-labs.com/magic/man2html/1/8c Its target architecture is the x86-64, referred to by these tools as amd64. */ -package documentation +package main diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h index b0081abb5..d1133ee21 100644 --- a/src/cmd/6c/gc.h +++ b/src/cmd/6c/gc.h @@ -292,6 +292,7 @@ void gbranch(int); void patch(Prog*, int32); int sconst(Node*); void gpseudo(int, Sym*, Node*); +void gprefetch(Node*); /* * swt.c @@ -366,8 +367,6 @@ int BtoF(int32); #define D_HI D_NONE #define D_LO D_NONE -#define isregtype(t) ((t)>= D_AX && (t)<=D_R15) - /* * bound */ diff --git a/src/cmd/6c/list.c b/src/cmd/6c/list.c index 4293203c0..b5a60ac9a 100644 --- a/src/cmd/6c/list.c +++ b/src/cmd/6c/list.c @@ -93,7 +93,7 @@ Pconv(Fmt *fp) break; default: - sprint(str, "(%L) %A %D,%lD", + sprint(str, "(%L) %A %D,%D", p->lineno, p->as, &p->from, &p->to); break; } @@ -120,13 +120,12 @@ Dconv(Fmt *fp) i = a->type; if(fp->flags & FmtLong) { - if(i != D_CONST) { + if(i == D_CONST) + sprint(str, "$%lld-%lld", a->offset&0xffffffffLL, a->offset>>32); + else { // ATEXT dst is not constant sprint(str, "!!%D", a); - goto brk; } - sprint(str, "$%lld-%lld", a->offset&0xffffffffLL, - (a->offset>>32)&0xffffffffLL); goto brk; } @@ -138,7 +137,6 @@ Dconv(Fmt *fp) goto brk; } switch(i) { - default: if(a->offset) sprint(str, "$%lld,%R", a->offset, i); @@ -151,7 +149,7 @@ Dconv(Fmt *fp) break; case D_BRANCH: - sprint(str, "%lld(PC)", a->offset-pc); + sprint(str, "%lld", a->offset); break; case D_EXTERN: @@ -159,24 +157,21 @@ Dconv(Fmt *fp) break; case D_STATIC: - sprint(str, "%s<>+%lld(SB)", a->sym->name, - a->offset); + sprint(str, "%s<>+%lld(SB)", a->sym->name, a->offset); break; case D_AUTO: - if(a->sym) { + if(a->sym) sprint(str, "%s+%lld(SP)", a->sym->name, a->offset); - break; - } - sprint(str, "%lld(SP)", a->offset); + else + sprint(str, "%lld(SP)", a->offset); break; case D_PARAM: - if(a->sym) { + if(a->sym) sprint(str, "%s+%lld(FP)", a->sym->name, a->offset); - break; - } - sprint(str, "%lld(FP)", a->offset); + else + sprint(str, "%lld(FP)", a->offset); break; case D_CONST: @@ -210,7 +205,7 @@ conv: char* regstr[] = { - "AL", /* [D_AL] */ + "AL", /* [D_AL] */ "CL", "DL", "BL", @@ -227,7 +222,7 @@ char* regstr[] = "R14B", "R15B", - "AX", /* [D_AX] */ + "AX", /* [D_AX] */ "CX", "DX", "BX", @@ -249,7 +244,7 @@ char* regstr[] = "DH", "BH", - "F0", /* [D_F0] */ + "F0", /* [D_F0] */ "F1", "F2", "F3", @@ -284,20 +279,20 @@ char* regstr[] = "X14", "X15", - "CS", /* [D_CS] */ + "CS", /* [D_CS] */ "SS", "DS", "ES", "FS", "GS", - "GDTR", /* [D_GDTR] */ - "IDTR", /* [D_IDTR] */ - "LDTR", /* [D_LDTR] */ - "MSW", /* [D_MSW] */ - "TASK", /* [D_TASK] */ + "GDTR", /* [D_GDTR] */ + "IDTR", /* [D_IDTR] */ + "LDTR", /* [D_LDTR] */ + "MSW", /* [D_MSW] */ + "TASK", /* [D_TASK] */ - "CR0", /* [D_CR] */ + "CR0", /* [D_CR] */ "CR1", "CR2", "CR3", @@ -314,7 +309,7 @@ char* regstr[] = "CR14", "CR15", - "DR0", /* [D_DR] */ + "DR0", /* [D_DR] */ "DR1", "DR2", "DR3", @@ -323,7 +318,7 @@ char* regstr[] = "DR6", "DR7", - "TR0", /* [D_TR] */ + "TR0", /* [D_TR] */ "TR1", "TR2", "TR3", @@ -332,7 +327,7 @@ char* regstr[] = "TR6", "TR7", - "NONE", /* [D_NONE] */ + "NONE", /* [D_NONE] */ }; int diff --git a/src/cmd/6c/mul.c b/src/cmd/6c/mul.c index ab6883e7a..510edc05c 100644 --- a/src/cmd/6c/mul.c +++ b/src/cmd/6c/mul.c @@ -35,17 +35,17 @@ typedef struct Mparam Mparam; struct Malg { - char vals[10]; + schar vals[10]; }; struct Mparam { uint32 value; - char alg; + schar alg; char neg; char shift; char arg; - char off; + schar off; }; static Mparam multab[32]; @@ -101,7 +101,7 @@ mulparam(uint32 m, Mparam *mp) { int c, i, j, n, o, q, s; int bc, bi, bn, bo, bq, bs, bt; - char *p; + schar *p; int32 u; uint32 t; diff --git a/src/cmd/6c/peep.c b/src/cmd/6c/peep.c index 8b82adbf5..c648d8c00 100644 --- a/src/cmd/6c/peep.c +++ b/src/cmd/6c/peep.c @@ -330,20 +330,7 @@ subprop(Reg *r0) case AIMULW: if(p->to.type != D_NONE) break; - - case ADIVB: - case ADIVL: - case ADIVQ: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVQ: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULQ: - case AMULW: + goto giveup; case AROLB: case AROLL: @@ -369,6 +356,23 @@ subprop(Reg *r0) case ASHRL: case ASHRQ: case ASHRW: + if(p->from.type == D_CONST) + break; + goto giveup; + + case ADIVB: + case ADIVL: + case ADIVQ: + case ADIVW: + case AIDIVB: + case AIDIVL: + case AIDIVQ: + case AIDIVW: + case AIMULB: + case AMULB: + case AMULL: + case AMULQ: + case AMULW: case AREP: case AREPN: @@ -383,6 +387,8 @@ subprop(Reg *r0) case AMOVSB: case AMOVSL: case AMOVSQ: + case AMOVQL: + giveup: return 0; case AMOVL: @@ -581,6 +587,7 @@ copyu(Prog *p, Adr *v, Adr *s) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case AMOVQL: case AMOVSS: case AMOVSD: @@ -695,6 +702,11 @@ copyu(Prog *p, Adr *v, Adr *s) case ACMPB: case ACMPQ: + case APREFETCHT0: + case APREFETCHT1: + case APREFETCHT2: + case APREFETCHNTA: + case ACOMISD: case ACOMISS: case AUCOMISD: diff --git a/src/cmd/6c/reg.c b/src/cmd/6c/reg.c index 996128f75..e40e6c8f0 100644 --- a/src/cmd/6c/reg.c +++ b/src/cmd/6c/reg.c @@ -30,6 +30,8 @@ #include "gc.h" +static void fixjmp(Reg*); + Reg* rega(void) { @@ -185,6 +187,10 @@ regopt(Prog *p) case ACMPL: case ACMPQ: case ACMPW: + case APREFETCHT0: + case APREFETCHT1: + case APREFETCHT2: + case APREFETCHNTA: case ACOMISS: case ACOMISD: case AUCOMISS: @@ -211,6 +217,7 @@ regopt(Prog *p) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case AMOVQL: case AMOVSS: case AMOVSD: @@ -437,6 +444,12 @@ regopt(Prog *p) print("\n%L %D\n", p->lineno, &p->from); } + /* + * pass 2.1 + * fix jumps + */ + fixjmp(firstr); + /* * pass 2.5 * find looping structure @@ -1384,3 +1397,126 @@ BtoF(int32 b) return 0; return bitno(b) - 16 + FREGMIN; } + +/* what instruction does a JMP to p eventually land on? */ +static Reg* +chasejmp(Reg *r, int *jmploop) +{ + int n; + + n = 0; + for(; r; r=r->s2) { + if(r->prog->as != AJMP || r->prog->to.type != D_BRANCH) + break; + if(++n > 10) { + *jmploop = 1; + break; + } + } + return r; +} + +/* mark all code reachable from firstp as alive */ +static void +mark(Reg *firstr) +{ + Reg *r; + Prog *p; + + for(r=firstr; r; r=r->link) { + if(r->active) + break; + r->active = 1; + p = r->prog; + if(p->as != ACALL && p->to.type == D_BRANCH) + mark(r->s2); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) + break; + } +} + +/* + * the code generator depends on being able to write out JMP + * instructions that it can jump to now but fill in later. + * the linker will resolve them nicely, but they make the code + * longer and more difficult to follow during debugging. + * remove them. + */ +static void +fixjmp(Reg *firstr) +{ + int jmploop; + Reg *r; + Prog *p; + + if(debug['R'] && debug['v']) + print("\nfixjmp\n"); + + // pass 1: resolve jump to AJMP, mark all code as dead. + jmploop = 0; + for(r=firstr; r; r=r->link) { + p = r->prog; + if(debug['R'] && debug['v']) + print("%04d %P\n", r->pc, p); + if(p->as != ACALL && p->to.type == D_BRANCH && r->s2 && r->s2->prog->as == AJMP) { + r->s2 = chasejmp(r->s2, &jmploop); + p->to.offset = r->s2->pc; + if(debug['R'] && debug['v']) + print("->%P\n", p); + } + r->active = 0; + } + if(debug['R'] && debug['v']) + print("\n"); + + // pass 2: mark all reachable code alive + mark(firstr); + + // pass 3: delete dead code (mostly JMPs). + for(r=firstr; r; r=r->link) { + if(!r->active) { + p = r->prog; + if(p->link == P && p->as == ARET && r->p1 && r->p1->prog->as != ARET) { + // This is the final ARET, and the code so far doesn't have one. + // Let it stay. + } else { + if(debug['R'] && debug['v']) + print("del %04d %P\n", r->pc, p); + p->as = ANOP; + } + } + } + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + if(!jmploop) { + for(r=firstr; r; r=r->link) { + p = r->prog; + if(p->as == AJMP && p->to.type == D_BRANCH && r->s2 == r->link) { + if(debug['R'] && debug['v']) + print("del %04d %P\n", r->pc, p); + p->as = ANOP; + } + } + } + + // fix back pointers. + for(r=firstr; r; r=r->link) { + r->p2 = R; + r->p2link = R; + } + for(r=firstr; r; r=r->link) { + if(r->s2) { + r->p2link = r->s2->p2; + r->s2->p2 = r; + } + } + + if(debug['R'] && debug['v']) { + print("\n"); + for(r=firstr; r; r=r->link) + print("%04d %P\n", r->pc, r->prog); + print("\n"); + } +} + diff --git a/src/cmd/6c/sgen.c b/src/cmd/6c/sgen.c index 42045f8fa..2402a020d 100644 --- a/src/cmd/6c/sgen.c +++ b/src/cmd/6c/sgen.c @@ -126,7 +126,10 @@ xcom(Node *n) break; case ONAME: - n->addable = 10; + if(flag_largemodel) + n->addable = 9; + else + n->addable = 10; if(n->class == CPARAM || n->class == CAUTO) n->addable = 11; break; diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c index f16d0f78a..58d6d5129 100644 --- a/src/cmd/6c/swt.c +++ b/src/cmd/6c/swt.c @@ -247,18 +247,12 @@ outcode(void) Binit(&b, f, OWRITE); Bprint(&b, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); - if(ndynimp > 0 || ndynexp > 0) { - int i; - + if(pragcgobuf.to > pragcgobuf.start) { Bprint(&b, "\n"); Bprint(&b, "$$ // exports\n\n"); Bprint(&b, "$$ // local types\n\n"); - Bprint(&b, "$$ // dynimport\n"); - for(i=0; ilink) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -400,6 +420,11 @@ outhist(Biobuf *b) Bputc(b, pg.lineno>>24); zaddr(b, &pg.from, 0); zaddr(b, &pg.to, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } @@ -591,8 +616,8 @@ align(int32 i, Type *t, int op, int32 *maxalign) int32 maxround(int32 max, int32 v) { - v += SZ_VLONG-1; + v = xround(v, SZ_VLONG); if(v > max) - max = xround(v, SZ_VLONG); + return v; return max; } diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c index 2cb8c1585..364b189f2 100644 --- a/src/cmd/6c/txt.c +++ b/src/cmd/6c/txt.c @@ -809,7 +809,6 @@ gmove(Node *f, Node *t) case CASE( TUINT, TCHAR): case CASE( TLONG, TCHAR): case CASE( TULONG, TCHAR): - case CASE( TIND, TCHAR): case CASE( TCHAR, TUCHAR): case CASE( TUCHAR, TUCHAR): @@ -819,7 +818,6 @@ gmove(Node *f, Node *t) case CASE( TUINT, TUCHAR): case CASE( TLONG, TUCHAR): case CASE( TULONG, TUCHAR): - case CASE( TIND, TUCHAR): case CASE( TSHORT, TSHORT): case CASE( TUSHORT,TSHORT): @@ -827,7 +825,6 @@ gmove(Node *f, Node *t) case CASE( TUINT, TSHORT): case CASE( TLONG, TSHORT): case CASE( TULONG, TSHORT): - case CASE( TIND, TSHORT): case CASE( TSHORT, TUSHORT): case CASE( TUSHORT,TUSHORT): @@ -835,42 +832,26 @@ gmove(Node *f, Node *t) case CASE( TUINT, TUSHORT): case CASE( TLONG, TUSHORT): case CASE( TULONG, TUSHORT): - case CASE( TIND, TUSHORT): case CASE( TINT, TINT): case CASE( TUINT, TINT): case CASE( TLONG, TINT): case CASE( TULONG, TINT): - case CASE( TIND, TINT): case CASE( TINT, TUINT): case CASE( TUINT, TUINT): case CASE( TLONG, TUINT): case CASE( TULONG, TUINT): - case CASE( TIND, TUINT): - - case CASE( TUINT, TIND): - case CASE( TVLONG, TUINT): - case CASE( TVLONG, TULONG): - case CASE( TUVLONG, TUINT): - case CASE( TUVLONG, TULONG): *****/ a = AMOVL; break; - case CASE( TVLONG, TCHAR): - case CASE( TVLONG, TSHORT): - case CASE( TVLONG, TINT): - case CASE( TVLONG, TLONG): - case CASE( TUVLONG, TCHAR): - case CASE( TUVLONG, TSHORT): - case CASE( TUVLONG, TINT): - case CASE( TUVLONG, TLONG): + case CASE( TINT, TIND): case CASE( TINT, TVLONG): case CASE( TINT, TUVLONG): - case CASE( TLONG, TVLONG): - case CASE( TINT, TIND): case CASE( TLONG, TIND): + case CASE( TLONG, TVLONG): + case CASE( TLONG, TUVLONG): a = AMOVLQSX; if(f->op == OCONST) { f->vconst &= (uvlong)0xffffffffU; @@ -886,22 +867,53 @@ gmove(Node *f, Node *t) case CASE( TULONG, TVLONG): case CASE( TULONG, TUVLONG): case CASE( TULONG, TIND): - a = AMOVL; /* same effect as AMOVLQZX */ + a = AMOVLQZX; if(f->op == OCONST) { f->vconst &= (uvlong)0xffffffffU; a = AMOVQ; } break; + + case CASE( TIND, TCHAR): + case CASE( TIND, TUCHAR): + case CASE( TIND, TSHORT): + case CASE( TIND, TUSHORT): + case CASE( TIND, TINT): + case CASE( TIND, TUINT): + case CASE( TIND, TLONG): + case CASE( TIND, TULONG): + case CASE( TVLONG, TCHAR): + case CASE( TVLONG, TUCHAR): + case CASE( TVLONG, TSHORT): + case CASE( TVLONG, TUSHORT): + case CASE( TVLONG, TINT): + case CASE( TVLONG, TUINT): + case CASE( TVLONG, TLONG): + case CASE( TVLONG, TULONG): + case CASE( TUVLONG, TCHAR): + case CASE( TUVLONG, TUCHAR): + case CASE( TUVLONG, TSHORT): + case CASE( TUVLONG, TUSHORT): + case CASE( TUVLONG, TINT): + case CASE( TUVLONG, TUINT): + case CASE( TUVLONG, TLONG): + case CASE( TUVLONG, TULONG): + a = AMOVQL; + if(f->op == OCONST) { + f->vconst &= (int)0xffffffffU; + a = AMOVL; + } + break; + case CASE( TIND, TIND): case CASE( TIND, TVLONG): - case CASE( TVLONG, TVLONG): - case CASE( TUVLONG, TVLONG): - case CASE( TVLONG, TUVLONG): - case CASE( TUVLONG, TUVLONG): case CASE( TIND, TUVLONG): case CASE( TVLONG, TIND): + case CASE( TVLONG, TVLONG): + case CASE( TVLONG, TUVLONG): case CASE( TUVLONG, TIND): - case CASE( TIND, TIND): + case CASE( TUVLONG, TVLONG): + case CASE( TUVLONG, TUVLONG): a = AMOVQ; break; @@ -1348,6 +1360,16 @@ gopcode(int o, Type *ty, Node *f, Node *t) a = ASALQ; break; + case OROTL: + a = AROLL; + if(et == TCHAR || et == TUCHAR) + a = AROLB; + if(et == TSHORT || et == TUSHORT) + a = AROLW; + if(et == TVLONG || et == TUVLONG || et == TIND) + a = AROLQ; + break; + case OFUNC: a = ACALL; break; @@ -1490,6 +1512,18 @@ gpseudo(int a, Sym *s, Node *n) pc--; } +void +gprefetch(Node *n) +{ + Node n1; + + regalloc(&n1, n, Z); + gmove(n, &n1); + n1.op = OINDREG; + gins(APREFETCHNTA, &n1, Z); + regfree(&n1); +} + int sconst(Node *n) { diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index 00334e71b..a51c0ca58 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -33,9 +33,26 @@ cgen(Node *n, Node *res) while(n->op == OCONVNOP) n = n->left; - // inline slices - if(cgen_inline(n, res)) + switch(n->op) { + case OSLICE: + case OSLICEARR: + case OSLICESTR: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_slice(n, &n1); + cgen(&n1, res); + } else + cgen_slice(n, res); goto ret; + case OEFACE: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_eface(n, &n1); + cgen(&n1, res); + } else + cgen_eface(n, res); + goto ret; + } if(n->ullman >= UINF) { if(n->op == OINDREG) @@ -174,7 +191,7 @@ cgen(Node *n, Node *res) switch(n->op) { default: dump("cgen", n); - fatal("cgen: unknown op %N", n); + fatal("cgen: unknown op %+hN", n); break; // these call bgen to get a bool value @@ -187,12 +204,12 @@ cgen(Node *n, Node *res) case OGE: case OGT: case ONOT: - p1 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); p2 = pc; gmove(nodbool(1), res); - p3 = gbranch(AJMP, T); + p3 = gbranch(AJMP, T, 0); patch(p1, pc); - bgen(n, 1, p2); + bgen(n, 1, 0, p2); gmove(nodbool(0), res); patch(p3, pc); goto ret; @@ -229,17 +246,41 @@ cgen(Node *n, Node *res) case OADD: case OMUL: a = optoas(n->op, nl->type); - if(a != AIMULB) - goto sbop; - cgen_bmul(n->op, nl, nr, res); - break; + if(a == AIMULB) { + cgen_bmul(n->op, nl, nr, res); + break; + } + goto sbop; // asymmetric binary case OSUB: a = optoas(n->op, nl->type); goto abop; + case OHMUL: + cgen_hmul(nl, nr, res); + break; + case OCONV: + if(n->type->width > nl->type->width) { + // If loading from memory, do conversion during load, + // so as to avoid use of 8-bit register in, say, int(*byteptr). + switch(nl->op) { + case ODOT: + case ODOTPTR: + case OINDEX: + case OIND: + case ONAME: + igen(nl, &n1, res); + regalloc(&n2, n->type, res); + gmove(&n1, &n2); + gmove(&n2, res); + regfree(&n2); + regfree(&n1); + goto ret; + } + } + regalloc(&n1, nl->type, res); regalloc(&n2, n->type, &n1); cgen(nl, &n1); @@ -273,18 +314,18 @@ cgen(Node *n, Node *res) case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { - // map and chan have len in the first 32-bit word. + // map and chan have len in the first int-sized word. // a zero pointer means zero length regalloc(&n1, types[tptr], res); cgen(nl, &n1); nodconst(&n2, types[tptr], 0); gins(optoas(OCMP, types[tptr]), &n1, &n2); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + p1 = gbranch(optoas(OEQ, types[tptr]), T, 0); n2 = n1; n2.op = OINDREG; - n2.type = types[TINT32]; + n2.type = types[simtype[TINT]]; gmove(&n2, &n1); patch(p1, pc); @@ -297,7 +338,7 @@ cgen(Node *n, Node *res) // both slice and string have len one pointer into the struct. // a zero pointer means zero length igen(nl, &n1, res); - n1.type = types[TUINT32]; + n1.type = types[simtype[TUINT]]; n1.xoffset += Array_nel; gmove(&n1, res); regfree(&n1); @@ -308,19 +349,19 @@ cgen(Node *n, Node *res) case OCAP: if(istype(nl->type, TCHAN)) { - // chan has cap in the second 32-bit word. + // chan has cap in the second int-sized word. // a zero pointer means zero length regalloc(&n1, types[tptr], res); cgen(nl, &n1); nodconst(&n2, types[tptr], 0); gins(optoas(OCMP, types[tptr]), &n1, &n2); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + p1 = gbranch(optoas(OEQ, types[tptr]), T, 0); n2 = n1; n2.op = OINDREG; - n2.xoffset = 4; - n2.type = types[TINT32]; + n2.xoffset = widthint; + n2.type = types[simtype[TINT]]; gmove(&n2, &n1); patch(p1, pc); @@ -331,7 +372,7 @@ cgen(Node *n, Node *res) } if(isslice(nl->type)) { igen(nl, &n1, res); - n1.type = types[TUINT32]; + n1.type = types[simtype[TUINT]]; n1.xoffset += Array_cap; gmove(&n1, res); regfree(&n1); @@ -365,18 +406,53 @@ cgen(Node *n, Node *res) a = optoas(n->op, nl->type); goto abop; } - cgen_div(n->op, nl, nr, res); + + if(nl->ullman >= nr->ullman) { + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + cgen_div(n->op, &n1, nr, res); + regfree(&n1); + } else { + if(!smallintconst(nr)) { + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + } else { + n2 = *nr; + } + cgen_div(n->op, nl, &n2, res); + if(n2.op != OLITERAL) + regfree(&n2); + } break; case OLSH: case ORSH: - cgen_shift(n->op, nl, nr, res); + case OLROT: + cgen_shift(n->op, n->bounded, nl, nr, res); break; } goto ret; sbop: // symmetric binary - if(nl->ullman < nr->ullman) { + /* + * put simplest on right - we'll generate into left + * and then adjust it using the computation of right. + * constants and variables have the same ullman + * count, so look for constants specially. + * + * an integer constant we can use as an immediate + * is simpler than a variable - we can use the immediate + * in the adjustment instruction directly - so it goes + * on the right. + * + * other constants, like big integers or floating point + * constants, require a mov into a register, so those + * might as well go on the left, so we can reuse that + * register for the computation. + */ + if(nl->ullman < nr->ullman || + (nl->ullman == nr->ullman && + (smallintconst(nl) || (nr->op == OLITERAL && !smallintconst(nr))))) { r = nl; nl = nr; nr = r; @@ -386,7 +462,13 @@ abop: // asymmetric binary if(nl->ullman >= nr->ullman) { regalloc(&n1, nl->type, res); cgen(nl, &n1); - + /* + * This generates smaller code - it avoids a MOV - but it's + * easily 10% slower due to not being able to + * optimize/manipulate the move. + * To see, run: go test -bench . crypto/md5 + * with and without. + * if(sudoaddable(a, nr, &addr)) { p1 = gins(a, N, &n1); p1->from = addr; @@ -395,18 +477,30 @@ abop: // asymmetric binary regfree(&n1); goto ret; } - regalloc(&n2, nr->type, N); - cgen(nr, &n2); + * + */ + + if(smallintconst(nr)) + n2 = *nr; + else { + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + } } else { - regalloc(&n2, nr->type, res); - cgen(nr, &n2); + if(smallintconst(nr)) + n2 = *nr; + else { + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + } regalloc(&n1, nl->type, N); cgen(nl, &n1); } gins(a, &n2, &n1); gmove(&n1, res); regfree(&n1); - regfree(&n2); + if(n2.op != OLITERAL) + regfree(&n2); goto ret; uop: // unary @@ -422,93 +516,142 @@ ret: } /* - * generate: - * res = &n; + * allocate a register in res and generate + * newreg = &n + * The caller must call regfree(a). */ void -agen(Node *n, Node *res) +cgenr(Node *n, Node *a, Node *res) +{ + Node n1; + + if(debug['g']) + dump("cgenr-n", n); + + if(isfat(n->type)) + fatal("cgenr on fat node"); + + if(n->addable) { + regalloc(a, n->type, res); + gmove(n, a); + return; + } + + switch(n->op) { + case ONAME: + case ODOT: + case ODOTPTR: + case OINDEX: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + break; + default: + regalloc(a, n->type, res); + cgen(n, a); + break; + } +} + +/* + * allocate a register in res and generate + * res = &n + */ +void +agenr(Node *n, Node *a, Node *res) { Node *nl, *nr; - Node n1, n2, n3, tmp, n4, n5; + Node n1, n2, n3, n4, n5, tmp, tmp2, nlen; Prog *p1; + Type *t; uint32 w; uint64 v; - Type *t; + int freelen; if(debug['g']) { - dump("\nagen-res", res); - dump("agen-r", n); - } - if(n == N || n->type == T) - return; - - while(n->op == OCONVNOP) - n = n->left; - - if(n->addable) { - regalloc(&n1, types[tptr], res); - gins(ALEAQ, n, &n1); - gmove(&n1, res); - regfree(&n1); - goto ret; + dump("\nagenr-n", n); } nl = n->left; nr = n->right; switch(n->op) { - default: - fatal("agen: unknown op %N", n); - break; - + case ODOT: + case ODOTPTR: + case OCALLFUNC: case OCALLMETH: - cgen_callmeth(n, 0); - cgen_aret(n, res); - break; - case OCALLINTER: - cgen_callinter(n, res, 0); - cgen_aret(n, res); + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + agen(&n1, a); + regfree(&n1); break; - case OCALLFUNC: - cgen_call(n, 0); - cgen_aret(n, res); + case OIND: + cgenr(n->left, a, res); break; case OINDEX: + freelen = 0; w = n->type->width; + // Generate the non-addressable child first. if(nr->addable) goto irad; if(nl->addable) { - if(!isconst(nr, CTINT)) { - regalloc(&n1, nr->type, N); - cgen(nr, &n1); - } + cgenr(nr, &n1, N); if(!isconst(nl, CTSTR)) { - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(isfixedarray(nl->type)) { + agenr(nl, &n3, res); + } else { + igen(nl, &nlen, res); + freelen = 1; + nlen.type = types[tptr]; + nlen.xoffset += Array_array; + regalloc(&n3, types[tptr], res); + gmove(&nlen, &n3); + nlen.type = types[simtype[TUINT]]; + nlen.xoffset += Array_nel-Array_array; + } } goto index; } tempname(&tmp, nr->type); cgen(nr, &tmp); nr = &tmp; - irad: if(!isconst(nl, CTSTR)) { - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(isfixedarray(nl->type)) { + agenr(nl, &n3, res); + } else { + if(!nl->addable) { + // igen will need an addressable node. + tempname(&tmp2, nl->type); + cgen(nl, &tmp2); + nl = &tmp2; + } + igen(nl, &nlen, res); + freelen = 1; + nlen.type = types[tptr]; + nlen.xoffset += Array_array; + regalloc(&n3, types[tptr], res); + gmove(&nlen, &n3); + nlen.type = types[simtype[TUINT]]; + nlen.xoffset += Array_nel-Array_array; + } } if(!isconst(nr, CTINT)) { - regalloc(&n1, nr->type, N); - cgen(nr, &n1); + cgenr(nr, &n1, N); } goto index; index: // &a is in &n3 (allocated in res) // i is in &n1 (if not constant) + // len(a) is in nlen (if needed) // w is width // explicit check for nil if array is large enough @@ -529,29 +672,26 @@ agen(Node *n, Node *res) fatal("constant string constant index"); // front end should handle v = mpgetfix(nr->val.u.xval); if(isslice(nl->type) || nl->type->etype == TSTRING) { - if(!debug['B'] && !n->etype) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_nel; - nodconst(&n2, types[TUINT32], v); - gins(optoas(OCMP, types[TUINT32]), &n1, &n2); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); - ginscall(panicindex, 0); + if(!debug['B'] && !n->bounded) { + nodconst(&n2, types[simtype[TUINT]], v); + if(smallintconst(nr)) { + gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &n2); + } else { + regalloc(&tmp, types[simtype[TUINT]], N); + gmove(&n2, &tmp); + gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &tmp); + regfree(&tmp); + } + p1 = gbranch(optoas(OGT, types[simtype[TUINT]]), T, +1); + ginscall(panicindex, -1); patch(p1, pc); } - - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_array; - gmove(&n1, &n3); + regfree(&nlen); } if (v*w != 0) ginscon(optoas(OADD, types[tptr]), v*w, &n3); - gmove(&n3, res); - regfree(&n3); + *a = n3; break; } @@ -564,32 +704,32 @@ agen(Node *n, Node *res) gmove(&n1, &n2); regfree(&n1); - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { // check bounds - n5.op = OXXX; - t = types[TUINT32]; + t = types[simtype[TUINT]]; if(is64(nr->type)) t = types[TUINT64]; if(isconst(nl, CTSTR)) { - nodconst(&n1, t, nl->val.u.sval->len); + nodconst(&nlen, t, nl->val.u.sval->len); } else if(isslice(nl->type) || nl->type->etype == TSTRING) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[TUINT32]; - n1.xoffset = Array_nel; if(is64(nr->type)) { regalloc(&n5, t, N); - gmove(&n1, &n5); - n1 = n5; + gmove(&nlen, &n5); + regfree(&nlen); + nlen = n5; } } else { - nodconst(&n1, t, nl->type->bound); + nodconst(&nlen, t, nl->type->bound); + if(!smallintconst(&nlen)) { + regalloc(&n5, t, N); + gmove(&nlen, &n5); + nlen = n5; + freelen = 1; + } } - gins(optoas(OCMP, t), &n2, &n1); - p1 = gbranch(optoas(OLT, t), T); - if(n5.op != OXXX) - regfree(&n5); - ginscall(panicindex, 0); + gins(optoas(OCMP, t), &n2, &nlen); + p1 = gbranch(optoas(OLT, t), T, +1); + ginscall(panicindex, -1); patch(p1, pc); } @@ -597,19 +737,15 @@ agen(Node *n, Node *res) regalloc(&n3, types[tptr], res); p1 = gins(ALEAQ, N, &n3); datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); - p1->from.scale = 1; - p1->from.index = n2.val.u.reg; + if(flag_largemodel) { + gins(AADDQ, &n2, &n3); + } else { + p1->from.scale = 1; + p1->from.index = n2.val.u.reg; + } goto indexdone; } - if(isslice(nl->type) || nl->type->etype == TSTRING) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_array; - gmove(&n1, &n3); - } - if(w == 0) { // nothing to do } else if(w == 1 || w == 2 || w == 4 || w == 8) { @@ -623,9 +759,103 @@ agen(Node *n, Node *res) } indexdone: - gmove(&n3, res); + *a = n3; regfree(&n2); - regfree(&n3); + if(freelen) + regfree(&nlen); + break; + + default: + regalloc(a, types[tptr], res); + agen(n, a); + break; + } +} + +/* + * generate: + * res = &n; + */ +void +agen(Node *n, Node *res) +{ + Node *nl, *nr; + Node n1, n2; + + if(debug['g']) { + dump("\nagen-res", res); + dump("agen-r", n); + } + if(n == N || n->type == T) + return; + + while(n->op == OCONVNOP) + n = n->left; + + if(isconst(n, CTNIL) && n->type->width > widthptr) { + // Use of a nil interface or nil slice. + // Create a temporary we can take the address of and read. + // The generated code is just going to panic, so it need not + // be terribly efficient. See issue 3670. + tempname(&n1, n->type); + clearfat(&n1); + regalloc(&n2, types[tptr], res); + gins(ALEAQ, &n1, &n2); + gmove(&n2, res); + regfree(&n2); + goto ret; + } + + if(n->addable) { + regalloc(&n1, types[tptr], res); + gins(ALEAQ, n, &n1); + gmove(&n1, res); + regfree(&n1); + goto ret; + } + + nl = n->left; + nr = n->right; + USED(nr); + + switch(n->op) { + default: + fatal("agen: unknown op %+hN", n); + break; + + case OCALLMETH: + cgen_callmeth(n, 0); + cgen_aret(n, res); + break; + + case OCALLINTER: + cgen_callinter(n, res, 0); + cgen_aret(n, res); + break; + + case OCALLFUNC: + cgen_call(n, 0); + cgen_aret(n, res); + break; + + case OSLICE: + case OSLICEARR: + case OSLICESTR: + tempname(&n1, n->type); + cgen_slice(n, &n1); + agen(&n1, res); + break; + + case OEFACE: + tempname(&n1, n->type); + cgen_eface(n, &n1); + agen(&n1, res); + break; + + case OINDEX: + agenr(n, &n1, res); + gmove(&n1, res); + regfree(&n1); break; case ONAME: @@ -692,7 +922,11 @@ igen(Node *n, Node *a, Node *res) { Type *fp; Iter flist; - + Node n1; + + if(debug['g']) { + dump("\nigen-n", n); + } switch(n->op) { case ONAME: if((n->class&PHEAP) || n->class == PPARAMREF) @@ -700,9 +934,53 @@ igen(Node *n, Node *a, Node *res) *a = *n; return; + case OINDREG: + // Increase the refcount of the register so that igen's caller + // has to call regfree. + if(n->val.u.reg != D_SP) + reg[n->val.u.reg]++; + *a = *n; + return; + + case ODOT: + igen(n->left, a, res); + a->xoffset += n->xoffset; + a->type = n->type; + return; + + case ODOTPTR: + cgenr(n->left, a, res); + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(n->left->type->type->width >= unmappedzero) { + n1 = *a; + n1.op = OINDREG; + n1.type = types[TUINT8]; + n1.xoffset = 0; + gins(ATESTB, nodintconst(0), &n1); + } + } + a->op = OINDREG; + a->xoffset += n->xoffset; + a->type = n->type; + return; + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + switch(n->op) { + case OCALLFUNC: + cgen_call(n, 0); + break; + case OCALLMETH: + cgen_callmeth(n, 0); + break; + case OCALLINTER: + cgen_callinter(n, N, 0); + break; + } fp = structfirst(&flist, getoutarg(n->left->type)); - cgen_call(n, 0); memset(a, 0, sizeof *a); a->op = OINDREG; a->val.u.reg = D_SP; @@ -710,10 +988,34 @@ igen(Node *n, Node *a, Node *res) a->xoffset = fp->width; a->type = n->type; return; + + case OINDEX: + // Index of fixed-size array by constant can + // put the offset in the addressing. + // Could do the same for slice except that we need + // to use the real index for the bounds checking. + if(isfixedarray(n->left->type) || + (isptr[n->left->type->etype] && isfixedarray(n->left->left->type))) + if(isconst(n->right, CTINT)) { + // Compute &a. + if(!isptr[n->left->type->etype]) + igen(n->left, a, res); + else { + igen(n->left, &n1, res); + regalloc(a, types[tptr], res); + gmove(&n1, a); + regfree(&n1); + a->op = OINDREG; + } + + // Compute &a[i] as &a + i*width. + a->type = n->type; + a->xoffset += mpgetfix(n->right->val.u.xval)*n->type->width; + return; + } } - - regalloc(a, types[tptr], res); - agen(n, a); + + agenr(n, a, res); a->op = OINDREG; a->type = n->type; } @@ -723,7 +1025,7 @@ igen(Node *n, Node *a, Node *res) * if(n == true) goto to; */ void -bgen(Node *n, int true, Prog *to) +bgen(Node *n, int true, int likely, Prog *to) { int et, a; Node *nl, *nr, *l, *r; @@ -765,14 +1067,14 @@ bgen(Node *n, int true, Prog *to) a = AJNE; if(!true) a = AJEQ; - patch(gbranch(a, n->type), to); + patch(gbranch(a, n->type, likely), to); regfree(&n1); goto ret; case OLITERAL: // need to ask if it is bool? if(!true == !n->val.u.bval) - patch(gbranch(AJMP, T), to); + patch(gbranch(AJMP, T, likely), to); goto ret; case ONAME: @@ -783,7 +1085,7 @@ bgen(Node *n, int true, Prog *to) a = AJNE; if(!true) a = AJEQ; - patch(gbranch(a, n->type), to); + patch(gbranch(a, n->type, likely), to); goto ret; case OANDAND: @@ -791,12 +1093,12 @@ bgen(Node *n, int true, Prog *to) goto caseor; caseand: - p1 = gbranch(AJMP, T); - p2 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); - bgen(n->left, !true, p2); - bgen(n->right, !true, p2); - p1 = gbranch(AJMP, T); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AJMP, T, 0); patch(p1, to); patch(p2, pc); goto ret; @@ -806,8 +1108,8 @@ bgen(Node *n, int true, Prog *to) goto caseand; caseor: - bgen(n->left, true, to); - bgen(n->right, true, to); + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); goto ret; case OEQ: @@ -830,7 +1132,7 @@ bgen(Node *n, int true, Prog *to) switch(n->op) { case ONOT: - bgen(nl, !true, to); + bgen(nl, !true, likely, to); goto ret; case OEQ: @@ -843,14 +1145,14 @@ bgen(Node *n, int true, Prog *to) if(!true) { if(isfloat[nr->type->etype]) { // brcom is not valid on floats when NaN is involved. - p1 = gbranch(AJMP, T); - p2 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); ll = n->ninit; // avoid re-genning ninit n->ninit = nil; - bgen(n, 1, p2); + bgen(n, 1, -likely, p2); n->ninit = ll; - patch(gbranch(AJMP, T), to); + patch(gbranch(AJMP, T, 0), to); patch(p2, pc); goto ret; } @@ -865,47 +1167,41 @@ bgen(Node *n, int true, Prog *to) nl = nr; nr = r; } - + if(isslice(nl->type)) { - // only valid to cmp darray to literal nil + // front end should only leave cmp to literal nil if((a != OEQ && a != ONE) || nr->op != OLITERAL) { - yyerror("illegal array comparison"); + yyerror("illegal slice comparison"); break; } a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = Array_array; - n2.type = types[tptr]; + igen(nl, &n1, N); + n1.xoffset += Array_array; + n1.type = types[tptr]; nodconst(&tmp, types[tptr], 0); - gins(optoas(OCMP, types[tptr]), &n2, &tmp); - patch(gbranch(a, types[tptr]), to); + gins(optoas(OCMP, types[tptr]), &n1, &tmp); + patch(gbranch(a, types[tptr], likely), to); regfree(&n1); break; } if(isinter(nl->type)) { - // front end shold only leave cmp to literal nil + // front end should only leave cmp to literal nil if((a != OEQ && a != ONE) || nr->op != OLITERAL) { yyerror("illegal interface comparison"); break; } a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = 0; + igen(nl, &n1, N); + n1.type = types[tptr]; nodconst(&tmp, types[tptr], 0); - gins(optoas(OCMP, types[tptr]), &n2, &tmp); - patch(gbranch(a, types[tptr]), to); + gins(optoas(OCMP, types[tptr]), &n1, &tmp); + patch(gbranch(a, types[tptr], likely), to); regfree(&n1); break; } if(iscomplex[nl->type->etype]) { - complexbool(a, nl, nr, true, to); + complexbool(a, nl, nr, true, likely, to); break; } @@ -931,7 +1227,7 @@ bgen(Node *n, int true, Prog *to) if(smallintconst(nr)) { gins(optoas(OCMP, nr->type), &n1, nr); - patch(gbranch(optoas(a, nr->type), nr->type), to); + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); regfree(&n1); break; } @@ -953,18 +1249,18 @@ bgen(Node *n, int true, Prog *to) if(isfloat[nr->type->etype] && (n->op == OEQ || n->op == ONE)) { if(n->op == OEQ) { // neither NE nor P - p1 = gbranch(AJNE, T); - p2 = gbranch(AJPS, T); - patch(gbranch(AJMP, T), to); + p1 = gbranch(AJNE, T, -likely); + p2 = gbranch(AJPS, T, -likely); + patch(gbranch(AJMP, T, 0), to); patch(p1, pc); patch(p2, pc); } else { // either NE or P - patch(gbranch(AJNE, T), to); - patch(gbranch(AJPS, T), to); + patch(gbranch(AJNE, T, likely), to); + patch(gbranch(AJPS, T, likely), to); } } else - patch(gbranch(optoas(a, nr->type), nr->type), to); + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); regfree(&n1); regfree(&n2); break; @@ -1036,8 +1332,8 @@ stkof(Node *n) void sgen(Node *n, Node *ns, int64 w) { - Node nodl, nodr, oldl, oldr, cx, oldcx, tmp; - int32 c, q, odst, osrc; + Node nodl, nodr, nodsi, noddi, cx, oldcx, tmp; + vlong c, q, odst, osrc; if(debug['g']) { print("\nsgen w=%lld\n", w); @@ -1051,9 +1347,9 @@ sgen(Node *n, Node *ns, int64 w) if(w < 0) fatal("sgen copy %lld", w); - if(w == 16) - if(componentgen(n, ns)) - return; + // Avoid taking the address for simple enough types. + if(componentgen(n, ns)) + return; if(w == 0) { // evaluate side effects only @@ -1080,22 +1376,18 @@ sgen(Node *n, Node *ns, int64 w) } if(n->ullman >= ns->ullman) { - savex(D_SI, &nodr, &oldr, N, types[tptr]); - agen(n, &nodr); - - regalloc(&nodr, types[tptr], &nodr); // mark nodr as live - savex(D_DI, &nodl, &oldl, N, types[tptr]); - agen(ns, &nodl); - regfree(&nodr); + agenr(n, &nodr, N); + agenr(ns, &nodl, N); } else { - savex(D_DI, &nodl, &oldl, N, types[tptr]); - agen(ns, &nodl); - - regalloc(&nodl, types[tptr], &nodl); // mark nodl as live - savex(D_SI, &nodr, &oldr, N, types[tptr]); - agen(n, &nodr); - regfree(&nodl); + agenr(ns, &nodl, N); + agenr(n, &nodr, N); } + nodreg(&noddi, types[tptr], D_DI); + nodreg(&nodsi, types[tptr], D_SI); + gmove(&nodl, &noddi); + gmove(&nodr, &nodsi); + regfree(&nodl); + regfree(&nodr); c = w % 8; // bytes q = w / 8; // quads @@ -1152,9 +1444,6 @@ sgen(Node *n, Node *ns, int64 w) } } - - restx(&nodl, &oldl); - restx(&nodr, &oldr); restx(&cx, &oldcx); } @@ -1175,15 +1464,21 @@ cadable(Node *n) } /* - * copy a structure component by component + * copy a composite value by moving its individual components. + * Slices, strings and interfaces are supported. + * Small structs or arrays with elements of basic type are + * also supported. + * nr is N when assigning a zero value. * return 1 if can do, 0 if cant. - * nr is N for copy zero */ int componentgen(Node *nr, Node *nl) { Node nodl, nodr; + Type *t; int freel, freer; + vlong fldcount; + vlong loffset, roffset; freel = 0; freer = 0; @@ -1193,8 +1488,33 @@ componentgen(Node *nr, Node *nl) goto no; case TARRAY: - if(!isslice(nl->type)) + t = nl->type; + + // Slices are ok. + if(isslice(t)) + break; + // Small arrays are ok. + if(t->bound > 0 && t->bound <= 3 && !isfat(t->type)) + break; + + goto no; + + case TSTRUCT: + // Small structs with non-fat types are ok. + // Zero-sized structs are treated separately elsewhere. + fldcount = 0; + for(t=nl->type->type; t; t=t->down) { + if(isfat(t->type)) + goto no; + if(t->etype != TFIELD) + fatal("componentgen: not a TFIELD: %lT", t); + fldcount++; + } + if(fldcount == 0 || fldcount > 3) goto no; + + break; + case TSTRING: case TINTER: break; @@ -1218,9 +1538,23 @@ componentgen(Node *nr, Node *nl) switch(nl->type->etype) { case TARRAY: - if(!isslice(nl->type)) - goto no; + // componentgen for arrays. + t = nl->type; + if(!isslice(t)) { + nodl.type = t->type; + nodr.type = nodl.type; + for(fldcount=0; fldcount < t->bound; fldcount++) { + if(nr == N) + clearslim(&nodl); + else + gmove(&nodr, &nodl); + nodl.xoffset += t->type->width; + nodr.xoffset += t->type->width; + } + goto yes; + } + // componentgen for slices. nodl.xoffset += Array_array; nodl.type = ptrto(nl->type->type); @@ -1232,7 +1566,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_nel-Array_array; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_nel-Array_array; @@ -1242,7 +1576,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_cap-Array_nel; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_cap-Array_nel; @@ -1265,7 +1599,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_nel-Array_array; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_nel-Array_array; @@ -1300,7 +1634,27 @@ componentgen(Node *nr, Node *nl) goto yes; case TSTRUCT: - goto no; + loffset = nodl.xoffset; + roffset = nodr.xoffset; + // funarg structs may not begin at offset zero. + if(nl->type->etype == TSTRUCT && nl->type->funarg && nl->type->type) + loffset -= nl->type->type->width; + if(nr != N && nr->type->etype == TSTRUCT && nr->type->funarg && nr->type->type) + roffset -= nr->type->type->width; + + for(t=nl->type->type; t; t=t->down) { + nodl.xoffset = loffset + t->width; + nodl.type = t->type; + + if(nr == N) + clearslim(&nodl); + else { + nodr.xoffset = roffset + t->width; + nodr.type = nodl.type; + gmove(&nodr, &nodl); + } + } + goto yes; } no: diff --git a/src/cmd/6g/doc.go b/src/cmd/6g/doc.go index 64f1d2ba9..07b2818da 100644 --- a/src/cmd/6g/doc.go +++ b/src/cmd/6g/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 6g is the version of the gc compiler for the x86-64. @@ -10,4 +12,4 @@ The $GOARCH for these tools is amd64. It reads .go files and outputs .6 files. The flags are documented in ../gc/doc.go. */ -package documentation +package main diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c index b03ac1ed6..526c04c06 100644 --- a/src/cmd/6g/galign.c +++ b/src/cmd/6g/galign.c @@ -17,8 +17,8 @@ vlong MAXWIDTH = 1LL<<50; */ Typedef typedefs[] = { - "int", TINT, TINT32, - "uint", TUINT, TUINT32, + "int", TINT, TINT64, + "uint", TUINT, TUINT64, "uintptr", TUINTPTR, TUINT64, 0 }; @@ -27,6 +27,7 @@ void betypeinit(void) { widthptr = 8; + widthint = 8; zprog.link = P; zprog.as = AGOK; diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 47a540082..ceb6a2caa 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -14,19 +14,22 @@ typedef struct Addr Addr; struct Addr { vlong offset; - double dval; - Prog* branch; - char sval[NSNAME]; + + union { + double dval; + vlong vval; + Prog* branch; + char sval[NSNAME]; + } u; Sym* gotype; Sym* sym; Node* node; - int width; + int64 width; uchar type; uchar index; uchar etype; uchar scale; /* doubles as width in DATA op */ - uchar pun; /* dont register variable */ }; #define A ((Addr*)0) @@ -58,7 +61,7 @@ EXTERN Node* throwreturn; extern vlong unmappedzero; /* - * gen.c + * ggen.c */ void compile(Node*); void proglist(void); @@ -71,29 +74,31 @@ void cgen_proc(Node*, int); void cgen_callret(Node*, Node*); void cgen_div(int, Node*, Node*, Node*); void cgen_bmul(int, Node*, Node*, Node*); -void cgen_shift(int, Node*, Node*, Node*); +void cgen_hmul(Node*, Node*, Node*); +void cgen_shift(int, int, Node*, Node*, Node*); void cgen_dcl(Node*); int needconvert(Type*, Type*); void genconv(Type*, Type*); void allocparams(void); -void checklabels(); +void checklabels(void); void ginscall(Node*, int); int gen_as_init(Node*); +void clearslim(Node*); /* - * cgen + * cgen.c */ void agen(Node*, Node*); +void agenr(Node*, Node*, Node*); +void cgenr(Node*, Node*, Node*); void igen(Node*, Node*, Node*); vlong fieldoffset(Type*, Node*); -void bgen(Node*, int, Prog*); void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); int samaddr(Node*, Node*); void naddr(Node*, Addr*, int); void cgen_aret(Node*, Node*); -int cgen_inline(Node*, Node*); void restx(Node*, Node*); void savex(int, Node*, Node*, Node*, Type*); int componentgen(Node*, Node*); @@ -103,9 +108,8 @@ int componentgen(Node*, Node*); */ void clearp(Prog*); void proglist(void); -Prog* gbranch(int, Type*); +Prog* gbranch(int, Type*, int); Prog* prog(int); -void gaddoffset(Node*); void gconv(int, int); int conv2pt(Type*); vlong convvtox(vlong, int); @@ -126,9 +130,9 @@ Plist* newplist(void); int isfat(Type*); void sudoclean(void); int sudoaddable(int, Node*, Addr*); -void afunclit(Addr*); -void datagostring(Strlit*, Addr*); +void afunclit(Addr*, Node*); void nodfconst(Node*, Type*, Mpflt*); +void gtrack(Sym*); /* * cplx.c @@ -136,12 +140,12 @@ void nodfconst(Node*, Type*, Mpflt*); int complexop(Node*, Node*); void complexmove(Node*, Node*); void complexgen(Node*, Node*); -void complexbool(int, Node*, Node*, int, Prog*); /* * gobj.c */ void datastring(char*, int, Addr*); +void datagostring(Strlit*, Addr*); /* * list.c diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 02e67d6d4..23bb5093f 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -25,6 +25,9 @@ void markautoused(Prog* p) { for (; p; p = p->link) { + if (p->as == ATYPE) + continue; + if (p->from.type == D_AUTO && p->from.node) p->from.node->used = 1; @@ -35,14 +38,22 @@ markautoused(Prog* p) // Fixup instructions after compactframe has moved all autos around. void -fixautoused(Prog* p) +fixautoused(Prog *p) { - for (; p; p = p->link) { + Prog **lp; + + for (lp=&p; (p=*lp) != P; ) { + if (p->as == ATYPE && p->from.node && p->from.type == D_AUTO && !p->from.node->used) { + *lp = p->link; + continue; + } if (p->from.type == D_AUTO && p->from.node) p->from.offset += p->from.node->stkdelta; if (p->to.type == D_AUTO && p->to.node) p->to.offset += p->to.node->stkdelta; + + lp = &p->link; } } @@ -50,15 +61,18 @@ fixautoused(Prog* p) /* * generate: * call f + * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack + * proc=3 normal call to C pointer (not Go func value) */ void ginscall(Node *f, int proc) { Prog *p; Node reg, con; + Node r1; switch(proc) { default: @@ -66,14 +80,38 @@ ginscall(Node *f, int proc) break; case 0: // normal call - p = gins(ACALL, N, f); - afunclit(&p->to); + case -1: // normal call but no return + if(f->op == ONAME && f->class == PFUNC) { + p = gins(ACALL, N, f); + afunclit(&p->to, f); + if(proc == -1 || noreturn(p)) + gins(AUNDEF, N, N); + break; + } + nodreg(®, types[tptr], D_DX); + nodreg(&r1, types[tptr], D_BX); + gmove(f, ®); + reg.op = OINDREG; + gmove(®, &r1); + reg.op = OREGISTER; + gins(ACALL, ®, &r1); + break; + + case 3: // normal call of c function pointer + gins(ACALL, N, f); break; case 1: // call in new proc (go) case 2: // deferred call (defer) nodreg(®, types[TINT64], D_CX); - gins(APUSHQ, f, N); + if(flag_largemodel) { + regalloc(&r1, f->type, f); + gmove(f, &r1); + gins(APUSHQ, &r1, N); + regfree(&r1); + } else { + gins(APUSHQ, f, N); + } nodconst(&con, types[TINT32], argsize(f->type)); gins(APUSHQ, &con, N); if(proc == 1) @@ -88,7 +126,7 @@ ginscall(Node *f, int proc) if(proc == 2) { nodreg(®, types[TINT64], D_AX); gins(ATESTQ, ®, ®); - patch(gbranch(AJNE, T), retpc); + patch(gbranch(AJNE, T, -1), retpc); } break; } @@ -102,7 +140,7 @@ void cgen_callinter(Node *n, Node *res, int proc) { Node *i, *f; - Node tmpi, nodo, nodr, nodsp; + Node tmpi, nodi, nodo, nodr, nodsp; i = n->left; if(i->op != ODOTINTER) @@ -122,21 +160,34 @@ cgen_callinter(Node *n, Node *res, int proc) genlist(n->list); // assign the args - regalloc(&nodr, types[tptr], res); - regalloc(&nodo, types[tptr], &nodr); - nodo.op = OINDREG; - - agen(i, &nodr); // REG = &inter + // i is now addable, prepare an indirected + // register to hold its address. + igen(i, &nodi, res); // REG = &inter nodindreg(&nodsp, types[tptr], D_SP); - nodo.xoffset += widthptr; - cgen(&nodo, &nodsp); // 0(SP) = 8(REG) -- i.data - - nodo.xoffset -= widthptr; - cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab - + nodi.type = types[tptr]; + nodi.xoffset += widthptr; + cgen(&nodi, &nodsp); // 0(SP) = 8(REG) -- i.data + + regalloc(&nodo, types[tptr], res); + nodi.type = types[tptr]; + nodi.xoffset -= widthptr; + cgen(&nodi, &nodo); // REG = 0(REG) -- i.tab + regfree(&nodi); + + regalloc(&nodr, types[tptr], &nodo); + if(n->left->xoffset == BADWIDTH) + fatal("cgen_callinter: badwidth"); + nodo.op = OINDREG; nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.tab->fun[f] + if(proc == 0) { + // plain call: use direct c function pointer - more efficient + cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.tab->fun[f] + proc = 3; + } else { + // go/defer. generate go func value. + gins(ALEAQ, &nodo, &nodr); // REG = &(32+offset(REG)) -- i.tab->fun[f] + } // BOTCH nodr.type = fntype; nodr.type = n->left->type; @@ -182,7 +233,7 @@ cgen_call(Node *n, int proc) nod.type = t; ginscall(&nod, proc); regfree(&nod); - goto ret; + return; } // call pointer @@ -192,16 +243,12 @@ cgen_call(Node *n, int proc) nod.type = t; ginscall(&nod, proc); regfree(&nod); - goto ret; + return; } // call direct n->left->method = 1; ginscall(n->left, proc); - - -ret: - ; } /* @@ -389,7 +436,9 @@ cgen_asop(Node *n) hard: n2.op = 0; n1.op = 0; - if(nr->ullman >= nl->ullman || nl->addable) { + if(nr->op == OLITERAL) { + // don't allocate a register for literals. + } else if(nr->ullman >= nl->ullman || nl->addable) { regalloc(&n2, nr->type, N); cgen(nr, &n2); nr = &n2; @@ -447,10 +496,10 @@ void dodiv(int op, Node *nl, Node *nr, Node *res) { int a, check; - Node n3, n4, n5; + Node n3, n4; Type *t, *t0; Node ax, dx, ax1, n31, oldax, olddx; - Prog *p1, *p2, *p3; + Prog *p1, *p2; // Have to be careful about handling // most negative int divided by -1 correctly. @@ -501,30 +550,22 @@ dodiv(int op, Node *nl, Node *nr, Node *res) gmove(&n31, &n3); } - p3 = P; + p2 = P; if(check) { nodconst(&n4, t, -1); gins(optoas(OCMP, t), &n3, &n4); - p1 = gbranch(optoas(ONE, t), T); - nodconst(&n4, t, -1LL<<(t->width*8-1)); - if(t->width == 8) { - n5 = n4; - regalloc(&n4, t, N); - gins(AMOVQ, &n5, &n4); - } - gins(optoas(OCMP, t), &ax, &n4); - p2 = gbranch(optoas(ONE, t), T); - if(op == ODIV) - gmove(&n4, res); - if(t->width == 8) - regfree(&n4); - if(op == OMOD) { + p1 = gbranch(optoas(ONE, t), T, +1); + if(op == ODIV) { + // a / (-1) is -a. + gins(optoas(OMINUS, t), N, &ax); + gmove(&ax, res); + } else { + // a % (-1) is 0. nodconst(&n4, t, 0); gmove(&n4, res); } - p3 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); - patch(p2, pc); } savex(D_DX, &dx, &olddx, res, t); if(!issigned[t->etype]) { @@ -540,7 +581,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res) gmove(&dx, res); restx(&dx, &olddx); if(check) - patch(p3, pc); + patch(p2, pc); restx(&ax, &oldax); } @@ -594,134 +635,21 @@ restx(Node *x, Node *oldx) void cgen_div(int op, Node *nl, Node *nr, Node *res) { - Node n1, n2, n3, savl, savr; - Node ax, dx, oldax, olddx; - int n, w, s, a; + Node n1, n2, n3; + int w, a; Magic m; - if(nl->ullman >= UINF) { - tempname(&savl, nl->type); - cgen(nl, &savl); - nl = &savl; - } - if(nr->ullman >= UINF) { - tempname(&savr, nr->type); - cgen(nr, &savr); - nr = &savr; - } - if(nr->op != OLITERAL) goto longdiv; - - // special cases of mod/div - // by a constant w = nl->type->width*8; - s = 0; - n = powtwo(nr); - if(n >= 1000) { - // negative power of 2 - s = 1; - n -= 1000; - } - - if(n+1 >= w) { - // just sign bit - goto longdiv; - } - if(n < 0) - goto divbymul; - switch(n) { - case 0: - // divide by 1 - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - if(op == OMOD) { - gins(optoas(OXOR, nl->type), &n1, &n1); - } else - if(s) - gins(optoas(OMINUS, nl->type), N, &n1); - gmove(&n1, res); - regfree(&n1); - return; - case 1: - // divide by 2 - if(op == OMOD) { - if(issigned[nl->type->etype]) - goto longmod; - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - nodconst(&n2, nl->type, 1); - gins(optoas(OAND, nl->type), &n2, &n1); - gmove(&n1, res); - regfree(&n1); - return; - } - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - if(!issigned[nl->type->etype]) - break; - - // develop -1 iff nl is negative - regalloc(&n2, nl->type, N); - gmove(&n1, &n2); - nodconst(&n3, nl->type, w-1); - gins(optoas(ORSH, nl->type), &n3, &n2); - gins(optoas(OSUB, nl->type), &n2, &n1); - regfree(&n2); - break; - default: - if(op == OMOD) { - if(issigned[nl->type->etype]) - goto longmod; - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - nodconst(&n2, nl->type, mpgetfix(nr->val.u.xval)-1); - if(!smallintconst(&n2)) { - regalloc(&n3, nl->type, N); - gmove(&n2, &n3); - gins(optoas(OAND, nl->type), &n3, &n1); - regfree(&n3); - } else - gins(optoas(OAND, nl->type), &n2, &n1); - gmove(&n1, res); - regfree(&n1); - return; - } - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - if(!issigned[nl->type->etype]) - break; - - // develop (2^k)-1 iff nl is negative - regalloc(&n2, nl->type, N); - gmove(&n1, &n2); - nodconst(&n3, nl->type, w-1); - gins(optoas(ORSH, nl->type), &n3, &n2); - nodconst(&n3, nl->type, w-n); - gins(optoas(ORSH, tounsigned(nl->type)), &n3, &n2); - gins(optoas(OADD, nl->type), &n2, &n1); - regfree(&n2); - break; - } - nodconst(&n2, nl->type, n); - gins(optoas(ORSH, nl->type), &n2, &n1); - if(s) - gins(optoas(OMINUS, nl->type), N, &n1); - gmove(&n1, res); - regfree(&n1); - return; - -divbymul: + // Front end handled 32-bit division. We only need to handle 64-bit. // try to do division by multiply by (2^w)/d // see hacker's delight chapter 10 switch(simtype[nl->type->etype]) { default: goto longdiv; - case TUINT8: - case TUINT16: - case TUINT32: case TUINT64: m.w = w; m.ud = mpgetfix(nr->val.u.xval); @@ -731,47 +659,28 @@ divbymul: if(op == OMOD) goto longmod; - regalloc(&n1, nl->type, N); - cgen(nl, &n1); // num -> reg(n1) - - savex(D_AX, &ax, &oldax, res, nl->type); - savex(D_DX, &dx, &olddx, res, nl->type); - + cgenr(nl, &n1, N); nodconst(&n2, nl->type, m.um); - gmove(&n2, &ax); // const->ax - - gins(optoas(OHMUL, nl->type), &n1, N); // imul reg - if(w == 8) { - // fix up 8-bit multiply - Node ah, dl; - nodreg(&ah, types[TUINT8], D_AH); - nodreg(&dl, types[TUINT8], D_DL); - gins(AMOVB, &ah, &dl); - } + regalloc(&n3, nl->type, res); + cgen_hmul(&n1, &n2, &n3); if(m.ua) { // need to add numerator accounting for overflow - gins(optoas(OADD, nl->type), &n1, &dx); + gins(optoas(OADD, nl->type), &n1, &n3); nodconst(&n2, nl->type, 1); - gins(optoas(ORRC, nl->type), &n2, &dx); + gins(optoas(ORROTC, nl->type), &n2, &n3); nodconst(&n2, nl->type, m.s-1); - gins(optoas(ORSH, nl->type), &n2, &dx); + gins(optoas(ORSH, nl->type), &n2, &n3); } else { nodconst(&n2, nl->type, m.s); - gins(optoas(ORSH, nl->type), &n2, &dx); // shift dx + gins(optoas(ORSH, nl->type), &n2, &n3); // shift dx } - + gmove(&n3, res); regfree(&n1); - gmove(&dx, res); - - restx(&ax, &oldax); - restx(&dx, &olddx); + regfree(&n3); return; - case TINT8: - case TINT16: - case TINT32: case TINT64: m.w = w; m.sd = mpgetfix(nr->val.u.xval); @@ -781,47 +690,32 @@ divbymul: if(op == OMOD) goto longmod; - regalloc(&n1, nl->type, N); - cgen(nl, &n1); // num -> reg(n1) - - savex(D_AX, &ax, &oldax, res, nl->type); - savex(D_DX, &dx, &olddx, res, nl->type); - + cgenr(nl, &n1, res); nodconst(&n2, nl->type, m.sm); - gmove(&n2, &ax); // const->ax - - gins(optoas(OHMUL, nl->type), &n1, N); // imul reg - if(w == 8) { - // fix up 8-bit multiply - Node ah, dl; - nodreg(&ah, types[TUINT8], D_AH); - nodreg(&dl, types[TUINT8], D_DL); - gins(AMOVB, &ah, &dl); - } + regalloc(&n3, nl->type, N); + cgen_hmul(&n1, &n2, &n3); if(m.sm < 0) { // need to add numerator - gins(optoas(OADD, nl->type), &n1, &dx); + gins(optoas(OADD, nl->type), &n1, &n3); } nodconst(&n2, nl->type, m.s); - gins(optoas(ORSH, nl->type), &n2, &dx); // shift dx + gins(optoas(ORSH, nl->type), &n2, &n3); // shift n3 nodconst(&n2, nl->type, w-1); gins(optoas(ORSH, nl->type), &n2, &n1); // -1 iff num is neg - gins(optoas(OSUB, nl->type), &n1, &dx); // added + gins(optoas(OSUB, nl->type), &n1, &n3); // added if(m.sd < 0) { // this could probably be removed // by factoring it into the multiplier - gins(optoas(OMINUS, nl->type), N, &dx); + gins(optoas(OMINUS, nl->type), N, &n3); } + gmove(&n3, res); regfree(&n1); - gmove(&dx, res); - - restx(&ax, &oldax); - restx(&dx, &olddx); + regfree(&n3); return; } goto longdiv; @@ -857,13 +751,49 @@ longmod: regfree(&n2); } +/* + * generate high multiply: + * res = (nl*nr) >> width + */ +void +cgen_hmul(Node *nl, Node *nr, Node *res) +{ + Type *t; + int a; + Node n1, n2, ax, dx, *tmp; + + t = nl->type; + a = optoas(OHMUL, t); + if(nl->ullman < nr->ullman) { + tmp = nl; + nl = nr; + nr = tmp; + } + cgenr(nl, &n1, res); + cgenr(nr, &n2, N); + nodreg(&ax, t, D_AX); + gmove(&n1, &ax); + gins(a, &n2, N); + regfree(&n2); + regfree(&n1); + + if(t->width == 1) { + // byte multiply behaves differently. + nodreg(&ax, t, D_AH); + nodreg(&dx, t, D_DL); + gmove(&ax, &dx); + } + nodreg(&dx, t, D_DX); + gmove(&dx, res); +} + /* * generate shift according to op, one of: * res = nl << nr * res = nl >> nr */ void -cgen_shift(int op, Node *nl, Node *nr, Node *res) +cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) { Node n1, n2, n3, n4, n5, cx, oldcx; int a, rcx; @@ -878,7 +808,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) cgen(nl, &n1); sc = mpgetfix(nr->val.u.xval); if(sc >= nl->type->width*8) { - // large shift gets 2 shifts by width + // large shift gets 2 shifts by width-1 nodconst(&n3, types[TUINT32], nl->type->width*8-1); gins(a, &n3, &n1); gins(a, &n3, &n1); @@ -937,17 +867,20 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) regfree(&n3); // test and fix up large shifts - nodconst(&n3, tcount, nl->type->width*8); - gins(optoas(OCMP, tcount), &n1, &n3); - p1 = gbranch(optoas(OLT, tcount), T); - if(op == ORSH && issigned[nl->type->etype]) { - nodconst(&n3, types[TUINT32], nl->type->width*8-1); - gins(a, &n3, &n2); - } else { - nodconst(&n3, nl->type, 0); - gmove(&n3, &n2); + if(!bounded) { + nodconst(&n3, tcount, nl->type->width*8); + gins(optoas(OCMP, tcount), &n1, &n3); + p1 = gbranch(optoas(OLT, tcount), T, +1); + if(op == ORSH && issigned[nl->type->etype]) { + nodconst(&n3, types[TUINT32], nl->type->width*8-1); + gins(a, &n3, &n2); + } else { + nodconst(&n3, nl->type, 0); + gmove(&n3, &n2); + } + patch(p1, pc); } - patch(p1, pc); + gins(a, &n1, &n2); if(oldcx.op != 0) { @@ -968,46 +901,40 @@ ret: /* * generate byte multiply: * res = nl * nr - * no 2-operand byte multiply instruction so have to do - * 16-bit multiply and take bottom half. + * there is no 2-operand byte multiply instruction so + * we do a full-width multiplication and truncate afterwards. */ void cgen_bmul(int op, Node *nl, Node *nr, Node *res) { - Node n1b, n2b, n1w, n2w; + Node n1, n2, n1b, n2b, *tmp; Type *t; int a; - if(nl->ullman >= nr->ullman) { - regalloc(&n1b, nl->type, res); - cgen(nl, &n1b); - regalloc(&n2b, nr->type, N); - cgen(nr, &n2b); - } else { - regalloc(&n2b, nr->type, N); - cgen(nr, &n2b); - regalloc(&n1b, nl->type, res); - cgen(nl, &n1b); + // largest ullman on left. + if(nl->ullman < nr->ullman) { + tmp = nl; + nl = nr; + nr = tmp; } - // copy from byte to short registers - t = types[TUINT16]; - if(issigned[nl->type->etype]) - t = types[TINT16]; - - regalloc(&n2w, t, &n2b); - cgen(&n2b, &n2w); - - regalloc(&n1w, t, &n1b); - cgen(&n1b, &n1w); + // generate operands in "8-bit" registers. + regalloc(&n1b, nl->type, res); + cgen(nl, &n1b); + regalloc(&n2b, nr->type, N); + cgen(nr, &n2b); + // perform full-width multiplication. + t = types[TUINT64]; + if(issigned[nl->type->etype]) + t = types[TINT64]; + nodreg(&n1, t, n1b.val.u.reg); + nodreg(&n2, t, n2b.val.u.reg); a = optoas(op, t); - gins(a, &n2w, &n1w); - cgen(&n1w, &n1b); - cgen(&n1b, res); + gins(a, &n2, &n1); - regfree(&n1w); - regfree(&n2w); + // truncate. + gmove(&n1, res); regfree(&n1b); regfree(&n2b); } @@ -1024,9 +951,9 @@ clearfat(Node *nl) w = nl->type->width; - if(w == 16) - if(componentgen(N, nl)) - return; + // Avoid taking the address for simple enough types. + if(componentgen(N, nl)) + return; c = w % 8; // bytes q = w / 8; // quads @@ -1060,366 +987,3 @@ clearfat(Node *nl) restx(&n1, &oldn1); restx(&ax, &oldax); } - -static int -regcmp(const void *va, const void *vb) -{ - Node *ra, *rb; - - ra = (Node*)va; - rb = (Node*)vb; - return ra->local - rb->local; -} - -static Prog* throwpc; - -void -getargs(NodeList *nn, Node *reg, int n) -{ - NodeList *l; - int i; - - throwpc = nil; - - l = nn; - for(i=0; in->right) && !isslice(l->n->right->type)) { - regalloc(reg+i, l->n->right->type, N); - cgen(l->n->right, reg+i); - } else - reg[i] = *l->n->right; - if(reg[i].local != 0) - yyerror("local used"); - reg[i].local = l->n->left->xoffset; - l = l->next; - } - qsort((void*)reg, n, sizeof(*reg), regcmp); - for(i=0; iop == OCONV && is64(nl->type)) - nl = nl->left; - if(nr->op == OCONV && is64(nr->type)) - nr = nr->left; - - op = OLE; - if(smallintconst(nl)) { - cl = mpgetfix(nl->val.u.xval); - if(cl == 0) - return; - if(smallintconst(nr)) - return; - // put the constant on the right - op = brrev(op); - c = nl; - nl = nr; - nr = c; - } - if(is64(nr->type) && smallintconst(nr)) - nr->type = types[TUINT32]; - - n1.op = OXXX; - t = types[TUINT32]; - if(nl->type->width != t->width || nr->type->width != t->width) { - if((is64(nl->type) && nl->op != OLITERAL) || (is64(nr->type) && nr->op != OLITERAL)) - t = types[TUINT64]; - - // Check if we need to use a temporary. - // At least one of the arguments is 32 bits - // (the len or cap) so one temporary suffices. - if(nl->type->width != t->width && nl->op != OLITERAL) { - regalloc(&n1, t, nl); - gmove(nl, &n1); - nl = &n1; - } else if(nr->type->width != t->width && nr->op != OLITERAL) { - regalloc(&n1, t, nr); - gmove(nr, &n1); - nr = &n1; - } - } - gins(optoas(OCMP, t), nl, nr); - if(n1.op != OXXX) - regfree(&n1); - if(throwpc == nil) { - p1 = gbranch(optoas(op, t), T); - throwpc = pc; - ginscall(panicslice, 0); - patch(p1, pc); - } else { - op = brcom(op); - p1 = gbranch(optoas(op, t), T); - patch(p1, throwpc); - } -} - -int -sleasy(Node *n) -{ - if(n->op != ONAME) - return 0; - if(!n->addable) - return 0; - return 1; -} - -// generate inline code for -// slicearray -// sliceslice -// arraytoslice -int -cgen_inline(Node *n, Node *res) -{ - Node nodes[5]; - Node n1, n2, nres, ntemp; - vlong v; - int i, narg, nochk; - - if(n->op != OCALLFUNC) - goto no; - if(!n->left->addable) - goto no; - if(n->left->sym == S) - goto no; - if(n->left->sym->pkg != runtimepkg) - goto no; - if(strcmp(n->left->sym->name, "slicearray") == 0) - goto slicearray; - if(strcmp(n->left->sym->name, "sliceslice") == 0) { - narg = 4; - goto sliceslice; - } - if(strcmp(n->left->sym->name, "sliceslice1") == 0) { - narg = 3; - goto sliceslice; - } - goto no; - -slicearray: - if(!sleasy(res)) - goto no; - getargs(n->list, nodes, 5); - - // if(hb[3] > nel[1]) goto throw - cmpandthrow(&nodes[3], &nodes[1]); - - // if(lb[2] > hb[3]) goto throw - cmpandthrow(&nodes[2], &nodes[3]); - - // len = hb[3] - lb[2] (destroys hb) - n2 = *res; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[3]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[3].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[3]); - gmove(&nodes[3], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } - - // cap = nel[1] - lb[2] (destroys nel) - n2 = *res; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[1]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[1].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[1]); - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } - - // if slice could be too big, dereference to - // catch nil array pointer. - if(nodes[0].op == OREGISTER && nodes[0].type->type->width >= unmappedzero) { - n2 = nodes[0]; - n2.xoffset = 0; - n2.op = OINDREG; - n2.type = types[TUINT8]; - gins(ATESTB, nodintconst(0), &n2); - } - - // ary = old[0] + (lb[2] * width[4]) (destroys old) - n2 = *res; - n2.xoffset += Array_array; - n2.type = types[tptr]; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { - v = mpgetfix(nodes[2].val.u.xval) * - mpgetfix(nodes[4].val.u.xval); - if(v != 0) - ginscon(optoas(OADD, types[tptr]), v, &nodes[0]); - } else { - regalloc(&n1, types[tptr], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[4]) || mpgetfix(nodes[4].val.u.xval) != 1) - gins(optoas(OMUL, types[tptr]), &nodes[4], &n1); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - regfree(&n1); - } - gins(optoas(OAS, types[tptr]), &nodes[0], &n2); - - for(i=0; i<5; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - return 1; - -sliceslice: - nochk = n->etype; // skip bounds checking - ntemp.op = OXXX; - if(!sleasy(n->list->n->right)) { - Node *n0; - - n0 = n->list->n->right; - tempname(&ntemp, res->type); - cgen(n0, &ntemp); - n->list->n->right = &ntemp; - getargs(n->list, nodes, narg); - n->list->n->right = n0; - } else - getargs(n->list, nodes, narg); - - nres = *res; // result - if(!sleasy(res)) { - if(ntemp.op == OXXX) - tempname(&ntemp, res->type); - nres = ntemp; - } - - if(narg == 3) { // old[lb:] - // move width to where it would be for old[lb:hb] - nodes[3] = nodes[2]; - nodes[2].op = OXXX; - - // if(lb[1] > old.nel[0]) goto throw; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - if(!nochk) - cmpandthrow(&nodes[1], &n2); - - // ret.nel = old.nel[0]-lb[1]; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - regalloc(&n1, types[TUINT32], N); - gins(optoas(OAS, types[TUINT32]), &n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } else { // old[lb:hb] - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - if(!nochk) { - // if(hb[2] > old.cap[0]) goto throw; - cmpandthrow(&nodes[2], &n2); - // if(lb[1] > hb[2]) goto throw; - cmpandthrow(&nodes[1], &nodes[2]); - } - // ret.len = hb[2]-lb[1]; (destroys hb[2]) - n2 = nres; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[1])) { - v = mpgetfix(nodes[2].val.u.xval) - - mpgetfix(nodes[1].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } - } - - // ret.cap = old.cap[0]-lb[1]; (uses hb[2]) - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - - regalloc(&n1, types[TUINT32], &nodes[2]); - gins(optoas(OAS, types[TUINT32]), &n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - - // ret.array = old.array[0]+lb[1]*width[3]; (uses lb[1]) - n2 = nodes[0]; - n2.xoffset += Array_array; - n2.type = types[tptr]; - regalloc(&n1, types[tptr], &nodes[1]); - if(smallintconst(&nodes[1]) && smallintconst(&nodes[3])) { - gins(optoas(OAS, types[tptr]), &n2, &n1); - v = mpgetfix(nodes[1].val.u.xval) * - mpgetfix(nodes[3].val.u.xval); - if(v != 0) - ginscon(optoas(OADD, types[tptr]), v, &n1); - } else { - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) - gins(optoas(OMUL, types[tptr]), &nodes[3], &n1); - gins(optoas(OADD, types[tptr]), &n2, &n1); - } - - n2 = nres; - n2.xoffset += Array_array; - n2.type = types[tptr]; - gins(optoas(OAS, types[tptr]), &n1, &n2); - regfree(&n1); - - for(i=0; i<4; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - - if(!sleasy(res)) { - cgen(&nres, res); - } - return 1; - -no: - return 0; -} diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c index 8c9208374..508a3548f 100644 --- a/src/cmd/6g/gobj.c +++ b/src/cmd/6g/gobj.c @@ -94,9 +94,9 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) switch(a->type) { case D_BRANCH: - if(a->branch == nil) + if(a->u.branch == nil) fatal("unpatched branch"); - a->offset = a->branch->loc; + a->offset = a->u.branch->loc; default: t |= T_TYPE; @@ -139,7 +139,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) if(t & T_SYM) /* implies sym */ Bputc(b, s); if(t & T_FCONST) { - ieeedtod(&e, a->dval); + ieeedtod(&e, a->u.dval); l = e; Bputc(b, l); Bputc(b, l>>8); @@ -153,7 +153,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) return; } if(t & T_SCONST) { - n = a->sval; + n = a->u.sval; for(i=0; ito.type = D_SCONST; p->to.index = D_NONE; - memmove(p->to.sval, t, n); + memmove(p->to.u.sval, t, n); return off + n; } @@ -312,8 +312,8 @@ datastring(char *s, int len, Addr *a) a->type = D_EXTERN; a->sym = sym; a->node = sym->def; - a->offset = widthptr+4; // skip header - a->etype = TINT32; + a->offset = widthptr+widthint; // skip header + a->etype = simtype[TINT]; } /* @@ -324,7 +324,7 @@ void datagostring(Strlit *sval, Addr *a) { Sym *sym; - + sym = stringsym(sval->s, sval->len); a->type = D_EXTERN; a->sym = sym; @@ -364,13 +364,13 @@ gdatacomplex(Node *nam, Mpcplx *cval) p = gins(ADATA, nam, N); p->from.scale = w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->real); + p->to.u.dval = mpgetflt(&cval->real); p = gins(ADATA, nam, N); p->from.scale = w; p->from.offset += w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->imag); + p->to.u.dval = mpgetflt(&cval->imag); } void @@ -386,10 +386,10 @@ gdatastring(Node *nam, Strlit *sval) p->to.type = D_ADDR; //print("%P\n", p); - nodconst(&nod1, types[TINT32], sval->len); + nodconst(&nod1, types[TINT], sval->len); p = gins(ADATA, nam, &nod1); - p->from.scale = types[TINT32]->width; - p->from.offset += types[tptr]->width; + p->from.scale = widthint; + p->from.offset += widthptr; } int @@ -408,7 +408,7 @@ dstringptr(Sym *s, int off, char *str) datastring(str, strlen(str)+1, &p->to); p->to.index = p->to.type; p->to.type = D_ADDR; - p->to.etype = TINT32; + p->to.etype = simtype[TINT]; off += widthptr; return off; @@ -432,7 +432,7 @@ dgostrlitptr(Sym *s, int off, Strlit *lit) datagostring(lit, &p->to); p->to.index = p->to.type; p->to.type = D_ADDR; - p->to.etype = TINT32; + p->to.etype = simtype[TINT]; off += widthptr; return off; diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index ededcf673..fc5407a1f 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -103,9 +103,13 @@ dumpdata(void) /* * generate a branch. * t is ignored. + * likely values are for branch prediction: + * -1 unlikely + * 0 no opinion + * +1 likely */ Prog* -gbranch(int as, Type *t) +gbranch(int as, Type *t, int likely) { Prog *p; @@ -113,7 +117,11 @@ gbranch(int as, Type *t) p = prog(as); p->to.type = D_BRANCH; - p->to.branch = P; + p->to.u.branch = P; + if(as != AJMP && likely != 0) { + p->from.type = D_CONST; + p->from.offset = likely > 0; + } return p; } @@ -125,7 +133,7 @@ patch(Prog *p, Prog *to) { if(p->to.type != D_BRANCH) fatal("patch: not a branch"); - p->to.branch = to; + p->to.u.branch = to; p->to.offset = to->loc; } @@ -136,8 +144,8 @@ unpatch(Prog *p) if(p->to.type != D_BRANCH) fatal("unpatch: not a branch"); - q = p->to.branch; - p->to.branch = P; + q = p->to.u.branch; + p->to.u.branch = P; p->to.offset = 0; return q; } @@ -164,44 +172,6 @@ newplist(void) return pl; } -void -clearstk(void) -{ - Plist *pl; - Prog *p1, *p2; - Node sp, di, cx, con, ax; - - if((uint32)plast->firstpc->to.offset <= 0) - return; - - // reestablish context for inserting code - // at beginning of function. - pl = plast; - p1 = pl->firstpc; - p2 = p1->link; - pc = mal(sizeof(*pc)); - clearp(pc); - p1->link = pc; - - // zero stack frame - nodreg(&sp, types[tptr], D_SP); - nodreg(&di, types[tptr], D_DI); - nodreg(&cx, types[TUINT64], D_CX); - nodconst(&con, types[TUINT64], (uint32)p1->to.offset / widthptr); - gins(ACLD, N, N); - gins(AMOVQ, &sp, &di); - gins(AMOVQ, &con, &cx); - nodconst(&con, types[TUINT64], 0); - nodreg(&ax, types[TUINT64], D_AX); - gins(AMOVQ, &con, &ax); - gins(AREP, N, N); - gins(ASTOSQ, N, N); - - // continue with original code. - gins(ANOP, N, N)->link = p2; - pc = P; -} - void gused(Node *n) { @@ -213,22 +183,23 @@ gjmp(Prog *to) { Prog *p; - p = gbranch(AJMP, T); + p = gbranch(AJMP, T, 0); if(to != P) patch(p, to); return p; } void -ggloblnod(Node *nam, int32 width) +ggloblnod(Node *nam) { Prog *p; p = gins(AGLOBL, nam, N); p->lineno = nam->lineno; + p->from.gotype = ngotype(nam); p->to.sym = S; p->to.type = D_CONST; - p->to.offset = width; + p->to.offset = nam->type->width; if(nam->readonly) p->from.scale = RODATA; if(nam->type != T && !haspointers(nam->type)) @@ -236,7 +207,18 @@ ggloblnod(Node *nam, int32 width) } void -ggloblsym(Sym *s, int32 width, int dupok) +gtrack(Sym *s) +{ + Prog *p; + + p = gins(AUSEFIELD, N, N); + p->from.type = D_EXTERN; + p->from.index = D_NONE; + p->from.sym = s; +} + +void +ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -248,8 +230,9 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.index = D_NONE; p->to.offset = width; if(dupok) - p->from.scale = DUPOK; - p->from.scale |= RODATA; + p->from.scale |= DUPOK; + if(rodata) + p->from.scale |= RODATA; } int @@ -272,11 +255,12 @@ isfat(Type *t) * call afunclit to fix up the argument. */ void -afunclit(Addr *a) +afunclit(Addr *a, Node *n) { if(a->type == D_ADDR && a->index == D_EXTERN) { a->type = D_EXTERN; a->index = D_NONE; + a->sym = n->sym; } } @@ -300,7 +284,7 @@ ginit(void) reg[i] = 1; for(i=D_AX; i<=D_R15; i++) reg[i] = 0; - for(i=D_X0; i<=D_X7; i++) + for(i=D_X0; i<=D_X15; i++) reg[i] = 0; for(i=0; iop == OREGISTER) { i = o->val.u.reg; - if(i >= D_X0 && i <= D_X7) + if(i >= D_X0 && i <= D_X15) goto out; } - for(i=D_X0; i<=D_X7; i++) + for(i=D_X0; i<=D_X15; i++) if(reg[i] == 0) goto out; fatal("out of floating registers"); @@ -572,6 +556,10 @@ ismem(Node *n) case ONAME: case OPARAM: return 1; + case OADDR: + if(flag_largemodel) + return 1; + break; } return 0; } @@ -616,7 +604,7 @@ gmove(Node *f, Node *t) Prog *p1, *p2; if(debug['M']) - print("gmove %N -> %N\n", f, t); + print("gmove %lN -> %lN\n", f, t); ft = simsimtype(f->type); tt = simsimtype(t->type); @@ -706,11 +694,14 @@ gmove(Node *f, Node *t) case CASE(TINT32, TUINT32): case CASE(TUINT32, TINT32): case CASE(TUINT32, TUINT32): + a = AMOVL; + break; + case CASE(TINT64, TINT32): // truncate case CASE(TUINT64, TINT32): case CASE(TINT64, TUINT32): case CASE(TUINT64, TUINT32): - a = AMOVL; + a = AMOVQL; break; case CASE(TINT64, TINT64): // same size @@ -822,9 +813,9 @@ gmove(Node *f, Node *t) // algorithm is: // if small enough, use native float64 -> int64 conversion. // otherwise, subtract 2^63, convert, and add it back. - a = ACVTSS2SQ; + a = ACVTTSS2SQ; if(ft == TFLOAT64) - a = ACVTSD2SQ; + a = ACVTTSD2SQ; bignodes(); regalloc(&r1, types[ft], N); regalloc(&r2, types[tt], t); @@ -832,9 +823,9 @@ gmove(Node *f, Node *t) regalloc(&r4, types[tt], N); gins(optoas(OAS, f->type), f, &r1); gins(optoas(OCMP, f->type), &bigf, &r1); - p1 = gbranch(optoas(OLE, f->type), T); + p1 = gbranch(optoas(OLE, f->type), T, +1); gins(a, &r1, &r2); - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); gins(optoas(OAS, f->type), &bigf, &r3); gins(optoas(OSUB, f->type), &r3, &r1); @@ -903,9 +894,9 @@ gmove(Node *f, Node *t) regalloc(&r4, f->type, N); gmove(f, &r1); gins(ACMPQ, &r1, &zero); - p1 = gbranch(AJLT, T); + p1 = gbranch(AJLT, T, +1); gins(a, &r1, &r2); - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); gmove(&r1, &r3); gins(ASHRQ, &one, &r3); @@ -1016,6 +1007,13 @@ gins(int as, Node *f, Node *t) case AMOVSD: if(f != N && t != N && samaddr(f, t)) return nil; + break; + + case ALEAQ: + if(f != N && isconst(f, CTNIL)) { + fatal("gins LEAQ nil %T", f->type); + } + break; } memset(&af, 0, sizeof af); @@ -1047,13 +1045,34 @@ gins(int as, Node *f, Node *t) w = 8; break; } - if(w != 0 && f != N && (af.width > w || at.width > w)) { + if(w != 0 && ((f != N && af.width < w) || (t != N && at.width > w))) { + dump("f", f); + dump("t", t); fatal("bad width: %P (%d, %d)\n", p, af.width, at.width); } return p; } +// Generate an instruction referencing *n +// to force segv on nil pointer dereference. +void +checkref(Node *n) +{ + Node m; + + if(n->type->type->width < unmappedzero) + return; + + regalloc(&m, types[TUINTPTR], n); + cgen(n, &m); + m.xoffset = 0; + m.op = OINDREG; + m.type = types[TUINT8]; + gins(ATESTB, nodintconst(0), &m); + regfree(&m); +} + static void checkoffset(Addr *a, int canemitcode) { @@ -1079,14 +1098,22 @@ checkoffset(Addr *a, int canemitcode) void naddr(Node *n, Addr *a, int canemitcode) { + Prog *p; + a->scale = 0; a->index = D_NONE; a->type = D_NONE; a->gotype = S; a->node = N; + a->width = 0; if(n == N) return; + if(n->type != T && n->type->etype != TIDEAL) { + dowidth(n->type); + a->width = n->type->width; + } + switch(n->op) { default: fatal("naddr: bad %O %D", n->op, a); @@ -1134,15 +1161,27 @@ naddr(Node *n, Addr *a, int canemitcode) a->type = D_PARAM; a->node = n->left->orig; break; + + case OCLOSUREVAR: + if(!canemitcode) + fatal("naddr OCLOSUREVAR cannot emit code"); + p = gins(AMOVQ, N, N); + p->from.type = D_DX+D_INDIR; + p->from.offset = n->xoffset; + p->to.type = D_BX; + a->type = D_BX; + a->sym = S; + break; + + case OCFUNC: + naddr(n->left, a, canemitcode); + a->sym = n->left->sym; + break; case ONAME: a->etype = 0; - a->width = 0; - if(n->type != T) { + if(n->type != T) a->etype = simtype[n->type->etype]; - a->width = n->type->width; - a->gotype = ngotype(n); - } a->offset = n->xoffset; a->sym = n->sym; a->node = n->orig; @@ -1173,6 +1212,8 @@ naddr(Node *n, Addr *a, int canemitcode) case PFUNC: a->index = D_EXTERN; a->type = D_ADDR; + a->width = widthptr; + a->sym = funcsym(a->sym); break; } break; @@ -1184,7 +1225,7 @@ naddr(Node *n, Addr *a, int canemitcode) break; case CTFLT: a->type = D_FCONST; - a->dval = mpgetflt(n->val.u.fval); + a->u.dval = mpgetflt(n->val.u.fval); break; case CTINT: case CTRUNE: @@ -1210,6 +1251,7 @@ naddr(Node *n, Addr *a, int canemitcode) case OADDR: naddr(n->left, a, canemitcode); + a->width = widthptr; if(a->type >= D_INDIR) { a->type -= D_INDIR; break; @@ -1239,9 +1281,9 @@ naddr(Node *n, Addr *a, int canemitcode) naddr(n->left, a, canemitcode); if(a->type == D_CONST && a->offset == 0) break; // len(nil) - a->etype = TUINT32; + a->etype = simtype[TUINT]; a->offset += Array_nel; - a->width = 4; + a->width = widthint; if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) checkoffset(a, canemitcode); break; @@ -1251,9 +1293,9 @@ naddr(Node *n, Addr *a, int canemitcode) naddr(n->left, a, canemitcode); if(a->type == D_CONST && a->offset == 0) break; // cap(nil) - a->etype = TUINT32; + a->etype = simtype[TUINT]; a->offset += Array_cap; - a->width = 4; + a->width = widthint; if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero) checkoffset(a, canemitcode); break; @@ -1645,6 +1687,28 @@ optoas(int op, Type *t) a = AXORQ; break; + case CASE(OLROT, TINT8): + case CASE(OLROT, TUINT8): + a = AROLB; + break; + + case CASE(OLROT, TINT16): + case CASE(OLROT, TUINT16): + a = AROLW; + break; + + case CASE(OLROT, TINT32): + case CASE(OLROT, TUINT32): + case CASE(OLROT, TPTR32): + a = AROLL; + break; + + case CASE(OLROT, TINT64): + case CASE(OLROT, TUINT64): + case CASE(OLROT, TPTR64): + a = AROLQ; + break; + case CASE(OLSH, TINT8): case CASE(OLSH, TUINT8): a = ASHLB; @@ -1701,23 +1765,23 @@ optoas(int op, Type *t) a = ASARQ; break; - case CASE(ORRC, TINT8): - case CASE(ORRC, TUINT8): + case CASE(ORROTC, TINT8): + case CASE(ORROTC, TUINT8): a = ARCRB; break; - case CASE(ORRC, TINT16): - case CASE(ORRC, TUINT16): + case CASE(ORROTC, TINT16): + case CASE(ORROTC, TUINT16): a = ARCRW; break; - case CASE(ORRC, TINT32): - case CASE(ORRC, TUINT32): + case CASE(ORROTC, TINT32): + case CASE(ORROTC, TUINT32): a = ARCRL; break; - case CASE(ORRC, TINT64): - case CASE(ORRC, TUINT64): + case CASE(ORROTC, TINT64): + case CASE(ORROTC, TUINT64): a = ARCRQ; break; @@ -1919,6 +1983,9 @@ sudoaddable(int as, Node *n, Addr *a) goto odot; case OINDEX: + return 0; + // disabled: OINDEX case is now covered by agenr + // for a more suitable register allocation pattern. if(n->left->type->etype == TSTRING) return 0; goto oindex; @@ -2053,32 +2120,20 @@ oindex: } // check bounds - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { // check bounds n4.op = OXXX; - t = types[TUINT32]; + t = types[simtype[TUINT]]; if(o & ODynam) { if(o & OAddable) { n2 = *l; n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - if(is64(r->type)) { - t = types[TUINT64]; - regalloc(&n4, t, N); - gmove(&n2, &n4); - n2 = n4; - } + n2.type = types[simtype[TUINT]]; } else { n2 = *reg; n2.xoffset = Array_nel; n2.op = OINDREG; - n2.type = types[TUINT32]; - if(is64(r->type)) { - t = types[TUINT64]; - regalloc(&n4, t, N); - gmove(&n2, &n4); - n2 = n4; - } + n2.type = types[simtype[TUINT]]; } } else { if(is64(r->type)) @@ -2086,10 +2141,10 @@ oindex: nodconst(&n2, types[TUINT64], l->type->bound); } gins(optoas(OCMP, t), reg1, &n2); - p1 = gbranch(optoas(OLT, t), T); + p1 = gbranch(optoas(OLT, t), T, +1); if(n4.op != OXXX) regfree(&n4); - ginscall(panicindex, 0); + ginscall(panicindex, -1); patch(p1, pc); } @@ -2140,19 +2195,19 @@ oindex_const: reg->op = OEMPTY; reg1->op = OEMPTY; - regalloc(reg, types[tptr], N); - agen(l, reg); - if(o & ODynam) { - if(!debug['B'] && !n->etype) { + regalloc(reg, types[tptr], N); + agen(l, reg); + + if(!debug['B'] && !n->bounded) { n1 = *reg; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; nodconst(&n2, types[TUINT64], v); - gins(optoas(OCMP, types[TUINT32]), &n1, &n2); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); - ginscall(panicindex, 0); + gins(optoas(OCMP, types[simtype[TUINT]]), &n1, &n2); + p1 = gbranch(optoas(OGT, types[simtype[TUINT]]), T, +1); + ginscall(panicindex, -1); patch(p1, pc); } @@ -2162,14 +2217,24 @@ oindex_const: n1.xoffset = Array_array; gmove(&n1, reg); + n2 = *reg; + n2.op = OINDREG; + n2.xoffset = v*w; + a->type = D_NONE; + a->index = D_NONE; + naddr(&n2, a, 1); + goto yes; } - - n2 = *reg; - n2.op = OINDREG; - n2.xoffset = v*w; + + igen(l, &n1, N); + if(n1.op == OINDREG) { + *reg = n1; + reg->op = OREGISTER; + } + n1.xoffset += v*w; a->type = D_NONE; - a->index = D_NONE; - naddr(&n2, a, 1); + a->index= D_NONE; + naddr(&n1, a, 1); goto yes; oindex_const_sudo: @@ -2180,13 +2245,13 @@ oindex_const_sudo: } // slice indexed by a constant - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { a->offset += Array_nel; nodconst(&n2, types[TUINT64], v); - p1 = gins(optoas(OCMP, types[TUINT32]), N, &n2); + p1 = gins(optoas(OCMP, types[simtype[TUINT]]), N, &n2); p1->from = *a; - p1 = gbranch(optoas(OGT, types[TUINT32]), T); - ginscall(panicindex, 0); + p1 = gbranch(optoas(OGT, types[simtype[TUINT]]), T, +1); + ginscall(panicindex, -1); patch(p1, pc); a->offset -= Array_nel; } diff --git a/src/cmd/6g/list.c b/src/cmd/6g/list.c index ad63f7d29..9d27a6a09 100644 --- a/src/cmd/6g/list.c +++ b/src/cmd/6g/list.c @@ -107,10 +107,10 @@ Dconv(Fmt *fp) break; case D_BRANCH: - if(a->branch == nil) + if(a->u.branch == nil) snprint(str, sizeof(str), ""); else - snprint(str, sizeof(str), "%d", a->branch->loc); + snprint(str, sizeof(str), "%d", a->u.branch->loc); break; case D_EXTERN: @@ -140,11 +140,11 @@ Dconv(Fmt *fp) break; case D_FCONST: - snprint(str, sizeof(str), "$(%.17e)", a->dval); + snprint(str, sizeof(str), "$(%.17e)", a->u.dval); break; case D_SCONST: - snprint(str, sizeof(str), "$\"%Y\"", a->sval); + snprint(str, sizeof(str), "$\"%Y\"", a->u.sval); break; case D_ADDR: @@ -161,7 +161,10 @@ brk: strcat(str, s); } conv: - return fmtstrcpy(fp, str); + fmtstrcpy(fp, str); + if(a->gotype) + fmtprint(fp, "{%s}", a->gotype->name); + return 0; } static char* regstr[] = diff --git a/src/cmd/6g/opt.h b/src/cmd/6g/opt.h index 9a8866b8d..9b0ea1b5a 100644 --- a/src/cmd/6g/opt.h +++ b/src/cmd/6g/opt.h @@ -34,8 +34,6 @@ #define D_HI D_NONE #define D_LO D_NONE -#define isregtype(t) ((t)>= D_AX && (t)<=D_R15) - #define BLOAD(r) band(bnot(r->refbehind), r->refahead) #define BSTORE(r) band(bnot(r->calbehind), r->calahead) #define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z]) @@ -49,12 +47,16 @@ typedef struct Reg Reg; typedef struct Rgn Rgn; +// A Reg is a wrapper around a single Prog (one instruction) that holds +// register optimization information while the optimizer runs. +// r->prog is the instruction. +// r->prog->regp points back to r. struct Reg { - Bits set; - Bits use1; - Bits use2; + Bits set; // variables written by this instruction. + Bits use1; // variables read by prog->from. + Bits use2; // variables read by prog->to. Bits refbehind; Bits refahead; @@ -70,13 +72,13 @@ struct Reg uint16 loop; // x5 for every loop uchar refset; // diagnostic generated - Reg* p1; - Reg* p2; + Reg* p1; // predecessors of this instruction: p1, + Reg* p2; // and then p2 linked though p2link. Reg* p2link; - Reg* s1; + Reg* s1; // successors of this instruction (at most two: s1 and s2). Reg* s2; - Reg* link; - Prog* prog; + Reg* link; // next instruction in function code + Prog* prog; // actual instruction }; #define R ((Reg*)0) diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c index 3710033b2..569655786 100644 --- a/src/cmd/6g/peep.c +++ b/src/cmd/6g/peep.c @@ -34,6 +34,10 @@ #include "opt.h" static void conprop(Reg *r); +static void elimshortmov(Reg *r); +static int prevl(Reg *r, int reg); +static void pushback(Reg *r); +static int regconsttyp(Adr*); // do we need the carry bit static int @@ -45,11 +49,17 @@ needc(Prog *p) case AADCQ: case ASBBL: case ASBBQ: + case ARCRB: + case ARCRW: case ARCRL: case ARCRQ: return 1; + case AADDB: + case AADDW: case AADDL: case AADDQ: + case ASUBB: + case ASUBW: case ASUBL: case ASUBQ: case AJMP: @@ -122,9 +132,14 @@ peep(void) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: p = p->link; } } + + // byte, word arithmetic elimination. + elimshortmov(r); // constant propagation // find MOV $con,R followed by @@ -200,6 +215,7 @@ loop1: case AMOVWQZX: case AMOVLQSX: case AMOVLQZX: + case AMOVQL: if(regtyp(&p->to)) { r1 = rnops(uniqs(r)); if(r1 != R) { @@ -272,6 +288,115 @@ loop1: } if(t) goto loop1; + + // MOVLQZX removal. + // The MOVLQZX exists to avoid being confused for a + // MOVL that is just copying 32-bit data around during + // copyprop. Now that copyprop is done, remov MOVLQZX R1, R2 + // if it is dominated by an earlier ADDL/MOVL/etc into R1 that + // will have already cleared the high bits. + // + // MOVSD removal. + // We never use packed registers, so a MOVSD between registers + // can be replaced by MOVAPD, which moves the pair of float64s + // instead of just the lower one. We only use the lower one, but + // the processor can do better if we do moves using both. + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(p->as == AMOVLQZX) + if(regtyp(&p->from)) + if(p->from.type == p->to.type) + if(prevl(r, p->from.type)) + excise(r); + + if(p->as == AMOVSD) + if(regtyp(&p->from)) + if(regtyp(&p->to)) + p->as = AMOVAPD; + } + + // load pipelining + // push any load from memory as early as possible + // to give it time to complete before use. + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case AMOVB: + case AMOVW: + case AMOVL: + case AMOVQ: + case AMOVLQZX: + if(regtyp(&p->to) && !regconsttyp(&p->from)) + pushback(r); + } + } +} + +static void +pushback(Reg *r0) +{ + Reg *r, *b; + Prog *p0, *p, t; + + b = R; + p0 = r0->prog; + for(r=uniqp(r0); r!=R && uniqs(r)!=R; r=uniqp(r)) { + p = r->prog; + if(p->as != ANOP) { + if(!regconsttyp(&p->from) || !regtyp(&p->to)) + break; + if(copyu(p, &p0->to, A) || copyu(p0, &p->to, A)) + break; + } + if(p->as == ACALL) + break; + b = r; + } + + if(b == R) { + if(debug['v']) { + print("no pushback: %P\n", r0->prog); + if(r) + print("\t%P [%d]\n", r->prog, uniqs(r)!=R); + } + return; + } + + if(debug['v']) { + print("pushback\n"); + for(r=b;; r=r->link) { + print("\t%P\n", r->prog); + if(r == r0) + break; + } + } + + t = *r0->prog; + for(r=uniqp(r0);; r=uniqp(r)) { + p0 = r->link->prog; + p = r->prog; + p0->as = p->as; + p0->lineno = p->lineno; + p0->from = p->from; + p0->to = p->to; + + if(r == b) + break; + } + p0 = r->prog; + p0->as = t.as; + p0->lineno = t.lineno; + p0->from = t.from; + p0->to = t.to; + + if(debug['v']) { + print("\tafter\n"); + for(r=b;; r=r->link) { + print("\t%P\n", r->prog); + if(r == r0) + break; + } + } } void @@ -335,6 +460,155 @@ regtyp(Adr *a) return 0; } +// movb elimination. +// movb is simulated by the linker +// when a register other than ax, bx, cx, dx +// is used, so rewrite to other instructions +// when possible. a movb into a register +// can smash the entire 32-bit register without +// causing any trouble. +static void +elimshortmov(Reg *r) +{ + Prog *p; + + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(regtyp(&p->to)) { + switch(p->as) { + case AINCB: + case AINCW: + p->as = AINCQ; + break; + case ADECB: + case ADECW: + p->as = ADECQ; + break; + case ANEGB: + case ANEGW: + p->as = ANEGQ; + break; + case ANOTB: + case ANOTW: + p->as = ANOTQ; + break; + } + if(regtyp(&p->from) || p->from.type == D_CONST) { + // move or artihmetic into partial register. + // from another register or constant can be movl. + // we don't switch to 64-bit arithmetic if it can + // change how the carry bit is set (and the carry bit is needed). + switch(p->as) { + case AMOVB: + case AMOVW: + p->as = AMOVQ; + break; + case AADDB: + case AADDW: + if(!needc(p->link)) + p->as = AADDQ; + break; + case ASUBB: + case ASUBW: + if(!needc(p->link)) + p->as = ASUBQ; + break; + case AMULB: + case AMULW: + p->as = AMULQ; + break; + case AIMULB: + case AIMULW: + p->as = AIMULQ; + break; + case AANDB: + case AANDW: + p->as = AANDQ; + break; + case AORB: + case AORW: + p->as = AORQ; + break; + case AXORB: + case AXORW: + p->as = AXORQ; + break; + case ASHLB: + case ASHLW: + p->as = ASHLQ; + break; + } + } else if(p->from.type >= D_NONE) { + // explicit zero extension, but don't + // do that if source is a byte register + // (only AH can occur and it's forbidden). + switch(p->as) { + case AMOVB: + p->as = AMOVBQZX; + break; + case AMOVW: + p->as = AMOVWQZX; + break; + } + } + } + } +} + +int +regconsttyp(Adr *a) +{ + if(regtyp(a)) + return 1; + switch(a->type) { + case D_CONST: + case D_FCONST: + case D_SCONST: + case D_ADDR: + return 1; + } + return 0; +} + +// is reg guaranteed to be truncated by a previous L instruction? +static int +prevl(Reg *r0, int reg) +{ + Prog *p; + Reg *r; + + for(r=uniqp(r0); r!=R; r=uniqp(r)) { + p = r->prog; + if(p->to.type == reg) { + switch(p->as) { + case AADDL: + case AANDL: + case ADECL: + case ADIVL: + case AIDIVL: + case AIMULL: + case AINCL: + case AMOVL: + case AMULL: + case AORL: + case ARCLL: + case ARCRL: + case AROLL: + case ARORL: + case ASALL: + case ASARL: + case ASHLL: + case ASHRL: + case ASUBL: + case AXORL: + return 1; + } + return 0; + } + } + return 0; +} + /* * the idea is to substitute * one register for another @@ -357,19 +631,34 @@ subprop(Reg *r0) Reg *r; int t; + if(debug['P'] && debug['v']) + print("subprop %P\n", r0->prog); p = r0->prog; v1 = &p->from; - if(!regtyp(v1)) + if(!regtyp(v1)) { + if(debug['P'] && debug['v']) + print("\tnot regtype %D; return 0\n", v1); return 0; + } v2 = &p->to; - if(!regtyp(v2)) + if(!regtyp(v2)) { + if(debug['P'] && debug['v']) + print("\tnot regtype %D; return 0\n", v2); return 0; + } for(r=uniqp(r0); r!=R; r=uniqp(r)) { - if(uniqs(r) == R) + if(debug['P'] && debug['v']) + print("\t? %P\n", r->prog); + if(uniqs(r) == R) { + if(debug['P'] && debug['v']) + print("\tno unique successor\n"); break; + } p = r->prog; switch(p->as) { case ACALL: + if(debug['P'] && debug['v']) + print("\tfound %P; return 0\n", p); return 0; case AIMULL: @@ -377,20 +666,7 @@ subprop(Reg *r0) case AIMULW: if(p->to.type != D_NONE) break; - - case ADIVB: - case ADIVL: - case ADIVQ: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVQ: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULQ: - case AMULW: + goto giveup; case ARCLB: case ARCLL: @@ -424,6 +700,23 @@ subprop(Reg *r0) case ASHRL: case ASHRQ: case ASHRW: + if(p->from.type == D_CONST) + break; + goto giveup; + + case ADIVB: + case ADIVL: + case ADIVQ: + case ADIVW: + case AIDIVB: + case AIDIVL: + case AIDIVQ: + case AIDIVW: + case AIMULB: + case AMULB: + case AMULL: + case AMULQ: + case AMULW: case AREP: case AREPN: @@ -438,21 +731,34 @@ subprop(Reg *r0) case AMOVSB: case AMOVSL: case AMOVSQ: + giveup: + if(debug['P'] && debug['v']) + print("\tfound %P; return 0\n", p); return 0; case AMOVL: case AMOVQ: + case AMOVSS: + case AMOVSD: if(p->to.type == v1->type) goto gotit; break; } if(copyau(&p->from, v2) || - copyau(&p->to, v2)) + copyau(&p->to, v2)) { + if(debug['P'] && debug['v']) + print("\tcopyau %D failed\n", v2); break; + } if(copysub(&p->from, v1, v2, 0) || - copysub(&p->to, v1, v2, 0)) + copysub(&p->to, v1, v2, 0)) { + if(debug['P'] && debug['v']) + print("\tcopysub failed\n"); break; + } } + if(debug['P'] && debug['v']) + print("\tran off end; return 0\n", p); return 0; gotit: @@ -497,6 +803,8 @@ copyprop(Reg *r0) Adr *v1, *v2; Reg *r; + if(debug['P'] && debug['v']) + print("copyprop %P\n", r0->prog); p = r0->prog; v1 = &p->from; v2 = &p->to; @@ -636,6 +944,7 @@ copyu(Prog *p, Adr *v, Adr *s) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case AMOVQL: case AMOVSS: case AMOVSD: @@ -853,8 +1162,6 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; case ARET: /* funny */ - if(v->type == REGRET || v->type == FREGRET) - return 2; if(s != A) return 1; return 3; @@ -864,6 +1171,8 @@ copyu(Prog *p, Adr *v, Adr *s) return 2; if(REGARG >= 0 && v->type == (uchar)REGARG) return 2; + if(v->type == p->from.type) + return 2; if(s != A) { if(copysub(&p->to, v, s, 1)) @@ -907,13 +1216,22 @@ int copyau(Adr *a, Adr *v) { - if(copyas(a, v)) + if(copyas(a, v)) { + if(debug['P'] && debug['v']) + print("\tcopyau: copyas returned 1\n"); return 1; + } if(regtyp(v)) { - if(a->type-D_INDIR == v->type) + if(a->type-D_INDIR == v->type) { + if(debug['P'] && debug['v']) + print("\tcopyau: found indir use - return 1\n"); return 1; - if(a->index == v->type) + } + if(a->index == v->type) { + if(debug['P'] && debug['v']) + print("\tcopyau: found index use - return 1\n"); return 1; + } } return 0; } @@ -990,7 +1308,7 @@ loop: if(p->from.node == p0->from.node) if(p->from.offset == p0->from.offset) if(p->from.scale == p0->from.scale) - if(p->from.dval == p0->from.dval) + if(p->from.u.vval == p0->from.u.vval) if(p->from.index == p0->from.index) { excise(r); goto loop; diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index 049c63f17..c56d71678 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -151,6 +151,8 @@ static char* regname[] = { ".X15", }; +static Node* regnodes[NREGVAR]; + static void fixjmp(Prog*); void @@ -164,7 +166,7 @@ regopt(Prog *firstp) if(first) { fmtinstall('Q', Qconv); - exregoffset = D_R13; // R14,R15 are external + exregoffset = D_R15; first = 0; } @@ -191,8 +193,11 @@ regopt(Prog *firstp) */ nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; ias == ACALL && p->to.type == D_EXTERN) + continue; + + // Addressing makes some registers used. + if(p->from.type >= D_INDIR) + r->use1.b[0] |= RtoB(p->from.type-D_INDIR); + if(p->from.index != D_NONE) + r->use1.b[0] |= RtoB(p->from.index); + if(p->to.type >= D_INDIR) + r->use2.b[0] |= RtoB(p->to.type-D_INDIR); + if(p->to.index != D_NONE) + r->use2.b[0] |= RtoB(p->to.index); + bit = mkvar(r, &p->from); if(bany(&bit)) switch(p->as) { @@ -326,6 +347,7 @@ regopt(Prog *firstp) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case AMOVQL: case APOPQ: case AMOVSS: @@ -578,8 +600,9 @@ regopt(Prog *firstp) addrs.b[z] |= bit.b[z]; } -// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n", -// i, v->addr, v->etype, v->width, v->sym, v->offset); + if(debug['R'] && debug['v']) + print("bit=%2d addr=%d et=%-6E w=%-2d s=%N + %lld\n", + i, v->addr, v->etype, v->width, v->node, v->offset); } if(debug['R'] && debug['v']) @@ -593,9 +616,9 @@ regopt(Prog *firstp) for(r=firstr; r!=R; r=r->link) { p = r->prog; if(p->to.type == D_BRANCH) { - if(p->to.branch == P) + if(p->to.u.branch == P) fatal("pnil %P", p); - r1 = p->to.branch->reg; + r1 = p->to.u.branch->reg; if(r1 == R) fatal("rnil %P", p); if(r1 == r) { @@ -742,6 +765,9 @@ loop2: brk: qsort(region, nregion, sizeof(region[0]), rcmp); + if(debug['R'] && debug['v']) + dumpit("pass5", firstr); + /* * pass 6 * determine used registers (paint2) @@ -752,8 +778,16 @@ brk: bit = blsh(rgp->varno); vreg = paint2(rgp->enter, rgp->varno); vreg = allreg(vreg, rgp); - if(rgp->regno != 0) + if(rgp->regno != 0) { + if(debug['R'] && debug['v']) { + Var *v; + + v = var + rgp->varno; + print("registerize %N+%d (bit=%2d et=%2E) in %R\n", + v->node, v->offset, rgp->varno, v->etype, rgp->regno); + } paint3(rgp->enter, rgp->varno, vreg, rgp->regno); + } rgp++; } @@ -776,8 +810,8 @@ brk: while(p->link != P && p->link->as == ANOP) p->link = p->link->link; if(p->to.type == D_BRANCH) - while(p->to.branch != P && p->to.branch->as == ANOP) - p->to.branch = p->to.branch->link; + while(p->to.u.branch != P && p->to.u.branch->as == ANOP) + p->to.u.branch = p->to.u.branch->link; } if(lastr != R) { @@ -838,7 +872,6 @@ addmove(Reg *r, int bn, int rn, int f) a->offset = v->offset; a->etype = v->etype; a->type = v->name; - a->gotype = v->gotype; a->node = v->node; a->sym = v->node->sym; @@ -847,7 +880,7 @@ addmove(Reg *r, int bn, int rn, int f) p1->as = AMOVL; switch(v->etype) { default: - fatal("unknown type\n"); + fatal("unknown type %E", v->etype); case TINT8: case TUINT8: case TBOOL: @@ -932,7 +965,8 @@ Bits mkvar(Reg *r, Adr *a) { Var *v; - int i, t, n, et, z, w, flag; + int i, t, n, et, z, flag; + int64 w; uint32 regu; int32 o; Bits bit; @@ -984,6 +1018,8 @@ mkvar(Reg *r, Adr *a) et = a->etype; o = a->offset; w = a->width; + if(w < 0) + fatal("bad width %lld for %D", w, a); flag = 0; for(i=0; ioffset = o; v->name = n; - v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d %#N %D\n", i, et, w, node, a); + print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); + ostats.nvar++; bit = blsh(i); @@ -1089,8 +1125,12 @@ prop(Reg *r, Bits ref, Bits cal) default: // Work around for issue 1304: // flush modified globals before each instruction. - for(z=0; z FREGEXT) + if(f < D_X0 || f > D_X15) return 0; - return 1L << (f - FREGMIN + 16); + return 1L << (f - D_X0 + 16); } int BtoF(int32 b) { - b &= 0xFF0000L; + b &= 0xFFFF0000L; if(b == 0) return 0; - return bitno(b) - 16 + FREGMIN; + return bitno(b) - 16 + D_X0; } void @@ -1719,7 +1759,7 @@ chasejmp(Prog *p, int *jmploop) *jmploop = 1; break; } - p = p->to.branch; + p = p->to.u.branch; } return p; } @@ -1741,9 +1781,9 @@ mark(Prog *firstp) if(p->reg != dead) break; p->reg = alive; - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.branch) - mark(p->to.branch); - if(p->as == AJMP || p->as == ARET || (p->as == ACALL && noreturn(p))) + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch) + mark(p->to.u.branch); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) break; } } @@ -1762,8 +1802,8 @@ fixjmp(Prog *firstp) for(p=firstp; p; p=p->link) { if(debug['R'] && debug['v']) print("%P\n", p); - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.branch && p->to.branch->as == AJMP) { - p->to.branch = chasejmp(p->to.branch, &jmploop); + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) { + p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); if(debug['R'] && debug['v']) print("->%P\n", p); } @@ -1799,7 +1839,7 @@ fixjmp(Prog *firstp) if(!jmploop) { last = nil; for(p=firstp; p; p=p->link) { - if(p->as == AJMP && p->to.type == D_BRANCH && p->to.branch == p->link) { + if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) { if(debug['R'] && debug['v']) print("del %P\n", p); continue; diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index 849915954..805b3fc6f 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -736,6 +736,30 @@ enum as ACRC32B, ACRC32Q, AIMUL3Q, + + APREFETCHT0, + APREFETCHT1, + APREFETCHT2, + APREFETCHNTA, + + AMOVQL, + ABSWAPL, + ABSWAPQ, + + AUNDEF, + + AAESENC, + AAESENCLAST, + AAESDEC, + AAESDECLAST, + AAESIMC, + AAESKEYGENASSIST, + + APSHUFD, + + AUSEFIELD, + ALOCALS, + ATYPE, ALAST }; @@ -794,6 +818,14 @@ enum D_X5, D_X6, D_X7, + D_X8, + D_X9, + D_X10, + D_X11, + D_X12, + D_X13, + D_X14, + D_X15, D_CS = 68, D_SS, @@ -854,7 +886,7 @@ enum /* * this is the ranlib header */ -#define SYMDEF "__.SYMDEF" +#define SYMDEF "__.GOSYMDEF" /* * this is the simulated IEEE floating point diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index ee31a05cd..5fb75ba4d 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -74,34 +74,6 @@ datoff(vlong addr) return 0; } -enum { - ElfStrEmpty, - ElfStrInterp, - ElfStrHash, - ElfStrGot, - ElfStrGotPlt, - ElfStrDynamic, - ElfStrDynsym, - ElfStrDynstr, - ElfStrRela, - ElfStrText, - ElfStrData, - ElfStrBss, - ElfStrShstrtab, - ElfStrSymtab, - ElfStrStrtab, - ElfStrRelaPlt, - ElfStrPlt, - ElfStrGnuVersion, - ElfStrGnuVersionR, - ElfStrNoteNetbsdIdent, - ElfStrNoPtrData, - ElfStrNoPtrBss, - NElfStr -}; - -vlong elfstr[NElfStr]; - static int needlib(char *name) { @@ -127,6 +99,20 @@ int nelfsym = 1; static void addpltsym(Sym*); static void addgotsym(Sym*); +Sym * +lookuprel(void) +{ + return lookup(".rela", 0); +} + +void +adddynrela(Sym *rela, Sym *s, Reloc *r) +{ + addaddrplus(rela, s, r->off); + adduint64(rela, R_X86_64_RELATIVE); + addaddrplus(rela, r->sym, r->add); // Addend +} + void adddynrel(Sym *s, Reloc *r) { @@ -298,6 +284,37 @@ adddynrel(Sym *s, Reloc *r) diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } +int +elfreloc1(Reloc *r, vlong off, int32 elfsym, vlong add) +{ + VPUT(off); + + switch(r->type) { + default: + return -1; + + case D_ADDR: + if(r->siz == 4) + VPUT(R_X86_64_32 | (uint64)elfsym<<32); + else if(r->siz == 8) + VPUT(R_X86_64_64 | (uint64)elfsym<<32); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + VPUT(R_X86_64_PC32 | (uint64)elfsym<<32); + else + return -1; + add -= r->siz; + break; + } + + VPUT(add); + return 0; +} + int archreloc(Reloc *r, Sym *s, vlong *val) { @@ -307,7 +324,7 @@ archreloc(Reloc *r, Sym *s, vlong *val) return -1; } -static void +void elfsetupplt(void) { Sym *plt, *got; @@ -434,6 +451,7 @@ adddynsym(Sym *s) Sym *d, *str; int t; char *name; + vlong off; if(s->dynid >= 0) return; @@ -452,7 +470,7 @@ adddynsym(Sym *s) adduint32(d, addstring(lookup(".dynstr", 0), name)); /* type */ t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) + if(s->dynexport && (s->type&SMASK) == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; @@ -490,7 +508,7 @@ adddynsym(Sym *s) addaddr(d, s); /* size of object */ - adduint64(d, 0); + adduint64(d, s->size); if(!s->dynexport && s->dynimplib && needlib(s->dynimplib)) { elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, @@ -502,35 +520,51 @@ adddynsym(Sym *s) name = s->dynimpname; if(name == nil) name = s->name; - s->dynid = d->size/16; + if(d->size == 0 && ndynexp > 0) { // pre-allocate for dynexps + symgrow(d, ndynexp*16); + } + if(s->dynid <= -100) { // pre-allocated, see cmd/ld/go.c:^sortdynexp() + s->dynid = -s->dynid-100; + off = s->dynid*16; + } else { + off = d->size; + s->dynid = off/16; + } // darwin still puts _ prefixes on all C symbols str = lookup(".dynstr", 0); - adduint32(d, str->size); + setuint32(d, off, str->size); + off += 4; adduint8(str, '_'); addstring(str, name); if(s->type == SDYNIMPORT) { - adduint8(d, 0x01); // type - N_EXT - external symbol - adduint8(d, 0); // section + setuint8(d, off, 0x01); // type - N_EXT - external symbol + off++; + setuint8(d, off, 0); // section + off++; } else { - adduint8(d, 0x0f); + setuint8(d, off, 0x0f); + off++; switch(s->type) { default: case STEXT: - adduint8(d, 1); + setuint8(d, off, 1); break; case SDATA: - adduint8(d, 2); + setuint8(d, off, 2); break; case SBSS: - adduint8(d, 4); + setuint8(d, off, 4); break; } + off++; } - adduint16(d, 0); // desc + setuint16(d, off, 0); // desc + off += 2; if(s->type == SDYNIMPORT) - adduint64(d, 0); // value + setuint64(d, off, 0); // value else - addaddr(d, s); + setaddr(d, off, s); + off += 8; } else if(HEADTYPE != Hwindows) { diag("adddynsym: unsupported binary format"); } @@ -556,182 +590,31 @@ adddynlib(char *lib) } } -void -doelf(void) -{ - Sym *s, *shstrtab, *dynstr; - - if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd && HEADTYPE != Hnetbsd) - return; - - /* predefine strings we need for section headers */ - shstrtab = lookup(".shstrtab", 0); - shstrtab->type = SELFROSECT; - shstrtab->reachable = 1; - - elfstr[ElfStrEmpty] = addstring(shstrtab, ""); - elfstr[ElfStrText] = addstring(shstrtab, ".text"); - elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); - elfstr[ElfStrData] = addstring(shstrtab, ".data"); - elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); - elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss"); - if(HEADTYPE == Hnetbsd) - elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident"); - addstring(shstrtab, ".elfdata"); - addstring(shstrtab, ".rodata"); - addstring(shstrtab, ".gosymtab"); - addstring(shstrtab, ".gopclntab"); - if(!debug['s']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); - dwarfaddshstrings(shstrtab); - } - elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); - - if(!debug['d']) { /* -d suppresses dynamic loader format */ - elfstr[ElfStrInterp] = addstring(shstrtab, ".interp"); - elfstr[ElfStrHash] = addstring(shstrtab, ".hash"); - elfstr[ElfStrGot] = addstring(shstrtab, ".got"); - elfstr[ElfStrGotPlt] = addstring(shstrtab, ".got.plt"); - elfstr[ElfStrDynamic] = addstring(shstrtab, ".dynamic"); - elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); - elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); - elfstr[ElfStrRela] = addstring(shstrtab, ".rela"); - elfstr[ElfStrRelaPlt] = addstring(shstrtab, ".rela.plt"); - elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); - elfstr[ElfStrGnuVersion] = addstring(shstrtab, ".gnu.version"); - elfstr[ElfStrGnuVersionR] = addstring(shstrtab, ".gnu.version_r"); - - /* dynamic symbol table - first entry all zeros */ - s = lookup(".dynsym", 0); - s->type = SELFROSECT; - s->reachable = 1; - s->size += ELF64SYMSIZE; - - /* dynamic string table */ - s = lookup(".dynstr", 0); - s->type = SELFROSECT; - s->reachable = 1; - if(s->size == 0) - addstring(s, ""); - dynstr = s; - - /* relocation table */ - s = lookup(".rela", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* global offset table */ - s = lookup(".got", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* hash */ - s = lookup(".hash", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".got.plt", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - s = lookup(".plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - elfsetupplt(); - - s = lookup(".rela.plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".gnu.version", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".gnu.version_r", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* define dynamic elf table */ - s = lookup(".dynamic", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* - * .dynamic table - */ - elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); - elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); - elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); - elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); - elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); - elfwritedynentsym(s, DT_RELA, lookup(".rela", 0)); - elfwritedynentsymsize(s, DT_RELASZ, lookup(".rela", 0)); - elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); - if(rpath) - elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - - elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); - elfwritedynent(s, DT_PLTREL, DT_RELA); - elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0)); - elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0)); - - elfwritedynent(s, DT_DEBUG, 0); - - // Do not write DT_NULL. elfdynhash will finish it. - } -} - -void -shsym(ElfShdr *sh, Sym *s) -{ - vlong addr; - addr = symaddr(s); - if(sh->flags&SHF_ALLOC) - sh->addr = addr; - sh->off = datoff(addr); - sh->size = s->size; -} - -void -phsh(ElfPhdr *ph, ElfShdr *sh) -{ - ph->vaddr = sh->addr; - ph->paddr = ph->vaddr; - ph->off = sh->off; - ph->filesz = sh->size; - ph->memsz = sh->size; - ph->align = sh->addralign; -} - void asmb(void) { int32 magic; - int a, dynsym; - vlong vl, startva, symo, dwarfoff, machlink, resoff; - ElfEhdr *eh; - ElfPhdr *ph, *pph; - ElfShdr *sh; + int i; + vlong vl, symo, dwarfoff, machlink; Section *sect; - int o; + Sym *sym; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - elftextsh = 0; - if(debug['v']) Bprint(&bso, "%5.2f codeblk\n", cputime()); Bflush(&bso); + if(iself) + asmbelfsetup(); + sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); @@ -763,6 +646,7 @@ asmb(void) default: diag("unknown header type %d", HEADTYPE); case Hplan9x32: + case Hplan9x64: case Helf: break; case Hdarwin: @@ -773,16 +657,6 @@ asmb(void) case Hnetbsd: case Hopenbsd: debug['8'] = 1; /* 64-bit addresses */ - /* index of elf text section; needed by asmelfsym, double-checked below */ - /* !debug['d'] causes extra sections before the .text section */ - elftextsh = 2; - if(!debug['d']) { - elftextsh += 10; - if(elfverneed) - elftextsh += 2; - } - if(HEADTYPE == Hnetbsd) - elftextsh += 1; break; case Hwindows: break; @@ -798,7 +672,7 @@ asmb(void) Bflush(&bso); switch(HEADTYPE) { default: - case Hplan9x32: + case Hplan9x64: case Helf: debug['s'] = 1; symo = HEADR+segtext.len+segdata.filelen; @@ -831,6 +705,22 @@ asmb(void) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); + } + break; + case Hplan9x64: + asmplan9sym(); + cflush(); + + sym = lookup("pclntab", 0); + if(sym != nil) { + lcsize = sym->np; + for(i=0; i < lcsize; i++) + cput(sym->p[i]); + + cflush(); } break; case Hwindows: @@ -848,7 +738,7 @@ asmb(void) cseek(0L); switch(HEADTYPE) { default: - case Hplan9x32: /* plan9 */ + case Hplan9x64: /* plan9 */ magic = 4*26*26+7; magic |= 0x00008000; /* fat header */ lputb(magic); /* magic */ @@ -862,7 +752,7 @@ asmb(void) lputb(lcsize); /* line offsets */ vputb(vl); /* va of entry */ break; - case Hplan9x64: /* plan9 */ + case Hplan9x32: /* plan9 */ magic = 4*26*26+7; lputb(magic); /* magic */ lputb(segtext.filelen); /* sizes */ @@ -880,257 +770,7 @@ asmb(void) case Hfreebsd: case Hnetbsd: case Hopenbsd: - /* elf amd-64 */ - - eh = getElfEhdr(); - startva = INITTEXT - HEADR; - resoff = ELFRESERVE; - - /* This null SHdr must appear before all others */ - newElfShdr(elfstr[ElfStrEmpty]); - - /* program header info */ - pph = newElfPhdr(); - pph->type = PT_PHDR; - pph->flags = PF_R + PF_X; - pph->off = eh->ehsize; - pph->vaddr = INITTEXT - HEADR + pph->off; - pph->paddr = INITTEXT - HEADR + pph->off; - pph->align = INITRND; - - /* - * PHDR must be in a loaded segment. Adjust the text - * segment boundaries downwards to include it. - */ - o = segtext.vaddr - pph->vaddr; - segtext.vaddr -= o; - segtext.len += o; - o = segtext.fileoff - pph->off; - segtext.fileoff -= o; - segtext.filelen += o; - - if(!debug['d']) { - /* interpreter */ - sh = newElfShdr(elfstr[ElfStrInterp]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - if(interpreter == nil) { - switch(HEADTYPE) { - case Hlinux: - interpreter = linuxdynld; - break; - case Hfreebsd: - interpreter = freebsddynld; - break; - case Hnetbsd: - interpreter = netbsddynld; - break; - case Hopenbsd: - interpreter = openbsddynld; - break; - } - } - resoff -= elfinterp(sh, startva, resoff, interpreter); - - ph = newElfPhdr(); - ph->type = PT_INTERP; - ph->flags = PF_R; - phsh(ph, sh); - } - - if(HEADTYPE == Hnetbsd) { - sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]); - sh->type = SHT_NOTE; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - resoff -= elfnetbsdsig(sh, startva, resoff); - - ph = newElfPhdr(); - ph->type = PT_NOTE; - ph->flags = PF_R; - phsh(ph, sh); - } - - elfphload(&segtext); - elfphload(&segdata); - - /* Dynamic linking sections */ - if(!debug['d']) { /* -d suppresses dynamic loader format */ - /* S headers for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrGot]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 8; - sh->addralign = 8; - shsym(sh, lookup(".got", 0)); - - sh = newElfShdr(elfstr[ElfStrGotPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 8; - sh->addralign = 8; - shsym(sh, lookup(".got.plt", 0)); - - dynsym = eh->shnum; - sh = newElfShdr(elfstr[ElfStrDynsym]); - sh->type = SHT_DYNSYM; - sh->flags = SHF_ALLOC; - sh->entsize = ELF64SYMSIZE; - sh->addralign = 8; - sh->link = dynsym+1; // dynstr - // sh->info = index of first non-local symbol (number of local symbols) - shsym(sh, lookup(".dynsym", 0)); - - sh = newElfShdr(elfstr[ElfStrDynstr]); - sh->type = SHT_STRTAB; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup(".dynstr", 0)); - - if(elfverneed) { - sh = newElfShdr(elfstr[ElfStrGnuVersion]); - sh->type = SHT_GNU_VERSYM; - sh->flags = SHF_ALLOC; - sh->addralign = 2; - sh->link = dynsym; - sh->entsize = 2; - shsym(sh, lookup(".gnu.version", 0)); - - sh = newElfShdr(elfstr[ElfStrGnuVersionR]); - sh->type = SHT_GNU_VERNEED; - sh->flags = SHF_ALLOC; - sh->addralign = 8; - sh->info = elfverneed; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".gnu.version_r", 0)); - } - - sh = newElfShdr(elfstr[ElfStrRelaPlt]); - sh->type = SHT_RELA; - sh->flags = SHF_ALLOC; - sh->entsize = ELF64RELASIZE; - sh->addralign = 8; - sh->link = dynsym; - sh->info = eh->shnum; // .plt - shsym(sh, lookup(".rela.plt", 0)); - - sh = newElfShdr(elfstr[ElfStrPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->entsize = 16; - sh->addralign = 4; - shsym(sh, lookup(".plt", 0)); - - sh = newElfShdr(elfstr[ElfStrHash]); - sh->type = SHT_HASH; - sh->flags = SHF_ALLOC; - sh->entsize = 4; - sh->addralign = 8; - sh->link = dynsym; - shsym(sh, lookup(".hash", 0)); - - sh = newElfShdr(elfstr[ElfStrRela]); - sh->type = SHT_RELA; - sh->flags = SHF_ALLOC; - sh->entsize = ELF64RELASIZE; - sh->addralign = 8; - sh->link = dynsym; - shsym(sh, lookup(".rela", 0)); - - /* sh and PT_DYNAMIC for .dynamic section */ - sh = newElfShdr(elfstr[ElfStrDynamic]); - sh->type = SHT_DYNAMIC; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 16; - sh->addralign = 8; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".dynamic", 0)); - ph = newElfPhdr(); - ph->type = PT_DYNAMIC; - ph->flags = PF_R + PF_W; - phsh(ph, sh); - - /* - * Thread-local storage segment (really just size). - */ - if(tlsoffset != 0) { - ph = newElfPhdr(); - ph->type = PT_TLS; - ph->flags = PF_R; - ph->memsz = -tlsoffset; - ph->align = 8; - } - } - - ph = newElfPhdr(); - ph->type = PT_GNU_STACK; - ph->flags = PF_W+PF_R; - ph->align = 8; - - sh = newElfShstrtab(elfstr[ElfStrShstrtab]); - sh->type = SHT_STRTAB; - sh->addralign = 1; - shsym(sh, lookup(".shstrtab", 0)); - - if(elftextsh != eh->shnum) - diag("elftextsh = %d, want %d", elftextsh, eh->shnum); - for(sect=segtext.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - for(sect=segdata.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - - if(!debug['s']) { - sh = newElfShdr(elfstr[ElfStrSymtab]); - sh->type = SHT_SYMTAB; - sh->off = symo; - sh->size = symsize; - sh->addralign = 8; - sh->entsize = 24; - sh->link = eh->shnum; // link to strtab - - sh = newElfShdr(elfstr[ElfStrStrtab]); - sh->type = SHT_STRTAB; - sh->off = symo+symsize; - sh->size = elfstrsize; - sh->addralign = 1; - - dwarfaddelfheaders(); - } - - /* Main header */ - eh->ident[EI_MAG0] = '\177'; - eh->ident[EI_MAG1] = 'E'; - eh->ident[EI_MAG2] = 'L'; - eh->ident[EI_MAG3] = 'F'; - if(HEADTYPE == Hfreebsd) - eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; - else if(HEADTYPE == Hnetbsd) - eh->ident[EI_OSABI] = ELFOSABI_NETBSD; - else if(HEADTYPE == Hopenbsd) - eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; - eh->ident[EI_CLASS] = ELFCLASS64; - eh->ident[EI_DATA] = ELFDATA2LSB; - eh->ident[EI_VERSION] = EV_CURRENT; - - eh->type = ET_EXEC; - eh->machine = EM_X86_64; - eh->version = EV_CURRENT; - eh->entry = entryvalue(); - - pph->filesz = eh->phnum * eh->phentsize; - pph->memsz = pph->filesz; - - cseek(0); - a = 0; - a += elfwritehdr(); - a += elfwritephdrs(); - a += elfwriteshdrs(); - a += elfwriteinterp(elfstr[ElfStrInterp]); - if(HEADTYPE == Hnetbsd) - a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); - if(a > ELFRESERVE) - diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); + asmbelf(symo); break; case Hwindows: asmbpe(); @@ -1153,78 +793,3 @@ rnd(vlong v, vlong r) v -= c; return v; } - -void -genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) -{ - Auto *a; - Sym *s; - - s = lookup("etext", 0); - if(s->type == STEXT) - put(s, s->name, 'T', s->value, s->size, s->version, 0); - - for(s=allsym; s!=S; s=s->allsym) { - if(s->hide) - continue; - switch(s->type&~SSUB) { - case SCONST: - case SRODATA: - case SSYMTAB: - case SPCLNTAB: - case SDATA: - case SNOPTRDATA: - case SELFROSECT: - case SMACHOGOT: - case STYPE: - case SSTRING: - case SGOSTRING: - case SWINDOWS: - if(!s->reachable) - continue; - put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SBSS: - case SNOPTRBSS: - if(!s->reachable) - continue; - if(s->np > 0) - diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special); - put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SFILE: - put(nil, s->name, 'f', s->value, 0, s->version, 0); - continue; - } - } - - for(s = textp; s != nil; s = s->next) { - if(s->text == nil) - continue; - - /* filenames first */ - for(a=s->autom; a; a=a->link) - if(a->type == D_FILE) - put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); - - put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); - - /* frame, auto and param after */ - put(nil, ".frame", 'm', s->text->to.offset+8, 0, 0, 0); - - for(a=s->autom; a; a=a->link) - if(a->type == D_AUTO) - put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); - else - if(a->type == D_PARAM) - put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %ud\n", symsize); - Bflush(&bso); -} diff --git a/src/cmd/6l/doc.go b/src/cmd/6l/doc.go index 4d94b209b..6287dd9be 100644 --- a/src/cmd/6l/doc.go +++ b/src/cmd/6l/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 6l is the linker for the x86-64. @@ -10,4 +12,4 @@ The $GOARCH for these tools is amd64. The flags are documented in ../ld/doc.go. */ -package documentation +package main diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 5f62239a1..ffb8a4552 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -40,7 +40,26 @@ enum { thechar = '6', - PtrSize = 8 + PtrSize = 8, + IntSize = 8, + + // Loop alignment constants: + // want to align loop entry to LoopAlign-byte boundary, + // and willing to insert at most MaxLoopPad bytes of NOP to do so. + // We define a loop entry as the target of a backward jump. + // + // gcc uses MaxLoopPad = 10 for its 'generic x86-64' config, + // and it aligns all jump targets, not just backward jump targets. + // + // As of 6/1/2012, the effect of setting MaxLoopPad = 10 here + // is very slight but negative, so the alignment is disabled by + // setting MaxLoopPad = 0. The code is here for reference and + // for future experiments. + // + LoopAlign = 16, + MaxLoopPad = 0, + + FuncAlign = 16 }; #define P ((Prog*)0) @@ -134,11 +153,16 @@ struct Sym int32 plt; int32 got; int32 align; // if non-zero, required alignment in bytes + int32 elfsym; + int32 locals; // size of stack frame locals area + int32 args; // size of stack frame incoming arguments area Sym* hash; // in hash table Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in SSUB list Sym* outer; // container of sub + Sym* reachparent; + Sym* queue; vlong value; vlong size; Sym* gotype; @@ -146,6 +170,7 @@ struct Sym char* dynimpname; char* dynimplib; char* dynimpvers; + struct Section* sect; // STEXT Auto* autom; @@ -158,6 +183,7 @@ struct Sym Reloc* r; int32 nr; int32 maxr; + int rel_ro; }; struct Optab { @@ -294,9 +320,10 @@ enum EXTERN int32 HEADR; EXTERN int32 HEADTYPE; EXTERN int32 INITRND; -EXTERN vlong INITTEXT; -EXTERN vlong INITDAT; +EXTERN int64 INITTEXT; +EXTERN int64 INITDAT; EXTERN char* INITENTRY; /* entry point */ +EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN char* pcstr; EXTERN Auto* curauto; EXTERN Auto* curhist; @@ -304,7 +331,7 @@ EXTERN Prog* curp; EXTERN Sym* cursym; EXTERN Sym* datap; EXTERN vlong elfdatsize; -EXTERN char debug[128]; +EXTERN int debug[128]; EXTERN char literal[32]; EXTERN Sym* textp; EXTERN Sym* etextp; diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index 692cab7b8..10e4a9860 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -58,8 +58,8 @@ Header headers[] = { }; /* - * -Hplan9x32 -T4136 -R4096 is plan9 64-bit format - * -Hplan9 -T4128 -R4096 is plan9 32-bit format + * -Hplan9x32 -T4128 -R4096 is plan9 32-bit format + * -Hplan9 -T0x200028 -R0x200000 is plan9 64-bit format * -Helf -T0x80110000 -R4096 is ELF32 * -Hdarwin -Tx -Rx is apple MH-exec * -Hlinux -Tx -Rx is linux elf-exec @@ -67,23 +67,11 @@ Header headers[] = { * -Hnetbsd -Tx -Rx is NetBSD elf-exec * -Hopenbsd -Tx -Rx is OpenBSD elf-exec * -Hwindows -Tx -Rx is MS Windows PE32+ - * - * options used: 189BLQSWabcjlnpsvz */ -void -usage(void) -{ - fprint(2, "usage: 6l [-options] [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.6\n"); - exits("usage"); -} - void main(int argc, char *argv[]) { - int c; - char *name, *val; - Binit(&bso, 1, OWRITE); listinit(); memset(debug, 0, sizeof(debug)); @@ -94,52 +82,46 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + LIBINITENTRY = 0; nuxiinit(); - ARGBEGIN { - default: - c = ARGC(); - if(c == 'l') - usage(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - case 'o': /* output to (next arg) */ - outfile = EARGF(usage()); - break; - case 'E': - INITENTRY = EARGF(usage()); - break; - case 'H': - HEADTYPE = headtype(EARGF(usage())); - break; - case 'I': - interpreter = EARGF(usage()); - break; - case 'L': - Lflag(EARGF(usage())); - break; - case 'T': - INITTEXT = atolwhex(EARGF(usage())); - break; - case 'D': - INITDAT = atolwhex(EARGF(usage())); - break; - case 'R': - INITRND = atolwhex(EARGF(usage())); - break; - case 'r': - rpath = EARGF(usage()); - break; - case 'V': - print("%cl version %s\n", thechar, getgoversion()); - errorexit(); - case 'X': - name = EARGF(usage()); - val = EARGF(usage()); - addstrdata(name, val); - break; - } ARGEND + flagcount("1", "use alternate profiling code", &debug['1']); + flagcount("8", "assume 64-bit addresses", &debug['8']); + flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); + flagint64("D", "addr: data address", &INITDAT); + flagstr("E", "sym: entry symbol", &INITENTRY); + flagfn1("I", "interp: set ELF interp", setinterp); + flagfn1("L", "dir: add dir to library path", Lflag); + flagfn1("H", "head: header type", setheadtype); + flagcount("K", "add stack underflow checks", &debug['K']); + flagcount("O", "print pc-line tables", &debug['O']); + flagcount("Q", "debug byte-register code gen", &debug['Q']); + flagint32("R", "rnd: address rounding", &INITRND); + flagcount("S", "check type signatures", &debug['S']); + flagint64("T", "addr: text address", &INITTEXT); + flagfn0("V", "print version and exit", doversion); + flagcount("W", "disassemble input", &debug['W']); + flagfn2("X", "name value: define string data", addstrdata); + flagcount("Z", "clear stack frame on entry", &debug['Z']); + flagcount("a", "disassemble output", &debug['a']); + flagcount("c", "dump call graph", &debug['c']); + flagcount("d", "disable dynamic executable", &debug['d']); + flagcount("f", "ignore version mismatch", &debug['f']); + flagcount("g", "disable go package data checks", &debug['g']); + flagcount("hostobj", "generate host object file", &isobj); + flagstr("k", "sym: set field tracking symbol", &tracksym); + flagcount("n", "dump symbol table", &debug['n']); + flagstr("o", "outfile: set output file", &outfile); + flagcount("p", "insert profiling code", &debug['p']); + flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); + flagcount("race", "enable race detector", &flag_race); + flagcount("s", "disable symbol table", &debug['s']); + flagcount("u", "reject unsafe packages", &debug['u']); + flagcount("v", "print link trace", &debug['v']); + flagcount("w", "disable DWARF generation", &debug['w']); + flagcount("shared", "generate shared object", &flag_shared); + + flagparse(&argc, &argv, usage); if(argc != 1) usage(); @@ -149,6 +131,15 @@ main(int argc, char *argv[]) if(HEADTYPE == -1) HEADTYPE = headtype(goos); + if(isobj) { + switch(HEADTYPE) { + default: + sysfatal("cannot use -hostobj with -H %s", headstr(HEADTYPE)); + case Hlinux: + break; + } + } + if(outfile == nil) { if(HEADTYPE == Hwindows) outfile = "6.out.exe"; @@ -163,7 +154,7 @@ main(int argc, char *argv[]) diag("unknown -H option"); errorexit(); case Hplan9x32: /* plan 9 */ - HEADR = 32L+8L; + HEADR = 32L; if(INITTEXT == -1) INITTEXT = 4096+HEADR; if(INITDAT == -1) @@ -172,13 +163,13 @@ main(int argc, char *argv[]) INITRND = 4096; break; case Hplan9x64: /* plan 9 */ - HEADR = 32L; + HEADR = 32L + 8L; if(INITTEXT == -1) - INITTEXT = 4096+32; + INITTEXT = 0x200000+HEADR; if(INITDAT == -1) INITDAT = 0; if(INITRND == -1) - INITRND = 4096; + INITRND = 0x200000; break; case Helf: /* elf32 executable */ HEADR = rnd(52L+3*32L, 16); @@ -506,8 +497,6 @@ loop: p->line = Bget4(f); p->back = 2; p->mode = mode; - p->ft = 0; - p->tt = 0; zaddr(pn, f, &p->from, h); fromgotype = adrgotype; zaddr(pn, f, &p->to, h); @@ -597,6 +586,15 @@ loop: pc++; goto loop; + case ALOCALS: + cursym->locals = p->to.offset; + pc++; + goto loop; + + case ATYPE: + pc++; + goto loop; + case ATEXT: s = p->from.sym; if(s->text != nil) { @@ -640,6 +638,7 @@ loop: } s->type = STEXT; s->value = pc; + s->args = p->to.offset >> 32; lastp = p; p->pc = pc++; goto loop; @@ -677,7 +676,7 @@ loop: sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { - s->type = SDATA; + s->type = SRODATA; adduint32(s, ieeedtof(&p->from.ieee)); s->reachable = 0; } @@ -711,7 +710,7 @@ loop: p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { - s->type = SDATA; + s->type = SRODATA; adduint32(s, p->from.ieee.l); adduint32(s, p->from.ieee.h); s->reachable = 0; diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c index 5746ded19..a163e6faa 100644 --- a/src/cmd/6l/optab.c +++ b/src/cmd/6l/optab.c @@ -269,7 +269,7 @@ uchar yimul[] = }; uchar yimul3[] = { - Yml, Yrl, Zibm_r, 1, + Yml, Yrl, Zibm_r, 2, 0 }; uchar ybyte[] = @@ -302,6 +302,11 @@ uchar ypopl[] = Ynone, Ym, Zo_m, 2, 0 }; +uchar ybswap[] = +{ + Ynone, Yrl, Z_rp, 2, + 0, +}; uchar yscond[] = { Ynone, Ymb, Zo_m, 2, @@ -309,7 +314,9 @@ uchar yscond[] = }; uchar yjcond[] = { - Ynone, Ybr, Zbr, 1, + Ynone, Ybr, Zbr, 0, + Yi0, Ybr, Zbr, 0, + Yi1, Ybr, Zbr, 1, 0 }; uchar yloop[] = @@ -319,7 +326,8 @@ uchar yloop[] = }; uchar ycall[] = { - Ynone, Yml, Zo_m64, 2, + Ynone, Yml, Zo_m64, 0, + Yrx, Yrx, Zo_m64, 2, Ynone, Ybr, Zcall, 1, 0 }; @@ -511,17 +519,17 @@ uchar ymrxr[] = }; uchar ymshuf[] = { - Ymm, Ymr, Zibm_r, 1, + Ymm, Ymr, Zibm_r, 2, 0 }; uchar yxshuf[] = { - Yxm, Yxr, Zibm_r, 1, + Yxm, Yxr, Zibm_r, 2, 0 }; uchar yextrw[] = { - Yxr, Yrl, Zibm_r, 1, + Yxr, Yrl, Zibm_r, 2, 0 }; uchar ypsdq[] = @@ -539,6 +547,21 @@ uchar ycrc32l[] = { Yml, Yrl, Zlitm_r, 0, }; +uchar yprefetch[] = +{ + Ym, Ynone, Zm_o, 2, + 0, +}; +uchar yaes[] = +{ + Yxm, Yxr, Zlitm_r, 2, + 0 +}; +uchar yaes2[] = +{ + Yxm, Yxr, Zibm_r, 2, + 0 +}; /* * You are doasm, holding in your hand a Prog* with p->as set to, say, ACRC32, @@ -636,6 +659,8 @@ Optab optab[] = { ABSRL, yml_rl, Pm, 0xbd }, { ABSRQ, yml_rl, Pw, 0x0f,0xbd }, { ABSRW, yml_rl, Pq, 0xbd }, + { ABSWAPL, ybswap, Px, 0x0f,0xc8 }, + { ABSWAPQ, ybswap, Pw, 0x0f,0xc8 }, { ABTCL, ybtl, Pm, 0xba,(07),0xbb }, { ABTCQ, ybtl, Pw, 0x0f,0xba,(07),0x0f,0xbb }, { ABTCW, ybtl, Pq, 0xba,(07),0xbb }, @@ -777,7 +802,7 @@ Optab optab[] = { AIMULL, yimul, Px, 0xf7,(05),0x6b,0x69,Pm,0xaf }, { AIMULQ, yimul, Pw, 0xf7,(05),0x6b,0x69,Pm,0xaf }, { AIMULW, yimul, Pe, 0xf7,(05),0x6b,0x69,Pm,0xaf }, - { AIMUL3Q, yimul3, Pw, 0x6b }, + { AIMUL3Q, yimul3, Pw, 0x6b,(00) }, { AINB, yin, Pb, 0xe4,0xec }, { AINCB, yincb, Pb, 0xfe,(00) }, { AINCL, yincl, Px, 0xff,(00) }, @@ -936,7 +961,7 @@ Optab optab[] = { APCMPGTB, ymm, Py, 0x64,Pe,0x64 }, { APCMPGTL, ymm, Py, 0x66,Pe,0x66 }, { APCMPGTW, ymm, Py, 0x65,Pe,0x65 }, - { APEXTRW, yextrw, Pq, 0xc5 }, + { APEXTRW, yextrw, Pq, 0xc5,(00) }, { APF2IL, ymfp, Px, 0x1d }, { APF2IW, ymfp, Px, 0x1c }, { API2FL, ymfp, Px, 0x0d }, @@ -957,7 +982,7 @@ Optab optab[] = { APFRSQRT, ymfp, Px, 0x97 }, { APFSUB, ymfp, Px, 0x9a }, { APFSUBR, ymfp, Px, 0xaa }, - { APINSRW, yextrw, Pq, 0xc4 }, + { APINSRW, yextrw, Pq, 0xc4,(00) }, { APMADDWL, ymm, Py, 0xf5,Pe,0xf5 }, { APMAXSW, yxm, Pe, 0xee }, { APMAXUB, yxm, Pe, 0xde }, @@ -979,10 +1004,10 @@ Optab optab[] = { APOPW, ypopl, Pe, 0x58,0x8f,(00) }, { APOR, ymm, Py, 0xeb,Pe,0xeb }, { APSADBW, yxm, Pq, 0xf6 }, - { APSHUFHW, yxshuf, Pf3, 0x70 }, - { APSHUFL, yxshuf, Pq, 0x70 }, - { APSHUFLW, yxshuf, Pf2, 0x70 }, - { APSHUFW, ymshuf, Pm, 0x70 }, + { APSHUFHW, yxshuf, Pf3, 0x70,(00) }, + { APSHUFL, yxshuf, Pq, 0x70,(00) }, + { APSHUFLW, yxshuf, Pf2, 0x70,(00) }, + { APSHUFW, ymshuf, Pm, 0x70,(00) }, { APSLLO, ypsdq, Pq, 0x73,(07) }, { APSLLL, yps, Py, 0xf2, 0x72,(06), Pe,0xf2, Pe,0x72,(06) }, { APSLLQ, yps, Py, 0xf3, 0x73,(06), Pe,0xf3, Pe,0x73,(06) }, @@ -1087,8 +1112,8 @@ Optab optab[] = { ASHRL, yshl, Px, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, { ASHRQ, yshl, Pw, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, { ASHRW, yshl, Pe, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, - { ASHUFPD, yxshuf, Pq, 0xc6 }, - { ASHUFPS, yxshuf, Pm, 0xc6 }, + { ASHUFPD, yxshuf, Pq, 0xc6,(00) }, + { ASHUFPS, yxshuf, Pm, 0xc6,(00) }, { ASQRTPD, yxm, Pe, 0x51 }, { ASQRTPS, yxm, Pm, 0x51 }, { ASQRTSD, yxm, Pf2, 0x51 }, @@ -1270,8 +1295,30 @@ Optab optab[] = { AXADDQ, yrl_ml, Pw, 0x0f,0xc1 }, { AXADDW, yrl_ml, Pe, 0x0f,0xc1 }, - { ACRC32B, ycrc32l,Px, 0xf2,0x0f,0x38,0xf0,0}, - { ACRC32Q, ycrc32l,Pw, 0xf2,0x0f,0x38,0xf1,0}, + { ACRC32B, ycrc32l,Px, 0xf2,0x0f,0x38,0xf0,0 }, + { ACRC32Q, ycrc32l,Pw, 0xf2,0x0f,0x38,0xf1,0 }, + + { APREFETCHT0, yprefetch, Pm, 0x18,(01) }, + { APREFETCHT1, yprefetch, Pm, 0x18,(02) }, + { APREFETCHT2, yprefetch, Pm, 0x18,(03) }, + { APREFETCHNTA, yprefetch, Pm, 0x18,(00) }, + + { AMOVQL, yrl_ml, Px, 0x89 }, + + { AUNDEF, ynone, Px, 0x0f, 0x0b }, + + { AAESENC, yaes, Pq, 0x38,0xdc,(0) }, + { AAESENCLAST, yaes, Pq, 0x38,0xdd,(0) }, + { AAESDEC, yaes, Pq, 0x38,0xde,(0) }, + { AAESDECLAST, yaes, Pq, 0x38,0xdf,(0) }, + { AAESIMC, yaes, Pq, 0x38,0xdb,(0) }, + { AAESKEYGENASSIST, yaes2, Pq, 0x3a,0xdf,(0) }, + + { APSHUFD, yaes2, Pq, 0x70,(0) }, + + { AUSEFIELD, ynop, Px, 0,0 }, + { ALOCALS }, + { ATYPE }, { AEND }, 0 diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c index c9b477627..0054b329f 100644 --- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -79,6 +79,7 @@ nofollow(int a) case ARETFL: case ARETFQ: case ARETFW: + case AUNDEF: return 1; } return 0; @@ -192,20 +193,34 @@ loop: * recurse to follow one path. * continue loop on the other. */ - q = brchain(p->link); - if(q != P && q->mark) - if(a != ALOOP) { - p->as = relinv(a); - p->link = p->pcond; + if((q = brchain(p->pcond)) != P) p->pcond = q; + if((q = brchain(p->link)) != P) + p->link = q; + if(p->from.type == D_CONST) { + if(p->from.offset == 1) { + /* + * expect conditional jump to be taken. + * rewrite so that's the fall-through case. + */ + p->as = relinv(a); + q = p->link; + p->link = p->pcond; + p->pcond = q; + } + } else { + q = p->link; + if(q->mark) + if(a != ALOOP) { + p->as = relinv(a); + p->link = p->pcond; + p->pcond = q; + } } xfol(p->link, last); - q = brchain(p->pcond); - if(q->mark) { - p->pcond = q; + if(p->pcond->mark) return; - } - p = q; + p = p->pcond; goto loop; } p = p->link; @@ -295,7 +310,8 @@ patch(void) } } if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd - || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) { + || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd + || HEADTYPE == Hplan9x64) { // ELF uses FS instead of GS. if(p->from.type == D_INDIR+D_GS) p->from.type = D_INDIR+D_FS; @@ -307,7 +323,7 @@ patch(void) if(s) { if(debug['c']) Bprint(&bso, "%s calls %s\n", TNAME, s->name); - if((s->type&~SSUB) != STEXT) { + if((s->type&SMASK) != STEXT) { /* diag prints TNAME first */ diag("undefined: %s", s->name); s->type = STEXT; @@ -405,7 +421,7 @@ dostkoff(void) for(cursym = textp; cursym != nil; cursym = cursym->next) { if(cursym->text == nil || cursym->text->link == nil) - continue; + continue; p = cursym->text; parsetextconst(p->to.offset); @@ -413,6 +429,14 @@ dostkoff(void) if(autoffset < 0) autoffset = 0; + if(autoffset < StackSmall && !(p->from.scale & NOSPLIT)) { + for(q = p; q != P; q = q->link) + if(q->as == ACALL) + goto noleaf; + p->from.scale |= NOSPLIT; + noleaf:; + } + q = P; if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) diag("nosplit func likely to overflow stack"); @@ -421,7 +445,8 @@ dostkoff(void) p = appendp(p); // load g into CX p->as = AMOVQ; if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd - || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) // ELF uses FS + || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd + || HEADTYPE == Hplan9x64) // ELF uses FS p->from.type = D_INDIR+D_FS; else p->from.type = D_INDIR+D_GS; @@ -579,6 +604,16 @@ dostkoff(void) p->spadj = autoffset; if(q != P) q->pcond = p; + } else { + // zero-byte stack adjustment. + // Insert a fake non-zero adjustment so that stkcheck can + // recognize the end of the stack-splitting prolog. + p = appendp(p); + p->as = ANOP; + p->spadj = -PtrSize; + p = appendp(p); + p->as = ANOP; + p->spadj = PtrSize; } deltasp = autoffset; @@ -618,6 +653,34 @@ dostkoff(void) q1->pcond = p; } + if(debug['Z'] && autoffset && !(cursym->text->from.scale&NOSPLIT)) { + // 6l -Z means zero the stack frame on entry. + // This slows down function calls but can help avoid + // false positives in garbage collection. + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_SP; + p->to.type = D_DI; + + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_CONST; + p->from.offset = autoffset/8; + p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_AX; + + p = appendp(p); + p->as = AREP; + + p = appendp(p); + p->as = ASTOSQ; + } + for(; p != P; p = p->link) { pcsize = p->mode/8; a = p->from.type; diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c index 28eb38f40..283a0e349 100644 --- a/src/cmd/6l/span.c +++ b/src/cmd/6l/span.c @@ -37,6 +37,37 @@ static int rexflag; static int asmode; static vlong vaddr(Adr*, Reloc*); +// single-instruction no-ops of various lengths. +// constructed by hand and disassembled with gdb to verify. +// see http://www.agner.org/optimize/optimizing_assembly.pdf for discussion. +static uchar nop[][16] = { + {0x90}, + {0x66, 0x90}, + {0x0F, 0x1F, 0x00}, + {0x0F, 0x1F, 0x40, 0x00}, + {0x0F, 0x1F, 0x44, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00}, + {0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00}, + {0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +static void +fillnop(uchar *p, int n) +{ + int m; + + while(n > 0) { + m = n; + if(m > nelem(nop)) + m = nelem(nop); + memmove(p, nop[m-1], m); + p += m; + n -= m; + } +} + void span1(Sym *s) { @@ -52,8 +83,10 @@ span1(Sym *s) for(p = s->text; p != P; p = p->link) { p->back = 2; // use short branches first time through - if((q = p->pcond) != P && (q->back & 2)) + if((q = p->pcond) != P && (q->back & 2)) { p->back |= 1; // backward jump + q->back |= 4; // loop head + } if(p->as == AADJSP) { p->to.type = D_SP; @@ -78,6 +111,16 @@ span1(Sym *s) s->np = 0; c = 0; for(p = s->text; p != P; p = p->link) { + if((p->back & 4) && (c&(LoopAlign-1)) != 0) { + // pad with NOPs + v = -c&(LoopAlign-1); + if(v <= MaxLoopPad) { + symgrow(s, c+v); + fillnop(s->p+c, v); + c += v; + } + } + p->pc = c; // process forward jumps to p @@ -329,7 +372,10 @@ oclass(Adr *a) switch(a->index) { case D_EXTERN: case D_STATIC: - return Yi32; /* TO DO: Yi64 */ + if(flag_shared) + return Yiauto; + else + return Yi32; /* TO DO: Yi64 */ case D_AUTO: case D_PARAM: return Yiauto; @@ -688,7 +734,10 @@ vaddr(Adr *a, Reloc *r) diag("need reloc for %D", a); errorexit(); } - r->type = D_ADDR; + if(flag_shared) + r->type = D_PCREL; + else + r->type = D_ADDR; r->siz = 4; // TODO: 8 for external symbols r->off = -1; // caller must fill in r->sym = s; @@ -717,6 +766,8 @@ asmandsz(Adr *a, int r, int rex, int m64) goto bad; case D_STATIC: case D_EXTERN: + if(flag_shared) + goto bad; t = D_NONE; v = vaddr(a, &rel); break; @@ -777,7 +828,7 @@ asmandsz(Adr *a, int r, int rex, int m64) rexflag |= (regrex[t] & Rxb) | rex; if(t == D_NONE || (D_CS <= t && t <= D_GS)) { - if(asmode != 64){ + if(flag_shared && t == D_NONE && (a->type == D_STATIC || a->type == D_EXTERN) || asmode != 64) { *andptr++ = (0 << 6) | (5 << 0) | (r << 3); goto putrelv; } @@ -1204,7 +1255,8 @@ found: break; case Zibm_r: - *andptr++ = op; + while ((op = o->op[z++]) != 0) + *andptr++ = op; asmand(&p->from, &p->to); *andptr++ = p->to.offset; break; @@ -1574,7 +1626,9 @@ bad: pp = *p; z = p->from.type; if(z >= D_BP && z <= D_DI) { - if(isax(&p->to)) { + if(isax(&p->to) || p->to.type == D_NONE) { + // We certainly don't want to exchange + // with AX if the op is MUL or DIV. *andptr++ = 0x87; /* xchg lhs,bx */ asmando(&p->from, reg[D_BX]); subreg(&pp, z, D_BX); @@ -1730,13 +1784,17 @@ asmins(Prog *p) if(c != 0xf2 && c != 0xf3 && (c < 0x64 || c > 0x67) && c != 0x2e && c != 0x3e && c != 0x26) break; } - for(r=cursym->r+cursym->nr; r-- > cursym->r; ) { - if(r->off < p->pc) - break; - r->off++; - } memmove(and+np+1, and+np, n-np); and[np] = 0x40 | rexflag; andptr++; } + n = andptr - and; + for(r=cursym->r+cursym->nr; r-- > cursym->r; ) { + if(r->off < p->pc) + break; + if(rexflag) + r->off++; + if(r->type == D_PCREL) + r->add -= p->pc + n - (r->off + r->siz); + } } diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y index f1881808f..60707d1c9 100644 --- a/src/cmd/8a/a.y +++ b/src/cmd/8a/a.y @@ -53,9 +53,9 @@ %left '+' '-' %left '*' '/' '%' %token LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4 -%token LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG +%token LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG LTYPEXC %token LCONST LFP LPC LSB -%token LBREG LLREG LSREG LFREG +%token LBREG LLREG LSREG LFREG LXREG %token LFCONST %token LSCONST LSP %token LNAME LLAB LVAR @@ -63,7 +63,7 @@ %type con2 %type mem imm imm2 reg nam rel rem rim rom omem nmem %type nonnon nonrel nonrem rimnon rimrem remrim -%type spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 +%type spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 %% prog: | prog @@ -116,6 +116,7 @@ inst: | LTYPEM spec6 { outcode($1, &$2); } | LTYPEI spec7 { outcode($1, &$2); } | LTYPEG spec8 { outcode($1, &$2); } +| LTYPEXC spec9 { outcode($1, &$2); } nonnon: { @@ -177,6 +178,11 @@ nonrel: $$.from = nullgen; $$.to = $1; } +| imm ',' rel + { + $$.from = $1; + $$.to = $3; + } spec1: /* DATA */ nam '/' con ',' imm @@ -282,6 +288,14 @@ spec8: /* GLOBL */ $$.to = $5; } +spec9: /* CMPPS/CMPPD */ + reg ',' rem ',' con + { + $$.from = $1; + $$.to = $3; + $$.to.offset = $5; + } + rem: reg | mem @@ -345,6 +359,11 @@ reg: $$ = nullgen; $$.type = $1; } +| LXREG + { + $$ = nullgen; + $$.type = $1; + } | LSP { $$ = nullgen; diff --git a/src/cmd/8a/doc.go b/src/cmd/8a/doc.go index a43b4461f..737c56f13 100644 --- a/src/cmd/8a/doc.go +++ b/src/cmd/8a/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 8a is a version of the Plan 9 assembler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2a + http://plan9.bell-labs.com/magic/man2html/1/8a Its target architecture is the x86, referred to by these tools for historical reasons as 386. */ -package documentation +package main diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index 1cc6e591d..770ca5a88 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -210,6 +210,15 @@ struct "F6", LFREG, D_F0+6, "F7", LFREG, D_F0+7, + "X0", LXREG, D_X0+0, + "X1", LXREG, D_X0+1, + "X2", LXREG, D_X0+2, + "X3", LXREG, D_X0+3, + "X4", LXREG, D_X0+4, + "X5", LXREG, D_X0+5, + "X6", LXREG, D_X0+6, + "X7", LXREG, D_X0+7, + "CS", LSREG, D_CS, "SS", LSREG, D_SS, "DS", LSREG, D_DS, @@ -271,6 +280,7 @@ struct "BSFW", LTYPE3, ABSFW, "BSRL", LTYPE3, ABSRL, "BSRW", LTYPE3, ABSRW, + "BSWAPL", LTYPE1, ABSWAPL, "BTCL", LTYPE3, ABTCL, "BTCW", LTYPE3, ABTCW, "BTL", LTYPE3, ABTL, @@ -667,6 +677,120 @@ struct "MFENCE", LTYPE0, AMFENCE, "SFENCE", LTYPE0, ASFENCE, "EMMS", LTYPE0, AEMMS, + "PREFETCHT0", LTYPE2, APREFETCHT0, + "PREFETCHT1", LTYPE2, APREFETCHT1, + "PREFETCHT2", LTYPE2, APREFETCHT2, + "PREFETCHNTA", LTYPE2, APREFETCHNTA, + "UNDEF", LTYPE0, AUNDEF, + + "ADDPD", LTYPE3, AADDPD, + "ADDPS", LTYPE3, AADDPS, + "ADDSD", LTYPE3, AADDSD, + "ADDSS", LTYPE3, AADDSS, + "ANDNPD", LTYPE3, AANDNPD, + "ANDNPS", LTYPE3, AANDNPS, + "ANDPD", LTYPE3, AANDPD, + "ANDPS", LTYPE3, AANDPS, + "CMPPD", LTYPEXC,ACMPPD, + "CMPPS", LTYPEXC,ACMPPS, + "CMPSD", LTYPEXC,ACMPSD, + "CMPSS", LTYPEXC,ACMPSS, + "COMISD", LTYPE3, ACOMISD, + "COMISS", LTYPE3, ACOMISS, + "CVTPL2PD", LTYPE3, ACVTPL2PD, + "CVTPL2PS", LTYPE3, ACVTPL2PS, + "CVTPD2PL", LTYPE3, ACVTPD2PL, + "CVTPD2PS", LTYPE3, ACVTPD2PS, + "CVTPS2PL", LTYPE3, ACVTPS2PL, + "CVTPS2PD", LTYPE3, ACVTPS2PD, + "CVTSD2SL", LTYPE3, ACVTSD2SL, + "CVTSD2SS", LTYPE3, ACVTSD2SS, + "CVTSL2SD", LTYPE3, ACVTSL2SD, + "CVTSL2SS", LTYPE3, ACVTSL2SS, + "CVTSS2SD", LTYPE3, ACVTSS2SD, + "CVTSS2SL", LTYPE3, ACVTSS2SL, + "CVTTPD2PL", LTYPE3, ACVTTPD2PL, + "CVTTPS2PL", LTYPE3, ACVTTPS2PL, + "CVTTSD2SL", LTYPE3, ACVTTSD2SL, + "CVTTSS2SL", LTYPE3, ACVTTSS2SL, + "DIVPD", LTYPE3, ADIVPD, + "DIVPS", LTYPE3, ADIVPS, + "DIVSD", LTYPE3, ADIVSD, + "DIVSS", LTYPE3, ADIVSS, + "MASKMOVOU", LTYPE3, AMASKMOVOU, + "MASKMOVDQU", LTYPE3, AMASKMOVOU, /* syn */ + "MAXPD", LTYPE3, AMAXPD, + "MAXPS", LTYPE3, AMAXPS, + "MAXSD", LTYPE3, AMAXSD, + "MAXSS", LTYPE3, AMAXSS, + "MINPD", LTYPE3, AMINPD, + "MINPS", LTYPE3, AMINPS, + "MINSD", LTYPE3, AMINSD, + "MINSS", LTYPE3, AMINSS, + "MOVAPD", LTYPE3, AMOVAPD, + "MOVAPS", LTYPE3, AMOVAPS, + "MOVO", LTYPE3, AMOVO, + "MOVOA", LTYPE3, AMOVO, /* syn */ + "MOVOU", LTYPE3, AMOVOU, + "MOVHLPS", LTYPE3, AMOVHLPS, + "MOVHPD", LTYPE3, AMOVHPD, + "MOVHPS", LTYPE3, AMOVHPS, + "MOVLHPS", LTYPE3, AMOVLHPS, + "MOVLPD", LTYPE3, AMOVLPD, + "MOVLPS", LTYPE3, AMOVLPS, + "MOVMSKPD", LTYPE3, AMOVMSKPD, + "MOVMSKPS", LTYPE3, AMOVMSKPS, + "MOVNTO", LTYPE3, AMOVNTO, + "MOVNTDQ", LTYPE3, AMOVNTO, /* syn */ + "MOVNTPD", LTYPE3, AMOVNTPD, + "MOVNTPS", LTYPE3, AMOVNTPS, + "MOVSD", LTYPE3, AMOVSD, + "MOVSS", LTYPE3, AMOVSS, + "MOVUPD", LTYPE3, AMOVUPD, + "MOVUPS", LTYPE3, AMOVUPS, + "MULPD", LTYPE3, AMULPD, + "MULPS", LTYPE3, AMULPS, + "MULSD", LTYPE3, AMULSD, + "MULSS", LTYPE3, AMULSS, + "ORPD", LTYPE3, AORPD, + "ORPS", LTYPE3, AORPS, + "PADDQ", LTYPE3, APADDQ, + "PMAXSW", LTYPE3, APMAXSW, + "PMAXUB", LTYPE3, APMAXUB, + "PMINSW", LTYPE3, APMINSW, + "PMINUB", LTYPE3, APMINUB, + "PSADBW", LTYPE3, APSADBW, + "PSUBB", LTYPE3, APSUBB, + "PSUBL", LTYPE3, APSUBL, + "PSUBQ", LTYPE3, APSUBQ, + "PSUBSB", LTYPE3, APSUBSB, + "PSUBSW", LTYPE3, APSUBSW, + "PSUBUSB", LTYPE3, APSUBUSB, + "PSUBUSW", LTYPE3, APSUBUSW, + "PSUBW", LTYPE3, APSUBW, + "PUNPCKHQDQ", LTYPE3, APUNPCKHQDQ, + "PUNPCKLQDQ", LTYPE3, APUNPCKLQDQ, + "RCPPS", LTYPE3, ARCPPS, + "RCPSS", LTYPE3, ARCPSS, + "RSQRTPS", LTYPE3, ARSQRTPS, + "RSQRTSS", LTYPE3, ARSQRTSS, + "SQRTPD", LTYPE3, ASQRTPD, + "SQRTPS", LTYPE3, ASQRTPS, + "SQRTSD", LTYPE3, ASQRTSD, + "SQRTSS", LTYPE3, ASQRTSS, + "SUBPD", LTYPE3, ASUBPD, + "SUBPS", LTYPE3, ASUBPS, + "SUBSD", LTYPE3, ASUBSD, + "SUBSS", LTYPE3, ASUBSS, + "UCOMISD", LTYPE3, AUCOMISD, + "UCOMISS", LTYPE3, AUCOMISS, + "UNPCKHPD", LTYPE3, AUNPCKHPD, + "UNPCKHPS", LTYPE3, AUNPCKHPS, + "UNPCKLPD", LTYPE3, AUNPCKLPD, + "UNPCKLPS", LTYPE3, AUNPCKLPS, + "XORPD", LTYPE3, AXORPD, + "XORPS", LTYPE3, AXORPS, + "USEFIELD", LTYPEN, AUSEFIELD, 0 }; @@ -915,11 +1039,38 @@ outhist(void) Hist *h; char *p, *q, *op, c; int n; + char *tofree; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + + tofree = nil; g = nullgen; c = pathchar(); for(h = hist; h != H; h = h->link) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -971,6 +1122,11 @@ outhist(void) Bputc(&obuf, h->line>>24); zaddr(&nullgen, 0); zaddr(&g, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } diff --git a/src/cmd/8a/y.tab.c b/src/cmd/8a/y.tab.c index ccd3a2563..38f2de52d 100644 --- a/src/cmd/8a/y.tab.c +++ b/src/cmd/8a/y.tab.c @@ -1,10 +1,8 @@ +/* A Bison parser, made by GNU Bison 2.6.5. */ -/* A Bison parser, made by GNU Bison 2.4.1. */ - -/* Skeleton implementation for Bison's Yacc-like parsers in C +/* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.4.1" +#define YYBISON_VERSION "2.6.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -60,14 +58,11 @@ /* Pull parsers. */ #define YYPULL 1 -/* Using locations. */ -#define YYLSP_NEEDED 0 /* Copy the first part of user declarations. */ - -/* Line 189 of yacc.c */ +/* Line 360 of yacc.c */ #line 31 "a.y" #include @@ -75,14 +70,16 @@ #include #include "a.h" +/* Line 360 of yacc.c */ +#line 75 "y.tab.c" -/* Line 189 of yacc.c */ -#line 81 "y.tab.c" - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE @@ -92,11 +89,17 @@ # define YYERROR_VERBOSE 0 #endif -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 +/* In a future release of Bison, this section will be replaced + by #include "y.tab.h". */ +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; #endif - /* Tokens. */ #ifndef YYTOKENTYPE @@ -118,20 +121,22 @@ LTYPEM = 269, LTYPEI = 270, LTYPEG = 271, - LCONST = 272, - LFP = 273, - LPC = 274, - LSB = 275, - LBREG = 276, - LLREG = 277, - LSREG = 278, - LFREG = 279, - LFCONST = 280, - LSCONST = 281, - LSP = 282, - LNAME = 283, - LLAB = 284, - LVAR = 285 + LTYPEXC = 272, + LCONST = 273, + LFP = 274, + LPC = 275, + LSB = 276, + LBREG = 277, + LLREG = 278, + LSREG = 279, + LFREG = 280, + LXREG = 281, + LFCONST = 282, + LSCONST = 283, + LSP = 284, + LNAME = 285, + LLAB = 286, + LVAR = 287 }; #endif /* Tokens. */ @@ -149,29 +154,29 @@ #define LTYPEM 269 #define LTYPEI 270 #define LTYPEG 271 -#define LCONST 272 -#define LFP 273 -#define LPC 274 -#define LSB 275 -#define LBREG 276 -#define LLREG 277 -#define LSREG 278 -#define LFREG 279 -#define LFCONST 280 -#define LSCONST 281 -#define LSP 282 -#define LNAME 283 -#define LLAB 284 -#define LVAR 285 - +#define LTYPEXC 272 +#define LCONST 273 +#define LFP 274 +#define LPC 275 +#define LSB 276 +#define LBREG 277 +#define LLREG 278 +#define LSREG 279 +#define LFREG 280 +#define LXREG 281 +#define LFCONST 282 +#define LSCONST 283 +#define LSP 284 +#define LNAME 285 +#define LLAB 286 +#define LVAR 287 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { - -/* Line 214 of yacc.c */ +/* Line 376 of yacc.c */ #line 37 "a.y" Sym *sym; @@ -186,21 +191,36 @@ typedef union YYSTYPE Gen2 gen2; - -/* Line 214 of yacc.c */ -#line 192 "y.tab.c" +/* Line 376 of yacc.c */ +#line 196 "y.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif +extern YYSTYPE yylval; -/* Copy the second part of user declarations. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ +/* Copy the second part of user declarations. */ -/* Line 264 of yacc.c */ -#line 204 "y.tab.c" +/* Line 379 of yacc.c */ +#line 224 "y.tab.c" #ifdef short # undef short @@ -250,27 +270,27 @@ typedef short int yytype_int16; #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ -# if YYENABLE_NLS +# if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ -# define YY_(msgid) dgettext ("bison-runtime", msgid) +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ -# define YY_(msgid) msgid +# define YY_(Msgid) Msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ -# define YYUSE(e) ((void) (e)) +# define YYUSE(E) ((void) (E)) #else -# define YYUSE(e) /* empty */ +# define YYUSE(E) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint -# define YYID(n) (n) +# define YYID(N) (N) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) @@ -303,11 +323,12 @@ YYID (yyi) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -330,24 +351,24 @@ YYID (yyi) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -376,23 +397,7 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (YYID (0)) -# endif -# endif +# define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of @@ -412,23 +417,43 @@ union yyalloc #endif +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 483 +#define YYLAST 544 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 49 +#define YYNTOKENS 51 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 37 +#define YYNNTS 38 /* YYNRULES -- Number of rules. */ -#define YYNRULES 124 +#define YYNRULES 128 /* YYNRULES -- Number of states. */ -#define YYNSTATES 244 +#define YYNSTATES 255 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 285 +#define YYMAXUTOK 287 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -439,16 +464,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 47, 12, 5, 2, - 45, 46, 10, 8, 44, 9, 2, 11, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 41, 42, - 6, 43, 7, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 49, 12, 5, 2, + 47, 48, 10, 8, 46, 9, 2, 11, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 43, 44, + 6, 45, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 2, 48, 2, 2, 2, + 2, 2, 2, 2, 3, 2, 50, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -464,7 +489,7 @@ static const yytype_uint8 yytranslate[] = 2, 2, 2, 2, 2, 2, 1, 2, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40 + 35, 36, 37, 38, 39, 40, 41, 42 }; #if YYDEBUG @@ -474,64 +499,66 @@ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 4, 5, 9, 10, 15, 16, 21, 23, 26, 29, 33, 37, 40, 43, 46, 49, 52, - 55, 58, 61, 64, 67, 70, 73, 76, 79, 80, - 82, 86, 90, 93, 95, 98, 100, 103, 105, 111, - 115, 121, 124, 126, 129, 131, 133, 137, 143, 147, - 153, 156, 158, 162, 166, 172, 174, 176, 178, 180, - 183, 186, 188, 190, 192, 194, 196, 201, 204, 207, - 209, 211, 213, 215, 217, 220, 223, 226, 229, 234, - 240, 244, 247, 249, 252, 256, 261, 263, 265, 267, - 272, 277, 284, 294, 298, 302, 307, 313, 322, 324, - 331, 337, 345, 346, 349, 352, 354, 356, 358, 360, - 362, 365, 368, 371, 375, 377, 381, 385, 389, 393, - 397, 402, 407, 411, 415 + 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, + 83, 85, 89, 93, 96, 98, 101, 103, 106, 108, + 112, 118, 122, 128, 131, 133, 136, 138, 140, 144, + 150, 154, 160, 163, 165, 169, 173, 179, 185, 187, + 189, 191, 193, 196, 199, 201, 203, 205, 207, 209, + 214, 217, 220, 222, 224, 226, 228, 230, 232, 235, + 238, 241, 244, 249, 255, 259, 262, 264, 267, 271, + 276, 278, 280, 282, 287, 292, 299, 309, 313, 317, + 322, 328, 337, 339, 346, 352, 360, 361, 364, 367, + 369, 371, 373, 375, 377, 380, 383, 386, 390, 392, + 396, 400, 404, 408, 412, 417, 422, 426, 430 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 50, 0, -1, -1, -1, 50, 51, 52, -1, -1, - 39, 41, 53, 52, -1, -1, 38, 41, 54, 52, - -1, 42, -1, 55, 42, -1, 1, 42, -1, 38, - 43, 85, -1, 40, 43, 85, -1, 13, 56, -1, - 14, 60, -1, 15, 59, -1, 16, 57, -1, 17, - 58, -1, 21, 61, -1, 19, 62, -1, 22, 63, - -1, 18, 64, -1, 20, 65, -1, 23, 66, -1, - 24, 67, -1, 25, 68, -1, 26, 69, -1, -1, - 44, -1, 72, 44, 70, -1, 70, 44, 72, -1, - 72, 44, -1, 72, -1, 44, 70, -1, 70, -1, - 44, 73, -1, 73, -1, 81, 11, 84, 44, 75, - -1, 78, 44, 76, -1, 78, 44, 84, 44, 76, - -1, 44, 71, -1, 71, -1, 10, 81, -1, 56, - -1, 60, -1, 72, 44, 70, -1, 72, 44, 70, - 41, 32, -1, 72, 44, 70, -1, 72, 44, 70, - 41, 33, -1, 72, 44, -1, 72, -1, 72, 44, - 70, -1, 78, 44, 75, -1, 78, 44, 84, 44, - 75, -1, 74, -1, 78, -1, 73, -1, 80, -1, - 10, 74, -1, 10, 79, -1, 74, -1, 79, -1, - 75, -1, 70, -1, 75, -1, 84, 45, 29, 46, - -1, 38, 82, -1, 39, 82, -1, 31, -1, 34, - -1, 32, -1, 37, -1, 33, -1, 47, 84, -1, - 47, 81, -1, 47, 36, -1, 47, 35, -1, 47, - 45, 35, 46, -1, 47, 45, 9, 35, 46, -1, - 47, 9, 35, -1, 47, 77, -1, 27, -1, 9, - 27, -1, 27, 9, 27, -1, 9, 27, 9, 27, - -1, 79, -1, 80, -1, 84, -1, 84, 45, 32, - 46, -1, 84, 45, 37, 46, -1, 84, 45, 32, - 10, 84, 46, -1, 84, 45, 32, 46, 45, 32, - 10, 84, 46, -1, 45, 32, 46, -1, 45, 37, - 46, -1, 84, 45, 33, 46, -1, 45, 32, 10, - 84, 46, -1, 45, 32, 46, 45, 32, 10, 84, - 46, -1, 81, -1, 81, 45, 32, 10, 84, 46, - -1, 38, 82, 45, 83, 46, -1, 38, 6, 7, - 82, 45, 30, 46, -1, -1, 8, 84, -1, 9, - 84, -1, 30, -1, 37, -1, 28, -1, 27, -1, - 40, -1, 9, 84, -1, 8, 84, -1, 48, 84, - -1, 45, 85, 46, -1, 84, -1, 85, 8, 85, - -1, 85, 9, 85, -1, 85, 10, 85, -1, 85, - 11, 85, -1, 85, 12, 85, -1, 85, 6, 6, - 85, -1, 85, 7, 7, 85, -1, 85, 5, 85, - -1, 85, 4, 85, -1, 85, 3, 85, -1 + 52, 0, -1, -1, -1, 52, 53, 54, -1, -1, + 41, 43, 55, 54, -1, -1, 40, 43, 56, 54, + -1, 44, -1, 57, 44, -1, 1, 44, -1, 40, + 45, 88, -1, 42, 45, 88, -1, 13, 58, -1, + 14, 62, -1, 15, 61, -1, 16, 59, -1, 17, + 60, -1, 21, 63, -1, 19, 64, -1, 22, 65, + -1, 18, 66, -1, 20, 67, -1, 23, 68, -1, + 24, 69, -1, 25, 70, -1, 26, 71, -1, 27, + 72, -1, -1, 46, -1, 75, 46, 73, -1, 73, + 46, 75, -1, 75, 46, -1, 75, -1, 46, 73, + -1, 73, -1, 46, 76, -1, 76, -1, 78, 46, + 76, -1, 84, 11, 87, 46, 78, -1, 81, 46, + 79, -1, 81, 46, 87, 46, 79, -1, 46, 74, + -1, 74, -1, 10, 84, -1, 58, -1, 62, -1, + 75, 46, 73, -1, 75, 46, 73, 43, 33, -1, + 75, 46, 73, -1, 75, 46, 73, 43, 34, -1, + 75, 46, -1, 75, -1, 75, 46, 73, -1, 81, + 46, 78, -1, 81, 46, 87, 46, 78, -1, 77, + 46, 73, 46, 87, -1, 77, -1, 81, -1, 76, + -1, 83, -1, 10, 77, -1, 10, 82, -1, 77, + -1, 82, -1, 78, -1, 73, -1, 78, -1, 87, + 47, 30, 48, -1, 40, 85, -1, 41, 85, -1, + 32, -1, 35, -1, 33, -1, 36, -1, 39, -1, + 34, -1, 49, 87, -1, 49, 84, -1, 49, 38, + -1, 49, 37, -1, 49, 47, 37, 48, -1, 49, + 47, 9, 37, 48, -1, 49, 9, 37, -1, 49, + 80, -1, 28, -1, 9, 28, -1, 28, 9, 28, + -1, 9, 28, 9, 28, -1, 82, -1, 83, -1, + 87, -1, 87, 47, 33, 48, -1, 87, 47, 39, + 48, -1, 87, 47, 33, 10, 87, 48, -1, 87, + 47, 33, 48, 47, 33, 10, 87, 48, -1, 47, + 33, 48, -1, 47, 39, 48, -1, 87, 47, 34, + 48, -1, 47, 33, 10, 87, 48, -1, 47, 33, + 48, 47, 33, 10, 87, 48, -1, 84, -1, 84, + 47, 33, 10, 87, 48, -1, 40, 85, 47, 86, + 48, -1, 40, 6, 7, 85, 47, 31, 48, -1, + -1, 8, 87, -1, 9, 87, -1, 31, -1, 39, + -1, 29, -1, 28, -1, 42, -1, 9, 87, -1, + 8, 87, -1, 50, 87, -1, 47, 88, 48, -1, + 87, -1, 88, 8, 88, -1, 88, 9, 88, -1, + 88, 10, 88, -1, 88, 11, 88, -1, 88, 12, + 88, -1, 88, 6, 6, 88, -1, 88, 7, 7, + 88, -1, 88, 5, 88, -1, 88, 4, 88, -1, + 88, 3, 88, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -539,21 +566,21 @@ static const yytype_uint16 yyrline[] = { 0, 68, 68, 70, 69, 77, 76, 84, 83, 89, 90, 91, 94, 99, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 121, 125, - 132, 139, 146, 151, 158, 163, 170, 175, 182, 190, - 195, 203, 208, 213, 222, 223, 226, 231, 241, 246, - 256, 261, 266, 273, 278, 286, 287, 290, 291, 292, - 296, 300, 301, 302, 305, 306, 309, 315, 324, 333, - 338, 343, 348, 353, 360, 366, 377, 383, 389, 395, - 401, 409, 418, 423, 428, 433, 440, 441, 444, 450, - 456, 462, 471, 480, 485, 490, 496, 504, 514, 518, - 527, 534, 543, 546, 550, 556, 557, 561, 564, 565, - 569, 573, 577, 581, 587, 588, 592, 596, 600, 604, - 608, 612, 616, 620, 624 + 111, 112, 113, 114, 115, 116, 117, 118, 119, 122, + 126, 133, 140, 147, 152, 159, 164, 171, 176, 181, + 188, 196, 201, 209, 214, 219, 228, 229, 232, 237, + 247, 252, 262, 267, 272, 279, 284, 292, 300, 301, + 304, 305, 306, 310, 314, 315, 316, 319, 320, 323, + 329, 338, 347, 352, 357, 362, 367, 372, 379, 385, + 396, 402, 408, 414, 420, 428, 437, 442, 447, 452, + 459, 460, 463, 469, 475, 481, 490, 499, 504, 509, + 515, 523, 533, 537, 546, 553, 562, 565, 569, 575, + 576, 580, 583, 584, 588, 592, 596, 600, 606, 607, + 611, 615, 619, 623, 627, 631, 635, 639, 643 }; #endif -#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +#if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = @@ -561,14 +588,14 @@ static const char *const yytname[] = "$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "LTYPE0", "LTYPE1", "LTYPE2", "LTYPE3", "LTYPE4", "LTYPEC", "LTYPED", "LTYPEN", "LTYPER", "LTYPET", "LTYPES", - "LTYPEM", "LTYPEI", "LTYPEG", "LCONST", "LFP", "LPC", "LSB", "LBREG", - "LLREG", "LSREG", "LFREG", "LFCONST", "LSCONST", "LSP", "LNAME", "LLAB", - "LVAR", "':'", "';'", "'='", "','", "'('", "')'", "'$'", "'~'", - "$accept", "prog", "$@1", "line", "$@2", "$@3", "inst", "nonnon", + "LTYPEM", "LTYPEI", "LTYPEG", "LTYPEXC", "LCONST", "LFP", "LPC", "LSB", + "LBREG", "LLREG", "LSREG", "LFREG", "LXREG", "LFCONST", "LSCONST", "LSP", + "LNAME", "LLAB", "LVAR", "':'", "';'", "'='", "','", "'('", "')'", "'$'", + "'~'", "$accept", "prog", "$@1", "line", "$@2", "$@3", "inst", "nonnon", "rimrem", "remrim", "rimnon", "nonrem", "nonrel", "spec1", "spec2", - "spec3", "spec4", "spec5", "spec6", "spec7", "spec8", "rem", "rom", - "rim", "rel", "reg", "imm", "imm2", "con2", "mem", "omem", "nmem", "nam", - "offset", "pointer", "con", "expr", 0 + "spec3", "spec4", "spec5", "spec6", "spec7", "spec8", "spec9", "rem", + "rom", "rim", "rel", "reg", "imm", "imm2", "con2", "mem", "omem", "nmem", + "nam", "offset", "pointer", "con", "expr", YY_NULL }; #endif @@ -581,26 +608,27 @@ static const yytype_uint16 yytoknum[] = 42, 47, 37, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, - 285, 58, 59, 61, 44, 40, 41, 36, 126 + 285, 286, 287, 58, 59, 61, 44, 40, 41, 36, + 126 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 49, 50, 51, 50, 53, 52, 54, 52, 52, - 52, 52, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, - 57, 58, 59, 59, 60, 60, 61, 61, 62, 63, - 63, 64, 64, 64, 65, 65, 66, 66, 67, 67, - 68, 68, 68, 69, 69, 70, 70, 71, 71, 71, - 71, 71, 71, 71, 72, 72, 73, 73, 73, 74, - 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, - 75, 76, 77, 77, 77, 77, 78, 78, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 80, 80, - 81, 81, 82, 82, 82, 83, 83, 83, 84, 84, - 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85 + 0, 51, 52, 53, 52, 55, 54, 56, 54, 54, + 54, 54, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 58, + 58, 59, 60, 61, 61, 62, 62, 63, 63, 63, + 64, 65, 65, 66, 66, 66, 67, 67, 68, 68, + 69, 69, 70, 70, 70, 71, 71, 72, 73, 73, + 74, 74, 74, 74, 74, 74, 74, 75, 75, 76, + 76, 76, 77, 77, 77, 77, 77, 77, 78, 78, + 78, 78, 78, 78, 78, 79, 80, 80, 80, 80, + 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 83, 83, 84, 84, 85, 85, 85, 86, + 86, 86, 87, 87, 87, 87, 87, 87, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -608,241 +636,261 @@ static const yytype_uint8 yyr2[] = { 0, 2, 0, 0, 3, 0, 4, 0, 4, 1, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, - 3, 3, 2, 1, 2, 1, 2, 1, 5, 3, - 5, 2, 1, 2, 1, 1, 3, 5, 3, 5, - 2, 1, 3, 3, 5, 1, 1, 1, 1, 2, - 2, 1, 1, 1, 1, 1, 4, 2, 2, 1, - 1, 1, 1, 1, 2, 2, 2, 2, 4, 5, - 3, 2, 1, 2, 3, 4, 1, 1, 1, 4, - 4, 6, 9, 3, 3, 4, 5, 8, 1, 6, - 5, 7, 0, 2, 2, 1, 1, 1, 1, 1, - 2, 2, 2, 3, 1, 3, 3, 3, 3, 3, - 4, 4, 3, 3, 3 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 1, 3, 3, 2, 1, 2, 1, 2, 1, 3, + 5, 3, 5, 2, 1, 2, 1, 1, 3, 5, + 3, 5, 2, 1, 3, 3, 5, 5, 1, 1, + 1, 1, 2, 2, 1, 1, 1, 1, 1, 4, + 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 4, 5, 3, 2, 1, 2, 3, 4, + 1, 1, 1, 4, 4, 6, 9, 3, 3, 4, + 5, 8, 1, 6, 5, 7, 0, 2, 2, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 1, 3, + 3, 3, 3, 3, 4, 4, 3, 3, 3 }; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 3, 1, 0, 0, 28, 0, 0, 0, 0, - 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 4, 0, 11, 29, 14, 0, 0, - 108, 69, 71, 73, 70, 72, 102, 109, 0, 0, - 0, 15, 35, 55, 56, 86, 87, 98, 88, 0, - 16, 64, 33, 65, 17, 0, 18, 0, 0, 102, - 102, 0, 22, 42, 57, 61, 63, 62, 58, 88, - 20, 0, 29, 44, 45, 23, 102, 0, 0, 19, - 37, 0, 21, 0, 24, 0, 25, 0, 26, 51, - 27, 0, 7, 0, 5, 0, 10, 111, 110, 0, - 0, 0, 0, 34, 0, 0, 114, 0, 112, 0, - 0, 0, 77, 76, 0, 75, 74, 32, 0, 0, - 59, 60, 43, 67, 68, 0, 41, 0, 0, 67, - 36, 0, 0, 0, 0, 50, 0, 0, 12, 0, - 13, 102, 103, 104, 0, 0, 93, 94, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, - 0, 0, 0, 80, 0, 0, 30, 31, 0, 0, - 0, 39, 0, 46, 48, 52, 53, 0, 8, 6, - 0, 107, 105, 106, 0, 0, 0, 124, 123, 122, - 0, 0, 115, 116, 117, 118, 119, 0, 0, 89, - 95, 90, 0, 78, 66, 0, 0, 82, 81, 0, - 0, 0, 0, 0, 100, 96, 0, 120, 121, 0, - 0, 0, 79, 38, 83, 0, 40, 47, 49, 54, - 0, 0, 99, 91, 0, 0, 84, 101, 0, 0, - 85, 97, 0, 92 + 2, 3, 1, 0, 0, 29, 0, 0, 0, 0, + 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 4, 0, 11, 30, 14, 0, + 0, 112, 72, 74, 77, 73, 75, 76, 106, 113, + 0, 0, 0, 15, 36, 58, 59, 90, 91, 102, + 92, 0, 16, 67, 34, 68, 17, 0, 18, 0, + 0, 106, 106, 0, 22, 44, 60, 64, 66, 65, + 61, 92, 20, 0, 30, 46, 47, 23, 106, 0, + 0, 19, 38, 0, 0, 21, 0, 24, 0, 25, + 0, 26, 53, 27, 0, 28, 0, 7, 0, 5, + 0, 10, 115, 114, 0, 0, 0, 0, 35, 0, + 0, 118, 0, 116, 0, 0, 0, 81, 80, 0, + 79, 78, 33, 0, 0, 62, 63, 45, 70, 71, + 0, 43, 0, 0, 70, 37, 0, 0, 0, 0, + 0, 52, 0, 0, 0, 12, 0, 13, 106, 107, + 108, 0, 0, 97, 98, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 117, 0, 0, 0, 0, + 84, 0, 0, 31, 32, 0, 0, 39, 0, 41, + 0, 48, 50, 54, 55, 0, 0, 8, 6, 0, + 111, 109, 110, 0, 0, 0, 128, 127, 126, 0, + 0, 119, 120, 121, 122, 123, 0, 0, 93, 99, + 94, 0, 82, 69, 0, 0, 86, 85, 0, 0, + 0, 0, 0, 0, 104, 100, 0, 124, 125, 0, + 0, 0, 83, 40, 87, 0, 42, 49, 51, 56, + 57, 0, 0, 103, 95, 0, 0, 88, 105, 0, + 0, 89, 101, 0, 96 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 23, 139, 137, 24, 27, 54, 56, - 50, 41, 79, 70, 82, 62, 75, 84, 86, 88, - 90, 51, 63, 52, 64, 43, 53, 171, 208, 44, - 45, 46, 47, 102, 184, 48, 107 + -1, 1, 3, 24, 146, 144, 25, 28, 56, 58, + 52, 43, 81, 72, 85, 64, 77, 87, 89, 91, + 93, 95, 53, 65, 54, 66, 45, 55, 179, 217, + 46, 47, 48, 49, 107, 193, 50, 112 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -107 +#define YYPACT_NINF -96 static const yytype_int16 yypact[] = { - -107, 9, -107, 151, -28, -19, 235, 255, 255, 302, - 175, 6, 282, 34, 363, 255, 255, 255, 363, -3, - -6, 5, -107, -107, 4, -107, -107, -107, 373, 373, - -107, -107, -107, -107, -107, -107, 128, -107, 302, 343, - 373, -107, -107, -107, -107, -107, -107, 17, 19, 115, - -107, -107, 15, -107, -107, 27, -107, 42, 302, 128, - 72, 208, -107, -107, -107, -107, -107, -107, -107, 52, - -107, 44, 302, -107, -107, -107, 72, 359, 373, -107, - -107, 55, -107, 71, -107, 76, -107, 81, -107, 84, - -107, 97, -107, 373, -107, 373, -107, -107, -107, 142, - 373, 373, 114, -107, 1, 116, -107, 102, -107, 129, - 66, 385, -107, -107, 387, -107, -107, -107, 302, 255, - -107, -107, -107, 114, -107, 329, -107, 168, 373, -107, - -107, 149, 18, 302, 302, 302, 397, 151, 447, 151, - 447, 72, -107, -107, 47, 373, 134, -107, 373, 373, - 373, 176, 179, 373, 373, 373, 373, 373, -107, 182, - 3, 148, 152, -107, 401, 153, -107, -107, 158, 166, - 14, -107, 167, 154, 189, -107, -107, 187, -107, -107, - 188, -107, -107, -107, 186, 190, 202, 456, 464, 471, - 373, 373, 146, 146, -107, -107, -107, 373, 373, 192, - -107, -107, 203, -107, -107, 191, 223, 242, -107, 205, - 222, 224, 191, 228, -107, -107, 249, 216, 216, 214, - 215, 233, -107, -107, 261, 244, -107, -107, -107, -107, - 230, 373, -107, -107, 264, 250, -107, -107, 232, 373, - -107, -107, 238, -107 + -96, 34, -96, 158, -38, -30, 267, 288, 288, 338, + 195, 20, 317, 210, 428, 288, 288, 288, 428, 68, + -6, 2, 42, -96, -96, 45, -96, -96, -96, 449, + 449, -96, -96, -96, -96, -96, -96, -96, 127, -96, + 338, 388, 449, -96, -96, -96, -96, -96, -96, 46, + 47, 382, -96, -96, 52, -96, -96, 59, -96, 62, + 338, 127, 19, 238, -96, -96, -96, -96, -96, -96, + -96, 63, -96, 103, 338, -96, -96, -96, 19, 403, + 449, -96, -96, 69, 78, -96, 81, -96, 94, -96, + 97, -96, 98, -96, 102, -96, 111, -96, 449, -96, + 449, -96, -96, -96, 142, 449, 449, 113, -96, 23, + 110, -96, 74, -96, 137, 58, 432, -96, -96, 439, + -96, -96, -96, 338, 288, -96, -96, -96, 113, -96, + 367, -96, 13, 449, -96, -96, 403, 156, 16, 338, + 338, 338, 443, 338, 158, 523, 158, 523, 19, -96, + -96, 11, 449, 140, -96, 449, 449, 449, 182, 194, + 449, 449, 449, 449, 449, -96, 197, 25, 160, 161, + -96, 475, 162, -96, -96, 163, 168, -96, 8, -96, + 169, 173, 174, -96, -96, 175, 176, -96, -96, 177, + -96, -96, -96, 172, 178, 192, 532, 157, 498, 449, + 449, 64, 64, -96, -96, -96, 449, 449, 186, -96, + -96, 191, -96, -96, 200, 215, 244, -96, 205, 222, + 224, 200, 449, 230, -96, -96, 252, 183, 183, 216, + 217, 234, -96, -96, 254, 240, -96, -96, -96, -96, + -96, 221, 449, -96, -96, 271, 255, -96, -96, 236, + 449, -96, -96, 241, -96 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -107, -107, -107, -106, -107, -107, -107, 269, -107, -107, - -107, 273, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -2, 236, 0, -1, -8, -9, 85, -107, 10, - -4, -5, 11, -39, -107, -10, -61 + -96, -96, -96, -95, -96, -96, -96, 270, -96, -96, + -96, 274, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -2, 227, 6, -12, -1, -8, 73, -96, + 12, 1, 5, -3, -49, -96, -10, -44 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ + number is the opposite. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint8 yytable[] = { - 69, 66, 65, 81, 42, 68, 67, 57, 55, 2, - 42, 145, 80, 198, 25, 85, 87, 89, 97, 98, - 123, 124, 71, 206, 83, 26, 28, 29, 91, 106, - 108, 178, 138, 179, 140, 94, 103, 129, 92, 116, - 93, 207, 28, 29, 36, 30, 96, 146, 95, 199, - 120, 69, 66, 65, 121, 128, 68, 67, 37, 117, - 115, 30, 109, 78, 110, 170, 40, 81, 106, 122, - 103, 118, 76, 60, 37, 181, 130, 182, 77, 78, - 100, 101, 40, 106, 183, 106, 119, 187, 188, 189, - 142, 143, 192, 193, 194, 195, 196, 127, 160, 161, - 131, 98, 180, 162, 106, 148, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 132, 166, 120, 169, 167, - 133, 121, 172, 28, 111, 134, 177, 176, 135, 217, - 218, 173, 174, 175, 99, 185, 100, 101, 106, 106, - 106, 136, 30, 106, 106, 106, 106, 106, 158, 141, - 112, 113, 4, 36, 98, 37, 155, 156, 157, 144, - 114, 159, 147, 40, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 168, 186, - 106, 106, 190, 28, 29, 58, 191, 219, 220, 19, - 20, 21, 197, 22, 200, 210, 223, 168, 201, 203, - 160, 161, 30, 229, 204, 162, 31, 32, 33, 34, - 205, 209, 35, 59, 60, 37, 28, 29, 125, 61, - 39, 238, 49, 40, 153, 154, 155, 156, 157, 242, - 211, 212, 214, 213, 216, 30, 215, 221, 49, 31, - 32, 33, 34, 28, 29, 35, 59, 60, 37, 222, - 224, 225, 170, 39, 227, 49, 40, 228, 230, 231, - 232, 233, 30, 28, 29, 234, 31, 32, 33, 34, - 235, 236, 35, 36, 239, 37, 237, 240, 241, 38, - 39, 73, 30, 40, 243, 74, 31, 32, 33, 34, - 28, 29, 35, 36, 226, 37, 0, 126, 0, 0, - 39, 0, 49, 40, 0, 0, 0, 0, 0, 30, - 28, 29, 0, 31, 32, 33, 34, 0, 0, 35, - 36, 0, 37, 0, 0, 0, 72, 39, 0, 30, - 40, 0, 0, 31, 32, 33, 34, 28, 29, 35, - 36, 0, 37, 0, 0, 0, 0, 39, 0, 0, - 40, 28, 29, 0, 0, 0, 30, 0, 0, 0, - 31, 32, 33, 34, 0, 0, 35, 28, 29, 37, - 30, 28, 29, 0, 39, 104, 0, 40, 0, 0, - 105, 28, 29, 37, 0, 0, 30, 0, 78, 0, - 30, 40, 0, 28, 29, 28, 164, 76, 60, 37, - 30, 36, 0, 37, 78, 28, 29, 40, 39, 28, - 29, 40, 30, 37, 30, 0, 0, 0, 78, 0, - 163, 40, 165, 0, 30, 37, 0, 37, 30, 0, - 78, 0, 78, 40, 0, 40, 202, 37, 0, 0, - 0, 37, 78, 0, 49, 40, 78, 0, 0, 40, - 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, - 149, 150, 151, 152, 153, 154, 155, 156, 157, 150, - 151, 152, 153, 154, 155, 156, 157, 151, 152, 153, - 154, 155, 156, 157 + 71, 82, 68, 84, 44, 83, 26, 59, 73, 67, + 44, 69, 128, 129, 57, 70, 27, 215, 96, 102, + 103, 88, 90, 92, 29, 30, 86, 105, 106, 134, + 94, 111, 113, 152, 2, 207, 216, 97, 108, 98, + 190, 121, 191, 175, 31, 99, 167, 168, 120, 187, + 192, 188, 169, 71, 145, 68, 147, 127, 39, 125, + 38, 126, 67, 80, 69, 178, 42, 135, 70, 84, + 111, 153, 108, 208, 162, 163, 164, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 100, 111, 101, + 111, 167, 168, 114, 115, 149, 150, 169, 122, 189, + 32, 33, 34, 35, 36, 123, 103, 37, 124, 111, + 132, 196, 197, 198, 133, 136, 201, 202, 203, 204, + 205, 173, 165, 176, 177, 137, 84, 138, 180, 125, + 174, 126, 185, 104, 184, 105, 106, 181, 182, 183, + 139, 186, 194, 140, 141, 111, 111, 111, 142, 148, + 111, 111, 111, 111, 111, 227, 228, 143, 154, 4, + 151, 103, 157, 158, 159, 160, 161, 162, 163, 164, + 166, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 175, 195, 199, 111, + 111, 160, 161, 162, 163, 164, 229, 230, 20, 21, + 22, 200, 23, 29, 30, 60, 233, 206, 209, 210, + 212, 213, 240, 239, 214, 218, 219, 220, 29, 30, + 224, 221, 222, 31, 223, 226, 225, 32, 33, 34, + 35, 36, 249, 231, 37, 61, 62, 39, 31, 232, + 253, 63, 41, 234, 51, 42, 29, 30, 130, 51, + 78, 62, 39, 235, 178, 237, 79, 80, 238, 51, + 42, 241, 242, 246, 243, 244, 31, 245, 247, 248, + 32, 33, 34, 35, 36, 29, 30, 37, 61, 62, + 39, 250, 75, 251, 252, 41, 76, 51, 42, 254, + 131, 236, 0, 0, 0, 31, 29, 30, 0, 32, + 33, 34, 35, 36, 0, 0, 37, 38, 0, 39, + 0, 0, 0, 40, 41, 0, 31, 42, 0, 0, + 32, 33, 34, 35, 36, 29, 30, 37, 38, 0, + 39, 0, 0, 0, 0, 41, 0, 51, 42, 0, + 0, 0, 0, 0, 0, 31, 29, 30, 0, 32, + 33, 34, 35, 36, 0, 0, 37, 38, 0, 39, + 0, 0, 0, 74, 41, 0, 31, 42, 0, 0, + 32, 33, 34, 35, 36, 29, 30, 37, 38, 0, + 39, 0, 0, 0, 0, 41, 0, 0, 42, 0, + 29, 116, 0, 0, 0, 31, 29, 30, 0, 32, + 33, 34, 35, 36, 0, 0, 37, 0, 0, 39, + 31, 29, 30, 0, 41, 0, 31, 42, 0, 117, + 118, 109, 38, 0, 39, 0, 0, 110, 0, 119, + 39, 31, 42, 0, 0, 80, 29, 30, 42, 0, + 29, 30, 0, 78, 62, 39, 0, 29, 171, 0, + 80, 29, 30, 42, 0, 0, 31, 29, 30, 0, + 31, 0, 0, 0, 0, 0, 0, 31, 38, 170, + 39, 31, 0, 0, 39, 41, 172, 31, 42, 80, + 0, 39, 42, 29, 30, 39, 80, 0, 0, 42, + 80, 39, 51, 42, 0, 0, 80, 0, 0, 42, + 0, 0, 0, 31, 158, 159, 160, 161, 162, 163, + 164, 0, 211, 0, 0, 0, 0, 39, 0, 0, + 0, 0, 80, 0, 0, 42, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 156, 157, 158, 159, + 160, 161, 162, 163, 164 }; +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-96))) + +#define yytable_value_is_error(Yytable_value) \ + YYID (0) + static const yytype_int16 yycheck[] = { - 10, 10, 10, 13, 6, 10, 10, 9, 8, 0, - 12, 10, 13, 10, 42, 15, 16, 17, 28, 29, - 59, 60, 11, 9, 14, 44, 8, 9, 18, 39, - 40, 137, 93, 139, 95, 41, 38, 76, 41, 49, - 43, 27, 8, 9, 38, 27, 42, 46, 43, 46, - 58, 61, 61, 61, 58, 11, 61, 61, 40, 44, - 49, 27, 45, 45, 45, 47, 48, 77, 78, 58, - 72, 44, 38, 39, 40, 28, 77, 30, 44, 45, - 8, 9, 48, 93, 37, 95, 44, 148, 149, 150, - 100, 101, 153, 154, 155, 156, 157, 45, 32, 33, - 45, 111, 141, 37, 114, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 44, 118, 125, 128, 119, - 44, 125, 132, 8, 9, 44, 136, 136, 44, 190, - 191, 133, 134, 135, 6, 145, 8, 9, 148, 149, - 150, 44, 27, 153, 154, 155, 156, 157, 46, 7, - 35, 36, 1, 38, 164, 40, 10, 11, 12, 45, - 45, 32, 46, 48, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, 29, 45, - 190, 191, 6, 8, 9, 10, 7, 197, 198, 38, - 39, 40, 10, 42, 46, 41, 205, 29, 46, 46, - 32, 33, 27, 212, 46, 37, 31, 32, 33, 34, - 44, 44, 37, 38, 39, 40, 8, 9, 10, 44, - 45, 231, 47, 48, 8, 9, 10, 11, 12, 239, - 41, 44, 46, 45, 32, 27, 46, 45, 47, 31, - 32, 33, 34, 8, 9, 37, 38, 39, 40, 46, - 27, 9, 47, 45, 32, 47, 48, 33, 30, 10, - 46, 46, 27, 8, 9, 32, 31, 32, 33, 34, - 9, 27, 37, 38, 10, 40, 46, 27, 46, 44, - 45, 12, 27, 48, 46, 12, 31, 32, 33, 34, - 8, 9, 37, 38, 209, 40, -1, 61, -1, -1, - 45, -1, 47, 48, -1, -1, -1, -1, -1, 27, - 8, 9, -1, 31, 32, 33, 34, -1, -1, 37, - 38, -1, 40, -1, -1, -1, 44, 45, -1, 27, - 48, -1, -1, 31, 32, 33, 34, 8, 9, 37, - 38, -1, 40, -1, -1, -1, -1, 45, -1, -1, - 48, 8, 9, -1, -1, -1, 27, -1, -1, -1, - 31, 32, 33, 34, -1, -1, 37, 8, 9, 40, - 27, 8, 9, -1, 45, 32, -1, 48, -1, -1, - 37, 8, 9, 40, -1, -1, 27, -1, 45, -1, - 27, 48, -1, 8, 9, 8, 9, 38, 39, 40, - 27, 38, -1, 40, 45, 8, 9, 48, 45, 8, - 9, 48, 27, 40, 27, -1, -1, -1, 45, -1, - 35, 48, 35, -1, 27, 40, -1, 40, 27, -1, - 45, -1, 45, 48, -1, 48, 35, 40, -1, -1, - -1, 40, 45, -1, 47, 48, 45, -1, -1, 48, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, - 6, 7, 8, 9, 10, 11, 12, 6, 7, 8, - 9, 10, 11, 12 + 10, 13, 10, 13, 6, 13, 44, 9, 11, 10, + 12, 10, 61, 62, 8, 10, 46, 9, 19, 29, + 30, 15, 16, 17, 8, 9, 14, 8, 9, 78, + 18, 41, 42, 10, 0, 10, 28, 43, 40, 45, + 29, 51, 31, 30, 28, 43, 33, 34, 51, 144, + 39, 146, 39, 63, 98, 63, 100, 60, 42, 60, + 40, 60, 63, 47, 63, 49, 50, 79, 63, 79, + 80, 48, 74, 48, 10, 11, 12, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 45, 98, 44, + 100, 33, 34, 47, 47, 105, 106, 39, 46, 148, + 32, 33, 34, 35, 36, 46, 116, 39, 46, 119, + 47, 155, 156, 157, 11, 46, 160, 161, 162, 163, + 164, 123, 48, 133, 136, 47, 136, 46, 138, 130, + 124, 130, 142, 6, 142, 8, 9, 139, 140, 141, + 46, 143, 152, 46, 46, 155, 156, 157, 46, 7, + 160, 161, 162, 163, 164, 199, 200, 46, 48, 1, + 47, 171, 5, 6, 7, 8, 9, 10, 11, 12, + 33, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 30, 47, 6, 199, + 200, 8, 9, 10, 11, 12, 206, 207, 40, 41, + 42, 7, 44, 8, 9, 10, 214, 10, 48, 48, + 48, 48, 222, 221, 46, 46, 43, 43, 8, 9, + 48, 46, 46, 28, 47, 33, 48, 32, 33, 34, + 35, 36, 242, 47, 39, 40, 41, 42, 28, 48, + 250, 46, 47, 28, 49, 50, 8, 9, 10, 49, + 40, 41, 42, 9, 49, 33, 46, 47, 34, 49, + 50, 31, 10, 9, 48, 48, 28, 33, 28, 48, + 32, 33, 34, 35, 36, 8, 9, 39, 40, 41, + 42, 10, 12, 28, 48, 47, 12, 49, 50, 48, + 63, 218, -1, -1, -1, 28, 8, 9, -1, 32, + 33, 34, 35, 36, -1, -1, 39, 40, -1, 42, + -1, -1, -1, 46, 47, -1, 28, 50, -1, -1, + 32, 33, 34, 35, 36, 8, 9, 39, 40, -1, + 42, -1, -1, -1, -1, 47, -1, 49, 50, -1, + -1, -1, -1, -1, -1, 28, 8, 9, -1, 32, + 33, 34, 35, 36, -1, -1, 39, 40, -1, 42, + -1, -1, -1, 46, 47, -1, 28, 50, -1, -1, + 32, 33, 34, 35, 36, 8, 9, 39, 40, -1, + 42, -1, -1, -1, -1, 47, -1, -1, 50, -1, + 8, 9, -1, -1, -1, 28, 8, 9, -1, 32, + 33, 34, 35, 36, -1, -1, 39, -1, -1, 42, + 28, 8, 9, -1, 47, -1, 28, 50, -1, 37, + 38, 33, 40, -1, 42, -1, -1, 39, -1, 47, + 42, 28, 50, -1, -1, 47, 8, 9, 50, -1, + 8, 9, -1, 40, 41, 42, -1, 8, 9, -1, + 47, 8, 9, 50, -1, -1, 28, 8, 9, -1, + 28, -1, -1, -1, -1, -1, -1, 28, 40, 37, + 42, 28, -1, -1, 42, 47, 37, 28, 50, 47, + -1, 42, 50, 8, 9, 42, 47, -1, -1, 50, + 47, 42, 49, 50, -1, -1, 47, -1, -1, 50, + -1, -1, -1, 28, 6, 7, 8, 9, 10, 11, + 12, -1, 37, -1, -1, -1, -1, 42, -1, -1, + -1, -1, 47, -1, -1, 50, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 4, 5, 6, 7, + 8, 9, 10, 11, 12 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 50, 0, 51, 1, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 38, - 39, 40, 42, 52, 55, 42, 44, 56, 8, 9, - 27, 31, 32, 33, 34, 37, 38, 40, 44, 45, - 48, 60, 70, 74, 78, 79, 80, 81, 84, 47, - 59, 70, 72, 75, 57, 72, 58, 70, 10, 38, - 39, 44, 64, 71, 73, 74, 75, 79, 80, 84, - 62, 81, 44, 56, 60, 65, 38, 44, 45, 61, - 73, 84, 63, 78, 66, 72, 67, 72, 68, 72, - 69, 78, 41, 43, 41, 43, 42, 84, 84, 6, - 8, 9, 82, 70, 32, 37, 84, 85, 84, 45, - 45, 9, 35, 36, 45, 81, 84, 44, 44, 44, - 74, 79, 81, 82, 82, 10, 71, 45, 11, 82, - 73, 45, 44, 44, 44, 44, 44, 54, 85, 53, - 85, 7, 84, 84, 45, 10, 46, 46, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 46, 32, - 32, 33, 37, 35, 9, 35, 70, 72, 29, 84, - 47, 76, 84, 70, 70, 70, 75, 84, 52, 52, - 82, 28, 30, 37, 83, 84, 45, 85, 85, 85, - 6, 7, 85, 85, 85, 85, 85, 10, 10, 46, - 46, 46, 35, 46, 46, 44, 9, 27, 77, 44, - 41, 41, 44, 45, 46, 46, 32, 85, 85, 84, - 84, 45, 46, 75, 27, 9, 76, 32, 33, 75, - 30, 10, 46, 46, 32, 9, 27, 46, 84, 10, - 27, 46, 84, 46 + 0, 52, 0, 53, 1, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 40, 41, 42, 44, 54, 57, 44, 46, 58, 8, + 9, 28, 32, 33, 34, 35, 36, 39, 40, 42, + 46, 47, 50, 62, 73, 77, 81, 82, 83, 84, + 87, 49, 61, 73, 75, 78, 59, 75, 60, 73, + 10, 40, 41, 46, 66, 74, 76, 77, 78, 82, + 83, 87, 64, 84, 46, 58, 62, 67, 40, 46, + 47, 63, 76, 78, 87, 65, 81, 68, 75, 69, + 75, 70, 75, 71, 81, 72, 77, 43, 45, 43, + 45, 44, 87, 87, 6, 8, 9, 85, 73, 33, + 39, 87, 88, 87, 47, 47, 9, 37, 38, 47, + 84, 87, 46, 46, 46, 77, 82, 84, 85, 85, + 10, 74, 47, 11, 85, 76, 46, 47, 46, 46, + 46, 46, 46, 46, 56, 88, 55, 88, 7, 87, + 87, 47, 10, 48, 48, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 48, 33, 33, 34, 39, + 37, 9, 37, 73, 75, 30, 87, 76, 49, 79, + 87, 73, 73, 73, 78, 87, 73, 54, 54, 85, + 29, 31, 39, 86, 87, 47, 88, 88, 88, 6, + 7, 88, 88, 88, 88, 88, 10, 10, 48, 48, + 48, 37, 48, 48, 46, 9, 28, 80, 46, 43, + 43, 46, 46, 47, 48, 48, 33, 88, 88, 87, + 87, 47, 48, 78, 28, 9, 79, 33, 34, 78, + 87, 31, 10, 48, 48, 33, 9, 28, 48, 87, + 10, 28, 48, 87, 48 }; #define yyerrok (yyerrstatus = 0) @@ -857,78 +905,50 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ #define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif #define YYRECOVERING() (!!yyerrstatus) -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK (1); \ - goto yybackup; \ - } \ - else \ - { \ +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) - +/* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 -/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - do \ - if (YYID (N)) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ - } \ - while (YYID (0)) -#endif - - -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ - +/* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT -# if YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif /* YYLEX -- calling `yylex' with the right arguments. */ - #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else @@ -978,6 +998,8 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep) YYSTYPE const * const yyvaluep; #endif { + FILE *yyo = yyoutput; + YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT @@ -1115,7 +1137,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1218,115 +1239,142 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } - if (yysize_overflow) - return YYSIZE_MAXIMUM; + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -1359,36 +1407,31 @@ yydestruct (yymsg, yytype, yyvaluep) } } -/* Prevent warnings from -Wmissing-prototypes. */ -#ifdef YYPARSE_PARAM -#if defined __STDC__ || defined __cplusplus -int yyparse (void *YYPARSE_PARAM); -#else -int yyparse (); -#endif -#else /* ! YYPARSE_PARAM */ -#if defined __STDC__ || defined __cplusplus -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ + /* The lookahead symbol. */ int yychar; + +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + /* The semantic value of the lookahead symbol. */ -YYSTYPE yylval; +YYSTYPE yylval YY_INITIAL_VALUE(yyval_default); /* Number of syntax errors so far. */ int yynerrs; - -/*-------------------------. -| yyparse or yypush_parse. | -`-------------------------*/ +/*----------. +| yyparse. | +`----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ @@ -1412,8 +1455,6 @@ yyparse () #endif #endif { - - int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; @@ -1422,7 +1463,7 @@ yyparse () `yyss': related to states. `yyvs': related to semantic values. - Refer to the stacks thru separate pointers, to allow yyoverflow + Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ @@ -1440,7 +1481,7 @@ yyparse () int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ - int yytoken; + int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; @@ -1458,9 +1499,8 @@ yyparse () Keep to zero when no symbol should be popped. */ int yylen = 0; - yytoken = 0; - yyss = yyssa; - yyvs = yyvsa; + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); @@ -1469,14 +1509,6 @@ yyparse () yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - yyssp = yyss; - yyvsp = yyvs; - goto yysetstate; /*------------------------------------------------------------. @@ -1568,7 +1600,7 @@ yybackup: /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ @@ -1599,8 +1631,8 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } @@ -1617,7 +1649,9 @@ yybackup: yychar = YYEMPTY; yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; @@ -1654,8 +1688,7 @@ yyreduce: switch (yyn) { case 3: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 70 "a.y" { stmtline = lineno; @@ -1663,8 +1696,7 @@ yyreduce: break; case 5: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 77 "a.y" { if((yyvsp[(1) - (2)].sym)->value != pc) @@ -1674,8 +1706,7 @@ yyreduce: break; case 7: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 84 "a.y" { (yyvsp[(1) - (2)].sym)->type = LLAB; @@ -1684,8 +1715,7 @@ yyreduce: break; case 12: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 95 "a.y" { (yyvsp[(1) - (3)].sym)->type = LVAR; @@ -1694,8 +1724,7 @@ yyreduce: break; case 13: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 100 "a.y" { if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval)) @@ -1705,207 +1734,197 @@ yyreduce: break; case 14: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 105 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 15: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 106 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 16: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 107 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 17: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 108 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 18: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 109 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 19: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 110 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 20: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 111 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 21: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 112 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 22: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 113 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 23: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 114 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 24: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 115 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 25: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 116 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 26: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 117 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 27: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 118 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 28: +/* Line 1778 of yacc.c */ +#line 119 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; -/* Line 1455 of yacc.c */ -#line 121 "a.y" + case 29: +/* Line 1778 of yacc.c */ +#line 122 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 29: - -/* Line 1455 of yacc.c */ -#line 126 "a.y" + case 30: +/* Line 1778 of yacc.c */ +#line 127 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 30: - -/* Line 1455 of yacc.c */ -#line 133 "a.y" + case 31: +/* Line 1778 of yacc.c */ +#line 134 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 31: - -/* Line 1455 of yacc.c */ -#line 140 "a.y" + case 32: +/* Line 1778 of yacc.c */ +#line 141 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 32: - -/* Line 1455 of yacc.c */ -#line 147 "a.y" + case 33: +/* Line 1778 of yacc.c */ +#line 148 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 33: - -/* Line 1455 of yacc.c */ -#line 152 "a.y" + case 34: +/* Line 1778 of yacc.c */ +#line 153 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 34: - -/* Line 1455 of yacc.c */ -#line 159 "a.y" + case 35: +/* Line 1778 of yacc.c */ +#line 160 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 35: - -/* Line 1455 of yacc.c */ -#line 164 "a.y" + case 36: +/* Line 1778 of yacc.c */ +#line 165 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 36: - -/* Line 1455 of yacc.c */ -#line 171 "a.y" + case 37: +/* Line 1778 of yacc.c */ +#line 172 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 37: - -/* Line 1455 of yacc.c */ -#line 176 "a.y" + case 38: +/* Line 1778 of yacc.c */ +#line 177 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 38: + case 39: +/* Line 1778 of yacc.c */ +#line 182 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; -/* Line 1455 of yacc.c */ -#line 183 "a.y" + case 40: +/* Line 1778 of yacc.c */ +#line 189 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1913,20 +1932,18 @@ yyreduce: } break; - case 39: - -/* Line 1455 of yacc.c */ -#line 191 "a.y" + case 41: +/* Line 1778 of yacc.c */ +#line 197 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 40: - -/* Line 1455 of yacc.c */ -#line 196 "a.y" + case 42: +/* Line 1778 of yacc.c */ +#line 202 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1934,30 +1951,27 @@ yyreduce: } break; - case 41: - -/* Line 1455 of yacc.c */ -#line 204 "a.y" + case 43: +/* Line 1778 of yacc.c */ +#line 210 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 42: - -/* Line 1455 of yacc.c */ -#line 209 "a.y" + case 44: +/* Line 1778 of yacc.c */ +#line 215 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 43: - -/* Line 1455 of yacc.c */ -#line 214 "a.y" + case 45: +/* Line 1778 of yacc.c */ +#line 220 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); @@ -1966,20 +1980,18 @@ yyreduce: } break; - case 46: - -/* Line 1455 of yacc.c */ -#line 227 "a.y" + case 48: +/* Line 1778 of yacc.c */ +#line 233 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 47: - -/* Line 1455 of yacc.c */ -#line 232 "a.y" + case 49: +/* Line 1778 of yacc.c */ +#line 238 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1989,20 +2001,18 @@ yyreduce: } break; - case 48: - -/* Line 1455 of yacc.c */ -#line 242 "a.y" + case 50: +/* Line 1778 of yacc.c */ +#line 248 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 49: - -/* Line 1455 of yacc.c */ -#line 247 "a.y" + case 51: +/* Line 1778 of yacc.c */ +#line 253 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -2012,50 +2022,45 @@ yyreduce: } break; - case 50: - -/* Line 1455 of yacc.c */ -#line 257 "a.y" + case 52: +/* Line 1778 of yacc.c */ +#line 263 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 51: - -/* Line 1455 of yacc.c */ -#line 262 "a.y" + case 53: +/* Line 1778 of yacc.c */ +#line 268 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 52: - -/* Line 1455 of yacc.c */ -#line 267 "a.y" + case 54: +/* Line 1778 of yacc.c */ +#line 273 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 53: - -/* Line 1455 of yacc.c */ -#line 274 "a.y" + case 55: +/* Line 1778 of yacc.c */ +#line 280 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 54: - -/* Line 1455 of yacc.c */ -#line 279 "a.y" + case 56: +/* Line 1778 of yacc.c */ +#line 285 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -2063,28 +2068,35 @@ yyreduce: } break; - case 59: - -/* Line 1455 of yacc.c */ + case 57: +/* Line 1778 of yacc.c */ #line 293 "a.y" { - (yyval.gen) = (yyvsp[(2) - (2)].gen); + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).to = (yyvsp[(3) - (5)].gen); + (yyval.gen2).to.offset = (yyvsp[(5) - (5)].lval); } break; - case 60: - -/* Line 1455 of yacc.c */ -#line 297 "a.y" + case 62: +/* Line 1778 of yacc.c */ +#line 307 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 66: + case 63: +/* Line 1778 of yacc.c */ +#line 311 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + } + break; -/* Line 1455 of yacc.c */ -#line 310 "a.y" + case 69: +/* Line 1778 of yacc.c */ +#line 324 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2092,10 +2104,9 @@ yyreduce: } break; - case 67: - -/* Line 1455 of yacc.c */ -#line 316 "a.y" + case 70: +/* Line 1778 of yacc.c */ +#line 330 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2106,10 +2117,9 @@ yyreduce: } break; - case 68: - -/* Line 1455 of yacc.c */ -#line 325 "a.y" + case 71: +/* Line 1778 of yacc.c */ +#line 339 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2118,60 +2128,63 @@ yyreduce: } break; - case 69: - -/* Line 1455 of yacc.c */ -#line 334 "a.y" + case 72: +/* Line 1778 of yacc.c */ +#line 348 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 70: - -/* Line 1455 of yacc.c */ -#line 339 "a.y" + case 73: +/* Line 1778 of yacc.c */ +#line 353 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 71: - -/* Line 1455 of yacc.c */ -#line 344 "a.y" + case 74: +/* Line 1778 of yacc.c */ +#line 358 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 72: + case 75: +/* Line 1778 of yacc.c */ +#line 363 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; -/* Line 1455 of yacc.c */ -#line 349 "a.y" + case 76: +/* Line 1778 of yacc.c */ +#line 368 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SP; } break; - case 73: - -/* Line 1455 of yacc.c */ -#line 354 "a.y" + case 77: +/* Line 1778 of yacc.c */ +#line 373 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 74: - -/* Line 1455 of yacc.c */ -#line 361 "a.y" + case 78: +/* Line 1778 of yacc.c */ +#line 380 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2179,10 +2192,9 @@ yyreduce: } break; - case 75: - -/* Line 1455 of yacc.c */ -#line 367 "a.y" + case 79: +/* Line 1778 of yacc.c */ +#line 386 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).index = (yyvsp[(2) - (2)].gen).type; @@ -2195,10 +2207,9 @@ yyreduce: } break; - case 76: - -/* Line 1455 of yacc.c */ -#line 378 "a.y" + case 80: +/* Line 1778 of yacc.c */ +#line 397 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2206,10 +2217,9 @@ yyreduce: } break; - case 77: - -/* Line 1455 of yacc.c */ -#line 384 "a.y" + case 81: +/* Line 1778 of yacc.c */ +#line 403 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2217,10 +2227,9 @@ yyreduce: } break; - case 78: - -/* Line 1455 of yacc.c */ -#line 390 "a.y" + case 82: +/* Line 1778 of yacc.c */ +#line 409 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2228,10 +2237,9 @@ yyreduce: } break; - case 79: - -/* Line 1455 of yacc.c */ -#line 396 "a.y" + case 83: +/* Line 1778 of yacc.c */ +#line 415 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2239,10 +2247,9 @@ yyreduce: } break; - case 80: - -/* Line 1455 of yacc.c */ -#line 402 "a.y" + case 84: +/* Line 1778 of yacc.c */ +#line 421 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2250,10 +2257,9 @@ yyreduce: } break; - case 81: - -/* Line 1455 of yacc.c */ -#line 410 "a.y" + case 85: +/* Line 1778 of yacc.c */ +#line 429 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST2; @@ -2262,50 +2268,45 @@ yyreduce: } break; - case 82: - -/* Line 1455 of yacc.c */ -#line 419 "a.y" + case 86: +/* Line 1778 of yacc.c */ +#line 438 "a.y" { (yyval.con2).v1 = (yyvsp[(1) - (1)].lval); (yyval.con2).v2 = 0; } break; - case 83: - -/* Line 1455 of yacc.c */ -#line 424 "a.y" + case 87: +/* Line 1778 of yacc.c */ +#line 443 "a.y" { (yyval.con2).v1 = -(yyvsp[(2) - (2)].lval); (yyval.con2).v2 = 0; } break; - case 84: - -/* Line 1455 of yacc.c */ -#line 429 "a.y" + case 88: +/* Line 1778 of yacc.c */ +#line 448 "a.y" { (yyval.con2).v1 = (yyvsp[(1) - (3)].lval); (yyval.con2).v2 = (yyvsp[(3) - (3)].lval); } break; - case 85: - -/* Line 1455 of yacc.c */ -#line 434 "a.y" + case 89: +/* Line 1778 of yacc.c */ +#line 453 "a.y" { (yyval.con2).v1 = -(yyvsp[(2) - (4)].lval); (yyval.con2).v2 = (yyvsp[(4) - (4)].lval); } break; - case 88: - -/* Line 1455 of yacc.c */ -#line 445 "a.y" + case 92: +/* Line 1778 of yacc.c */ +#line 464 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2313,10 +2314,9 @@ yyreduce: } break; - case 89: - -/* Line 1455 of yacc.c */ -#line 451 "a.y" + case 93: +/* Line 1778 of yacc.c */ +#line 470 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2324,10 +2324,9 @@ yyreduce: } break; - case 90: - -/* Line 1455 of yacc.c */ -#line 457 "a.y" + case 94: +/* Line 1778 of yacc.c */ +#line 476 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; @@ -2335,10 +2334,9 @@ yyreduce: } break; - case 91: - -/* Line 1455 of yacc.c */ -#line 463 "a.y" + case 95: +/* Line 1778 of yacc.c */ +#line 482 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2349,10 +2347,9 @@ yyreduce: } break; - case 92: - -/* Line 1455 of yacc.c */ -#line 472 "a.y" + case 96: +/* Line 1778 of yacc.c */ +#line 491 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); @@ -2363,30 +2360,27 @@ yyreduce: } break; - case 93: - -/* Line 1455 of yacc.c */ -#line 481 "a.y" + case 97: +/* Line 1778 of yacc.c */ +#line 500 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval); } break; - case 94: - -/* Line 1455 of yacc.c */ -#line 486 "a.y" + case 98: +/* Line 1778 of yacc.c */ +#line 505 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; } break; - case 95: - -/* Line 1455 of yacc.c */ -#line 491 "a.y" + case 99: +/* Line 1778 of yacc.c */ +#line 510 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2394,10 +2388,9 @@ yyreduce: } break; - case 96: - -/* Line 1455 of yacc.c */ -#line 497 "a.y" + case 100: +/* Line 1778 of yacc.c */ +#line 516 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2407,10 +2400,9 @@ yyreduce: } break; - case 97: - -/* Line 1455 of yacc.c */ -#line 505 "a.y" + case 101: +/* Line 1778 of yacc.c */ +#line 524 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval); @@ -2420,19 +2412,17 @@ yyreduce: } break; - case 98: - -/* Line 1455 of yacc.c */ -#line 515 "a.y" + case 102: +/* Line 1778 of yacc.c */ +#line 534 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); } break; - case 99: - -/* Line 1455 of yacc.c */ -#line 519 "a.y" + case 103: +/* Line 1778 of yacc.c */ +#line 538 "a.y" { (yyval.gen) = (yyvsp[(1) - (6)].gen); (yyval.gen).index = (yyvsp[(3) - (6)].lval); @@ -2441,10 +2431,9 @@ yyreduce: } break; - case 100: - -/* Line 1455 of yacc.c */ -#line 528 "a.y" + case 104: +/* Line 1778 of yacc.c */ +#line 547 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(4) - (5)].lval); @@ -2453,10 +2442,9 @@ yyreduce: } break; - case 101: - -/* Line 1455 of yacc.c */ -#line 535 "a.y" + case 105: +/* Line 1778 of yacc.c */ +#line 554 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_STATIC; @@ -2465,183 +2453,174 @@ yyreduce: } break; - case 102: - -/* Line 1455 of yacc.c */ -#line 543 "a.y" + case 106: +/* Line 1778 of yacc.c */ +#line 562 "a.y" { (yyval.lval) = 0; } break; - case 103: - -/* Line 1455 of yacc.c */ -#line 547 "a.y" + case 107: +/* Line 1778 of yacc.c */ +#line 566 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 104: - -/* Line 1455 of yacc.c */ -#line 551 "a.y" + case 108: +/* Line 1778 of yacc.c */ +#line 570 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 106: - -/* Line 1455 of yacc.c */ -#line 558 "a.y" + case 110: +/* Line 1778 of yacc.c */ +#line 577 "a.y" { (yyval.lval) = D_AUTO; } break; - case 109: - -/* Line 1455 of yacc.c */ -#line 566 "a.y" + case 113: +/* Line 1778 of yacc.c */ +#line 585 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 110: - -/* Line 1455 of yacc.c */ -#line 570 "a.y" + case 114: +/* Line 1778 of yacc.c */ +#line 589 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 111: - -/* Line 1455 of yacc.c */ -#line 574 "a.y" + case 115: +/* Line 1778 of yacc.c */ +#line 593 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 112: - -/* Line 1455 of yacc.c */ -#line 578 "a.y" + case 116: +/* Line 1778 of yacc.c */ +#line 597 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 113: - -/* Line 1455 of yacc.c */ -#line 582 "a.y" + case 117: +/* Line 1778 of yacc.c */ +#line 601 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 115: - -/* Line 1455 of yacc.c */ -#line 589 "a.y" + case 119: +/* Line 1778 of yacc.c */ +#line 608 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 116: - -/* Line 1455 of yacc.c */ -#line 593 "a.y" + case 120: +/* Line 1778 of yacc.c */ +#line 612 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 117: - -/* Line 1455 of yacc.c */ -#line 597 "a.y" + case 121: +/* Line 1778 of yacc.c */ +#line 616 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 118: - -/* Line 1455 of yacc.c */ -#line 601 "a.y" + case 122: +/* Line 1778 of yacc.c */ +#line 620 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 119: - -/* Line 1455 of yacc.c */ -#line 605 "a.y" + case 123: +/* Line 1778 of yacc.c */ +#line 624 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 120: - -/* Line 1455 of yacc.c */ -#line 609 "a.y" + case 124: +/* Line 1778 of yacc.c */ +#line 628 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 121: - -/* Line 1455 of yacc.c */ -#line 613 "a.y" + case 125: +/* Line 1778 of yacc.c */ +#line 632 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 122: - -/* Line 1455 of yacc.c */ -#line 617 "a.y" + case 126: +/* Line 1778 of yacc.c */ +#line 636 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 123: - -/* Line 1455 of yacc.c */ -#line 621 "a.y" + case 127: +/* Line 1778 of yacc.c */ +#line 640 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 124: - -/* Line 1455 of yacc.c */ -#line 625 "a.y" + case 128: +/* Line 1778 of yacc.c */ +#line 644 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } break; - -/* Line 1455 of yacc.c */ -#line 2643 "y.tab.c" +/* Line 1778 of yacc.c */ +#line 2611 "y.tab.c" default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -2669,6 +2648,10 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -2676,37 +2659,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -2765,7 +2747,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) + if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -2788,7 +2770,9 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ @@ -2812,7 +2796,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -2824,8 +2808,13 @@ yyexhaustedlab: yyreturn: if (yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); @@ -2849,4 +2838,3 @@ yyreturn: } - diff --git a/src/cmd/8a/y.tab.h b/src/cmd/8a/y.tab.h index 69a966a4b..621aba79f 100644 --- a/src/cmd/8a/y.tab.h +++ b/src/cmd/8a/y.tab.h @@ -1,10 +1,8 @@ +/* A Bison parser, made by GNU Bison 2.6.5. */ -/* A Bison parser, made by GNU Bison 2.4.1. */ - -/* Skeleton interface for Bison's Yacc-like parsers in C +/* Bison interface for Yacc-like parsers in C - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,6 +30,15 @@ This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif /* Tokens. */ #ifndef YYTOKENTYPE @@ -53,20 +60,22 @@ LTYPEM = 269, LTYPEI = 270, LTYPEG = 271, - LCONST = 272, - LFP = 273, - LPC = 274, - LSB = 275, - LBREG = 276, - LLREG = 277, - LSREG = 278, - LFREG = 279, - LFCONST = 280, - LSCONST = 281, - LSP = 282, - LNAME = 283, - LLAB = 284, - LVAR = 285 + LTYPEXC = 272, + LCONST = 273, + LFP = 274, + LPC = 275, + LSB = 276, + LBREG = 277, + LLREG = 278, + LSREG = 279, + LFREG = 280, + LXREG = 281, + LFCONST = 282, + LSCONST = 283, + LSP = 284, + LNAME = 285, + LLAB = 286, + LVAR = 287 }; #endif /* Tokens. */ @@ -84,29 +93,29 @@ #define LTYPEM 269 #define LTYPEI 270 #define LTYPEG 271 -#define LCONST 272 -#define LFP 273 -#define LPC 274 -#define LSB 275 -#define LBREG 276 -#define LLREG 277 -#define LSREG 278 -#define LFREG 279 -#define LFCONST 280 -#define LSCONST 281 -#define LSP 282 -#define LNAME 283 -#define LLAB 284 -#define LVAR 285 - +#define LTYPEXC 272 +#define LCONST 273 +#define LFP 274 +#define LPC 275 +#define LSB 276 +#define LBREG 277 +#define LLREG 278 +#define LSREG 279 +#define LFREG 280 +#define LXREG 281 +#define LFCONST 282 +#define LSCONST 283 +#define LSP 284 +#define LNAME 285 +#define LLAB 286 +#define LVAR 287 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { - -/* Line 1676 of yacc.c */ +/* Line 2042 of yacc.c */ #line 37 "a.y" Sym *sym; @@ -121,9 +130,8 @@ typedef union YYSTYPE Gen2 gen2; - -/* Line 1676 of yacc.c */ -#line 127 "y.tab.h" +/* Line 2042 of yacc.c */ +#line 135 "y.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ @@ -132,4 +140,18 @@ typedef union YYSTYPE extern YYSTYPE yylval; +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c index 869d31ace..78eb7eced 100644 --- a/src/cmd/8c/cgen.c +++ b/src/cmd/8c/cgen.c @@ -277,6 +277,18 @@ cgen(Node *n, Node *nn) break; } } + if(n->op == OOR && l->op == OASHL && r->op == OLSHR + && l->right->op == OCONST && r->right->op == OCONST + && l->left->op == ONAME && r->left->op == ONAME + && l->left->sym == r->left->sym + && l->right->vconst + r->right->vconst == 8 * l->left->type->width) { + regalloc(&nod, l->left, nn); + cgen(l->left, &nod); + gopcode(OROTL, n->type, l->right, &nod); + gmove(&nod, nn); + regfree(&nod); + break; + } if(n->op == OADD && l->op == OASHL && l->right->op == OCONST && (r->op != OCONST || r->vconst < -128 || r->vconst > 127)) { c = l->right->vconst; @@ -1703,6 +1715,7 @@ copy: } } + v = w == 8; if(n->complex >= FNX && nn != nil && nn->complex >= FNX) { t = nn->type; nn->type = types[TLONG]; @@ -1728,8 +1741,28 @@ copy: } x = 0; - v = w == 8; if(v) { + if(nn != nil && nn->complex >= FNX) { + t = nn->type; + nn->type = types[TLONG]; + regialloc(&nod2, nn, Z); + lcgen(nn, &nod2); + nn->type = t; + + nod2.type = typ(TIND, t); + + nod1 = nod2; + nod1.op = OIND; + nod1.left = &nod2; + nod1.right = Z; + nod1.complex = 1; + nod1.type = t; + + sugen(n, &nod1, w); + regfree(&nod2); + return; + } + c = cursafe; if(n->left != Z && n->left->complex >= FNX && n->right != Z && n->right->complex >= FNX) { diff --git a/src/cmd/8c/doc.go b/src/cmd/8c/doc.go index e3aae857f..0d07db14d 100644 --- a/src/cmd/8c/doc.go +++ b/src/cmd/8c/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 8c is a version of the Plan 9 C compiler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2c + http://plan9.bell-labs.com/magic/man2html/1/8c Its target architecture is the x86, referred to by these tools for historical reasons as 386. */ -package documentation +package main diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h index 4a57f5d3c..bdf981b4c 100644 --- a/src/cmd/8c/gc.h +++ b/src/cmd/8c/gc.h @@ -297,6 +297,7 @@ void gbranch(int); void patch(Prog*, int32); int sconst(Node*); void gpseudo(int, Sym*, Node*); +void gprefetch(Node*); /* * swt.c diff --git a/src/cmd/8c/list.c b/src/cmd/8c/list.c index c422905cd..8506e08ef 100644 --- a/src/cmd/8c/list.c +++ b/src/cmd/8c/list.c @@ -93,7 +93,7 @@ Pconv(Fmt *fp) break; default: - sprint(str, "(%L) %A %D,%lD", + sprint(str, "(%L) %A %D,%D", p->lineno, p->as, &p->from, &p->to); break; } @@ -118,6 +118,17 @@ Dconv(Fmt *fp) a = va_arg(fp->args, Adr*); i = a->type; + + if(fp->flags & FmtLong) { + if(i == D_CONST2) + sprint(str, "$%d-%d", a->offset, a->offset2); + else { + // ATEXT dst is not constant + sprint(str, "!!%D", a); + } + goto brk; + } + if(i >= D_INDIR) { if(a->offset) sprint(str, "%d(%R)", a->offset, i-D_INDIR); @@ -126,7 +137,6 @@ Dconv(Fmt *fp) goto brk; } switch(i) { - default: if(a->offset) sprint(str, "$%d,%R", a->offset, i); @@ -139,7 +149,7 @@ Dconv(Fmt *fp) break; case D_BRANCH: - sprint(str, "%d(PC)", a->offset-pc); + sprint(str, "%d", a->offset); break; case D_EXTERN: @@ -147,12 +157,14 @@ Dconv(Fmt *fp) break; case D_STATIC: - sprint(str, "%s<>+%d(SB)", a->sym->name, - a->offset); + sprint(str, "%s<>+%d(SB)", a->sym->name, a->offset); break; case D_AUTO: - sprint(str, "%s+%d(SP)", a->sym->name, a->offset); + if(a->sym) + sprint(str, "%s+%d(SP)", a->sym->name, a->offset); + else + sprint(str, "%d(SP)", a->offset); break; case D_PARAM: @@ -167,7 +179,10 @@ Dconv(Fmt *fp) break; case D_CONST2: - sprint(str, "$%d-%d", a->offset, a->offset2); + if(!(fp->flags & FmtLong)) { + // D_CONST2 outside of ATEXT should not happen + sprint(str, "!!$%d-%d", a->offset, a->offset2); + } break; case D_FCONST: @@ -197,7 +212,7 @@ conv: char* regstr[] = { - "AL", /*[D_AL]*/ + "AL", /* [D_AL] */ "CL", "DL", "BL", @@ -206,7 +221,7 @@ char* regstr[] = "DH", "BH", - "AX", /*[D_AX]*/ + "AX", /* [D_AX] */ "CX", "DX", "BX", @@ -215,7 +230,7 @@ char* regstr[] = "SI", "DI", - "F0", /*[D_F0]*/ + "F0", /* [D_F0] */ "F1", "F2", "F3", @@ -224,20 +239,20 @@ char* regstr[] = "F6", "F7", - "CS", /*[D_CS]*/ + "CS", /* [D_CS] */ "SS", "DS", "ES", "FS", "GS", - "GDTR", /*[D_GDTR]*/ - "IDTR", /*[D_IDTR]*/ - "LDTR", /*[D_LDTR]*/ - "MSW", /*[D_MSW] */ - "TASK", /*[D_TASK]*/ + "GDTR", /* [D_GDTR] */ + "IDTR", /* [D_IDTR] */ + "LDTR", /* [D_LDTR] */ + "MSW", /* [D_MSW] */ + "TASK", /* [D_TASK] */ - "CR0", /*[D_CR]*/ + "CR0", /* [D_CR] */ "CR1", "CR2", "CR3", @@ -246,7 +261,7 @@ char* regstr[] = "CR6", "CR7", - "DR0", /*[D_DR]*/ + "DR0", /* [D_DR] */ "DR1", "DR2", "DR3", @@ -255,7 +270,7 @@ char* regstr[] = "DR6", "DR7", - "TR0", /*[D_TR]*/ + "TR0", /* [D_TR] */ "TR1", "TR2", "TR3", @@ -264,7 +279,16 @@ char* regstr[] = "TR6", "TR7", - "NONE", /*[D_NONE]*/ + "X0", /* [D_X0] */ + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", + + "NONE", /* [D_NONE] */ }; int diff --git a/src/cmd/8c/mul.c b/src/cmd/8c/mul.c index a0742807e..9955e762f 100644 --- a/src/cmd/8c/mul.c +++ b/src/cmd/8c/mul.c @@ -35,17 +35,17 @@ typedef struct Mparam Mparam; struct Malg { - char vals[10]; + schar vals[10]; }; struct Mparam { uint32 value; - char alg; + schar alg; char neg; char shift; char arg; - char off; + schar off; }; static Mparam multab[32]; @@ -101,7 +101,7 @@ mulparam(uint32 m, Mparam *mp) { int c, i, j, n, o, q, s; int bc, bi, bn, bo, bq, bs, bt; - char *p; + schar *p; int32 u; uint32 t; diff --git a/src/cmd/8c/peep.c b/src/cmd/8c/peep.c index 9511a5579..9c3e9a5af 100644 --- a/src/cmd/8c/peep.c +++ b/src/cmd/8c/peep.c @@ -602,6 +602,12 @@ copyu(Prog *p, Adr *v, Adr *s) case ACMPL: /* read only */ case ACMPW: case ACMPB: + + case APREFETCHT0: + case APREFETCHT1: + case APREFETCHT2: + case APREFETCHNTA: + case AFCOMB: case AFCOMBP: diff --git a/src/cmd/8c/reg.c b/src/cmd/8c/reg.c index 6ba07bed2..6c87d70a5 100644 --- a/src/cmd/8c/reg.c +++ b/src/cmd/8c/reg.c @@ -30,6 +30,8 @@ #include "gc.h" +static void fixjmp(Reg*); + Reg* rega(void) { @@ -148,7 +150,6 @@ regopt(Prog *p) r->p1 = R; r1->s1 = R; } - bit = mkvar(r, &p->from); if(bany(&bit)) switch(p->as) { @@ -182,6 +183,10 @@ regopt(Prog *p) case ACMPB: case ACMPL: case ACMPW: + case APREFETCHT0: + case APREFETCHT1: + case APREFETCHT2: + case APREFETCHNTA: for(z=0; zuse2.b[z] |= bit.b[z]; break; @@ -371,6 +376,12 @@ regopt(Prog *p) print("\n%L %D\n", p->lineno, &p->from); } + /* + * pass 2.1 + * fix jumps + */ + fixjmp(firstr); + /* * pass 2.5 * find looping structure @@ -543,6 +554,13 @@ brk: if(!debug['R'] || debug['P']) peep(); + if(debug['R'] && debug['v']) { + print("after pass 7 (peep)\n"); + for(r=firstr; r; r=r->link) + print("%04d %P\n", r->pc, r->prog); + print("\n"); + } + /* * pass 8 * recalculate pc @@ -596,6 +614,14 @@ brk: while(p->link && p->link->as == ANOP) p->link = p->link->link; } + + if(debug['R'] && debug['v']) { + print("after pass 8 (fixup pc)\n"); + for(p1=firstr->prog; p1!=P; p1=p1->link) + print("%P\n", p1); + print("\n"); + } + if(r1 != R) { r1->link = freer; freer = firstr; @@ -1285,3 +1311,126 @@ BtoR(int32 b) return 0; return bitno(b) + D_AX; } + +/* what instruction does a JMP to p eventually land on? */ +static Reg* +chasejmp(Reg *r, int *jmploop) +{ + int n; + + n = 0; + for(; r; r=r->s2) { + if(r->prog->as != AJMP || r->prog->to.type != D_BRANCH) + break; + if(++n > 10) { + *jmploop = 1; + break; + } + } + return r; +} + +/* mark all code reachable from firstp as alive */ +static void +mark(Reg *firstr) +{ + Reg *r; + Prog *p; + + for(r=firstr; r; r=r->link) { + if(r->active) + break; + r->active = 1; + p = r->prog; + if(p->as != ACALL && p->to.type == D_BRANCH) + mark(r->s2); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) + break; + } +} + +/* + * the code generator depends on being able to write out JMP + * instructions that it can jump to now but fill in later. + * the linker will resolve them nicely, but they make the code + * longer and more difficult to follow during debugging. + * remove them. + */ +static void +fixjmp(Reg *firstr) +{ + int jmploop; + Reg *r; + Prog *p; + + if(debug['R'] && debug['v']) + print("\nfixjmp\n"); + + // pass 1: resolve jump to AJMP, mark all code as dead. + jmploop = 0; + for(r=firstr; r; r=r->link) { + p = r->prog; + if(debug['R'] && debug['v']) + print("%04d %P\n", r->pc, p); + if(p->as != ACALL && p->to.type == D_BRANCH && r->s2 && r->s2->prog->as == AJMP) { + r->s2 = chasejmp(r->s2, &jmploop); + p->to.offset = r->s2->pc; + if(debug['R'] && debug['v']) + print("->%P\n", p); + } + r->active = 0; + } + if(debug['R'] && debug['v']) + print("\n"); + + // pass 2: mark all reachable code alive + mark(firstr); + + // pass 3: delete dead code (mostly JMPs). + for(r=firstr; r; r=r->link) { + if(!r->active) { + p = r->prog; + if(p->link == P && p->as == ARET && r->p1 && r->p1->prog->as != ARET) { + // This is the final ARET, and the code so far doesn't have one. + // Let it stay. + } else { + if(debug['R'] && debug['v']) + print("del %04d %P\n", r->pc, p); + p->as = ANOP; + } + } + } + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + if(!jmploop) { + for(r=firstr; r; r=r->link) { + p = r->prog; + if(p->as == AJMP && p->to.type == D_BRANCH && r->s2 == r->link) { + if(debug['R'] && debug['v']) + print("del %04d %P\n", r->pc, p); + p->as = ANOP; + } + } + } + + // fix back pointers. + for(r=firstr; r; r=r->link) { + r->p2 = R; + r->p2link = R; + } + for(r=firstr; r; r=r->link) { + if(r->s2) { + r->p2link = r->s2->p2; + r->s2->p2 = r; + } + } + + if(debug['R'] && debug['v']) { + print("\n"); + for(r=firstr; r; r=r->link) + print("%04d %P\n", r->pc, r->prog); + print("\n"); + } +} + diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index f1ca4c25f..d331eee1a 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -251,18 +251,12 @@ outcode(void) Binit(&b, f, OWRITE); Bprint(&b, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); - if(ndynimp > 0 || ndynexp > 0) { - int i; - + if(pragcgobuf.to > pragcgobuf.start) { Bprint(&b, "\n"); Bprint(&b, "$$ // exports\n\n"); Bprint(&b, "$$ // local types\n\n"); - Bprint(&b, "$$ // dynimport\n"); - for(i=0; ilink) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -404,6 +424,11 @@ outhist(Biobuf *b) Bputc(b, pg.lineno>>24); zaddr(b, &pg.from, 0); zaddr(b, &pg.to, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } @@ -597,8 +622,8 @@ align(int32 i, Type *t, int op, int32 *maxalign) int32 maxround(int32 max, int32 v) { - v += SZ_LONG-1; + v = xround(v, SZ_LONG); if(v > max) - max = xround(v, SZ_LONG); + return v; return max; } diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c index 3a08da7cd..1b7617bc5 100644 --- a/src/cmd/8c/txt.c +++ b/src/cmd/8c/txt.c @@ -1253,6 +1253,14 @@ gopcode(int o, Type *ty, Node *f, Node *t) a = ASALW; break; + case OROTL: + a = AROLL; + if(et == TCHAR || et == TUCHAR) + a = AROLB; + if(et == TSHORT || et == TUSHORT) + a = AROLW; + break; + case OFUNC: a = ACALL; break; @@ -1383,6 +1391,21 @@ gpseudo(int a, Sym *s, Node *n) pc--; } +void +gprefetch(Node *n) +{ + Node n1; + + if(strcmp(getgo386(), "sse2") != 0) // assume no prefetch on old machines + return; + + regalloc(&n1, n, Z); + gmove(n, &n1); + n1.op = OINDREG; + gins(APREFETCHNTA, &n1, Z); + regfree(&n1); +} + int sconst(Node *n) { diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index 48619ac73..0b2f2b76e 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -49,7 +49,7 @@ mfree(Node *n) void cgen(Node *n, Node *res) { - Node *nl, *nr, *r, n1, n2, nt, f0, f1; + Node *nl, *nr, *r, n1, n2, nt; Prog *p1, *p2, *p3; int a; @@ -63,9 +63,26 @@ cgen(Node *n, Node *res) if(res == N || res->type == T) fatal("cgen: res nil"); - // inline slices - if(cgen_inline(n, res)) + switch(n->op) { + case OSLICE: + case OSLICEARR: + case OSLICESTR: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_slice(n, &n1); + cgen(&n1, res); + } else + cgen_slice(n, res); + return; + case OEFACE: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_eface(n, &n1); + cgen(&n1, res); + } else + cgen_eface(n, res); return; + } while(n->op == OCONVNOP) n = n->left; @@ -160,6 +177,7 @@ cgen(Node *n, Node *res) case OADD: case OSUB: case OMUL: + case OLROT: case OLSH: case ORSH: case OAND: @@ -170,8 +188,10 @@ cgen(Node *n, Node *res) } } - if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype]) - goto flt; + if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype]) { + cgen_float(n, res); + return; + } switch(n->op) { default: @@ -195,12 +215,12 @@ cgen(Node *n, Node *res) case OGE: case OGT: case ONOT: - p1 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); p2 = pc; gmove(nodbool(1), res); - p3 = gbranch(AJMP, T); + p3 = gbranch(AJMP, T, 0); patch(p1, pc); - bgen(n, 1, p2); + bgen(n, 1, 0, p2); gmove(nodbool(0), res); patch(p3, pc); return; @@ -232,6 +252,10 @@ cgen(Node *n, Node *res) a = optoas(n->op, nl->type); goto abop; + case OHMUL: + cgen_hmul(nl, nr, res); + break; + case OCONV: if(eqtype(n->type, nl->type) || noconv(n->type, nl->type)) { cgen(nl, res); @@ -274,7 +298,7 @@ cgen(Node *n, Node *res) nodconst(&n2, types[tptr], 0); gins(optoas(OCMP, types[tptr]), &n1, &n2); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + p1 = gbranch(optoas(OEQ, types[tptr]), T, -1); n2 = n1; n2.op = OINDREG; @@ -308,7 +332,7 @@ cgen(Node *n, Node *res) nodconst(&n2, types[tptr], 0); gins(optoas(OCMP, types[tptr]), &n1, &n2); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + p1 = gbranch(optoas(OEQ, types[tptr]), T, -1); n2 = n1; n2.op = OINDREG; @@ -324,9 +348,8 @@ cgen(Node *n, Node *res) } if(isslice(nl->type)) { igen(nl, &n1, res); - n1.op = OINDREG; n1.type = types[TUINT32]; - n1.xoffset = Array_cap; + n1.xoffset += Array_cap; gmove(&n1, res); regfree(&n1); break; @@ -360,20 +383,29 @@ cgen(Node *n, Node *res) case OLSH: case ORSH: - cgen_shift(n->op, nl, nr, res); + case OLROT: + cgen_shift(n->op, n->bounded, nl, nr, res); break; } return; sbop: // symmetric binary - if(nl->ullman < nr->ullman) { + if(nl->ullman < nr->ullman || nl->op == OLITERAL) { r = nl; nl = nr; nr = r; } abop: // asymmetric binary - if(nl->ullman >= nr->ullman) { + if(smallintconst(nr)) { + mgen(nl, &n1, res); + regalloc(&n2, nl->type, &n1); + gmove(&n1, &n2); + gins(a, nr, &n2); + gmove(&n2, res); + regfree(&n2); + mfree(&n1); + } else if(nl->ullman >= nr->ullman) { tempname(&nt, nl->type); cgen(nl, &nt); mgen(nr, &n2, N); @@ -401,69 +433,42 @@ uop: // unary gins(a, N, &n1); gmove(&n1, res); return; - -flt: // floating-point. 387 (not SSE2) to interoperate with 8c - nodreg(&f0, nl->type, D_F0); - nodreg(&f1, n->type, D_F0+1); - if(nr != N) - goto flt2; - - // unary - cgen(nl, &f0); - if(n->op != OCONV && n->op != OPLUS) - gins(foptoas(n->op, n->type, 0), N, N); - gmove(&f0, res); - return; - -flt2: // binary - if(nl->ullman >= nr->ullman) { - cgen(nl, &f0); - if(nr->addable) - gins(foptoas(n->op, n->type, 0), nr, &f0); - else { - cgen(nr, &f0); - gins(foptoas(n->op, n->type, Fpop), &f0, &f1); - } - } else { - cgen(nr, &f0); - if(nl->addable) - gins(foptoas(n->op, n->type, Frev), nl, &f0); - else { - cgen(nl, &f0); - gins(foptoas(n->op, n->type, Frev|Fpop), &f0, &f1); - } - } - gmove(&f0, res); - return; } /* - * generate array index into res. - * n might be any size; res is 32-bit. + * generate an addressable node in res, containing the value of n. + * n is an array index, and might be any size; res width is <= 32-bit. * returns Prog* to patch to panic call. */ -Prog* -cgenindex(Node *n, Node *res) +static Prog* +igenindex(Node *n, Node *res, int bounded) { Node tmp, lo, hi, zero; if(!is64(n->type)) { - cgen(n, res); + if(n->addable) { + // nothing to do. + *res = *n; + } else { + tempname(res, types[TUINT32]); + cgen(n, res); + } return nil; } tempname(&tmp, types[TINT64]); cgen(n, &tmp); split64(&tmp, &lo, &hi); + tempname(res, types[TUINT32]); gmove(&lo, res); - if(debug['B']) { + if(bounded) { splitclean(); return nil; } nodconst(&zero, types[TINT32], 0); gins(ACMPL, &hi, &zero); splitclean(); - return gbranch(AJNE, T); + return gbranch(AJNE, T, +1); } /* @@ -474,11 +479,12 @@ void agen(Node *n, Node *res) { Node *nl, *nr; - Node n1, n2, n3, n4, tmp; + Node n1, n2, n3, n4, tmp, nlen; Type *t; uint32 w; uint64 v; Prog *p1, *p2; + int bounded; if(debug['g']) { dump("\nagen-res", res); @@ -490,6 +496,20 @@ agen(Node *n, Node *res) while(n->op == OCONVNOP) n = n->left; + if(isconst(n, CTNIL) && n->type->width > widthptr) { + // Use of a nil interface or nil slice. + // Create a temporary we can take the address of and read. + // The generated code is just going to panic, so it need not + // be terribly efficient. See issue 3670. + tempname(&n1, n->type); + clearfat(&n1); + regalloc(&n2, types[tptr], res); + gins(ALEAL, &n1, &n2); + gmove(&n2, res); + regfree(&n2); + return; + } + // addressable var is easy if(n->addable) { if(n->op == OREGISTER) @@ -524,113 +544,136 @@ agen(Node *n, Node *res) cgen_aret(n, res); break; + case OSLICE: + case OSLICEARR: + case OSLICESTR: + tempname(&n1, n->type); + cgen_slice(n, &n1); + agen(&n1, res); + break; + + case OEFACE: + tempname(&n1, n->type); + cgen_eface(n, &n1); + agen(&n1, res); + break; + case OINDEX: p2 = nil; // to be patched to panicindex. w = n->type->width; + bounded = debug['B'] || n->bounded; if(nr->addable) { - if(!isconst(nr, CTINT)) - tempname(&tmp, types[TINT32]); + // Generate &nl first, and move nr into register. if(!isconst(nl, CTSTR)) - agenr(nl, &n3, res); + igen(nl, &n3, res); if(!isconst(nr, CTINT)) { - p2 = cgenindex(nr, &tmp); + p2 = igenindex(nr, &tmp, bounded); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } } else if(nl->addable) { + // Generate nr first, and move &nl into register. if(!isconst(nr, CTINT)) { - tempname(&tmp, types[TINT32]); - p2 = cgenindex(nr, &tmp); + p2 = igenindex(nr, &tmp, bounded); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } - if(!isconst(nl, CTSTR)) { - regalloc(&n3, types[tptr], res); - agen(nl, &n3); - } + if(!isconst(nl, CTSTR)) + igen(nl, &n3, res); } else { - tempname(&tmp, types[TINT32]); - p2 = cgenindex(nr, &tmp); + p2 = igenindex(nr, &tmp, bounded); nr = &tmp; if(!isconst(nl, CTSTR)) - agenr(nl, &n3, res); + igen(nl, &n3, res); regalloc(&n1, tmp.type, N); gins(optoas(OAS, tmp.type), &tmp, &n1); } - // &a is in &n3 (allocated in res) - // i is in &n1 (if not constant) + // For fixed array we really want the pointer in n3. + if(isfixedarray(nl->type)) { + regalloc(&n2, types[tptr], &n3); + agen(&n3, &n2); + regfree(&n3); + n3 = n2; + } + + // &a[0] is in n3 (allocated in res) + // i is in n1 (if not constant) + // len(a) is in nlen (if needed) // w is width // explicit check for nil if array is large enough // that we might derive too big a pointer. if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { - regalloc(&n4, types[tptr], &n3); - gmove(&n3, &n4); + n4 = n3; n4.op = OINDREG; n4.type = types[TUINT8]; n4.xoffset = 0; gins(ATESTB, nodintconst(0), &n4); - regfree(&n4); } // constant index if(isconst(nr, CTINT)) { if(isconst(nl, CTSTR)) - fatal("constant string constant index"); + fatal("constant string constant index"); // front end should handle v = mpgetfix(nr->val.u.xval); if(isslice(nl->type) || nl->type->etype == TSTRING) { - if(!debug['B'] && !n->etype) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_nel; + if(!debug['B'] && !n->bounded) { + nlen = n3; + nlen.type = types[TUINT32]; + nlen.xoffset += Array_nel; nodconst(&n2, types[TUINT32], v); - gins(optoas(OCMP, types[TUINT32]), &n1, &n2); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); - ginscall(panicindex, 0); + gins(optoas(OCMP, types[TUINT32]), &nlen, &n2); + p1 = gbranch(optoas(OGT, types[TUINT32]), T, +1); + ginscall(panicindex, -1); patch(p1, pc); } - - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_array; - gmove(&n1, &n3); } + // Load base pointer in n2 = n3. + regalloc(&n2, types[tptr], &n3); + n3.type = types[tptr]; + n3.xoffset += Array_array; + gmove(&n3, &n2); + regfree(&n3); if (v*w != 0) { - nodconst(&n2, types[tptr], v*w); - gins(optoas(OADD, types[tptr]), &n2, &n3); + nodconst(&n1, types[tptr], v*w); + gins(optoas(OADD, types[tptr]), &n1, &n2); } - gmove(&n3, res); - regfree(&n3); + gmove(&n2, res); + regfree(&n2); break; } - regalloc(&n2, types[TINT32], &n1); // i + // i is in register n1, extend to 32 bits. + t = types[TUINT32]; + if(issigned[n1.type->etype]) + t = types[TINT32]; + + regalloc(&n2, t, &n1); // i gmove(&n1, &n2); regfree(&n1); - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { // check bounds - if(isconst(nl, CTSTR)) - nodconst(&n1, types[TUINT32], nl->val.u.sval->len); - else if(isslice(nl->type) || nl->type->etype == TSTRING) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_nel; - } else - nodconst(&n1, types[TUINT32], nl->type->bound); - gins(optoas(OCMP, types[TUINT32]), &n2, &n1); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + t = types[TUINT32]; + if(isconst(nl, CTSTR)) { + nodconst(&nlen, t, nl->val.u.sval->len); + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { + nlen = n3; + nlen.type = t; + nlen.xoffset += Array_nel; + } else { + nodconst(&nlen, t, nl->type->bound); + } + gins(optoas(OCMP, t), &n2, &nlen); + p1 = gbranch(optoas(OLT, t), T, +1); if(p2) patch(p2, pc); - ginscall(panicindex, 0); + ginscall(panicindex, -1); patch(p1, pc); } - + if(isconst(nl, CTSTR)) { regalloc(&n3, types[tptr], res); p1 = gins(ALEAL, N, &n3); @@ -640,24 +683,27 @@ agen(Node *n, Node *res) goto indexdone; } + // Load base pointer in n3. + regalloc(&tmp, types[tptr], &n3); if(isslice(nl->type) || nl->type->etype == TSTRING) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_array; - gmove(&n1, &n3); + n3.type = types[tptr]; + n3.xoffset += Array_array; + gmove(&n3, &tmp); } + regfree(&n3); + n3 = tmp; if(w == 0) { // nothing to do } else if(w == 1 || w == 2 || w == 4 || w == 8) { + // LEAL (n3)(n2*w), n3 p1 = gins(ALEAL, &n2, &n3); p1->from.scale = w; p1->from.index = p1->from.type; p1->from.type = p1->to.type + D_INDIR; } else { - nodconst(&n1, types[TUINT32], w); - gins(optoas(OMUL, types[TUINT32]), &n1, &n2); + nodconst(&tmp, types[TUINT32], w); + gins(optoas(OMUL, types[TUINT32]), &tmp, &n2); gins(optoas(OADD, types[tptr]), &n2, &n3); } @@ -734,20 +780,82 @@ agen(Node *n, Node *res) void igen(Node *n, Node *a, Node *res) { - Node n1; Type *fp; Iter flist; - + Node n1; + + if(debug['g']) { + dump("\nigen-n", n); + } switch(n->op) { case ONAME: if((n->class&PHEAP) || n->class == PPARAMREF) break; *a = *n; return; - + + case OINDREG: + // Increase the refcount of the register so that igen's caller + // has to call regfree. + if(n->val.u.reg != D_SP) + reg[n->val.u.reg]++; + *a = *n; + return; + + case ODOT: + igen(n->left, a, res); + a->xoffset += n->xoffset; + a->type = n->type; + return; + + case ODOTPTR: + switch(n->left->op) { + case ODOT: + case ODOTPTR: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + // igen-able nodes. + igen(n->left, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + break; + default: + regalloc(a, types[tptr], res); + cgen(n->left, a); + } + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(n->left->type->type->width >= unmappedzero) { + n1 = *a; + n1.op = OINDREG; + n1.type = types[TUINT8]; + n1.xoffset = 0; + gins(ATESTB, nodintconst(0), &n1); + } + } + a->op = OINDREG; + a->xoffset += n->xoffset; + a->type = n->type; + return; + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + switch(n->op) { + case OCALLFUNC: + cgen_call(n, 0); + break; + case OCALLMETH: + cgen_callmeth(n, 0); + break; + case OCALLINTER: + cgen_callinter(n, N, 0); + break; + } fp = structfirst(&flist, getoutarg(n->left->type)); - cgen_call(n, 0); memset(a, 0, sizeof *a); a->op = OINDREG; a->val.u.reg = D_SP; @@ -770,34 +878,16 @@ igen(Node *n, Node *a, Node *res) a->type = n->type; } -/* - * generate: - * newreg = &n; - * - * caller must regfree(a). - */ -void -agenr(Node *n, Node *a, Node *res) -{ - Node n1; - - tempname(&n1, types[tptr]); - agen(n, &n1); - regalloc(a, types[tptr], res); - gmove(&n1, a); -} - /* * branch gen * if(n == true) goto to; */ void -bgen(Node *n, int true, Prog *to) +bgen(Node *n, int true, int likely, Prog *to) { int et, a; Node *nl, *nr, *r; - Node n1, n2, tmp, t1, t2, ax; - NodeList *ll; + Node n1, n2, tmp; Prog *p1, *p2; if(debug['g']) { @@ -822,8 +912,14 @@ bgen(Node *n, int true, Prog *to) patch(gins(AEND, N, N), to); return; } + nl = n->left; nr = N; + if(nl != N && isfloat[nl->type->etype]) { + bgen_float(n, true, likely, to); + return; + } + switch(n->op) { default: def: @@ -834,14 +930,14 @@ bgen(Node *n, int true, Prog *to) a = AJNE; if(!true) a = AJEQ; - patch(gbranch(a, n->type), to); + patch(gbranch(a, n->type, likely), to); regfree(&n1); return; case OLITERAL: // need to ask if it is bool? if(!true == !n->val.u.bval) - patch(gbranch(AJMP, T), to); + patch(gbranch(AJMP, T, 0), to); return; case ONAME: @@ -852,7 +948,7 @@ bgen(Node *n, int true, Prog *to) a = AJNE; if(!true) a = AJEQ; - patch(gbranch(a, n->type), to); + patch(gbranch(a, n->type, likely), to); return; case OANDAND: @@ -860,12 +956,12 @@ bgen(Node *n, int true, Prog *to) goto caseor; caseand: - p1 = gbranch(AJMP, T); - p2 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); - bgen(n->left, !true, p2); - bgen(n->right, !true, p2); - p1 = gbranch(AJMP, T); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AJMP, T, 0); patch(p1, to); patch(p2, pc); return; @@ -875,8 +971,8 @@ bgen(Node *n, int true, Prog *to) goto caseand; caseor: - bgen(n->left, true, to); - bgen(n->right, true, to); + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); return; case OEQ: @@ -897,7 +993,7 @@ bgen(Node *n, int true, Prog *to) switch(n->op) { case ONOT: - bgen(nl, !true, to); + bgen(nl, !true, likely, to); break; case OEQ: @@ -908,19 +1004,6 @@ bgen(Node *n, int true, Prog *to) case OGE: a = n->op; if(!true) { - if(isfloat[nl->type->etype]) { - // brcom is not valid on floats when NaN is involved. - p1 = gbranch(AJMP, T); - p2 = gbranch(AJMP, T); - patch(p1, pc); - ll = n->ninit; // avoid re-genning ninit - n->ninit = nil; - bgen(n, 1, p2); - n->ninit = ll; - patch(gbranch(AJMP, T), to); - patch(p2, pc); - break; - } a = brcom(a); true = !true; } @@ -936,19 +1019,16 @@ bgen(Node *n, int true, Prog *to) if(isslice(nl->type)) { // front end should only leave cmp to literal nil if((a != OEQ && a != ONE) || nr->op != OLITERAL) { - yyerror("illegal array comparison"); + yyerror("illegal slice comparison"); break; } a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = Array_array; - n2.type = types[tptr]; + igen(nl, &n1, N); + n1.xoffset += Array_array; + n1.type = types[tptr]; nodconst(&tmp, types[tptr], 0); - gins(optoas(OCMP, types[tptr]), &n2, &tmp); - patch(gbranch(a, types[tptr]), to); + gins(optoas(OCMP, types[tptr]), &n1, &tmp); + patch(gbranch(a, types[tptr], likely), to); regfree(&n1); break; } @@ -960,75 +1040,17 @@ bgen(Node *n, int true, Prog *to) break; } a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = 0; + igen(nl, &n1, N); + n1.type = types[tptr]; nodconst(&tmp, types[tptr], 0); - gins(optoas(OCMP, types[tptr]), &n2, &tmp); - patch(gbranch(a, types[tptr]), to); + gins(optoas(OCMP, types[tptr]), &n1, &tmp); + patch(gbranch(a, types[tptr], likely), to); regfree(&n1); break; } - if(isfloat[nr->type->etype]) { - a = brrev(a); // because the args are stacked - if(a == OGE || a == OGT) { - // only < and <= work right with NaN; reverse if needed - r = nr; - nr = nl; - nl = r; - a = brrev(a); - } - nodreg(&tmp, nr->type, D_F0); - nodreg(&n2, nr->type, D_F0 + 1); - nodreg(&ax, types[TUINT16], D_AX); - et = simsimtype(nr->type); - if(et == TFLOAT64) { - if(nl->ullman > nr->ullman) { - cgen(nl, &tmp); - cgen(nr, &tmp); - gins(AFXCHD, &tmp, &n2); - } else { - cgen(nr, &tmp); - cgen(nl, &tmp); - } - gins(AFUCOMIP, &tmp, &n2); - gins(AFMOVDP, &tmp, &tmp); // annoying pop but still better than STSW+SAHF - } else { - // TODO(rsc): The moves back and forth to memory - // here are for truncating the value to 32 bits. - // This handles 32-bit comparison but presumably - // all the other ops have the same problem. - // We need to figure out what the right general - // solution is, besides telling people to use float64. - tempname(&t1, types[TFLOAT32]); - tempname(&t2, types[TFLOAT32]); - cgen(nr, &t1); - cgen(nl, &t2); - gmove(&t2, &tmp); - gins(AFCOMFP, &t1, &tmp); - gins(AFSTSW, N, &ax); - gins(ASAHF, N, N); - } - if(a == OEQ) { - // neither NE nor P - p1 = gbranch(AJNE, T); - p2 = gbranch(AJPS, T); - patch(gbranch(AJMP, T), to); - patch(p1, pc); - patch(p2, pc); - } else if(a == ONE) { - // either NE or P - patch(gbranch(AJNE, T), to); - patch(gbranch(AJPS, T), to); - } else - patch(gbranch(optoas(a, nr->type), T), to); - break; - } if(iscomplex[nl->type->etype]) { - complexbool(a, nl, nr, true, to); + complexbool(a, nl, nr, true, likely, to); break; } @@ -1043,40 +1065,55 @@ bgen(Node *n, int true, Prog *to) cgen(nr, &n2); nr = &n2; } - cmp64(nl, nr, a, to); + cmp64(nl, nr, a, likely, to); break; } - a = optoas(a, nr->type); - if(nr->ullman >= UINF) { - tempname(&n1, nl->type); - tempname(&tmp, nr->type); - cgen(nl, &n1); - cgen(nr, &tmp); + if(!nl->addable) { + tempname(&n1, nl->type); + cgen(nl, &n1); + nl = &n1; + } + if(!nr->addable) { + tempname(&tmp, nr->type); + cgen(nr, &tmp); + nr = &tmp; + } regalloc(&n2, nr->type, N); - cgen(&tmp, &n2); + cgen(nr, &n2); + nr = &n2; goto cmp; } - tempname(&n1, nl->type); - cgen(nl, &n1); + if(!nl->addable) { + tempname(&n1, nl->type); + cgen(nl, &n1); + nl = &n1; + } if(smallintconst(nr)) { - gins(optoas(OCMP, nr->type), &n1, nr); - patch(gbranch(a, nr->type), to); + gins(optoas(OCMP, nr->type), nl, nr); + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); break; } - tempname(&tmp, nr->type); - cgen(nr, &tmp); + if(!nr->addable) { + tempname(&tmp, nr->type); + cgen(nr, &tmp); + nr = &tmp; + } regalloc(&n2, nr->type, N); - gmove(&tmp, &n2); + gmove(nr, &n2); + nr = &n2; cmp: - gins(optoas(OCMP, nr->type), &n1, &n2); - patch(gbranch(a, nr->type), to); - regfree(&n2); + gins(optoas(OCMP, nr->type), nl, nr); + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); + + if(nl->op == OREGISTER) + regfree(nl); + regfree(nr); break; } } @@ -1164,6 +1201,10 @@ sgen(Node *n, Node *res, int64 w) return; } + // Avoid taking the address for simple enough types. + if(componentgen(n, res)) + return; + // offset on the stack osrc = stkof(n); odst = stkof(res); @@ -1247,3 +1288,157 @@ sgen(Node *n, Node *res, int64 w) } } +static int +cadable(Node *n) +{ + if(!n->addable) { + // dont know how it happens, + // but it does + return 0; + } + + switch(n->op) { + case ONAME: + return 1; + } + return 0; +} + +/* + * copy a composite value by moving its individual components. + * Slices, strings and interfaces are supported. + * nr is N when assigning a zero value. + * return 1 if can do, 0 if cant. + */ +int +componentgen(Node *nr, Node *nl) +{ + Node nodl, nodr; + int freel, freer; + + freel = 0; + freer = 0; + + switch(nl->type->etype) { + default: + goto no; + + case TARRAY: + if(!isslice(nl->type)) + goto no; + case TSTRING: + case TINTER: + break; + } + + nodl = *nl; + if(!cadable(nl)) { + if(nr == N || !cadable(nr)) + goto no; + igen(nl, &nodl, N); + freel = 1; + } + + if(nr != N) { + nodr = *nr; + if(!cadable(nr)) { + igen(nr, &nodr, N); + freer = 1; + } + } + + switch(nl->type->etype) { + case TARRAY: + nodl.xoffset += Array_array; + nodl.type = ptrto(nl->type->type); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_cap-Array_nel; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_cap-Array_nel; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TSTRING: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TINTER: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + } + +no: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 0; + +yes: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 1; +} diff --git a/src/cmd/8g/cgen64.c b/src/cmd/8g/cgen64.c index 8e568a0f9..dc50a409b 100644 --- a/src/cmd/8g/cgen64.c +++ b/src/cmd/8g/cgen64.c @@ -49,6 +49,7 @@ cgen64(Node *n, Node *res) case OADD: case OSUB: case OMUL: + case OLROT: case OLSH: case ORSH: case OAND: @@ -113,9 +114,9 @@ cgen64(Node *n, Node *res) // if DX and EX are zero, use 32 x 32 -> 64 unsigned multiply. gins(AMOVL, &dx, &fx); gins(AORL, &ex, &fx); - p1 = gbranch(AJNE, T); + p1 = gbranch(AJNE, T, 0); gins(AMULL, &cx, N); // implicit &ax - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); // full 64x64 -> 64, from 32x32 -> 64. @@ -131,6 +132,40 @@ cgen64(Node *n, Node *res) regfree(&ex); regfree(&fx); break; + + case OLROT: + // We only rotate by a constant c in [0,64). + // if c >= 32: + // lo, hi = hi, lo + // c -= 32 + // if c == 0: + // no-op + // else: + // t = hi + // shld hi:lo, c + // shld lo:t, c + v = mpgetfix(r->val.u.xval); + if(v >= 32) { + // reverse during load to do the first 32 bits of rotate + v -= 32; + gins(AMOVL, &lo1, &dx); + gins(AMOVL, &hi1, &ax); + } else { + gins(AMOVL, &lo1, &ax); + gins(AMOVL, &hi1, &dx); + } + if(v == 0) { + // done + } else { + gins(AMOVL, &dx, &cx); + p1 = gins(ASHLL, ncon(v), &dx); + p1->from.index = D_AX; // double-width shift + p1->from.scale = 0; + p1 = gins(ASHLL, ncon(v), &ax); + p1->from.index = D_CX; // double-width shift + p1->from.scale = 0; + } + break; case OLSH: if(r->op == OLITERAL) { @@ -178,7 +213,7 @@ cgen64(Node *n, Node *res) p1 = P; if(is64(r->type)) { gins(ACMPL, &hi2, ncon(0)); - p1 = gbranch(AJNE, T); + p1 = gbranch(AJNE, T, +1); gins(AMOVL, &lo2, &cx); } else { cx.type = types[TUINT32]; @@ -187,7 +222,7 @@ cgen64(Node *n, Node *res) // if shift count is >=64, zero value gins(ACMPL, &cx, ncon(64)); - p2 = gbranch(optoas(OLT, types[TUINT32]), T); + p2 = gbranch(optoas(OLT, types[TUINT32]), T, +1); if(p1 != P) patch(p1, pc); gins(AXORL, &dx, &dx); @@ -196,11 +231,11 @@ cgen64(Node *n, Node *res) // if shift count is >= 32, zero low. gins(ACMPL, &cx, ncon(32)); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); gins(AMOVL, &ax, &dx); gins(ASHLL, &cx, &dx); // SHLL only uses bottom 5 bits of count gins(AXORL, &ax, &ax); - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); // general shift @@ -267,7 +302,7 @@ cgen64(Node *n, Node *res) p1 = P; if(is64(r->type)) { gins(ACMPL, &hi2, ncon(0)); - p1 = gbranch(AJNE, T); + p1 = gbranch(AJNE, T, +1); gins(AMOVL, &lo2, &cx); } else { cx.type = types[TUINT32]; @@ -276,7 +311,7 @@ cgen64(Node *n, Node *res) // if shift count is >=64, zero or sign-extend value gins(ACMPL, &cx, ncon(64)); - p2 = gbranch(optoas(OLT, types[TUINT32]), T); + p2 = gbranch(optoas(OLT, types[TUINT32]), T, +1); if(p1 != P) patch(p1, pc); if(hi1.type->etype == TINT32) { @@ -290,7 +325,7 @@ cgen64(Node *n, Node *res) // if shift count is >= 32, sign-extend hi. gins(ACMPL, &cx, ncon(32)); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); gins(AMOVL, &dx, &ax); if(hi1.type->etype == TINT32) { gins(ASARL, &cx, &ax); // SARL only uses bottom 5 bits of count @@ -299,7 +334,7 @@ cgen64(Node *n, Node *res) gins(ASHRL, &cx, &ax); gins(AXORL, &dx, &dx); } - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); // general shift @@ -427,7 +462,7 @@ out:; * nl is memory; nr is constant or memory. */ void -cmp64(Node *nl, Node *nr, int op, Prog *to) +cmp64(Node *nl, Node *nr, int op, int likely, Prog *to) { Node lo1, hi1, lo2, hi2, rr; Prog *br; @@ -457,14 +492,14 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // jeq to // L: - br = gbranch(AJNE, T); + br = gbranch(AJNE, T, -likely); break; case ONE: // cmp hi // jne to // cmp lo // jne to - patch(gbranch(AJNE, T), to); + patch(gbranch(AJNE, T, likely), to); break; case OGE: case OGT: @@ -474,8 +509,8 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // jge to (or jgt to) // L: - patch(gbranch(optoas(OGT, t), T), to); - br = gbranch(optoas(OLT, t), T); + patch(gbranch(optoas(OGT, t), T, likely), to); + br = gbranch(optoas(OLT, t), T, -likely); break; case OLE: case OLT: @@ -485,8 +520,8 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // jle to (or jlt to) // L: - patch(gbranch(optoas(OLT, t), T), to); - br = gbranch(optoas(OGT, t), T); + patch(gbranch(optoas(OLT, t), T, likely), to); + br = gbranch(optoas(OGT, t), T, -likely); break; } @@ -502,7 +537,7 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) } // jump again - patch(gbranch(optoas(op, t), T), to); + patch(gbranch(optoas(op, t), T, likely), to); // point first branch down here if appropriate if(br != P) diff --git a/src/cmd/8g/doc.go b/src/cmd/8g/doc.go index 6d678eac8..9e46dcad8 100644 --- a/src/cmd/8g/doc.go +++ b/src/cmd/8g/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 8g is the version of the gc compiler for the x86. @@ -10,4 +12,4 @@ The $GOARCH for these tools is 386. It reads .go files and outputs .8 files. The flags are documented in ../gc/doc.go. */ -package documentation +package main diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c index 4526a2efb..2c8aaa0a4 100644 --- a/src/cmd/8g/galign.c +++ b/src/cmd/8g/galign.c @@ -27,6 +27,7 @@ void betypeinit(void) { widthptr = 4; + widthint = 4; zprog.link = P; zprog.as = AGOK; diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h index 0a4f0ad2d..03c206aa9 100644 --- a/src/cmd/8g/gg.h +++ b/src/cmd/8g/gg.h @@ -16,9 +16,12 @@ struct Addr int32 offset; int32 offset2; - double dval; - Prog* branch; - char sval[NSNAME]; + union { + double dval; + vlong vval; + Prog* branch; + char sval[NSNAME]; + } u; Sym* gotype; Sym* sym; @@ -28,7 +31,6 @@ struct Addr uchar index; uchar etype; uchar scale; /* doubles as width in DATA op */ - uchar pun; /* dont register variable */ }; #define A ((Addr*)0) @@ -83,37 +85,38 @@ void cgen_proc(Node*, int); void cgen_callret(Node*, Node*); void cgen_div(int, Node*, Node*, Node*); void cgen_bmul(int, Node*, Node*, Node*); -void cgen_shift(int, Node*, Node*, Node*); +void cgen_hmul(Node*, Node*, Node*); +void cgen_shift(int, int, Node*, Node*, Node*); +void cgen_float(Node*, Node*); +void bgen_float(Node *n, int true, int likely, Prog *to); void cgen_dcl(Node*); int needconvert(Type*, Type*); void genconv(Type*, Type*); void allocparams(void); -void checklabels(); +void checklabels(void); void ginscall(Node*, int); /* * cgen.c */ void agen(Node*, Node*); -void agenr(Node *n, Node *a, Node *res); void igen(Node*, Node*, Node*); vlong fieldoffset(Type*, Node*); -void bgen(Node*, int, Prog*); void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); int samaddr(Node*, Node*); void naddr(Node*, Addr*, int); void cgen_aret(Node*, Node*); -int cgen_inline(Node*, Node*); Node* ncon(uint32); void mgen(Node*, Node*, Node*); void mfree(Node*); +int componentgen(Node*, Node*); /* * cgen64.c */ -void cmp64(Node*, Node*, int, Prog*); +void cmp64(Node*, Node*, int, int, Prog*); void cgen64(Node*, Node*); /* @@ -121,16 +124,13 @@ void cgen64(Node*, Node*); */ void clearp(Prog*); void proglist(void); -Prog* gbranch(int, Type*); +Prog* gbranch(int, Type*, int); Prog* prog(int); -void gaddoffset(Node*); void gconv(int, int); int conv2pt(Type*); vlong convvtox(vlong, int); void fnparam(Type*, int, int); Prog* gop(int, Node*, Node*, Node*); -void setconst(Addr*, vlong); -void setaddr(Addr*, Node*); int optoas(int, Type*); int foptoas(int, Type*, int); void ginit(void); @@ -142,18 +142,17 @@ void nodreg(Node*, Type*, int); void nodindreg(Node*, Type*, int); void nodconst(Node*, Type*, int64); void gconreg(int, vlong, int); -void datagostring(Strlit*, Addr*); -void datastring(char*, int, Addr*); void buildtxt(void); Plist* newplist(void); int isfat(Type*); void sudoclean(void); int sudoaddable(int, Node*, Addr*); int dotaddable(Node*, Node*); -void afunclit(Addr*); +void afunclit(Addr*, Node*); void split64(Node*, Node*, Node*); void splitclean(void); void nswap(Node*, Node*); +void gtrack(Sym*); /* * cplx.c @@ -161,7 +160,12 @@ void nswap(Node*, Node*); int complexop(Node*, Node*); void complexmove(Node*, Node*); void complexgen(Node*, Node*); -void complexbool(int, Node*, Node*, int, Prog*); + +/* + * gobj.c + */ +void datastring(char*, int, Addr*); +void datagostring(Strlit*, Addr*); /* * list.c diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 6a4570199..70148106c 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -27,6 +27,9 @@ void markautoused(Prog* p) { for (; p; p = p->link) { + if (p->as == ATYPE) + continue; + if (p->from.type == D_AUTO && p->from.node) p->from.node->used = 1; @@ -39,12 +42,21 @@ markautoused(Prog* p) void fixautoused(Prog* p) { - for (; p; p = p->link) { + Prog **lp; + + for (lp=&p; (p=*lp) != P; ) { + if (p->as == ATYPE && p->from.node && p->from.type == D_AUTO && !p->from.node->used) { + *lp = p->link; + continue; + } + if (p->from.type == D_AUTO && p->from.node) p->from.offset += p->from.node->stkdelta; if (p->to.type == D_AUTO && p->to.node) p->to.offset += p->to.node->stkdelta; + + lp = &p->link; } } @@ -59,6 +71,10 @@ clearfat(Node *nl) dump("\nclearfat", nl); w = nl->type->width; + // Avoid taking the address for simple enough types. + if(componentgen(N, nl)) + return; + c = w % 4; // bytes q = w / 4; // quads @@ -90,15 +106,17 @@ clearfat(Node *nl) /* * generate: * call f + * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack + * proc=3 normal call to C pointer (not Go func value) */ void ginscall(Node *f, int proc) { Prog *p; - Node reg, con; + Node reg, r1, con; switch(proc) { default: @@ -106,8 +124,25 @@ ginscall(Node *f, int proc) break; case 0: // normal call - p = gins(ACALL, N, f); - afunclit(&p->to); + case -1: // normal call but no return + if(f->op == ONAME && f->class == PFUNC) { + p = gins(ACALL, N, f); + afunclit(&p->to, f); + if(proc == -1 || noreturn(p)) + gins(AUNDEF, N, N); + break; + } + nodreg(®, types[tptr], D_DX); + nodreg(&r1, types[tptr], D_BX); + gmove(f, ®); + reg.op = OINDREG; + gmove(®, &r1); + reg.op = OREGISTER; + gins(ACALL, ®, &r1); + break; + + case 3: // normal call of c function pointer + gins(ACALL, N, f); break; case 1: // call in new proc (go) @@ -125,7 +160,7 @@ ginscall(Node *f, int proc) if(proc == 2) { nodreg(®, types[TINT64], D_AX); gins(ATESTL, ®, ®); - patch(gbranch(AJNE, T), retpc); + patch(gbranch(AJNE, T, -1), retpc); } break; } @@ -139,7 +174,7 @@ void cgen_callinter(Node *n, Node *res, int proc) { Node *i, *f; - Node tmpi, nodo, nodr, nodsp; + Node tmpi, nodi, nodo, nodr, nodsp; i = n->left; if(i->op != ODOTINTER) @@ -159,25 +194,35 @@ cgen_callinter(Node *n, Node *res, int proc) genlist(n->list); // assign the args - // Can regalloc now; i is known to be addable, - // so the agen will be easy. - regalloc(&nodr, types[tptr], res); - regalloc(&nodo, types[tptr], &nodr); - nodo.op = OINDREG; - - agen(i, &nodr); // REG = &inter + // i is now addable, prepare an indirected + // register to hold its address. + igen(i, &nodi, res); // REG = &inter nodindreg(&nodsp, types[tptr], D_SP); - nodo.xoffset += widthptr; - cgen(&nodo, &nodsp); // 0(SP) = 4(REG) -- i.data + nodi.type = types[tptr]; + nodi.xoffset += widthptr; + cgen(&nodi, &nodsp); // 0(SP) = 4(REG) -- i.data - nodo.xoffset -= widthptr; - cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab + regalloc(&nodo, types[tptr], res); + nodi.type = types[tptr]; + nodi.xoffset -= widthptr; + cgen(&nodi, &nodo); // REG = 0(REG) -- i.tab + regfree(&nodi); + regalloc(&nodr, types[tptr], &nodo); if(n->left->xoffset == BADWIDTH) fatal("cgen_callinter: badwidth"); + nodo.op = OINDREG; nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + + if(proc == 0) { + // plain call: use direct c function pointer - more efficient + cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + proc = 3; + } else { + // go/defer. generate go func value. + gins(ALEAL, &nodo, &nodr); // REG = &(20+offset(REG)) -- i.tab->fun[f] + } // BOTCH nodr.type = fntype; nodr.type = n->left->type; @@ -486,7 +531,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) int check; Node n1, t1, t2, t3, t4, n4, nz; Type *t, *t0; - Prog *p1, *p2, *p3; + Prog *p1, *p2; // Have to be careful about handling // most negative int divided by -1 correctly. @@ -535,23 +580,22 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) regalloc(&n1, t, N); gmove(&t2, &n1); gmove(&t1, ax); - p3 = P; + p2 = P; if(check) { nodconst(&n4, t, -1); gins(optoas(OCMP, t), &n1, &n4); - p1 = gbranch(optoas(ONE, t), T); - nodconst(&n4, t, -1LL<<(t->width*8-1)); - gins(optoas(OCMP, t), ax, &n4); - p2 = gbranch(optoas(ONE, t), T); - if(op == ODIV) - gmove(&n4, res); - if(op == OMOD) { + p1 = gbranch(optoas(ONE, t), T, +1); + if(op == ODIV) { + // a / (-1) is -a. + gins(optoas(OMINUS, t), N, ax); + gmove(ax, res); + } else { + // a % (-1) is 0. nodconst(&n4, t, 0); gmove(&n4, res); } - p3 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); - patch(p2, pc); } if(!issigned[t->etype]) { nodconst(&nz, t, 0); @@ -566,7 +610,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) else gmove(dx, res); if(check) - patch(p3, pc); + patch(p2, pc); } static void @@ -630,7 +674,7 @@ cgen_div(int op, Node *nl, Node *nr, Node *res) * res = nl >> nr */ void -cgen_shift(int op, Node *nl, Node *nr, Node *res) +cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) { Node n1, n2, nt, cx, oldcx, hi, lo; int a, w; @@ -651,7 +695,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) gmove(&n2, &n1); sc = mpgetfix(nr->val.u.xval); if(sc >= nl->type->width*8) { - // large shift gets 2 shifts by width + // large shift gets 2 shifts by width-1 gins(a, ncon(w-1), &n1); gins(a, ncon(w-1), &n1); } else @@ -689,27 +733,39 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) } // test and fix up large shifts - if(nr->type->width > 4) { - // delayed reg alloc - nodreg(&n1, types[TUINT32], D_CX); - regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX - split64(&nt, &lo, &hi); - gmove(&lo, &n1); - gins(optoas(OCMP, types[TUINT32]), &hi, ncon(0)); - p2 = gbranch(optoas(ONE, types[TUINT32]), T); - gins(optoas(OCMP, types[TUINT32]), &n1, ncon(w)); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); - patch(p2, pc); - } else { - gins(optoas(OCMP, nr->type), &n1, ncon(w)); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); - } - if(op == ORSH && issigned[nl->type->etype]) { - gins(a, ncon(w-1), &n2); + if(bounded) { + if(nr->type->width > 4) { + // delayed reg alloc + nodreg(&n1, types[TUINT32], D_CX); + regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX + split64(&nt, &lo, &hi); + gmove(&lo, &n1); + splitclean(); + } } else { - gmove(ncon(0), &n2); + if(nr->type->width > 4) { + // delayed reg alloc + nodreg(&n1, types[TUINT32], D_CX); + regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX + split64(&nt, &lo, &hi); + gmove(&lo, &n1); + gins(optoas(OCMP, types[TUINT32]), &hi, ncon(0)); + p2 = gbranch(optoas(ONE, types[TUINT32]), T, +1); + gins(optoas(OCMP, types[TUINT32]), &n1, ncon(w)); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); + splitclean(); + patch(p2, pc); + } else { + gins(optoas(OCMP, nr->type), &n1, ncon(w)); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); + } + if(op == ORSH && issigned[nl->type->etype]) { + gins(a, ncon(w-1), &n2); + } else { + gmove(ncon(0), &n2); + } + patch(p1, pc); } - patch(p1, pc); gins(a, &n1, &n2); if(oldcx.op != 0) @@ -724,444 +780,361 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) /* * generate byte multiply: * res = nl * nr - * no byte multiply instruction so have to do - * 16-bit multiply and take bottom half. + * there is no 2-operand byte multiply instruction so + * we do a full-width multiplication and truncate afterwards. */ void cgen_bmul(int op, Node *nl, Node *nr, Node *res) { - Node n1b, n2b, n1w, n2w; + Node n1, n2, nt, *tmp; Type *t; int a; - if(nl->ullman >= nr->ullman) { - regalloc(&n1b, nl->type, res); - cgen(nl, &n1b); - regalloc(&n2b, nr->type, N); - cgen(nr, &n2b); - } else { - regalloc(&n2b, nr->type, N); - cgen(nr, &n2b); - regalloc(&n1b, nl->type, res); - cgen(nl, &n1b); - } - - // copy from byte to short registers - t = types[TUINT16]; + // copy from byte to full registers + t = types[TUINT32]; if(issigned[nl->type->etype]) - t = types[TINT16]; - - regalloc(&n2w, t, &n2b); - cgen(&n2b, &n2w); + t = types[TINT32]; - regalloc(&n1w, t, &n1b); - cgen(&n1b, &n1w); + // largest ullman on left. + if(nl->ullman < nr->ullman) { + tmp = nl; + nl = nr; + nr = tmp; + } + tempname(&nt, nl->type); + cgen(nl, &nt); + regalloc(&n1, t, res); + cgen(nr, &n1); + regalloc(&n2, t, N); + gmove(&nt, &n2); a = optoas(op, t); - gins(a, &n2w, &n1w); - cgen(&n1w, &n1b); - cgen(&n1b, res); - - regfree(&n1w); - regfree(&n2w); - regfree(&n1b); - regfree(&n2b); + gins(a, &n2, &n1); + regfree(&n2); + gmove(&n1, res); + regfree(&n1); } -static int -regcmp(const void *va, const void *vb) +/* + * generate high multiply: + * res = (nl*nr) >> width + */ +void +cgen_hmul(Node *nl, Node *nr, Node *res) { - Node *ra, *rb; - - ra = (Node*)va; - rb = (Node*)vb; - return ra->local - rb->local; -} + Type *t; + int a; + Node n1, n2, ax, dx; -static Prog* throwpc; + t = nl->type; + a = optoas(OHMUL, t); + // gen nl in n1. + tempname(&n1, t); + cgen(nl, &n1); + // gen nr in n2. + regalloc(&n2, t, res); + cgen(nr, &n2); + + // multiply. + nodreg(&ax, t, D_AX); + gmove(&n2, &ax); + gins(a, &n1, N); + regfree(&n2); -// We're only going to bother inlining if we can -// convert all the arguments to 32 bits safely. Can we? -static int -fix64(NodeList *nn, int n) -{ - NodeList *l; - Node *r; - int i; - - l = nn; - for(i=0; in->right; - if(is64(r->type) && !smallintconst(r)) { - if(r->op == OCONV) - r = r->left; - if(is64(r->type)) - return 0; - } - l = l->next; + if(t->width == 1) { + // byte multiply behaves differently. + nodreg(&ax, t, D_AH); + nodreg(&dx, t, D_DL); + gmove(&ax, &dx); } - return 1; + nodreg(&dx, t, D_DX); + gmove(&dx, res); } +static void cgen_float387(Node *n, Node *res); +static void cgen_floatsse(Node *n, Node *res); + +/* + * generate floating-point operation. + */ void -getargs(NodeList *nn, Node *reg, int n) +cgen_float(Node *n, Node *res) { - NodeList *l; - Node *r; - int i; - - throwpc = nil; - - l = nn; - for(i=0; in->right; - if(is64(r->type)) { - if(r->op == OCONV) - r = r->left; - else if(smallintconst(r)) - r->type = types[TUINT32]; - if(is64(r->type)) - fatal("getargs"); + Node *nl; + Node n1, n2; + Prog *p1, *p2, *p3; + + nl = n->left; + switch(n->op) { + case OEQ: + case ONE: + case OLT: + case OLE: + case OGE: + p1 = gbranch(AJMP, T, 0); + p2 = pc; + gmove(nodbool(1), res); + p3 = gbranch(AJMP, T, 0); + patch(p1, pc); + bgen(n, 1, 0, p2); + gmove(nodbool(0), res); + patch(p3, pc); + return; + + case OPLUS: + cgen(nl, res); + return; + + case OCONV: + if(eqtype(n->type, nl->type) || noconv(n->type, nl->type)) { + cgen(nl, res); + return; } - if(!smallintconst(r) && !isslice(r->type)) { - if(i < 3) // AX CX DX - nodreg(reg+i, r->type, D_AX+i); - else - reg[i].op = OXXX; - regalloc(reg+i, r->type, reg+i); - cgen(r, reg+i); - } else - reg[i] = *r; - if(reg[i].local != 0) - yyerror("local used"); - reg[i].local = l->n->left->xoffset; - l = l->next; + + tempname(&n2, n->type); + mgen(nl, &n1, res); + gmove(&n1, &n2); + gmove(&n2, res); + mfree(&n1); + return; } - qsort((void*)reg, n, sizeof(*reg), regcmp); - for(i=0; ival.u.xval); - if(cl == 0) - return; - if(smallintconst(nr)) - return; - // put the constant on the right - op = brrev(op); - c = nl; - nl = nr; - nr = c; - } - - // Arguments are known not to be 64-bit, - // but they might be smaller than 32 bits. - // Check if we need to use a temporary. - // At least one of the arguments is 32 bits - // (the len or cap) so one temporary suffices. - n1.op = OXXX; - t = types[TUINT32]; - if(nl->type->width != t->width) { - regalloc(&n1, t, nl); - gmove(nl, &n1); - nl = &n1; - } else if(nr->type->width != t->width) { - regalloc(&n1, t, nr); - gmove(nr, &n1); - nr = &n1; - } - gins(optoas(OCMP, t), nl, nr); - if(n1.op != OXXX) - regfree(&n1); - if(throwpc == nil) { - p1 = gbranch(optoas(op, t), T); - throwpc = pc; - ginscall(panicslice, 0); - patch(p1, pc); + nl = n->left; + nr = n->right; + nodreg(&f0, nl->type, D_F0); + nodreg(&f1, n->type, D_F0+1); + if(nr != N) + goto flt2; + + // unary + cgen(nl, &f0); + if(n->op != OCONV && n->op != OPLUS) + gins(foptoas(n->op, n->type, 0), N, N); + gmove(&f0, res); + return; + +flt2: // binary + if(nl->ullman >= nr->ullman) { + cgen(nl, &f0); + if(nr->addable) + gins(foptoas(n->op, n->type, 0), nr, &f0); + else { + cgen(nr, &f0); + gins(foptoas(n->op, n->type, Fpop), &f0, &f1); + } } else { - op = brcom(op); - p1 = gbranch(optoas(op, t), T); - patch(p1, throwpc); + cgen(nr, &f0); + if(nl->addable) + gins(foptoas(n->op, n->type, Frev), nl, &f0); + else { + cgen(nl, &f0); + gins(foptoas(n->op, n->type, Frev|Fpop), &f0, &f1); + } } -} + gmove(&f0, res); + return; -int -sleasy(Node *n) -{ - if(n->op != ONAME) - return 0; - if(!n->addable) - return 0; - return 1; } -// generate inline code for -// slicearray -// sliceslice -// arraytoslice -int -cgen_inline(Node *n, Node *res) +static void +cgen_floatsse(Node *n, Node *res) { - Node nodes[5]; - Node n1, n2, nres, ntemp; - vlong v; - int i, narg, nochk; - - if(n->op != OCALLFUNC) - goto no; - if(!n->left->addable) - goto no; - if(n->left->sym == S) - goto no; - if(n->left->sym->pkg != runtimepkg) - goto no; - if(strcmp(n->left->sym->name, "slicearray") == 0) - goto slicearray; - if(strcmp(n->left->sym->name, "sliceslice") == 0) { - narg = 4; - goto sliceslice; - } - if(strcmp(n->left->sym->name, "sliceslice1") == 0) { - narg = 3; - goto sliceslice; - } - goto no; - -slicearray: - if(!sleasy(res)) - goto no; - if(!fix64(n->list, 5)) - goto no; - getargs(n->list, nodes, 5); - - // if(hb[3] > nel[1]) goto throw - cmpandthrow(&nodes[3], &nodes[1]); - - // if(lb[2] > hb[3]) goto throw - cmpandthrow(&nodes[2], &nodes[3]); - - // len = hb[3] - lb[2] (destroys hb) - n2 = *res; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[3]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[3].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[3]); - gmove(&nodes[3], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } + Node *nl, *nr, *r; + Node n1, n2, nt; + int a; - // cap = nel[1] - lb[2] (destroys nel) - n2 = *res; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; + nl = n->left; + nr = n->right; + switch(n->op) { + default: + dump("cgen_floatsse", n); + fatal("cgen_floatsse %O", n->op); + return; - if(smallintconst(&nodes[1]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[1].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[1]); - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); + case OMINUS: + case OCOM: + nr = nodintconst(-1); + convlit(&nr, n->type); + a = foptoas(OMUL, nl->type, 0); + goto sbop; + + // symmetric binary + case OADD: + case OMUL: + a = foptoas(n->op, nl->type, 0); + goto sbop; + + // asymmetric binary + case OSUB: + case OMOD: + case ODIV: + a = foptoas(n->op, nl->type, 0); + goto abop; } - // if slice could be too big, dereference to - // catch nil array pointer. - if(nodes[0].op == OREGISTER && nodes[0].type->type->width >= unmappedzero) { - n2 = nodes[0]; - n2.xoffset = 0; - n2.op = OINDREG; - n2.type = types[TUINT8]; - gins(ATESTB, nodintconst(0), &n2); +sbop: // symmetric binary + if(nl->ullman < nr->ullman || nl->op == OLITERAL) { + r = nl; + nl = nr; + nr = r; } - // ary = old[0] + (lb[2] * width[4]) (destroys old) - n2 = *res; - n2.xoffset += Array_array; - n2.type = types[tptr]; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { - v = mpgetfix(nodes[2].val.u.xval) * - mpgetfix(nodes[4].val.u.xval); - if(v != 0) { - nodconst(&n1, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - } +abop: // asymmetric binary + if(nl->ullman >= nr->ullman) { + tempname(&nt, nl->type); + cgen(nl, &nt); + mgen(nr, &n2, N); + regalloc(&n1, nl->type, res); + gmove(&nt, &n1); + gins(a, &n2, &n1); + gmove(&n1, res); + regfree(&n1); + mfree(&n2); } else { - regalloc(&n1, types[tptr], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[4]) || mpgetfix(nodes[4].val.u.xval) != 1) - gins(optoas(OMUL, types[tptr]), &nodes[4], &n1); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + gins(a, &n2, &n1); + regfree(&n2); + gmove(&n1, res); regfree(&n1); } - gins(optoas(OAS, types[tptr]), &nodes[0], &n2); - - for(i=0; i<5; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - return 1; + return; +} -sliceslice: - if(!fix64(n->list, narg)) - goto no; - nochk = n->etype; // skip bounds checking - ntemp.op = OXXX; - if(!sleasy(n->list->n->right)) { - Node *n0; - - n0 = n->list->n->right; - tempname(&ntemp, res->type); - cgen(n0, &ntemp); - n->list->n->right = &ntemp; - getargs(n->list, nodes, narg); - n->list->n->right = n0; - } else - getargs(n->list, nodes, narg); +void +bgen_float(Node *n, int true, int likely, Prog *to) +{ + int et, a; + Node *nl, *nr, *r; + Node n1, n2, n3, tmp, t1, t2, ax; + Prog *p1, *p2; - nres = *res; // result - if(!sleasy(res)) { - if(ntemp.op == OXXX) - tempname(&ntemp, res->type); - nres = ntemp; + nl = n->left; + nr = n->right; + a = n->op; + if(!true) { + // brcom is not valid on floats when NaN is involved. + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); + patch(p1, pc); + // No need to avoid re-genning ninit. + bgen_float(n, 1, -likely, p2); + patch(gbranch(AJMP, T, 0), to); + patch(p2, pc); + return; } - if(narg == 3) { // old[lb:] - // move width to where it would be for old[lb:hb] - nodes[3] = nodes[2]; - nodes[2].op = OXXX; - - // if(lb[1] > old.nel[0]) goto throw; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - if(!nochk) - cmpandthrow(&nodes[1], &n2); - - // ret.nel = old.nel[0]-lb[1]; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - regalloc(&n1, types[TUINT32], N); - gins(optoas(OAS, types[TUINT32]), &n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } else { // old[lb:hb] - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - if (!nochk) { - // if(hb[2] > old.cap[0]) goto throw; - cmpandthrow(&nodes[2], &n2); - // if(lb[1] > hb[2]) goto throw; - cmpandthrow(&nodes[1], &nodes[2]); - } - - // ret.len = hb[2]-lb[1]; (destroys hb[2]) - n2 = nres; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[1])) { - v = mpgetfix(nodes[2].val.u.xval) - - mpgetfix(nodes[1].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } + if(use_sse) + goto sse; + else + goto x87; + +x87: + a = brrev(a); // because the args are stacked + if(a == OGE || a == OGT) { + // only < and <= work right with NaN; reverse if needed + r = nr; + nr = nl; + nl = r; + a = brrev(a); } - // ret.cap = old.cap[0]-lb[1]; (uses hb[2]) - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - - regalloc(&n1, types[TUINT32], &nodes[2]); - gins(optoas(OAS, types[TUINT32]), &n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - - // ret.array = old.array[0]+lb[1]*width[3]; (uses lb[1]) - n2 = nodes[0]; - n2.xoffset += Array_array; - n2.type = types[tptr]; - - regalloc(&n1, types[tptr], &nodes[1]); - if(smallintconst(&nodes[1]) && smallintconst(&nodes[3])) { - gins(optoas(OAS, types[tptr]), &n2, &n1); - v = mpgetfix(nodes[1].val.u.xval) * - mpgetfix(nodes[3].val.u.xval); - if(v != 0) { - nodconst(&n2, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n2, &n1); + nodreg(&tmp, nr->type, D_F0); + nodreg(&n2, nr->type, D_F0 + 1); + nodreg(&ax, types[TUINT16], D_AX); + et = simsimtype(nr->type); + if(et == TFLOAT64) { + if(nl->ullman > nr->ullman) { + cgen(nl, &tmp); + cgen(nr, &tmp); + gins(AFXCHD, &tmp, &n2); + } else { + cgen(nr, &tmp); + cgen(nl, &tmp); } + gins(AFUCOMIP, &tmp, &n2); + gins(AFMOVDP, &tmp, &tmp); // annoying pop but still better than STSW+SAHF } else { - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) - gins(optoas(OMUL, types[tptr]), &nodes[3], &n1); - gins(optoas(OADD, types[tptr]), &n2, &n1); + // TODO(rsc): The moves back and forth to memory + // here are for truncating the value to 32 bits. + // This handles 32-bit comparison but presumably + // all the other ops have the same problem. + // We need to figure out what the right general + // solution is, besides telling people to use float64. + tempname(&t1, types[TFLOAT32]); + tempname(&t2, types[TFLOAT32]); + cgen(nr, &t1); + cgen(nl, &t2); + gmove(&t2, &tmp); + gins(AFCOMFP, &t1, &tmp); + gins(AFSTSW, N, &ax); + gins(ASAHF, N, N); } - n2 = nres; - n2.xoffset += Array_array; - n2.type = types[tptr]; - gins(optoas(OAS, types[tptr]), &n1, &n2); - regfree(&n1); + goto ret; - for(i=0; i<4; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); +sse: + if(!nl->addable) { + tempname(&n1, nl->type); + cgen(nl, &n1); + nl = &n1; + } + if(!nr->addable) { + tempname(&tmp, nr->type); + cgen(nr, &tmp); + nr = &tmp; + } + regalloc(&n2, nr->type, N); + gmove(nr, &n2); + nr = &n2; + + if(nl->op != OREGISTER) { + regalloc(&n3, nl->type, N); + gmove(nl, &n3); + nl = &n3; } - if(!sleasy(res)) { - cgen(&nres, res); + if(a == OGE || a == OGT) { + // only < and <= work right with NaN; reverse if needed + r = nr; + nr = nl; + nl = r; + a = brrev(a); } - return 1; -no: - return 0; + gins(foptoas(OCMP, nr->type, 0), nl, nr); + if(nl->op == OREGISTER) + regfree(nl); + regfree(nr); + +ret: + if(a == OEQ) { + // neither NE nor P + p1 = gbranch(AJNE, T, -likely); + p2 = gbranch(AJPS, T, -likely); + patch(gbranch(AJMP, T, 0), to); + patch(p1, pc); + patch(p2, pc); + } else if(a == ONE) { + // either NE or P + patch(gbranch(AJNE, T, likely), to); + patch(gbranch(AJPS, T, likely), to); + } else + patch(gbranch(optoas(a, nr->type), T, likely), to); + } diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c index da0055cd9..39717d5b1 100644 --- a/src/cmd/8g/gobj.c +++ b/src/cmd/8g/gobj.c @@ -94,9 +94,9 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) switch(a->type) { case D_BRANCH: - if(a->branch == nil) + if(a->u.branch == nil) fatal("unpatched branch"); - a->offset = a->branch->loc; + a->offset = a->u.branch->loc; default: t |= T_TYPE; @@ -137,7 +137,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) if(t & T_SYM) /* implies sym */ Bputc(b, s); if(t & T_FCONST) { - ieeedtod(&e, a->dval); + ieeedtod(&e, a->u.dval); l = e; Bputc(b, l); Bputc(b, l>>8); @@ -151,7 +151,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) return; } if(t & T_SCONST) { - n = a->sval; + n = a->u.sval; for(i=0; ito.type = D_SCONST; p->to.index = D_NONE; - memmove(p->to.sval, t, n); + memmove(p->to.u.sval, t, n); return off + n; } @@ -373,13 +373,13 @@ gdatacomplex(Node *nam, Mpcplx *cval) p = gins(ADATA, nam, N); p->from.scale = w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->real); + p->to.u.dval = mpgetflt(&cval->real); p = gins(ADATA, nam, N); p->from.scale = w; p->from.offset += w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->imag); + p->to.u.dval = mpgetflt(&cval->imag); } void diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 5e89af04a..c4c184bb9 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -105,16 +105,24 @@ dumpdata(void) /* * generate a branch. * t is ignored. + * likely values are for branch prediction: + * -1 unlikely + * 0 no opinion + * +1 likely */ Prog* -gbranch(int as, Type *t) +gbranch(int as, Type *t, int likely) { Prog *p; USED(t); p = prog(as); p->to.type = D_BRANCH; - p->to.branch = P; + p->to.u.branch = P; + if(likely != 0) { + p->from.type = D_CONST; + p->from.offset = likely > 0; + } return p; } @@ -126,7 +134,7 @@ patch(Prog *p, Prog *to) { if(p->to.type != D_BRANCH) fatal("patch: not a branch"); - p->to.branch = to; + p->to.u.branch = to; p->to.offset = to->loc; } @@ -137,8 +145,8 @@ unpatch(Prog *p) if(p->to.type != D_BRANCH) fatal("unpatch: not a branch"); - q = p->to.branch; - p->to.branch = P; + q = p->to.u.branch; + p->to.u.branch = P; p->to.offset = 0; return q; } @@ -165,44 +173,6 @@ newplist(void) return pl; } -void -clearstk(void) -{ - Plist *pl; - Prog *p1, *p2; - Node sp, di, cx, con, ax; - - if(plast->firstpc->to.offset <= 0) - return; - - // reestablish context for inserting code - // at beginning of function. - pl = plast; - p1 = pl->firstpc; - p2 = p1->link; - pc = mal(sizeof(*pc)); - clearp(pc); - p1->link = pc; - - // zero stack frame - nodreg(&sp, types[tptr], D_SP); - nodreg(&di, types[tptr], D_DI); - nodreg(&cx, types[TUINT32], D_CX); - nodconst(&con, types[TUINT32], p1->to.offset / widthptr); - gins(ACLD, N, N); - gins(AMOVL, &sp, &di); - gins(AMOVL, &con, &cx); - nodconst(&con, types[TUINT32], 0); - nodreg(&ax, types[TUINT32], D_AX); - gins(AMOVL, &con, &ax); - gins(AREP, N, N); - gins(ASTOSL, N, N); - - // continue with original code. - gins(ANOP, N, N)->link = p2; - pc = P; -} - void gused(Node *n) { @@ -214,22 +184,23 @@ gjmp(Prog *to) { Prog *p; - p = gbranch(AJMP, T); + p = gbranch(AJMP, T, 0); if(to != P) patch(p, to); return p; } void -ggloblnod(Node *nam, int32 width) +ggloblnod(Node *nam) { Prog *p; p = gins(AGLOBL, nam, N); p->lineno = nam->lineno; + p->from.gotype = ngotype(nam); p->to.sym = S; p->to.type = D_CONST; - p->to.offset = width; + p->to.offset = nam->type->width; if(nam->readonly) p->from.scale = RODATA; if(nam->type != T && !haspointers(nam->type)) @@ -237,7 +208,7 @@ ggloblnod(Node *nam, int32 width) } void -ggloblsym(Sym *s, int32 width, int dupok) +ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -249,8 +220,20 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.index = D_NONE; p->to.offset = width; if(dupok) - p->from.scale = DUPOK; - p->from.scale |= RODATA; + p->from.scale |= DUPOK; + if(rodata) + p->from.scale |= RODATA; +} + +void +gtrack(Sym *s) +{ + Prog *p; + + p = gins(AUSEFIELD, N, N); + p->from.type = D_EXTERN; + p->from.index = D_NONE; + p->from.sym = s; } int @@ -273,11 +256,12 @@ isfat(Type *t) * call afunclit to fix up the argument. */ void -afunclit(Addr *a) +afunclit(Addr *a, Node *n) { if(a->type == D_ADDR && a->index == D_EXTERN) { a->type = D_EXTERN; a->index = D_NONE; + a->sym = n->sym; } } @@ -572,6 +556,22 @@ optoas(int op, Type *t) a = AXORL; break; + case CASE(OLROT, TINT8): + case CASE(OLROT, TUINT8): + a = AROLB; + break; + + case CASE(OLROT, TINT16): + case CASE(OLROT, TUINT16): + a = AROLW; + break; + + case CASE(OLROT, TINT32): + case CASE(OLROT, TUINT32): + case CASE(OLROT, TPTR32): + a = AROLL; + break; + case CASE(OLSH, TINT8): case CASE(OLSH, TUINT8): a = ASHLB; @@ -613,22 +613,38 @@ optoas(int op, Type *t) a = ASARL; break; + case CASE(OHMUL, TINT8): case CASE(OMUL, TINT8): case CASE(OMUL, TUINT8): a = AIMULB; break; + case CASE(OHMUL, TINT16): case CASE(OMUL, TINT16): case CASE(OMUL, TUINT16): a = AIMULW; break; + case CASE(OHMUL, TINT32): case CASE(OMUL, TINT32): case CASE(OMUL, TUINT32): case CASE(OMUL, TPTR32): a = AIMULL; break; + case CASE(OHMUL, TUINT8): + a = AMULB; + break; + + case CASE(OHMUL, TUINT16): + a = AMULW; + break; + + case CASE(OHMUL, TUINT32): + case CASE(OHMUL, TPTR32): + a = AMULL; + break; + case CASE(ODIV, TINT8): case CASE(OMOD, TINT8): a = AIDIVB; @@ -676,10 +692,14 @@ optoas(int op, Type *t) int foptoas(int op, Type *t, int flg) { - int et; + int et, a; + a = AGOK; et = simtype[t->etype]; + if(use_sse) + goto sse; + // If we need Fpop, it means we're working on // two different floating-point registers, not memory. // There the instruction only has a float64 form. @@ -756,8 +776,65 @@ foptoas(int op, Type *t, int flg) fatal("foptoas %O %T %#x", op, t, flg); return 0; + +sse: + switch(CASE(op, et)) { + default: + fatal("foptoas-sse: no entry %O-%T", op, t); + break; + + case CASE(OCMP, TFLOAT32): + a = AUCOMISS; + break; + + case CASE(OCMP, TFLOAT64): + a = AUCOMISD; + break; + + case CASE(OAS, TFLOAT32): + a = AMOVSS; + break; + + case CASE(OAS, TFLOAT64): + a = AMOVSD; + break; + + case CASE(OADD, TFLOAT32): + a = AADDSS; + break; + + case CASE(OADD, TFLOAT64): + a = AADDSD; + break; + + case CASE(OSUB, TFLOAT32): + a = ASUBSS; + break; + + case CASE(OSUB, TFLOAT64): + a = ASUBSD; + break; + + case CASE(OMUL, TFLOAT32): + a = AMULSS; + break; + + case CASE(OMUL, TFLOAT64): + a = AMULSD; + break; + + case CASE(ODIV, TFLOAT32): + a = ADIVSS; + break; + + case CASE(ODIV, TFLOAT64): + a = ADIVSD; + break; + } + return a; } + static int resvd[] = { // D_DI, // for movstring @@ -779,7 +856,9 @@ ginit(void) for(i=0; ietype]; switch(et) { + case TINT64: + case TUINT64: + fatal("regalloc64"); + case TINT8: case TUINT8: case TINT16: case TUINT16: case TINT32: case TUINT32: - case TINT64: - case TUINT64: case TPTR32: case TPTR64: case TBOOL: @@ -860,8 +947,22 @@ regalloc(Node *n, Type *t, Node *o) case TFLOAT32: case TFLOAT64: - i = D_F0; - goto out; + if(!use_sse) { + i = D_F0; + goto out; + } + if(o != N && o->op == OREGISTER) { + i = o->val.u.reg; + if(i >= D_X0 && i <= D_X7) + goto out; + } + for(i=D_X0; i<=D_X7; i++) + if(reg[i] == 0) + goto out; + fprint(2, "registers allocated at\n"); + for(i=D_X0; i<=D_X7; i++) + fprint(2, "\t%R\t%#lux\n", i, regpc[i]); + fatal("out of floating registers"); } yyerror("regalloc: unknown type %T", t); @@ -1069,9 +1170,9 @@ split64(Node *n, Node *lo, Node *hi) if(!is64(n->type)) fatal("split64 %T", n->type); - sclean[nsclean].op = OEMPTY; if(nsclean >= nelem(sclean)) fatal("split64 clean"); + sclean[nsclean].op = OEMPTY; nsclean++; switch(n->op) { default: @@ -1165,13 +1266,16 @@ memname(Node *n, Type *t) n->orig->sym = n->sym; } +static void floatmove(Node *f, Node *t); +static void floatmove_387(Node *f, Node *t); +static void floatmove_sse(Node *f, Node *t); + void gmove(Node *f, Node *t) { int a, ft, tt; Type *cvt; - Node r1, r2, t1, t2, flo, fhi, tlo, thi, con, f0, f1, ax, dx, cx; - Prog *p1, *p2, *p3; + Node r1, r2, flo, fhi, tlo, thi, con; if(debug['M']) print("gmove %N -> %N\n", f, t); @@ -1179,11 +1283,15 @@ gmove(Node *f, Node *t) ft = simsimtype(f->type); tt = simsimtype(t->type); cvt = t->type; - + if(iscomplex[ft] || iscomplex[tt]) { complexmove(f, t); return; } + if(isfloat[ft] || isfloat[tt]) { + floatmove(f, t); + return; + } // cannot have two integer memory operands; // except 64-bit, which always copies via registers anyway. @@ -1192,19 +1300,9 @@ gmove(Node *f, Node *t) // convert constant to desired type if(f->op == OLITERAL) { - if(tt == TFLOAT32) - convconst(&con, types[TFLOAT64], &f->val); - else - convconst(&con, t->type, &f->val); + convconst(&con, t->type, &f->val); f = &con; ft = simsimtype(con.type); - - // some constants can't move directly to memory. - if(ismem(t)) { - // float constants come from memory. - if(isfloat[tt]) - goto hard; - } } // value -> value copy, only one memory operand. @@ -1380,25 +1478,97 @@ gmove(Node *f, Node *t) gins(AMOVL, ncon(0), &thi); splitclean(); return; + } - /* - * float to integer - */ - case CASE(TFLOAT32, TINT16): - case CASE(TFLOAT32, TINT32): + gins(a, f, t); + return; + +rsrc: + // requires register source + regalloc(&r1, f->type, t); + gmove(f, &r1); + gins(a, &r1, t); + regfree(&r1); + return; + +rdst: + // requires register destination + regalloc(&r1, t->type, t); + gins(a, f, &r1); + gmove(&r1, t); + regfree(&r1); + return; + +hard: + // requires register intermediate + regalloc(&r1, cvt, t); + gmove(f, &r1); + gmove(&r1, t); + regfree(&r1); + return; + +fatal: + // should not happen + fatal("gmove %N -> %N", f, t); +} + +static void +floatmove(Node *f, Node *t) +{ + Node r1, r2, t1, t2, tlo, thi, con, f0, f1, ax, dx, cx; + Type *cvt; + int ft, tt; + Prog *p1, *p2, *p3; + + ft = simsimtype(f->type); + tt = simsimtype(t->type); + cvt = t->type; + + // cannot have two floating point memory operands. + if(isfloat[ft] && isfloat[tt] && ismem(f) && ismem(t)) + goto hard; + + // convert constant to desired type + if(f->op == OLITERAL) { + convconst(&con, t->type, &f->val); + f = &con; + ft = simsimtype(con.type); + + // some constants can't move directly to memory. + if(ismem(t)) { + // float constants come from memory. + if(isfloat[tt]) + goto hard; + } + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch(CASE(ft, tt)) { + default: + if(use_sse) + floatmove_sse(f, t); + else + floatmove_387(f, t); + return; + + // float to very long integer. case CASE(TFLOAT32, TINT64): - case CASE(TFLOAT64, TINT16): - case CASE(TFLOAT64, TINT32): case CASE(TFLOAT64, TINT64): - if(t->op == OREGISTER) + if(f->op == OREGISTER) { + cvt = f->type; goto hardmem; - nodreg(&r1, types[ft], D_F0); - if(f->op != OREGISTER) { - if(ft == TFLOAT32) - gins(AFMOVF, f, &r1); - else - gins(AFMOVD, f, &r1); } + nodreg(&r1, types[ft], D_F0); + if(ft == TFLOAT32) + gins(AFMOVF, f, &r1); + else + gins(AFMOVD, f, &r1); // set round to zero mode during conversion memname(&t1, types[TUINT16]); @@ -1415,85 +1585,37 @@ gmove(Node *f, Node *t) gins(AFLDCW, &t1, N); return; - case CASE(TFLOAT32, TINT8): - case CASE(TFLOAT32, TUINT16): - case CASE(TFLOAT32, TUINT8): - case CASE(TFLOAT64, TINT8): - case CASE(TFLOAT64, TUINT16): - case CASE(TFLOAT64, TUINT8): - // convert via int32. - tempname(&t1, types[TINT32]); - gmove(f, &t1); - switch(tt) { - default: - fatal("gmove %T", t); - case TINT8: - gins(ACMPL, &t1, ncon(-0x80)); - p1 = gbranch(optoas(OLT, types[TINT32]), T); - gins(ACMPL, &t1, ncon(0x7f)); - p2 = gbranch(optoas(OGT, types[TINT32]), T); - p3 = gbranch(AJMP, T); - patch(p1, pc); - patch(p2, pc); - gmove(ncon(-0x80), &t1); - patch(p3, pc); - gmove(&t1, t); - break; - case TUINT8: - gins(ATESTL, ncon(0xffffff00), &t1); - p1 = gbranch(AJEQ, T); - gins(AMOVL, ncon(0), &t1); - patch(p1, pc); - gmove(&t1, t); - break; - case TUINT16: - gins(ATESTL, ncon(0xffff0000), &t1); - p1 = gbranch(AJEQ, T); - gins(AMOVL, ncon(0), &t1); - patch(p1, pc); - gmove(&t1, t); - break; - } - return; - - case CASE(TFLOAT32, TUINT32): - case CASE(TFLOAT64, TUINT32): - // convert via int64. - tempname(&t1, types[TINT64]); - gmove(f, &t1); - split64(&t1, &tlo, &thi); - gins(ACMPL, &thi, ncon(0)); - p1 = gbranch(AJEQ, T); - gins(AMOVL, ncon(0), &tlo); - patch(p1, pc); - gmove(&tlo, t); - splitclean(); - return; - case CASE(TFLOAT32, TUINT64): case CASE(TFLOAT64, TUINT64): + if(!ismem(f)) { + cvt = f->type; + goto hardmem; + } bignodes(); nodreg(&f0, types[ft], D_F0); nodreg(&f1, types[ft], D_F0 + 1); nodreg(&ax, types[TUINT16], D_AX); - gmove(f, &f0); + if(ft == TFLOAT32) + gins(AFMOVF, f, &f0); + else + gins(AFMOVD, f, &f0); // if 0 > v { answer = 0 } - gmove(&zerof, &f0); + gins(AFMOVD, &zerof, &f0); gins(AFUCOMIP, &f0, &f1); - p1 = gbranch(optoas(OGT, types[tt]), T); + p1 = gbranch(optoas(OGT, types[tt]), T, 0); // if 1<<64 <= v { answer = 0 too } - gmove(&two64f, &f0); + gins(AFMOVD, &two64f, &f0); gins(AFUCOMIP, &f0, &f1); - p2 = gbranch(optoas(OGT, types[tt]), T); + p2 = gbranch(optoas(OGT, types[tt]), T, 0); patch(p1, pc); gins(AFMOVVP, &f0, t); // don't care about t, but will pop the stack split64(t, &tlo, &thi); gins(AMOVL, ncon(0), &tlo); gins(AMOVL, ncon(0), &thi); splitclean(); - p1 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); patch(p2, pc); // in range; algorithm is: @@ -1508,13 +1630,13 @@ gmove(Node *f, Node *t) gins(AFLDCW, &t2, N); // actual work - gmove(&two63f, &f0); + gins(AFMOVD, &two63f, &f0); gins(AFUCOMIP, &f0, &f1); - p2 = gbranch(optoas(OLE, types[tt]), T); + p2 = gbranch(optoas(OLE, types[tt]), T, 0); gins(AFMOVVP, &f0, t); - p3 = gbranch(AJMP, T); + p3 = gbranch(AJMP, T, 0); patch(p2, pc); - gmove(&two63f, &f0); + gins(AFMOVD, &two63f, &f0); gins(AFSUBDP, &f0, &f1); gins(AFMOVVP, &f0, t); split64(t, &tlo, &thi); @@ -1530,51 +1652,22 @@ gmove(Node *f, Node *t) /* * integer to float */ - case CASE(TINT16, TFLOAT32): - case CASE(TINT16, TFLOAT64): - case CASE(TINT32, TFLOAT32): - case CASE(TINT32, TFLOAT64): case CASE(TINT64, TFLOAT32): case CASE(TINT64, TFLOAT64): - if(t->op != OREGISTER) - goto hard; - if(f->op == OREGISTER) { - cvt = f->type; + if(t->op == OREGISTER) goto hardmem; - } - switch(ft) { - case TINT16: - a = AFMOVW; - break; - case TINT32: - a = AFMOVL; - break; - default: - a = AFMOVV; - break; - } - break; - - case CASE(TINT8, TFLOAT32): - case CASE(TINT8, TFLOAT64): - case CASE(TUINT16, TFLOAT32): - case CASE(TUINT16, TFLOAT64): - case CASE(TUINT8, TFLOAT32): - case CASE(TUINT8, TFLOAT64): - // convert via int32 memory - cvt = types[TINT32]; - goto hardmem; - - case CASE(TUINT32, TFLOAT32): - case CASE(TUINT32, TFLOAT64): - // convert via int64 memory - cvt = types[TINT64]; - goto hardmem; + nodreg(&f0, t->type, D_F0); + gins(AFMOVV, f, &f0); + if(tt == TFLOAT32) + gins(AFMOVFP, &f0, t); + else + gins(AFMOVDP, &f0, t); + return; case CASE(TUINT64, TFLOAT32): case CASE(TUINT64, TFLOAT64): // algorithm is: - // if small enough, use native int64 -> uint64 conversion. + // if small enough, use native int64 -> float64 conversion. // otherwise, halve (rounding to odd?), convert, and double. nodreg(&ax, types[TUINT32], D_AX); nodreg(&dx, types[TUINT32], D_DX); @@ -1583,11 +1676,16 @@ gmove(Node *f, Node *t) split64(&t1, &tlo, &thi); gmove(f, &t1); gins(ACMPL, &thi, ncon(0)); - p1 = gbranch(AJLT, T); + p1 = gbranch(AJLT, T, 0); // native t1.type = types[TINT64]; - gmove(&t1, t); - p2 = gbranch(AJMP, T); + nodreg(&r1, types[tt], D_F0); + gins(AFMOVV, &t1, &r1); + if(tt == TFLOAT32) + gins(AFMOVFP, &r1, t); + else + gins(AFMOVDP, &r1, t); + p2 = gbranch(AJMP, T, 0); // simulated patch(p1, pc); gmove(&tlo, &ax); @@ -1595,20 +1693,182 @@ gmove(Node *f, Node *t) p1 = gins(ASHRL, ncon(1), &ax); p1->from.index = D_DX; // double-width shift DX -> AX p1->from.scale = 0; + gins(AMOVL, ncon(0), &cx); gins(ASETCC, N, &cx); - gins(AORB, &cx, &ax); + gins(AORL, &cx, &ax); gins(ASHRL, ncon(1), &dx); gmove(&dx, &thi); gmove(&ax, &tlo); nodreg(&r1, types[tt], D_F0); nodreg(&r2, types[tt], D_F0 + 1); - gmove(&t1, &r1); // t1.type is TINT64 now, set above + gins(AFMOVV, &t1, &r1); gins(AFMOVD, &r1, &r1); gins(AFADDDP, &r1, &r2); - gmove(&r1, t); + if(tt == TFLOAT32) + gins(AFMOVFP, &r1, t); + else + gins(AFMOVDP, &r1, t); patch(p2, pc); splitclean(); return; + } + +hard: + // requires register intermediate + regalloc(&r1, cvt, t); + gmove(f, &r1); + gmove(&r1, t); + regfree(&r1); + return; + +hardmem: + // requires memory intermediate + tempname(&r1, cvt); + gmove(f, &r1); + gmove(&r1, t); + return; +} + +static void +floatmove_387(Node *f, Node *t) +{ + Node r1, t1, t2; + Type *cvt; + Prog *p1, *p2, *p3; + int a, ft, tt; + + ft = simsimtype(f->type); + tt = simsimtype(t->type); + cvt = t->type; + + switch(CASE(ft, tt)) { + default: + goto fatal; + + /* + * float to integer + */ + case CASE(TFLOAT32, TINT16): + case CASE(TFLOAT32, TINT32): + case CASE(TFLOAT32, TINT64): + case CASE(TFLOAT64, TINT16): + case CASE(TFLOAT64, TINT32): + case CASE(TFLOAT64, TINT64): + if(t->op == OREGISTER) + goto hardmem; + nodreg(&r1, types[ft], D_F0); + if(f->op != OREGISTER) { + if(ft == TFLOAT32) + gins(AFMOVF, f, &r1); + else + gins(AFMOVD, f, &r1); + } + + // set round to zero mode during conversion + memname(&t1, types[TUINT16]); + memname(&t2, types[TUINT16]); + gins(AFSTCW, N, &t1); + gins(AMOVW, ncon(0xf7f), &t2); + gins(AFLDCW, &t2, N); + if(tt == TINT16) + gins(AFMOVWP, &r1, t); + else if(tt == TINT32) + gins(AFMOVLP, &r1, t); + else + gins(AFMOVVP, &r1, t); + gins(AFLDCW, &t1, N); + return; + + case CASE(TFLOAT32, TINT8): + case CASE(TFLOAT32, TUINT16): + case CASE(TFLOAT32, TUINT8): + case CASE(TFLOAT64, TINT8): + case CASE(TFLOAT64, TUINT16): + case CASE(TFLOAT64, TUINT8): + // convert via int32. + tempname(&t1, types[TINT32]); + gmove(f, &t1); + switch(tt) { + default: + fatal("gmove %T", t); + case TINT8: + gins(ACMPL, &t1, ncon(-0x80)); + p1 = gbranch(optoas(OLT, types[TINT32]), T, -1); + gins(ACMPL, &t1, ncon(0x7f)); + p2 = gbranch(optoas(OGT, types[TINT32]), T, -1); + p3 = gbranch(AJMP, T, 0); + patch(p1, pc); + patch(p2, pc); + gmove(ncon(-0x80), &t1); + patch(p3, pc); + gmove(&t1, t); + break; + case TUINT8: + gins(ATESTL, ncon(0xffffff00), &t1); + p1 = gbranch(AJEQ, T, +1); + gins(AMOVL, ncon(0), &t1); + patch(p1, pc); + gmove(&t1, t); + break; + case TUINT16: + gins(ATESTL, ncon(0xffff0000), &t1); + p1 = gbranch(AJEQ, T, +1); + gins(AMOVL, ncon(0), &t1); + patch(p1, pc); + gmove(&t1, t); + break; + } + return; + + case CASE(TFLOAT32, TUINT32): + case CASE(TFLOAT64, TUINT32): + // convert via int64. + cvt = types[TINT64]; + goto hardmem; + + /* + * integer to float + */ + case CASE(TINT16, TFLOAT32): + case CASE(TINT16, TFLOAT64): + case CASE(TINT32, TFLOAT32): + case CASE(TINT32, TFLOAT64): + case CASE(TINT64, TFLOAT32): + case CASE(TINT64, TFLOAT64): + if(t->op != OREGISTER) + goto hard; + if(f->op == OREGISTER) { + cvt = f->type; + goto hardmem; + } + switch(ft) { + case TINT16: + a = AFMOVW; + break; + case TINT32: + a = AFMOVL; + break; + default: + a = AFMOVV; + break; + } + break; + + case CASE(TINT8, TFLOAT32): + case CASE(TINT8, TFLOAT64): + case CASE(TUINT16, TFLOAT32): + case CASE(TUINT16, TFLOAT64): + case CASE(TUINT8, TFLOAT32): + case CASE(TUINT8, TFLOAT64): + // convert via int32 memory + cvt = types[TINT32]; + goto hardmem; + + case CASE(TUINT32, TFLOAT32): + case CASE(TUINT32, TFLOAT64): + // convert via int64 memory + cvt = types[TINT64]; + goto hardmem; /* * float to float @@ -1673,20 +1933,121 @@ gmove(Node *f, Node *t) gins(a, f, t); return; -rsrc: - // requires register source - regalloc(&r1, f->type, t); +hard: + // requires register intermediate + regalloc(&r1, cvt, t); gmove(f, &r1); - gins(a, &r1, t); + gmove(&r1, t); regfree(&r1); return; -rdst: - // requires register destination - regalloc(&r1, t->type, t); - gins(a, f, &r1); +hardmem: + // requires memory intermediate + tempname(&r1, cvt); + gmove(f, &r1); gmove(&r1, t); - regfree(&r1); + return; + +fatal: + // should not happen + fatal("gmove %lN -> %lN", f, t); + return; +} + +static void +floatmove_sse(Node *f, Node *t) +{ + Node r1; + Type *cvt; + int a, ft, tt; + + ft = simsimtype(f->type); + tt = simsimtype(t->type); + + switch(CASE(ft, tt)) { + default: + // should not happen + fatal("gmove %N -> %N", f, t); + return; + /* + * float to integer + */ + case CASE(TFLOAT32, TINT16): + case CASE(TFLOAT32, TINT8): + case CASE(TFLOAT32, TUINT16): + case CASE(TFLOAT32, TUINT8): + case CASE(TFLOAT64, TINT16): + case CASE(TFLOAT64, TINT8): + case CASE(TFLOAT64, TUINT16): + case CASE(TFLOAT64, TUINT8): + // convert via int32. + cvt = types[TINT32]; + goto hard; + + case CASE(TFLOAT32, TUINT32): + case CASE(TFLOAT64, TUINT32): + // convert via int64. + cvt = types[TINT64]; + goto hardmem; + + case CASE(TFLOAT32, TINT32): + a = ACVTTSS2SL; + goto rdst; + + case CASE(TFLOAT64, TINT32): + a = ACVTTSD2SL; + goto rdst; + + /* + * integer to float + */ + case CASE(TINT8, TFLOAT32): + case CASE(TINT8, TFLOAT64): + case CASE(TINT16, TFLOAT32): + case CASE(TINT16, TFLOAT64): + case CASE(TUINT16, TFLOAT32): + case CASE(TUINT16, TFLOAT64): + case CASE(TUINT8, TFLOAT32): + case CASE(TUINT8, TFLOAT64): + // convert via int32 memory + cvt = types[TINT32]; + goto hard; + + case CASE(TUINT32, TFLOAT32): + case CASE(TUINT32, TFLOAT64): + // convert via int64 memory + cvt = types[TINT64]; + goto hardmem; + + case CASE(TINT32, TFLOAT32): + a = ACVTSL2SS; + goto rdst; + + case CASE(TINT32, TFLOAT64): + a = ACVTSL2SD; + goto rdst; + + /* + * float to float + */ + case CASE(TFLOAT32, TFLOAT32): + a = AMOVSS; + break; + + case CASE(TFLOAT64, TFLOAT64): + a = AMOVSD; + break; + + case CASE(TFLOAT32, TFLOAT64): + a = ACVTSS2SD; + goto rdst; + + case CASE(TFLOAT64, TFLOAT32): + a = ACVTSD2SS; + goto rdst; + } + + gins(a, f, t); return; hard: @@ -1704,9 +2065,13 @@ hardmem: gmove(&r1, t); return; -fatal: - // should not happen - fatal("gmove %N -> %N", f, t); +rdst: + // requires register destination + regalloc(&r1, t->type, t); + gins(a, f, &r1); + gmove(&r1, t); + regfree(&r1); + return; } int @@ -1737,6 +2102,10 @@ gins(int as, Node *f, Node *t) if(as == AFMOVF && f && f->op == OREGISTER && t && t->op == OREGISTER) fatal("gins MOVF reg, reg"); + if(as == ACVTSD2SS && f && f->op == OLITERAL) + fatal("gins CVTSD2SS const"); + if(as == AMOVSD && t && t->op == OREGISTER && t->val.u.reg == D_F0) + fatal("gins MOVSD into F0"); switch(as) { case AMOVB: @@ -1744,6 +2113,12 @@ gins(int as, Node *f, Node *t) case AMOVL: if(f != N && t != N && samaddr(f, t)) return nil; + break; + + case ALEAL: + if(f != N && isconst(f, CTNIL)) + fatal("gins LEAL nil %T", f->type); + break; } memset(&af, 0, sizeof af); @@ -1782,6 +2157,25 @@ gins(int as, Node *f, Node *t) return p; } +// Generate an instruction referencing *n +// to force segv on nil pointer dereference. +void +checkref(Node *n) +{ + Node m; + + if(n->type->type->width < unmappedzero) + return; + + regalloc(&m, types[TUINTPTR], n); + cgen(n, &m); + m.xoffset = 0; + m.op = OINDREG; + m.type = types[TUINT8]; + gins(ATESTB, nodintconst(0), &m); + regfree(&m); +} + static void checkoffset(Addr *a, int canemitcode) { @@ -1807,6 +2201,8 @@ checkoffset(Addr *a, int canemitcode) void naddr(Node *n, Addr *a, int canemitcode) { + Prog *p; + a->scale = 0; a->index = D_NONE; a->type = D_NONE; @@ -1842,13 +2238,29 @@ naddr(Node *n, Addr *a, int canemitcode) a->node = n->left->orig; break; + case OCLOSUREVAR: + if(!canemitcode) + fatal("naddr OCLOSUREVAR cannot emit code"); + p = gins(AMOVL, N, N); + p->from.type = D_DX+D_INDIR; + p->from.offset = n->xoffset; + p->to.type = D_BX; + a->type = D_BX; + a->sym = S; + break; + + case OCFUNC: + naddr(n->left, a, canemitcode); + a->sym = n->left->sym; + break; + case ONAME: a->etype = 0; a->width = 0; if(n->type != T) { a->etype = simtype[n->type->etype]; + dowidth(n->type); a->width = n->type->width; - a->gotype = ngotype(n); } a->offset = n->xoffset; a->sym = n->sym; @@ -1880,6 +2292,7 @@ naddr(Node *n, Addr *a, int canemitcode) case PFUNC: a->index = D_EXTERN; a->type = D_ADDR; + a->sym = funcsym(a->sym); break; } break; @@ -1891,7 +2304,7 @@ naddr(Node *n, Addr *a, int canemitcode) break; case CTFLT: a->type = D_FCONST; - a->dval = mpgetflt(n->val.u.fval); + a->u.dval = mpgetflt(n->val.u.fval); break; case CTINT: case CTRUNE: diff --git a/src/cmd/8g/list.c b/src/cmd/8g/list.c index 88d3d5f7e..ec02ba5c5 100644 --- a/src/cmd/8g/list.c +++ b/src/cmd/8g/list.c @@ -107,7 +107,7 @@ Dconv(Fmt *fp) break; case D_BRANCH: - snprint(str, sizeof(str), "%d", a->branch->loc); + snprint(str, sizeof(str), "%d", a->u.branch->loc); break; case D_EXTERN: @@ -137,11 +137,11 @@ Dconv(Fmt *fp) break; case D_FCONST: - snprint(str, sizeof(str), "$(%.17e)", a->dval); + snprint(str, sizeof(str), "$(%.17e)", a->u.dval); break; case D_SCONST: - snprint(str, sizeof(str), "$\"%Y\"", a->sval); + snprint(str, sizeof(str), "$\"%Y\"", a->u.sval); break; case D_ADDR: @@ -158,7 +158,10 @@ brk: strcat(str, s); } conv: - return fmtstrcpy(fp, str); + fmtstrcpy(fp, str); + if(a->gotype) + fmtprint(fp, "{%s}", a->gotype->name); + return 0; } static char* regstr[] = @@ -231,6 +234,15 @@ static char* regstr[] = "TR6", "TR7", + "X0", /* [D_X0] */ + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", + "NONE", /* [D_NONE] */ }; diff --git a/src/cmd/8g/opt.h b/src/cmd/8g/opt.h index ed6eb15ab..b80043e0f 100644 --- a/src/cmd/8g/opt.h +++ b/src/cmd/8g/opt.h @@ -47,12 +47,16 @@ typedef struct Reg Reg; typedef struct Rgn Rgn; +// A Reg is a wrapper around a single Prog (one instruction) that holds +// register optimization information while the optimizer runs. +// r->prog is the instruction. +// r->prog->regp points back to r. struct Reg { - Bits set; - Bits use1; - Bits use2; + Bits set; // variables written by this instruction. + Bits use1; // variables read by prog->from. + Bits use2; // variables read by prog->to. Bits refbehind; Bits refahead; @@ -68,13 +72,13 @@ struct Reg uint16 loop; // x5 for every loop uchar refset; // diagnostic generated - Reg* p1; - Reg* p2; + Reg* p1; // predecessors of this instruction: p1, + Reg* p2; // and then p2 linked though p2link. Reg* p2link; - Reg* s1; + Reg* s1; // successors of this instruction (at most two: s1 and s2). Reg* s2; - Reg* link; - Prog* prog; + Reg* link; // next instruction in function code + Prog* prog; // actual instruction }; #define R ((Reg*)0) diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c index b8a2825e5..e5a3149cf 100644 --- a/src/cmd/8g/peep.c +++ b/src/cmd/8g/peep.c @@ -36,6 +36,7 @@ #define REGEXT 0 static void conprop(Reg *r); +static void elimshortmov(Reg *r); // do we need the carry bit static int @@ -45,9 +46,15 @@ needc(Prog *p) switch(p->as) { case AADCL: case ASBBL: + case ARCRB: + case ARCRW: case ARCRL: return 1; + case AADDB: + case AADDW: case AADDL: + case ASUBB: + case ASUBW: case ASUBL: case AJMP: case ARET: @@ -119,28 +126,14 @@ peep(void) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: p = p->link; } } - - // movb elimination. - // movb is simulated by the linker - // when a register other than ax, bx, cx, dx - // is used, so rewrite to other instructions - // when possible. a movb into a register - // can smash the entire 32-bit register without - // causing any trouble. - for(r=firstr; r!=R; r=r->link) { - p = r->prog; - if(p->as == AMOVB && regtyp(&p->to)) { - // movb into register. - // from another register or constant can be movl. - if(regtyp(&p->from) || p->from.type == D_CONST) - p->as = AMOVL; - else - p->as = AMOVBLZX; - } - } + + // byte, word arithmetic elimination. + elimshortmov(r); // constant propagation // find MOV $con,R followed by @@ -158,6 +151,8 @@ peep(void) case AMOVB: case AMOVW: case AMOVL: + case AMOVSS: + case AMOVSD: if(regtyp(&p->to)) if(p->from.type == D_CONST) conprop(r); @@ -173,9 +168,9 @@ loop1: for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { - case AMOVB: - case AMOVW: case AMOVL: + case AMOVSS: + case AMOVSD: if(regtyp(&p->to)) if(regtyp(&p->from)) { if(copyprop(r)) { @@ -205,7 +200,6 @@ loop1: } break; - case AADDB: case AADDL: case AADDW: if(p->from.type != D_CONST || needc(p->link)) @@ -228,7 +222,6 @@ loop1: } break; - case ASUBB: case ASUBL: case ASUBW: if(p->from.type != D_CONST || needc(p->link)) @@ -254,6 +247,19 @@ loop1: } if(t) goto loop1; + + // MOVSD removal. + // We never use packed registers, so a MOVSD between registers + // can be replaced by MOVAPD, which moves the pair of float64s + // instead of just the lower one. We only use the lower one, but + // the processor can do better if we do moves using both. + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(p->as == AMOVSD) + if(regtyp(&p->from)) + if(regtyp(&p->to)) + p->as = AMOVAPD; + } } void @@ -312,9 +318,104 @@ regtyp(Adr *a) t = a->type; if(t >= D_AX && t <= D_DI) return 1; + if(t >= D_X0 && t <= D_X7) + return 1; return 0; } +// movb elimination. +// movb is simulated by the linker +// when a register other than ax, bx, cx, dx +// is used, so rewrite to other instructions +// when possible. a movb into a register +// can smash the entire 64-bit register without +// causing any trouble. +static void +elimshortmov(Reg *r) +{ + Prog *p; + + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(regtyp(&p->to)) { + switch(p->as) { + case AINCB: + case AINCW: + p->as = AINCL; + break; + case ADECB: + case ADECW: + p->as = ADECL; + break; + case ANEGB: + case ANEGW: + p->as = ANEGL; + break; + case ANOTB: + case ANOTW: + p->as = ANOTL; + break; + } + if(regtyp(&p->from) || p->from.type == D_CONST) { + // move or artihmetic into partial register. + // from another register or constant can be movl. + // we don't switch to 32-bit arithmetic if it can + // change how the carry bit is set (and the carry bit is needed). + switch(p->as) { + case AMOVB: + case AMOVW: + p->as = AMOVL; + break; + case AADDB: + case AADDW: + if(!needc(p->link)) + p->as = AADDL; + break; + case ASUBB: + case ASUBW: + if(!needc(p->link)) + p->as = ASUBL; + break; + case AMULB: + case AMULW: + p->as = AMULL; + break; + case AIMULB: + case AIMULW: + p->as = AIMULL; + break; + case AANDB: + case AANDW: + p->as = AANDL; + break; + case AORB: + case AORW: + p->as = AORL; + break; + case AXORB: + case AXORW: + p->as = AXORL; + break; + case ASHLB: + case ASHLW: + p->as = ASHLL; + break; + } + } else { + // explicit zero extension + switch(p->as) { + case AMOVB: + p->as = AMOVBLZX; + break; + case AMOVW: + p->as = AMOVWLZX; + break; + } + } + } + } +} + /* * the idea is to substitute * one register for another @@ -357,17 +458,6 @@ subprop(Reg *r0) if(p->to.type != D_NONE) break; - case ADIVB: - case ADIVL: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULW: - case ARCLB: case ARCLL: case ARCLW: @@ -392,6 +482,19 @@ subprop(Reg *r0) case ASHRB: case ASHRL: case ASHRW: + if(p->from.type == D_CONST) + break; + + case ADIVB: + case ADIVL: + case ADIVW: + case AIDIVB: + case AIDIVL: + case AIDIVW: + case AIMULB: + case AMULB: + case AMULL: + case AMULW: case AREP: case AREPN: @@ -403,11 +506,16 @@ subprop(Reg *r0) case ASTOSL: case AMOVSB: case AMOVSL: + + case AFMOVF: + case AFMOVD: + case AFMOVFP: + case AFMOVDP: return 0; - case AMOVB: - case AMOVW: case AMOVL: + case AMOVSS: + case AMOVSD: if(p->to.type == v1->type) goto gotit; break; @@ -587,13 +695,22 @@ copyu(Prog *p, Adr *v, Adr *s) case ANOP: /* rhs store */ - case AMOVB: - case AMOVW: case AMOVL: case AMOVBLSX: case AMOVBLZX: case AMOVWLSX: case AMOVWLZX: + + case AMOVSS: + case AMOVSD: + case ACVTSD2SL: + case ACVTSD2SS: + case ACVTSL2SD: + case ACVTSL2SS: + case ACVTSS2SD: + case ACVTSS2SL: + case ACVTTSD2SL: + case ACVTTSS2SL: if(copyas(&p->to, v)) { if(s != A) return copysub(&p->from, v, s, 1); @@ -653,6 +770,28 @@ copyu(Prog *p, Adr *v, Adr *s) case AXORB: case AXORL: case AXORW: + case AMOVB: + case AMOVW: + + case AADDSD: + case AADDSS: + case ACMPSD: + case ACMPSS: + case ADIVSD: + case ADIVSS: + case AMAXSD: + case AMAXSS: + case AMINSD: + case AMINSS: + case AMULSD: + case AMULSS: + case ARCPSS: + case ARSQRTSS: + case ASQRTSD: + case ASQRTSS: + case ASUBSD: + case ASUBSS: + case AXORPD: if(copyas(&p->to, v)) return 2; goto caseread; @@ -660,6 +799,11 @@ copyu(Prog *p, Adr *v, Adr *s) case ACMPL: /* read only */ case ACMPW: case ACMPB: + + case ACOMISD: + case ACOMISS: + case AUCOMISD: + case AUCOMISS: caseread: if(s != A) { if(copysub(&p->from, v, s, 1)) @@ -744,8 +888,6 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; case ARET: /* funny */ - if(v->type == REGRET || v->type == FREGRET) - return 2; if(s != A) return 1; return 3; @@ -755,6 +897,8 @@ copyu(Prog *p, Adr *v, Adr *s) return 2; if(REGARG >= 0 && v->type == (uchar)REGARG) return 2; + if(v->type == p->from.type) + return 2; if(s != A) { if(copysub(&p->to, v, s, 1)) @@ -820,7 +964,7 @@ copysub(Adr *a, Adr *v, Adr *s, int f) if(copyas(a, v)) { t = s->type; - if(t >= D_AX && t <= D_DI) { + if(t >= D_AX && t <= D_DI || t >= D_X0 && t <= D_X7) { if(f) a->type = t; } @@ -881,7 +1025,7 @@ loop: if(p->from.node == p0->from.node) if(p->from.offset == p0->from.offset) if(p->from.scale == p0->from.scale) - if(p->from.dval == p0->from.dval) + if(p->from.u.vval == p0->from.u.vval) if(p->from.index == p0->from.index) { excise(r); goto loop; diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 45ffdf96c..985f6ccbc 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -33,13 +33,14 @@ #include "gg.h" #include "opt.h" -#define NREGVAR 8 -#define REGBITS ((uint32)0xff) +#define NREGVAR 16 /* 8 integer + 8 floating */ +#define REGBITS ((uint32)0xffff) #define P2R(p) (Reg*)(p->reg) static int first = 1; static void fixjmp(Prog*); +static void fixtemp(Prog*); Reg* rega(void) @@ -118,7 +119,12 @@ setaddrs(Bits bit) } } -static char* regname[] = { ".ax", ".cx", ".dx", ".bx", ".sp", ".bp", ".si", ".di" }; +static char* regname[] = { + ".ax", ".cx", ".dx", ".bx", ".sp", ".bp", ".si", ".di", + ".x0", ".x1", ".x2", ".x3", ".x4", ".x5", ".x6", ".x7", +}; + +static Node* regnodes[NREGVAR]; void regopt(Prog *firstp) @@ -135,6 +141,7 @@ regopt(Prog *firstp) first = 0; } + fixtemp(firstp); fixjmp(firstp); // count instructions @@ -147,7 +154,6 @@ regopt(Prog *firstp) return; } - r1 = R; firstr = R; lastr = R; @@ -158,8 +164,11 @@ regopt(Prog *firstp) */ nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; ias == ACALL && p->to.type == D_EXTERN) + continue; + + // Addressing makes some registers used. + if(p->from.type >= D_INDIR) + r->use1.b[0] |= RtoB(p->from.type-D_INDIR); + if(p->from.index != D_NONE) + r->use1.b[0] |= RtoB(p->from.index); + if(p->to.type >= D_INDIR) + r->use2.b[0] |= RtoB(p->to.type-D_INDIR); + if(p->to.index != D_NONE) + r->use2.b[0] |= RtoB(p->to.index); + bit = mkvar(r, &p->from); if(bany(&bit)) switch(p->as) { @@ -220,6 +245,8 @@ regopt(Prog *firstp) * funny */ case ALEAL: + case AFMOVD: + case AFMOVF: case AFMOVL: case AFMOVW: case AFMOVV: @@ -260,6 +287,10 @@ regopt(Prog *firstp) case ACMPB: case ACMPL: case ACMPW: + case ACOMISS: + case ACOMISD: + case AUCOMISS: + case AUCOMISD: case ATESTB: case ATESTL: case ATESTW: @@ -283,6 +314,17 @@ regopt(Prog *firstp) case AMOVWLSX: case AMOVWLZX: case APOPL: + + case AMOVSS: + case AMOVSD: + case ACVTSD2SL: + case ACVTSD2SS: + case ACVTSL2SD: + case ACVTSL2SS: + case ACVTSS2SD: + case ACVTSS2SL: + case ACVTTSD2SL: + case ACVTTSS2SL: for(z=0; zset.b[z] |= bit.b[z]; break; @@ -367,6 +409,26 @@ regopt(Prog *firstp) case AXCHGB: case AXCHGW: case AXCHGL: + + case AADDSD: + case AADDSS: + case ACMPSD: + case ACMPSS: + case ADIVSD: + case ADIVSS: + case AMAXSD: + case AMAXSS: + case AMINSD: + case AMINSS: + case AMULSD: + case AMULSS: + case ARCPSS: + case ARSQRTSS: + case ASQRTSD: + case ASQRTSS: + case ASUBSD: + case ASUBSS: + case AXORPD: for(z=0; zset.b[z] |= bit.b[z]; r->use2.b[z] |= bit.b[z]; @@ -475,8 +537,9 @@ regopt(Prog *firstp) addrs.b[z] |= bit.b[z]; } -// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n", -// i, v->addr, v->etype, v->width, v->sym, v->offset); + if(debug['R'] && debug['v']) + print("bit=%2d addr=%d et=%-6E w=%-2d s=%N + %lld\n", + i, v->addr, v->etype, v->width, v->node, v->offset); } if(debug['R'] && debug['v']) @@ -490,9 +553,9 @@ regopt(Prog *firstp) for(r=firstr; r!=R; r=r->link) { p = r->prog; if(p->to.type == D_BRANCH) { - if(p->to.branch == P) + if(p->to.u.branch == P) fatal("pnil %P", p); - r1 = p->to.branch->reg; + r1 = p->to.u.branch->reg; if(r1 == R) fatal("rnil %P", p); if(r1 == r) { @@ -673,8 +736,16 @@ brk: while(p->link != P && p->link->as == ANOP) p->link = p->link->link; if(p->to.type == D_BRANCH) - while(p->to.branch != P && p->to.branch->as == ANOP) - p->to.branch = p->to.branch->link; + while(p->to.u.branch != P && p->to.u.branch->as == ANOP) + p->to.u.branch = p->to.u.branch->link; + } + + if(!use_sse) + for(p=firstp; p!=P; p=p->link) { + if(p->from.type >= D_X0 && p->from.type <= D_X7) + fatal("invalid use of %R with GO386=387: %P", p->from.type, p); + if(p->to.type >= D_X0 && p->to.type <= D_X7) + fatal("invalid use of %R with GO386=387: %P", p->to.type, p); } if(lastr != R) { @@ -735,7 +806,6 @@ addmove(Reg *r, int bn, int rn, int f) a->offset = v->offset; a->etype = v->etype; a->type = v->name; - a->gotype = v->gotype; a->node = v->node; a->sym = v->node->sym; @@ -754,6 +824,12 @@ addmove(Reg *r, int bn, int rn, int f) case TUINT16: p1->as = AMOVW; break; + case TFLOAT32: + p1->as = AMOVSS; + break; + case TFLOAT64: + p1->as = AMOVSD; + break; case TINT: case TUINT: case TINT32: @@ -793,6 +869,9 @@ doregbits(int r) else if(r >= D_AH && r <= D_BH) b |= RtoB(r-D_AH+D_AX); + else + if(r >= D_X0 && r <= D_X0+7) + b |= FtoB(r); return b; } @@ -865,6 +944,8 @@ mkvar(Reg *r, Adr *a) et = a->etype; o = a->offset; w = a->width; + if(w < 0) + fatal("bad width %d for %D", w, a); flag = 0; for(i=0; ioffset = o; v->name = n; - v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); + print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); ostats.nvar++; bit = blsh(i); @@ -972,8 +1052,12 @@ prop(Reg *r, Bits ref, Bits cal) default: // Work around for issue 1304: // flush modified globals before each instruction. - for(z=0; zcost > 0) { + r->regno = i; + return FtoB(i); + } break; } return 0; @@ -1279,7 +1370,7 @@ regset(Reg *r, uint32 bb) set = 0; v = zprog.from; while(b = bb & ~(bb-1)) { - v.type = BtoR(b); + v.type = b & 0xFF ? BtoR(b): BtoF(b); c = copyu(r->prog, &v, A); if(c == 3) set |= b; @@ -1298,7 +1389,7 @@ reguse(Reg *r, uint32 bb) set = 0; v = zprog.from; while(b = bb & ~(bb-1)) { - v.type = BtoR(b); + v.type = b & 0xFF ? BtoR(b): BtoF(b); c = copyu(r->prog, &v, A); if(c == 1 || c == 2 || c == 4) set |= b; @@ -1468,6 +1559,23 @@ BtoR(int32 b) return bitno(b) + D_AX; } +int32 +FtoB(int f) +{ + if(f < D_X0 || f > D_X7) + return 0; + return 1L << (f - D_X0 + 8); +} + +int +BtoF(int32 b) +{ + b &= 0xFF00L; + if(b == 0) + return 0; + return bitno(b) - 8 + D_X0; +} + void dumpone(Reg *r) { @@ -1581,7 +1689,7 @@ chasejmp(Prog *p, int *jmploop) *jmploop = 1; break; } - p = p->to.branch; + p = p->to.u.branch; } return p; } @@ -1603,9 +1711,9 @@ mark(Prog *firstp) if(p->reg != dead) break; p->reg = alive; - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.branch) - mark(p->to.branch); - if(p->as == AJMP || p->as == ARET || (p->as == ACALL && noreturn(p))) + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch) + mark(p->to.u.branch); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) break; } } @@ -1624,8 +1732,8 @@ fixjmp(Prog *firstp) for(p=firstp; p; p=p->link) { if(debug['R'] && debug['v']) print("%P\n", p); - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.branch && p->to.branch->as == AJMP) { - p->to.branch = chasejmp(p->to.branch, &jmploop); + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) { + p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); if(debug['R'] && debug['v']) print("->%P\n", p); } @@ -1661,7 +1769,7 @@ fixjmp(Prog *firstp) if(!jmploop) { last = nil; for(p=firstp; p; p=p->link) { - if(p->as == AJMP && p->to.type == D_BRANCH && p->to.branch == p->link) { + if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) { if(debug['R'] && debug['v']) print("del %P\n", p); continue; @@ -1680,3 +1788,131 @@ fixjmp(Prog *firstp) print("\n"); } } + +static uint32 +fnv1(Sym *sym) +{ + uint32 h; + char *s; + + h = 2166136261U; + for(s=sym->name;*s;s++) { + h = (16777619 * h) ^ (uint32)(uint8)(*s); + } + return h; +} + +static uint16 +hash32to16(uint32 h) +{ + return (h & 0xffff) ^ (h >> 16); +} + +/* + * fixtemp eliminates sequences like: + * MOV reg1, mem + * OP mem, reg2 + * when mem is a stack variable which is not mentioned + * anywhere else. The instructions are replaced by + * OP reg1, reg2 + * this reduces the number of variables that the register optimizer + * sees, which lets it do a better job and makes it less likely to turn + * itself off. + */ +static void +fixtemp(Prog *firstp) +{ + static uint8 counts[1<<16]; // A hash table to count variable occurences. + int i; + Prog *p, *p2; + uint32 h; + + if(debug['R'] && debug['v']) + print("\nfixtemp\n"); + + // Count variable references. We actually use a hashtable so this + // is only approximate. + for(i=0; ilink) { + if(p->from.type == D_AUTO) { + h = hash32to16(fnv1(p->from.sym)); + //print("seen %S hash %d\n", p->from.sym, hash32to16(h)); + if(counts[h] < 10) + counts[h]++; + } + if(p->to.type == D_AUTO) { + h = hash32to16(fnv1(p->to.sym)); + //print("seen %S hash %d\n", p->to.sym, hash32to16(h)); + if(counts[h] < 10) + counts[h]++; + } + } + + // Eliminate single-write, single-read stack variables. + for(p=firstp; p!=P; p=p->link) { + if(debug['R'] && debug['v']) + print("%P\n", p); + if(p->link == P || p->to.type != D_AUTO) + continue; + if(isfloat[p->to.etype] && FtoB(p->from.type)) { + switch(p->as) { + case AMOVSS: + case AMOVSD: + break; + default: + continue; + } + } else if(!isfloat[p->to.etype] && RtoB(p->from.type)) { + switch(p->as) { + case AMOVB: + if(p->to.width == 1) + break; + case AMOVW: + if(p->to.width == 2) + break; + case AMOVL: + if(p->to.width == 4) + break; + default: + continue; + } + } else + continue; + // p is a MOV reg, mem. + p2 = p->link; + h = hash32to16(fnv1(p->to.sym)); + if(counts[h] != 2) { + continue; + } + switch(p2->as) { + case ALEAL: + case AFMOVD: + case AFMOVF: + case AFMOVL: + case AFMOVW: + case AFMOVV: + // funny + continue; + } + // p2 is OP mem, reg2 + // and OP is not a funny instruction. + if(p2->from.sym == p->to.sym + && p2->from.offset == p->to.offset + && p2->from.type == p->to.type) { + if(debug['R'] && debug['v']) { + print(" ===elide== %D\n", &p->to); + print("%P", p2); + } + // p2 is OP mem, reg2. + // change to OP reg, reg2 and + // eliminate the mov. + p2->from = p->from; + *p = *p2; + p->link = p2->link; + if(debug['R'] && debug['v']) { + print(" ===change== %P\n", p); + } + } + } +} diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index 9d2751cf0..386889956 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -451,6 +451,125 @@ enum as ASFENCE, AEMMS, + + APREFETCHT0, + APREFETCHT1, + APREFETCHT2, + APREFETCHNTA, + + ABSWAPL, + + AUNDEF, + + // SSE2 + AADDPD, + AADDPS, + AADDSD, + AADDSS, + AANDNPD, + AANDNPS, + AANDPD, + AANDPS, + ACMPPD, + ACMPPS, + ACMPSD, + ACMPSS, + ACOMISD, + ACOMISS, + ACVTPL2PD, + ACVTPL2PS, + ACVTPD2PL, + ACVTPD2PS, + ACVTPS2PL, + ACVTPS2PD, + ACVTSD2SL, + ACVTSD2SS, + ACVTSL2SD, + ACVTSL2SS, + ACVTSS2SD, + ACVTSS2SL, + ACVTTPD2PL, + ACVTTPS2PL, + ACVTTSD2SL, + ACVTTSS2SL, + ADIVPD, + ADIVPS, + ADIVSD, + ADIVSS, + AMASKMOVOU, + AMAXPD, + AMAXPS, + AMAXSD, + AMAXSS, + AMINPD, + AMINPS, + AMINSD, + AMINSS, + AMOVAPD, + AMOVAPS, + AMOVO, + AMOVOU, + AMOVHLPS, + AMOVHPD, + AMOVHPS, + AMOVLHPS, + AMOVLPD, + AMOVLPS, + AMOVMSKPD, + AMOVMSKPS, + AMOVNTO, + AMOVNTPD, + AMOVNTPS, + AMOVSD, + AMOVSS, + AMOVUPD, + AMOVUPS, + AMULPD, + AMULPS, + AMULSD, + AMULSS, + AORPD, + AORPS, + APADDQ, + APMAXSW, + APMAXUB, + APMINSW, + APMINUB, + APSADBW, + APSUBB, + APSUBL, + APSUBQ, + APSUBSB, + APSUBSW, + APSUBUSB, + APSUBUSW, + APSUBW, + APUNPCKHQDQ, + APUNPCKLQDQ, + ARCPPS, + ARCPSS, + ARSQRTPS, + ARSQRTSS, + ASQRTPD, + ASQRTPS, + ASQRTSD, + ASQRTSS, + ASUBPD, + ASUBPS, + ASUBSD, + ASUBSS, + AUCOMISD, + AUCOMISS, + AUNPCKHPD, + AUNPCKHPS, + AUNPCKLPD, + AUNPCKLPS, + AXORPD, + AXORPS, + + AUSEFIELD, + ALOCALS, + ATYPE, ALAST }; @@ -496,17 +615,26 @@ enum D_DR = 43, D_TR = 51, - D_NONE = 59, - - D_BRANCH = 60, - D_EXTERN = 61, - D_STATIC = 62, - D_AUTO = 63, - D_PARAM = 64, - D_CONST = 65, - D_FCONST = 66, - D_SCONST = 67, - D_ADDR = 68, + D_X0 = 59, + D_X1, + D_X2, + D_X3, + D_X4, + D_X5, + D_X6, + D_X7, + + D_NONE = 67, + + D_BRANCH = 68, + D_EXTERN = 69, + D_STATIC = 70, + D_AUTO = 71, + D_PARAM = 72, + D_CONST = 73, + D_FCONST = 74, + D_SCONST = 75, + D_ADDR = 76, D_FILE, D_FILE1, @@ -538,7 +666,7 @@ enum /* * this is the ranlib header */ -#define SYMDEF "__.SYMDEF" +#define SYMDEF "__.GOSYMDEF" /* * this is the simulated IEEE floating point diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 25ffc786f..a00174c36 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -70,34 +70,6 @@ datoff(vlong addr) return 0; } -enum { - ElfStrEmpty, - ElfStrInterp, - ElfStrHash, - ElfStrGot, - ElfStrGotPlt, - ElfStrDynamic, - ElfStrDynsym, - ElfStrDynstr, - ElfStrRel, - ElfStrText, - ElfStrData, - ElfStrBss, - ElfStrShstrtab, - ElfStrSymtab, - ElfStrStrtab, - ElfStrRelPlt, - ElfStrPlt, - ElfStrGnuVersion, - ElfStrGnuVersionR, - ElfStrNoteNetbsdIdent, - ElfStrNoPtrData, - ElfStrNoPtrBss, - NElfStr -}; - -vlong elfstr[NElfStr]; - static int needlib(char *name) { @@ -123,6 +95,21 @@ int nelfsym = 1; static void addpltsym(Sym*); static void addgotsym(Sym*); +Sym * +lookuprel(void) +{ + return lookup(".rel", 0); +} + +void +adddynrela(Sym *rela, Sym *s, Reloc *r) +{ + USED(rela); + USED(s); + USED(r); + sysfatal("adddynrela not implemented"); +} + void adddynrel(Sym *s, Reloc *r) { @@ -282,7 +269,36 @@ adddynrel(Sym *s, Reloc *r) diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } -static void +int +elfreloc1(Reloc *r, vlong off, int32 elfsym, vlong add) +{ + USED(add); // written to obj file by ../ld/data.c's reloc + + LPUT(off); + + switch(r->type) { + default: + return -1; + + case D_ADDR: + if(r->siz == 4) + LPUT(R_386_32 | elfsym<<8); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + LPUT(R_386_PC32 | elfsym<<8); + else + return -1; + break; + } + + return 0; +} + +void elfsetupplt(void) { Sym *plt, *got; @@ -415,6 +431,7 @@ adddynsym(Sym *s) Sym *d, *str; int t; char *name; + vlong off; if(s->dynid >= 0) return; @@ -444,7 +461,7 @@ adddynsym(Sym *s) /* type */ t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) + if(s->dynexport && (s->type&SMASK) == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; @@ -478,16 +495,51 @@ adddynsym(Sym *s) name = s->dynimpname; if(name == nil) name = s->name; - s->dynid = d->size/12; + if(d->size == 0 && ndynexp > 0) { // pre-allocate for dynexps + symgrow(d, ndynexp*12); + } + if(s->dynid <= -100) { // pre-allocated, see cmd/ld/go.c:^sortdynexp() + s->dynid = -s->dynid-100; + off = s->dynid*12; + } else { + off = d->size; + s->dynid = off/12; + } // darwin still puts _ prefixes on all C symbols str = lookup(".dynstr", 0); - adduint32(d, str->size); + setuint32(d, off, str->size); + off += 4; adduint8(str, '_'); addstring(str, name); - adduint8(d, 0x01); // type - N_EXT - external symbol - adduint8(d, 0); // section - adduint16(d, 0); // desc - adduint32(d, 0); // value + if(s->type == SDYNIMPORT) { + setuint8(d, off, 0x01); // type - N_EXT - external symbol + off++; + setuint8(d, off, 0); // section + off++; + } else { + setuint8(d, off, 0x0f); + off++; + switch(s->type) { + default: + case STEXT: + setuint8(d, off, 1); + break; + case SDATA: + setuint8(d, off, 2); + break; + case SBSS: + setuint8(d, off, 4); + break; + } + off++; + } + setuint16(d, off, 0); // desc + off += 2; + if(s->type == SDYNIMPORT) + setuint32(d, off, 0); // value + else + setaddr(d, off, s); + off += 4; } else if(HEADTYPE != Hwindows) { diag("adddynsym: unsupported binary format"); } @@ -513,178 +565,27 @@ adddynlib(char *lib) } } -void -doelf(void) -{ - Sym *s, *shstrtab, *dynstr; - - if(!iself) - return; - - /* predefine strings we need for section headers */ - shstrtab = lookup(".shstrtab", 0); - shstrtab->type = SELFROSECT; - shstrtab->reachable = 1; - - elfstr[ElfStrEmpty] = addstring(shstrtab, ""); - elfstr[ElfStrText] = addstring(shstrtab, ".text"); - elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); - elfstr[ElfStrData] = addstring(shstrtab, ".data"); - elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); - elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss"); - if(HEADTYPE == Hnetbsd) - elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident"); - addstring(shstrtab, ".elfdata"); - addstring(shstrtab, ".rodata"); - addstring(shstrtab, ".gosymtab"); - addstring(shstrtab, ".gopclntab"); - if(!debug['s']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); - dwarfaddshstrings(shstrtab); - } - elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); - - if(!debug['d']) { /* -d suppresses dynamic loader format */ - elfstr[ElfStrInterp] = addstring(shstrtab, ".interp"); - elfstr[ElfStrHash] = addstring(shstrtab, ".hash"); - elfstr[ElfStrGot] = addstring(shstrtab, ".got"); - elfstr[ElfStrGotPlt] = addstring(shstrtab, ".got.plt"); - elfstr[ElfStrDynamic] = addstring(shstrtab, ".dynamic"); - elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); - elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); - elfstr[ElfStrRel] = addstring(shstrtab, ".rel"); - elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); - elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); - elfstr[ElfStrGnuVersion] = addstring(shstrtab, ".gnu.version"); - elfstr[ElfStrGnuVersionR] = addstring(shstrtab, ".gnu.version_r"); - - /* dynamic symbol table - first entry all zeros */ - s = lookup(".dynsym", 0); - s->type = SELFROSECT; - s->reachable = 1; - s->size += ELF32SYMSIZE; - - /* dynamic string table */ - s = lookup(".dynstr", 0); - s->reachable = 1; - s->type = SELFROSECT; - if(s->size == 0) - addstring(s, ""); - dynstr = s; - - /* relocation table */ - s = lookup(".rel", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* global offset table */ - s = lookup(".got", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* hash */ - s = lookup(".hash", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* got.plt */ - s = lookup(".got.plt", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - s = lookup(".plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".rel.plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".gnu.version", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".gnu.version_r", 0); - s->reachable = 1; - s->type = SELFROSECT; - - elfsetupplt(); - - /* define dynamic elf table */ - s = lookup(".dynamic", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* - * .dynamic table - */ - elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); - elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); - elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); - elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); - elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); - elfwritedynentsym(s, DT_REL, lookup(".rel", 0)); - elfwritedynentsymsize(s, DT_RELSZ, lookup(".rel", 0)); - elfwritedynent(s, DT_RELENT, ELF32RELSIZE); - if(rpath) - elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); - elfwritedynent(s, DT_PLTREL, DT_REL); - elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); - elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); - - elfwritedynent(s, DT_DEBUG, 0); - - // Do not write DT_NULL. elfdynhash will finish it. - } -} - -void -shsym(Elf64_Shdr *sh, Sym *s) -{ - vlong addr; - addr = symaddr(s); - if(sh->flags&SHF_ALLOC) - sh->addr = addr; - sh->off = datoff(addr); - sh->size = s->size; -} - -void -phsh(Elf64_Phdr *ph, Elf64_Shdr *sh) -{ - ph->vaddr = sh->addr; - ph->paddr = ph->vaddr; - ph->off = sh->off; - ph->filesz = sh->size; - ph->memsz = sh->size; - ph->align = sh->addralign; -} - void asmb(void) { int32 v, magic; - int a, dynsym; - uint32 symo, startva, dwarfoff, machlink, resoff; - ElfEhdr *eh; - ElfPhdr *ph, *pph; - ElfShdr *sh; + uint32 symo, dwarfoff, machlink; Section *sect; Sym *sym; - int o; int i; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); + if(iself) + asmbelfsetup(); + sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); @@ -712,19 +613,6 @@ asmb(void) machlink = domacholink(); } - if(iself) { - /* index of elf text section; needed by asmelfsym, double-checked below */ - /* !debug['d'] causes extra sections before the .text section */ - elftextsh = 2; - if(!debug['d']) { - elftextsh += 10; - if(elfverneed) - elftextsh += 2; - } - if(HEADTYPE == Hnetbsd) - elftextsh += 1; - } - symsize = 0; spsize = 0; lcsize = 0; @@ -777,6 +665,9 @@ asmb(void) if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); } break; case Hplan9x32: @@ -805,8 +696,6 @@ asmb(void) cseek(0L); switch(HEADTYPE) { default: - if(iself) - goto Elfput; case Hgarbunix: /* garbage */ lputb(0x160L<<16); /* magic and sections */ lputb(0L); /* time and date */ @@ -932,273 +821,15 @@ asmb(void) wputl(0x003E); /* reloc table offset */ wputl(0x0000); /* overlay number */ break; - case Hdarwin: asmbmacho(); break; - - Elfput: - eh = getElfEhdr(); - startva = INITTEXT - HEADR; - resoff = ELFRESERVE; - - /* This null SHdr must appear before all others */ - newElfShdr(elfstr[ElfStrEmpty]); - - /* program header info */ - pph = newElfPhdr(); - pph->type = PT_PHDR; - pph->flags = PF_R + PF_X; - pph->off = eh->ehsize; - pph->vaddr = INITTEXT - HEADR + pph->off; - pph->paddr = INITTEXT - HEADR + pph->off; - pph->align = INITRND; - - /* - * PHDR must be in a loaded segment. Adjust the text - * segment boundaries downwards to include it. - */ - o = segtext.vaddr - pph->vaddr; - segtext.vaddr -= o; - segtext.len += o; - o = segtext.fileoff - pph->off; - segtext.fileoff -= o; - segtext.filelen += o; - - if(!debug['d']) { - /* interpreter */ - sh = newElfShdr(elfstr[ElfStrInterp]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - if(interpreter == nil) { - switch(HEADTYPE) { - case Hlinux: - interpreter = linuxdynld; - break; - case Hfreebsd: - interpreter = freebsddynld; - break; - case Hnetbsd: - interpreter = netbsddynld; - break; - case Hopenbsd: - interpreter = openbsddynld; - break; - } - } - resoff -= elfinterp(sh, startva, resoff, interpreter); - - ph = newElfPhdr(); - ph->type = PT_INTERP; - ph->flags = PF_R; - phsh(ph, sh); - } - - if(HEADTYPE == Hnetbsd) { - sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]); - sh->type = SHT_NOTE; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - resoff -= elfnetbsdsig(sh, startva, resoff); - - ph = newElfPhdr(); - ph->type = PT_NOTE; - ph->flags = PF_R; - phsh(ph, sh); - } - - // Additions to the reserved area must be above this line. - USED(resoff); - - elfphload(&segtext); - elfphload(&segdata); - - /* Dynamic linking sections */ - if(!debug['d']) { /* -d suppresses dynamic loader format */ - /* S headers for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrGot]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".got", 0)); - - sh = newElfShdr(elfstr[ElfStrGotPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".got.plt", 0)); - - dynsym = eh->shnum; - sh = newElfShdr(elfstr[ElfStrDynsym]); - sh->type = SHT_DYNSYM; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32SYMSIZE; - sh->addralign = 4; - sh->link = dynsym+1; // dynstr - // sh->info = index of first non-local symbol (number of local symbols) - shsym(sh, lookup(".dynsym", 0)); - - sh = newElfShdr(elfstr[ElfStrDynstr]); - sh->type = SHT_STRTAB; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup(".dynstr", 0)); - - if(elfverneed) { - sh = newElfShdr(elfstr[ElfStrGnuVersion]); - sh->type = SHT_GNU_VERSYM; - sh->flags = SHF_ALLOC; - sh->addralign = 2; - sh->link = dynsym; - sh->entsize = 2; - shsym(sh, lookup(".gnu.version", 0)); - - sh = newElfShdr(elfstr[ElfStrGnuVersionR]); - sh->type = SHT_GNU_VERNEED; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - sh->info = elfverneed; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".gnu.version_r", 0)); - } - - sh = newElfShdr(elfstr[ElfStrRelPlt]); - sh->type = SHT_REL; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32RELSIZE; - sh->addralign = 4; - sh->link = dynsym; - sh->info = eh->shnum; // .plt - shsym(sh, lookup(".rel.plt", 0)); - - sh = newElfShdr(elfstr[ElfStrPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".plt", 0)); - - sh = newElfShdr(elfstr[ElfStrHash]); - sh->type = SHT_HASH; - sh->flags = SHF_ALLOC; - sh->entsize = 4; - sh->addralign = 4; - sh->link = dynsym; - shsym(sh, lookup(".hash", 0)); - - sh = newElfShdr(elfstr[ElfStrRel]); - sh->type = SHT_REL; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32RELSIZE; - sh->addralign = 4; - sh->link = dynsym; - shsym(sh, lookup(".rel", 0)); - - /* sh and PT_DYNAMIC for .dynamic section */ - sh = newElfShdr(elfstr[ElfStrDynamic]); - sh->type = SHT_DYNAMIC; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 8; - sh->addralign = 4; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".dynamic", 0)); - ph = newElfPhdr(); - ph->type = PT_DYNAMIC; - ph->flags = PF_R + PF_W; - phsh(ph, sh); - - /* - * Thread-local storage segment (really just size). - */ - if(tlsoffset != 0) { - ph = newElfPhdr(); - ph->type = PT_TLS; - ph->flags = PF_R; - ph->memsz = -tlsoffset; - ph->align = 4; - } - } - - ph = newElfPhdr(); - ph->type = PT_GNU_STACK; - ph->flags = PF_W+PF_R; - ph->align = 4; - - sh = newElfShstrtab(elfstr[ElfStrShstrtab]); - sh->type = SHT_STRTAB; - sh->addralign = 1; - shsym(sh, lookup(".shstrtab", 0)); - - if(elftextsh != eh->shnum) - diag("elftextsh = %d, want %d", elftextsh, eh->shnum); - for(sect=segtext.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - for(sect=segdata.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - - if(!debug['s']) { - sh = newElfShdr(elfstr[ElfStrSymtab]); - sh->type = SHT_SYMTAB; - sh->off = symo; - sh->size = symsize; - sh->addralign = 4; - sh->entsize = 16; - sh->link = eh->shnum; // link to strtab - - sh = newElfShdr(elfstr[ElfStrStrtab]); - sh->type = SHT_STRTAB; - sh->off = symo+symsize; - sh->size = elfstrsize; - sh->addralign = 1; - - dwarfaddelfheaders(); - } - - /* Main header */ - eh->ident[EI_MAG0] = '\177'; - eh->ident[EI_MAG1] = 'E'; - eh->ident[EI_MAG2] = 'L'; - eh->ident[EI_MAG3] = 'F'; - eh->ident[EI_CLASS] = ELFCLASS32; - eh->ident[EI_DATA] = ELFDATA2LSB; - eh->ident[EI_VERSION] = EV_CURRENT; - switch(HEADTYPE) { - case Hfreebsd: - eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; - break; - case Hnetbsd: - eh->ident[EI_OSABI] = ELFOSABI_NETBSD; - break; - case Hopenbsd: - eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; - break; - } - - eh->type = ET_EXEC; - eh->machine = EM_386; - eh->version = EV_CURRENT; - eh->entry = entryvalue(); - - if(pph != nil) { - pph->filesz = eh->phnum * eh->phentsize; - pph->memsz = pph->filesz; - } - - cseek(0); - a = 0; - a += elfwritehdr(); - a += elfwritephdrs(); - a += elfwriteshdrs(); - a += elfwriteinterp(elfstr[ElfStrInterp]); - if(HEADTYPE == Hnetbsd) - a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); - if(a > ELFRESERVE) - diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); + case Hlinux: + case Hfreebsd: + case Hnetbsd: + case Hopenbsd: + asmbelf(symo); break; - case Hwindows: asmbpe(); break; @@ -1231,80 +862,3 @@ rnd(int32 v, int32 r) v -= c; return v; } - -void -genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) -{ - Auto *a; - Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - put(s, s->name, 'T', s->value, s->size, s->version, 0); - - for(h=0; hhash) { - if(s->hide) - continue; - switch(s->type&~SSUB) { - case SCONST: - case SRODATA: - case SDATA: - case SELFROSECT: - case SMACHO: - case SMACHOGOT: - case STYPE: - case SSTRING: - case SGOSTRING: - case SWINDOWS: - case SNOPTRDATA: - case SSYMTAB: - case SPCLNTAB: - if(!s->reachable) - continue; - put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SBSS: - case SNOPTRBSS: - if(!s->reachable) - continue; - put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SFILE: - put(nil, s->name, 'f', s->value, 0, s->version, 0); - continue; - } - } - } - - for(s = textp; s != nil; s = s->next) { - if(s->text == nil) - continue; - - /* filenames first */ - for(a=s->autom; a; a=a->link) - if(a->type == D_FILE) - put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); - - put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); - - /* frame, auto and param after */ - put(nil, ".frame", 'm', s->text->to.offset+4, 0, 0, 0); - - for(a=s->autom; a; a=a->link) - if(a->type == D_AUTO) - put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); - else - if(a->type == D_PARAM) - put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %d\n", symsize); - Bflush(&bso); -} diff --git a/src/cmd/8l/doc.go b/src/cmd/8l/doc.go index 12301d4f2..ff06bc376 100644 --- a/src/cmd/8l/doc.go +++ b/src/cmd/8l/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 8l is the linker for the 32-bit x86. @@ -10,4 +12,4 @@ The $GOARCH for these tools is 386. The flags are documented in ../ld/doc.go. */ -package documentation +package main diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index b974f464b..f88f058e3 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -40,7 +40,9 @@ enum { thechar = '8', - PtrSize = 4 + PtrSize = 4, + IntSize = 4, + FuncAlign = 16 }; #define P ((Prog*)0) @@ -135,16 +137,22 @@ struct Sym int32 plt; int32 got; int32 align; // if non-zero, required alignment in bytes + int32 elfsym; + int32 locals; // size of stack frame locals area + int32 args; // size of stack frame incoming arguments area Sym* hash; // in hash table Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in sub list Sym* outer; // container of sub Sym* gotype; + Sym* reachparent; + Sym* queue; char* file; char* dynimpname; char* dynimplib; char* dynimpvers; + struct Section* sect; // STEXT Auto* autom; @@ -157,6 +165,7 @@ struct Sym Reloc* r; int32 nr; int32 maxr; + int rel_ro; }; struct Optab { @@ -201,6 +210,8 @@ enum Ycr0, Ycr1, Ycr2, Ycr3, Ycr4, Ycr5, Ycr6, Ycr7, Ydr0, Ydr1, Ydr2, Ydr3, Ydr4, Ydr5, Ydr6, Ydr7, Ytr0, Ytr1, Ytr2, Ytr3, Ytr4, Ytr5, Ytr6, Ytr7, + Ymr, Ymm, + Yxr, Yxm, Ymax, Zxxx = 0, @@ -222,10 +233,14 @@ enum Zloop, Zm_o, Zm_r, + Zm_r_xm, + Zm_r_i_xm, Zaut_r, Zo_m, Zpseudo, Zr_m, + Zr_m_xm, + Zr_m_i_xm, Zrp_, Z_ib, Z_il, @@ -243,6 +258,8 @@ enum Pm = 0x0f, /* 2byte opcode escape */ Pq = 0xff, /* both escape */ Pb = 0xfe, /* byte operands */ + Pf2 = 0xf2, /* xmm escape 1 */ + Pf3 = 0xf3, /* xmm escape 2 */ }; #pragma varargck type "A" int @@ -261,6 +278,7 @@ EXTERN int32 INITRND; EXTERN int32 INITTEXT; EXTERN int32 INITDAT; EXTERN char* INITENTRY; /* entry point */ +EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN int32 casepc; EXTERN char* pcstr; EXTERN Auto* curauto; @@ -269,7 +287,7 @@ EXTERN Prog* curp; EXTERN Sym* cursym; EXTERN Sym* datap; EXTERN int32 elfdatsize; -EXTERN char debug[128]; +EXTERN int debug[128]; EXTERN char literal[32]; EXTERN Sym* etextp; EXTERN Prog* firstp; @@ -282,7 +300,6 @@ EXTERN int maxop; EXTERN int nerrors; EXTERN char* noname; EXTERN int32 pc; -EXTERN char* interpreter; EXTERN char* rpath; EXTERN int32 spsize; EXTERN Sym* symlist; diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c index 31ae02346..0b544fbce 100644 --- a/src/cmd/8l/list.c +++ b/src/cmd/8l/list.c @@ -254,6 +254,15 @@ char* regstr[] = "TR5", "TR6", "TR7", + + "X0", + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", "NONE", /* [D_NONE] */ }; diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index af4bc844f..dcb8390b9 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -76,19 +76,9 @@ Header headers[] = { * -Hwindows -Tx -Rx is MS Windows PE32 */ -void -usage(void) -{ - fprint(2, "usage: 8l [-options] [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.8\n"); - exits("usage"); -} - void main(int argc, char *argv[]) { - int c; - char *name, *val; - Binit(&bso, 1, OWRITE); listinit(); memset(debug, 0, sizeof(debug)); @@ -99,52 +89,44 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + LIBINITENTRY = 0; nuxiinit(); - ARGBEGIN { - default: - c = ARGC(); - if(c == 'l') - usage(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - case 'o': /* output to (next arg) */ - outfile = EARGF(usage()); - break; - case 'E': - INITENTRY = EARGF(usage()); - break; - case 'H': - HEADTYPE = headtype(EARGF(usage())); - break; - case 'I': - interpreter = EARGF(usage()); - break; - case 'L': - Lflag(EARGF(usage())); - break; - case 'T': - INITTEXT = atolwhex(EARGF(usage())); - break; - case 'D': - INITDAT = atolwhex(EARGF(usage())); - break; - case 'R': - INITRND = atolwhex(EARGF(usage())); - break; - case 'r': - rpath = EARGF(usage()); - break; - case 'V': - print("%cl version %s\n", thechar, getgoversion()); - errorexit(); - case 'X': - name = EARGF(usage()); - val = EARGF(usage()); - addstrdata(name, val); - break; - } ARGEND + flagcount("1", "use alternate profiling code", &debug['1']); + flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); + flagstr("E", "sym: entry symbol", &INITENTRY); + flagint32("D", "addr: data address", &INITDAT); + flagfn1("I", "interp: set ELF interp", setinterp); + flagfn1("L", "dir: add dir to library path", Lflag); + flagfn1("H", "head: header type", setheadtype); + flagcount("K", "add stack underflow checks", &debug['K']); + flagcount("O", "print pc-line tables", &debug['O']); + flagcount("Q", "debug byte-register code gen", &debug['Q']); + flagint32("R", "rnd: address rounding", &INITRND); + flagcount("S", "check type signatures", &debug['S']); + flagint32("T", "addr: text address", &INITTEXT); + flagfn0("V", "print version and exit", doversion); + flagcount("W", "disassemble input", &debug['W']); + flagfn2("X", "name value: define string data", addstrdata); + flagcount("Z", "clear stack frame on entry", &debug['Z']); + flagcount("a", "disassemble output", &debug['a']); + flagcount("c", "dump call graph", &debug['c']); + flagcount("d", "disable dynamic executable", &debug['d']); + flagcount("f", "ignore version mismatch", &debug['f']); + flagcount("g", "disable go package data checks", &debug['g']); + flagcount("hostobj", "generate host object file", &isobj); + flagstr("k", "sym: set field tracking symbol", &tracksym); + flagstr("o", "outfile: set output file", &outfile); + flagcount("p", "insert profiling code", &debug['p']); + flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); + flagcount("race", "enable race detector", &flag_race); + flagcount("s", "disable symbol table", &debug['s']); + flagcount("n", "dump symbol table", &debug['n']); + flagcount("u", "reject unsafe packages", &debug['u']); + flagcount("v", "print link trace", &debug['v']); + flagcount("w", "disable DWARF generation", &debug['w']); + + flagparse(&argc, &argv, usage); if(argc != 1) usage(); @@ -154,6 +136,15 @@ main(int argc, char *argv[]) if(HEADTYPE == -1) HEADTYPE = headtype(goos); + if(isobj) { + switch(HEADTYPE) { + default: + sysfatal("cannot use -hostobj with -H %s", headstr(HEADTYPE)); + case Hlinux: + break; + } + } + if(outfile == nil) { if(HEADTYPE == Hwindows) outfile = "8.out.exe"; @@ -300,6 +291,7 @@ main(int argc, char *argv[]) if(HEADTYPE == Hwindows) dope(); dostkoff(); + dostkcheck(); if(debug['p']) if(debug['1']) doprof1(); @@ -332,7 +324,7 @@ zsym(char *pn, Biobuf *f, Sym *h[]) { int o; - o = Bgetc(f); + o = BGETC(f); if(o < 0 || o >= NSYM || h[o] == nil) mangle(pn); return h[o]; @@ -346,12 +338,12 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) Sym *s; Auto *u; - t = Bgetc(f); + t = BGETC(f); a->index = D_NONE; a->scale = 0; if(t & T_INDEX) { - a->index = Bgetc(f); - a->scale = Bgetc(f); + a->index = BGETC(f); + a->scale = BGETC(f); } a->type = D_NONE; a->offset = 0; @@ -375,7 +367,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) a->type = D_SCONST; } if(t & T_TYPE) - a->type = Bgetc(f); + a->type = BGETC(f); adrgotype = S; if(t & T_GOTYPE) adrgotype = zsym(pn, f, h); @@ -451,10 +443,10 @@ newloop: loop: if(f->state == Bracteof || Boffset(f) >= eof) goto eof; - o = Bgetc(f); + o = BGETC(f); if(o == Beof) goto eof; - o |= Bgetc(f) << 8; + o |= BGETC(f) << 8; if(o <= AXXX || o >= ALAST) { if(o < 0) goto eof; @@ -467,8 +459,8 @@ loop: sig = 0; if(o == ASIGNAME) sig = Bget4(f); - v = Bgetc(f); /* type */ - o = Bgetc(f); /* sym */ + v = BGETC(f); /* type */ + o = BGETC(f); /* sym */ r = 0; if(v == D_STATIC) r = version; @@ -523,8 +515,6 @@ loop: p->as = o; p->line = Bget4(f); p->back = 2; - p->ft = 0; - p->tt = 0; zaddr(pn, f, &p->from, h); fromgotype = adrgotype; zaddr(pn, f, &p->to, h); @@ -605,6 +595,15 @@ loop: pc++; goto loop; + case ALOCALS: + cursym->locals = p->to.offset; + pc++; + goto loop; + + case ATYPE: + pc++; + goto loop; + case ATEXT: s = p->from.sym; if(s->text != nil) { @@ -643,6 +642,7 @@ loop: } s->type = STEXT; s->value = pc; + s->args = p->to.offset2; lastp = p; p->pc = pc++; goto loop; @@ -656,6 +656,13 @@ loop: case AFDIVRF: case AFCOMF: case AFCOMFP: + case AMOVSS: + case AADDSS: + case ASUBSS: + case AMULSS: + case ADIVSS: + case ACOMISS: + case AUCOMISS: if(skip) goto casdef; if(p->from.type == D_FCONST) { @@ -663,7 +670,7 @@ loop: sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { - s->type = SDATA; + s->type = SRODATA; adduint32(s, ieeedtof(&p->from.ieee)); s->reachable = 0; } @@ -682,6 +689,13 @@ loop: case AFDIVRD: case AFCOMD: case AFCOMDP: + case AMOVSD: + case AADDSD: + case ASUBSD: + case AMULSD: + case ADIVSD: + case ACOMISD: + case AUCOMISD: if(skip) goto casdef; if(p->from.type == D_FCONST) { @@ -690,7 +704,7 @@ loop: p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { - s->type = SDATA; + s->type = SRODATA; adduint32(s, p->from.ieee.l); adduint32(s, p->from.ieee.h); s->reachable = 0; diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c index 856482290..79d7b39f0 100644 --- a/src/cmd/8l/optab.c +++ b/src/cmd/8l/optab.c @@ -242,6 +242,11 @@ uchar ypopl[] = Ynone, Ym, Zo_m, 2, 0 }; +uchar ybswap[] = +{ + Ynone, Yrl, Z_rp, 1, + 0, +}; uchar yscond[] = { Ynone, Ymb, Zo_m, 2, @@ -249,7 +254,9 @@ uchar yscond[] = }; uchar yjcond[] = { - Ynone, Ybr, Zbr, 1, + Ynone, Ybr, Zbr, 0, + Yi0, Ybr, Zbr, 0, + Yi1, Ybr, Zbr, 1, 0 }; uchar yloop[] = @@ -259,7 +266,8 @@ uchar yloop[] = }; uchar ycall[] = { - Ynone, Yml, Zo_m, 2, + Ynone, Yml, Zo_m, 0, + Yrx, Yrx, Zo_m, 2, Ynone, Ycol, Zcallind, 2, Ynone, Ybr, Zcall, 0, Ynone, Yi32, Zcallcon, 1, @@ -349,6 +357,84 @@ uchar ysvrs[] = Ym, Ynone, Zm_o, 2, 0 }; +uchar yxm[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +uchar yxcvm1[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + Yxm, Ymr, Zm_r_xm, 2, + 0 +}; +uchar yxcvm2[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + Ymm, Yxr, Zm_r_xm, 2, + 0 +}; +uchar yxmq[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + 0 +}; +uchar yxr[] = +{ + Yxr, Yxr, Zm_r_xm, 1, + 0 +}; +uchar yxr_ml[] = +{ + Yxr, Yml, Zr_m_xm, 1, + 0 +}; +uchar yxcmp[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +uchar yxcmpi[] = +{ + Yxm, Yxr, Zm_r_i_xm, 2, + 0 +}; +uchar yxmov[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + Yxr, Yxm, Zr_m_xm, 1, + 0 +}; +uchar yxcvfl[] = +{ + Yxm, Yrl, Zm_r_xm, 1, + 0 +}; +uchar yxcvlf[] = +{ + Yml, Yxr, Zm_r_xm, 1, + 0 +}; +uchar yxcvfq[] = +{ + Yxm, Yrl, Zm_r_xm, 2, + 0 +}; +uchar yxcvqf[] = +{ + Yml, Yxr, Zm_r_xm, 2, + 0 +}; +uchar yxrrl[] = +{ + Yxr, Yrl, Zm_r, 1, + 0 +}; +uchar yprefetch[] = +{ + Ym, Ynone, Zm_o, 2, + 0, +}; Optab optab[] = /* as, ytab, andproto, opcode */ @@ -761,5 +847,123 @@ Optab optab[] = { AEMMS, ynone, Pm, 0x77 }, + { APREFETCHT0, yprefetch, Pm, 0x18,(01) }, + { APREFETCHT1, yprefetch, Pm, 0x18,(02) }, + { APREFETCHT2, yprefetch, Pm, 0x18,(03) }, + { APREFETCHNTA, yprefetch, Pm, 0x18,(00) }, + + { ABSWAPL, ybswap, Pm, 0xc8 }, + + { AUNDEF, ynone, Px, 0x0f, 0x0b }, + + { AADDPD, yxm, Pq, 0x58 }, + { AADDPS, yxm, Pm, 0x58 }, + { AADDSD, yxm, Pf2, 0x58 }, + { AADDSS, yxm, Pf3, 0x58 }, + { AANDNPD, yxm, Pq, 0x55 }, + { AANDNPS, yxm, Pm, 0x55 }, + { AANDPD, yxm, Pq, 0x54 }, + { AANDPS, yxm, Pq, 0x54 }, + { ACMPPD, yxcmpi, Px, Pe,0xc2 }, + { ACMPPS, yxcmpi, Pm, 0xc2,0 }, + { ACMPSD, yxcmpi, Px, Pf2,0xc2 }, + { ACMPSS, yxcmpi, Px, Pf3,0xc2 }, + { ACOMISD, yxcmp, Pe, 0x2f }, + { ACOMISS, yxcmp, Pm, 0x2f }, + { ACVTPL2PD, yxcvm2, Px, Pf3,0xe6,Pe,0x2a }, + { ACVTPL2PS, yxcvm2, Pm, 0x5b,0,0x2a,0, }, + { ACVTPD2PL, yxcvm1, Px, Pf2,0xe6,Pe,0x2d }, + { ACVTPD2PS, yxm, Pe, 0x5a }, + { ACVTPS2PL, yxcvm1, Px, Pe,0x5b,Pm,0x2d }, + { ACVTPS2PD, yxm, Pm, 0x5a }, + { ACVTSD2SL, yxcvfl, Pf2, 0x2d }, + { ACVTSD2SS, yxm, Pf2, 0x5a }, + { ACVTSL2SD, yxcvlf, Pf2, 0x2a }, + { ACVTSL2SS, yxcvlf, Pf3, 0x2a }, + { ACVTSS2SD, yxm, Pf3, 0x5a }, + { ACVTSS2SL, yxcvfl, Pf3, 0x2d }, + { ACVTTPD2PL, yxcvm1, Px, Pe,0xe6,Pe,0x2c }, + { ACVTTPS2PL, yxcvm1, Px, Pf3,0x5b,Pm,0x2c }, + { ACVTTSD2SL, yxcvfl, Pf2, 0x2c }, + { ACVTTSS2SL, yxcvfl, Pf3, 0x2c }, + { ADIVPD, yxm, Pe, 0x5e }, + { ADIVPS, yxm, Pm, 0x5e }, + { ADIVSD, yxm, Pf2, 0x5e }, + { ADIVSS, yxm, Pf3, 0x5e }, + { AMASKMOVOU, yxr, Pe, 0xf7 }, + { AMAXPD, yxm, Pe, 0x5f }, + { AMAXPS, yxm, Pm, 0x5f }, + { AMAXSD, yxm, Pf2, 0x5f }, + { AMAXSS, yxm, Pf3, 0x5f }, + { AMINPD, yxm, Pe, 0x5d }, + { AMINPS, yxm, Pm, 0x5d }, + { AMINSD, yxm, Pf2, 0x5d }, + { AMINSS, yxm, Pf3, 0x5d }, + { AMOVAPD, yxmov, Pe, 0x28,0x29 }, + { AMOVAPS, yxmov, Pm, 0x28,0x29 }, + { AMOVO, yxmov, Pe, 0x6f,0x7f }, + { AMOVOU, yxmov, Pf3, 0x6f,0x7f }, + { AMOVHLPS, yxr, Pm, 0x12 }, + { AMOVHPD, yxmov, Pe, 0x16,0x17 }, + { AMOVHPS, yxmov, Pm, 0x16,0x17 }, + { AMOVLHPS, yxr, Pm, 0x16 }, + { AMOVLPD, yxmov, Pe, 0x12,0x13 }, + { AMOVLPS, yxmov, Pm, 0x12,0x13 }, + { AMOVMSKPD, yxrrl, Pq, 0x50 }, + { AMOVMSKPS, yxrrl, Pm, 0x50 }, + { AMOVNTO, yxr_ml, Pe, 0xe7 }, + { AMOVNTPD, yxr_ml, Pe, 0x2b }, + { AMOVNTPS, yxr_ml, Pm, 0x2b }, + { AMOVSD, yxmov, Pf2, 0x10,0x11 }, + { AMOVSS, yxmov, Pf3, 0x10,0x11 }, + { AMOVUPD, yxmov, Pe, 0x10,0x11 }, + { AMOVUPS, yxmov, Pm, 0x10,0x11 }, + { AMULPD, yxm, Pe, 0x59 }, + { AMULPS, yxm, Ym, 0x59 }, + { AMULSD, yxm, Pf2, 0x59 }, + { AMULSS, yxm, Pf3, 0x59 }, + { AORPD, yxm, Pq, 0x56 }, + { AORPS, yxm, Pm, 0x56 }, + { APADDQ, yxm, Pe, 0xd4 }, + { APMAXSW, yxm, Pe, 0xee }, + { APMAXUB, yxm, Pe, 0xde }, + { APMINSW, yxm, Pe, 0xea }, + { APMINUB, yxm, Pe, 0xda }, + { APSADBW, yxm, Pq, 0xf6 }, + { APSUBB, yxm, Pe, 0xf8 }, + { APSUBL, yxm, Pe, 0xfa }, + { APSUBQ, yxm, Pe, 0xfb }, + { APSUBSB, yxm, Pe, 0xe8 }, + { APSUBSW, yxm, Pe, 0xe9 }, + { APSUBUSB, yxm, Pe, 0xd8 }, + { APSUBUSW, yxm, Pe, 0xd9 }, + { APSUBW, yxm, Pe, 0xf9 }, + { APUNPCKHQDQ, yxm, Pe, 0x6d }, + { APUNPCKLQDQ, yxm, Pe, 0x6c }, + { ARCPPS, yxm, Pm, 0x53 }, + { ARCPSS, yxm, Pf3, 0x53 }, + { ARSQRTPS, yxm, Pm, 0x52 }, + { ARSQRTSS, yxm, Pf3, 0x52 }, + { ASQRTPD, yxm, Pe, 0x51 }, + { ASQRTPS, yxm, Pm, 0x51 }, + { ASQRTSD, yxm, Pf2, 0x51 }, + { ASQRTSS, yxm, Pf3, 0x51 }, + { ASUBPD, yxm, Pe, 0x5c }, + { ASUBPS, yxm, Pm, 0x5c }, + { ASUBSD, yxm, Pf2, 0x5c }, + { ASUBSS, yxm, Pf3, 0x5c }, + { AUCOMISD, yxcmp, Pe, 0x2e }, + { AUCOMISS, yxcmp, Pm, 0x2e }, + { AUNPCKHPD, yxm, Pe, 0x15 }, + { AUNPCKHPS, yxm, Pm, 0x15 }, + { AUNPCKLPD, yxm, Pe, 0x14 }, + { AUNPCKLPS, yxm, Pm, 0x14 }, + { AXORPD, yxm, Pe, 0x57 }, + { AXORPS, yxm, Pm, 0x57 }, + + { AUSEFIELD, ynop, Px, 0,0 }, + { ALOCALS }, + { ATYPE }, + 0 }; diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 9034fdf3a..14dd3e0dc 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -75,6 +75,7 @@ nofollow(int a) case ARET: case AIRETL: case AIRETW: + case AUNDEF: return 1; } return 0; @@ -184,20 +185,34 @@ loop: * recurse to follow one path. * continue loop on the other. */ - q = brchain(p->link); - if(q != P && q->mark) - if(a != ALOOP) { - p->as = relinv(a); - p->link = p->pcond; + if((q = brchain(p->pcond)) != P) p->pcond = q; + if((q = brchain(p->link)) != P) + p->link = q; + if(p->from.type == D_CONST) { + if(p->from.offset == 1) { + /* + * expect conditional jump to be taken. + * rewrite so that's the fall-through case. + */ + p->as = relinv(a); + q = p->link; + p->link = p->pcond; + p->pcond = q; + } + } else { + q = p->link; + if(q->mark) + if(a != ALOOP) { + p->as = relinv(a); + p->link = p->pcond; + p->pcond = q; + } } xfol(p->link, last); - q = brchain(p->pcond); - if(q->mark) { - p->pcond = q; + if(p->pcond->mark) return; - } - p = q; + p = p->pcond; goto loop; } p = p->link; @@ -315,7 +330,7 @@ patch(void) } else if(s) { if(debug['c']) Bprint(&bso, "%s calls %s\n", TNAME, s->name); - if((s->type&~SSUB) != STEXT) { + if((s->type&SMASK) != STEXT) { /* diag prints TNAME first */ diag("undefined: %s", s->name); s->type = STEXT; @@ -524,9 +539,9 @@ dostkoff(void) q = p; } - p = appendp(p); // save frame size in DX + p = appendp(p); // save frame size in DI p->as = AMOVL; - p->to.type = D_DX; + p->to.type = D_DI; p->from.type = D_CONST; // If we ask for more stack, we'll get a minimum of StackMin bytes. @@ -565,9 +580,47 @@ dostkoff(void) p->spadj = autoffset; if(q != P) q->pcond = p; + } else { + // zero-byte stack adjustment. + // Insert a fake non-zero adjustment so that stkcheck can + // recognize the end of the stack-splitting prolog. + p = appendp(p); + p->as = ANOP; + p->spadj = -PtrSize; + p = appendp(p); + p->as = ANOP; + p->spadj = PtrSize; } deltasp = autoffset; + if(debug['Z'] && autoffset && !(cursym->text->from.scale&NOSPLIT)) { + // 8l -Z means zero the stack frame on entry. + // This slows down function calls but can help avoid + // false positives in garbage collection. + p = appendp(p); + p->as = AMOVL; + p->from.type = D_SP; + p->to.type = D_DI; + + p = appendp(p); + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = autoffset/4; + p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_AX; + + p = appendp(p); + p->as = AREP; + + p = appendp(p); + p->as = ASTOSL; + } + for(; p != P; p = p->link) { a = p->from.type; if(a == D_AUTO) diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c index 81c1d37eb..b828d8645 100644 --- a/src/cmd/8l/span.c +++ b/src/cmd/8l/span.c @@ -194,7 +194,7 @@ instinit(void) for(i=1; optab[i].as; i++) if(i != optab[i].as) { - diag("phase error in optab: %d", i); + diag("phase error in optab: at %A found %A", i, optab[i].as); errorexit(); } maxop = i; @@ -238,6 +238,16 @@ instinit(void) ycover[Yrl*Ymax + Yml] = 1; ycover[Ym*Ymax + Yml] = 1; + ycover[Yax*Ymax + Ymm] = 1; + ycover[Ycx*Ymax + Ymm] = 1; + ycover[Yrx*Ymax + Ymm] = 1; + ycover[Yrl*Ymax + Ymm] = 1; + ycover[Ym*Ymax + Ymm] = 1; + ycover[Ymr*Ymax + Ymm] = 1; + + ycover[Ym*Ymax + Yxm] = 1; + ycover[Yxr*Ymax + Yxm] = 1; + for(i=0; i= D_AL && i <= D_BH) @@ -246,6 +256,8 @@ instinit(void) reg[i] = (i-D_AX) & 7; if(i >= D_F0 && i <= D_F0+7) reg[i] = (i-D_F0) & 7; + if(i >= D_X0 && i <= D_X0+7) + reg[i] = (i-D_X0) & 7; } } @@ -333,6 +345,16 @@ oclass(Adr *a) case D_F0+7: return Yrf; + case D_X0+0: + case D_X0+1: + case D_X0+2: + case D_X0+3: + case D_X0+4: + case D_X0+5: + case D_X0+6: + case D_X0+7: + return Yxr; + case D_NONE: return Ynone; @@ -585,7 +607,7 @@ asmand(Adr *a, int r) asmidx(a->scale, a->index, t); goto putrelv; } - if(t >= D_AL && t <= D_F0+7) { + if(t >= D_AL && t <= D_F7 || t >= D_X0 && t <= D_X7) { if(v) goto bad; *andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); @@ -772,19 +794,71 @@ uchar ymovtab[] = 0 }; +// byteswapreg returns a byte-addressable register (AX, BX, CX, DX) +// which is not referenced in a->type. +// If a is empty, it returns BX to account for MULB-like instructions +// that might use DX and AX. int -isax(Adr *a) +byteswapreg(Adr *a) { + int cana, canb, canc, cand; + + cana = canb = canc = cand = 1; switch(a->type) { + case D_NONE: + cana = cand = 0; + break; case D_AX: case D_AL: case D_AH: case D_INDIR+D_AX: - return 1; + cana = 0; + break; + case D_BX: + case D_BL: + case D_BH: + case D_INDIR+D_BX: + canb = 0; + break; + case D_CX: + case D_CL: + case D_CH: + case D_INDIR+D_CX: + canc = 0; + break; + case D_DX: + case D_DL: + case D_DH: + case D_INDIR+D_DX: + cand = 0; + break; + } + switch(a->index) { + case D_AX: + cana = 0; + break; + case D_BX: + canb = 0; + break; + case D_CX: + canc = 0; + break; + case D_DX: + cand = 0; + break; } - if(a->index == D_AX) - return 1; + if(cana) + return D_AX; + if(canb) + return D_BX; + if(canc) + return D_CX; + if(cand) + return D_DX; + + diag("impossible byte register"); + errorexit(); return 0; } @@ -827,13 +901,37 @@ subreg(Prog *p, int from, int to) print("%P\n", p); } +static int +mediaop(Optab *o, int op, int osize, int z) +{ + switch(op){ + case Pm: + case Pe: + case Pf2: + case Pf3: + if(osize != 1){ + if(op != Pm) + *andptr++ = op; + *andptr++ = Pm; + op = o->op[++z]; + break; + } + default: + if(andptr == and || andptr[-1] != Pm) + *andptr++ = Pm; + break; + } + *andptr++ = op; + return z; +} + void doasm(Prog *p) { Optab *o; Prog *q, pp; uchar *t; - int z, op, ft, tt; + int z, op, ft, tt, breg; int32 v, pre; Reloc rel, *r; Adr *a; @@ -873,6 +971,12 @@ found: *andptr++ = Pm; break; + case Pf2: /* xmm opcode escape */ + case Pf3: + *andptr++ = o->prefix; + *andptr++ = Pm; + break; + case Pm: /* opcode escape */ *andptr++ = Pm; break; @@ -904,6 +1008,17 @@ found: asmand(&p->from, reg[p->to.type]); break; + case Zm_r_xm: + mediaop(o, op, t[3], z); + asmand(&p->from, reg[p->to.type]); + break; + + case Zm_r_i_xm: + mediaop(o, op, t[3], z); + asmand(&p->from, reg[p->to.type]); + *andptr++ = p->to.offset; + break; + case Zaut_r: *andptr++ = 0x8d; /* leal */ if(p->from.type != D_ADDR) @@ -927,6 +1042,17 @@ found: asmand(&p->to, reg[p->from.type]); break; + case Zr_m_xm: + mediaop(o, op, t[3], z); + asmand(&p->to, reg[p->from.type]); + break; + + case Zr_m_i_xm: + mediaop(o, op, t[3], z); + asmand(&p->to, reg[p->from.type]); + *andptr++ = p->from.offset; + break; + case Zo_m: *andptr++ = op; asmand(&p->to, o->op[z+1]); @@ -1198,13 +1324,13 @@ bad: pp = *p; z = p->from.type; if(z >= D_BP && z <= D_DI) { - if(isax(&p->to)) { + if((breg = byteswapreg(&p->to)) != D_AX) { *andptr++ = 0x87; /* xchg lhs,bx */ - asmand(&p->from, reg[D_BX]); - subreg(&pp, z, D_BX); + asmand(&p->from, reg[breg]); + subreg(&pp, z, breg); doasm(&pp); *andptr++ = 0x87; /* xchg lhs,bx */ - asmand(&p->from, reg[D_BX]); + asmand(&p->from, reg[breg]); } else { *andptr++ = 0x90 + reg[z]; /* xchg lsh,ax */ subreg(&pp, z, D_AX); @@ -1215,13 +1341,13 @@ bad: } z = p->to.type; if(z >= D_BP && z <= D_DI) { - if(isax(&p->from)) { + if((breg = byteswapreg(&p->from)) != D_AX) { *andptr++ = 0x87; /* xchg rhs,bx */ - asmand(&p->to, reg[D_BX]); - subreg(&pp, z, D_BX); + asmand(&p->to, reg[breg]); + subreg(&pp, z, breg); doasm(&pp); *andptr++ = 0x87; /* xchg rhs,bx */ - asmand(&p->to, reg[D_BX]); + asmand(&p->to, reg[breg]); } else { *andptr++ = 0x90 + reg[z]; /* xchg rsh,ax */ subreg(&pp, z, D_AX); diff --git a/src/cmd/api/clone.go b/src/cmd/api/clone.go new file mode 100644 index 000000000..180215f4b --- /dev/null +++ b/src/cmd/api/clone.go @@ -0,0 +1,251 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "go/ast" + "log" + "reflect" +) + +const debugClone = false + +// TODO(bradfitz): delete this function (and whole file) once +// http://golang.org/issue/4380 is fixed. +func clone(i interface{}) (cloned interface{}) { + if debugClone { + defer func() { + if !reflect.DeepEqual(i, cloned) { + log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned) + } + }() + } + switch v := i.(type) { + case nil: + return nil + case *ast.File: + o := &ast.File{ + Doc: v.Doc, // shallow + Package: v.Package, + Comments: v.Comments, // shallow + Name: v.Name, + Scope: v.Scope, + } + for _, x := range v.Decls { + o.Decls = append(o.Decls, clone(x).(ast.Decl)) + } + for _, x := range v.Imports { + o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec)) + } + for _, x := range v.Unresolved { + o.Unresolved = append(o.Unresolved, x) + } + return o + case *ast.GenDecl: + o := new(ast.GenDecl) + *o = *v + o.Specs = nil + for _, x := range v.Specs { + o.Specs = append(o.Specs, clone(x).(ast.Spec)) + } + return o + case *ast.TypeSpec: + o := new(ast.TypeSpec) + *o = *v + o.Type = cloneExpr(v.Type) + return o + case *ast.InterfaceType: + o := new(ast.InterfaceType) + *o = *v + o.Methods = clone(v.Methods).(*ast.FieldList) + return o + case *ast.FieldList: + if v == nil { + return v + } + o := new(ast.FieldList) + *o = *v + o.List = nil + for _, x := range v.List { + o.List = append(o.List, clone(x).(*ast.Field)) + } + return o + case *ast.Field: + o := &ast.Field{ + Doc: v.Doc, // shallow + Type: cloneExpr(v.Type), + Tag: clone(v.Tag).(*ast.BasicLit), + Comment: v.Comment, // shallow + } + for _, x := range v.Names { + o.Names = append(o.Names, clone(x).(*ast.Ident)) + } + return o + case *ast.FuncType: + if v == nil { + return v + } + return &ast.FuncType{ + Func: v.Func, + Params: clone(v.Params).(*ast.FieldList), + Results: clone(v.Results).(*ast.FieldList), + } + case *ast.FuncDecl: + if v == nil { + return v + } + return &ast.FuncDecl{ + Recv: clone(v.Recv).(*ast.FieldList), + Name: v.Name, + Type: clone(v.Type).(*ast.FuncType), + Body: v.Body, // shallow + } + case *ast.ValueSpec: + if v == nil { + return v + } + o := &ast.ValueSpec{ + Type: cloneExpr(v.Type), + } + for _, x := range v.Names { + o.Names = append(o.Names, x) + } + for _, x := range v.Values { + o.Values = append(o.Values, cloneExpr(x)) + } + return o + case *ast.CallExpr: + if v == nil { + return v + } + o := &ast.CallExpr{} + *o = *v + o.Args = cloneExprs(v.Args) + o.Fun = cloneExpr(v.Fun) + return o + case *ast.SelectorExpr: + if v == nil { + return nil + } + return &ast.SelectorExpr{ + X: cloneExpr(v.X), + Sel: v.Sel, + } + case *ast.ArrayType: + return &ast.ArrayType{ + Lbrack: v.Lbrack, + Len: cloneExpr(v.Len), + Elt: cloneExpr(v.Elt), + } + case *ast.StructType: + return &ast.StructType{ + Struct: v.Struct, + Fields: clone(v.Fields).(*ast.FieldList), + Incomplete: v.Incomplete, + } + case *ast.StarExpr: + return &ast.StarExpr{ + Star: v.Star, + X: cloneExpr(v.X), + } + case *ast.CompositeLit: + return &ast.CompositeLit{ + Type: cloneExpr(v.Type), + Lbrace: v.Lbrace, + Elts: cloneExprs(v.Elts), + Rbrace: v.Rbrace, + } + case *ast.UnaryExpr: + return &ast.UnaryExpr{ + OpPos: v.OpPos, + Op: v.Op, + X: cloneExpr(v.X), + } + case *ast.BinaryExpr: + return &ast.BinaryExpr{ + OpPos: v.OpPos, + Op: v.Op, + X: cloneExpr(v.X), + Y: cloneExpr(v.Y), + } + case *ast.Ellipsis: + return &ast.Ellipsis{ + Ellipsis: v.Ellipsis, + Elt: cloneExpr(v.Elt), + } + case *ast.KeyValueExpr: + return &ast.KeyValueExpr{ + Key: cloneExpr(v.Key), + Colon: v.Colon, + Value: cloneExpr(v.Value), + } + case *ast.FuncLit: + return &ast.FuncLit{ + Type: clone(v.Type).(*ast.FuncType), + Body: v.Body, // shallow + } + case *ast.MapType: + return &ast.MapType{ + Map: v.Map, + Key: cloneExpr(v.Key), + Value: cloneExpr(v.Value), + } + case *ast.ParenExpr: + return &ast.ParenExpr{ + Lparen: v.Lparen, + X: cloneExpr(v.X), + Rparen: v.Rparen, + } + case *ast.Ident, *ast.BasicLit: + return v + case *ast.ImportSpec: + return &ast.ImportSpec{ + Doc: v.Doc, // shallow + Name: v.Name, + Path: clone(v.Path).(*ast.BasicLit), + Comment: v.Comment, // shallow + EndPos: v.EndPos, + } + case *ast.ChanType: + return &ast.ChanType{ + Begin: v.Begin, + Arrow: v.Arrow, + Dir: v.Dir, + Value: cloneExpr(v.Value), + } + case *ast.TypeAssertExpr: + return &ast.TypeAssertExpr{ + X: cloneExpr(v.X), + Type: cloneExpr(v.Type), + } + case *ast.IndexExpr: + return &ast.IndexExpr{ + X: cloneExpr(v.X), + Index: cloneExpr(v.Index), + Lbrack: v.Lbrack, + Rbrack: v.Rbrack, + } + } + panic(fmt.Sprintf("Uncloneable type %T", i)) +} + +func cloneExpr(x ast.Expr) ast.Expr { + if x == nil { + return nil + } + return clone(x).(ast.Expr) +} + +func cloneExprs(x []ast.Expr) []ast.Expr { + if x == nil { + return nil + } + o := make([]ast.Expr, len(x)) + for i, x := range x { + o[i] = cloneExpr(x) + } + return o +} diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index ad1c6bb8c..0d76b0cdb 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -22,12 +22,14 @@ import ( "go/parser" "go/printer" "go/token" + "io" "io/ioutil" "log" "os" "os/exec" "path" "path/filepath" + "regexp" "runtime" "sort" "strconv" @@ -38,11 +40,12 @@ import ( var ( // TODO(bradfitz): once Go 1.1 comes out, allow the -c flag to take a comma-separated // list of files, rather than just one. - checkFile = flag.String("c", "", "optional filename to check API against") - allowNew = flag.Bool("allow_new", true, "allow API additions") - nextFile = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.") - verbose = flag.Bool("v", false, "verbose debugging") - forceCtx = flag.String("contexts", "", "optional comma-separated list of -[-cgo] to override default contexts.") + checkFile = flag.String("c", "", "optional filename to check API against") + allowNew = flag.Bool("allow_new", true, "allow API additions") + exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool") + nextFile = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.") + verbose = flag.Bool("v", false, "verbose debugging") + forceCtx = flag.String("contexts", "", "optional comma-separated list of -[-cgo] to override default contexts.") ) // contexts are the default contexts which are scanned, unless @@ -100,7 +103,7 @@ func setContexts() { func main() { flag.Parse() - if !strings.Contains(runtime.Version(), "weekly") { + if !strings.Contains(runtime.Version(), "weekly") && !strings.Contains(runtime.Version(), "devel") { if *nextFile != "" { fmt.Printf("Go version is %q, ignoring -next %s\n", runtime.Version(), *nextFile) *nextFile = "" @@ -166,7 +169,6 @@ func main() { features = append(features, f2) } } - sort.Strings(features) fail := false defer func() { @@ -179,24 +181,45 @@ func main() { defer bw.Flush() if *checkFile == "" { + sort.Strings(features) for _, f := range features { fmt.Fprintf(bw, "%s\n", f) } return } - var required []string - for _, filename := range []string{*checkFile} { - required = append(required, fileFeatures(filename)...) + required := fileFeatures(*checkFile) + optional := fileFeatures(*nextFile) + exception := fileFeatures(*exceptFile) + fail = !compareAPI(bw, features, required, optional, exception) +} + +func set(items []string) map[string]bool { + s := make(map[string]bool) + for _, v := range items { + s[v] = true } - sort.Strings(required) + return s +} - var optional = make(map[string]bool) // feature => true - if *nextFile != "" { - for _, feature := range fileFeatures(*nextFile) { - optional[feature] = true - } +var spaceParensRx = regexp.MustCompile(` \(\S+?\)`) + +func featureWithoutContext(f string) string { + if !strings.Contains(f, "(") { + return f } + return spaceParensRx.ReplaceAllString(f, "") +} + +func compareAPI(w io.Writer, features, required, optional, exception []string) (ok bool) { + ok = true + + optionalSet := set(optional) + exceptionSet := set(exception) + featureSet := set(features) + + sort.Strings(features) + sort.Strings(required) take := func(sl *[]string) string { s := (*sl)[0] @@ -206,20 +229,27 @@ func main() { for len(required) > 0 || len(features) > 0 { switch { - case len(features) == 0 || required[0] < features[0]: - fmt.Fprintf(bw, "-%s\n", take(&required)) - fail = true // broke compatibility - case len(required) == 0 || required[0] > features[0]: + case len(features) == 0 || (len(required) > 0 && required[0] < features[0]): + feature := take(&required) + if exceptionSet[feature] { + fmt.Fprintf(w, "~%s\n", feature) + } else if featureSet[featureWithoutContext(feature)] { + // okay. + } else { + fmt.Fprintf(w, "-%s\n", feature) + ok = false // broke compatibility + } + case len(required) == 0 || (len(features) > 0 && required[0] > features[0]): newFeature := take(&features) - if optional[newFeature] { + if optionalSet[newFeature] { // Known added feature to the upcoming release. // Delete it from the map so we can detect any upcoming features // which were never seen. (so we can clean up the nextFile) - delete(optional, newFeature) + delete(optionalSet, newFeature) } else { - fmt.Fprintf(bw, "+%s\n", newFeature) + fmt.Fprintf(w, "+%s\n", newFeature) if !*allowNew { - fail = true // we're in lock-down mode for next release + ok = false // we're in lock-down mode for next release } } default: @@ -228,17 +258,22 @@ func main() { } } + // In next file, but not in API. var missing []string - for feature := range optional { + for feature := range optionalSet { missing = append(missing, feature) } sort.Strings(missing) for _, feature := range missing { - fmt.Fprintf(bw, "(in next file, but not in API) -%s\n", feature) + fmt.Fprintf(w, "±%s\n", feature) } + return } func fileFeatures(filename string) []string { + if filename == "" { + return nil + } bs, err := ioutil.ReadFile(filename) if err != nil { log.Fatalf("Error reading file %s: %v", filename, err) @@ -256,10 +291,11 @@ type pkgSymbol struct { symbol string // "RoundTripper" } +var fset = token.NewFileSet() + type Walker struct { context *build.Context root string - fset *token.FileSet scope []string features map[string]bool // set lastConstType string @@ -276,7 +312,6 @@ type Walker struct { func NewWalker() *Walker { return &Walker{ - fset: token.NewFileSet(), features: make(map[string]bool), packageState: make(map[string]loadState), interfaces: make(map[pkgSymbol]*ast.InterfaceType), @@ -297,20 +332,6 @@ const ( loaded ) -// hardCodedConstantType is a hack until the type checker is sufficient for our needs. -// Rather than litter the code with unnecessary type annotations, we'll hard-code -// the cases we can't handle yet. -func (w *Walker) hardCodedConstantType(name string) (typ string, ok bool) { - switch w.scope[0] { - case "pkg syscall": - switch name { - case "darwinAMD64": - return "bool", true - } - } - return "", false -} - func (w *Walker) Features() (fs []string) { for f := range w.features { fs = append(fs, f) @@ -333,6 +354,21 @@ func fileDeps(f *ast.File) (pkgs []string) { return } +var parsedFileCache = make(map[string]*ast.File) + +func parseFile(filename string) (*ast.File, error) { + f, ok := parsedFileCache[filename] + if !ok { + var err error + f, err = parser.ParseFile(fset, filename, nil, 0) + if err != nil { + return nil, err + } + parsedFileCache[filename] = f + } + return clone(f).(*ast.File), nil +} + // WalkPackage walks all files in package `name'. // WalkPackage does nothing if the package has already been loaded. func (w *Walker) WalkPackage(name string) { @@ -366,7 +402,7 @@ func (w *Walker) WalkPackage(name string) { files := append(append([]string{}, info.GoFiles...), info.CgoFiles...) for _, file := range files { - f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), nil, 0) + f, err := parseFile(filepath.Join(dir, file)) if err != nil { log.Fatalf("error parsing package %s, file %s: %v", name, file, err) } @@ -501,7 +537,7 @@ func (w *Walker) walkFile(file *ast.File) { // Ignore. Handled in subsequent pass, by go/doc. default: log.Printf("unhandled %T, %#v\n", di, di) - printer.Fprint(os.Stderr, w.fset, di) + printer.Fprint(os.Stderr, fset, di) os.Stderr.Write([]byte("\n")) } } @@ -562,6 +598,10 @@ func (w *Walker) constValueType(vi interface{}) (string, error) { } return constDepPrefix + v.Name, nil case *ast.BinaryExpr: + switch v.Op { + case token.EQL, token.LSS, token.GTR, token.NOT, token.NEQ, token.LEQ, token.GEQ: + return "bool", nil + } left, err := w.constValueType(v.X) if err != nil { return "", err @@ -734,17 +774,11 @@ func (w *Walker) walkConst(vs *ast.ValueSpec) { var err error litType, err = w.constValueType(vs.Values[0]) if err != nil { - if t, ok := w.hardCodedConstantType(ident.Name); ok { - litType = t - err = nil - } else { - log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err) - } + log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err) } } } - if strings.HasPrefix(litType, constDepPrefix) { - dep := litType[len(constDepPrefix):] + if dep := strings.TrimPrefix(litType, constDepPrefix); dep != litType { w.constDep[ident.Name] = dep continue } @@ -816,7 +850,7 @@ func (w *Walker) nodeString(node interface{}) string { return "" } var b bytes.Buffer - printer.Fprint(&b, w.fset, node) + printer.Fprint(&b, fset, node) return b.String() } @@ -825,7 +859,7 @@ func (w *Walker) nodeDebug(node interface{}) string { return "" } var b bytes.Buffer - ast.Fprint(&b, w.fset, node, nil) + ast.Fprint(&b, fset, node, nil) return b.String() } @@ -844,7 +878,7 @@ func (w *Walker) walkTypeSpec(ts *ast.TypeSpec) { case *ast.InterfaceType: w.walkInterfaceType(name, t) default: - w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(ts.Type))) + w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(w.namelessType(ts.Type)))) } } @@ -892,15 +926,18 @@ type method struct { sig string // "([]byte) (int, error)", from funcSigString } -// interfaceMethods returns the expanded list of methods for an interface. +// interfaceMethods returns the expanded list of exported methods for an interface. +// The boolean complete reports whether the list contains all methods (that is, the +// interface has no unexported methods). // pkg is the complete package name ("net/http") // iname is the interface name. -func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { +func (w *Walker) interfaceMethods(pkg, iname string) (methods []method, complete bool) { t, ok := w.interfaces[pkgSymbol{pkg, iname}] if !ok { log.Fatalf("failed to find interface %s.%s", pkg, iname) } + complete = true for _, f := range t.Methods.List { typ := f.Type switch tv := typ.(type) { @@ -912,6 +949,8 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { name: mname.Name, sig: w.funcSigString(ft), }) + } else { + complete = false } } case *ast.Ident: @@ -927,7 +966,9 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { log.Fatalf("unexported embedded interface %q in exported interface %s.%s; confused", embedded, pkg, iname) } - methods = append(methods, w.interfaceMethods(pkg, embedded)...) + m, c := w.interfaceMethods(pkg, embedded) + methods = append(methods, m...) + complete = complete && c case *ast.SelectorExpr: lhs := w.nodeString(tv.X) rhs := w.nodeString(tv.Sel) @@ -935,7 +976,9 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { if !ok { log.Fatalf("can't resolve selector %q in interface %s.%s", lhs, pkg, iname) } - methods = append(methods, w.interfaceMethods(fpkg, rhs)...) + m, c := w.interfaceMethods(fpkg, rhs) + methods = append(methods, m...) + complete = complete && c default: log.Fatalf("unknown type %T in interface field", typ) } @@ -945,14 +988,28 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) { methNames := []string{} - pop := w.pushScope("type " + name + " interface") - for _, m := range w.interfaceMethods(w.curPackageName, name) { + methods, complete := w.interfaceMethods(w.curPackageName, name) + for _, m := range methods { methNames = append(methNames, m.name) w.emitFeature(fmt.Sprintf("%s%s", m.name, m.sig)) } + if !complete { + // The method set has unexported methods, so all the + // implementations are provided by the same package, + // so the method set can be extended. Instead of recording + // the full set of names (below), record only that there were + // unexported methods. (If the interface shrinks, we will notice + // because a method signature emitted during the last loop, + // will disappear.) + w.emitFeature("unexported methods") + } pop() + if !complete { + return + } + sort.Strings(methNames) if len(methNames) == 0 { w.emitFeature(fmt.Sprintf("type %s interface {}", name)) @@ -994,18 +1051,38 @@ func (w *Walker) walkFuncDecl(f *ast.FuncDecl) { func (w *Walker) funcSigString(ft *ast.FuncType) string { var b bytes.Buffer + writeField := func(b *bytes.Buffer, f *ast.Field) { + if n := len(f.Names); n > 1 { + for i := 0; i < n; i++ { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(w.nodeString(w.namelessType(f.Type))) + } + } else { + b.WriteString(w.nodeString(w.namelessType(f.Type))) + } + } b.WriteByte('(') if ft.Params != nil { for i, f := range ft.Params.List { if i > 0 { b.WriteString(", ") } - b.WriteString(w.nodeString(w.namelessType(f.Type))) + writeField(&b, f) } } b.WriteByte(')') if ft.Results != nil { - if nr := len(ft.Results.List); nr > 0 { + nr := 0 + for _, f := range ft.Results.List { + if n := len(f.Names); n > 1 { + nr += n + } else { + nr++ + } + } + if nr > 0 { b.WriteByte(' ') if nr > 1 { b.WriteByte('(') @@ -1014,7 +1091,7 @@ func (w *Walker) funcSigString(ft *ast.FuncType) string { if i > 0 { b.WriteString(", ") } - b.WriteString(w.nodeString(w.namelessType(f.Type))) + writeField(&b, f) } if nr > 1 { b.WriteByte(')') @@ -1042,7 +1119,13 @@ func (w *Walker) namelessFieldList(fl *ast.FieldList) *ast.FieldList { fl2 := &ast.FieldList{} if fl != nil { for _, f := range fl.List { - fl2.List = append(fl2.List, w.namelessField(f)) + repeats := 1 + if len(f.Names) > 1 { + repeats = len(f.Names) + } + for i := 0; i < repeats; i++ { + fl2.List = append(fl2.List, w.namelessField(f)) + } } } return fl2 @@ -1056,10 +1139,21 @@ func (w *Walker) namelessField(f *ast.Field) *ast.Field { } } +var ( + byteRx = regexp.MustCompile(`\bbyte\b`) + runeRx = regexp.MustCompile(`\brune\b`) +) + func (w *Walker) emitFeature(feature string) { if !w.wantedPkg[w.curPackageName] { return } + if strings.Contains(feature, "byte") { + feature = byteRx.ReplaceAllString(feature, "uint8") + } + if strings.Contains(feature, "rune") { + feature = runeRx.ReplaceAllString(feature, "int32") + } f := strings.Join(w.scope, ", ") + ", " + feature if _, dup := w.features[f]; dup { panic("duplicate feature inserted: " + f) @@ -1075,6 +1169,7 @@ func (w *Walker) emitFeature(feature string) { } panic("feature contains newlines: " + f) } + w.features[f] = true if *verbose { log.Printf("feature: %s", f) diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go index c7cc601b1..1a86c0ec7 100644 --- a/src/cmd/api/goapi_test.go +++ b/src/cmd/api/goapi_test.go @@ -5,6 +5,7 @@ package main import ( + "bytes" "flag" "fmt" "io/ioutil" @@ -73,3 +74,68 @@ func TestGolden(t *testing.T) { } } } + +func TestCompareAPI(t *testing.T) { + tests := []struct { + name string + features, required, optional, exception []string + ok bool // want + out string // want + }{ + { + name: "feature added", + features: []string{"A", "B", "C", "D", "E", "F"}, + required: []string{"B", "D"}, + ok: true, + out: "+A\n+C\n+E\n+F\n", + }, + { + name: "feature removed", + features: []string{"C", "A"}, + required: []string{"A", "B", "C"}, + ok: false, + out: "-B\n", + }, + { + name: "feature added then removed", + features: []string{"A", "C"}, + optional: []string{"B"}, + required: []string{"A", "C"}, + ok: true, + out: "±B\n", + }, + { + name: "exception removal", + required: []string{"A", "B", "C"}, + features: []string{"A", "C"}, + exception: []string{"B"}, + ok: true, + out: "~B\n", + }, + { + // http://golang.org/issue/4303 + name: "contexts reconverging", + required: []string{ + "A", + "pkg syscall (darwin-386), type RawSockaddrInet6 struct", + "pkg syscall (darwin-amd64), type RawSockaddrInet6 struct", + }, + features: []string{ + "A", + "pkg syscall, type RawSockaddrInet6 struct", + }, + ok: true, + out: "+pkg syscall, type RawSockaddrInet6 struct\n", + }, + } + for _, tt := range tests { + buf := new(bytes.Buffer) + gotok := compareAPI(buf, tt.features, tt.required, tt.optional, tt.exception) + if gotok != tt.ok { + t.Errorf("%s: ok = %v; want %v", tt.name, gotok, tt.ok) + } + if got := buf.String(); got != tt.out { + t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out) + } + } +} diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt index e334e5776..abcc0ce6c 100644 --- a/src/cmd/api/testdata/src/pkg/p1/golden.txt +++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt @@ -10,6 +10,7 @@ pkg p1, func Bar(int8, int16, int64) pkg p1, func Bar1(int8, int16, int64) uint64 pkg p1, func Bar2(int8, int16, int64) (uint8, uint64) pkg p1, func BarE() Error +pkg p1, func PlainFunc(int, int, string) (*B, error) pkg p1, func TakesFunc(func(int) int) pkg p1, method (*B) JustOnB() pkg p1, method (*B) OnBothTandBPtr() @@ -27,6 +28,9 @@ pkg p1, method (TPtrExported) OnEmbedded() pkg p1, method (TPtrUnexported) OnBothTandBPtr() pkg p1, method (TPtrUnexported) OnBothTandBVal() pkg p1, type B struct +pkg p1, type ByteStruct struct +pkg p1, type ByteStruct struct, B uint8 +pkg p1, type ByteStruct struct, R int32 pkg p1, type Codec struct pkg p1, type Codec struct, Func func(int, int) int pkg p1, type EmbedSelector struct @@ -37,15 +41,21 @@ pkg p1, type Embedded struct pkg p1, type Error interface { Error, Temporary } pkg p1, type Error interface, Error() string pkg p1, type Error interface, Temporary() bool -pkg p1, type I interface { Get, GetNamed, Name, PackageTwoMeth, Set } +pkg p1, type FuncType func(int, int, string) (*B, error) pkg p1, type I interface, Get(string) int64 pkg p1, type I interface, GetNamed(string) int64 pkg p1, type I interface, Name() string pkg p1, type I interface, PackageTwoMeth() pkg p1, type I interface, Set(string, int64) +pkg p1, type I interface, unexported methods pkg p1, type MyInt int pkg p1, type Namer interface { Name } pkg p1, type Namer interface, Name() string +pkg p1, type Private interface, X() +pkg p1, type Private interface, unexported methods +pkg p1, type Public interface { X, Y } +pkg p1, type Public interface, X() +pkg p1, type Public interface, Y() pkg p1, type S struct pkg p1, type S struct, Public *int pkg p1, type S struct, PublicTime time.Time @@ -58,7 +68,9 @@ pkg p1, type T struct pkg p1, type TPtrExported struct pkg p1, type TPtrExported struct, embedded *Embedded pkg p1, type TPtrUnexported struct -pkg p1, var ByteConv []byte +pkg p1, var Byte uint8 +pkg p1, var ByteConv []uint8 +pkg p1, var ByteFunc func(uint8) int32 pkg p1, var ChecksumError error pkg p1, var SIPtr *SI pkg p1, var SIPtr2 *SI diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go index d965bb75e..f94c9ceeb 100644 --- a/src/cmd/api/testdata/src/pkg/p1/p1.go +++ b/src/cmd/api/testdata/src/pkg/p1/p1.go @@ -78,6 +78,16 @@ type I interface { private() } +type Public interface { + X() + Y() +} + +type Private interface { + X() + y() +} + type Error interface { error Temporary() bool @@ -139,8 +149,12 @@ type TPtrExported struct { *Embedded } +type FuncType func(x, y int, s string) (b *B, err error) + type Embedded struct{} +func PlainFunc(x, y int, s string) (b *B, err error) + func (*Embedded) OnEmbedded() {} func (*T) JustOnT() {} @@ -151,3 +165,39 @@ func (common) OnBothTandBVal() {} type EmbedSelector struct { time.Time } + +const ( + foo = "foo" + foo2 string = "foo2" + truth = foo == "foo" || foo2 == "foo2" +) + +func ellipsis(...string) {} + +var x = &S{ + Public: nil, + private: nil, + publicTime: time.Now(), +} + +var parenExpr = (1 + 5) + +var funcLit = func() {} + +var m map[string]int + +var chanVar chan int + +var ifaceVar interface{} = 5 + +var assertVar = ifaceVar.(int) + +var indexVar = m["foo"] + +var Byte byte +var ByteFunc func(byte) rune + +type ByteStruct struct { + B byte + R rune +} diff --git a/src/cmd/api/testdata/src/pkg/p3/golden.txt b/src/cmd/api/testdata/src/pkg/p3/golden.txt new file mode 100644 index 000000000..a7dcccd1b --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p3/golden.txt @@ -0,0 +1,3 @@ +pkg p3, func BadHop(int, int, int) (bool, bool, *ThirdBase, *ThirdBase, error) +pkg p3, method (*ThirdBase) GoodPlayer() (int, int, int) +pkg p3, type ThirdBase struct diff --git a/src/cmd/api/testdata/src/pkg/p3/p3.go b/src/cmd/api/testdata/src/pkg/p3/p3.go new file mode 100644 index 000000000..1b2b1a426 --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p3/p3.go @@ -0,0 +1,6 @@ +package p3 + +type ThirdBase struct{} + +func (tb *ThirdBase) GoodPlayer() (i, j, k int) +func BadHop(i, j, k int) (l, m bool, n, o *ThirdBase, err error) diff --git a/src/cmd/cc/Makefile b/src/cmd/cc/Makefile index 109578297..34df31d6f 100644 --- a/src/cmd/cc/Makefile +++ b/src/cmd/cc/Makefile @@ -7,4 +7,4 @@ include ../../Make.dist install: y.tab.h y.tab.h: cc.y - LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y + LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y cc.y diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index 4c527a2b3..866aeb0ab 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -300,6 +300,7 @@ enum OPOSTINC, OPREDEC, OPREINC, + OPREFETCH, OPROTO, OREGISTER, ORETURN, @@ -324,6 +325,7 @@ enum OINDEX, OFAS, OREGPAIR, + OROTL, OEND }; @@ -442,25 +444,6 @@ struct Funct Sym* castfr[NTYPE]; }; -struct Dynimp -{ - char* local; - char* remote; - char* path; -}; - -EXTERN Dynimp *dynimp; -EXTERN int ndynimp; - -struct Dynexp -{ - char* local; - char* remote; -}; - -EXTERN Dynexp *dynexp; -EXTERN int ndynexp; - EXTERN struct { Type* tenum; /* type of entire enum */ @@ -473,7 +456,7 @@ EXTERN int autobn; EXTERN int32 autoffset; EXTERN int blockno; EXTERN Decl* dclstack; -EXTERN char debug[256]; +EXTERN int debug[256]; EXTERN Hist* ehist; EXTERN int32 firstbit; EXTERN Sym* firstarg; @@ -526,10 +509,12 @@ EXTERN int packflg; EXTERN int fproundflg; EXTERN int textflag; EXTERN int dataflag; +EXTERN int flag_largemodel; EXTERN int ncontin; EXTERN int canreach; EXTERN int warnreach; EXTERN Bits zbits; +EXTERN Fmt pragcgobuf; extern char *onames[], *tnames[], *gnames[]; extern char *cnames[], *qnames[], *bnames[]; @@ -771,8 +756,7 @@ void pragfpround(void); void pragdataflag(void); void pragtextflag(void); void pragincomplete(void); -void pragdynimport(void); -void pragdynexport(void); +void pragcgo(char*); /* * calls to machine depend part diff --git a/src/cmd/cc/cc.y b/src/cmd/cc/cc.y index 515a80372..830dd21f8 100644 --- a/src/cmd/cc/cc.y +++ b/src/cmd/cc/cc.y @@ -93,7 +93,7 @@ %token LSTRING LLSTRING %token LAUTO LBREAK LCASE LCHAR LCONTINUE LDEFAULT LDO %token LDOUBLE LELSE LEXTERN LFLOAT LFOR LGOTO -%token LIF LINT LLONG LREGISTER LRETURN LSHORT LSIZEOF LUSED +%token LIF LINT LLONG LPREFETCH LREGISTER LRETURN LSHORT LSIZEOF LUSED %token LSTATIC LSTRUCT LSWITCH LTYPEDEF LTYPESTR LUNION LUNSIGNED %token LWHILE LVOID LENUM LSIGNED LCONSTNT LVOLATILE LSET LSIGNOF %token LRESTRICT LINLINE @@ -535,6 +535,10 @@ ulstmnt: { $$ = new(OUSED, $3, Z); } +| LPREFETCH '(' zelist ')' ';' + { + $$ = new(OPREFETCH, $3, Z); + } | LSET '(' zelist ')' ';' { $$ = new(OSET, $3, Z); diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index a3ed9772d..edfc7e75a 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -1058,6 +1058,10 @@ sigind(Type *t, Typetab *tt) return p-a; if((n&15) == 0){ na = malloc((n+16)*sizeof(Type*)); + if(na == nil) { + print("%s: out of memory", argv0); + errorexit(); + } memmove(na, a, n*sizeof(Type*)); free(a); a = tt->a = na; diff --git a/src/cmd/cc/doc.go b/src/cmd/cc/doc.go index 51aa8b192..10901b441 100644 --- a/src/cmd/cc/doc.go +++ b/src/cmd/cc/doc.go @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* This directory contains the portable section of the Plan 9 C compilers. See ../6c, ../8c, and ../5c for more information. */ -package documentation +package main diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c index c579e20d9..2f038f520 100644 --- a/src/cmd/cc/dpchk.c +++ b/src/cmd/cc/dpchk.c @@ -662,65 +662,93 @@ getimpsym(void) return lookup(); } -void -pragdynimport(void) +static int +more(void) { - Sym *local, *remote; - char *path; - Dynimp *f; - - local = getimpsym(); - if(local == nil) - goto err; - - remote = getimpsym(); - if(remote == nil) - goto err; - - path = getquoted(); - if(path == nil) - goto err; - - if(ndynimp%32 == 0) - dynimp = realloc(dynimp, (ndynimp+32)*sizeof dynimp[0]); - f = &dynimp[ndynimp++]; - f->local = local->name; - f->remote = remote->name; - f->path = path; - goto out; - -err: - yyerror("usage: #pragma dynimport local remote \"path\""); - -out: - while(getnsc() != '\n') - ; + int c; + + do + c = getnsc(); + while(c == ' ' || c == '\t'); + unget(c); + return c != '\n'; } void -pragdynexport(void) +pragcgo(char *verb) { Sym *local, *remote; - Dynexp *f; - - local = getsym(); - if(local == nil) - goto err; - - remote = getsym(); - if(remote == nil) - goto err; + char *p; - if(ndynexp%32 == 0) - dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); - f = &dynexp[ndynexp++]; - f->local = local->name; - f->remote = remote->name; - goto out; - -err: - yyerror("usage: #pragma dynexport local remote"); + if(strcmp(verb, "cgo_dynamic_linker") == 0 || strcmp(verb, "dynlinker") == 0) { + p = getquoted(); + if(p == nil) + goto err1; + fmtprint(&pragcgobuf, "cgo_dynamic_linker %q\n", p); + goto out; + + err1: + yyerror("usage: #pragma cgo_dynamic_linker \"path\""); + goto out; + } + + if(strcmp(verb, "cgo_export") == 0 || strcmp(verb, "dynexport") == 0) { + local = getimpsym(); + if(local == nil) + goto err2; + if(!more()) { + fmtprint(&pragcgobuf, "cgo_export %q\n", local->name); + goto out; + } + remote = getimpsym(); + if(remote == nil) + goto err2; + fmtprint(&pragcgobuf, "cgo_export %q %q\n", local->name, remote->name); + goto out; + + err2: + yyerror("usage: #pragma cgo_export local [remote]"); + goto out; + } + + if(strcmp(verb, "cgo_import_dynamic") == 0 || strcmp(verb, "dynimport") == 0) { + local = getimpsym(); + if(local == nil) + goto err3; + if(!more()) { + fmtprint(&pragcgobuf, "cgo_import_dynamic %q\n", local->name); + goto out; + } + remote = getimpsym(); + if(remote == nil) + goto err3; + if(!more()) { + fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q\n", local->name, remote->name); + goto out; + } + p = getquoted(); + if(p == nil) + goto err3; + fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q %q\n", local->name, remote->name, p); + goto out; + + err3: + yyerror("usage: #pragma cgo_import_dynamic local [remote [\"library\"]]"); + goto out; + } + + if(strcmp(verb, "cgo_import_static") == 0) { + local = getimpsym(); + if(local == nil) + goto err4; + fmtprint(&pragcgobuf, "cgo_import_static %q\n", local->name); + goto out; + err4: + yyerror("usage: #pragma cgo_import_static local [remote]"); + goto out; + } + out: while(getnsc() != '\n') ; diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c index 4274c5626..7457bd000 100644 --- a/src/cmd/cc/godefs.c +++ b/src/cmd/cc/godefs.c @@ -188,10 +188,10 @@ printtypename(Type *t) switch(t->etype) { case TINT: - Bprint(&outbuf, "int"); + Bprint(&outbuf, "int32"); break; case TUINT: - Bprint(&outbuf, "uint"); + Bprint(&outbuf, "uint32"); break; case TCHAR: Bprint(&outbuf, "int8"); diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 8aeb1a334..4fb0be9a4 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -85,11 +85,38 @@ pathchar(void) * -. Inhibit search for includes in source directory */ +void +usage(void) +{ + print("usage: %cc [options] file.c...\n", thechar); + flagprint(1); + errorexit(); +} + +void +dospim(void) +{ + thechar = '0'; + thestring = "spim"; +} + +char **defs; +int ndef; + +void +dodef(char *p) +{ + if(ndef%8 == 0) + defs = allocn(defs, ndef*sizeof(char *), + 8*sizeof(char *)); + defs[ndef++] = p; + dodefine(p); +} + void main(int argc, char *argv[]) { - char **defs, *p; - int c, ndef; + int c; ensuresymb(NSYMB); memset(debug, 0, sizeof(debug)); @@ -97,52 +124,67 @@ main(int argc, char *argv[]) cinit(); ginit(); arginit(); + + fmtstrinit(&pragcgobuf); + quotefmtinstall(); tufield = simplet((1L<etype) | BUNSIGNED); ndef = 0; defs = nil; outfile = 0; setinclude("."); - ARGBEGIN { - default: - c = ARGC(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - - case 'l': /* for little-endian mips */ - if(thechar != 'v'){ - print("can only use -l with vc\n"); - errorexit(); - } - thechar = '0'; - thestring = "spim"; - break; - - case 'o': - outfile = ARGF(); - break; - case 'D': - p = ARGF(); - if(p) { - if(ndef%8 == 0) - defs = allocn(defs, ndef*sizeof(char *), - 8*sizeof(char *)); - defs[ndef++] = p; - dodefine(p); - } - break; + flagcount("+", "pass -+ to preprocessor", &debug['+']); + flagcount(".", "pass -. to preprocessor", &debug['.']); + flagcount("<", "debug shift", &debug['<']); + flagcount("A", "debug alignment", &debug['A']); + flagcount("B", "allow pre-ANSI code", &debug['B']); + if(thechar == '5') + flagcount("C", "debug constant propagation", &debug['C']); + flagfn1("D", "name[=value]: add #define", dodef); + flagcount("F", "enable print format checks", &debug['F']); + if(thechar == '5') + flagcount("H", "debug shift propagation", &debug['H']); + flagfn1("I", "dir: add dir to include path", setinclude); + flagcount("L", "debug lexer", &debug['L']); + flagcount("M", "debug move generation", &debug['M']); + flagcount("N", "disable optimizations", &debug['N']); + flagcount("P", "debug peephole optimizer", &debug['P']); + flagcount("Q", "print exported Go definitions", &debug['Q']); + flagcount("R", "debug register optimizer", &debug['R']); + flagcount("S", "print assembly", &debug['S']); + flagcount("T", "enable type signatures", &debug['T']); + flagcount("V", "enable pointer type checks", &debug['V']); + flagcount("W", "debug switch generation", &debug['W']); + flagcount("X", "abort on error", &debug['X']); + flagcount("Y", "debug index generation", &debug['Y']); + flagcount("Z", "skip code generation", &debug['Z']); + flagcount("a", "print acid definitions", &debug['a']); + flagcount("c", "debug constant evaluation", &debug['c']); + flagcount("d", "debug declarations", &debug['d']); + flagcount("e", "debug macro expansion", &debug['e']); + flagcount("f", "debug pragmas", &debug['f']); + flagcount("g", "debug code generation", &debug['g']); + flagcount("i", "debug initialization", &debug['i']); + if(thechar == 'v') + flagfn0("l", "little-endian mips mode", dospim); + flagcount("m", "debug multiplication", &debug['m']); + flagcount("n", "print acid/Go to file, not stdout", &debug['n']); + flagstr("o", "file: set output file", &outfile); + flagcount("p", "invoke C preprocessor", &debug['p']); + flagcount("q", "print Go definitions", &debug['q']); + flagcount("s", "print #define assembly offsets", &debug['s']); + flagcount("t", "debug code generation", &debug['t']); + flagcount("w", "enable warnings", &debug['w']); + flagcount("v", "increase debug verbosity", &debug['v']); + if(thechar == '6') + flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel); + + flagparse(&argc, &argv, usage); + + if(argc < 1 && outfile == 0) + usage(); - case 'I': - p = ARGF(); - setinclude(p); - break; - } ARGEND - if(argc < 1 && outfile == 0) { - print("usage: %cc [-options] files\n", thechar); - errorexit(); - } if(argc > 1){ print("can't compile multiple files\n"); errorexit(); @@ -377,11 +419,16 @@ lookup(void) symb[1] = '"'; } - // turn · into . for(r=w=symb; *r; r++) { + // turn · (U+00B7) into . + // turn ∕ (U+2215) into / if((uchar)*r == 0xc2 && (uchar)*(r+1) == 0xb7) { *w++ = '.'; r++; + }else if((uchar)*r == 0xe2 && (uchar)*(r+1) == 0x88 && (uchar)*(r+2) == 0x95) { + *w++ = '/'; + r++; + r++; }else *w++ = *r; } @@ -1174,6 +1221,7 @@ struct "inline", LINLINE, 0, "int", LINT, TINT, "long", LLONG, TLONG, + "PREFETCH", LPREFETCH, 0, "register", LREGISTER, 0, "restrict", LRESTRICT, 0, "return", LRETURN, 0, diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody index d339cf9a2..5fa980267 100644 --- a/src/cmd/cc/lexbody +++ b/src/cmd/cc/lexbody @@ -47,15 +47,9 @@ pragvararg(void) } void -pragdynimport(void) -{ - while(getnsc() != '\n') - ; -} - -void -pragdynexport(void) +pragcgo(char *name) { + USED(name); while(getnsc() != '\n') ; } @@ -244,11 +238,16 @@ lookup(void) symb[1] = '"'; } - // turn · into . for(r=w=symb; *r; r++) { + // turn · (U+00B7) into . + // turn ∕ (U+2215) into / if((uchar)*r == 0xc2 && (uchar)*(r+1) == 0xb7) { *w++ = '.'; r++; + }else if((uchar)*r == 0xe2 && (uchar)*(r+1) == 0x88 && (uchar)*(r+2) == 0x95) { + *w++ = '/'; + r++; + r++; }else *w++ = *r; } diff --git a/src/cmd/cc/macbody b/src/cmd/cc/macbody index 874e82d25..f0a507669 100644 --- a/src/cmd/cc/macbody +++ b/src/cmd/cc/macbody @@ -743,12 +743,8 @@ macprag(void) pragincomplete(); return; } - if(s && strcmp(s->name, "dynimport") == 0) { - pragdynimport(); - return; - } - if(s && strcmp(s->name, "dynexport") == 0) { - pragdynexport(); + if(s && (strncmp(s->name, "cgo_", 4) == 0 || strncmp(s->name, "dyn", 3) == 0)) { + pragcgo(s->name); return; } while(getnsc() != '\n') diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index 3a686102f..b06aa996d 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -528,6 +528,7 @@ loop: case OSET: case OUSED: + case OPREFETCH: usedset(n->left, o); break; } @@ -542,6 +543,10 @@ usedset(Node *n, int o) return; } complex(n); + if(o == OPREFETCH) { + gprefetch(n); + return; + } switch(n->op) { case OADDR: /* volatile */ gins(ANOP, n, Z); diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c index 72d671b2f..3a5576385 100644 --- a/src/cmd/cc/sub.c +++ b/src/cmd/cc/sub.c @@ -847,12 +847,6 @@ simplifyshift(Node *n) c2 = n->left->left->right->vconst; c3 = n->left->right->vconst; -/* - if(debug['h']) - print("%.3o %d %d %d #%.ux\n", - (s1<<3)|s2, c1, c2, topbit(c3), c3); -*/ - o = n->op; switch((s1<<3)|s2) { case 000: /* (((e <type = types[TLONG]; + (yyval.node) = Z; } break; - case 106: -#line 565 "cc.y" + case 105: +#line 561 "cc.y" { - (yyval.node) = new(OCOMMA, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OCAST, (yyvsp[(1) - (1)].node), Z); + (yyval.node)->type = types[TLONG]; } break; - case 108: -#line 572 "cc.y" + case 107: +#line 569 "cc.y" { - (yyval.node) = new(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OCOMMA, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 109: #line 576 "cc.y" { - (yyval.node) = new(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 110: #line 580 "cc.y" { - (yyval.node) = new(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 111: #line 584 "cc.y" { - (yyval.node) = new(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 112: #line 588 "cc.y" { - (yyval.node) = new(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 113: #line 592 "cc.y" { - (yyval.node) = new(OASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 114: #line 596 "cc.y" { - (yyval.node) = new(OASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 115: #line 600 "cc.y" { - (yyval.node) = new(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 116: #line 604 "cc.y" { - (yyval.node) = new(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 117: #line 608 "cc.y" { - (yyval.node) = new(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 118: #line 612 "cc.y" { - (yyval.node) = new(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 119: #line 616 "cc.y" { - (yyval.node) = new(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 120: #line 620 "cc.y" { - (yyval.node) = new(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 121: #line 624 "cc.y" { - (yyval.node) = new(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 122: #line 628 "cc.y" { - (yyval.node) = new(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 123: #line 632 "cc.y" { - (yyval.node) = new(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 124: #line 636 "cc.y" { - (yyval.node) = new(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 125: #line 640 "cc.y" { - (yyval.node) = new(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 126: #line 644 "cc.y" { - (yyval.node) = new(OCOND, (yyvsp[(1) - (5)].node), new(OLIST, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node))); + (yyval.node) = new(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 127: #line 648 "cc.y" { - (yyval.node) = new(OAS, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OCOND, (yyvsp[(1) - (5)].node), new(OLIST, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node))); } break; case 128: #line 652 "cc.y" { - (yyval.node) = new(OASADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OAS, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 129: #line 656 "cc.y" { - (yyval.node) = new(OASSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 130: #line 660 "cc.y" { - (yyval.node) = new(OASMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 131: #line 664 "cc.y" { - (yyval.node) = new(OASDIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 132: #line 668 "cc.y" { - (yyval.node) = new(OASMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASDIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 133: #line 672 "cc.y" { - (yyval.node) = new(OASASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 134: #line 676 "cc.y" { - (yyval.node) = new(OASASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 135: #line 680 "cc.y" { - (yyval.node) = new(OASAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 136: #line 684 "cc.y" { - (yyval.node) = new(OASXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 137: #line 688 "cc.y" + { + (yyval.node) = new(OASXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 138: +#line 692 "cc.y" { (yyval.node) = new(OASOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 139: -#line 695 "cc.y" + case 140: +#line 699 "cc.y" { (yyval.node) = new(OCAST, (yyvsp[(5) - (5)].node), Z); dodecl(NODECL, CXXX, (yyvsp[(2) - (5)].type), (yyvsp[(3) - (5)].node)); @@ -2851,8 +2861,8 @@ yyreduce: } break; - case 140: -#line 702 "cc.y" + case 141: +#line 706 "cc.y" { (yyval.node) = new(OSTRUCT, (yyvsp[(6) - (7)].node), Z); dodecl(NODECL, CXXX, (yyvsp[(2) - (7)].type), (yyvsp[(3) - (7)].node)); @@ -2860,85 +2870,85 @@ yyreduce: } break; - case 142: -#line 711 "cc.y" - { - (yyval.node) = new(OIND, (yyvsp[(2) - (2)].node), Z); - } - break; - case 143: #line 715 "cc.y" { - (yyval.node) = new(OADDR, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OIND, (yyvsp[(2) - (2)].node), Z); } break; case 144: #line 719 "cc.y" { - (yyval.node) = new(OPOS, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OADDR, (yyvsp[(2) - (2)].node), Z); } break; case 145: #line 723 "cc.y" { - (yyval.node) = new(ONEG, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OPOS, (yyvsp[(2) - (2)].node), Z); } break; case 146: #line 727 "cc.y" { - (yyval.node) = new(ONOT, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(ONEG, (yyvsp[(2) - (2)].node), Z); } break; case 147: #line 731 "cc.y" { - (yyval.node) = new(OCOM, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(ONOT, (yyvsp[(2) - (2)].node), Z); } break; case 148: #line 735 "cc.y" { - (yyval.node) = new(OPREINC, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OCOM, (yyvsp[(2) - (2)].node), Z); } break; case 149: #line 739 "cc.y" { - (yyval.node) = new(OPREDEC, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OPREINC, (yyvsp[(2) - (2)].node), Z); } break; case 150: #line 743 "cc.y" { - (yyval.node) = new(OSIZE, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OPREDEC, (yyvsp[(2) - (2)].node), Z); } break; case 151: #line 747 "cc.y" { - (yyval.node) = new(OSIGN, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OSIZE, (yyvsp[(2) - (2)].node), Z); } break; case 152: -#line 753 "cc.y" +#line 751 "cc.y" { - (yyval.node) = (yyvsp[(2) - (3)].node); + (yyval.node) = new(OSIGN, (yyvsp[(2) - (2)].node), Z); } break; case 153: #line 757 "cc.y" + { + (yyval.node) = (yyvsp[(2) - (3)].node); + } + break; + + case 154: +#line 761 "cc.y" { (yyval.node) = new(OSIZE, Z, Z); dodecl(NODECL, CXXX, (yyvsp[(3) - (5)].type), (yyvsp[(4) - (5)].node)); @@ -2946,8 +2956,8 @@ yyreduce: } break; - case 154: -#line 763 "cc.y" + case 155: +#line 767 "cc.y" { (yyval.node) = new(OSIGN, Z, Z); dodecl(NODECL, CXXX, (yyvsp[(3) - (5)].type), (yyvsp[(4) - (5)].node)); @@ -2955,8 +2965,8 @@ yyreduce: } break; - case 155: -#line 769 "cc.y" + case 156: +#line 773 "cc.y" { (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), Z); if((yyvsp[(1) - (4)].node)->op == ONAME) @@ -2966,45 +2976,45 @@ yyreduce: } break; - case 156: -#line 777 "cc.y" + case 157: +#line 781 "cc.y" { (yyval.node) = new(OIND, new(OADD, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)), Z); } break; - case 157: -#line 781 "cc.y" + case 158: +#line 785 "cc.y" { (yyval.node) = new(ODOT, new(OIND, (yyvsp[(1) - (3)].node), Z), Z); (yyval.node)->sym = (yyvsp[(3) - (3)].sym); } break; - case 158: -#line 786 "cc.y" + case 159: +#line 790 "cc.y" { (yyval.node) = new(ODOT, (yyvsp[(1) - (3)].node), Z); (yyval.node)->sym = (yyvsp[(3) - (3)].sym); } break; - case 159: -#line 791 "cc.y" + case 160: +#line 795 "cc.y" { (yyval.node) = new(OPOSTINC, (yyvsp[(1) - (2)].node), Z); } break; - case 160: -#line 795 "cc.y" + case 161: +#line 799 "cc.y" { (yyval.node) = new(OPOSTDEC, (yyvsp[(1) - (2)].node), Z); } break; - case 162: -#line 800 "cc.y" + case 163: +#line 804 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TINT]; @@ -3013,8 +3023,8 @@ yyreduce: } break; - case 163: -#line 807 "cc.y" + case 164: +#line 811 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TLONG]; @@ -3023,8 +3033,8 @@ yyreduce: } break; - case 164: -#line 814 "cc.y" + case 165: +#line 818 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TUINT]; @@ -3033,8 +3043,8 @@ yyreduce: } break; - case 165: -#line 821 "cc.y" + case 166: +#line 825 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TULONG]; @@ -3043,8 +3053,8 @@ yyreduce: } break; - case 166: -#line 828 "cc.y" + case 167: +#line 832 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TDOUBLE]; @@ -3053,8 +3063,8 @@ yyreduce: } break; - case 167: -#line 835 "cc.y" + case 168: +#line 839 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TFLOAT]; @@ -3063,8 +3073,8 @@ yyreduce: } break; - case 168: -#line 842 "cc.y" + case 169: +#line 846 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TVLONG]; @@ -3073,8 +3083,8 @@ yyreduce: } break; - case 169: -#line 849 "cc.y" + case 170: +#line 853 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TUVLONG]; @@ -3083,8 +3093,8 @@ yyreduce: } break; - case 172: -#line 860 "cc.y" + case 173: +#line 864 "cc.y" { (yyval.node) = new(OSTRING, Z, Z); (yyval.node)->type = typ(TARRAY, types[TCHAR]); @@ -3096,8 +3106,8 @@ yyreduce: } break; - case 173: -#line 870 "cc.y" + case 174: +#line 874 "cc.y" { char *s; int n; @@ -3115,8 +3125,8 @@ yyreduce: } break; - case 174: -#line 888 "cc.y" + case 175: +#line 892 "cc.y" { (yyval.node) = new(OLSTRING, Z, Z); (yyval.node)->type = typ(TARRAY, types[TUSHORT]); @@ -3128,8 +3138,8 @@ yyreduce: } break; - case 175: -#line 898 "cc.y" + case 176: +#line 902 "cc.y" { char *s; int n; @@ -3147,22 +3157,22 @@ yyreduce: } break; - case 176: -#line 915 "cc.y" + case 177: +#line 919 "cc.y" { (yyval.node) = Z; } break; - case 179: -#line 923 "cc.y" + case 180: +#line 927 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 180: -#line 929 "cc.y" + case 181: +#line 933 "cc.y" { (yyval.tyty).t1 = strf; (yyval.tyty).t2 = strl; @@ -3177,8 +3187,8 @@ yyreduce: } break; - case 181: -#line 942 "cc.y" + case 182: +#line 946 "cc.y" { (yyval.type) = strf; strf = (yyvsp[(2) - (4)].tyty).t1; @@ -3188,32 +3198,32 @@ yyreduce: } break; - case 182: -#line 951 "cc.y" + case 183: +#line 955 "cc.y" { lastclass = CXXX; lasttype = types[TINT]; } break; - case 184: -#line 959 "cc.y" + case 185: +#line 963 "cc.y" { (yyval.tycl).t = (yyvsp[(1) - (1)].type); (yyval.tycl).c = CXXX; } break; - case 185: -#line 964 "cc.y" + case 186: +#line 968 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval)); (yyval.tycl).c = CXXX; } break; - case 186: -#line 969 "cc.y" + case 187: +#line 973 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval)); (yyval.tycl).c = simplec((yyvsp[(1) - (1)].lval)); @@ -3221,8 +3231,8 @@ yyreduce: } break; - case 187: -#line 975 "cc.y" + case 188: +#line 979 "cc.y" { (yyval.tycl).t = (yyvsp[(1) - (2)].type); (yyval.tycl).c = simplec((yyvsp[(2) - (2)].lval)); @@ -3232,8 +3242,8 @@ yyreduce: } break; - case 188: -#line 983 "cc.y" + case 189: +#line 987 "cc.y" { (yyval.tycl).t = simplet(typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval))); (yyval.tycl).c = simplec((yyvsp[(2) - (2)].lval)); @@ -3241,8 +3251,8 @@ yyreduce: } break; - case 189: -#line 989 "cc.y" + case 190: +#line 993 "cc.y" { (yyval.tycl).t = (yyvsp[(2) - (3)].type); (yyval.tycl).c = simplec((yyvsp[(1) - (3)].lval)); @@ -3250,8 +3260,8 @@ yyreduce: } break; - case 190: -#line 995 "cc.y" + case 191: +#line 999 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(2) - (2)].lval)); (yyval.tycl).c = simplec((yyvsp[(1) - (2)].lval)); @@ -3259,8 +3269,8 @@ yyreduce: } break; - case 191: -#line 1001 "cc.y" + case 192: +#line 1005 "cc.y" { (yyval.tycl).t = simplet(typebitor((yyvsp[(2) - (3)].lval), (yyvsp[(3) - (3)].lval))); (yyval.tycl).c = simplec((yyvsp[(1) - (3)].lval)|(yyvsp[(3) - (3)].lval)); @@ -3268,8 +3278,8 @@ yyreduce: } break; - case 192: -#line 1009 "cc.y" + case 193: +#line 1013 "cc.y" { (yyval.type) = (yyvsp[(1) - (1)].tycl).t; if((yyvsp[(1) - (1)].tycl).c != CXXX) @@ -3277,31 +3287,31 @@ yyreduce: } break; - case 193: -#line 1017 "cc.y" + case 194: +#line 1021 "cc.y" { lasttype = (yyvsp[(1) - (1)].tycl).t; lastclass = (yyvsp[(1) - (1)].tycl).c; } break; - case 194: -#line 1024 "cc.y" + case 195: +#line 1028 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TSTRUCT, 0); (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; } break; - case 195: -#line 1029 "cc.y" + case 196: +#line 1033 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TSTRUCT, autobn); } break; - case 196: -#line 1033 "cc.y" + case 197: +#line 1037 "cc.y" { (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag; if((yyval.type)->link != T) @@ -3311,8 +3321,8 @@ yyreduce: } break; - case 197: -#line 1041 "cc.y" + case 198: +#line 1045 "cc.y" { taggen++; sprint(symb, "_%d_", taggen); @@ -3322,23 +3332,23 @@ yyreduce: } break; - case 198: -#line 1049 "cc.y" + case 199: +#line 1053 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TUNION, 0); (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; } break; - case 199: -#line 1054 "cc.y" + case 200: +#line 1058 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TUNION, autobn); } break; - case 200: -#line 1058 "cc.y" + case 201: +#line 1062 "cc.y" { (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag; if((yyval.type)->link != T) @@ -3348,8 +3358,8 @@ yyreduce: } break; - case 201: -#line 1066 "cc.y" + case 202: +#line 1070 "cc.y" { taggen++; sprint(symb, "_%d_", taggen); @@ -3359,8 +3369,8 @@ yyreduce: } break; - case 202: -#line 1074 "cc.y" + case 203: +#line 1078 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TENUM, 0); (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; @@ -3370,23 +3380,23 @@ yyreduce: } break; - case 203: -#line 1082 "cc.y" + case 204: +#line 1086 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TENUM, autobn); } break; - case 204: -#line 1086 "cc.y" + case 205: +#line 1090 "cc.y" { en.tenum = T; en.cenum = T; } break; - case 205: -#line 1091 "cc.y" + case 206: +#line 1095 "cc.y" { (yyval.type) = (yyvsp[(2) - (7)].sym)->suetag; if((yyval.type)->link != T) @@ -3400,167 +3410,167 @@ yyreduce: } break; - case 206: -#line 1103 "cc.y" + case 207: +#line 1107 "cc.y" { en.tenum = T; en.cenum = T; } break; - case 207: -#line 1108 "cc.y" + case 208: +#line 1112 "cc.y" { (yyval.type) = en.tenum; } break; - case 208: -#line 1112 "cc.y" + case 209: +#line 1116 "cc.y" { (yyval.type) = tcopy((yyvsp[(1) - (1)].sym)->type); } break; - case 210: -#line 1119 "cc.y" + case 211: +#line 1123 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); } break; - case 211: -#line 1124 "cc.y" + case 212: +#line 1128 "cc.y" { (yyval.lval) = 0; } break; - case 212: -#line 1128 "cc.y" + case 213: +#line 1132 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); } break; - case 217: -#line 1140 "cc.y" + case 218: +#line 1144 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); } break; - case 220: -#line 1150 "cc.y" + case 221: +#line 1154 "cc.y" { doenum((yyvsp[(1) - (1)].sym), Z); } break; - case 221: -#line 1154 "cc.y" + case 222: +#line 1158 "cc.y" { doenum((yyvsp[(1) - (3)].sym), (yyvsp[(3) - (3)].node)); } break; - case 224: -#line 1161 "cc.y" + case 225: +#line 1165 "cc.y" { (yyval.lval) = BCHAR; } break; - case 225: -#line 1162 "cc.y" + case 226: +#line 1166 "cc.y" { (yyval.lval) = BSHORT; } break; - case 226: -#line 1163 "cc.y" + case 227: +#line 1167 "cc.y" { (yyval.lval) = BINT; } break; - case 227: -#line 1164 "cc.y" + case 228: +#line 1168 "cc.y" { (yyval.lval) = BLONG; } break; - case 228: -#line 1165 "cc.y" + case 229: +#line 1169 "cc.y" { (yyval.lval) = BSIGNED; } break; - case 229: -#line 1166 "cc.y" + case 230: +#line 1170 "cc.y" { (yyval.lval) = BUNSIGNED; } break; - case 230: -#line 1167 "cc.y" + case 231: +#line 1171 "cc.y" { (yyval.lval) = BFLOAT; } break; - case 231: -#line 1168 "cc.y" + case 232: +#line 1172 "cc.y" { (yyval.lval) = BDOUBLE; } break; - case 232: -#line 1169 "cc.y" + case 233: +#line 1173 "cc.y" { (yyval.lval) = BVOID; } break; - case 233: -#line 1172 "cc.y" + case 234: +#line 1176 "cc.y" { (yyval.lval) = BAUTO; } break; - case 234: -#line 1173 "cc.y" + case 235: +#line 1177 "cc.y" { (yyval.lval) = BSTATIC; } break; - case 235: -#line 1174 "cc.y" + case 236: +#line 1178 "cc.y" { (yyval.lval) = BEXTERN; } break; - case 236: -#line 1175 "cc.y" + case 237: +#line 1179 "cc.y" { (yyval.lval) = BTYPEDEF; } break; - case 237: -#line 1176 "cc.y" + case 238: +#line 1180 "cc.y" { (yyval.lval) = BTYPESTR; } break; - case 238: -#line 1177 "cc.y" + case 239: +#line 1181 "cc.y" { (yyval.lval) = BREGISTER; } break; - case 239: -#line 1178 "cc.y" + case 240: +#line 1182 "cc.y" { (yyval.lval) = 0; } break; - case 240: -#line 1181 "cc.y" + case 241: +#line 1185 "cc.y" { (yyval.lval) = BCONSTNT; } break; - case 241: -#line 1182 "cc.y" + case 242: +#line 1186 "cc.y" { (yyval.lval) = BVOLATILE; } break; - case 242: -#line 1183 "cc.y" + case 243: +#line 1187 "cc.y" { (yyval.lval) = 0; } break; - case 243: -#line 1187 "cc.y" + case 244: +#line 1191 "cc.y" { (yyval.node) = new(ONAME, Z, Z); if((yyvsp[(1) - (1)].sym)->class == CLOCAL) @@ -3576,8 +3586,8 @@ yyreduce: } break; - case 244: -#line 1202 "cc.y" + case 245: +#line 1206 "cc.y" { (yyval.node) = new(ONAME, Z, Z); (yyval.node)->sym = (yyvsp[(1) - (1)].sym); @@ -3592,7 +3602,7 @@ yyreduce: /* Line 1267 of yacc.c. */ -#line 3596 "y.tab.c" +#line 3606 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -3806,6 +3816,6 @@ yyreturn: } -#line 1215 "cc.y" +#line 1219 "cc.y" diff --git a/src/cmd/cc/y.tab.h b/src/cmd/cc/y.tab.h index c56a1d85a..32daca9b6 100644 --- a/src/cmd/cc/y.tab.h +++ b/src/cmd/cc/y.tab.h @@ -88,28 +88,29 @@ LIF = 304, LINT = 305, LLONG = 306, - LREGISTER = 307, - LRETURN = 308, - LSHORT = 309, - LSIZEOF = 310, - LUSED = 311, - LSTATIC = 312, - LSTRUCT = 313, - LSWITCH = 314, - LTYPEDEF = 315, - LTYPESTR = 316, - LUNION = 317, - LUNSIGNED = 318, - LWHILE = 319, - LVOID = 320, - LENUM = 321, - LSIGNED = 322, - LCONSTNT = 323, - LVOLATILE = 324, - LSET = 325, - LSIGNOF = 326, - LRESTRICT = 327, - LINLINE = 328 + LPREFETCH = 307, + LREGISTER = 308, + LRETURN = 309, + LSHORT = 310, + LSIZEOF = 311, + LUSED = 312, + LSTATIC = 313, + LSTRUCT = 314, + LSWITCH = 315, + LTYPEDEF = 316, + LTYPESTR = 317, + LUNION = 318, + LUNSIGNED = 319, + LWHILE = 320, + LVOID = 321, + LENUM = 322, + LSIGNED = 323, + LCONSTNT = 324, + LVOLATILE = 325, + LSET = 326, + LSIGNOF = 327, + LRESTRICT = 328, + LINLINE = 329 }; #endif /* Tokens. */ @@ -162,28 +163,29 @@ #define LIF 304 #define LINT 305 #define LLONG 306 -#define LREGISTER 307 -#define LRETURN 308 -#define LSHORT 309 -#define LSIZEOF 310 -#define LUSED 311 -#define LSTATIC 312 -#define LSTRUCT 313 -#define LSWITCH 314 -#define LTYPEDEF 315 -#define LTYPESTR 316 -#define LUNION 317 -#define LUNSIGNED 318 -#define LWHILE 319 -#define LVOID 320 -#define LENUM 321 -#define LSIGNED 322 -#define LCONSTNT 323 -#define LVOLATILE 324 -#define LSET 325 -#define LSIGNOF 326 -#define LRESTRICT 327 -#define LINLINE 328 +#define LPREFETCH 307 +#define LREGISTER 308 +#define LRETURN 309 +#define LSHORT 310 +#define LSIZEOF 311 +#define LUSED 312 +#define LSTATIC 313 +#define LSTRUCT 314 +#define LSWITCH 315 +#define LTYPEDEF 316 +#define LTYPESTR 317 +#define LUNION 318 +#define LUNSIGNED 319 +#define LWHILE 320 +#define LVOID 321 +#define LENUM 322 +#define LSIGNED 323 +#define LCONSTNT 324 +#define LVOLATILE 325 +#define LSET 326 +#define LSIGNOF 327 +#define LRESTRICT 328 +#define LINLINE 329 @@ -217,7 +219,7 @@ typedef union YYSTYPE vlong vval; } /* Line 1529 of yacc.c. */ -#line 221 "y.tab.h" +#line 223 "y.tab.h" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 381e606ef..dbae3b7b1 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -78,7 +78,7 @@ func (f *File) ReadGo(name string) { } if cg != nil { f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) - f.Preamble += cg.Text() + "\n" + f.Preamble += commentText(cg) + "\n" } } } @@ -131,6 +131,30 @@ func (f *File) ReadGo(name string) { f.AST = ast2 } +// Like ast.CommentGroup's Text method but preserves +// leading blank lines, so that line numbers line up. +func commentText(g *ast.CommentGroup) string { + if g == nil { + return "" + } + var pieces []string + for _, com := range g.List { + c := string(com.Text) + // Remove comment markers. + // The parser has given us exactly the comment text. + switch c[1] { + case '/': + //-style comment (no newline at the end) + c = c[2:] + "\n" + case '*': + /*-style comment */ + c = c[2 : len(c)-2] + } + pieces = append(pieces, c) + } + return strings.Join(pieces, "") +} + // Save references to C.xxx for later processing. func (f *File) saveRef(x interface{}, context string) { n, ok := x.(*ast.Expr) diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 1bb48f44e..955b7c495 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -65,11 +65,13 @@ struct_, union_, or enum_, as in C.struct_stat. Go structs cannot embed fields with C types. -Any C function that returns a value may be called in a multiple -assignment context to retrieve both the return value and the -C errno variable as an error. For example: +Any C function (even void functions) may be called in a multiple +assignment context to retrieve both the return value (if any) and the +C errno variable as an error (use _ to skip the result value if the +function returns void). For example: n, err := C.atoi("abc") + _, err := C.voidFunc() In C, a function argument written as a fixed size array actually requires a pointer to the first element of the array. @@ -83,7 +85,8 @@ by making copies of the data. In pseudo-Go definitions: // Go string to C string // The C string is allocated in the C heap using malloc. // It is the caller's responsibility to arrange for it to be - // freed, such as by calling C.free. + // freed, such as by calling C.free (be sure to include stdlib.h + // if C.free is needed). func C.CString(string) *C.char // C string to Go string @@ -113,6 +116,11 @@ copied from the cgo input files. Functions with multiple return values are mapped to functions returning a struct. Not all Go types can be mapped to C types in a useful way. +Using //export in a file places a restriction on the preamble: +since it is copied into two different C output files, it must not +contain any definitions, only declarations. Definitions must be +placed in preambles in other files, or in C source files. + Cgo transforms the input file into four output files: two Go source files, a C file for 6c (or 8c or 5c), and a C file for gcc. @@ -125,4 +133,492 @@ Cgo does not yet work with gccgo. See "C? Go? Cgo!" for an introduction to using cgo: http://golang.org/doc/articles/c_go_cgo.html */ -package documentation +package main + +/* +Implementation details. + +Cgo provides a way for Go programs to call C code linked into the same +address space. This comment explains the operation of cgo. + +Cgo reads a set of Go source files and looks for statements saying +import "C". If the import has a doc comment, that comment is +taken as literal C code to be used as a preamble to any C code +generated by cgo. A typical preamble #includes necessary definitions: + + // #include + import "C" + +For more details about the usage of cgo, see the documentation +comment at the top of this file. + +Understanding C + +Cgo scans the Go source files that import "C" for uses of that +package, such as C.puts. It collects all such identifiers. The next +step is to determine each kind of name. In C.xxx the xxx might refer +to a type, a function, a constant, or a global variable. Cgo must +decide which. + +The obvious thing for cgo to do is to process the preamble, expanding +#includes and processing the corresponding C code. That would require +a full C parser and type checker that was also aware of any extensions +known to the system compiler (for example, all the GNU C extensions) as +well as the system-specific header locations and system-specific +pre-#defined macros. This is certainly possible to do, but it is an +enormous amount of work. + +Cgo takes a different approach. It determines the meaning of C +identifiers not by parsing C code but by feeding carefully constructed +programs into the system C compiler and interpreting the generated +error messages, debug information, and object files. In practice, +parsing these is significantly less work and more robust than parsing +C source. + +Cgo first invokes gcc -E -dM on the preamble, in order to find out +about simple #defines for constants and the like. These are recorded +for later use. + +Next, cgo needs to identify the kinds for each identifier. For the +identifiers C.foo and C.bar, cgo generates this C program: + + + void __cgo__f__(void) { + #line 1 "cgo-test" + foo; + enum { _cgo_enum_0 = foo }; + bar; + enum { _cgo_enum_1 = bar }; + } + +This program will not compile, but cgo can look at the error messages +to infer the kind of each identifier. The line number given in the +error tells cgo which identifier is involved. + +An error like "unexpected type name" or "useless type name in empty +declaration" or "declaration does not declare anything" tells cgo that +the identifier is a type. + +An error like "statement with no effect" or "expression result unused" +tells cgo that the identifier is not a type, but not whether it is a +constant, function, or global variable. + +An error like "not an integer constant" tells cgo that the identifier +is not a constant. If it is also not a type, it must be a function or +global variable. For now, those can be treated the same. + +Next, cgo must learn the details of each type, variable, function, or +constant. It can do this by reading object files. If cgo has decided +that t1 is a type, v2 and v3 are variables or functions, and c4, c5, +and c6 are constants, it generates: + + + typeof(t1) *__cgo__1; + typeof(v2) *__cgo__2; + typeof(v3) *__cgo__3; + typeof(c4) *__cgo__4; + enum { __cgo_enum__4 = c4 }; + typeof(c5) *__cgo__5; + enum { __cgo_enum__5 = c5 }; + typeof(c6) *__cgo__6; + enum { __cgo_enum__6 = c6 }; + + long long __cgo_debug_data[] = { + 0, // t1 + 0, // v2 + 0, // v3 + c4, + c5, + c6, + 1 + }; + +and again invokes the system C compiler, to produce an object file +containing debug information. Cgo parses the DWARF debug information +for __cgo__N to learn the type of each identifier. (The types also +distinguish functions from global variables.) If using a standard gcc, +cgo can parse the DWARF debug information for the __cgo_enum__N to +learn the identifier's value. The LLVM-based gcc on OS X emits +incomplete DWARF information for enums; in that case cgo reads the +constant values from the __cgo_debug_data from the object file's data +segment. + +At this point cgo knows the meaning of each C.xxx well enough to start +the translation process. + +Translating Go + +[The rest of this comment refers to 6g and 6c, the Go and C compilers +that are part of the amd64 port of the gc Go toolchain. Everything here +applies to another architecture's compilers as well.] + +Given the input Go files x.go and y.go, cgo generates these source +files: + + x.cgo1.go # for 6g + y.cgo1.go # for 6g + _cgo_gotypes.go # for 6g + _cgo_defun.c # for 6c + x.cgo2.c # for gcc + y.cgo2.c # for gcc + _cgo_export.c # for gcc + _cgo_main.c # for gcc + +The file x.cgo1.go is a copy of x.go with the import "C" removed and +references to C.xxx replaced with names like _Cfunc_xxx or _Ctype_xxx. +The definitions of those identifiers, written as Go functions, types, +or variables, are provided in _cgo_gotypes.go. + +Here is a _cgo_gotypes.go containing definitions for C.flush (provided +in the preamble) and C.puts (from stdio): + + type _Ctype_char int8 + type _Ctype_int int32 + type _Ctype_void [0]byte + + func _Cfunc_CString(string) *_Ctype_char + func _Cfunc_flush() _Ctype_void + func _Cfunc_puts(*_Ctype_char) _Ctype_int + +For functions, cgo only writes an external declaration in the Go +output. The implementation is in a combination of C for 6c (meaning +any gc-toolchain compiler) and C for gcc. + +The 6c file contains the definitions of the functions. They all have +similar bodies that invoke runtime·cgocall to make a switch from the +Go runtime world to the system C (GCC-based) world. + +For example, here is the definition of _Cfunc_puts: + + void _cgo_be59f0f25121_Cfunc_puts(void*); + + void + ·_Cfunc_puts(struct{uint8 x[1];}p) + { + runtime·cgocall(_cgo_be59f0f25121_Cfunc_puts, &p); + } + +The hexadecimal number is a hash of cgo's input, chosen to be +deterministic yet unlikely to collide with other uses. The actual +function _cgo_be59f0f25121_Cfunc_puts is implemented in a C source +file compiled by gcc, the file x.cgo2.c: + + void + _cgo_be59f0f25121_Cfunc_puts(void *v) + { + struct { + char* p0; + int r; + char __pad12[4]; + } __attribute__((__packed__)) *a = v; + a->r = puts((void*)a->p0); + } + +It extracts the arguments from the pointer to _Cfunc_puts's argument +frame, invokes the system C function (in this case, puts), stores the +result in the frame, and returns. + +Linking + +Once the _cgo_export.c and *.cgo2.c files have been compiled with gcc, +they need to be linked into the final binary, along with the libraries +they might depend on (in the case of puts, stdio). 6l has been +extended to understand basic ELF files, but it does not understand ELF +in the full complexity that modern C libraries embrace, so it cannot +in general generate direct references to the system libraries. + +Instead, the build process generates an object file using dynamic +linkage to the desired libraries. The main function is provided by +_cgo_main.c: + + int main() { return 0; } + void crosscall2(void(*fn)(void*, int), void *a, int c) { } + void _cgo_allocate(void *a, int c) { } + void _cgo_panic(void *a, int c) { } + +The extra functions here are stubs to satisfy the references in the C +code generated for gcc. The build process links this stub, along with +_cgo_export.c and *.cgo2.c, into a dynamic executable and then lets +cgo examine the executable. Cgo records the list of shared library +references and resolved names and writes them into a new file +_cgo_import.c, which looks like: + + #pragma cgo_dynamic_linker "/lib64/ld-linux-x86-64.so.2" + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic __libc_start_main __libc_start_main#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic stdout stdout#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic fflush fflush#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic _ _ "libpthread.so.0" + #pragma cgo_import_dynamic _ _ "libc.so.6" + +In the end, the compiled Go package, which will eventually be +presented to 6l as part of a larger program, contains: + + _go_.6 # 6g-compiled object for _cgo_gotypes.go *.cgo1.go + _cgo_defun.6 # 6c-compiled object for _cgo_defun.c + _all.o # gcc-compiled object for _cgo_export.c, *.cgo2.c + _cgo_import.6 # 6c-compiled object for _cgo_import.c + +The final program will be a dynamic executable, so that 6l can avoid +needing to process arbitrary .o files. It only needs to process the .o +files generated from C files that cgo writes, and those are much more +limited in the ELF or other features that they use. + +In essence, the _cgo_import.6 file includes the extra linking +directives that 6l is not sophisticated enough to derive from _all.o +on its own. Similarly, the _all.o uses dynamic references to real +system object code because 6l is not sophisticated enough to process +the real code. + +The main benefits of this system are that 6l remains relatively simple +(it does not need to implement a complete ELF and Mach-O linker) and +that gcc is not needed after the package is compiled. For example, +package net uses cgo for access to name resolution functions provided +by libc. Although gcc is needed to compile package net, gcc is not +needed to link programs that import package net. + +Runtime + +When using cgo, Go must not assume that it owns all details of the +process. In particular it needs to coordinate with C in the use of +threads and thread-local storage. The runtime package, in its own +(6c-compiled) C code, declares a few uninitialized (default bss) +variables: + + bool runtime·iscgo; + void (*libcgo_thread_start)(void*); + void (*initcgo)(G*); + +Any package using cgo imports "runtime/cgo", which provides +initializations for these variables. It sets iscgo to 1, initcgo to a +gcc-compiled function that can be called early during program startup, +and libcgo_thread_start to a gcc-compiled function that can be used to +create a new thread, in place of the runtime's usual direct system +calls. + +[NOTE: From here down is planned but not yet implemented.] + +Internal and External Linking + +The text above describes "internal" linking, in which 6l parses and +links host object files (ELF, Mach-O, PE, and so on) into the final +executable itself. Keeping 6l simple means we cannot possibly +implement the full semantics of the host linker, so the kinds of +objects that can be linked directly into the binary is limited (other +code can only be used as a dynamic library). On the other hand, when +using internal linking, 6l can generate Go binaries by itself. + +In order to allow linking arbitrary object files without requiring +dynamic libraries, cgo will soon support an "external" linking mode +too. In external linking mode, 6l does not process any host object +files. Instead, it collects all the Go code and writes a single go.o +object file containing it. Then it invokes the host linker (usually +gcc) to combine the go.o object file and any supporting non-Go code +into a final executable. External linking avoids the dynamic library +requirement but introduces a requirement that the host linker be +present to create such a binary. + +Most builds both compile source code and invoke the linker to create a +binary. When cgo is involved, the compile step already requires gcc, so +it is not problematic for the link step to require gcc too. + +An important exception is builds using a pre-compiled copy of the +standard library. In particular, package net uses cgo on most systems, +and we want to preserve the ability to compile pure Go code that +imports net without requiring gcc to be present at link time. (In this +case, the dynamic library requirement is less significant, because the +only library involved is libc.so, which can usually be assumed +present.) + +This conflict between functionality and the gcc requirement means we +must support both internal and external linking, depending on the +circumstances: if net is the only cgo-using package, then internal +linking is probably fine, but if other packages are involved, so that there +are dependencies on libraries beyond libc, external linking is likely +to work better. The compilation of a package records the relevant +information to support both linking modes, leaving the decision +to be made when linking the final binary. + +Linking Directives + +In either linking mode, package-specific directives must be passed +through to 6l. These are communicated by writing #pragma directives +in a C source file compiled by 6c. The directives are copied into the .6 object file +and then processed by the linker. + +The directives are: + +#pragma cgo_import_dynamic [ [""]] + + In internal linking mode, allow an unresolved reference to + , assuming it will be resolved by a dynamic library + symbol. The optional specifies the symbol's name and + possibly version in the dynamic library, and the optional "" + names the specific library where the symbol should be found. + + In the , # or @ can be used to introduce a symbol version. + + Examples: + #pragma cgo_import_dynamic puts + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 "libc.so.6" + + A side effect of the cgo_dynamic_import directive with a + library is to make the final binary depend on that dynamic + library. To get the dependency without importing any specific + symbols, use _ for local and remote. + + Example: + #pragma cgo_import_dynamic _ _ "libc.so.6" + + For compatibility with current versions of SWIG, + #pragma dynimport is an alias for #pragma cgo_dynamic_import. + +#pragma cgo_dynamic_linker "" + + In internal linking mode, use "" as the dynamic linker + in the final binary. This directive is only needed from one + package when constructing a binary; by convention it is + supplied by runtime/cgo. + + Example: + #pragma cgo_dynamic_linker "/lib/ld-linux.so.2" + +#pragma cgo_export + + In both internal and external linking modes, put the Go symbol + named into the program's exported symbol table as + , so that C code can refer to it by that name. This + mechanism makes it possible for C code to call back into Go or + to share Go's data. + + For compatibility with current versions of SWIG, + #pragma dynexport is an alias for #pragma cgo_export. + +#pragma cgo_import_static + + In external linking mode, allow unresolved references to + in the go.o object file prepared for the host linker, + under the assumption that will be supplied by the + other object files that will be linked with go.o. + + Example: + #pragma cgo_import_static puts_wrapper + +#pragma cgo_ldflag "" + + In external linking mode, invoke the host linker (usually gcc) + with "" as a command-line argument following the .o files. + Note that the arguments are for "gcc", not "ld". + + Example: + #pragma cgo_ldflag "-lpthread" + #pragma cgo_ldflag "-L/usr/local/sqlite3/lib" + +A package compiled with cgo will include directives for both +internal and external linking; the linker will select the appropriate +subset for the chosen linking mode. + +Example + +As a simple example, consider a package that uses cgo to call C.sin. +The following code will be generated by cgo: + + // compiled by 6g + + type _Ctype_double float64 + func _Cfunc_sin(_Ctype_double) _Ctype_double + + // compiled by 6c + + #pragma cgo_import_dynamic sin sin#GLIBC_2.2.5 "libm.so.6" + + #pragma cgo_import_static _cgo_gcc_Cfunc_sin + #pragma cgo_ldflag "-lm" + + void _cgo_gcc_Cfunc_sin(void*); + + void + ·_Cfunc_sin(struct{uint8 x[16];}p) + { + runtime·cgocall(_cgo_gcc_Cfunc_sin, &p); + } + + // compiled by gcc, into foo.cgo2.o + + void + _cgo_gcc_Cfunc_sin(void *v) + { + struct { + double p0; + double r; + } __attribute__((__packed__)) *a = v; + a->r = sin(a->p0); + } + +What happens at link time depends on whether the final binary is linked +using the internal or external mode. If other packages are compiled in +"external only" mode, then the final link will be an external one. +Otherwise the link will be an internal one. + +The directives in the 6c-compiled file are used according to the kind +of final link used. + +In internal mode, 6l itself processes all the host object files, in +particular foo.cgo2.o. To do so, it uses the cgo_dynamic_import and +cgo_dynamic_linker directives to learn that the otherwise undefined +reference to sin in foo.cgo2.o should be rewritten to refer to the +symbol sin with version GLIBC_2.2.5 from the dynamic library +"libm.so.6", and the binary should request "/lib/ld-linux.so.2" as its +runtime dynamic linker. + +In external mode, 6l does not process any host object files, in +particular foo.cgo2.o. It links together the 6g- and 6c-generated +object files, along with any other Go code, into a go.o file. While +doing that, 6l will discover that there is no definition for +_cgo_gcc_Cfunc_sin, referred to by the 6c-compiled source file. This +is okay, because 6l also processes the cgo_import_static directive and +knows that _cgo_gcc_Cfunc_sin is expected to be supplied by a host +object file, so 6l does not treat the missing symbol as an error when +creating go.o. Indeed, the definition for _cgo_gcc_Cfunc_sin will be +provided to the host linker by foo2.cgo.o, which in turn will need the +symbol 'sin'. 6l also processes the cgo_ldflag directives, so that it +knows that the eventual host link command must include the -lm +argument, so that the host linker will be able to find 'sin' in the +math library. + +6l Command Line Interface + +The go command and any other Go-aware build systems invoke 6l +to link a collection of packages into a single binary. By default, 6l will +present the same interface it does today: + + 6l main.a + +produces a file named 6.out, even if 6l does so by invoking the host +linker in external linking mode. + +By default, 6l will decide the linking mode as follows: if the only +packages using cgo are those on a whitelist of standard library +packages (net, os/user, runtime/cgo), 6l will use internal linking +mode. Otherwise, there are non-standard cgo packages involved, and 6l +will use external linking mode. The first rule means that a build of +the godoc binary, which uses net but no other cgo, can run without +needing gcc available. The second rule means that a build of a +cgo-wrapped library like sqlite3 can generate a standalone executable +instead of needing to refer to a dynamic library. The specific choice +can be overridden using a command line flag: 6l -cgolink=internal or +6l -cgolink=external. + +In an external link, 6l will create a temporary directory, write any +host object files found in package archives to that directory (renamed +to avoid conflicts), write the go.o file to that directory, and invoke +the host linker. The default value for the host linker is $CC, split +into fields, or else "gcc". The specific host linker command line can +be overridden using a command line flag: 6l -hostld='gcc -ggdb' + +These defaults mean that Go-aware build systems can ignore the linking +changes and keep running plain '6l' and get reasonable results, but +they can also control the linking details if desired. + +*/ diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 98a847e6f..4b0a521a8 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -391,9 +391,9 @@ func (p *Package) guessKinds(f *File) []*Name { b.WriteString(builtinProlog) b.WriteString(f.Preamble) b.WriteString("void __cgo__f__(void) {\n") - b.WriteString("#line 0 \"cgo-test\"\n") + b.WriteString("#line 1 \"cgo-test\"\n") for i, n := range toSniff { - fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i) + fmt.Fprintf(&b, "%s; /* #%d */\nenum { _cgo_enum_%d = %s }; /* #%d */\n", n.C, i, i, n.C, i) } b.WriteString("}\n") stderr := p.gccErrors(b.Bytes()) @@ -423,14 +423,18 @@ func (p *Package) guessKinds(f *File) []*Name { if err != nil { continue } + i = (i - 1) / 2 what := "" switch { default: continue - case strings.Contains(line, ": useless type name in empty declaration"): + case strings.Contains(line, ": useless type name in empty declaration"), + strings.Contains(line, ": declaration does not declare anything"), + strings.Contains(line, ": unexpected type name"): what = "type" isConst[i] = false - case strings.Contains(line, ": statement with no effect"): + case strings.Contains(line, ": statement with no effect"), + strings.Contains(line, ": expression result unused"): what = "not-type" // const or func or var case strings.Contains(line, "undeclared"): error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) @@ -508,7 +512,12 @@ func (p *Package) loadDWARF(f *File, names []*Name) { fmt.Fprintf(&b, "\t0,\n") } } - fmt.Fprintf(&b, "\t0\n") + // for the last entry, we can not use 0, otherwise + // in case all __cgodebug_data is zero initialized, + // LLVM-based gcc will place the it in the __DATA.__common + // zero-filled section (our debug/macho doesn't support + // this) + fmt.Fprintf(&b, "\t1\n") fmt.Fprintf(&b, "};\n") d, bo, debugData := p.gccDebug(b.Bytes()) @@ -596,7 +605,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // Record types and typedef information. var conv typeConv - conv.Init(p.PtrSize) + conv.Init(p.PtrSize, p.IntSize) for i, n := range names { if types[i] == nil { continue @@ -618,7 +627,9 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // Remove injected enum to ensure the value will deep-compare // equally in future loads of the same constant. delete(n.Type.EnumValues, k) - } else if n.Kind == "const" && i < len(enumVal) { + } + // Prefer debug data over DWARF debug output, if we have it. + if n.Kind == "const" && i < len(enumVal) { n.Const = fmt.Sprintf("%#x", enumVal[i]) } } @@ -637,7 +648,14 @@ func (p *Package) rewriteRef(f *File) { n.Kind = "var" } if n.Mangle == "" { - n.Mangle = "_C" + n.Kind + "_" + n.Go + // When using gccgo variables have to be + // exported so that they become global symbols + // that the C code can refer to. + prefix := "_C" + if *gccgo && n.Kind == "var" { + prefix = "C" + } + n.Mangle = prefix + n.Kind + "_" + n.Go } } @@ -662,9 +680,6 @@ func (p *Package) rewriteRef(f *File) { break } if r.Context == "call2" { - if r.Name.FuncType.Result == nil { - error_(r.Pos(), "assignment count mismatch: 2 = 0") - } // Invent new Name for the two-result function. n := f.Name["2"+r.Name.Go] if n == nil { @@ -720,23 +735,30 @@ func (p *Package) rewriteRef(f *File) { } } -// gccName returns the name of the compiler to run. Use $GCC if set in +// gccName returns the name of the compiler to run. Use $CC if set in // the environment, otherwise just "gcc". -func (p *Package) gccName() (ret string) { - if ret = os.Getenv("GCC"); ret == "" { - ret = "gcc" +func (p *Package) gccName() string { + // Use $CC if set, since that's what the build uses. + if ret := os.Getenv("CC"); ret != "" { + return ret } - return + // Fall back to $GCC if set, since that's what we used to use. + if ret := os.Getenv("GCC"); ret != "" { + return ret + } + return "gcc" } -// gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". +// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". func (p *Package) gccMachine() []string { switch goarch { case "amd64": return []string{"-m64"} case "386": return []string{"-m32"} + case "arm": + return []string{"-marm"} // not thumb } return nil } @@ -755,9 +777,22 @@ func (p *Package) gccCmd() []string { "-o" + gccTmp(), // write object to tmp "-gdwarf-2", // generate DWARF v2 debugging symbols "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise - "-c", // do not link - "-xc", // input language is C + "-c", // do not link + "-xc", // input language is C + } + if strings.Contains(p.gccName(), "clang") { + c = append(c, + "-ferror-limit=0", + // Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn) + // doesn't have -Wno-unneeded-internal-declaration, so we need yet another + // flag to disable the warning. Yes, really good diagnostics, clang. + "-Wno-unknown-warning-option", + "-Wno-unneeded-internal-declaration", + "-Wno-unused-function", + "-Qunused-arguments", + ) } + c = append(c, p.GccOptions...) c = append(c, p.gccMachine()...) c = append(c, "-") //read input from standard input @@ -795,15 +830,30 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) return d, f.ByteOrder, data } - // Can skip debug data block in ELF and PE for now. - // The DWARF information is complete. - if f, err := elf.Open(gccTmp()); err == nil { d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } - return d, f.ByteOrder, nil + var data []byte + symtab, err := f.Symbols() + if err == nil { + for i := range symtab { + s := &symtab[i] + if s.Name == "__cgodebug_data" { + // Found it. Now find data section. + if i := int(s.Section); 0 <= i && i < len(f.Sections) { + sect := f.Sections[i] + if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + if sdat, err := sect.Data(); err == nil { + data = sdat[s.Value-sect.Addr:] + } + } + } + } + } + } + return d, f.ByteOrder, data } if f, err := pe.Open(gccTmp()); err == nil { @@ -811,7 +861,20 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } - return d, binary.LittleEndian, nil + var data []byte + for _, s := range f.Symbols { + if s.Name == "_"+"__cgodebug_data" { + if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { + sect := f.Sections[i] + if s.Value < sect.Size { + if sdat, err := sect.Data(); err == nil { + data = sdat[s.Value:] + } + } + } + } + } + return d, binary.LittleEndian, data } fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp()) @@ -889,16 +952,19 @@ type typeConv struct { void ast.Expr unsafePointer ast.Expr string ast.Expr + goVoid ast.Expr // _Ctype_void, denotes C's void ptrSize int64 + intSize int64 } var tagGen int var typedef = make(map[string]*Type) var goIdent = make(map[string]*ast.Ident) -func (c *typeConv) Init(ptrSize int64) { +func (c *typeConv) Init(ptrSize, intSize int64) { c.ptrSize = ptrSize + c.intSize = intSize c.m = make(map[dwarf.Type]*Type) c.bool = c.Ident("bool") c.byte = c.Ident("byte") @@ -918,6 +984,7 @@ func (c *typeConv) Init(ptrSize int64) { c.unsafePointer = c.Ident("unsafe.Pointer") c.void = c.Ident("void") c.string = c.Ident("string") + c.goVoid = c.Ident("_Ctype_void") } // base strips away qualifiers and typedefs to get the underlying type @@ -988,6 +1055,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { return t } + // clang won't generate DW_AT_byte_size for pointer types, + // so we have to fix it here. + if dt, ok := base(dtype).(*dwarf.PtrType); ok && dt.ByteSize == -1 { + dt.ByteSize = c.ptrSize + } + t := new(Type) t.Size = dtype.Size() t.Align = -1 @@ -1032,7 +1105,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { case *dwarf.BoolType: t.Go = c.bool - t.Align = c.ptrSize + t.Align = 1 case *dwarf.CharType: if t.Size != 1 { @@ -1163,11 +1236,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { t.Go = name // publish before recursive calls goIdent[name.Name] = name switch dt.Kind { - case "union", "class": + case "class", "union": t.Go = c.Opaque(t.Size) if t.C.Empty() { t.C.Set("typeof(unsigned char[%d])", t.Size) } + t.Align = 1 // TODO: should probably base this on field alignment. typedef[name.Name] = t case "struct": g, csyntax, align := c.Struct(dt, pos) @@ -1245,8 +1319,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { } case *dwarf.VoidType: - t.Go = c.void + t.Go = c.goVoid t.C.Set("void") + t.Align = 1 } switch dtype.(type) { @@ -1334,7 +1409,9 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType { } var r *Type var gr []*ast.Field - if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil { + if _, ok := dtype.ReturnType.(*dwarf.VoidType); ok { + gr = []*ast.Field{{Type: c.goVoid}} + } else if dtype.ReturnType != nil { r = c.Type(dtype.ReturnType, pos) gr = []*ast.Field{{Type: r.Go}} } @@ -1493,8 +1570,8 @@ func godefsFields(fld []*ast.Field) { npad := 0 for _, f := range fld { for _, n := range f.Names { - if strings.HasPrefix(n.Name, prefix) && n.Name != prefix { - n.Name = n.Name[len(prefix):] + if n.Name != prefix { + n.Name = strings.TrimPrefix(n.Name, prefix) } if n.Name == "_" { // Use exported name instead. @@ -1522,7 +1599,7 @@ func godefsFields(fld []*ast.Field) { } // fieldPrefix returns the prefix that should be removed from all the -// field names when generating the C or Go code. For generated +// field names when generating the C or Go code. For generated // C, we leave the names as is (tv_sec, tv_usec), since that's what // people are used to seeing in C. For generated Go code, such as // package syscall's data structures, we drop a common prefix @@ -1539,7 +1616,7 @@ func fieldPrefix(fld []*ast.Field) string { // named, say, _pad in an otherwise prefixed header. // If the struct has 3 fields tv_sec, tv_usec, _pad1, then we // still want to remove the tv_ prefix. - // The check for "orig_" here handles orig_eax in the + // The check for "orig_" here handles orig_eax in the // x86 ptrace register sets, which otherwise have all fields // with reg_ prefixes. if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") { diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go index fec70a334..20376170d 100644 --- a/src/cmd/cgo/godefs.go +++ b/src/cmd/cgo/godefs.go @@ -180,7 +180,7 @@ func (p *Package) cdefs(f *File, srcfile string) string { for _, line := range lines { line = strings.TrimSpace(line) if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { - s := line[len("type ") : len(line)-len(" struct {")] + s := strings.TrimSuffix(strings.TrimPrefix(line, "type "), " struct {") printf("typedef struct %s %s;\n", s, s) } } diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 60165961a..7adc795de 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -31,6 +31,7 @@ type Package struct { PackageName string // name of package PackagePath string PtrSize int64 + IntSize int64 GccOptions []string CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS) Written map[string]bool @@ -129,12 +130,19 @@ var ptrSizeMap = map[string]int64{ "arm": 4, } +var intSizeMap = map[string]int64{ + "386": 4, + "amd64": 8, + "arm": 4, +} + var cPrefix string var fset = token.NewFileSet() var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") var dynout = flag.String("dynout", "", "write -dynobj output to this file") +var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in dynimport mode") // These flags are for bootstrapping a new Go implementation, // to generate Go and C headers that match the data layout and @@ -144,8 +152,10 @@ var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C var objDir = flag.String("objdir", "", "object directory") var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo") -var gccgoprefix = flag.String("gccgoprefix", "go", "prefix of symbols generated by gccgo") +var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo") +var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo") var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code") +var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code") var goarch, goos string func main() { @@ -287,7 +297,11 @@ func newPackage(args []string) *Package { } ptrSize := ptrSizeMap[goarch] if ptrSize == 0 { - fatalf("unknown $GOARCH %q", goarch) + fatalf("unknown ptrSize for $GOARCH %q", goarch) + } + intSize := intSizeMap[goarch] + if intSize == 0 { + fatalf("unknown intSize for $GOARCH %q", goarch) } // Reset locale variables so gcc emits English errors [sic]. @@ -296,6 +310,7 @@ func newPackage(args []string) *Package { p := &Package{ PtrSize: ptrSize, + IntSize: intSize, GccOptions: gccOptions, CgoFlags: make(map[string]string), Written: make(map[string]bool), diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 5dfc16a02..a126cf17f 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -14,6 +14,7 @@ import ( "go/printer" "go/token" "os" + "sort" "strings" ) @@ -26,6 +27,8 @@ func (p *Package) writeDefs() { fc := creat(*objDir + "_cgo_defun.c") fm := creat(*objDir + "_cgo_main.c") + var gccgoInit bytes.Buffer + fflg := creat(*objDir + "_cgo_flags") for k, v := range p.CgoFlags { fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) @@ -50,19 +53,33 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") - fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + if *importSyscall { + fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + } if !*gccgo && *importRuntimeCgo { fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") } fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n") - fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int) { *dst = syscall.Errno(x) }\n") + if *importSyscall { + fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int32) { *dst = syscall.Errno(x) }\n") + } - for name, def := range typedef { + typedefNames := make([]string, 0, len(typedef)) + for name := range typedef { + typedefNames = append(typedefNames, name) + } + sort.Strings(typedefNames) + for _, name := range typedefNames { + def := typedef[name] fmt.Fprintf(fgo2, "type %s ", name) conf.Fprint(fgo2, fset, def.Go) fmt.Fprintf(fgo2, "\n\n") } - fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + if *gccgo { + fmt.Fprintf(fgo2, "type _Ctype_void byte\n") + } else { + fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + } if *gccgo { fmt.Fprintf(fc, cPrologGccgo) @@ -70,6 +87,8 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, cProlog) } + gccgoSymbolPrefix := p.gccgoSymbolPrefix() + cVars := make(map[string]bool) for _, key := range nameKeys(p.Name) { n := p.Name[key] @@ -86,7 +105,12 @@ func (p *Package) writeDefs() { cVars[n.C] = true } - fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + if *gccgo { + fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle) + fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C) + } else { + fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + } fmt.Fprintf(fc, "\n") fmt.Fprintf(fgo2, "var %s ", n.Mangle) @@ -116,6 +140,14 @@ func (p *Package) writeDefs() { p.writeExports(fgo2, fc, fm) } + init := gccgoInit.String() + if init != "" { + fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor));") + fmt.Fprintln(fc, "static void init(void) {") + fmt.Fprint(fc, init) + fmt.Fprintln(fc, "}") + } + fgo2.Close() fc.Close() } @@ -131,6 +163,15 @@ func dynimport(obj string) { } if f, err := elf.Open(obj); err == nil { + if *dynlinker { + // Emit the cgo_dynamic_linker line. + if sec := f.Section(".interp"); sec != nil { + if data, err := sec.Data(); err == nil && len(data) > 1 { + // skip trailing \0 in data + fmt.Fprintf(stdout, "#pragma cgo_dynamic_linker %q\n", string(data[:len(data)-1])) + } + } + } sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from ELF file %s: %v", obj, err) @@ -138,16 +179,16 @@ func dynimport(obj string) { for _, s := range sym { targ := s.Name if s.Version != "" { - targ += "@" + s.Version + targ += "#" + s.Version } - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library) } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) } for _, l := range lib { - fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic _ _ %q\n", l) } return } @@ -161,14 +202,14 @@ func dynimport(obj string) { if len(s) > 0 && s[0] == '_' { s = s[1:] } - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s, s, "") + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", s, s, "") } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) } for _, l := range lib { - fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic _ _ %q\n", l) } return } @@ -180,7 +221,8 @@ func dynimport(obj string) { } for _, s := range sym { ss := strings.Split(s, ":") - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) + name := strings.Split(ss[0], "@")[0] + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1])) } return } @@ -247,6 +289,7 @@ func (p *Package) structType(n *Name) (string, int64) { func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { name := n.Go gtype := n.FuncType.Go + void := gtype.Results == nil || len(gtype.Results.List) == 0 if n.AddError { // Add "error" to return type list. // Type list is known to be 0 or 1 element - it's a C function. @@ -271,38 +314,58 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { if *gccgo { // Gccgo style hooks. - // we hook directly into C. gccgo goes not support cgocall yet. - if !n.AddError { - fmt.Fprintf(fgo2, "//extern %s\n", n.C) - conf.Fprint(fgo2, fset, d) - fmt.Fprint(fgo2, "\n") - } else { - // write a small wrapper to retrieve errno. - cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) - paramnames := []string(nil) - for i, param := range d.Type.Params.List { - paramName := fmt.Sprintf("p%d", i) - param.Names = []*ast.Ident{ast.NewIdent(paramName)} - paramnames = append(paramnames, paramName) + fmt.Fprint(fgo2, "\n") + cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) + paramnames := []string(nil) + for i, param := range d.Type.Params.List { + paramName := fmt.Sprintf("p%d", i) + param.Names = []*ast.Ident{ast.NewIdent(paramName)} + paramnames = append(paramnames, paramName) + } + + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, " {\n") + fmt.Fprint(fgo2, "\tdefer syscall.CgocallDone()\n") + fmt.Fprint(fgo2, "\tsyscall.Cgocall()\n") + if n.AddError { + fmt.Fprint(fgo2, "\tsyscall.SetErrno(0)\n") + } + fmt.Fprint(fgo2, "\t") + if !void { + fmt.Fprint(fgo2, "r := ") + } + fmt.Fprintf(fgo2, "%s(%s)\n", cname, strings.Join(paramnames, ", ")) + + if n.AddError { + fmt.Fprint(fgo2, "\te := syscall.GetErrno()\n") + fmt.Fprint(fgo2, "\tif e != 0 {\n") + fmt.Fprint(fgo2, "\t\treturn ") + if !void { + fmt.Fprint(fgo2, "r, ") } - conf.Fprint(fgo2, fset, d) - fmt.Fprintf(fgo2, "{\n") - fmt.Fprintf(fgo2, "\tsyscall.SetErrno(0)\n") - fmt.Fprintf(fgo2, "\tr := %s(%s)\n", cname, strings.Join(paramnames, ", ")) - fmt.Fprintf(fgo2, "\te := syscall.GetErrno()\n") - fmt.Fprintf(fgo2, "\tif e != 0 {\n") - fmt.Fprintf(fgo2, "\t\treturn r, e\n") - fmt.Fprintf(fgo2, "\t}\n") - fmt.Fprintf(fgo2, "\treturn r, nil\n") - fmt.Fprintf(fgo2, "}\n") - // declare the C function. - fmt.Fprintf(fgo2, "//extern %s\n", n.C) - d.Name = ast.NewIdent(cname) + fmt.Fprint(fgo2, "e\n") + fmt.Fprint(fgo2, "\t}\n") + fmt.Fprint(fgo2, "\treturn ") + if !void { + fmt.Fprint(fgo2, "r, ") + } + fmt.Fprint(fgo2, "nil\n") + } else if !void { + fmt.Fprint(fgo2, "\treturn r\n") + } + + fmt.Fprint(fgo2, "}\n") + + // declare the C function. + fmt.Fprintf(fgo2, "//extern %s\n", n.C) + d.Name = ast.NewIdent(cname) + if n.AddError { l := d.Type.Results.List d.Type.Results.List = l[:len(l)-1] - conf.Fprint(fgo2, fset, d) - fmt.Fprint(fgo2, "\n") } + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, "\n") + return } conf.Fprint(fgo2, fset, d) @@ -317,6 +380,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { _, argSize = p.structType(n) // C wrapper calls into gcc, passing a pointer to the argument frame. + fmt.Fprintf(fc, "#pragma cgo_import_static _cgo%s%s\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "\n") fmt.Fprintf(fc, "void\n") @@ -456,11 +520,13 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcch, "%s\n", p.Preamble) - fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) + fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") + fmt.Fprintf(fgcc, "\nextern void crosscall2(void (*fn)(void *, int), void *, int);\n\n") + for _, exp := range p.ExpFunc { fn := exp.Func @@ -552,7 +618,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { s += ")" fmt.Fprintf(fgcch, "\nextern %s;\n", s) - fmt.Fprintf(fgcc, "extern _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "\t%s __attribute__((packed)) a;\n", ctype) @@ -585,7 +651,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { if fn.Recv != nil { goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname } - fmt.Fprintf(fc, "#pragma dynexport %s %s\n", goname, goname) + fmt.Fprintf(fc, "#pragma cgo_export %s\n", goname) fmt.Fprintf(fc, "extern void ·%s();\n\n", goname) fmt.Fprintf(fc, "#pragma textflag 7\n") // no split stack, so no use of m or g fmt.Fprintf(fc, "void\n") @@ -641,32 +707,22 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) { fgcc := creat(*objDir + "_cgo_export.c") fgcch := creat(*objDir + "_cgo_export.h") - _ = fgcc + + gccgoSymbolPrefix := p.gccgoSymbolPrefix() fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcch, "%s\n", p.Preamble) - fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) - fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n") + fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) - clean := func(r rune) rune { - switch { - case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', - '0' <= r && r <= '9': - return r - } - return '_' - } - gccgoSymbolPrefix := strings.Map(clean, *gccgoprefix) + fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") + + fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n") for _, exp := range p.ExpFunc { - // TODO: support functions with receivers. fn := exp.Func fntype := fn.Type - if !ast.IsExported(fn.Name.Name) { - fatalf("cannot export unexported function %s with gccgo", fn.Name) - } - cdeclBuf := new(bytes.Buffer) resultCount := 0 forFieldList(fntype.Results, @@ -692,28 +748,135 @@ func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) { fmt.Fprintf(cdeclBuf, "struct %s_result", exp.ExpName) } - // The function name. - fmt.Fprintf(cdeclBuf, " "+exp.ExpName) - gccgoSymbol := fmt.Sprintf("%s.%s.%s", gccgoSymbolPrefix, p.PackageName, exp.Func.Name) - fmt.Fprintf(cdeclBuf, " (") + cRet := cdeclBuf.String() + + cdeclBuf = new(bytes.Buffer) + fmt.Fprintf(cdeclBuf, "(") + if fn.Recv != nil { + fmt.Fprintf(cdeclBuf, "%s recv", p.cgoType(fn.Recv.List[0].Type).C.String()) + } // Function parameters. forFieldList(fntype.Params, func(i int, atype ast.Expr) { - if i > 0 { + if i > 0 || fn.Recv != nil { fmt.Fprintf(cdeclBuf, ", ") } t := p.cgoType(atype) fmt.Fprintf(cdeclBuf, "%s p%d", t.C, i) }) fmt.Fprintf(cdeclBuf, ")") - cdecl := cdeclBuf.String() + cParams := cdeclBuf.String() + + goName := "Cgoexp_" + exp.ExpName + fmt.Fprintf(fgcch, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName) + fmt.Fprint(fgcch, "\n") + + fmt.Fprint(fgcc, "\n") + fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams) + fmt.Fprint(fgcc, "\t") + if resultCount > 0 { + fmt.Fprint(fgcc, "return ") + } + fmt.Fprintf(fgcc, "%s(", goName) + if fn.Recv != nil { + fmt.Fprint(fgcc, "recv") + } + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + fmt.Fprintf(fgcc, ", ") + } + fmt.Fprintf(fgcc, "p%d", i) + }) + fmt.Fprint(fgcc, ");\n") + fmt.Fprint(fgcc, "}\n") - fmt.Fprintf(fgcch, "extern %s __asm__(\"%s\");\n", cdecl, gccgoSymbol) // Dummy declaration for _cgo_main.c - fmt.Fprintf(fm, "%s {}\n", cdecl) + fmt.Fprintf(fm, "%s %s %s {}\n", cRet, goName, cParams) + + // For gccgo we use a wrapper function in Go, in order + // to call CgocallBack and CgocallBackDone. + + // This code uses printer.Fprint, not conf.Fprint, + // because we don't want //line comments in the middle + // of the function types. + fmt.Fprint(fgo2, "\n") + fmt.Fprintf(fgo2, "func %s(", goName) + if fn.Recv != nil { + fmt.Fprint(fgo2, "recv ") + printer.Fprint(fgo2, fset, fn.Recv.List[0].Type) + } + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + fmt.Fprintf(fgo2, ", ") + } + fmt.Fprintf(fgo2, "p%d ", i) + printer.Fprint(fgo2, fset, atype) + }) + fmt.Fprintf(fgo2, ")") + if resultCount > 0 { + fmt.Fprintf(fgo2, " (") + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprint(fgo2, ", ") + } + printer.Fprint(fgo2, fset, atype) + }) + fmt.Fprint(fgo2, ")") + } + fmt.Fprint(fgo2, " {\n") + fmt.Fprint(fgo2, "\tsyscall.CgocallBack()\n") + fmt.Fprint(fgo2, "\tdefer syscall.CgocallBackDone()\n") + fmt.Fprint(fgo2, "\t") + if resultCount > 0 { + fmt.Fprint(fgo2, "return ") + } + if fn.Recv != nil { + fmt.Fprint(fgo2, "recv.") + } + fmt.Fprintf(fgo2, "%s(", exp.Func.Name) + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprint(fgo2, ", ") + } + fmt.Fprintf(fgo2, "p%d", i) + }) + fmt.Fprint(fgo2, ")\n") + fmt.Fprint(fgo2, "}\n") } } +// Return the package prefix when using gccgo. +func (p *Package) gccgoSymbolPrefix() string { + if !*gccgo { + return "" + } + + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + + if *gccgopkgpath != "" { + return strings.Map(clean, *gccgopkgpath) + } + if *gccgoprefix == "" && p.PackageName == "main" { + return "main" + } + prefix := strings.Map(clean, *gccgoprefix) + if prefix == "" { + prefix = "go" + } + return prefix + "." + p.PackageName +} + // Call a function for each entry in an ast.FieldList, passing the // index into the list and the type. func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) { @@ -742,8 +905,8 @@ func c(repr string, args ...interface{}) *TypeRepr { var goTypes = map[string]*Type{ "bool": {Size: 1, Align: 1, C: c("GoUint8")}, "byte": {Size: 1, Align: 1, C: c("GoUint8")}, - "int": {Size: 4, Align: 4, C: c("GoInt")}, - "uint": {Size: 4, Align: 4, C: c("GoUint")}, + "int": {Size: 0, Align: 0, C: c("GoInt")}, + "uint": {Size: 0, Align: 0, C: c("GoUint")}, "rune": {Size: 4, Align: 4, C: c("GoInt32")}, "int8": {Size: 1, Align: 1, C: c("GoInt8")}, "uint8": {Size: 1, Align: 1, C: c("GoUint8")}, @@ -804,12 +967,21 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoUintptr")} } if t.Name == "string" { - return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")} + // The string data is 1 pointer + 1 int, but this always + // rounds to 2 pointers due to alignment. + return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoString")} } if t.Name == "error" { return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} } if r, ok := goTypes[t.Name]; ok { + if r.Size == 0 { // int or uint + rr := new(Type) + *rr = *r + rr.Size = p.IntSize + rr.Align = p.IntSize + r = rr + } if r.Align > p.PtrSize { r.Align = p.PtrSize } @@ -898,18 +1070,21 @@ const cPrologGccgo = ` #include #include +typedef unsigned char byte; +typedef intptr_t intgo; + struct __go_string { const unsigned char *__data; - int __length; + intgo __length; }; typedef struct __go_open_array { void* __values; - int __count; - int __capacity; + intgo __count; + intgo __capacity; } Slice; -struct __go_string __go_byte_array_to_string(const void* p, int len); +struct __go_string __go_byte_array_to_string(const void* p, intgo len); struct __go_open_array __go_string_to_byte_array (struct __go_string str); const char *CString(struct __go_string s) { @@ -917,23 +1092,25 @@ const char *CString(struct __go_string s) { } struct __go_string GoString(char *p) { - int len = (p != NULL) ? strlen(p) : 0; + intgo len = (p != NULL) ? strlen(p) : 0; return __go_byte_array_to_string(p, len); } -struct __go_string GoStringN(char *p, int n) { +struct __go_string GoStringN(char *p, intgo n) { return __go_byte_array_to_string(p, n); } -Slice GoBytes(char *p, int n) { +Slice GoBytes(char *p, intgo n) { struct __go_string s = { (const unsigned char *)p, n }; return __go_string_to_byte_array(s); } ` +func (p *Package) gccExportHeaderProlog() string { + return strings.Replace(gccExportHeaderProlog, "GOINTBITS", fmt.Sprint(8*p.IntSize), -1) +} + const gccExportHeaderProlog = ` -typedef int GoInt; -typedef unsigned int GoUint; typedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; @@ -942,6 +1119,8 @@ typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; +typedef GoIntGOINTBITS GoInt; +typedef GoUintGOINTBITS GoUint; typedef __SIZE_TYPE__ GoUintptr; typedef float GoFloat32; typedef double GoFloat64; @@ -952,4 +1131,5 @@ typedef struct { char *p; int n; } GoString; typedef void *GoMap; typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; int len; int cap; } GoSlice; ` diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index a0f216614..4e7800d12 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -5,9 +5,9 @@ package main import ( + "bytes" "fmt" "go/token" - "io/ioutil" "os" "os/exec" ) @@ -16,50 +16,17 @@ import ( // It returns the output to standard output and standard error. // ok indicates whether the command exited successfully. func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { - cmd, err := exec.LookPath(argv[0]) - if err != nil { - fatalf("exec %s: %s", argv[0], err) - } - r0, w0, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - r1, w1, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - r2, w2, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - p, err := os.StartProcess(cmd, argv, &os.ProcAttr{Files: []*os.File{r0, w1, w2}}) - if err != nil { - fatalf("%s", err) - } - r0.Close() - w1.Close() - w2.Close() - c := make(chan bool) - go func() { - w0.Write(stdin) - w0.Close() - c <- true - }() - go func() { - stdout, _ = ioutil.ReadAll(r1) - r1.Close() - c <- true - }() - stderr, _ = ioutil.ReadAll(r2) - r2.Close() - <-c - <-c - - state, err := p.Wait() - if err != nil { + p := exec.Command(argv[0], argv[1:]...) + p.Stdin = bytes.NewReader(stdin) + var bout, berr bytes.Buffer + p.Stdout = &bout + p.Stderr = &berr + err := p.Run() + if _, ok := err.(*exec.ExitError); err != nil && !ok { fatalf("%s", err) } - ok = state.Success() + ok = p.ProcessState.Success() + stdout, stderr = bout.Bytes(), berr.Bytes() return } diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile new file mode 100644 index 000000000..3f528d751 --- /dev/null +++ b/src/cmd/cov/Makefile @@ -0,0 +1,5 @@ +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../Make.dist diff --git a/src/cmd/cov/doc.go b/src/cmd/cov/doc.go new file mode 100644 index 000000000..ab5d1220a --- /dev/null +++ b/src/cmd/cov/doc.go @@ -0,0 +1,36 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* + +Cov is a rudimentary code coverage tool. + +Usage: + go tool cov [-lsv] [-g substring] [-m minlines] [6.out args] + +Given a command to run, it runs the command while tracking which +sections of code have been executed. When the command finishes, +cov prints the line numbers of sections of code in the binary that +were not executed. With no arguments it assumes the command "6.out". + + +The options are: + + -l + print full path names instead of paths relative to the current directory + -s + show the source code that didn't execute, in addition to the line numbers. + -v + print debugging information during the run. + -g substring + restrict the coverage analysis to functions or files whose names contain substring + -m minlines + only report uncovered sections of code larger than minlines lines + +The program is the same for all architectures: 386, amd64, and arm. + +*/ +package main diff --git a/src/cmd/cov/main.c b/src/cmd/cov/main.c new file mode 100644 index 000000000..33ef49e17 --- /dev/null +++ b/src/cmd/cov/main.c @@ -0,0 +1,484 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * code coverage + */ + +#include +#include +#include +#include "tree.h" + +#include +#include +typedef struct Ureg Ureg; + +void +usage(void) +{ + fprint(2, "usage: cov [-lsv] [-g substring] [-m minlines] [6.out args...]\n"); + fprint(2, "-g specifies pattern of interesting functions or files\n"); + exits("usage"); +} + +typedef struct Range Range; +struct Range +{ + uvlong pc; + uvlong epc; +}; + +int chatty; +int fd; +int longnames; +int pid; +int doshowsrc; +Map *mem; +Map *text; +Fhdr fhdr; +char *substring; +char cwd[1000]; +int ncwd; +int minlines = -1000; + +Tree breakpoints; // code ranges not run + +/* + * comparison for Range structures + * they are "equal" if they overlap, so + * that a search for [pc, pc+1) finds the + * Range containing pc. + */ +int +rangecmp(void *va, void *vb) +{ + Range *a = va, *b = vb; + if(a->epc <= b->pc) + return 1; + if(b->epc <= a->pc) + return -1; + return 0; +} + +/* + * remember that we ran the section of code [pc, epc). + */ +void +ran(uvlong pc, uvlong epc) +{ + Range key; + Range *r; + uvlong oldepc; + + if(chatty) + print("run %#llux-%#llux\n", pc, epc); + + key.pc = pc; + key.epc = pc+1; + r = treeget(&breakpoints, &key); + if(r == nil) + sysfatal("unchecked breakpoint at %#llux+%d", pc, (int)(epc-pc)); + + // Might be that the tail of the sequence + // was run already, so r->epc is before the end. + // Adjust len. + if(epc > r->epc) + epc = r->epc; + + if(r->pc == pc) { + r->pc = epc; + } else { + // Chop r to before pc; + // add new entry for after if needed. + // Changing r->epc does not affect r's position in the tree. + oldepc = r->epc; + r->epc = pc; + if(epc < oldepc) { + Range *n; + n = malloc(sizeof *n); + if(n == nil) + sysfatal("out of memory"); + n->pc = epc; + n->epc = oldepc; + treeput(&breakpoints, n, n); + } + } +} + +void +showsrc(char *file, int line1, int line2) +{ + Biobuf *b; + char *p; + int n, stop; + + if((b = Bopen(file, OREAD)) == nil) { + print("\topen %s: %r\n", file); + return; + } + + for(n=1; n line2) + stop = line2; + if(stop < line2) + stop--; + for(; n<=stop && (p = Brdstr(b, '\n', 1)) != nil; n++) { + print(" %d %s\n", n, p); + free(p); + } + if(n < line2) + print(" ...\n"); + Bterm(b); +} + +/* + * if s is in the current directory or below, + * return the relative path. + */ +char* +shortname(char *s) +{ + if(!longnames && strlen(s) > ncwd && memcmp(s, cwd, ncwd) == 0 && s[ncwd] == '/') + return s+ncwd+1; + return s; +} + +/* + * we've decided that [pc, epc) did not run. + * do something about it. + */ +void +missing(uvlong pc, uvlong epc) +{ + char file[1000]; + int line1, line2; + char buf[100]; + Symbol s; + char *p; + uvlong uv; + + if(!findsym(pc, CTEXT, &s) || !fileline(file, sizeof file, pc)) { + notfound: + print("%#llux-%#llux\n", pc, epc); + return; + } + p = strrchr(file, ':'); + *p++ = 0; + line1 = atoi(p); + for(uv=pc; uvinstsize(text, uv); + } + p = strrchr(file, ':'); + *p++ = 0; + line2 = atoi(p); + + if(line2+1-line2 < minlines) + return; + + if(pc == s.value) { + // never entered function + print("%s:%d %s never called (%#llux-%#llux)\n", shortname(file), line1, s.name, pc, epc); + return; + } + if(pc <= s.value+13) { + // probably stub for stack growth. + // check whether last instruction is call to morestack. + // the -5 below is the length of + // CALL sys.morestack. + buf[0] = 0; + machdata->das(text, epc-5, 0, buf, sizeof buf); + if(strstr(buf, "morestack")) + return; + } + + if(epc - pc == 5) { + // check for CALL sys.panicindex + buf[0] = 0; + machdata->das(text, pc, 0, buf, sizeof buf); + if(strstr(buf, "panicindex")) + return; + } + + if(epc - pc == 2 || epc -pc == 3) { + // check for XORL inside shift. + // (on x86 have to implement large left or unsigned right shift with explicit zeroing). + // f+90 0x00002c9f CMPL CX,$20 + // f+93 0x00002ca2 JCS f+97(SB) + // f+95 0x00002ca4 XORL AX,AX <<< + // f+97 0x00002ca6 SHLL CL,AX + // f+99 0x00002ca8 MOVL $1,CX + // + // f+c8 0x00002cd7 CMPL CX,$40 + // f+cb 0x00002cda JCS f+d0(SB) + // f+cd 0x00002cdc XORQ AX,AX <<< + // f+d0 0x00002cdf SHLQ CL,AX + // f+d3 0x00002ce2 MOVQ $1,CX + buf[0] = 0; + machdata->das(text, pc, 0, buf, sizeof buf); + if(strncmp(buf, "XOR", 3) == 0) { + machdata->das(text, epc, 0, buf, sizeof buf); + if(strncmp(buf, "SHL", 3) == 0 || strncmp(buf, "SHR", 3) == 0) + return; + } + } + + if(epc - pc == 3) { + // check for SAR inside shift. + // (on x86 have to implement large signed right shift as >>31). + // f+36 0x00016216 CMPL CX,$20 + // f+39 0x00016219 JCS f+3e(SB) + // f+3b 0x0001621b SARL $1f,AX <<< + // f+3e 0x0001621e SARL CL,AX + // f+40 0x00016220 XORL CX,CX + // f+42 0x00016222 CMPL CX,AX + buf[0] = 0; + machdata->das(text, pc, 0, buf, sizeof buf); + if(strncmp(buf, "SAR", 3) == 0) { + machdata->das(text, epc, 0, buf, sizeof buf); + if(strncmp(buf, "SAR", 3) == 0) + return; + } + } + + // show first instruction to make clear where we were. + machdata->das(text, pc, 0, buf, sizeof buf); + + if(line1 != line2) + print("%s:%d,%d %#llux-%#llux %s\n", + shortname(file), line1, line2, pc, epc, buf); + else + print("%s:%d %#llux-%#llux %s\n", + shortname(file), line1, pc, epc, buf); + if(doshowsrc) + showsrc(file, line1, line2); +} + +/* + * walk the tree, calling missing for each non-empty + * section of missing code. + */ +void +walktree(TreeNode *t) +{ + Range *n; + + if(t == nil) + return; + walktree(t->left); + n = t->key; + if(n->pc < n->epc) + missing(n->pc, n->epc); + walktree(t->right); +} + +/* + * set a breakpoint all over [pc, epc) + * and remember that we did. + */ +void +breakpoint(uvlong pc, uvlong epc) +{ + Range *r; + + r = malloc(sizeof *r); + if(r == nil) + sysfatal("out of memory"); + r->pc = pc; + r->epc = epc; + treeput(&breakpoints, r, r); + + for(; pc < epc; pc+=machdata->bpsize) + put1(mem, pc, machdata->bpinst, machdata->bpsize); +} + +/* + * install breakpoints over all text symbols + * that match the pattern. + */ +void +cover(void) +{ + Symbol s; + char *lastfn; + uvlong lastpc; + int i; + char buf[200]; + + lastfn = nil; + lastpc = 0; + for(i=0; textsym(&s, i); i++) { + switch(s.type) { + case 'T': + case 't': + if(lastpc != 0) { + breakpoint(lastpc, s.value); + lastpc = 0; + } + // Ignore second entry for a given name; + // that's the debugging blob. + if(lastfn && strcmp(s.name, lastfn) == 0) + break; + lastfn = s.name; + buf[0] = 0; + fileline(buf, sizeof buf, s.value); + if(substring == nil || strstr(buf, substring) || strstr(s.name, substring)) + lastpc = s.value; + } + } +} + +uvlong +rgetzero(Map *map, char *reg) +{ + USED(map); + USED(reg); + + return 0; +} + +/* + * remove the breakpoints at pc and successive instructions, + * up to and including the first jump or other control flow transfer. + */ +void +uncover(uvlong pc) +{ + uchar buf[1000]; + int n, n1, n2; + uvlong foll[2]; + + // Double-check that we stopped at a breakpoint. + if(get1(mem, pc, buf, machdata->bpsize) < 0) + sysfatal("read mem inst at %#llux: %r", pc); + if(memcmp(buf, machdata->bpinst, machdata->bpsize) != 0) + sysfatal("stopped at %#llux; not at breakpoint %d", pc, machdata->bpsize); + + // Figure out how many bytes of straight-line code + // there are in the text starting at pc. + n = 0; + while(n < sizeof buf) { + n1 = machdata->instsize(text, pc+n); + if(n+n1 > sizeof buf) + break; + n2 = machdata->foll(text, pc+n, rgetzero, foll); + n += n1; + if(n2 != 1 || foll[0] != pc+n) + break; + } + + // Record that this section of code ran. + ran(pc, pc+n); + + // Put original instructions back. + if(get1(text, pc, buf, n) < 0) + sysfatal("get1: %r"); + if(put1(mem, pc, buf, n) < 0) + sysfatal("put1: %r"); +} + +int +startprocess(char **argv) +{ + int pid; + + if((pid = fork()) < 0) + sysfatal("fork: %r"); + if(pid == 0) { + pid = getpid(); + if(ctlproc(pid, "hang") < 0) + sysfatal("ctlproc hang: %r"); + exec(argv[0], argv); + sysfatal("exec %s: %r", argv[0]); + } + if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) + sysfatal("attach %d %s: %r", pid, argv[0]); + return pid; +} + +int +go(void) +{ + uvlong pc; + char buf[100]; + int n; + + for(n = 0;; n++) { + ctlproc(pid, "startstop"); + if(get8(mem, offsetof(Ureg, ip), &pc) < 0) { + rerrstr(buf, sizeof buf); + if(strstr(buf, "exited") || strstr(buf, "No such process")) + return n; + sysfatal("cannot read pc: %r"); + } + pc--; + if(put8(mem, offsetof(Ureg, ip), pc) < 0) + sysfatal("cannot write pc: %r"); + uncover(pc); + } +} + +void +main(int argc, char **argv) +{ + int n; + + ARGBEGIN{ + case 'g': + substring = EARGF(usage()); + break; + case 'l': + longnames++; + break; + case 'n': + minlines = atoi(EARGF(usage())); + break; + case 's': + doshowsrc = 1; + break; + case 'v': + chatty++; + break; + default: + usage(); + }ARGEND + + getwd(cwd, sizeof cwd); + ncwd = strlen(cwd); + + if(argc == 0) { + *--argv = "6.out"; + } + fd = open(argv[0], OREAD); + if(fd < 0) + sysfatal("open %s: %r", argv[0]); + if(crackhdr(fd, &fhdr) <= 0) + sysfatal("crackhdr: %r"); + machbytype(fhdr.type); + if(syminit(fd, &fhdr) <= 0) + sysfatal("syminit: %r"); + text = loadmap(nil, fd, &fhdr); + if(text == nil) + sysfatal("loadmap: %r"); + pid = startprocess(argv); + mem = attachproc(pid, &fhdr); + if(mem == nil) + sysfatal("attachproc: %r"); + breakpoints.cmp = rangecmp; + cover(); + n = go(); + walktree(breakpoints.root); + if(chatty) + print("%d breakpoints\n", n); + detachproc(mem); + exits(0); +} + diff --git a/src/cmd/cov/tree.c b/src/cmd/cov/tree.c new file mode 100644 index 000000000..366a47efd --- /dev/null +++ b/src/cmd/cov/tree.c @@ -0,0 +1,245 @@ +// Renamed from Map to Tree to avoid conflict with libmach. + +/* +Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements, + Massachusetts Institute of Technology +Portions Copyright (c) 2009 The Go Authors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Mutable map structure, but still based on +// Okasaki, Red Black Trees in a Functional Setting, JFP 1999, +// which is a lot easier than the traditional red-black +// and plenty fast enough for me. (Also I could copy +// and edit fmap.c.) + +#include +#include +#include "tree.h" + +enum +{ + Red = 0, + Black = 1 +}; + + +// Red-black trees are binary trees with this property: +// 1. No red node has a red parent. +// 2. Every path from the root to a leaf contains the +// same number of black nodes. + +static TreeNode* +rwTreeNode(TreeNode *p, int color, TreeNode *left, void *key, void *value, TreeNode *right) +{ + if(p == nil) + p = malloc(sizeof *p); + if(p == nil) + sysfatal("out of memory"); + p->color = color; + p->left = left; + p->key = key; + p->value = value; + p->right = right; + return p; +} + +static TreeNode* +balance(TreeNode *m0) +{ + void *xk, *xv, *yk, *yv, *zk, *zv; + TreeNode *a, *b, *c, *d; + TreeNode *m1, *m2; + int color; + TreeNode *left, *right; + void *key, *value; + + color = m0->color; + left = m0->left; + key = m0->key; + value = m0->value; + right = m0->right; + + // Okasaki notation: (T is mkTreeNode, B is Black, R is Red, x, y, z are key-value. + // + // balance B (T R (T R a x b) y c) z d + // balance B (T R a x (T R b y c)) z d + // balance B a x (T R (T R b y c) z d) + // balance B a x (T R b y (T R c z d)) + // + // = T R (T B a x b) y (T B c z d) + + if(color == Black){ + if(left && left->color == Red){ + if(left->left && left->left->color == Red){ + a = left->left->left; + xk = left->left->key; + xv = left->left->value; + b = left->left->right; + yk = left->key; + yv = left->value; + c = left->right; + zk = key; + zv = value; + d = right; + m1 = left; + m2 = left->left; + goto hard; + }else if(left->right && left->right->color == Red){ + a = left->left; + xk = left->key; + xv = left->value; + b = left->right->left; + yk = left->right->key; + yv = left->right->value; + c = left->right->right; + zk = key; + zv = value; + d = right; + m1 = left; + m2 = left->right; + goto hard; + } + }else if(right && right->color == Red){ + if(right->left && right->left->color == Red){ + a = left; + xk = key; + xv = value; + b = right->left->left; + yk = right->left->key; + yv = right->left->value; + c = right->left->right; + zk = right->key; + zv = right->value; + d = right->right; + m1 = right; + m2 = right->left; + goto hard; + }else if(right->right && right->right->color == Red){ + a = left; + xk = key; + xv = value; + b = right->left; + yk = right->key; + yv = right->value; + c = right->right->left; + zk = right->right->key; + zv = right->right->value; + d = right->right->right; + m1 = right; + m2 = right->right; + goto hard; + } + } + } + return rwTreeNode(m0, color, left, key, value, right); + +hard: + return rwTreeNode(m0, Red, rwTreeNode(m1, Black, a, xk, xv, b), + yk, yv, rwTreeNode(m2, Black, c, zk, zv, d)); +} + +static TreeNode* +ins0(TreeNode *p, void *k, void *v, TreeNode *rw) +{ + if(p == nil) + return rwTreeNode(rw, Red, nil, k, v, nil); + if(p->key == k){ + if(rw) + return rwTreeNode(rw, p->color, p->left, k, v, p->right); + p->value = v; + return p; + } + if(p->key < k) + p->left = ins0(p->left, k, v, rw); + else + p->right = ins0(p->right, k, v, rw); + return balance(p); +} + +static TreeNode* +ins1(Tree *m, TreeNode *p, void *k, void *v, TreeNode *rw) +{ + int i; + + if(p == nil) + return rwTreeNode(rw, Red, nil, k, v, nil); + i = m->cmp(p->key, k); + if(i == 0){ + if(rw) + return rwTreeNode(rw, p->color, p->left, k, v, p->right); + p->value = v; + return p; + } + if(i < 0) + p->left = ins1(m, p->left, k, v, rw); + else + p->right = ins1(m, p->right, k, v, rw); + return balance(p); +} + +void +treeputelem(Tree *m, void *key, void *val, TreeNode *rw) +{ + if(m->cmp) + m->root = ins1(m, m->root, key, val, rw); + else + m->root = ins0(m->root, key, val, rw); +} + +void +treeput(Tree *m, void *key, void *val) +{ + treeputelem(m, key, val, nil); +} + +void* +treeget(Tree *m, void *key) +{ + int i; + TreeNode *p; + + p = m->root; + if(m->cmp){ + for(;;){ + if(p == nil) + return nil; + i = m->cmp(p->key, key); + if(i < 0) + p = p->left; + else if(i > 0) + p = p->right; + else + return p->value; + } + }else{ + for(;;){ + if(p == nil) + return nil; + if(p->key == key) + return p->value; + if(p->key < key) + p = p->left; + else + p = p->right; + } + } +} diff --git a/src/cmd/cov/tree.h b/src/cmd/cov/tree.h new file mode 100644 index 000000000..a716d83ad --- /dev/null +++ b/src/cmd/cov/tree.h @@ -0,0 +1,47 @@ +// Renamed from Map to Tree to avoid conflict with libmach. + +/* +Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements, + Massachusetts Institute of Technology +Portions Copyright (c) 2009 The Go Authors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +typedef struct Tree Tree; +typedef struct TreeNode TreeNode; +struct Tree +{ + int (*cmp)(void*, void*); + TreeNode *root; +}; + +struct TreeNode +{ + int color; + TreeNode *left; + void *key; + void *value; + TreeNode *right; +}; + +void *treeget(Tree*, void*); +void treeput(Tree*, void*, void*); +void treeputelem(Tree*, void*, void*, TreeNode*); diff --git a/src/cmd/dist/README b/src/cmd/dist/README index cf194faf0..e6d08cf02 100644 --- a/src/cmd/dist/README +++ b/src/cmd/dist/README @@ -31,8 +31,8 @@ Bufs or Vecs on the stack should be ... main code ... bprintf(&b1, "hello, world"); vadd(&v1, bstr(&b1)); // v1 takes a copy of its argument - bprintf(&b1, "another string"); - vadd(&v1, bstr(&b1)); // v1 now has two strings + bprintf(&b2, "another string"); + vadd(&v1, bstr(&b2)); // v1 now has two strings bfree(&b1); bfree(&b2); diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h index c19b1f468..fcd81cc51 100644 --- a/src/cmd/dist/a.h +++ b/src/cmd/dist/a.h @@ -10,7 +10,9 @@ typedef long long Time; #define nil ((void*)0) #define nelem(x) (sizeof(x)/sizeof((x)[0])) +#ifndef PLAN9 #define USED(x) ((void)(x)) +#endif // A Buf is a byte buffer, like Go's []byte. typedef struct Buf Buf; @@ -106,6 +108,7 @@ void xmain(int argc, char **argv); // portability layer (plan9.c, unix.c, windows.c) bool contains(char *p, char *sep); +void errprintf(char*, ...); void fatal(char *msg, ...); bool hasprefix(char *p, char *prefix); bool hassuffix(char *p, char *suffix); @@ -120,6 +123,7 @@ void runv(Buf *b, char *dir, int mode, Vec *argv); void bgrunv(char *dir, int mode, Vec *argv); void bgwait(void); bool streq(char*, char*); +bool cansse2(void); void writefile(Buf*, char*, int); void xatexit(void (*f)(void)); void xexit(int); @@ -147,3 +151,6 @@ int xstrlen(char*); char* xstrrchr(char*, int); char* xstrstr(char*, char*); char* xworkdir(void); +int xsamefile(char*, char*); +char* xgetgoarm(void); +int xtryexecfunc(void (*)(void)); diff --git a/src/cmd/dist/arg.h b/src/cmd/dist/arg.h index 6eef0353b..9819765b1 100644 --- a/src/cmd/dist/arg.h +++ b/src/cmd/dist/arg.h @@ -28,7 +28,7 @@ THE SOFTWARE. /* command line */ extern char *argv0; -#define ARGBEGIN for((argv0?0:(argv0=(*argv))),argv++,argc--;\ +#define ARGBEGIN for((argv0=(argv0?argv0:*argv)),argv++,argc--;\ argv[0] && argv[0][0]=='-' && argv[0][1];\ argc--, argv++) {\ char *_args, *_argt;\ @@ -37,7 +37,6 @@ extern char *argv0; if(_args[0]=='-' && _args[1]==0){\ argc--; argv++; break;\ }\ - _argc = 0;\ while((_argc = *_args++) != 0)\ switch(_argc) #define ARGEND _argt=0;USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc); diff --git a/src/cmd/dist/arm.c b/src/cmd/dist/arm.c new file mode 100644 index 000000000..dafc5c1c2 --- /dev/null +++ b/src/cmd/dist/arm.c @@ -0,0 +1,79 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "a.h" + +#ifndef __ARMEL__ +char * +xgetgoarm(void) +{ + return "6"; +} +#else +static void useVFPv3(void); +static void useVFPv1(void); + +char * +xgetgoarm(void) +{ +#if defined(__NetBSD__) || defined(__FreeBSD__) + // NetBSD has buggy support for VFPv2 (incorrect inexact, + // denormial, and NaN handling). When GOARM=6, some of our + // math tests fails on Raspberry Pi. + // Thus we return "5" here for safety, the user is free + // to override. + // Note: using GOARM=6 with cgo can trigger a kernel assertion + // failure and crash NetBSD/evbarm kernel. + // FreeBSD also have broken VFP support, so disable VFP also + // on FreeBSD. + return "5"; +#endif + if(xtryexecfunc(useVFPv3)) + return "7"; + else if(xtryexecfunc(useVFPv1)) + return "6"; + return "5"; +} + +static void +useVFPv3(void) +{ + // try to run VFPv3-only "vmov.f64 d0, #112" instruction + // we can't use that instruction directly, because we + // might be compiling with a soft-float only toolchain. + // + // some newer toolchains are configured to use thumb + // by default, so we need to do some mode changing magic + // here. + // We can use "bx pc; nop" here, but GNU as(1) insists + // on warning us + // "use of r15 in bx in ARM mode is not really useful" + // so we workaround that by using "bx r0" + __asm__ __volatile__ ("mov r0, pc"); + __asm__ __volatile__ ("bx r0"); + __asm__ __volatile__ (".word 0xeeb70b00"); // vmov.f64 d0, #112 + __asm__ __volatile__ (".word 0xe12fff1e"); // bx lr +} + +static void +useVFPv1(void) +{ + // try to run "vmov.f64 d0, d0" instruction + // we can't use that instruction directly, because we + // might be compiling with a soft-float only toolchain + // + // some newer toolchains are configured to use thumb + // by default, so we need to do some mode changing magic + // here. + // We can use "bx pc; nop" here, but GNU as(1) insists + // on warning us + // "use of r15 in bx in ARM mode is not really useful" + // so we workaround that by using "bx r0" + __asm__ __volatile__ ("mov r0, pc"); + __asm__ __volatile__ ("bx r0"); + __asm__ __volatile__ (".word 0xeeb00b40"); // vomv.f64 d0, d0 + __asm__ __volatile__ (".word 0xe12fff1e"); // bx lr +} + +#endif diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c index 7f9aa7bcd..dda45ca62 100644 --- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -16,6 +16,8 @@ char *gohostarch; char *gohostchar; char *gohostos; char *goos; +char *goarm; +char *go386; char *goroot = GOROOT_FINAL; char *goroot_final = GOROOT_FINAL; char *workdir; @@ -96,6 +98,20 @@ init(void) if(find(goos, okgoos, nelem(okgoos)) < 0) fatal("unknown $GOOS %s", goos); + xgetenv(&b, "GOARM"); + if(b.len == 0) + bwritestr(&b, xgetgoarm()); + goarm = btake(&b); + + xgetenv(&b, "GO386"); + if(b.len == 0) { + if(cansse2()) + bwritestr(&b, "sse2"); + else + bwritestr(&b, "387"); + } + go386 = btake(&b); + p = bpathf(&b, "%s/include/u.h", goroot); if(!isfile(p)) { fatal("$GOROOT is not set correctly or not exported\n" @@ -126,6 +142,8 @@ init(void) xsetenv("GOROOT", goroot); xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); + xsetenv("GOARM", goarm); + xsetenv("GO386", go386); // Make the environment more predictable. xsetenv("LANG", "C"); @@ -147,7 +165,7 @@ static void rmworkdir(void) { if(vflag > 1) - xprintf("rm -rf %s\n", workdir); + errprintf("rm -rf %s\n", workdir); xremoveall(workdir); } @@ -207,7 +225,7 @@ findgoversion(void) chomp(&branch); // What are the tags along the current branch? - tag = ""; + tag = "devel"; rev = "."; run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "-r", ".:0", "--template", "{tags} + ", nil); splitfields(&tags, bstr(&b)); @@ -216,7 +234,9 @@ findgoversion(void) p = tags.p[i]; if(streq(p, "+")) nrev++; - if(hasprefix(p, "release.") || hasprefix(p, "weekly.") || hasprefix(p, "go")) { + // NOTE: Can reenable the /* */ code when we want to + // start reporting versions named 'weekly' again. + if(/*hasprefix(p, "weekly.") ||*/ hasprefix(p, "go")) { tag = xstrdup(p); // If this tag matches the current checkout // exactly (no "+" yet), don't show extra @@ -236,7 +256,7 @@ findgoversion(void) if(rev[0]) { // Tag is before the revision we're building. // Add extra information. - run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short}", "-r", rev, nil); + run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short} {date|date}", "-r", rev, nil); chomp(&bmore); } @@ -378,6 +398,9 @@ setup(void) // gccargs is the gcc command line to use for compiling a single C file. static char *proto_gccargs[] = { "-Wall", + // native Plan 9 compilers don't like non-standard prototypes + // so let gcc catch them. + "-Wstrict-prototypes", "-Wno-sign-compare", "-Wno-missing-braces", "-Wno-parentheses", @@ -387,7 +410,14 @@ static char *proto_gccargs[] = { "-Werror", "-fno-common", "-ggdb", + "-pipe", +#if defined(__NetBSD__) && defined(__arm__) + // GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c + // Fix available at http://patchwork.ozlabs.org/patch/64562/. + "-O1", +#else "-O2", +#endif }; static Vec gccargs; @@ -474,14 +504,7 @@ static struct { "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a", }}, {"cmd/5l", { - "../ld/data.c", - "../ld/elf.c", - "../ld/go.c", - "../ld/ldelf.c", - "../ld/ldmacho.c", - "../ld/ldpe.c", - "../ld/lib.c", - "../ld/symtab.c", + "../ld/*", "enam.c", }}, {"cmd/6l", { @@ -535,17 +558,17 @@ static void install(char *dir) { char *name, *p, *elem, *prefix, *exe; - bool islib, ispkg, isgo, stale; + bool islib, ispkg, isgo, stale, clang; Buf b, b1, path; Vec compile, files, link, go, missing, clean, lib, extra; Time ttarg, t; - int i, j, k, n, doclean, targ; + int i, j, k, n, doclean, targ, usecpp; if(vflag) { if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) - xprintf("%s (%s/%s)\n", dir, goos, goarch); + errprintf("%s (%s/%s)\n", dir, goos, goarch); else - xprintf("%s\n", dir); + errprintf("%s\n", dir); } binit(&b); @@ -560,6 +583,7 @@ install(char *dir) vinit(&lib); vinit(&extra); + // path = full path to dir. bpathf(&path, "%s/src/%s", goroot, dir); name = lastelem(dir); @@ -574,7 +598,7 @@ install(char *dir) // For release, cmd/prof and cmd/cov are not included. if((streq(dir, "cmd/cov") || streq(dir, "cmd/prof")) && !isdir(bstr(&path))) { if(vflag > 1) - xprintf("skipping %s - does not exist\n", dir); + errprintf("skipping %s - does not exist\n", dir); goto out; } @@ -583,12 +607,13 @@ install(char *dir) xgetenv(&b, "CC"); if(b.len == 0) bprintf(&b, "gcc"); + clang = contains(bstr(&b), "clang"); splitfields(&gccargs, bstr(&b)); for(i=0; i ttarg) stale = 1; @@ -767,7 +808,7 @@ install(char *dir) for(j=0; j 1) - xprintf("generate %s\n", p); + errprintf("generate %s\n", p); gentab[j].gen(bstr(&path), p); // Do not add generated file to clean list. // In pkg/runtime, we want to be able to @@ -799,10 +840,10 @@ install(char *dir) p = files.p[i]; if(!hassuffix(p, ".goc")) continue; - // b = path/zp but with _goarch.c instead of .goc + // b = path/zp but with _goos_goarch.c instead of .goc bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p)); b.len -= 4; - bwritef(&b, "_%s.c", goarch); + bwritef(&b, "_%s_%s.c", goos, goarch); goc2c(p, bstr(&b)); vadd(&files, bstr(&b)); } @@ -812,10 +853,24 @@ install(char *dir) if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) { // We've generated the right files; the go command can do the build. if(vflag > 1) - xprintf("skip build for cross-compile %s\n", dir); + errprintf("skip build for cross-compile %s\n", dir); goto nobuild; } + // The files generated by GNU Bison use macros that aren't + // supported by the Plan 9 compilers so we have to use the + // external preprocessor when compiling. + usecpp = 0; + if(streq(gohostos, "plan9")) { + for(i=0; i 1) - xprintf("cp %s %s\n", src, dst); + errprintf("cp %s %s\n", src, dst); binit(&b); readfile(&b, src); @@ -1125,6 +1221,7 @@ static char *buildorder[] = { "pkg/go/ast", "pkg/go/parser", "pkg/os/exec", + "pkg/os/signal", "pkg/net/url", "pkg/text/template/parse", "pkg/text/template", @@ -1294,6 +1391,9 @@ cmdenv(int argc, char **argv) format = "%s=\"%s\"\n"; pflag = 0; ARGBEGIN{ + case '9': + format = "%s='%s'\n"; + break; case 'p': pflag = 1; break; @@ -1301,7 +1401,7 @@ cmdenv(int argc, char **argv) vflag++; break; case 'w': - format = "set %s=%s\n"; + format = "set %s=%s\r\n"; break; default: usage(); @@ -1318,6 +1418,10 @@ cmdenv(int argc, char **argv) xprintf(format, "GOHOSTOS", gohostos); xprintf(format, "GOTOOLDIR", tooldir); xprintf(format, "GOCHAR", gochar); + if(streq(goarch, "arm")) + xprintf(format, "GOARM", goarm); + if(streq(goarch, "386")) + xprintf(format, "GO386", go386); if(pflag) { sep = ":"; @@ -1511,7 +1615,7 @@ cmdbanner(int argc, char **argv) "Read and run ./sudo.bash to install the debuggers.\n"); } - if(!streq(goroot_final, goroot)) { + if(!xsamefile(goroot_final, goroot)) { xprintf("\n" "The binaries expect %s to be copied or moved to %s\n", goroot, goroot_final); diff --git a/src/cmd/dist/buildgc.c b/src/cmd/dist/buildgc.c index da38760c6..03a797f2c 100644 --- a/src/cmd/dist/buildgc.c +++ b/src/cmd/dist/buildgc.c @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. #include "a.h" -#include /* * Helpers for building cmd/gc. diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c index a0c62010d..f46f72d4b 100644 --- a/src/cmd/dist/buildruntime.c +++ b/src/cmd/dist/buildruntime.c @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. #include "a.h" -#include /* * Helpers for building pkg/runtime. @@ -20,6 +19,8 @@ mkzversion(char *dir, char *file) { Buf b, out; + USED(dir); + binit(&b); binit(&out); @@ -46,6 +47,8 @@ void mkzgoarch(char *dir, char *file) { Buf b, out; + + USED(dir); binit(&b); binit(&out); @@ -72,6 +75,8 @@ void mkzgoos(char *dir, char *file) { Buf b, out; + + USED(dir); binit(&b); binit(&out); @@ -100,9 +105,15 @@ static struct { "#define m(r) 4(r)\n" }, {"386", "plan9", + "// Plan 9 does not have per-process segment descriptors with\n" + "// which to do thread-local storage. Instead, we will use a\n" + "// fixed offset from the per-process TOS struct address for\n" + "// the local storage. Since the process ID is contained in the\n" + "// TOS struct, we specify an offset for that here as well.\n" "#define get_tls(r) MOVL _tos(SB), r \n" "#define g(r) -8(r)\n" "#define m(r) -4(r)\n" + "#define procid(r) 48(r)\n" }, {"386", "linux", "// On Linux systems, what we call 0(GS) and 4(GS) for g and m\n" @@ -140,6 +151,12 @@ static struct { "#define g(r) 0(r)\n" "#define m(r) 8(r)\n" }, + {"amd64", "plan9", + "#define get_tls(r)\n" + "#define g(r) 0(GS)\n" + "#define m(r) 8(GS)\n" + "#define procid(r) 16(GS)\n" + }, {"amd64", "", "// The offsets 0 and 8 are known to:\n" "// ../../cmd/6l/pass.c:/D_GS\n" @@ -188,16 +205,23 @@ mkzasm(char *dir, char *file) fatal("unknown $GOOS/$GOARCH in mkzasm"); ok: - // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -a proc.c + // Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -a -n -o workdir/proc.acid proc.c // to get acid [sic] output. vreset(&argv); vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar)); - vadd(&argv, bprintf(&b, "-DGOOS_%s", goos)); - vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch)); - vadd(&argv, bprintf(&b, "-I%s", workdir)); + vadd(&argv, "-D"); + vadd(&argv, bprintf(&b, "GOOS_%s", goos)); + vadd(&argv, "-D"); + vadd(&argv, bprintf(&b, "GOARCH_%s", goarch)); + vadd(&argv, "-I"); + vadd(&argv, bprintf(&b, "%s", workdir)); vadd(&argv, "-a"); + vadd(&argv, "-n"); + vadd(&argv, "-o"); + vadd(&argv, bpathf(&b, "%s/proc.acid", workdir)); vadd(&argv, "proc.c"); - runv(&in, dir, CheckExit, &argv); + runv(nil, dir, CheckExit, &argv); + readfile(&in, bpathf(&b, "%s/proc.acid", workdir)); // Convert input like // aggr G @@ -222,6 +246,8 @@ ok: aggr = "gobuf"; else if(streq(fields.p[1], "WinCall")) aggr = "wincall"; + else if(streq(fields.p[1], "SEH")) + aggr = "seh"; } if(hasprefix(lines.p[i], "}")) aggr = nil; @@ -251,6 +277,7 @@ static char *runtimedefs[] = { "iface.c", "hashmap.c", "chan.c", + "parfor.c", }; // mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h, @@ -265,11 +292,12 @@ mkzruntimedefs(char *dir, char *file) { int i, skip; char *p; - Buf in, b, out; + Buf in, b, b1, out; Vec argv, lines, fields, seen; binit(&in); binit(&b); + binit(&b1); binit(&out); vinit(&argv); vinit(&lines); @@ -285,18 +313,25 @@ mkzruntimedefs(char *dir, char *file) ); - // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -q + // Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -q -n -o workdir/runtimedefs // on each of the runtimedefs C files. vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar)); - vadd(&argv, bprintf(&b, "-DGOOS_%s", goos)); - vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch)); - vadd(&argv, bprintf(&b, "-I%s", workdir)); + vadd(&argv, "-D"); + vadd(&argv, bprintf(&b, "GOOS_%s", goos)); + vadd(&argv, "-D"); + vadd(&argv, bprintf(&b, "GOARCH_%s", goarch)); + vadd(&argv, "-I"); + vadd(&argv, bprintf(&b, "%s", workdir)); vadd(&argv, "-q"); + vadd(&argv, "-n"); + vadd(&argv, "-o"); + vadd(&argv, bpathf(&b, "%s/runtimedefs", workdir)); vadd(&argv, ""); p = argv.p[argv.len-1]; for(i=0; i 0) { - q[len] = '*'; - ++len; - --pointer_count; + + // Turn int/uint into intgo/uintgo. + if((len == 3 && xmemcmp(q, "int", 3) == 0) || (len == 4 && xmemcmp(q, "uint", 4) == 0)) { + q[len++] = 'g'; + q[len++] = 'o'; } + + while (pointer_count-- > 0) + q[len++] = '*'; + q[len] = '\0'; xfree(op); return q; @@ -570,8 +584,9 @@ write_gcc_func_header(char *package, char *name, struct params *params, static void write_gcc_func_trailer(char *package, char *name, struct params *rets) { - if (rets == nil) - ; + if (rets == nil) { + // nothing to do + } else if (rets->next == nil) bwritef(output, "return %s;\n", rets->name); else { @@ -712,15 +727,25 @@ goc2c(char *goc, char *c) if(!gcc) { if(streq(goarch, "amd64")) { type_table[Uintptr].size = 8; - type_table[String].size = 16; - type_table[Slice].size = 8+4+4; type_table[Eface].size = 8+8; + type_table[String].size = 16; + if(use64bitint) { + type_table[Int].size = 8; + type_table[Uint].size = 8; + } + type_table[Slice].size = 8+2*type_table[Int].size; structround = 8; } else { + // NOTE: These are set in the initializer, + // but they might have been changed by a + // previous invocation of goc2c, so we have + // to restore them. type_table[Uintptr].size = 4; type_table[String].size = 8; type_table[Slice].size = 16; type_table[Eface].size = 4+4; + type_table[Int].size = 4; + type_table[Uint].size = 4; structround = 4; } } diff --git a/src/cmd/dist/plan9.c b/src/cmd/dist/plan9.c new file mode 100644 index 000000000..8a7c0ab1c --- /dev/null +++ b/src/cmd/dist/plan9.c @@ -0,0 +1,764 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// These #ifdefs are being used as a substitute for +// build configuration, so that on any system, this +// tool can be built with the local equivalent of +// cc *.c +// +#ifdef PLAN9 + +#include +#include +#include +#undef nil +#undef nelem +#include "a.h" + +// bprintf replaces the buffer with the result of the printf formatting +// and returns a pointer to the NUL-terminated buffer contents. +char* +bprintf(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + breset(b); + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); + return bstr(b); +} + +// bpathf is the same as bprintf (on windows it turns / into \ after the printf). +// It returns a pointer to the NUL-terminated buffer contents. +char* +bpathf(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + breset(b); + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); + return bstr(b); +} + +// bwritef is like bprintf but does not reset the buffer +// and does not return the NUL-terminated string. +void +bwritef(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); +} + +// breadfrom appends to b all the data that can be read from fd. +static void +breadfrom(Buf *b, int fd) +{ + int n; + + for(;;) { + bgrow(b, 4096); + n = read(fd, b->p+b->len, 4096); + if(n < 0) + fatal("read"); + if(n == 0) + break; + b->len += n; + } +} + +// xgetenv replaces b with the value of the named environment variable. +void +xgetenv(Buf *b, char *name) +{ + char *p; + + breset(b); + p = getenv(name); + if(p != nil) + bwritestr(b, p); +} + +static void genrun(Buf *b, char *dir, int mode, Vec *argv, int bg); + +// run runs the command named by cmd. +// If b is not nil, run replaces b with the output of the command. +// If dir is not nil, run runs the command in that directory. +// If mode is CheckExit, run calls fatal if the command is not successful. +void +run(Buf *b, char *dir, int mode, char *cmd, ...) +{ + va_list arg; + Vec argv; + char *p; + + vinit(&argv); + vadd(&argv, cmd); + va_start(arg, cmd); + while((p = va_arg(arg, char*)) != nil) + vadd(&argv, p); + va_end(arg); + + runv(b, dir, mode, &argv); + + vfree(&argv); +} + +// runv is like run but takes a vector. +void +runv(Buf *b, char *dir, int mode, Vec *argv) +{ + genrun(b, dir, mode, argv, 1); +} + +// bgrunv is like run but runs the command in the background. +// bgwait waits for pending bgrunv to finish. +void +bgrunv(char *dir, int mode, Vec *argv) +{ + genrun(nil, dir, mode, argv, 0); +} + +#define MAXBG 4 /* maximum number of jobs to run at once */ + +static struct { + int pid; + int mode; + char *cmd; + Buf *b; +} bg[MAXBG]; +static int nbg; +static int maxnbg = nelem(bg); + +static void bgwait1(void); + +// genrun is the generic run implementation. +static void +genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) +{ + int i, p[2], pid; + Buf b1, cmd; + char *q; + + while(nbg >= maxnbg) + bgwait1(); + + binit(&b1); + binit(&cmd); + + if(!isabs(argv->p[0])) { + bpathf(&b1, "/bin/%s", argv->p[0]); + free(argv->p[0]); + argv->p[0] = xstrdup(bstr(&b1)); + } + + // Generate a copy of the command to show in a log. + // Substitute $WORK for the work directory. + for(i=0; ilen; i++) { + if(i > 0) + bwritestr(&cmd, " "); + q = argv->p[i]; + if(workdir != nil && hasprefix(q, workdir)) { + bwritestr(&cmd, "$WORK"); + q += strlen(workdir); + } + bwritestr(&cmd, q); + } + if(vflag > 1) + errprintf("%s\n", bstr(&cmd)); + + if(b != nil) { + breset(b); + if(pipe(p) < 0) + fatal("pipe"); + } + + switch(pid = fork()) { + case -1: + fatal("fork"); + case 0: + if(b != nil) { + close(0); + close(p[0]); + dup(p[1], 1); + dup(p[1], 2); + if(p[1] > 2) + close(p[1]); + } + if(dir != nil) { + if(chdir(dir) < 0) { + fprint(2, "chdir: %r\n"); + _exits("chdir"); + } + } + vadd(argv, nil); + exec(argv->p[0], argv->p); + fprint(2, "%s\n", bstr(&cmd)); + fprint(2, "exec: %r\n"); + _exits("exec"); + } + if(b != nil) { + close(p[1]); + breadfrom(b, p[0]); + close(p[0]); + } + + if(nbg < 0) + fatal("bad bookkeeping"); + bg[nbg].pid = pid; + bg[nbg].mode = mode; + bg[nbg].cmd = btake(&cmd); + bg[nbg].b = b; + nbg++; + + if(wait) + bgwait(); + + bfree(&cmd); + bfree(&b1); +} + +// bgwait1 waits for a single background job. +static void +bgwait1(void) +{ + Waitmsg *w; + int i, mode; + char *cmd; + Buf *b; + + w = wait(); + if(w == nil) + fatal("wait"); + + for(i=0; ipid) + goto ok; + fatal("wait: unexpected pid"); + +ok: + cmd = bg[i].cmd; + mode = bg[i].mode; + bg[i].pid = 0; + b = bg[i].b; + bg[i].b = nil; + bg[i] = bg[--nbg]; + + if(mode == CheckExit && w->msg[0]) { + if(b != nil) + xprintf("%s\n", bstr(b)); + fatal("FAILED: %s", cmd); + } + xfree(cmd); +} + +// bgwait waits for all the background jobs. +void +bgwait(void) +{ + while(nbg > 0) + bgwait1(); +} + +// xgetwd replaces b with the current directory. +void +xgetwd(Buf *b) +{ + char buf[4096]; + + breset(b); + if(getwd(buf, sizeof buf) == nil) + fatal("getwd"); + bwritestr(b, buf); +} + +// xrealwd replaces b with the 'real' name for the given path. +// real is defined as what getcwd returns in that directory. +void +xrealwd(Buf *b, char *path) +{ + char buf[4096]; + int fd; + + fd = open(path, OREAD); + if(fd2path(fd, buf, sizeof buf) < 0) + fatal("fd2path"); + close(fd); + breset(b); + bwritestr(b, buf); +} + +// isdir reports whether p names an existing directory. +bool +isdir(char *p) +{ + Dir *d; + ulong mode; + + d = dirstat(p); + if(d == nil) + return 0; + mode = d->mode; + free(d); + return (mode & DMDIR) == DMDIR; +} + +// isfile reports whether p names an existing file. +bool +isfile(char *p) +{ + Dir *d; + ulong mode; + + d = dirstat(p); + if(d == nil) + return 0; + mode = d->mode; + free(d); + return (mode & DMDIR) == 0; +} + +// mtime returns the modification time of the file p. +Time +mtime(char *p) +{ + Dir *d; + ulong t; + + d = dirstat(p); + if(d == nil) + return 0; + t = d->mtime; + free(d); + return (Time)t; +} + +// isabs reports whether p is an absolute path. +bool +isabs(char *p) +{ + return hasprefix(p, "/"); +} + +// readfile replaces b with the content of the named file. +void +readfile(Buf *b, char *file) +{ + int fd; + + breset(b); + fd = open(file, OREAD); + if(fd < 0) + fatal("open %s", file); + breadfrom(b, fd); + close(fd); +} + +// writefile writes b to the named file, creating it if needed. +void +writefile(Buf *b, char *file, int exec) +{ + int fd; + Dir d; + + fd = create(file, ORDWR, 0666); + if(fd < 0) + fatal("create %s", file); + if(write(fd, b->p, b->len) != b->len) + fatal("short write"); + if(exec) { + nulldir(&d); + d.mode = 0755; + dirfwstat(fd, &d); + } + close(fd); +} + +// xmkdir creates the directory p. +void +xmkdir(char *p) +{ + int fd; + + if(isdir(p)) + return; + fd = create(p, OREAD, 0777|DMDIR); + close(fd); + if(fd < 0) + fatal("mkdir %s", p); +} + +// xmkdirall creates the directory p and its parents, as needed. +void +xmkdirall(char *p) +{ + char *q; + + if(isdir(p)) + return; + q = strrchr(p, '/'); + if(q != nil) { + *q = '\0'; + xmkdirall(p); + *q = '/'; + } + xmkdir(p); +} + +// xremove removes the file p. +void +xremove(char *p) +{ + if(vflag > 2) + errprintf("rm %s\n", p); + remove(p); +} + +// xremoveall removes the file or directory tree rooted at p. +void +xremoveall(char *p) +{ + int i; + Buf b; + Vec dir; + + binit(&b); + vinit(&dir); + + if(isdir(p)) { + xreaddir(&dir, p); + for(i=0; i 2) + errprintf("rm %s\n", p); + remove(p); + + bfree(&b); + vfree(&dir); +} + +// xreaddir replaces dst with a list of the names of the files in dir. +// The names are relative to dir; they are not full paths. +void +xreaddir(Vec *dst, char *dir) +{ + Dir *d; + int fd, i, n; + + vreset(dst); + + fd = open(dir, OREAD); + if(fd < 0) + fatal("open %s", dir); + n = dirreadall(fd, &d); + for(i=0; i= 0) + goto done; + } + fatal("xworkdir create"); + +done: + close(fd); + p = btake(&b); + + bfree(&b); + return p; +} + +// fatal prints an error message to standard error and exits. +void +fatal(char *msg, ...) +{ + char buf[ERRMAX]; + va_list arg; + + rerrstr(buf, sizeof buf); + + fflush(stdout); + fprintf(stderr, "go tool dist: "); + va_start(arg, msg); + vfprintf(stderr, msg, arg); + va_end(arg); + + if(buf[0]) + fprintf(stderr, ": %s", buf); + fprintf(stderr, "\n"); + + bgwait(); + exits(msg); +} + +// xmalloc returns a newly allocated zeroed block of n bytes of memory. +// It calls fatal if it runs out of memory. +void* +xmalloc(int n) +{ + void *p; + + p = malloc(n); + if(p == nil) + fatal("out of memory"); + memset(p, 0, n); + return p; +} + +// xstrdup returns a newly allocated copy of p. +// It calls fatal if it runs out of memory. +char* +xstrdup(char *p) +{ + p = strdup(p); + if(p == nil) + fatal("out of memory"); + return p; +} + +// xrealloc grows the allocation p to n bytes and +// returns the new (possibly moved) pointer. +// It calls fatal if it runs out of memory. +void* +xrealloc(void *p, int n) +{ + p = realloc(p, n); + if(p == nil) + fatal("out of memory"); + return p; +} + +// xfree frees the result returned by xmalloc, xstrdup, or xrealloc. +void +xfree(void *p) +{ + free(p); +} + +// hassuffix reports whether p ends with suffix. +bool +hassuffix(char *p, char *suffix) +{ + int np, ns; + + np = strlen(p); + ns = strlen(suffix); + return np >= ns && strcmp(p+np-ns, suffix) == 0; +} + +// hasprefix reports whether p begins wtih prefix. +bool +hasprefix(char *p, char *prefix) +{ + return strncmp(p, prefix, strlen(prefix)) == 0; +} + +// contains reports whether sep appears in p. +bool +contains(char *p, char *sep) +{ + return strstr(p, sep) != nil; +} + +// streq reports whether p and q are the same string. +bool +streq(char *p, char *q) +{ + return strcmp(p, q) == 0; +} + +// lastelem returns the final path element in p. +char* +lastelem(char *p) +{ + char *out; + + out = p; + for(; *p; p++) + if(*p == '/') + out = p+1; + return out; +} + +// xmemmove copies n bytes from src to dst. +void +xmemmove(void *dst, void *src, int n) +{ + memmove(dst, src, n); +} + +// xmemcmp compares the n-byte regions starting at a and at b. +int +xmemcmp(void *a, void *b, int n) +{ + return memcmp(a, b, n); +} + +// xstrlen returns the length of the NUL-terminated string at p. +int +xstrlen(char *p) +{ + return strlen(p); +} + +// xexit exits the process with return code n. +void +xexit(int n) +{ + char buf[32]; + + snprintf(buf, sizeof buf, "%d", n); + exits(buf); +} + +// xatexit schedules the exit-handler f to be run when the program exits. +void +xatexit(void (*f)(void)) +{ + atexit(f); +} + +// xprintf prints a message to standard output. +void +xprintf(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vprintf(fmt, arg); + va_end(arg); +} + +// errprintf prints a message to standard output. +void +errprintf(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vfprintf(stderr, fmt, arg); + va_end(arg); +} + +// xsetenv sets the environment variable $name to the given value. +void +xsetenv(char *name, char *value) +{ + putenv(name, value); +} + +// main takes care of OS-specific startup and dispatches to xmain. +void +main(int argc, char **argv) +{ + Buf b; + + setvbuf(stdout, nil, _IOLBF, BUFSIZ); + setvbuf(stderr, nil, _IOLBF, BUFSIZ); + + binit(&b); + + rfork(RFENVG); + + slash = "/"; + gohostos = "plan9"; + + xgetenv(&b, "objtype"); + if(b.len == 0) + fatal("$objtype is unset"); + gohostarch = btake(&b); + + srand(time(0)+getpid()); + init(); + xmain(argc, argv); + + bfree(&b); + exits(nil); +} + +// xqsort is a wrapper for the C standard qsort. +void +xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*)) +{ + qsort(data, n, elemsize, cmp); +} + +// xstrcmp compares the NUL-terminated strings a and b. +int +xstrcmp(char *a, char *b) +{ + return strcmp(a, b); +} + +// xstrstr returns a pointer to the first occurrence of b in a. +char* +xstrstr(char *a, char *b) +{ + return strstr(a, b); +} + +// xstrrchr returns a pointer to the final occurrence of c in p. +char* +xstrrchr(char *p, int c) +{ + return strrchr(p, c); +} + +// xsamefile returns whether f1 and f2 are the same file (or dir) +int +xsamefile(char *f1, char *f2) +{ + return streq(f1, f2); // suffice for now +} + +// xtryexecfunc tries to execute function f, if any illegal instruction +// signal received in the course of executing that function, it will +// return 0, otherwise it will return 1. +int +xtryexecfunc(void (*f)(void)) +{ + USED(f); + return 0; // suffice for now +} + +bool +cansse2(void) +{ + // if we had access to cpuid, could answer this question + // less conservatively. + return 0; +} + +#endif // PLAN9 diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c index e6d82e14e..e4e2dcc9f 100644 --- a/src/cmd/dist/unix.c +++ b/src/cmd/dist/unix.c @@ -23,6 +23,7 @@ #include #include #include +#include // bprintf replaces the buffer with the result of the printf formatting // and returns a pointer to the NUL-terminated buffer contents. @@ -177,7 +178,7 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) bwritestr(&cmd, q); } if(vflag > 1) - xprintf("%s\n", bstr(&cmd)); + errprintf("%s\n", bstr(&cmd)); if(b != nil) { breset(b); @@ -398,7 +399,7 @@ void xremove(char *p) { if(vflag > 2) - xprintf("rm %s\n", p); + errprintf("rm %s\n", p); unlink(p); } @@ -420,11 +421,11 @@ xremoveall(char *p) xremoveall(bstr(&b)); } if(vflag > 2) - xprintf("rm %s\n", p); + errprintf("rm %s\n", p); rmdir(p); } else { if(vflag > 2) - xprintf("rm %s\n", p); + errprintf("rm %s\n", p); unlink(p); } @@ -627,6 +628,17 @@ xprintf(char *fmt, ...) va_end(arg); } +// errprintf prints a message to standard output. +void +errprintf(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vfprintf(stderr, fmt, arg); + va_end(arg); +} + // xsetenv sets the environment variable $name to the given value. void xsetenv(char *name, char *value) @@ -658,6 +670,10 @@ main(int argc, char **argv) gohostos = "linux"; #elif defined(__FreeBSD__) gohostos = "freebsd"; +#elif defined(__FreeBSD_kernel__) + // detect debian/kFreeBSD. + // http://wiki.debian.org/Debian_GNU/kFreeBSD_FAQ#Q._How_do_I_detect_kfreebsd_with_preprocessor_directives_in_a_C_program.3F + gohostos = "freebsd"; #elif defined(__OpenBSD__) gohostos = "openbsd"; #elif defined(__NetBSD__) @@ -716,5 +732,76 @@ xstrrchr(char *p, int c) return strrchr(p, c); } +// xsamefile returns whether f1 and f2 are the same file (or dir) +int +xsamefile(char *f1, char *f2) +{ + return streq(f1, f2); // suffice for now +} + +sigjmp_buf sigill_jmpbuf; +static void sigillhand(int); + +// xtryexecfunc tries to execute function f, if any illegal instruction +// signal received in the course of executing that function, it will +// return 0, otherwise it will return 1. +// Some systems (notably NetBSD) will spin and spin when executing VFPv3 +// instructions on VFPv2 system (e.g. Raspberry Pi) without ever triggering +// SIGILL, so we set a 1-second alarm to catch that case. +int +xtryexecfunc(void (*f)(void)) +{ + int r; + r = 0; + signal(SIGILL, sigillhand); + signal(SIGALRM, sigillhand); + alarm(1); + if(sigsetjmp(sigill_jmpbuf, 1) == 0) { + f(); + r = 1; + } + signal(SIGILL, SIG_DFL); + alarm(0); + signal(SIGALRM, SIG_DFL); + return r; +} + +// SIGILL handler helper +static void +sigillhand(int signum) +{ + USED(signum); + siglongjmp(sigill_jmpbuf, 1); +} + +static void +__cpuid(int dst[4], int ax) +{ +#ifdef __i386__ + // we need to avoid ebx on i386 (esp. when -fPIC). + asm volatile( + "mov %%ebx, %%edi\n\t" + "cpuid\n\t" + "xchgl %%ebx, %%edi" + : "=a" (dst[0]), "=D" (dst[1]), "=c" (dst[2]), "=d" (dst[3]) + : "0" (ax)); +#elif defined(__x86_64__) + asm volatile("cpuid" + : "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3]) + : "0" (ax)); +#else + dst[0] = dst[1] = dst[2] = dst[3] = 0; +#endif +} + +bool +cansse2(void) +{ + int info[4]; + + __cpuid(info, 1); + return (info[3] & (1<<26)) != 0; // SSE2 +} + #endif // PLAN9 #endif // __WINDOWS__ diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c index 557e4b003..7bcda4508 100644 --- a/src/cmd/dist/windows.c +++ b/src/cmd/dist/windows.c @@ -115,7 +115,7 @@ errstr(void) binit(&b); code = GetLastError(); r = nil; - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, nil, code, 0, (Rune*)&r, 0, nil); toutf(&b, r); return bstr(&b); // leak but we're dying anyway @@ -285,9 +285,11 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) binit(&cmd); for(i=0; ilen; i++) { + q = argv->p[i]; + if(i == 0 && streq(q, "hg")) + bwritestr(&cmd, "cmd.exe /c "); if(i > 0) bwritestr(&cmd, " "); - q = argv->p[i]; if(contains(q, " ") || contains(q, "\t") || contains(q, "\"") || contains(q, "\\\\") || hassuffix(q, "\\")) { bwritestr(&cmd, "\""); nslash = 0; @@ -314,7 +316,7 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) } } if(vflag > 1) - xprintf("%s\n", bstr(&cmd)); + errprintf("%s\n", bstr(&cmd)); torune(&rcmd, bstr(&cmd)); rexe = nil; @@ -528,8 +530,9 @@ readfile(Buf *b, char *file) HANDLE h; Rune *r; + breset(b); if(vflag > 2) - xprintf("read %s\n", file); + errprintf("read %s\n", file); torune(&r, file); h = CreateFileW(r, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); if(h == INVALID_HANDLE_VALUE) @@ -548,7 +551,7 @@ writefile(Buf *b, char *file, int exec) USED(exec); if(vflag > 2) - xprintf("write %s\n", file); + errprintf("write %s\n", file); torune(&r, file); h = CreateFileW(r, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0); if(h == INVALID_HANDLE_VALUE) @@ -707,7 +710,7 @@ fatal(char *msg, ...) vsnprintf(buf1, sizeof buf1, msg, arg); va_end(arg); - xprintf("go tool dist: %s\n", buf1); + errprintf("go tool dist: %s\n", buf1); bgwait(); ExitProcess(1); @@ -848,6 +851,23 @@ xprintf(char *fmt, ...) xfree(p); } +void +errprintf(char *fmt, ...) +{ + va_list arg; + char *p; + DWORD n, w; + + va_start(arg, fmt); + n = vsnprintf(NULL, 0, fmt, arg); + p = xmalloc(n+1); + vsnprintf(p, n+1, fmt, arg); + va_end(arg); + w = 0; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), p, n, &w, 0); + xfree(p); +} + int main(int argc, char **argv) { @@ -907,4 +927,75 @@ xstrrchr(char *p, int c) return nil; } +// xsamefile returns whether f1 and f2 are the same file (or dir) +int +xsamefile(char *f1, char *f2) +{ + Rune *ru; + HANDLE fd1, fd2; + BY_HANDLE_FILE_INFORMATION fi1, fi2; + int r; + + // trivial case + if(streq(f1, f2)) + return 1; + + torune(&ru, f1); + // refer to ../../pkg/os/stat_windows.go:/sameFile + fd1 = CreateFileW(ru, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + xfree(ru); + if(fd1 == INVALID_HANDLE_VALUE) + return 0; + torune(&ru, f2); + fd2 = CreateFileW(ru, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + xfree(ru); + if(fd2 == INVALID_HANDLE_VALUE) { + CloseHandle(fd1); + return 0; + } + r = GetFileInformationByHandle(fd1, &fi1) != 0 && GetFileInformationByHandle(fd2, &fi2) != 0; + CloseHandle(fd2); + CloseHandle(fd1); + if(r != 0 && + fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber && + fi1.nFileIndexHigh == fi2.nFileIndexHigh && + fi1.nFileIndexLow == fi2.nFileIndexLow) + return 1; + return 0; +} + +// xtryexecfunc tries to execute function f, if any illegal instruction +// signal received in the course of executing that function, it will +// return 0, otherwise it will return 1. +int +xtryexecfunc(void (*f)(void)) +{ + return 0; // suffice for now +} + +static void +cpuid(int dst[4], int ax) +{ + // NOTE: This asm statement is for mingw. + // If we ever support MSVC, use __cpuid(dst, ax) + // to use the built-in. +#if defined(__i386__) || defined(__x86_64__) + asm volatile("cpuid" + : "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3]) + : "0" (ax)); +#else + dst[0] = dst[1] = dst[2] = dst[3] = 0; +#endif +} + +bool +cansse2(void) +{ + int info[4]; + + cpuid(info, 1); + return (info[3] & (1<<26)) != 0; // SSE2 +} + + #endif // __WINDOWS__ diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go index a92e0fc06..5de3e08c5 100644 --- a/src/cmd/fix/doc.go +++ b/src/cmd/fix/doc.go @@ -33,4 +33,4 @@ Fix does not make backup copies of the files that it edits. Instead, use a version control system's ``diff'' functionality to inspect the changes that fix makes before committing them. */ -package documentation +package main diff --git a/src/cmd/fix/error.go b/src/cmd/fix/error.go deleted file mode 100644 index 55613210a..000000000 --- a/src/cmd/fix/error.go +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "regexp" - "strings" -) - -func init() { - register(errorFix) -} - -var errorFix = fix{ - "error", - "2011-11-02", - errorFn, - `Use error instead of os.Error. - -This fix rewrites code using os.Error to use error: - - os.Error -> error - os.NewError -> errors.New - os.EOF -> io.EOF - -Seeing the old names above (os.Error and so on) triggers the following -heuristic rewrites. The heuristics can be forced using the -force=error flag. - -A top-level function, variable, or constant named error is renamed error_. - -Error implementations—those types used as os.Error or named -XxxError—have their String methods renamed to Error. Any existing -Error field or method is renamed to Err. - -Error values—those with type os.Error or named e, err, error, err1, -and so on—have method calls and field references rewritten just -as the types do (String to Error, Error to Err). Also, a type assertion -of the form err.(*os.Waitmsg) becomes err.(*exec.ExitError). - -http://codereview.appspot.com/5305066 -`, -} - -// At minimum, this fix applies the following rewrites: -// -// os.Error -> error -// os.NewError -> errors.New -// os.EOF -> io.EOF -// -// However, if can apply any of those rewrites, it assumes that the -// file predates the error type and tries to update the code to use -// the new definition for error - an Error method, not a String method. -// This more heuristic procedure may not be 100% accurate, so it is -// only run when the file needs updating anyway. The heuristic can -// be forced to run using -force=error. -// -// First, we must identify the implementations of os.Error. -// These include the type of any value returned as or assigned to an os.Error. -// To that set we add any type whose name contains "Error" or "error". -// The heuristic helps for implementations that are not used as os.Error -// in the file in which they are defined. -// -// In any implementation of os.Error, we rename an existing struct field -// or method named Error to Err and rename the String method to Error. -// -// Second, we must identify the values of type os.Error. -// These include any value that obviously has type os.Error. -// To that set we add any variable whose name is e or err or error -// possibly followed by _ or a numeric or capitalized suffix. -// The heuristic helps for variables that are initialized using calls -// to functions in other packages. The type checker does not have -// information about those packages available, and in general cannot -// (because the packages may themselves not compile). -// -// For any value of type os.Error, we replace a call to String with a call to Error. -// We also replace type assertion err.(*os.Waitmsg) with err.(*exec.ExitError). - -// Variables matching this regexp are assumed to have type os.Error. -var errVar = regexp.MustCompile(`^(e|err|error)_?([A-Z0-9].*)?$`) - -// Types matching this regexp are assumed to be implementations of os.Error. -var errType = regexp.MustCompile(`^\*?([Ee]rror|.*Error)$`) - -// Type-checking configuration: tell the type-checker this basic -// information about types, functions, and variables in external packages. -var errorTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "os.Error": {}, - }, - Func: map[string]string{ - "fmt.Errorf": "os.Error", - "os.NewError": "os.Error", - }, - Var: map[string]string{ - "os.EPERM": "os.Error", - "os.ENOENT": "os.Error", - "os.ESRCH": "os.Error", - "os.EINTR": "os.Error", - "os.EIO": "os.Error", - "os.ENXIO": "os.Error", - "os.E2BIG": "os.Error", - "os.ENOEXEC": "os.Error", - "os.EBADF": "os.Error", - "os.ECHILD": "os.Error", - "os.EDEADLK": "os.Error", - "os.ENOMEM": "os.Error", - "os.EACCES": "os.Error", - "os.EFAULT": "os.Error", - "os.EBUSY": "os.Error", - "os.EEXIST": "os.Error", - "os.EXDEV": "os.Error", - "os.ENODEV": "os.Error", - "os.ENOTDIR": "os.Error", - "os.EISDIR": "os.Error", - "os.EINVAL": "os.Error", - "os.ENFILE": "os.Error", - "os.EMFILE": "os.Error", - "os.ENOTTY": "os.Error", - "os.EFBIG": "os.Error", - "os.ENOSPC": "os.Error", - "os.ESPIPE": "os.Error", - "os.EROFS": "os.Error", - "os.EMLINK": "os.Error", - "os.EPIPE": "os.Error", - "os.EAGAIN": "os.Error", - "os.EDOM": "os.Error", - "os.ERANGE": "os.Error", - "os.EADDRINUSE": "os.Error", - "os.ECONNREFUSED": "os.Error", - "os.ENAMETOOLONG": "os.Error", - "os.EAFNOSUPPORT": "os.Error", - "os.ETIMEDOUT": "os.Error", - "os.ENOTCONN": "os.Error", - }, -} - -func errorFn(f *ast.File) bool { - if !imports(f, "os") && !force["error"] { - return false - } - - // Fix gets called once to run the heuristics described above - // when we notice that this file definitely needs fixing - // (it mentions os.Error or something similar). - var fixed bool - var didHeuristic bool - heuristic := func() { - if didHeuristic { - return - } - didHeuristic = true - - // We have identified a necessary fix (like os.Error -> error) - // but have not applied it or any others yet. Prepare the file - // for fixing and apply heuristic fixes. - - // Rename error to error_ to make room for error. - fixed = renameTop(f, "error", "error_") || fixed - - // Use type checker to build list of error implementations. - typeof, assign := typecheck(errorTypeConfig, f) - - isError := map[string]bool{} - for _, val := range assign["os.Error"] { - t := typeof[val] - if strings.HasPrefix(t, "*") { - t = t[1:] - } - if t != "" && !strings.HasPrefix(t, "func(") { - isError[t] = true - } - } - - // We use both the type check results and the "Error" name heuristic - // to identify implementations of os.Error. - isErrorImpl := func(typ string) bool { - return isError[typ] || errType.MatchString(typ) - } - - isErrorVar := func(x ast.Expr) bool { - if typ := typeof[x]; typ != "" { - return isErrorImpl(typ) || typ == "os.Error" - } - if sel, ok := x.(*ast.SelectorExpr); ok { - return sel.Sel.Name == "Error" || sel.Sel.Name == "Err" - } - if id, ok := x.(*ast.Ident); ok { - return errVar.MatchString(id.Name) - } - return false - } - - walk(f, func(n interface{}) { - // In method declaration on error implementation type, - // rename String() to Error() and Error() to Err(). - fn, ok := n.(*ast.FuncDecl) - if ok && - fn.Recv != nil && - len(fn.Recv.List) == 1 && - isErrorImpl(typeName(fn.Recv.List[0].Type)) { - // Rename. - switch fn.Name.Name { - case "String": - fn.Name.Name = "Error" - fixed = true - case "Error": - fn.Name.Name = "Err" - fixed = true - } - return - } - - // In type definition of an error implementation type, - // rename Error field to Err to make room for method. - // Given type XxxError struct { ... Error T } rename field to Err. - d, ok := n.(*ast.GenDecl) - if ok { - for _, s := range d.Specs { - switch s := s.(type) { - case *ast.TypeSpec: - if isErrorImpl(typeName(s.Name)) { - st, ok := s.Type.(*ast.StructType) - if ok { - for _, f := range st.Fields.List { - for _, n := range f.Names { - if n.Name == "Error" { - n.Name = "Err" - fixed = true - } - } - } - } - } - } - } - } - - // For values that are an error implementation type, - // rename .Error to .Err and .String to .Error - sel, selok := n.(*ast.SelectorExpr) - if selok && isErrorImpl(typeof[sel.X]) { - switch sel.Sel.Name { - case "Error": - sel.Sel.Name = "Err" - fixed = true - case "String": - sel.Sel.Name = "Error" - fixed = true - } - } - - // Assume x.Err is an error value and rename .String to .Error - // Children have been processed so the rewrite from Error to Err - // has already happened there. - if selok { - if subsel, ok := sel.X.(*ast.SelectorExpr); ok && subsel.Sel.Name == "Err" && sel.Sel.Name == "String" { - sel.Sel.Name = "Error" - fixed = true - } - } - - // For values that are an error variable, rename .String to .Error. - if selok && isErrorVar(sel.X) && sel.Sel.Name == "String" { - sel.Sel.Name = "Error" - fixed = true - } - - // Rewrite composite literal of error type to turn Error: into Err:. - lit, ok := n.(*ast.CompositeLit) - if ok && isErrorImpl(typeof[lit]) { - for _, e := range lit.Elts { - if kv, ok := e.(*ast.KeyValueExpr); ok && isName(kv.Key, "Error") { - kv.Key.(*ast.Ident).Name = "Err" - fixed = true - } - } - } - - // Rename os.Waitmsg to exec.ExitError - // when used in a type assertion on an error. - ta, ok := n.(*ast.TypeAssertExpr) - if ok && isErrorVar(ta.X) && isPtrPkgDot(ta.Type, "os", "Waitmsg") { - addImport(f, "exec") - sel := ta.Type.(*ast.StarExpr).X.(*ast.SelectorExpr) - sel.X.(*ast.Ident).Name = "exec" - sel.Sel.Name = "ExitError" - fixed = true - } - - }) - } - - fix := func() { - if fixed { - return - } - fixed = true - heuristic() - } - - if force["error"] { - heuristic() - } - - walk(f, func(n interface{}) { - p, ok := n.(*ast.Expr) - if !ok { - return - } - sel, ok := (*p).(*ast.SelectorExpr) - if !ok { - return - } - switch { - case isPkgDot(sel, "os", "Error"): - fix() - *p = &ast.Ident{NamePos: sel.Pos(), Name: "error"} - case isPkgDot(sel, "os", "NewError"): - fix() - addImport(f, "errors") - sel.X.(*ast.Ident).Name = "errors" - sel.Sel.Name = "New" - case isPkgDot(sel, "os", "EOF"): - fix() - addImport(f, "io") - sel.X.(*ast.Ident).Name = "io" - } - }) - - if fixed && !usesImport(f, "os") { - deleteImport(f, "os") - } - - return fixed -} - -func typeName(typ ast.Expr) string { - if p, ok := typ.(*ast.StarExpr); ok { - typ = p.X - } - id, ok := typ.(*ast.Ident) - if ok { - return id.Name - } - sel, ok := typ.(*ast.SelectorExpr) - if ok { - return typeName(sel.X) + "." + sel.Sel.Name - } - return "" -} diff --git a/src/cmd/fix/error_test.go b/src/cmd/fix/error_test.go deleted file mode 100644 index 027eed24f..000000000 --- a/src/cmd/fix/error_test.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(errorTests, errorFn) -} - -var errorTests = []testCase{ - { - Name: "error.0", - In: `package main - -func error() {} - -var error int -`, - Out: `package main - -func error() {} - -var error int -`, - }, - { - Name: "error.1", - In: `package main - -import "os" - -func f() os.Error { - return os.EOF -} - -func error() {} - -var error int - -func g() { - error := 1 - _ = error -} - -func h(os.Error) {} - -func i(...os.Error) {} -`, - Out: `package main - -import "io" - -func f() error { - return io.EOF -} - -func error_() {} - -var error_ int - -func g() { - error := 1 - _ = error -} - -func h(error) {} - -func i(...error) {} -`, - }, - { - Name: "error.2", - In: `package main - -import "os" - -func f() os.Error { - return os.EOF -} - -func g() string { - // these all convert because f is known - if err := f(); err != nil { - return err.String() - } - if err1 := f(); err1 != nil { - return err1.String() - } - if e := f(); e != nil { - return e.String() - } - if x := f(); x != nil { - return x.String() - } - - // only the error names (err, err1, e) convert; u is not known - if err := u(); err != nil { - return err.String() - } - if err1 := u(); err1 != nil { - return err1.String() - } - if e := u(); e != nil { - return e.String() - } - if x := u(); x != nil { - return x.String() - } - return "" -} - -type T int - -func (t T) String() string { return "t" } - -type PT int - -func (p *PT) String() string { return "pt" } - -type MyError int - -func (t MyError) String() string { return "myerror" } - -type PMyError int - -func (p *PMyError) String() string { return "pmyerror" } - -func error() {} - -var error int -`, - Out: `package main - -import "io" - -func f() error { - return io.EOF -} - -func g() string { - // these all convert because f is known - if err := f(); err != nil { - return err.Error() - } - if err1 := f(); err1 != nil { - return err1.Error() - } - if e := f(); e != nil { - return e.Error() - } - if x := f(); x != nil { - return x.Error() - } - - // only the error names (err, err1, e) convert; u is not known - if err := u(); err != nil { - return err.Error() - } - if err1 := u(); err1 != nil { - return err1.Error() - } - if e := u(); e != nil { - return e.Error() - } - if x := u(); x != nil { - return x.String() - } - return "" -} - -type T int - -func (t T) String() string { return "t" } - -type PT int - -func (p *PT) String() string { return "pt" } - -type MyError int - -func (t MyError) Error() string { return "myerror" } - -type PMyError int - -func (p *PMyError) Error() string { return "pmyerror" } - -func error_() {} - -var error_ int -`, - }, - { - Name: "error.3", - In: `package main - -import "os" - -func f() os.Error { - return os.EOF -} - -type PathError struct { - Name string - Error os.Error -} - -func (p *PathError) String() string { - return p.Name + ": " + p.Error.String() -} - -func (p *PathError) Error1() string { - p = &PathError{Error: nil} - return fmt.Sprint(p.Name, ": ", p.Error) -} -`, - Out: `package main - -import "io" - -func f() error { - return io.EOF -} - -type PathError struct { - Name string - Err error -} - -func (p *PathError) Error() string { - return p.Name + ": " + p.Err.Error() -} - -func (p *PathError) Error1() string { - p = &PathError{Err: nil} - return fmt.Sprint(p.Name, ": ", p.Err) -} -`, - }, -} diff --git a/src/cmd/fix/filepath.go b/src/cmd/fix/filepath.go deleted file mode 100644 index f31226018..000000000 --- a/src/cmd/fix/filepath.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(filepathFix) -} - -var filepathFix = fix{ - "filepath", - "2011-06-26", - filepathFunc, - `Adapt code from filepath.[List]SeparatorString to string(filepath.[List]Separator). - -http://codereview.appspot.com/4527090 -`, -} - -func filepathFunc(f *ast.File) (fixed bool) { - if !imports(f, "path/filepath") { - return - } - - walk(f, func(n interface{}) { - e, ok := n.(*ast.Expr) - if !ok { - return - } - - var ident string - switch { - case isPkgDot(*e, "filepath", "SeparatorString"): - ident = "filepath.Separator" - case isPkgDot(*e, "filepath", "ListSeparatorString"): - ident = "filepath.ListSeparator" - default: - return - } - - // string(filepath.[List]Separator) - *e = &ast.CallExpr{ - Fun: ast.NewIdent("string"), - Args: []ast.Expr{ast.NewIdent(ident)}, - } - - fixed = true - }) - - return -} diff --git a/src/cmd/fix/filepath_test.go b/src/cmd/fix/filepath_test.go deleted file mode 100644 index 37a2f5d9f..000000000 --- a/src/cmd/fix/filepath_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(filepathTests, filepathFunc) -} - -var filepathTests = []testCase{ - { - Name: "filepath.0", - In: `package main - -import ( - "path/filepath" -) - -var _ = filepath.SeparatorString -var _ = filepath.ListSeparatorString -`, - Out: `package main - -import ( - "path/filepath" -) - -var _ = string(filepath.Separator) -var _ = string(filepath.ListSeparator) -`, - }, -} diff --git a/src/cmd/fix/go1pkgrename.go b/src/cmd/fix/go1pkgrename.go deleted file mode 100644 index f701f62f0..000000000 --- a/src/cmd/fix/go1pkgrename.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "strings" -) - -func init() { - register(go1pkgrenameFix) -} - -var go1pkgrenameFix = fix{ - "go1rename", - "2011-11-08", - go1pkgrename, - `Rewrite imports for packages moved during transition to Go 1. - -http://codereview.appspot.com/5316078 -`, -} - -var go1PackageRenames = []struct{ old, new string }{ - {"asn1", "encoding/asn1"}, - {"big", "math/big"}, - {"cmath", "math/cmplx"}, - {"csv", "encoding/csv"}, - {"exec", "os/exec"}, - {"exp/template/html", "html/template"}, - {"gob", "encoding/gob"}, - {"http", "net/http"}, - {"http/cgi", "net/http/cgi"}, - {"http/fcgi", "net/http/fcgi"}, - {"http/httptest", "net/http/httptest"}, - {"http/pprof", "net/http/pprof"}, - {"json", "encoding/json"}, - {"mail", "net/mail"}, - {"rpc", "net/rpc"}, - {"rpc/jsonrpc", "net/rpc/jsonrpc"}, - {"scanner", "text/scanner"}, - {"smtp", "net/smtp"}, - {"syslog", "log/syslog"}, - {"tabwriter", "text/tabwriter"}, - {"template", "text/template"}, - {"template/parse", "text/template/parse"}, - {"rand", "math/rand"}, - {"url", "net/url"}, - {"utf16", "unicode/utf16"}, - {"utf8", "unicode/utf8"}, - {"xml", "encoding/xml"}, - - // go.crypto sub-repository - {"crypto/bcrypt", "code.google.com/p/go.crypto/bcrypt"}, - {"crypto/blowfish", "code.google.com/p/go.crypto/blowfish"}, - {"crypto/cast5", "code.google.com/p/go.crypto/cast5"}, - {"crypto/md4", "code.google.com/p/go.crypto/md4"}, - {"crypto/ocsp", "code.google.com/p/go.crypto/ocsp"}, - {"crypto/openpgp", "code.google.com/p/go.crypto/openpgp"}, - {"crypto/openpgp/armor", "code.google.com/p/go.crypto/openpgp/armor"}, - {"crypto/openpgp/elgamal", "code.google.com/p/go.crypto/openpgp/elgamal"}, - {"crypto/openpgp/errors", "code.google.com/p/go.crypto/openpgp/errors"}, - {"crypto/openpgp/packet", "code.google.com/p/go.crypto/openpgp/packet"}, - {"crypto/openpgp/s2k", "code.google.com/p/go.crypto/openpgp/s2k"}, - {"crypto/ripemd160", "code.google.com/p/go.crypto/ripemd160"}, - {"crypto/twofish", "code.google.com/p/go.crypto/twofish"}, - {"crypto/xtea", "code.google.com/p/go.crypto/xtea"}, - {"exp/ssh", "code.google.com/p/go.crypto/ssh"}, - - // go.image sub-repository - {"image/bmp", "code.google.com/p/go.image/bmp"}, - {"image/tiff", "code.google.com/p/go.image/tiff"}, - - // go.net sub-repository - {"net/dict", "code.google.com/p/go.net/dict"}, - {"net/websocket", "code.google.com/p/go.net/websocket"}, - {"exp/spdy", "code.google.com/p/go.net/spdy"}, - {"http/spdy", "code.google.com/p/go.net/spdy"}, - - // go.codereview sub-repository - {"encoding/git85", "code.google.com/p/go.codereview/git85"}, - {"patch", "code.google.com/p/go.codereview/patch"}, - - // exp - {"ebnf", "exp/ebnf"}, - {"go/types", "exp/types"}, - - // deleted - {"container/vector", ""}, - {"exp/datafmt", ""}, - {"go/typechecker", ""}, - {"old/netchan", ""}, - {"old/regexp", ""}, - {"old/template", ""}, - {"try", ""}, -} - -var go1PackageNameRenames = []struct{ newPath, old, new string }{ - {"html/template", "html", "template"}, - {"math/cmplx", "cmath", "cmplx"}, -} - -func go1pkgrename(f *ast.File) bool { - fixed := false - - // First update the imports. - for _, rename := range go1PackageRenames { - spec := importSpec(f, rename.old) - if spec == nil { - continue - } - if rename.new == "" { - warn(spec.Pos(), "package %q has been deleted in Go 1", rename.old) - continue - } - if rewriteImport(f, rename.old, rename.new) { - fixed = true - } - if strings.HasPrefix(rename.new, "exp/") { - warn(spec.Pos(), "package %q is not part of Go 1", rename.new) - } - } - if !fixed { - return false - } - - // Now update the package names used by importers. - for _, rename := range go1PackageNameRenames { - // These are rare packages, so do the import test before walking. - if imports(f, rename.newPath) { - walk(f, func(n interface{}) { - if sel, ok := n.(*ast.SelectorExpr); ok { - if isTopName(sel.X, rename.old) { - // We know Sel.X is an Ident. - sel.X.(*ast.Ident).Name = rename.new - return - } - } - }) - } - } - - return fixed -} diff --git a/src/cmd/fix/go1pkgrename_test.go b/src/cmd/fix/go1pkgrename_test.go deleted file mode 100644 index 840e443b0..000000000 --- a/src/cmd/fix/go1pkgrename_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(go1pkgrenameTests, go1pkgrename) -} - -var go1pkgrenameTests = []testCase{ - { - Name: "go1rename.0", - In: `package main - -import ( - "asn1" - "big" - "cmath" - "csv" - "exec" - "exp/template/html" - "gob" - "http" - "http/cgi" - "http/fcgi" - "http/httptest" - "http/pprof" - "json" - "mail" - "rand" - "rpc" - "rpc/jsonrpc" - "scanner" - "smtp" - "syslog" - "tabwriter" - "template" - "template/parse" - "url" - "utf16" - "utf8" - "xml" - - "crypto/bcrypt" -) -`, - Out: `package main - -import ( - "encoding/asn1" - "encoding/csv" - "encoding/gob" - "encoding/json" - "encoding/xml" - "html/template" - "log/syslog" - "math/big" - "math/cmplx" - "math/rand" - "net/http" - "net/http/cgi" - "net/http/fcgi" - "net/http/httptest" - "net/http/pprof" - "net/mail" - "net/rpc" - "net/rpc/jsonrpc" - "net/smtp" - "net/url" - "os/exec" - "text/scanner" - "text/tabwriter" - "text/template" - "text/template/parse" - "unicode/utf16" - "unicode/utf8" - - "code.google.com/p/go.crypto/bcrypt" -) -`, - }, - { - Name: "go1rename.1", - In: `package main - -import "cmath" -import poot "exp/template/html" - -import ( - "ebnf" - "old/regexp" -) - -var _ = cmath.Sin -var _ = poot.Poot -`, - Out: `package main - -import "math/cmplx" -import poot "html/template" - -import ( - "exp/ebnf" - "old/regexp" -) - -var _ = cmplx.Sin -var _ = poot.Poot -`, - }, - { - Name: "go1rename.2", - In: `package foo - -import ( - "fmt" - "http" - "url" - - "google/secret/project/go" -) - -func main() {} -`, - Out: `package foo - -import ( - "fmt" - "net/http" - "net/url" - - "google/secret/project/go" -) - -func main() {} -`, - }, -} diff --git a/src/cmd/fix/go1rename.go b/src/cmd/fix/go1rename.go deleted file mode 100644 index 9266c749c..000000000 --- a/src/cmd/fix/go1rename.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - register(go1renameFix) -} - -var go1renameFix = fix{ - "go1rename", - "2012-02-12", - renameFix(go1renameReplace), - `Rewrite package-level names that have been renamed in Go 1. - -http://codereview.appspot.com/5625045/ -http://codereview.appspot.com/5672072/ -`, -} - -var go1renameReplace = []rename{ - { - OldImport: "crypto/aes", - NewImport: "crypto/cipher", - Old: "*aes.Cipher", - New: "cipher.Block", - }, - { - OldImport: "crypto/des", - NewImport: "crypto/cipher", - Old: "*des.Cipher", - New: "cipher.Block", - }, - { - OldImport: "crypto/des", - NewImport: "crypto/cipher", - Old: "*des.TripleDESCipher", - New: "cipher.Block", - }, - { - OldImport: "encoding/json", - NewImport: "", - Old: "json.MarshalForHTML", - New: "json.Marshal", - }, - { - OldImport: "net/url", - NewImport: "", - Old: "url.ParseWithReference", - New: "url.Parse", - }, - { - OldImport: "net/url", - NewImport: "", - Old: "url.ParseRequest", - New: "url.ParseRequestURI", - }, - { - OldImport: "os", - NewImport: "syscall", - Old: "os.Exec", - New: "syscall.Exec", - }, - { - OldImport: "runtime", - NewImport: "", - Old: "runtime.Cgocalls", - New: "runtime.NumCgoCall", - }, - { - OldImport: "runtime", - NewImport: "", - Old: "runtime.Goroutines", - New: "runtime.NumGoroutine", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ErrPersistEOF", - New: "httputil.ErrPersistEOF", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ErrPipeline", - New: "httputil.ErrPipeline", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ErrClosed", - New: "httputil.ErrClosed", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ServerConn", - New: "httputil.ServerConn", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ClientConn", - New: "httputil.ClientConn", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewChunkedReader", - New: "httputil.NewChunkedReader", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewChunkedWriter", - New: "httputil.NewChunkedWriter", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ReverseProxy", - New: "httputil.ReverseProxy", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewSingleHostReverseProxy", - New: "httputil.NewSingleHostReverseProxy", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.DumpRequest", - New: "httputil.DumpRequest", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.DumpRequestOut", - New: "httputil.DumpRequestOut", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.DumpResponse", - New: "httputil.DumpResponse", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewClientConn", - New: "httputil.NewClientConn", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewServerConn", - New: "httputil.NewServerConn", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewProxyClientConn", - New: "httputil.NewProxyClientConn", - }, -} diff --git a/src/cmd/fix/go1rename_test.go b/src/cmd/fix/go1rename_test.go deleted file mode 100644 index 90219ba71..000000000 --- a/src/cmd/fix/go1rename_test.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(go1renameTests, go1renameFix.f) -} - -var go1renameTests = []testCase{ - { - Name: "go1rename.0", - In: `package main - -import ( - "crypto/aes" - "crypto/des" - "encoding/json" - "net/http" - "net/url" - "os" - "runtime" -) - -var ( - _ *aes.Cipher - _ *des.Cipher - _ *des.TripleDESCipher - _ = json.MarshalForHTML - _ = aes.New() - _ = url.Parse - _ = url.ParseWithReference - _ = url.ParseRequest - _ = os.Exec - _ = runtime.Cgocalls - _ = runtime.Goroutines - _ = http.ErrPersistEOF - _ = http.ErrPipeline - _ = http.ErrClosed - _ = http.NewSingleHostReverseProxy - _ = http.NewChunkedReader - _ = http.NewChunkedWriter - _ *http.ReverseProxy - _ *http.ClientConn - _ *http.ServerConn -) -`, - Out: `package main - -import ( - "crypto/aes" - "crypto/cipher" - "encoding/json" - "net/http/httputil" - "net/url" - "runtime" - "syscall" -) - -var ( - _ cipher.Block - _ cipher.Block - _ cipher.Block - _ = json.Marshal - _ = aes.New() - _ = url.Parse - _ = url.Parse - _ = url.ParseRequestURI - _ = syscall.Exec - _ = runtime.NumCgoCall - _ = runtime.NumGoroutine - _ = httputil.ErrPersistEOF - _ = httputil.ErrPipeline - _ = httputil.ErrClosed - _ = httputil.NewSingleHostReverseProxy - _ = httputil.NewChunkedReader - _ = httputil.NewChunkedWriter - _ *httputil.ReverseProxy - _ *httputil.ClientConn - _ *httputil.ServerConn -) -`, - }, - { - Name: "httputil.0", - In: `package main - -import "net/http" - -func f() { - http.DumpRequest(nil, false) - http.DumpRequestOut(nil, false) - http.DumpResponse(nil, false) - http.NewChunkedReader(nil) - http.NewChunkedWriter(nil) - http.NewClientConn(nil, nil) - http.NewProxyClientConn(nil, nil) - http.NewServerConn(nil, nil) - http.NewSingleHostReverseProxy(nil) -} -`, - Out: `package main - -import "net/http/httputil" - -func f() { - httputil.DumpRequest(nil, false) - httputil.DumpRequestOut(nil, false) - httputil.DumpResponse(nil, false) - httputil.NewChunkedReader(nil) - httputil.NewChunkedWriter(nil) - httputil.NewClientConn(nil, nil) - httputil.NewProxyClientConn(nil, nil) - httputil.NewServerConn(nil, nil) - httputil.NewSingleHostReverseProxy(nil) -} -`, - }, - { - Name: "httputil.1", - In: `package main - -import "net/http" - -func f() { - http.DumpRequest(nil, false) - http.DumpRequestOut(nil, false) - http.DumpResponse(nil, false) - http.NewChunkedReader(nil) - http.NewChunkedWriter(nil) - http.NewClientConn(nil, nil) - http.NewProxyClientConn(nil, nil) - http.NewServerConn(nil, nil) - http.NewSingleHostReverseProxy(nil) -} -`, - Out: `package main - -import "net/http/httputil" - -func f() { - httputil.DumpRequest(nil, false) - httputil.DumpRequestOut(nil, false) - httputil.DumpResponse(nil, false) - httputil.NewChunkedReader(nil) - httputil.NewChunkedWriter(nil) - httputil.NewClientConn(nil, nil) - httputil.NewProxyClientConn(nil, nil) - httputil.NewServerConn(nil, nil) - httputil.NewSingleHostReverseProxy(nil) -} -`, - }, - { - Name: "httputil.2", - In: `package main - -import "net/http" - -func f() { - http.DumpRequest(nil, false) - http.DumpRequestOut(nil, false) - http.DumpResponse(nil, false) - http.NewChunkedReader(nil) - http.NewChunkedWriter(nil) - http.NewClientConn(nil, nil) - http.NewProxyClientConn(nil, nil) - http.NewServerConn(nil, nil) - http.NewSingleHostReverseProxy(nil) - http.Get("") -} -`, - Out: `package main - -import ( - "net/http" - "net/http/httputil" -) - -func f() { - httputil.DumpRequest(nil, false) - httputil.DumpRequestOut(nil, false) - httputil.DumpResponse(nil, false) - httputil.NewChunkedReader(nil) - httputil.NewChunkedWriter(nil) - httputil.NewClientConn(nil, nil) - httputil.NewProxyClientConn(nil, nil) - httputil.NewServerConn(nil, nil) - httputil.NewSingleHostReverseProxy(nil) - http.Get("") -} -`, - }, -} diff --git a/src/cmd/fix/googlecode.go b/src/cmd/fix/googlecode.go deleted file mode 100644 index 143781a74..000000000 --- a/src/cmd/fix/googlecode.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "regexp" -) - -func init() { - register(googlecodeFix) -} - -var googlecodeFix = fix{ - "googlecode", - "2011-11-21", - googlecode, - `Rewrite Google Code imports from the deprecated form -"foo.googlecode.com/vcs/path" to "code.google.com/p/foo/path". -`, -} - -var googlecodeRe = regexp.MustCompile(`^([a-z0-9\-]+)\.googlecode\.com/(svn|git|hg)(/[a-z0-9A-Z_.\-/]+)?$`) - -func googlecode(f *ast.File) bool { - fixed := false - - for _, s := range f.Imports { - old := importPath(s) - if m := googlecodeRe.FindStringSubmatch(old); m != nil { - new := "code.google.com/p/" + m[1] + m[3] - if rewriteImport(f, old, new) { - fixed = true - } - } - } - - return fixed -} diff --git a/src/cmd/fix/googlecode_test.go b/src/cmd/fix/googlecode_test.go deleted file mode 100644 index c62ee4f32..000000000 --- a/src/cmd/fix/googlecode_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(googlecodeTests, googlecode) -} - -var googlecodeTests = []testCase{ - { - Name: "googlecode.0", - In: `package main - -import ( - "foo.googlecode.com/hg/bar" - "go-qux-23.googlecode.com/svn" - "zap.googlecode.com/git/some/path" -) -`, - Out: `package main - -import ( - "code.google.com/p/foo/bar" - "code.google.com/p/go-qux-23" - "code.google.com/p/zap/some/path" -) -`, - }, -} diff --git a/src/cmd/fix/hashsum.go b/src/cmd/fix/hashsum.go deleted file mode 100644 index 0df6ad749..000000000 --- a/src/cmd/fix/hashsum.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(hashSumFix) -} - -var hashSumFix = fix{ - "hashsum", - "2011-11-30", - hashSumFn, - `Pass a nil argument to calls to hash.Sum - -This fix rewrites code so that it passes a nil argument to hash.Sum. -The additional argument will allow callers to avoid an -allocation in the future. - -http://codereview.appspot.com/5448065 -`, -} - -// Type-checking configuration: tell the type-checker this basic -// information about types, functions, and variables in external packages. -var hashSumTypeConfig = &TypeConfig{ - Var: map[string]string{ - "crypto.MD4": "crypto.Hash", - "crypto.MD5": "crypto.Hash", - "crypto.SHA1": "crypto.Hash", - "crypto.SHA224": "crypto.Hash", - "crypto.SHA256": "crypto.Hash", - "crypto.SHA384": "crypto.Hash", - "crypto.SHA512": "crypto.Hash", - "crypto.MD5SHA1": "crypto.Hash", - "crypto.RIPEMD160": "crypto.Hash", - }, - - Func: map[string]string{ - "adler32.New": "hash.Hash", - "crc32.New": "hash.Hash", - "crc32.NewIEEE": "hash.Hash", - "crc64.New": "hash.Hash", - "fnv.New32a": "hash.Hash", - "fnv.New32": "hash.Hash", - "fnv.New64a": "hash.Hash", - "fnv.New64": "hash.Hash", - "hmac.New": "hash.Hash", - "hmac.NewMD5": "hash.Hash", - "hmac.NewSHA1": "hash.Hash", - "hmac.NewSHA256": "hash.Hash", - "md4.New": "hash.Hash", - "md5.New": "hash.Hash", - "ripemd160.New": "hash.Hash", - "sha1.New224": "hash.Hash", - "sha1.New": "hash.Hash", - "sha256.New224": "hash.Hash", - "sha256.New": "hash.Hash", - "sha512.New384": "hash.Hash", - "sha512.New": "hash.Hash", - }, - - Type: map[string]*Type{ - "crypto.Hash": { - Method: map[string]string{ - "New": "func() hash.Hash", - }, - }, - }, -} - -func hashSumFn(f *ast.File) bool { - typeof, _ := typecheck(hashSumTypeConfig, f) - - fixed := false - - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if ok && len(call.Args) == 0 { - sel, ok := call.Fun.(*ast.SelectorExpr) - if ok && sel.Sel.Name == "Sum" && typeof[sel.X] == "hash.Hash" { - call.Args = append(call.Args, ast.NewIdent("nil")) - fixed = true - } - } - }) - - return fixed -} diff --git a/src/cmd/fix/hashsum_test.go b/src/cmd/fix/hashsum_test.go deleted file mode 100644 index 241af2020..000000000 --- a/src/cmd/fix/hashsum_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(hashSumTests, hashSumFn) -} - -var hashSumTests = []testCase{ - { - Name: "hashsum.0", - In: `package main - -import "crypto/sha256" - -func f() []byte { - h := sha256.New() - return h.Sum() -} -`, - Out: `package main - -import "crypto/sha256" - -func f() []byte { - h := sha256.New() - return h.Sum(nil) -} -`, - }, - - { - Name: "hashsum.1", - In: `package main - -func f(h hash.Hash) []byte { - return h.Sum() -} -`, - Out: `package main - -func f(h hash.Hash) []byte { - return h.Sum(nil) -} -`, - }, - - { - Name: "hashsum.0", - In: `package main - -import "crypto/sha256" - -func f() []byte { - h := sha256.New() - h.Write([]byte("foo")) - digest := h.Sum() -} -`, - Out: `package main - -import "crypto/sha256" - -func f() []byte { - h := sha256.New() - h.Write([]byte("foo")) - digest := h.Sum(nil) -} -`, - }, - - { - Name: "hashsum.0", - In: `package main - -import _ "crypto/sha256" -import "crypto" - -func f() []byte { - hashType := crypto.SHA256 - h := hashType.New() - digest := h.Sum() -} -`, - Out: `package main - -import _ "crypto/sha256" -import "crypto" - -func f() []byte { - hashType := crypto.SHA256 - h := hashType.New() - digest := h.Sum(nil) -} -`, - }, -} diff --git a/src/cmd/fix/hmacnew.go b/src/cmd/fix/hmacnew.go deleted file mode 100644 index c0c44ef3e..000000000 --- a/src/cmd/fix/hmacnew.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(hmacNewFix) -} - -var hmacNewFix = fix{ - "hmacnew", - "2012-01-19", - hmacnew, - `Deprecate hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256. - -This fix rewrites code using hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256 to -use hmac.New: - - hmac.NewMD5(key) -> hmac.New(md5.New, key) - hmac.NewSHA1(key) -> hmac.New(sha1.New, key) - hmac.NewSHA256(key) -> hmac.New(sha256.New, key) - -`, -} - -func hmacnew(f *ast.File) (fixed bool) { - if !imports(f, "crypto/hmac") { - return - } - - walk(f, func(n interface{}) { - ce, ok := n.(*ast.CallExpr) - if !ok { - return - } - - var pkg string - switch { - case isPkgDot(ce.Fun, "hmac", "NewMD5"): - pkg = "md5" - case isPkgDot(ce.Fun, "hmac", "NewSHA1"): - pkg = "sha1" - case isPkgDot(ce.Fun, "hmac", "NewSHA256"): - pkg = "sha256" - default: - return - } - - addImport(f, "crypto/"+pkg) - - ce.Fun = ast.NewIdent("hmac.New") - ce.Args = append([]ast.Expr{ast.NewIdent(pkg + ".New")}, ce.Args...) - - fixed = true - }) - - return -} diff --git a/src/cmd/fix/hmacnew_test.go b/src/cmd/fix/hmacnew_test.go deleted file mode 100644 index 5aeee8573..000000000 --- a/src/cmd/fix/hmacnew_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(hmacNewTests, hmacnew) -} - -var hmacNewTests = []testCase{ - { - Name: "hmacnew.0", - In: `package main - -import "crypto/hmac" - -var f = hmac.NewSHA1([]byte("some key")) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/sha1" -) - -var f = hmac.New(sha1.New, []byte("some key")) -`, - }, - { - Name: "hmacnew.1", - In: `package main - -import "crypto/hmac" - -var key = make([]byte, 8) -var f = hmac.NewSHA1(key) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/sha1" -) - -var key = make([]byte, 8) -var f = hmac.New(sha1.New, key) -`, - }, - { - Name: "hmacnew.2", - In: `package main - -import "crypto/hmac" - -var f = hmac.NewMD5([]byte("some key")) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/md5" -) - -var f = hmac.New(md5.New, []byte("some key")) -`, - }, - { - Name: "hmacnew.3", - In: `package main - -import "crypto/hmac" - -var f = hmac.NewSHA256([]byte("some key")) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/sha256" -) - -var f = hmac.New(sha256.New, []byte("some key")) -`, - }, - { - Name: "hmacnew.4", - In: `package main - -import ( - "crypto/hmac" - "crypto/sha1" -) - -var f = hmac.New(sha1.New, []byte("some key")) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/sha1" -) - -var f = hmac.New(sha1.New, []byte("some key")) -`, - }, -} diff --git a/src/cmd/fix/htmlerr.go b/src/cmd/fix/htmlerr.go deleted file mode 100644 index b5105c822..000000000 --- a/src/cmd/fix/htmlerr.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(htmlerrFix) -} - -var htmlerrFix = fix{ - "htmlerr", - "2011-11-04", - htmlerr, - `Rename html's Tokenizer.Error method to Err. - -http://codereview.appspot.com/5327064/ -`, -} - -var htmlerrTypeConfig = &TypeConfig{ - Func: map[string]string{ - "html.NewTokenizer": "html.Tokenizer", - }, -} - -func htmlerr(f *ast.File) bool { - if !imports(f, "html") { - return false - } - - typeof, _ := typecheck(htmlerrTypeConfig, f) - - fixed := false - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - if ok && typeof[s.X] == "html.Tokenizer" && s.Sel.Name == "Error" { - s.Sel.Name = "Err" - fixed = true - } - }) - return fixed -} diff --git a/src/cmd/fix/htmlerr_test.go b/src/cmd/fix/htmlerr_test.go deleted file mode 100644 index 043abc42a..000000000 --- a/src/cmd/fix/htmlerr_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(htmlerrTests, htmlerr) -} - -var htmlerrTests = []testCase{ - { - Name: "htmlerr.0", - In: `package main - -import ( - "html" -) - -func f() { - e := errors.New("") - t := html.NewTokenizer(r) - _, _ = e.Error(), t.Error() -} -`, - Out: `package main - -import ( - "html" -) - -func f() { - e := errors.New("") - t := html.NewTokenizer(r) - _, _ = e.Error(), t.Err() -} -`, - }, -} diff --git a/src/cmd/fix/httpfinalurl.go b/src/cmd/fix/httpfinalurl.go deleted file mode 100644 index 49b9f1c51..000000000 --- a/src/cmd/fix/httpfinalurl.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(httpFinalURLFix) -} - -var httpFinalURLFix = fix{ - "httpfinalurl", - "2011-05-13", - httpfinalurl, - `Adapt http Get calls to not have a finalURL result parameter. - -http://codereview.appspot.com/4535056/ -`, -} - -func httpfinalurl(f *ast.File) bool { - if !imports(f, "http") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - // Fix up calls to http.Get. - // - // If they have blank identifiers, remove them: - // resp, _, err := http.Get(url) - // -> resp, err := http.Get(url) - // - // But if they're using the finalURL parameter, warn: - // resp, finalURL, err := http.Get(url) - as, ok := n.(*ast.AssignStmt) - if !ok || len(as.Lhs) != 3 || len(as.Rhs) != 1 { - return - } - - if !isCall(as.Rhs[0], "http", "Get") { - return - } - - if isBlank(as.Lhs[1]) { - as.Lhs = []ast.Expr{as.Lhs[0], as.Lhs[2]} - fixed = true - } else { - warn(as.Pos(), "call to http.Get records final URL") - } - }) - return fixed -} diff --git a/src/cmd/fix/httpfinalurl_test.go b/src/cmd/fix/httpfinalurl_test.go deleted file mode 100644 index 9249f7e18..000000000 --- a/src/cmd/fix/httpfinalurl_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(httpfinalurlTests, httpfinalurl) -} - -var httpfinalurlTests = []testCase{ - { - Name: "finalurl.0", - In: `package main - -import ( - "http" -) - -func f() { - resp, _, err := http.Get("http://www.google.com/") - _, _ = resp, err -} -`, - Out: `package main - -import ( - "http" -) - -func f() { - resp, err := http.Get("http://www.google.com/") - _, _ = resp, err -} -`, - }, -} diff --git a/src/cmd/fix/httpfs.go b/src/cmd/fix/httpfs.go deleted file mode 100644 index d87b30f9d..000000000 --- a/src/cmd/fix/httpfs.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register(httpFileSystemFix) -} - -var httpFileSystemFix = fix{ - "httpfs", - "2011-06-27", - httpfs, - `Adapt http FileServer to take a FileSystem. - -http://codereview.appspot.com/4629047 http FileSystem interface -`, -} - -func httpfs(f *ast.File) bool { - if !imports(f, "http") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || !isPkgDot(call.Fun, "http", "FileServer") { - return - } - if len(call.Args) != 2 { - return - } - dir, prefix := call.Args[0], call.Args[1] - call.Args = []ast.Expr{&ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent("http"), - Sel: ast.NewIdent("Dir"), - }, - Args: []ast.Expr{dir}, - }} - wrapInStripHandler := true - if prefixLit, ok := prefix.(*ast.BasicLit); ok { - if prefixLit.Kind == token.STRING && (prefixLit.Value == `"/"` || prefixLit.Value == `""`) { - wrapInStripHandler = false - } - } - if wrapInStripHandler { - call.Fun.(*ast.SelectorExpr).Sel = ast.NewIdent("StripPrefix") - call.Args = []ast.Expr{ - prefix, - &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent("http"), - Sel: ast.NewIdent("FileServer"), - }, - Args: call.Args, - }, - } - } - fixed = true - }) - return fixed -} diff --git a/src/cmd/fix/httpfs_test.go b/src/cmd/fix/httpfs_test.go deleted file mode 100644 index dd8ef2cfd..000000000 --- a/src/cmd/fix/httpfs_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(httpFileSystemTests, httpfs) -} - -var httpFileSystemTests = []testCase{ - { - Name: "httpfs.0", - In: `package httpfs - -import ( - "http" -) - -func f() { - _ = http.FileServer("/var/www/foo", "/") - _ = http.FileServer("/var/www/foo", "") - _ = http.FileServer("/var/www/foo/bar", "/bar") - s := "/foo" - _ = http.FileServer(s, "/") - prefix := "/p" - _ = http.FileServer(s, prefix) -} -`, - Out: `package httpfs - -import ( - "http" -) - -func f() { - _ = http.FileServer(http.Dir("/var/www/foo")) - _ = http.FileServer(http.Dir("/var/www/foo")) - _ = http.StripPrefix("/bar", http.FileServer(http.Dir("/var/www/foo/bar"))) - s := "/foo" - _ = http.FileServer(http.Dir(s)) - prefix := "/p" - _ = http.StripPrefix(prefix, http.FileServer(http.Dir(s))) -} -`, - }, -} diff --git a/src/cmd/fix/httpheaders.go b/src/cmd/fix/httpheaders.go deleted file mode 100644 index 15c21ac86..000000000 --- a/src/cmd/fix/httpheaders.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(httpHeadersFix) -} - -var httpHeadersFix = fix{ - "httpheaders", - "2011-06-16", - httpheaders, - `Rename http Referer, UserAgent, Cookie, SetCookie, which are now methods. - -http://codereview.appspot.com/4620049/ -`, -} - -func httpheaders(f *ast.File) bool { - if !imports(f, "http") { - return false - } - - called := make(map[ast.Node]bool) - walk(f, func(ni interface{}) { - switch n := ni.(type) { - case *ast.CallExpr: - called[n.Fun] = true - } - }) - - fixed := false - typeof, _ := typecheck(headerTypeConfig, f) - walk(f, func(ni interface{}) { - switch n := ni.(type) { - case *ast.SelectorExpr: - if called[n] { - break - } - if t := typeof[n.X]; t != "*http.Request" && t != "*http.Response" { - break - } - switch n.Sel.Name { - case "Referer", "UserAgent": - n.Sel.Name += "()" - fixed = true - case "Cookie": - n.Sel.Name = "Cookies()" - fixed = true - } - } - }) - return fixed -} - -var headerTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "*http.Request": {}, - "*http.Response": {}, - }, -} diff --git a/src/cmd/fix/httpheaders_test.go b/src/cmd/fix/httpheaders_test.go deleted file mode 100644 index 37506b82d..000000000 --- a/src/cmd/fix/httpheaders_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(httpHeadersTests, httpheaders) -} - -var httpHeadersTests = []testCase{ - { - Name: "httpheaders.0", - In: `package headertest - -import ( - "http" -) - -type Other struct { - Referer string - UserAgent string - Cookie []*http.Cookie -} - -func f(req *http.Request, res *http.Response, other *Other) { - _ = req.Referer - _ = req.UserAgent - _ = req.Cookie - - _ = res.Cookie - - _ = other.Referer - _ = other.UserAgent - _ = other.Cookie - - _ = req.Referer() - _ = req.UserAgent() - _ = req.Cookies() - _ = res.Cookies() -} -`, - Out: `package headertest - -import ( - "http" -) - -type Other struct { - Referer string - UserAgent string - Cookie []*http.Cookie -} - -func f(req *http.Request, res *http.Response, other *Other) { - _ = req.Referer() - _ = req.UserAgent() - _ = req.Cookies() - - _ = res.Cookies() - - _ = other.Referer - _ = other.UserAgent - _ = other.Cookie - - _ = req.Referer() - _ = req.UserAgent() - _ = req.Cookies() - _ = res.Cookies() -} -`, - }, -} diff --git a/src/cmd/fix/httpserver.go b/src/cmd/fix/httpserver.go deleted file mode 100644 index 7aa651786..000000000 --- a/src/cmd/fix/httpserver.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register(httpserverFix) -} - -var httpserverFix = fix{ - "httpserver", - "2011-03-15", - httpserver, - `Adapt http server methods and functions to changes -made to the http ResponseWriter interface. - -http://codereview.appspot.com/4245064 Hijacker -http://codereview.appspot.com/4239076 Header -http://codereview.appspot.com/4239077 Flusher -http://codereview.appspot.com/4248075 RemoteAddr, UsingTLS -`, -} - -func httpserver(f *ast.File) bool { - if !imports(f, "http") { - return false - } - - fixed := false - for _, decl := range f.Decls { - fn, ok := decl.(*ast.FuncDecl) - if !ok { - continue - } - w, req, ok := isServeHTTP(fn) - if !ok { - continue - } - walk(fn.Body, func(n interface{}) { - // Want to replace expression sometimes, - // so record pointer to it for updating below. - ptr, ok := n.(*ast.Expr) - if ok { - n = *ptr - } - - // Look for w.UsingTLS() and w.Remoteaddr(). - call, ok := n.(*ast.CallExpr) - if !ok || (len(call.Args) != 0 && len(call.Args) != 2) { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return - } - if !refersTo(sel.X, w) { - return - } - switch sel.Sel.String() { - case "Hijack": - // replace w with w.(http.Hijacker) - sel.X = &ast.TypeAssertExpr{ - X: sel.X, - Type: ast.NewIdent("http.Hijacker"), - } - fixed = true - case "Flush": - // replace w with w.(http.Flusher) - sel.X = &ast.TypeAssertExpr{ - X: sel.X, - Type: ast.NewIdent("http.Flusher"), - } - fixed = true - case "UsingTLS": - if ptr == nil { - // can only replace expression if we have pointer to it - break - } - // replace with req.TLS != nil - *ptr = &ast.BinaryExpr{ - X: &ast.SelectorExpr{ - X: ast.NewIdent(req.String()), - Sel: ast.NewIdent("TLS"), - }, - Op: token.NEQ, - Y: ast.NewIdent("nil"), - } - fixed = true - case "RemoteAddr": - if ptr == nil { - // can only replace expression if we have pointer to it - break - } - // replace with req.RemoteAddr - *ptr = &ast.SelectorExpr{ - X: ast.NewIdent(req.String()), - Sel: ast.NewIdent("RemoteAddr"), - } - fixed = true - case "SetHeader": - // replace w.SetHeader with w.Header().Set - // or w.Header().Del if second argument is "" - sel.X = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent(w.String()), - Sel: ast.NewIdent("Header"), - }, - } - sel.Sel = ast.NewIdent("Set") - if len(call.Args) == 2 && isEmptyString(call.Args[1]) { - sel.Sel = ast.NewIdent("Del") - call.Args = call.Args[:1] - } - fixed = true - } - }) - } - return fixed -} - -func isServeHTTP(fn *ast.FuncDecl) (w, req *ast.Ident, ok bool) { - for _, field := range fn.Type.Params.List { - if isPkgDot(field.Type, "http", "ResponseWriter") { - w = field.Names[0] - continue - } - if isPtrPkgDot(field.Type, "http", "Request") { - req = field.Names[0] - continue - } - } - - ok = w != nil && req != nil - return -} diff --git a/src/cmd/fix/httpserver_test.go b/src/cmd/fix/httpserver_test.go deleted file mode 100644 index b6ddff27e..000000000 --- a/src/cmd/fix/httpserver_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(httpserverTests, httpserver) -} - -var httpserverTests = []testCase{ - { - Name: "httpserver.0", - In: `package main - -import "http" - -func f(xyz http.ResponseWriter, abc *http.Request, b string) { - xyz.SetHeader("foo", "bar") - xyz.SetHeader("baz", "") - xyz.Hijack() - xyz.Flush() - go xyz.Hijack() - defer xyz.Flush() - _ = xyz.UsingTLS() - _ = true == xyz.UsingTLS() - _ = xyz.RemoteAddr() - _ = xyz.RemoteAddr() == "hello" - if xyz.UsingTLS() { - } -} -`, - Out: `package main - -import "http" - -func f(xyz http.ResponseWriter, abc *http.Request, b string) { - xyz.Header().Set("foo", "bar") - xyz.Header().Del("baz") - xyz.(http.Hijacker).Hijack() - xyz.(http.Flusher).Flush() - go xyz.(http.Hijacker).Hijack() - defer xyz.(http.Flusher).Flush() - _ = abc.TLS != nil - _ = true == (abc.TLS != nil) - _ = abc.RemoteAddr - _ = abc.RemoteAddr == "hello" - if abc.TLS != nil { - } -} -`, - }, -} diff --git a/src/cmd/fix/imagecolor.go b/src/cmd/fix/imagecolor.go deleted file mode 100644 index 1aac40a6f..000000000 --- a/src/cmd/fix/imagecolor.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(imagecolorFix) -} - -var imagecolorFix = fix{ - "imagecolor", - "2011-10-04", - imagecolor, - `Adapt code to types moved from image to color. - -http://codereview.appspot.com/5132048 -`, -} - -var colorRenames = []struct{ in, out string }{ - {"Color", "Color"}, - {"ColorModel", "Model"}, - {"ColorModelFunc", "ModelFunc"}, - {"PalettedColorModel", "Palette"}, - - {"RGBAColor", "RGBA"}, - {"RGBA64Color", "RGBA64"}, - {"NRGBAColor", "NRGBA"}, - {"NRGBA64Color", "NRGBA64"}, - {"AlphaColor", "Alpha"}, - {"Alpha16Color", "Alpha16"}, - {"GrayColor", "Gray"}, - {"Gray16Color", "Gray16"}, - - {"RGBAColorModel", "RGBAModel"}, - {"RGBA64ColorModel", "RGBA64Model"}, - {"NRGBAColorModel", "NRGBAModel"}, - {"NRGBA64ColorModel", "NRGBA64Model"}, - {"AlphaColorModel", "AlphaModel"}, - {"Alpha16ColorModel", "Alpha16Model"}, - {"GrayColorModel", "GrayModel"}, - {"Gray16ColorModel", "Gray16Model"}, -} - -func imagecolor(f *ast.File) (fixed bool) { - if !imports(f, "image") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - - if !ok || !isTopName(s.X, "image") { - return - } - - switch sel := s.Sel.String(); { - case sel == "ColorImage": - s.Sel = &ast.Ident{Name: "Uniform"} - fixed = true - case sel == "NewColorImage": - s.Sel = &ast.Ident{Name: "NewUniform"} - fixed = true - default: - for _, rename := range colorRenames { - if sel == rename.in { - addImport(f, "image/color") - s.X.(*ast.Ident).Name = "color" - s.Sel.Name = rename.out - fixed = true - } - } - } - }) - - if fixed && !usesImport(f, "image") { - deleteImport(f, "image") - } - return -} diff --git a/src/cmd/fix/imagecolor_test.go b/src/cmd/fix/imagecolor_test.go deleted file mode 100644 index c62365481..000000000 --- a/src/cmd/fix/imagecolor_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(colorTests, imagecolor) -} - -var colorTests = []testCase{ - { - Name: "color.0", - In: `package main - -import ( - "image" -) - -var ( - _ image.Image - _ image.RGBA - _ image.Black - _ image.Color - _ image.ColorModel - _ image.ColorModelFunc - _ image.PalettedColorModel - _ image.RGBAColor - _ image.RGBA64Color - _ image.NRGBAColor - _ image.NRGBA64Color - _ image.AlphaColor - _ image.Alpha16Color - _ image.GrayColor - _ image.Gray16Color -) - -func f() { - _ = image.RGBAColorModel - _ = image.RGBA64ColorModel - _ = image.NRGBAColorModel - _ = image.NRGBA64ColorModel - _ = image.AlphaColorModel - _ = image.Alpha16ColorModel - _ = image.GrayColorModel - _ = image.Gray16ColorModel -} -`, - Out: `package main - -import ( - "image" - "image/color" -) - -var ( - _ image.Image - _ image.RGBA - _ image.Black - _ color.Color - _ color.Model - _ color.ModelFunc - _ color.Palette - _ color.RGBA - _ color.RGBA64 - _ color.NRGBA - _ color.NRGBA64 - _ color.Alpha - _ color.Alpha16 - _ color.Gray - _ color.Gray16 -) - -func f() { - _ = color.RGBAModel - _ = color.RGBA64Model - _ = color.NRGBAModel - _ = color.NRGBA64Model - _ = color.AlphaModel - _ = color.Alpha16Model - _ = color.GrayModel - _ = color.Gray16Model -} -`, - }, - { - Name: "color.1", - In: `package main - -import ( - "fmt" - "image" -) - -func f() { - fmt.Println(image.RGBAColor{1, 2, 3, 4}.RGBA()) -} -`, - Out: `package main - -import ( - "fmt" - "image/color" -) - -func f() { - fmt.Println(color.RGBA{1, 2, 3, 4}.RGBA()) -} -`, - }, - { - Name: "color.2", - In: `package main - -import "image" - -var c *image.ColorImage = image.NewColorImage(nil) -`, - Out: `package main - -import "image" - -var c *image.Uniform = image.NewUniform(nil) -`, - }, -} diff --git a/src/cmd/fix/imagenew.go b/src/cmd/fix/imagenew.go deleted file mode 100644 index b4e36d4f0..000000000 --- a/src/cmd/fix/imagenew.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(imagenewFix) -} - -var imagenewFix = fix{ - "imagenew", - "2011-09-14", - imagenew, - `Adapt image.NewXxx calls to pass an image.Rectangle instead of (w, h int). - -http://codereview.appspot.com/4964073 -`, -} - -var imagenewFuncs = map[string]bool{ - "NewRGBA": true, - "NewRGBA64": true, - "NewNRGBA": true, - "NewNRGBA64": true, - "NewAlpha": true, - "NewAlpha16": true, - "NewGray": true, - "NewGray16": true, -} - -func imagenew(f *ast.File) bool { - if !imports(f, "image") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok { - return - } - isNewFunc := false - for newFunc := range imagenewFuncs { - if len(call.Args) == 2 && isPkgDot(call.Fun, "image", newFunc) { - isNewFunc = true - break - } - } - if len(call.Args) == 3 && isPkgDot(call.Fun, "image", "NewPaletted") { - isNewFunc = true - } - if !isNewFunc { - return - } - // Replace image.NewXxx(w, h) with image.NewXxx(image.Rect(0, 0, w, h)). - rectArgs := []ast.Expr{ - &ast.BasicLit{Value: "0"}, - &ast.BasicLit{Value: "0"}, - } - rectArgs = append(rectArgs, call.Args[:2]...) - rect := []ast.Expr{ - &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: &ast.Ident{ - Name: "image", - }, - Sel: &ast.Ident{ - Name: "Rect", - }, - }, - Args: rectArgs, - }, - } - call.Args = append(rect, call.Args[2:]...) - fixed = true - }) - return fixed -} diff --git a/src/cmd/fix/imagenew_test.go b/src/cmd/fix/imagenew_test.go deleted file mode 100644 index 30abed23c..000000000 --- a/src/cmd/fix/imagenew_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(imagenewTests, imagenew) -} - -var imagenewTests = []testCase{ - { - Name: "imagenew.0", - In: `package main - -import ( - "image" -) - -func f() { - image.NewRGBA(1, 2) - image.NewRGBA64(1, 2) - image.NewNRGBA(1, 2) - image.NewNRGBA64(1, 2) - image.NewAlpha(1, 2) - image.NewAlpha16(1, 2) - image.NewGray(1, 2) - image.NewGray16(1, 2) - image.NewPaletted(1, 2, nil) -} -`, - Out: `package main - -import ( - "image" -) - -func f() { - image.NewRGBA(image.Rect(0, 0, 1, 2)) - image.NewRGBA64(image.Rect(0, 0, 1, 2)) - image.NewNRGBA(image.Rect(0, 0, 1, 2)) - image.NewNRGBA64(image.Rect(0, 0, 1, 2)) - image.NewAlpha(image.Rect(0, 0, 1, 2)) - image.NewAlpha16(image.Rect(0, 0, 1, 2)) - image.NewGray(image.Rect(0, 0, 1, 2)) - image.NewGray16(image.Rect(0, 0, 1, 2)) - image.NewPaletted(image.Rect(0, 0, 1, 2), nil) -} -`, - }, -} diff --git a/src/cmd/fix/imageycbcr.go b/src/cmd/fix/imageycbcr.go deleted file mode 100644 index 41b96d18d..000000000 --- a/src/cmd/fix/imageycbcr.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(imageycbcrFix) -} - -var imageycbcrFix = fix{ - "imageycbcr", - "2011-12-20", - imageycbcr, - `Adapt code to types moved from image/ycbcr to image and image/color. - -http://codereview.appspot.com/5493084 -`, -} - -func imageycbcr(f *ast.File) (fixed bool) { - if !imports(f, "image/ycbcr") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - - if !ok || !isTopName(s.X, "ycbcr") { - return - } - - switch s.Sel.String() { - case "RGBToYCbCr", "YCbCrToRGB": - addImport(f, "image/color") - s.X.(*ast.Ident).Name = "color" - case "YCbCrColor": - addImport(f, "image/color") - s.X.(*ast.Ident).Name = "color" - s.Sel.Name = "YCbCr" - case "YCbCrColorModel": - addImport(f, "image/color") - s.X.(*ast.Ident).Name = "color" - s.Sel.Name = "YCbCrModel" - case "SubsampleRatio", "SubsampleRatio444", "SubsampleRatio422", "SubsampleRatio420": - addImport(f, "image") - s.X.(*ast.Ident).Name = "image" - s.Sel.Name = "YCbCr" + s.Sel.Name - case "YCbCr": - addImport(f, "image") - s.X.(*ast.Ident).Name = "image" - default: - return - } - fixed = true - }) - - deleteImport(f, "image/ycbcr") - return -} diff --git a/src/cmd/fix/imageycbcr_test.go b/src/cmd/fix/imageycbcr_test.go deleted file mode 100644 index 23b599dcd..000000000 --- a/src/cmd/fix/imageycbcr_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(ycbcrTests, imageycbcr) -} - -var ycbcrTests = []testCase{ - { - Name: "ycbcr.0", - In: `package main - -import ( - "image/ycbcr" -) - -func f() { - _ = ycbcr.RGBToYCbCr - _ = ycbcr.YCbCrToRGB - _ = ycbcr.YCbCrColorModel - var _ ycbcr.YCbCrColor - var _ ycbcr.YCbCr - var ( - _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio444 - _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio422 - _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio420 - ) -} -`, - Out: `package main - -import ( - "image" - "image/color" -) - -func f() { - _ = color.RGBToYCbCr - _ = color.YCbCrToRGB - _ = color.YCbCrModel - var _ color.YCbCr - var _ image.YCbCr - var ( - _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio444 - _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio422 - _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio420 - ) -} -`, - }, -} diff --git a/src/cmd/fix/iocopyn.go b/src/cmd/fix/iocopyn.go deleted file mode 100644 index 720f3c689..000000000 --- a/src/cmd/fix/iocopyn.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(ioCopyNFix) -} - -var ioCopyNFix = fix{ - "iocopyn", - "2011-09-30", - ioCopyN, - `Rename io.Copyn to io.CopyN. - -http://codereview.appspot.com/5157045 -`, -} - -func ioCopyN(f *ast.File) bool { - if !imports(f, "io") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - if expr, ok := n.(ast.Expr); ok { - if isPkgDot(expr, "io", "Copyn") { - expr.(*ast.SelectorExpr).Sel.Name = "CopyN" - fixed = true - return - } - } - }) - return fixed -} diff --git a/src/cmd/fix/iocopyn_test.go b/src/cmd/fix/iocopyn_test.go deleted file mode 100644 index f86fad763..000000000 --- a/src/cmd/fix/iocopyn_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(ioCopyNTests, ioCopyN) -} - -var ioCopyNTests = []testCase{ - { - Name: "io.CopyN.0", - In: `package main - -import ( - "io" -) - -func f() { - io.Copyn(dst, src) - foo.Copyn(dst, src) -} -`, - Out: `package main - -import ( - "io" -) - -func f() { - io.CopyN(dst, src) - foo.Copyn(dst, src) -} -`, - }, -} diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go index b151408d7..dc10d6beb 100644 --- a/src/cmd/fix/main.go +++ b/src/cmd/fix/main.go @@ -9,8 +9,8 @@ import ( "flag" "fmt" "go/ast" + "go/format" "go/parser" - "go/printer" "go/scanner" "go/token" "io/ioutil" @@ -97,23 +97,11 @@ func main() { os.Exit(exitCode) } -const ( - tabWidth = 8 - parserMode = parser.ParseComments - printerMode = printer.TabIndent | printer.UseSpaces -) - -var printConfig = &printer.Config{ - Mode: printerMode, - Tabwidth: tabWidth, -} +const parserMode = parser.ParseComments func gofmtFile(f *ast.File) ([]byte, error) { var buf bytes.Buffer - - ast.SortImports(fset, f) - err := printConfig.Fprint(&buf, fset, f) - if err != nil { + if err := format.Node(&buf, fset, f); err != nil { return nil, err } return buf.Bytes(), nil @@ -211,8 +199,7 @@ var gofmtBuf bytes.Buffer func gofmt(n interface{}) string { gofmtBuf.Reset() - err := printConfig.Fprint(&gofmtBuf, fset, n) - if err != nil { + if err := format.Node(&gofmtBuf, fset, n); err != nil { return "<" + err.Error() + ">" } return gofmtBuf.String() diff --git a/src/cmd/fix/mapdelete.go b/src/cmd/fix/mapdelete.go deleted file mode 100644 index db89c7bf4..000000000 --- a/src/cmd/fix/mapdelete.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(mapdeleteFix) -} - -var mapdeleteFix = fix{ - "mapdelete", - "2011-10-18", - mapdelete, - `Use delete(m, k) instead of m[k] = 0, false. - -http://codereview.appspot.com/5272045 -`, -} - -func mapdelete(f *ast.File) bool { - fixed := false - walk(f, func(n interface{}) { - stmt, ok := n.(*ast.Stmt) - if !ok { - return - } - as, ok := (*stmt).(*ast.AssignStmt) - if !ok || len(as.Lhs) != 1 || len(as.Rhs) != 2 { - return - } - ix, ok := as.Lhs[0].(*ast.IndexExpr) - if !ok { - return - } - if !isTopName(as.Rhs[1], "false") { - warn(as.Pos(), "two-element map assignment with non-false second value") - return - } - if !canDrop(as.Rhs[0]) { - warn(as.Pos(), "two-element map assignment with non-trivial first value") - return - } - *stmt = &ast.ExprStmt{ - X: &ast.CallExpr{ - Fun: &ast.Ident{ - NamePos: as.Pos(), - Name: "delete", - }, - Args: []ast.Expr{ix.X, ix.Index}, - }, - } - fixed = true - }) - return fixed -} - -// canDrop reports whether it is safe to drop the -// evaluation of n from the program. -// It is very conservative. -func canDrop(n ast.Expr) bool { - switch n := n.(type) { - case *ast.Ident, *ast.BasicLit: - return true - case *ast.ParenExpr: - return canDrop(n.X) - case *ast.SelectorExpr: - return canDrop(n.X) - case *ast.CompositeLit: - if !canDrop(n.Type) { - return false - } - for _, e := range n.Elts { - if !canDrop(e) { - return false - } - } - return true - case *ast.StarExpr: - // Dropping *x is questionable, - // but we have to be able to drop (*T)(nil). - return canDrop(n.X) - case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType: - return true - } - return false -} diff --git a/src/cmd/fix/mapdelete_test.go b/src/cmd/fix/mapdelete_test.go deleted file mode 100644 index 8ed50328e..000000000 --- a/src/cmd/fix/mapdelete_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(mapdeleteTests, mapdelete) -} - -var mapdeleteTests = []testCase{ - { - Name: "mapdelete.0", - In: `package main - -func f() { - m[x] = 0, false - m[x] = g(), false - m[x] = 1 - delete(m, x) - m[x] = 0, b -} - -func g(false bool) { - m[x] = 0, false -} -`, - Out: `package main - -func f() { - delete(m, x) - m[x] = g(), false - m[x] = 1 - delete(m, x) - m[x] = 0, b -} - -func g(false bool) { - m[x] = 0, false -} -`, - }, -} diff --git a/src/cmd/fix/math.go b/src/cmd/fix/math.go deleted file mode 100644 index 2ec837eb0..000000000 --- a/src/cmd/fix/math.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(mathFix) -} - -var mathFix = fix{ - "math", - "2011-09-29", - math, - `Remove the leading F from math functions such as Fabs. - -http://codereview.appspot.com/5158043 -`, -} - -var mathRenames = []struct{ in, out string }{ - {"Fabs", "Abs"}, - {"Fdim", "Dim"}, - {"Fmax", "Max"}, - {"Fmin", "Min"}, - {"Fmod", "Mod"}, -} - -func math(f *ast.File) bool { - if !imports(f, "math") { - return false - } - - fixed := false - - walk(f, func(n interface{}) { - // Rename functions. - if expr, ok := n.(ast.Expr); ok { - for _, s := range mathRenames { - if isPkgDot(expr, "math", s.in) { - expr.(*ast.SelectorExpr).Sel.Name = s.out - fixed = true - return - } - } - } - }) - return fixed -} diff --git a/src/cmd/fix/math_test.go b/src/cmd/fix/math_test.go deleted file mode 100644 index b8d69d2f2..000000000 --- a/src/cmd/fix/math_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(mathTests, math) -} - -var mathTests = []testCase{ - { - Name: "math.0", - In: `package main - -import ( - "math" -) - -func f() { - math.Fabs(1) - math.Fdim(1) - math.Fmax(1) - math.Fmin(1) - math.Fmod(1) - math.Abs(1) - foo.Fabs(1) -} -`, - Out: `package main - -import ( - "math" -) - -func f() { - math.Abs(1) - math.Dim(1) - math.Max(1) - math.Min(1) - math.Mod(1) - math.Abs(1) - foo.Fabs(1) -} -`, - }, -} diff --git a/src/cmd/fix/netdial.go b/src/cmd/fix/netdial.go deleted file mode 100644 index 2de994cff..000000000 --- a/src/cmd/fix/netdial.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(netdialFix) - register(tlsdialFix) - register(netlookupFix) -} - -var netdialFix = fix{ - "netdial", - "2011-03-28", - netdial, - `Adapt 3-argument calls of net.Dial to use 2-argument form. - -http://codereview.appspot.com/4244055 -`, -} - -var tlsdialFix = fix{ - "tlsdial", - "2011-03-28", - tlsdial, - `Adapt 4-argument calls of tls.Dial to use 3-argument form. - -http://codereview.appspot.com/4244055 -`, -} - -var netlookupFix = fix{ - "netlookup", - "2011-03-28", - netlookup, - `Adapt 3-result calls to net.LookupHost to use 2-result form. - -http://codereview.appspot.com/4244055 -`, -} - -func netdial(f *ast.File) bool { - if !imports(f, "net") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || !isPkgDot(call.Fun, "net", "Dial") || len(call.Args) != 3 { - return - } - // net.Dial(a, "", b) -> net.Dial(a, b) - if !isEmptyString(call.Args[1]) { - warn(call.Pos(), "call to net.Dial with non-empty second argument") - return - } - call.Args[1] = call.Args[2] - call.Args = call.Args[:2] - fixed = true - }) - return fixed -} - -func tlsdial(f *ast.File) bool { - if !imports(f, "crypto/tls") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || !isPkgDot(call.Fun, "tls", "Dial") || len(call.Args) != 4 { - return - } - // tls.Dial(a, "", b, c) -> tls.Dial(a, b, c) - if !isEmptyString(call.Args[1]) { - warn(call.Pos(), "call to tls.Dial with non-empty second argument") - return - } - call.Args[1] = call.Args[2] - call.Args[2] = call.Args[3] - call.Args = call.Args[:3] - fixed = true - }) - return fixed -} - -func netlookup(f *ast.File) bool { - if !imports(f, "net") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - as, ok := n.(*ast.AssignStmt) - if !ok || len(as.Lhs) != 3 || len(as.Rhs) != 1 { - return - } - call, ok := as.Rhs[0].(*ast.CallExpr) - if !ok || !isPkgDot(call.Fun, "net", "LookupHost") { - return - } - if !isBlank(as.Lhs[2]) { - warn(as.Pos(), "call to net.LookupHost expecting cname; use net.LookupCNAME") - return - } - as.Lhs = as.Lhs[:2] - fixed = true - }) - return fixed -} diff --git a/src/cmd/fix/netdial_test.go b/src/cmd/fix/netdial_test.go deleted file mode 100644 index fff00b4ad..000000000 --- a/src/cmd/fix/netdial_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(netdialTests, nil) -} - -var netdialTests = []testCase{ - { - Name: "netdial.0", - Fn: netdial, - In: `package main - -import "net" - -func f() { - c, err := net.Dial(net, "", addr) - c, err = net.Dial(net, "", addr) -} -`, - Out: `package main - -import "net" - -func f() { - c, err := net.Dial(net, addr) - c, err = net.Dial(net, addr) -} -`, - }, - - { - Name: "netlookup.0", - Fn: netlookup, - In: `package main - -import "net" - -func f() { - foo, bar, _ := net.LookupHost(host) - foo, bar, _ = net.LookupHost(host) -} -`, - Out: `package main - -import "net" - -func f() { - foo, bar := net.LookupHost(host) - foo, bar = net.LookupHost(host) -} -`, - }, -} diff --git a/src/cmd/fix/netipv6zone.go b/src/cmd/fix/netipv6zone.go new file mode 100644 index 000000000..587b9ffec --- /dev/null +++ b/src/cmd/fix/netipv6zone.go @@ -0,0 +1,71 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "go/ast" + +func init() { + register(netipv6zoneFix) +} + +var netipv6zoneFix = fix{ + "netipv6zone", + "2012-11-26", + netipv6zone, + `Adapt element key to IPNet, IPAddr, UDPAddr or TCPAddr composite literals. + +https://codereview.appspot.com/6849045/ +`, +} + +func netipv6zone(f *ast.File) bool { + if !imports(f, "net") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + cl, ok := n.(*ast.CompositeLit) + if !ok { + return + } + se, ok := cl.Type.(*ast.SelectorExpr) + if !ok { + return + } + if !isTopName(se.X, "net") || se.Sel == nil { + return + } + switch ss := se.Sel.String(); ss { + case "IPNet", "IPAddr", "UDPAddr", "TCPAddr": + for i, e := range cl.Elts { + if _, ok := e.(*ast.KeyValueExpr); ok { + break + } + switch i { + case 0: + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("IP"), + Value: e, + } + case 1: + if ss == "IPNet" { + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("Mask"), + Value: e, + } + } else { + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("Port"), + Value: e, + } + } + } + fixed = true + } + } + }) + return fixed +} diff --git a/src/cmd/fix/netipv6zone_test.go b/src/cmd/fix/netipv6zone_test.go new file mode 100644 index 000000000..229daa386 --- /dev/null +++ b/src/cmd/fix/netipv6zone_test.go @@ -0,0 +1,51 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(netipv6zoneTests, netipv6zone) +} + +var netipv6zoneTests = []testCase{ + { + Name: "netipv6zone.0", + In: `package main + +import "net" + +var a = []struct { + *net.IPNet +}{ + &net.IPNet{net.ParseIP("2001:DB8::"), net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, +} + +func f() net.Addr { + b := net.IPNet{net.IPv4(127, 0, 0, 1), net.IPv4Mask(255, 0, 0, 0)} + c := &net.IPAddr{ip1} + sub(&net.UDPAddr{ip2, 12345}) + d := &net.TCPAddr{IP: ip3, Port: 54321} + return &net.TCPAddr{ip4}, nil +} +`, + Out: `package main + +import "net" + +var a = []struct { + *net.IPNet +}{ + &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, +} + +func f() net.Addr { + b := net.IPNet{IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 0, 0, 0)} + c := &net.IPAddr{IP: ip1} + sub(&net.UDPAddr{IP: ip2, Port: 12345}) + d := &net.TCPAddr{IP: ip3, Port: 54321} + return &net.TCPAddr{IP: ip4}, nil +} +`, + }, +} diff --git a/src/cmd/fix/netudpgroup.go b/src/cmd/fix/netudpgroup.go deleted file mode 100644 index b54beb0de..000000000 --- a/src/cmd/fix/netudpgroup.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(netudpgroupFix) -} - -var netudpgroupFix = fix{ - "netudpgroup", - "2011-08-18", - netudpgroup, - `Adapt 1-argument calls of net.(*UDPConn).JoinGroup, LeaveGroup to use 2-argument form. - -http://codereview.appspot.com/4815074 -`, -} - -func netudpgroup(f *ast.File) bool { - if !imports(f, "net") { - return false - } - - fixed := false - for _, d := range f.Decls { - fd, ok := d.(*ast.FuncDecl) - if !ok || fd.Body == nil { - continue - } - walk(fd.Body, func(n interface{}) { - ce, ok := n.(*ast.CallExpr) - if !ok { - return - } - se, ok := ce.Fun.(*ast.SelectorExpr) - if !ok || len(ce.Args) != 1 { - return - } - switch se.Sel.String() { - case "JoinGroup", "LeaveGroup": - // c.JoinGroup(a) -> c.JoinGroup(nil, a) - // c.LeaveGroup(a) -> c.LeaveGroup(nil, a) - arg := ce.Args[0] - ce.Args = make([]ast.Expr, 2) - ce.Args[0] = ast.NewIdent("nil") - ce.Args[1] = arg - fixed = true - } - }) - } - return fixed -} diff --git a/src/cmd/fix/netudpgroup_test.go b/src/cmd/fix/netudpgroup_test.go deleted file mode 100644 index 88c0e093f..000000000 --- a/src/cmd/fix/netudpgroup_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(netudpgroupTests, netudpgroup) -} - -var netudpgroupTests = []testCase{ - { - Name: "netudpgroup.0", - In: `package main - -import "net" - -func f() { - err := x.JoinGroup(gaddr) - err = y.LeaveGroup(gaddr) -} -`, - Out: `package main - -import "net" - -func f() { - err := x.JoinGroup(nil, gaddr) - err = y.LeaveGroup(nil, gaddr) -} -`, - }, - // Innocent function with no body. - { - Name: "netudpgroup.1", - In: `package main - -import "net" - -func f() - -var _ net.IP -`, - Out: `package main - -import "net" - -func f() - -var _ net.IP -`, - }, -} diff --git a/src/cmd/fix/newwriter.go b/src/cmd/fix/newwriter.go deleted file mode 100644 index 4befe24fb..000000000 --- a/src/cmd/fix/newwriter.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(newWriterFix) -} - -var newWriterFix = fix{ - "newWriter", - "2012-02-14", - newWriter, - `Adapt bufio, gzip and zlib NewWriterXxx calls for whether they return errors. - -Also rename gzip.Compressor and gzip.Decompressor to gzip.Writer and gzip.Reader. - -http://codereview.appspot.com/5639057 and -http://codereview.appspot.com/5642054 -`, -} - -func newWriter(f *ast.File) bool { - if !imports(f, "bufio") && !imports(f, "compress/gzip") && !imports(f, "compress/zlib") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - switch n := n.(type) { - case *ast.SelectorExpr: - if isTopName(n.X, "gzip") { - switch n.Sel.String() { - case "Compressor": - n.Sel = &ast.Ident{Name: "Writer"} - fixed = true - case "Decompressor": - n.Sel = &ast.Ident{Name: "Reader"} - fixed = true - } - } else if isTopName(n.X, "zlib") { - if n.Sel.String() == "NewWriterDict" { - n.Sel = &ast.Ident{Name: "NewWriterLevelDict"} - fixed = true - } - } - - case *ast.AssignStmt: - // Drop the ", _" in assignments of the form: - // w0, _ = gzip.NewWriter(w1) - if len(n.Lhs) != 2 || len(n.Rhs) != 1 { - return - } - i, ok := n.Lhs[1].(*ast.Ident) - if !ok { - return - } - if i.String() != "_" { - return - } - c, ok := n.Rhs[0].(*ast.CallExpr) - if !ok { - return - } - s, ok := c.Fun.(*ast.SelectorExpr) - if !ok { - return - } - sel := s.Sel.String() - switch { - case isTopName(s.X, "bufio") && (sel == "NewReaderSize" || sel == "NewWriterSize"): - // No-op. - case isTopName(s.X, "gzip") && sel == "NewWriter": - // No-op. - case isTopName(s.X, "zlib") && sel == "NewWriter": - // No-op. - default: - return - } - n.Lhs = n.Lhs[:1] - fixed = true - } - }) - return fixed -} diff --git a/src/cmd/fix/newwriter_test.go b/src/cmd/fix/newwriter_test.go deleted file mode 100644 index 1f59628a0..000000000 --- a/src/cmd/fix/newwriter_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(newWriterTests, newWriter) -} - -var newWriterTests = []testCase{ - { - Name: "newWriter.0", - In: `package main - -import ( - "bufio" - "compress/gzip" - "compress/zlib" - "io" - - "foo" -) - -func f() *gzip.Compressor { - var ( - _ gzip.Compressor - _ *gzip.Decompressor - _ struct { - W *gzip.Compressor - R gzip.Decompressor - } - ) - - var w io.Writer - br := bufio.NewReader(nil) - br, _ = bufio.NewReaderSize(nil, 256) - bw, err := bufio.NewWriterSize(w, 256) // Unfixable, as it declares an err variable. - bw, _ = bufio.NewWriterSize(w, 256) - fw, _ := foo.NewWriter(w) - gw, _ := gzip.NewWriter(w) - gw, _ = gzip.NewWriter(w) - zw, _ := zlib.NewWriter(w) - _ = zlib.NewWriterDict(zw, 0, nil) - return gw -} -`, - Out: `package main - -import ( - "bufio" - "compress/gzip" - "compress/zlib" - "io" - - "foo" -) - -func f() *gzip.Writer { - var ( - _ gzip.Writer - _ *gzip.Reader - _ struct { - W *gzip.Writer - R gzip.Reader - } - ) - - var w io.Writer - br := bufio.NewReader(nil) - br = bufio.NewReaderSize(nil, 256) - bw, err := bufio.NewWriterSize(w, 256) // Unfixable, as it declares an err variable. - bw = bufio.NewWriterSize(w, 256) - fw, _ := foo.NewWriter(w) - gw := gzip.NewWriter(w) - gw = gzip.NewWriter(w) - zw := zlib.NewWriter(w) - _ = zlib.NewWriterLevelDict(zw, 0, nil) - return gw -} -`, - }, -} diff --git a/src/cmd/fix/oserrorstring.go b/src/cmd/fix/oserrorstring.go deleted file mode 100644 index a75a2c12d..000000000 --- a/src/cmd/fix/oserrorstring.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(oserrorstringFix) -} - -var oserrorstringFix = fix{ - "oserrorstring", - "2011-06-22", - oserrorstring, - `Replace os.ErrorString() conversions with calls to os.NewError(). - -http://codereview.appspot.com/4607052 -`, -} - -func oserrorstring(f *ast.File) bool { - if !imports(f, "os") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - // The conversion os.ErrorString(x) looks like a call - // of os.ErrorString with one argument. - if call := callExpr(n, "os", "ErrorString"); call != nil { - // os.ErrorString(args) -> os.NewError(args) - call.Fun.(*ast.SelectorExpr).Sel.Name = "NewError" - // os.ErrorString(args) -> os.NewError(args) - call.Fun.(*ast.SelectorExpr).Sel.Name = "NewError" - fixed = true - return - } - - // Remove os.Error type from variable declarations initialized - // with an os.NewError. - // (An *ast.ValueSpec may also be used in a const declaration - // but those won't be initialized with a call to os.NewError.) - if spec, ok := n.(*ast.ValueSpec); ok && - len(spec.Names) == 1 && - isPkgDot(spec.Type, "os", "Error") && - len(spec.Values) == 1 && - callExpr(spec.Values[0], "os", "NewError") != nil { - // var name os.Error = os.NewError(x) -> - // var name = os.NewError(x) - spec.Type = nil - fixed = true - return - } - - // Other occurrences of os.ErrorString are not fixed - // but they are rare. - - }) - return fixed -} - -// callExpr returns the call expression if x is a call to pkg.name with one argument; -// otherwise it returns nil. -func callExpr(x interface{}, pkg, name string) *ast.CallExpr { - if call, ok := x.(*ast.CallExpr); ok && - len(call.Args) == 1 && - isPkgDot(call.Fun, pkg, name) { - return call - } - return nil -} diff --git a/src/cmd/fix/oserrorstring_test.go b/src/cmd/fix/oserrorstring_test.go deleted file mode 100644 index 75551480c..000000000 --- a/src/cmd/fix/oserrorstring_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(oserrorstringTests, oserrorstring) -} - -var oserrorstringTests = []testCase{ - { - Name: "oserrorstring.0", - In: `package main - -import "os" - -var _ = os.ErrorString("foo") -var _ os.Error = os.ErrorString("bar1") -var _ os.Error = os.NewError("bar2") -var _ os.Error = MyError("bal") // don't rewrite this one - -var ( - _ = os.ErrorString("foo") - _ os.Error = os.ErrorString("bar1") - _ os.Error = os.NewError("bar2") - _ os.Error = MyError("bal") // don't rewrite this one -) - -func _() (err os.Error) { - err = os.ErrorString("foo") - return os.ErrorString("foo") -} -`, - Out: `package main - -import "os" - -var _ = os.NewError("foo") -var _ = os.NewError("bar1") -var _ = os.NewError("bar2") -var _ os.Error = MyError("bal") // don't rewrite this one - -var ( - _ = os.NewError("foo") - _ = os.NewError("bar1") - _ = os.NewError("bar2") - _ os.Error = MyError("bal") // don't rewrite this one -) - -func _() (err os.Error) { - err = os.NewError("foo") - return os.NewError("foo") -} -`, - }, -} diff --git a/src/cmd/fix/osopen.go b/src/cmd/fix/osopen.go deleted file mode 100644 index af2796ac2..000000000 --- a/src/cmd/fix/osopen.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(osopenFix) -} - -var osopenFix = fix{ - "osopen", - "2011-04-04", - osopen, - `Adapt os.Open calls to new, easier API and rename O_CREAT O_CREATE. - -http://codereview.appspot.com/4357052 -`, -} - -func osopen(f *ast.File) bool { - if !imports(f, "os") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - // Rename O_CREAT to O_CREATE. - if expr, ok := n.(ast.Expr); ok && isPkgDot(expr, "os", "O_CREAT") { - expr.(*ast.SelectorExpr).Sel.Name = "O_CREATE" - fixed = true - return - } - - // Fix up calls to Open. - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 3 { - return - } - if !isPkgDot(call.Fun, "os", "Open") { - return - } - sel := call.Fun.(*ast.SelectorExpr) - args := call.Args - // os.Open(a, os.O_RDONLY, c) -> os.Open(a) - if isPkgDot(args[1], "os", "O_RDONLY") || isPkgDot(args[1], "syscall", "O_RDONLY") { - call.Args = call.Args[0:1] - fixed = true - return - } - // os.Open(a, createlike_flags, c) -> os.Create(a, c) - if isCreateFlag(args[1]) { - sel.Sel.Name = "Create" - if !isSimplePerm(args[2]) { - warn(sel.Pos(), "rewrote os.Open to os.Create with permission not 0666") - } - call.Args = args[0:1] - fixed = true - return - } - // Fallback: os.Open(a, b, c) -> os.OpenFile(a, b, c) - sel.Sel.Name = "OpenFile" - fixed = true - }) - return fixed -} - -func isCreateFlag(flag ast.Expr) bool { - foundCreate := false - foundTrunc := false - // OR'ing of flags: is O_CREATE on? + or | would be fine; we just look for os.O_CREATE - // and don't worry about the actual operator. - p := flag.Pos() - for { - lhs := flag - expr, isBinary := flag.(*ast.BinaryExpr) - if isBinary { - lhs = expr.Y - } - sel, ok := lhs.(*ast.SelectorExpr) - if !ok || !isTopName(sel.X, "os") { - return false - } - switch sel.Sel.Name { - case "O_CREATE": - foundCreate = true - case "O_TRUNC": - foundTrunc = true - case "O_RDONLY", "O_WRONLY", "O_RDWR": - // okay - default: - // Unexpected flag, like O_APPEND or O_EXCL. - // Be conservative and do not rewrite. - return false - } - if !isBinary { - break - } - flag = expr.X - } - if !foundCreate { - return false - } - if !foundTrunc { - warn(p, "rewrote os.Open with O_CREATE but not O_TRUNC to os.Create") - } - return foundCreate -} - -func isSimplePerm(perm ast.Expr) bool { - basicLit, ok := perm.(*ast.BasicLit) - if !ok { - return false - } - switch basicLit.Value { - case "0666": - return true - } - return false -} diff --git a/src/cmd/fix/osopen_test.go b/src/cmd/fix/osopen_test.go deleted file mode 100644 index 5797adb7b..000000000 --- a/src/cmd/fix/osopen_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(osopenTests, osopen) -} - -var osopenTests = []testCase{ - { - Name: "osopen.0", - In: `package main - -import ( - "os" -) - -func f() { - os.OpenFile(a, b, c) - os.Open(a, os.O_RDONLY, 0) - os.Open(a, os.O_RDONLY, 0666) - os.Open(a, os.O_RDWR, 0) - os.Open(a, os.O_CREAT, 0666) - os.Open(a, os.O_CREAT|os.O_TRUNC, 0664) - os.Open(a, os.O_CREATE, 0666) - os.Open(a, os.O_CREATE|os.O_TRUNC, 0664) - os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) - os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) - os.Open(a, os.O_SURPRISE|os.O_CREATE, 0666) - _ = os.O_CREAT -} -`, - Out: `package main - -import ( - "os" -) - -func f() { - os.OpenFile(a, b, c) - os.Open(a) - os.Open(a) - os.OpenFile(a, os.O_RDWR, 0) - os.Create(a) - os.Create(a) - os.Create(a) - os.Create(a) - os.Create(a) - os.OpenFile(a, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - os.OpenFile(a, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) - os.OpenFile(a, os.O_SURPRISE|os.O_CREATE, 0666) - _ = os.O_CREATE -} -`, - }, - { - Name: "osopen.1", - In: `package main - -import ( - "os" -) - -func f() { - _ = os.O_CREAT -} -`, - Out: `package main - -import ( - "os" -) - -func f() { - _ = os.O_CREATE -} -`, - }, -} diff --git a/src/cmd/fix/printerconfig.go b/src/cmd/fix/printerconfig.go new file mode 100644 index 000000000..432e18bb2 --- /dev/null +++ b/src/cmd/fix/printerconfig.go @@ -0,0 +1,61 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "go/ast" + +func init() { + register(printerconfigFix) +} + +var printerconfigFix = fix{ + "printerconfig", + "2012-12-11", + printerconfig, + `Add element keys to Config composite literals.`, +} + +func printerconfig(f *ast.File) bool { + if !imports(f, "go/printer") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + cl, ok := n.(*ast.CompositeLit) + if !ok { + return + } + se, ok := cl.Type.(*ast.SelectorExpr) + if !ok { + return + } + if !isTopName(se.X, "printer") || se.Sel == nil { + return + } + + if ss := se.Sel.String(); ss == "Config" { + for i, e := range cl.Elts { + if _, ok := e.(*ast.KeyValueExpr); ok { + break + } + switch i { + case 0: + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("Mode"), + Value: e, + } + case 1: + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("Tabwidth"), + Value: e, + } + } + fixed = true + } + } + }) + return fixed +} diff --git a/src/cmd/fix/printerconfig_test.go b/src/cmd/fix/printerconfig_test.go new file mode 100644 index 000000000..72e2bdcac --- /dev/null +++ b/src/cmd/fix/printerconfig_test.go @@ -0,0 +1,37 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(printerconfigTests, printerconfig) +} + +var printerconfigTests = []testCase{ + { + Name: "printerconfig.0", + In: `package main + +import "go/printer" + +func f() printer.Config { + b := printer.Config{0, 8} + c := &printer.Config{0} + d := &printer.Config{Tabwidth: 8, Mode: 0} + return printer.Config{0, 8} +} +`, + Out: `package main + +import "go/printer" + +func f() printer.Config { + b := printer.Config{Mode: 0, Tabwidth: 8} + c := &printer.Config{Mode: 0} + d := &printer.Config{Tabwidth: 8, Mode: 0} + return printer.Config{Mode: 0, Tabwidth: 8} +} +`, + }, +} diff --git a/src/cmd/fix/procattr.go b/src/cmd/fix/procattr.go deleted file mode 100644 index ea375ec9d..000000000 --- a/src/cmd/fix/procattr.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register(procattrFix) -} - -var procattrFix = fix{ - "procattr", - "2011-03-15", - procattr, - `Adapt calls to os.StartProcess to use new ProcAttr type. - -http://codereview.appspot.com/4253052 -`, -} - -func procattr(f *ast.File) bool { - if !imports(f, "os") && !imports(f, "syscall") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 5 { - return - } - var pkg string - if isPkgDot(call.Fun, "os", "StartProcess") { - pkg = "os" - } else if isPkgDot(call.Fun, "syscall", "StartProcess") { - pkg = "syscall" - } else { - return - } - // os.StartProcess(a, b, c, d, e) -> os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d, Files: e}) - lit := &ast.CompositeLit{Type: ast.NewIdent(pkg + ".ProcAttr")} - env, dir, files := call.Args[2], call.Args[3], call.Args[4] - if !isName(env, "nil") && !isCall(env, "os", "Environ") { - lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Env"), Value: env}) - } - if !isEmptyString(dir) { - lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Dir"), Value: dir}) - } - if !isName(files, "nil") { - lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Files"), Value: files}) - } - call.Args[2] = &ast.UnaryExpr{Op: token.AND, X: lit} - call.Args = call.Args[:3] - fixed = true - }) - return fixed -} diff --git a/src/cmd/fix/procattr_test.go b/src/cmd/fix/procattr_test.go deleted file mode 100644 index 9e2b86e74..000000000 --- a/src/cmd/fix/procattr_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(procattrTests, procattr) -} - -var procattrTests = []testCase{ - { - Name: "procattr.0", - In: `package main - -import ( - "os" - "syscall" -) - -func f() { - os.StartProcess(a, b, c, d, e) - os.StartProcess(a, b, os.Environ(), d, e) - os.StartProcess(a, b, nil, d, e) - os.StartProcess(a, b, c, "", e) - os.StartProcess(a, b, c, d, nil) - os.StartProcess(a, b, nil, "", nil) - - os.StartProcess( - a, - b, - c, - d, - e, - ) - - syscall.StartProcess(a, b, c, d, e) - syscall.StartProcess(a, b, os.Environ(), d, e) - syscall.StartProcess(a, b, nil, d, e) - syscall.StartProcess(a, b, c, "", e) - syscall.StartProcess(a, b, c, d, nil) - syscall.StartProcess(a, b, nil, "", nil) -} -`, - Out: `package main - -import ( - "os" - "syscall" -) - -func f() { - os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d, Files: e}) - os.StartProcess(a, b, &os.ProcAttr{Dir: d, Files: e}) - os.StartProcess(a, b, &os.ProcAttr{Dir: d, Files: e}) - os.StartProcess(a, b, &os.ProcAttr{Env: c, Files: e}) - os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d}) - os.StartProcess(a, b, &os.ProcAttr{}) - - os.StartProcess( - a, - b, &os.ProcAttr{Env: c, Dir: d, Files: e}, - ) - - syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Dir: d, Files: e}) - syscall.StartProcess(a, b, &syscall.ProcAttr{Dir: d, Files: e}) - syscall.StartProcess(a, b, &syscall.ProcAttr{Dir: d, Files: e}) - syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Files: e}) - syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Dir: d}) - syscall.StartProcess(a, b, &syscall.ProcAttr{}) -} -`, - }, -} diff --git a/src/cmd/fix/reflect.go b/src/cmd/fix/reflect.go deleted file mode 100644 index 151da569d..000000000 --- a/src/cmd/fix/reflect.go +++ /dev/null @@ -1,862 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// TODO(rsc): Once there is better support for writing -// multi-package commands, this should really be in -// its own package, and then we can drop all the "reflect" -// prefixes on the global variables and functions. - -package main - -import ( - "go/ast" - "go/token" - "strings" -) - -func init() { - register(reflectFix) -} - -var reflectFix = fix{ - "reflect", - "2011-04-08", - reflectFn, - `Adapt code to new reflect API. - -http://codereview.appspot.com/4281055 -http://codereview.appspot.com/4433066 -`, -} - -// The reflect API change dropped the concrete types *reflect.ArrayType etc. -// Any type assertions prior to method calls can be deleted: -// x.(*reflect.ArrayType).Len() -> x.Len() -// -// Any type checks can be replaced by assignment and check of Kind: -// x, y := z.(*reflect.ArrayType) -// -> -// x := z -// y := x.Kind() == reflect.Array -// -// If z is an ordinary variable name and x is not subsequently assigned to, -// references to x can be replaced by z and the assignment deleted. -// We only bother if x and z are the same name. -// If y is not subsequently assigned to and neither is x, references to -// y can be replaced by its expression. We only bother when there is -// just one use or when the use appears in an if clause. -// -// Not all type checks result in a single Kind check. The rewrite of the type check for -// reflect.ArrayOrSliceType checks x.Kind() against reflect.Array and reflect.Slice. -// The rewrite for *reflect.IntType checks against Int, Int8, Int16, Int32, Int64. -// The rewrite for *reflect.UintType adds Uintptr. -// -// A type switch turns into an assignment and a switch on Kind: -// switch x := y.(type) { -// case reflect.ArrayOrSliceType: -// ... -// case *reflect.ChanType: -// ... -// case *reflect.IntType: -// ... -// } -// -> -// switch x := y; x.Kind() { -// case reflect.Array, reflect.Slice: -// ... -// case reflect.Chan: -// ... -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// ... -// } -// -// The same simplification applies: we drop x := x if x is not assigned -// to in the switch cases. -// -// Because the type check assignment includes a type assertion in its -// syntax and the rewrite traversal is bottom up, we must do a pass to -// rewrite the type check assignments and then a separate pass to -// rewrite the type assertions. -// -// The same process applies to the API changes for reflect.Value. -// -// For both cases, but especially Value, the code needs to be aware -// of the type of a receiver when rewriting a method call. For example, -// x.(*reflect.ArrayValue).Elem(i) becomes x.Index(i) while -// x.(*reflect.MapValue).Elem(v) becomes x.MapIndex(v). -// In general, reflectFn needs to know the type of the receiver expression. -// In most cases (and in all the cases in the Go source tree), the toy -// type checker in typecheck.go provides enough information for fix -// to make the rewrite. If fix misses a rewrite, the code that is left over -// will not compile, so it will be noticed immediately. - -func reflectFn(f *ast.File) bool { - if !imports(f, "reflect") { - return false - } - - fixed := false - - // Rewrite names in method calls. - // Needs basic type information (see above). - typeof, _ := typecheck(reflectTypeConfig, f) - walk(f, func(n interface{}) { - switch n := n.(type) { - case *ast.SelectorExpr: - typ := typeof[n.X] - if m := reflectRewriteMethod[typ]; m != nil { - if replace := m[n.Sel.Name]; replace != "" { - n.Sel.Name = replace - fixed = true - return - } - } - - // For all reflect Values, replace SetValue with Set. - if isReflectValue[typ] && n.Sel.Name == "SetValue" { - n.Sel.Name = "Set" - fixed = true - return - } - - // Replace reflect.MakeZero with reflect.Zero. - if isPkgDot(n, "reflect", "MakeZero") { - n.Sel.Name = "Zero" - fixed = true - return - } - } - }) - - // Replace PtrValue's PointTo(x) with Set(x.Addr()). - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 1 { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok || sel.Sel.Name != "PointTo" { - return - } - typ := typeof[sel.X] - if typ != "*reflect.PtrValue" { - return - } - sel.Sel.Name = "Set" - if !isTopName(call.Args[0], "nil") { - call.Args[0] = &ast.SelectorExpr{ - X: call.Args[0], - Sel: ast.NewIdent("Addr()"), - } - } - fixed = true - }) - - // Fix type switches. - walk(f, func(n interface{}) { - if reflectFixSwitch(n) { - fixed = true - } - }) - - // Fix type assertion checks (multiple assignment statements). - // Have to work on the statement context (statement list or if statement) - // so that we can insert an extra statement occasionally. - // Ignoring for and switch because they don't come up in - // typical code. - walk(f, func(n interface{}) { - switch n := n.(type) { - case *[]ast.Stmt: - // v is the replacement statement list. - var v []ast.Stmt - insert := func(x ast.Stmt) { - v = append(v, x) - } - for i, x := range *n { - // Tentatively append to v; if we rewrite x - // we'll have to update the entry, so remember - // the index. - j := len(v) - v = append(v, x) - if reflectFixTypecheck(&x, insert, (*n)[i+1:]) { - // reflectFixTypecheck may have overwritten x. - // Update the entry we appended just before the call. - v[j] = x - fixed = true - } - } - *n = v - case *ast.IfStmt: - x := &ast.ExprStmt{X: n.Cond} - if reflectFixTypecheck(&n.Init, nil, []ast.Stmt{x, n.Body, n.Else}) { - n.Cond = x.X - fixed = true - } - } - }) - - // Warn about any typecheck statements that we missed. - walk(f, reflectWarnTypecheckStmt) - - // Now that those are gone, fix remaining type assertions. - // Delayed because the type checks have - // type assertions as part of their syntax. - walk(f, func(n interface{}) { - if reflectFixAssert(n) { - fixed = true - } - }) - - // Now that the type assertions are gone, rewrite remaining - // references to specific reflect types to use the general ones. - walk(f, func(n interface{}) { - ptr, ok := n.(*ast.Expr) - if !ok { - return - } - nn := *ptr - typ := reflectType(nn) - if typ == "" { - return - } - if strings.HasSuffix(typ, "Type") { - *ptr = newPkgDot(nn.Pos(), "reflect", "Type") - } else { - *ptr = newPkgDot(nn.Pos(), "reflect", "Value") - } - fixed = true - }) - - // Rewrite v.Set(nil) to v.Set(reflect.MakeZero(v.Type())). - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 1 || !isTopName(call.Args[0], "nil") { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok || !isReflectValue[typeof[sel.X]] || sel.Sel.Name != "Set" { - return - } - call.Args[0] = &ast.CallExpr{ - Fun: newPkgDot(call.Args[0].Pos(), "reflect", "Zero"), - Args: []ast.Expr{ - &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: sel.X, - Sel: &ast.Ident{Name: "Type"}, - }, - }, - }, - } - fixed = true - }) - - // Rewrite v != nil to v.IsValid(). - // Rewrite nil used as reflect.Value (in function argument or return) to reflect.Value{}. - walk(f, func(n interface{}) { - ptr, ok := n.(*ast.Expr) - if !ok { - return - } - if isTopName(*ptr, "nil") && isReflectValue[typeof[*ptr]] { - *ptr = ast.NewIdent("reflect.Value{}") - fixed = true - return - } - nn, ok := (*ptr).(*ast.BinaryExpr) - if !ok || (nn.Op != token.EQL && nn.Op != token.NEQ) || !isTopName(nn.Y, "nil") || !isReflectValue[typeof[nn.X]] { - return - } - var call ast.Expr = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: nn.X, - Sel: &ast.Ident{Name: "IsValid"}, - }, - } - if nn.Op == token.EQL { - call = &ast.UnaryExpr{Op: token.NOT, X: call} - } - *ptr = call - fixed = true - }) - - // Rewrite - // reflect.Typeof -> reflect.TypeOf, - walk(f, func(n interface{}) { - sel, ok := n.(*ast.SelectorExpr) - if !ok { - return - } - if isTopName(sel.X, "reflect") && sel.Sel.Name == "Typeof" { - sel.Sel.Name = "TypeOf" - fixed = true - } - if isTopName(sel.X, "reflect") && sel.Sel.Name == "NewValue" { - sel.Sel.Name = "ValueOf" - fixed = true - } - }) - - return fixed -} - -// reflectFixSwitch rewrites *n (if n is an *ast.Stmt) corresponding -// to a type switch. -func reflectFixSwitch(n interface{}) bool { - ptr, ok := n.(*ast.Stmt) - if !ok { - return false - } - n = *ptr - - ts, ok := n.(*ast.TypeSwitchStmt) - if !ok { - return false - } - - // Are any switch cases referring to reflect types? - // (That is, is this an old reflect type switch?) - for _, cas := range ts.Body.List { - for _, typ := range cas.(*ast.CaseClause).List { - if reflectType(typ) != "" { - goto haveReflect - } - } - } - return false - -haveReflect: - // Now we know it's an old reflect type switch. Prepare the new version, - // but don't replace or edit the original until we're sure of success. - - // Figure out the initializer statement, if any, and the receiver for the Kind call. - var init ast.Stmt - var rcvr ast.Expr - - init = ts.Init - switch n := ts.Assign.(type) { - default: - warn(ts.Pos(), "unexpected form in type switch") - return false - - case *ast.AssignStmt: - as := n - ta := as.Rhs[0].(*ast.TypeAssertExpr) - x := isIdent(as.Lhs[0]) - z := isIdent(ta.X) - - if isBlank(x) || x != nil && z != nil && x.Name == z.Name && !assignsTo(x, ts.Body.List) { - // Can drop the variable creation. - rcvr = ta.X - } else { - // Need to use initialization statement. - if init != nil { - warn(ts.Pos(), "cannot rewrite reflect type switch with initializing statement") - return false - } - init = &ast.AssignStmt{ - Lhs: []ast.Expr{as.Lhs[0]}, - TokPos: as.TokPos, - Tok: token.DEFINE, - Rhs: []ast.Expr{ta.X}, - } - rcvr = as.Lhs[0] - } - - case *ast.ExprStmt: - rcvr = n.X.(*ast.TypeAssertExpr).X - } - - // Prepare rewritten type switch (see large comment above for form). - sw := &ast.SwitchStmt{ - Switch: ts.Switch, - Init: init, - Tag: &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: rcvr, - Sel: &ast.Ident{ - NamePos: rcvr.End(), - Name: "Kind", - Obj: nil, - }, - }, - Lparen: rcvr.End(), - Rparen: rcvr.End(), - }, - Body: &ast.BlockStmt{ - Lbrace: ts.Body.Lbrace, - List: nil, // to be filled in - Rbrace: ts.Body.Rbrace, - }, - } - - // Translate cases. - for _, tcas := range ts.Body.List { - tcas := tcas.(*ast.CaseClause) - cas := &ast.CaseClause{ - Case: tcas.Case, - Colon: tcas.Colon, - Body: tcas.Body, - } - for _, t := range tcas.List { - if isTopName(t, "nil") { - cas.List = append(cas.List, newPkgDot(t.Pos(), "reflect", "Invalid")) - continue - } - - typ := reflectType(t) - if typ == "" { - warn(t.Pos(), "cannot rewrite reflect type switch case with non-reflect type %s", gofmt(t)) - cas.List = append(cas.List, t) - continue - } - - for _, k := range reflectKind[typ] { - cas.List = append(cas.List, newPkgDot(t.Pos(), "reflect", k)) - } - } - sw.Body.List = append(sw.Body.List, cas) - } - - // Everything worked. Rewrite AST. - *ptr = sw - return true -} - -// Rewrite x, y = z.(T) into -// x = z -// y = x.Kind() == K -// as described in the long comment above. -// -// If insert != nil, it can be called to insert a statement after *ptr in its block. -// If insert == nil, insertion is not possible. -// At most one call to insert is allowed. -// -// Scope gives the statements for which a declaration -// in *ptr would be in scope. -// -// The result is true of the statement was rewritten. -// -func reflectFixTypecheck(ptr *ast.Stmt, insert func(ast.Stmt), scope []ast.Stmt) bool { - st := *ptr - as, ok := st.(*ast.AssignStmt) - if !ok || len(as.Lhs) != 2 || len(as.Rhs) != 1 { - return false - } - - ta, ok := as.Rhs[0].(*ast.TypeAssertExpr) - if !ok { - return false - } - typ := reflectType(ta.Type) - if typ == "" { - return false - } - - // Have x, y := z.(t). - x := isIdent(as.Lhs[0]) - y := isIdent(as.Lhs[1]) - z := isIdent(ta.X) - - // First step is x := z, unless it's x := x and the resulting x is never reassigned. - // rcvr is the x in x.Kind(). - var rcvr ast.Expr - if isBlank(x) || - as.Tok == token.DEFINE && x != nil && z != nil && x.Name == z.Name && !assignsTo(x, scope) { - // Can drop the statement. - // If we need to insert a statement later, now we have a slot. - *ptr = &ast.EmptyStmt{} - insert = func(x ast.Stmt) { *ptr = x } - rcvr = ta.X - } else { - *ptr = &ast.AssignStmt{ - Lhs: []ast.Expr{as.Lhs[0]}, - TokPos: as.TokPos, - Tok: as.Tok, - Rhs: []ast.Expr{ta.X}, - } - rcvr = as.Lhs[0] - } - - // Prepare x.Kind() == T expression appropriate to t. - // If x is not a simple identifier, warn that we might be - // reevaluating x. - if x == nil { - warn(as.Pos(), "rewrite reevaluates expr with possible side effects: %s", gofmt(as.Lhs[0])) - } - yExpr, yNotExpr := reflectKindEq(rcvr, reflectKind[typ]) - - // Second step is y := x.Kind() == T, unless it's only used once - // or we have no way to insert that statement. - var yStmt *ast.AssignStmt - if as.Tok == token.DEFINE && countUses(y, scope) <= 1 || insert == nil { - // Can drop the statement and use the expression directly. - rewriteUses(y, - func(token.Pos) ast.Expr { return yExpr }, - func(token.Pos) ast.Expr { return yNotExpr }, - scope) - } else { - yStmt = &ast.AssignStmt{ - Lhs: []ast.Expr{as.Lhs[1]}, - TokPos: as.End(), - Tok: as.Tok, - Rhs: []ast.Expr{yExpr}, - } - insert(yStmt) - } - return true -} - -// reflectKindEq returns the expression z.Kind() == kinds[0] || z.Kind() == kinds[1] || ... -// and its negation. -// The qualifier "reflect." is inserted before each kinds[i] expression. -func reflectKindEq(z ast.Expr, kinds []string) (ast.Expr, ast.Expr) { - n := len(kinds) - if n == 1 { - y := &ast.BinaryExpr{ - X: &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: z, - Sel: ast.NewIdent("Kind"), - }, - }, - Op: token.EQL, - Y: newPkgDot(token.NoPos, "reflect", kinds[0]), - } - ynot := &ast.BinaryExpr{ - X: &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: z, - Sel: ast.NewIdent("Kind"), - }, - }, - Op: token.NEQ, - Y: newPkgDot(token.NoPos, "reflect", kinds[0]), - } - return y, ynot - } - - x, xnot := reflectKindEq(z, kinds[0:n-1]) - y, ynot := reflectKindEq(z, kinds[n-1:]) - - or := &ast.BinaryExpr{ - X: x, - Op: token.LOR, - Y: y, - } - andnot := &ast.BinaryExpr{ - X: xnot, - Op: token.LAND, - Y: ynot, - } - return or, andnot -} - -// if x represents a known old reflect type/value like *reflect.PtrType or reflect.ArrayOrSliceValue, -// reflectType returns the string form of that type. -func reflectType(x ast.Expr) string { - ptr, ok := x.(*ast.StarExpr) - if ok { - x = ptr.X - } - - sel, ok := x.(*ast.SelectorExpr) - if !ok || !isName(sel.X, "reflect") { - return "" - } - - var s = "reflect." - if ptr != nil { - s = "*reflect." - } - s += sel.Sel.Name - - if reflectKind[s] != nil { - return s - } - return "" -} - -// reflectWarnTypecheckStmt warns about statements -// of the form x, y = z.(T) for any old reflect type T. -// The last pass should have gotten them all, and if it didn't, -// the next pass is going to turn them into x, y = z. -func reflectWarnTypecheckStmt(n interface{}) { - as, ok := n.(*ast.AssignStmt) - if !ok || len(as.Lhs) != 2 || len(as.Rhs) != 1 { - return - } - ta, ok := as.Rhs[0].(*ast.TypeAssertExpr) - if !ok || reflectType(ta.Type) == "" { - return - } - warn(n.(ast.Node).Pos(), "unfixed reflect type check") -} - -// reflectFixAssert rewrites x.(T) to x for any old reflect type T. -func reflectFixAssert(n interface{}) bool { - ptr, ok := n.(*ast.Expr) - if ok { - ta, ok := (*ptr).(*ast.TypeAssertExpr) - if ok && reflectType(ta.Type) != "" { - *ptr = ta.X - return true - } - } - return false -} - -// Tables describing the transformations. - -// Description of old reflect API for partial type checking. -// We pretend the Elem method is on Type and Value instead -// of enumerating all the types it is actually on. -// Also, we pretend that ArrayType etc embeds Type for the -// purposes of describing the API. (In fact they embed commonType, -// which implements Type.) -var reflectTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "reflect.ArrayOrSliceType": {Embed: []string{"reflect.Type"}}, - "reflect.ArrayOrSliceValue": {Embed: []string{"reflect.Value"}}, - "reflect.ArrayType": {Embed: []string{"reflect.Type"}}, - "reflect.ArrayValue": {Embed: []string{"reflect.Value"}}, - "reflect.BoolType": {Embed: []string{"reflect.Type"}}, - "reflect.BoolValue": {Embed: []string{"reflect.Value"}}, - "reflect.ChanType": {Embed: []string{"reflect.Type"}}, - "reflect.ChanValue": { - Method: map[string]string{ - "Recv": "func() (reflect.Value, bool)", - "TryRecv": "func() (reflect.Value, bool)", - }, - Embed: []string{"reflect.Value"}, - }, - "reflect.ComplexType": {Embed: []string{"reflect.Type"}}, - "reflect.ComplexValue": {Embed: []string{"reflect.Value"}}, - "reflect.FloatType": {Embed: []string{"reflect.Type"}}, - "reflect.FloatValue": {Embed: []string{"reflect.Value"}}, - "reflect.FuncType": { - Method: map[string]string{ - "In": "func(int) reflect.Type", - "Out": "func(int) reflect.Type", - }, - Embed: []string{"reflect.Type"}, - }, - "reflect.FuncValue": { - Method: map[string]string{ - "Call": "func([]reflect.Value) []reflect.Value", - }, - }, - "reflect.IntType": {Embed: []string{"reflect.Type"}}, - "reflect.IntValue": {Embed: []string{"reflect.Value"}}, - "reflect.InterfaceType": {Embed: []string{"reflect.Type"}}, - "reflect.InterfaceValue": {Embed: []string{"reflect.Value"}}, - "reflect.MapType": { - Method: map[string]string{ - "Key": "func() reflect.Type", - }, - Embed: []string{"reflect.Type"}, - }, - "reflect.MapValue": { - Method: map[string]string{ - "Keys": "func() []reflect.Value", - }, - Embed: []string{"reflect.Value"}, - }, - "reflect.Method": { - Field: map[string]string{ - "Type": "*reflect.FuncType", - "Func": "*reflect.FuncValue", - }, - }, - "reflect.PtrType": {Embed: []string{"reflect.Type"}}, - "reflect.PtrValue": {Embed: []string{"reflect.Value"}}, - "reflect.SliceType": {Embed: []string{"reflect.Type"}}, - "reflect.SliceValue": { - Method: map[string]string{ - "Slice": "func(int, int) *reflect.SliceValue", - }, - Embed: []string{"reflect.Value"}, - }, - "reflect.StringType": {Embed: []string{"reflect.Type"}}, - "reflect.StringValue": {Embed: []string{"reflect.Value"}}, - "reflect.StructField": { - Field: map[string]string{ - "Type": "reflect.Type", - }, - }, - "reflect.StructType": { - Method: map[string]string{ - "Field": "func() reflect.StructField", - "FieldByIndex": "func() reflect.StructField", - "FieldByName": "func() reflect.StructField,bool", - "FieldByNameFunc": "func() reflect.StructField,bool", - }, - Embed: []string{"reflect.Type"}, - }, - "reflect.StructValue": { - Method: map[string]string{ - "Field": "func() reflect.Value", - "FieldByIndex": "func() reflect.Value", - "FieldByName": "func() reflect.Value", - "FieldByNameFunc": "func() reflect.Value", - }, - Embed: []string{"reflect.Value"}, - }, - "reflect.Type": { - Method: map[string]string{ - "Elem": "func() reflect.Type", - "Method": "func() reflect.Method", - }, - }, - "reflect.UintType": {Embed: []string{"reflect.Type"}}, - "reflect.UintValue": {Embed: []string{"reflect.Value"}}, - "reflect.UnsafePointerType": {Embed: []string{"reflect.Type"}}, - "reflect.UnsafePointerValue": {Embed: []string{"reflect.Value"}}, - "reflect.Value": { - Method: map[string]string{ - "Addr": "func() *reflect.PtrValue", - "Elem": "func() reflect.Value", - "Method": "func() *reflect.FuncValue", - "SetValue": "func(reflect.Value)", - }, - }, - }, - Func: map[string]string{ - "reflect.Append": "*reflect.SliceValue", - "reflect.AppendSlice": "*reflect.SliceValue", - "reflect.Indirect": "reflect.Value", - "reflect.MakeSlice": "*reflect.SliceValue", - "reflect.MakeChan": "*reflect.ChanValue", - "reflect.MakeMap": "*reflect.MapValue", - "reflect.MakeZero": "reflect.Value", - "reflect.NewValue": "reflect.Value", - "reflect.PtrTo": "*reflect.PtrType", - "reflect.Typeof": "reflect.Type", - }, -} - -var reflectRewriteMethod = map[string]map[string]string{ - // The type API didn't change much. - "*reflect.ChanType": {"Dir": "ChanDir"}, - "*reflect.FuncType": {"DotDotDot": "IsVariadic"}, - - // The value API has longer names to disambiguate - // methods with different signatures. - "reflect.ArrayOrSliceValue": { // interface, not pointer - "Elem": "Index", - }, - "*reflect.ArrayValue": { - "Elem": "Index", - }, - "*reflect.BoolValue": { - "Get": "Bool", - "Set": "SetBool", - }, - "*reflect.ChanValue": { - "Get": "Pointer", - }, - "*reflect.ComplexValue": { - "Get": "Complex", - "Set": "SetComplex", - "Overflow": "OverflowComplex", - }, - "*reflect.FloatValue": { - "Get": "Float", - "Set": "SetFloat", - "Overflow": "OverflowFloat", - }, - "*reflect.FuncValue": { - "Get": "Pointer", - }, - "*reflect.IntValue": { - "Get": "Int", - "Set": "SetInt", - "Overflow": "OverflowInt", - }, - "*reflect.InterfaceValue": { - "Get": "InterfaceData", - }, - "*reflect.MapValue": { - "Elem": "MapIndex", - "Get": "Pointer", - "Keys": "MapKeys", - "SetElem": "SetMapIndex", - }, - "*reflect.PtrValue": { - "Get": "Pointer", - }, - "*reflect.SliceValue": { - "Elem": "Index", - "Get": "Pointer", - }, - "*reflect.StringValue": { - "Get": "String", - "Set": "SetString", - }, - "*reflect.UintValue": { - "Get": "Uint", - "Set": "SetUint", - "Overflow": "OverflowUint", - }, - "*reflect.UnsafePointerValue": { - "Get": "Pointer", - "Set": "SetPointer", - }, -} - -var reflectKind = map[string][]string{ - "reflect.ArrayOrSliceType": {"Array", "Slice"}, // interface, not pointer - "*reflect.ArrayType": {"Array"}, - "*reflect.BoolType": {"Bool"}, - "*reflect.ChanType": {"Chan"}, - "*reflect.ComplexType": {"Complex64", "Complex128"}, - "*reflect.FloatType": {"Float32", "Float64"}, - "*reflect.FuncType": {"Func"}, - "*reflect.IntType": {"Int", "Int8", "Int16", "Int32", "Int64"}, - "*reflect.InterfaceType": {"Interface"}, - "*reflect.MapType": {"Map"}, - "*reflect.PtrType": {"Ptr"}, - "*reflect.SliceType": {"Slice"}, - "*reflect.StringType": {"String"}, - "*reflect.StructType": {"Struct"}, - "*reflect.UintType": {"Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"}, - "*reflect.UnsafePointerType": {"UnsafePointer"}, - - "reflect.ArrayOrSliceValue": {"Array", "Slice"}, // interface, not pointer - "*reflect.ArrayValue": {"Array"}, - "*reflect.BoolValue": {"Bool"}, - "*reflect.ChanValue": {"Chan"}, - "*reflect.ComplexValue": {"Complex64", "Complex128"}, - "*reflect.FloatValue": {"Float32", "Float64"}, - "*reflect.FuncValue": {"Func"}, - "*reflect.IntValue": {"Int", "Int8", "Int16", "Int32", "Int64"}, - "*reflect.InterfaceValue": {"Interface"}, - "*reflect.MapValue": {"Map"}, - "*reflect.PtrValue": {"Ptr"}, - "*reflect.SliceValue": {"Slice"}, - "*reflect.StringValue": {"String"}, - "*reflect.StructValue": {"Struct"}, - "*reflect.UintValue": {"Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"}, - "*reflect.UnsafePointerValue": {"UnsafePointer"}, -} - -var isReflectValue = map[string]bool{ - "reflect.ArrayOrSliceValue": true, // interface, not pointer - "*reflect.ArrayValue": true, - "*reflect.BoolValue": true, - "*reflect.ChanValue": true, - "*reflect.ComplexValue": true, - "*reflect.FloatValue": true, - "*reflect.FuncValue": true, - "*reflect.IntValue": true, - "*reflect.InterfaceValue": true, - "*reflect.MapValue": true, - "*reflect.PtrValue": true, - "*reflect.SliceValue": true, - "*reflect.StringValue": true, - "*reflect.StructValue": true, - "*reflect.UintValue": true, - "*reflect.UnsafePointerValue": true, - "reflect.Value": true, // interface, not pointer -} diff --git a/src/cmd/fix/reflect_test.go b/src/cmd/fix/reflect_test.go deleted file mode 100644 index 032cbc745..000000000 --- a/src/cmd/fix/reflect_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file - -package main - -import ( - "io/ioutil" - "log" - "path/filepath" -) - -func init() { - addTestCases(reflectTests(), reflectFn) -} - -func reflectTests() []testCase { - var tests []testCase - - names, _ := filepath.Glob("testdata/reflect.*.in") - for _, in := range names { - out := in[:len(in)-len(".in")] + ".out" - inb, err := ioutil.ReadFile(in) - if err != nil { - log.Fatal(err) - } - outb, err := ioutil.ReadFile(out) - if err != nil { - log.Fatal(err) - } - tests = append(tests, testCase{Name: in, In: string(inb), Out: string(outb)}) - } - - return tests -} diff --git a/src/cmd/fix/signal.go b/src/cmd/fix/signal.go deleted file mode 100644 index 5a583d41e..000000000 --- a/src/cmd/fix/signal.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "strings" -) - -func init() { - register(signalFix) -} - -var signalFix = fix{ - "signal", - "2011-06-29", - signal, - `Adapt code to types moved from os/signal to signal. - -http://codereview.appspot.com/4437091 -`, -} - -func signal(f *ast.File) (fixed bool) { - if !imports(f, "os/signal") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - - if !ok || !isTopName(s.X, "signal") { - return - } - - sel := s.Sel.String() - if sel == "Signal" || sel == "UnixSignal" || strings.HasPrefix(sel, "SIG") { - addImport(f, "os") - s.X = &ast.Ident{Name: "os"} - fixed = true - } - }) - - if fixed && !usesImport(f, "os/signal") { - deleteImport(f, "os/signal") - } - return -} diff --git a/src/cmd/fix/signal_test.go b/src/cmd/fix/signal_test.go deleted file mode 100644 index 7bca7d5c4..000000000 --- a/src/cmd/fix/signal_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(signalTests, signal) -} - -var signalTests = []testCase{ - { - Name: "signal.0", - In: `package main - -import ( - _ "a" - "os/signal" - _ "z" -) - -type T1 signal.UnixSignal -type T2 signal.Signal - -func f() { - _ = signal.SIGHUP - _ = signal.Incoming -} -`, - Out: `package main - -import ( - _ "a" - "os" - "os/signal" - _ "z" -) - -type T1 os.UnixSignal -type T2 os.Signal - -func f() { - _ = os.SIGHUP - _ = signal.Incoming -} -`, - }, - { - Name: "signal.1", - In: `package main - -import ( - "os" - "os/signal" -) - -func f() { - var _ os.Error - _ = signal.SIGHUP -} -`, - Out: `package main - -import "os" - -func f() { - var _ os.Error - _ = os.SIGHUP -} -`, - }, - { - Name: "signal.2", - In: `package main - -import "os" -import "os/signal" - -func f() { - var _ os.Error - _ = signal.SIGHUP -} -`, - Out: `package main - -import "os" - -func f() { - var _ os.Error - _ = os.SIGHUP -} -`, - }, -} diff --git a/src/cmd/fix/sorthelpers.go b/src/cmd/fix/sorthelpers.go deleted file mode 100644 index fa549313e..000000000 --- a/src/cmd/fix/sorthelpers.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(sorthelpersFix) -} - -var sorthelpersFix = fix{ - "sorthelpers", - "2011-07-08", - sorthelpers, - `Adapt code from sort.Sort[Ints|Float64s|Strings] to sort.[Ints|Float64s|Strings]. -`, -} - -func sorthelpers(f *ast.File) (fixed bool) { - if !imports(f, "sort") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - if !ok || !isTopName(s.X, "sort") { - return - } - - switch s.Sel.String() { - case "SortFloat64s": - s.Sel.Name = "Float64s" - case "SortInts": - s.Sel.Name = "Ints" - case "SortStrings": - s.Sel.Name = "Strings" - default: - return - } - - fixed = true - }) - - return -} diff --git a/src/cmd/fix/sorthelpers_test.go b/src/cmd/fix/sorthelpers_test.go deleted file mode 100644 index dd6b58e03..000000000 --- a/src/cmd/fix/sorthelpers_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(sorthelpersTests, sorthelpers) -} - -var sorthelpersTests = []testCase{ - { - Name: "sortslice.0", - In: `package main - -import ( - "sort" -) - -func main() { - var s []string - sort.SortStrings(s) - var i []ints - sort.SortInts(i) - var f []float64 - sort.SortFloat64s(f) -} -`, - Out: `package main - -import ( - "sort" -) - -func main() { - var s []string - sort.Strings(s) - var i []ints - sort.Ints(i) - var f []float64 - sort.Float64s(f) -} -`, - }, -} diff --git a/src/cmd/fix/sortslice.go b/src/cmd/fix/sortslice.go deleted file mode 100644 index 89267b847..000000000 --- a/src/cmd/fix/sortslice.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(sortsliceFix) -} - -var sortsliceFix = fix{ - "sortslice", - "2011-06-26", - sortslice, - `Adapt code from sort.[Float64|Int|String]Array to sort.[Float64|Int|String]Slice. - -http://codereview.appspot.com/4602054 -http://codereview.appspot.com/4639041 -`, -} - -func sortslice(f *ast.File) (fixed bool) { - if !imports(f, "sort") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - if !ok || !isTopName(s.X, "sort") { - return - } - - switch s.Sel.String() { - case "Float64Array": - s.Sel.Name = "Float64Slice" - case "IntArray": - s.Sel.Name = "IntSlice" - case "StringArray": - s.Sel.Name = "StringSlice" - default: - return - } - - fixed = true - }) - - return -} diff --git a/src/cmd/fix/sortslice_test.go b/src/cmd/fix/sortslice_test.go deleted file mode 100644 index 7b745a232..000000000 --- a/src/cmd/fix/sortslice_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(sortsliceTests, sortslice) -} - -var sortsliceTests = []testCase{ - { - Name: "sortslice.0", - In: `package main - -import ( - "sort" -) - -var _ = sort.Float64Array -var _ = sort.IntArray -var _ = sort.StringArray -`, - Out: `package main - -import ( - "sort" -) - -var _ = sort.Float64Slice -var _ = sort.IntSlice -var _ = sort.StringSlice -`, - }, -} diff --git a/src/cmd/fix/strconv.go b/src/cmd/fix/strconv.go deleted file mode 100644 index 6cd69020b..000000000 --- a/src/cmd/fix/strconv.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(strconvFix) -} - -var strconvFix = fix{ - "strconv", - "2011-12-01", - strconvFn, - `Convert to new strconv API. - -http://codereview.appspot.com/5434095 -http://codereview.appspot.com/5434069 -`, -} - -func strconvFn(f *ast.File) bool { - if !imports(f, "strconv") { - return false - } - - fixed := false - - walk(f, func(n interface{}) { - // Rename functions. - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) < 1 { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok || !isTopName(sel.X, "strconv") { - return - } - change := func(name string) { - fixed = true - sel.Sel.Name = name - } - add := func(s string) { - call.Args = append(call.Args, expr(s)) - } - switch sel.Sel.Name { - case "Atob": - change("ParseBool") - case "Atof32": - change("ParseFloat") - add("32") // bitSize - warn(call.Pos(), "rewrote strconv.Atof32(_) to strconv.ParseFloat(_, 32) but return value must be converted to float32") - case "Atof64": - change("ParseFloat") - add("64") // bitSize - case "AtofN": - change("ParseFloat") - case "Atoi": - // Atoi stayed as a convenience wrapper. - case "Atoi64": - change("ParseInt") - add("10") // base - add("64") // bitSize - case "Atoui": - change("ParseUint") - add("10") // base - add("0") // bitSize - warn(call.Pos(), "rewrote strconv.Atoui(_) to strconv.ParseUint(_, 10, 0) but return value must be converted to uint") - case "Atoui64": - change("ParseUint") - add("10") // base - add("64") // bitSize - case "Btoa": - change("FormatBool") - case "Btoi64": - change("ParseInt") - add("64") // bitSize - case "Btoui64": - change("ParseUint") - add("64") // bitSize - case "Ftoa32": - change("FormatFloat") - call.Args[0] = strconvRewrite("float32", "float64", call.Args[0]) - add("32") // bitSize - case "Ftoa64": - change("FormatFloat") - add("64") // bitSize - case "FtoaN": - change("FormatFloat") - case "Itoa": - // Itoa stayed as a convenience wrapper. - case "Itoa64": - change("FormatInt") - add("10") // base - case "Itob": - change("FormatInt") - call.Args[0] = strconvRewrite("int", "int64", call.Args[0]) - case "Itob64": - change("FormatInt") - case "Uitoa": - change("FormatUint") - call.Args[0] = strconvRewrite("uint", "uint64", call.Args[0]) - add("10") // base - case "Uitoa64": - change("FormatUint") - add("10") // base - case "Uitob": - change("FormatUint") - call.Args[0] = strconvRewrite("uint", "uint64", call.Args[0]) - case "Uitob64": - change("FormatUint") - } - }) - return fixed -} - -// rewrite from type t1 to type t2 -// If the expression x is of the form t1(_), use t2(_). Otherwise use t2(x). -func strconvRewrite(t1, t2 string, x ast.Expr) ast.Expr { - if call, ok := x.(*ast.CallExpr); ok && isTopName(call.Fun, t1) { - call.Fun.(*ast.Ident).Name = t2 - return x - } - return &ast.CallExpr{Fun: ast.NewIdent(t2), Args: []ast.Expr{x}} -} diff --git a/src/cmd/fix/strconv_test.go b/src/cmd/fix/strconv_test.go deleted file mode 100644 index 7fbd4e42e..000000000 --- a/src/cmd/fix/strconv_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(strconvTests, strconvFn) -} - -var strconvTests = []testCase{ - { - Name: "strconv.0", - In: `package main - -import "strconv" - -func f() { - foo.Atob("abc") - - strconv.Atob("true") - strconv.Btoa(false) - - strconv.Atof32("1.2") - strconv.Atof64("1.2") - strconv.AtofN("1.2", 64) - strconv.Ftoa32(1.2, 'g', 17) - strconv.Ftoa64(1.2, 'g', 17) - strconv.FtoaN(1.2, 'g', 17, 64) - - strconv.Atoi("3") - strconv.Atoi64("3") - strconv.Btoi64("1234", 5) - - strconv.Atoui("3") - strconv.Atoui64("3") - strconv.Btoui64("1234", 5) - - strconv.Itoa(123) - strconv.Itoa64(1234) - strconv.Itob(123, 5) - strconv.Itob64(1234, 5) - - strconv.Uitoa(123) - strconv.Uitoa64(1234) - strconv.Uitob(123, 5) - strconv.Uitob64(1234, 5) - - strconv.Uitoa(uint(x)) - strconv.Uitoa(f(x)) -} -`, - Out: `package main - -import "strconv" - -func f() { - foo.Atob("abc") - - strconv.ParseBool("true") - strconv.FormatBool(false) - - strconv.ParseFloat("1.2", 32) - strconv.ParseFloat("1.2", 64) - strconv.ParseFloat("1.2", 64) - strconv.FormatFloat(float64(1.2), 'g', 17, 32) - strconv.FormatFloat(1.2, 'g', 17, 64) - strconv.FormatFloat(1.2, 'g', 17, 64) - - strconv.Atoi("3") - strconv.ParseInt("3", 10, 64) - strconv.ParseInt("1234", 5, 64) - - strconv.ParseUint("3", 10, 0) - strconv.ParseUint("3", 10, 64) - strconv.ParseUint("1234", 5, 64) - - strconv.Itoa(123) - strconv.FormatInt(1234, 10) - strconv.FormatInt(int64(123), 5) - strconv.FormatInt(1234, 5) - - strconv.FormatUint(uint64(123), 10) - strconv.FormatUint(1234, 10) - strconv.FormatUint(uint64(123), 5) - strconv.FormatUint(1234, 5) - - strconv.FormatUint(uint64(x), 10) - strconv.FormatUint(uint64(f(x)), 10) -} -`, - }, -} diff --git a/src/cmd/fix/stringssplit.go b/src/cmd/fix/stringssplit.go deleted file mode 100644 index d89ecf039..000000000 --- a/src/cmd/fix/stringssplit.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register(stringssplitFix) -} - -var stringssplitFix = fix{ - "stringssplit", - "2011-06-28", - stringssplit, - `Restore strings.Split to its original meaning and add strings.SplitN. Bytes too. - -http://codereview.appspot.com/4661051 -`, -} - -func stringssplit(f *ast.File) bool { - if !imports(f, "bytes") && !imports(f, "strings") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - // func Split(s, sep string, n int) []string - // func SplitAfter(s, sep string, n int) []string - if !ok || len(call.Args) != 3 { - return - } - // Is this our function? - switch { - case isPkgDot(call.Fun, "bytes", "Split"): - case isPkgDot(call.Fun, "bytes", "SplitAfter"): - case isPkgDot(call.Fun, "strings", "Split"): - case isPkgDot(call.Fun, "strings", "SplitAfter"): - default: - return - } - - sel := call.Fun.(*ast.SelectorExpr) - args := call.Args - fixed = true // We're committed. - - // Is the last argument -1? If so, drop the arg. - // (Actually we just look for a negative integer literal.) - // Otherwise, Split->SplitN and keep the arg. - final := args[2] - if unary, ok := final.(*ast.UnaryExpr); ok && unary.Op == token.SUB { - if lit, ok := unary.X.(*ast.BasicLit); ok { - // Is it an integer? If so, it's a negative integer and that's what we're after. - if lit.Kind == token.INT { - // drop the last arg. - call.Args = args[0:2] - return - } - } - } - - // If not, rename and keep the argument list. - sel.Sel.Name += "N" - }) - return fixed -} diff --git a/src/cmd/fix/stringssplit_test.go b/src/cmd/fix/stringssplit_test.go deleted file mode 100644 index fa42b1bea..000000000 --- a/src/cmd/fix/stringssplit_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(stringssplitTests, stringssplit) -} - -var stringssplitTests = []testCase{ - { - Name: "stringssplit.0", - In: `package main - -import ( - "bytes" - "strings" -) - -func f() { - bytes.Split(a, b, c) - bytes.Split(a, b, -1) - bytes.SplitAfter(a, b, c) - bytes.SplitAfter(a, b, -1) - strings.Split(a, b, c) - strings.Split(a, b, -1) - strings.SplitAfter(a, b, c) - strings.SplitAfter(a, b, -1) -} -`, - Out: `package main - -import ( - "bytes" - "strings" -) - -func f() { - bytes.SplitN(a, b, c) - bytes.Split(a, b) - bytes.SplitAfterN(a, b, c) - bytes.SplitAfter(a, b) - strings.SplitN(a, b, c) - strings.Split(a, b) - strings.SplitAfterN(a, b, c) - strings.SplitAfter(a, b) -} -`, - }, -} diff --git a/src/cmd/fix/template.go b/src/cmd/fix/template.go deleted file mode 100644 index a3dd1440b..000000000 --- a/src/cmd/fix/template.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(templateFix) -} - -var templateFix = fix{ - "template", - "2011-11-22", - template, - `Rewrite calls to template.ParseFile to template.ParseFiles - -http://codereview.appspot.com/5433048 -`, -} - -var templateSetGlobals = []string{ - "ParseSetFiles", - "ParseSetGlob", - "ParseTemplateFiles", - "ParseTemplateGlob", - "Set", - "SetMust", -} - -var templateSetMethods = []string{ - "ParseSetFiles", - "ParseSetGlob", - "ParseTemplateFiles", - "ParseTemplateGlob", -} - -var templateTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "template.Template": { - Method: map[string]string{ - "Funcs": "func() *template.Template", - "Delims": "func() *template.Template", - "Parse": "func() (*template.Template, error)", - "ParseFile": "func() (*template.Template, error)", - "ParseInSet": "func() (*template.Template, error)", - }, - }, - "template.Set": { - Method: map[string]string{ - "ParseSetFiles": "func() (*template.Set, error)", - "ParseSetGlob": "func() (*template.Set, error)", - "ParseTemplateFiles": "func() (*template.Set, error)", - "ParseTemplateGlob": "func() (*template.Set, error)", - }, - }, - }, - - Func: map[string]string{ - "template.New": "*template.Template", - "template.Must": "(*template.Template, error)", - "template.SetMust": "(*template.Set, error)", - }, -} - -func template(f *ast.File) bool { - if !imports(f, "text/template") && !imports(f, "html/template") { - return false - } - - fixed := false - - typeof, _ := typecheck(templateTypeConfig, f) - - // Now update the names used by importers. - walk(f, func(n interface{}) { - if sel, ok := n.(*ast.SelectorExpr); ok { - // Reference to top-level function ParseFile. - if isPkgDot(sel, "template", "ParseFile") { - sel.Sel.Name = "ParseFiles" - fixed = true - return - } - // Reference to ParseFiles method. - if typeof[sel.X] == "*template.Template" && sel.Sel.Name == "ParseFile" { - sel.Sel.Name = "ParseFiles" - fixed = true - return - } - // The Set type and its functions are now gone. - for _, name := range templateSetGlobals { - if isPkgDot(sel, "template", name) { - warn(sel.Pos(), "reference to template.%s must be fixed manually", name) - return - } - } - // The methods of Set are now gone. - for _, name := range templateSetMethods { - if typeof[sel.X] == "*template.Set" && sel.Sel.Name == name { - warn(sel.Pos(), "reference to template.*Set.%s must be fixed manually", name) - return - } - } - } - }) - - return fixed -} diff --git a/src/cmd/fix/template_test.go b/src/cmd/fix/template_test.go deleted file mode 100644 index f713a2901..000000000 --- a/src/cmd/fix/template_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(templateTests, template) -} - -var templateTests = []testCase{ - { - Name: "template.0", - In: `package main - -import ( - "text/template" -) - -func f() { - template.ParseFile(a) - var t template.Template - x, y := template.ParseFile() - template.New("x").Funcs(m).ParseFile(a) // chained method - // Output should complain about these as functions or methods. - var s *template.Set - s.ParseSetFiles(a) - template.ParseSetGlob(a) - s.ParseTemplateFiles(a) - template.ParseTemplateGlob(a) - x := template.SetMust(a()) -} -`, - Out: `package main - -import ( - "text/template" -) - -func f() { - template.ParseFiles(a) - var t template.Template - x, y := template.ParseFiles() - template.New("x").Funcs(m).ParseFiles(a) // chained method - // Output should complain about these as functions or methods. - var s *template.Set - s.ParseSetFiles(a) - template.ParseSetGlob(a) - s.ParseTemplateFiles(a) - template.ParseTemplateGlob(a) - x := template.SetMust(a()) -} -`, - }, -} diff --git a/src/cmd/fix/testdata/reflect.encoder.go.in b/src/cmd/fix/testdata/reflect.encoder.go.in index 0202d79ac..702f6dc06 100644 --- a/src/cmd/fix/testdata/reflect.encoder.go.in +++ b/src/cmd/fix/testdata/reflect.encoder.go.in @@ -120,7 +120,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp return true } -// sendType sends the type info to the other side, if necessary. +// sendType sends the type info to the other side, if necessary. func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { ut := userType(origt) if ut.isGobEncoder { diff --git a/src/cmd/fix/testdata/reflect.encoder.go.out b/src/cmd/fix/testdata/reflect.encoder.go.out index 925d39301..f1a7b98f1 100644 --- a/src/cmd/fix/testdata/reflect.encoder.go.out +++ b/src/cmd/fix/testdata/reflect.encoder.go.out @@ -120,7 +120,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp return true } -// sendType sends the type info to the other side, if necessary. +// sendType sends the type info to the other side, if necessary. func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { ut := userType(origt) if ut.isGobEncoder { diff --git a/src/cmd/fix/testdata/reflect.export.go.in b/src/cmd/fix/testdata/reflect.export.go.in index ce7940b29..722387ac5 100644 --- a/src/cmd/fix/testdata/reflect.export.go.in +++ b/src/cmd/fix/testdata/reflect.export.go.in @@ -162,7 +162,7 @@ func (client *expClient) run() { // all messages <=N have been seen by the recipient. We check anyway. expLog("sequence out of order:", client.ackNum, hdr.SeqNum) } - if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. + if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. client.ackNum = hdr.SeqNum } client.mu.Unlock() diff --git a/src/cmd/fix/testdata/reflect.export.go.out b/src/cmd/fix/testdata/reflect.export.go.out index 7bd73c5e7..d1324f346 100644 --- a/src/cmd/fix/testdata/reflect.export.go.out +++ b/src/cmd/fix/testdata/reflect.export.go.out @@ -162,7 +162,7 @@ func (client *expClient) run() { // all messages <=N have been seen by the recipient. We check anyway. expLog("sequence out of order:", client.ackNum, hdr.SeqNum) } - if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. + if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. client.ackNum = hdr.SeqNum } client.mu.Unlock() diff --git a/src/cmd/fix/testdata/reflect.print.go.in b/src/cmd/fix/testdata/reflect.print.go.in index 6c9b8e4f9..14cf2b215 100644 --- a/src/cmd/fix/testdata/reflect.print.go.in +++ b/src/cmd/fix/testdata/reflect.print.go.in @@ -182,7 +182,7 @@ func Sprintf(format string, a ...interface{}) string { return s } -// Errorf formats according to a format specifier and returns the string +// Errorf formats according to a format specifier and returns the string // converted to an os.ErrorString, which satisfies the os.Error interface. func Errorf(format string, a ...interface{}) os.Error { return os.NewError(Sprintf(format, a...)) diff --git a/src/cmd/fix/testdata/reflect.print.go.out b/src/cmd/fix/testdata/reflect.print.go.out index b475a2ae1..e4e4c7368 100644 --- a/src/cmd/fix/testdata/reflect.print.go.out +++ b/src/cmd/fix/testdata/reflect.print.go.out @@ -182,7 +182,7 @@ func Sprintf(format string, a ...interface{}) string { return s } -// Errorf formats according to a format specifier and returns the string +// Errorf formats according to a format specifier and returns the string // converted to an os.ErrorString, which satisfies the os.Error interface. func Errorf(format string, a ...interface{}) os.Error { return os.NewError(Sprintf(format, a...)) diff --git a/src/cmd/fix/timefileinfo.go b/src/cmd/fix/timefileinfo.go deleted file mode 100644 index b2ea23d8f..000000000 --- a/src/cmd/fix/timefileinfo.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" - "strings" -) - -func init() { - register(timefileinfoFix) -} - -var timefileinfoFix = fix{ - "time+fileinfo", - "2011-11-29", - timefileinfo, - `Rewrite for new time and os.FileInfo APIs. - -This fix applies some of the more mechanical changes, -but most code will still need manual cleanup. - -http://codereview.appspot.com/5392041 -http://codereview.appspot.com/5416060 -`, -} - -var timefileinfoTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "os.File": { - Method: map[string]string{ - "Readdir": "func() []*os.FileInfo", - "Stat": "func() (*os.FileInfo, error)", - }, - }, - "time.Time": { - Method: map[string]string{ - "Seconds": "time.raw", - "Nanoseconds": "time.raw", - }, - }, - }, - Func: map[string]string{ - "ioutil.ReadDir": "([]*os.FileInfo, error)", - "os.Stat": "(*os.FileInfo, error)", - "os.Lstat": "(*os.FileInfo, error)", - "time.LocalTime": "*time.Time", - "time.UTC": "*time.Time", - "time.SecondsToLocalTime": "*time.Time", - "time.SecondsToUTC": "*time.Time", - "time.NanosecondsToLocalTime": "*time.Time", - "time.NanosecondsToUTC": "*time.Time", - "time.Parse": "(*time.Time, error)", - "time.Nanoseconds": "time.raw", - "time.Seconds": "time.raw", - }, -} - -// timefileinfoIsOld reports whether f has evidence of being -// "old code", from before the API changes. Evidence means: -// -// a mention of *os.FileInfo (the pointer) -// a mention of *time.Time (the pointer) -// a mention of old functions from package time -// an attempt to call time.UTC -// -func timefileinfoIsOld(f *ast.File, typeof map[interface{}]string) bool { - old := false - - // called records the expressions that appear as - // the function part of a function call, so that - // we can distinguish a ref to the possibly new time.UTC - // from the definitely old time.UTC() function call. - called := make(map[interface{}]bool) - - before := func(n interface{}) { - if old { - return - } - if star, ok := n.(*ast.StarExpr); ok { - if isPkgDot(star.X, "os", "FileInfo") || isPkgDot(star.X, "time", "Time") { - old = true - return - } - } - if sel, ok := n.(*ast.SelectorExpr); ok { - if isTopName(sel.X, "time") { - if timefileinfoOldTimeFunc[sel.Sel.Name] { - old = true - return - } - } - if typeof[sel.X] == "os.FileInfo" || typeof[sel.X] == "*os.FileInfo" { - switch sel.Sel.Name { - case "Mtime_ns", "IsDirectory", "IsRegular": - old = true - return - case "Name", "Mode", "Size": - if !called[sel] { - old = true - return - } - } - } - } - call, ok := n.(*ast.CallExpr) - if ok && isPkgDot(call.Fun, "time", "UTC") { - old = true - return - } - if ok { - called[call.Fun] = true - } - } - walkBeforeAfter(f, before, nop) - return old -} - -var timefileinfoOldTimeFunc = map[string]bool{ - "LocalTime": true, - "SecondsToLocalTime": true, - "SecondsToUTC": true, - "NanosecondsToLocalTime": true, - "NanosecondsToUTC": true, - "Seconds": true, - "Nanoseconds": true, -} - -var isTimeNow = map[string]bool{ - "LocalTime": true, - "UTC": true, - "Seconds": true, - "Nanoseconds": true, -} - -func timefileinfo(f *ast.File) bool { - if !imports(f, "os") && !imports(f, "time") && !imports(f, "io/ioutil") { - return false - } - - typeof, _ := typecheck(timefileinfoTypeConfig, f) - - if !timefileinfoIsOld(f, typeof) { - return false - } - - fixed := false - walk(f, func(n interface{}) { - p, ok := n.(*ast.Expr) - if !ok { - return - } - nn := *p - - // Rewrite *os.FileInfo and *time.Time to drop the pointer. - if star, ok := nn.(*ast.StarExpr); ok { - if isPkgDot(star.X, "os", "FileInfo") || isPkgDot(star.X, "time", "Time") { - fixed = true - *p = star.X - return - } - } - - // Rewrite old time API calls to new calls. - // The code will still not compile after this edit, - // but the compiler will catch that, and the replacement - // code will be the correct functions to use in the new API. - if sel, ok := nn.(*ast.SelectorExpr); ok && isTopName(sel.X, "time") { - fn := sel.Sel.Name - if fn == "LocalTime" || fn == "Seconds" || fn == "Nanoseconds" { - fixed = true - sel.Sel.Name = "Now" - return - } - } - - if call, ok := nn.(*ast.CallExpr); ok { - if sel, ok := call.Fun.(*ast.SelectorExpr); ok { - // Rewrite time.UTC but only when called (there's a new time.UTC var now). - if isPkgDot(sel, "time", "UTC") { - fixed = true - sel.Sel.Name = "Now" - // rewrite time.Now() into time.Now().UTC() - *p = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: call, - Sel: ast.NewIdent("UTC"), - }, - } - return - } - - // Rewrite conversions. - if ok && isTopName(sel.X, "time") && len(call.Args) == 1 { - fn := sel.Sel.Name - switch fn { - case "SecondsToLocalTime", "SecondsToUTC", - "NanosecondsToLocalTime", "NanosecondsToUTC": - fixed = true - sel.Sel.Name = "Unix" - call.Args = append(call.Args, nil) - if strings.HasPrefix(fn, "Seconds") { - // Unix(sec, 0) - call.Args[1] = ast.NewIdent("0") - } else { - // Unix(0, nsec) - call.Args[1] = call.Args[0] - call.Args[0] = ast.NewIdent("0") - } - if strings.HasSuffix(fn, "ToUTC") { - // rewrite call into call.UTC() - *p = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: call, - Sel: ast.NewIdent("UTC"), - }, - } - } - return - } - } - - // Rewrite method calls. - switch typeof[sel.X] { - case "*time.Time", "time.Time": - switch sel.Sel.Name { - case "Seconds": - fixed = true - sel.Sel.Name = "Unix" - return - case "Nanoseconds": - fixed = true - sel.Sel.Name = "UnixNano" - return - } - - case "*os.FileInfo", "os.FileInfo": - switch sel.Sel.Name { - case "IsDirectory": - fixed = true - sel.Sel.Name = "IsDir" - return - case "IsRegular": - fixed = true - sel.Sel.Name = "IsDir" - *p = &ast.UnaryExpr{ - Op: token.NOT, - X: call, - } - return - } - } - } - } - - // Rewrite subtraction of two times. - // Cannot handle +=/-=. - if bin, ok := nn.(*ast.BinaryExpr); ok && - bin.Op == token.SUB && - (typeof[bin.X] == "time.raw" || typeof[bin.Y] == "time.raw") { - fixed = true - *p = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: bin.X, - Sel: ast.NewIdent("Sub"), - }, - Args: []ast.Expr{bin.Y}, - } - } - - // Rewrite field references for os.FileInfo. - if sel, ok := nn.(*ast.SelectorExpr); ok { - if typ := typeof[sel.X]; typ == "*os.FileInfo" || typ == "os.FileInfo" { - addCall := false - switch sel.Sel.Name { - case "Name", "Size", "Mode": - fixed = true - addCall = true - case "Mtime_ns": - fixed = true - sel.Sel.Name = "ModTime" - addCall = true - } - if addCall { - *p = &ast.CallExpr{ - Fun: sel, - } - return - } - } - } - }) - - return true -} diff --git a/src/cmd/fix/timefileinfo_test.go b/src/cmd/fix/timefileinfo_test.go deleted file mode 100644 index 6573b8545..000000000 --- a/src/cmd/fix/timefileinfo_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(timefileinfoTests, timefileinfo) -} - -var timefileinfoTests = []testCase{ - { - Name: "timefileinfo.0", - In: `package main - -import "os" - -func main() { - st, _ := os.Stat("/etc/passwd") - _ = st.Name -} -`, - Out: `package main - -import "os" - -func main() { - st, _ := os.Stat("/etc/passwd") - _ = st.Name() -} -`, - }, - { - Name: "timefileinfo.1", - In: `package main - -import "os" - -func main() { - st, _ := os.Stat("/etc/passwd") - _ = st.Size - _ = st.Mode - _ = st.Mtime_ns - _ = st.IsDirectory() - _ = st.IsRegular() -} -`, - Out: `package main - -import "os" - -func main() { - st, _ := os.Stat("/etc/passwd") - _ = st.Size() - _ = st.Mode() - _ = st.ModTime() - _ = st.IsDir() - _ = !st.IsDir() -} -`, - }, - { - Name: "timefileinfo.2", - In: `package main - -import "os" - -func f(st *os.FileInfo) { - _ = st.Name - _ = st.Size - _ = st.Mode - _ = st.Mtime_ns - _ = st.IsDirectory() - _ = st.IsRegular() -} -`, - Out: `package main - -import "os" - -func f(st os.FileInfo) { - _ = st.Name() - _ = st.Size() - _ = st.Mode() - _ = st.ModTime() - _ = st.IsDir() - _ = !st.IsDir() -} -`, - }, - { - Name: "timefileinfo.3", - In: `package main - -import "time" - -func main() { - _ = time.Seconds() - _ = time.Nanoseconds() - _ = time.LocalTime() - _ = time.UTC() - _ = time.SecondsToLocalTime(sec) - _ = time.SecondsToUTC(sec) - _ = time.NanosecondsToLocalTime(nsec) - _ = time.NanosecondsToUTC(nsec) -} -`, - Out: `package main - -import "time" - -func main() { - _ = time.Now() - _ = time.Now() - _ = time.Now() - _ = time.Now().UTC() - _ = time.Unix(sec, 0) - _ = time.Unix(sec, 0).UTC() - _ = time.Unix(0, nsec) - _ = time.Unix(0, nsec).UTC() -} -`, - }, - { - Name: "timefileinfo.4", - In: `package main - -import "time" - -func f(*time.Time) - -func main() { - t := time.LocalTime() - _ = t.Seconds() - _ = t.Nanoseconds() - - t1 := time.Nanoseconds() - f(nil) - t2 := time.Nanoseconds() - dt := t2 - t1 -} -`, - Out: `package main - -import "time" - -func f(time.Time) - -func main() { - t := time.Now() - _ = t.Unix() - _ = t.UnixNano() - - t1 := time.Now() - f(nil) - t2 := time.Now() - dt := t2.Sub(t1) -} -`, - }, - { - Name: "timefileinfo.5", // test for issues 1505, 2636 - In: `package main - -import ( - "fmt" - "time" -) - -func main() { - fmt.Println(time.SecondsToUTC(now)) // this comment must not introduce an illegal linebreak -} -`, - Out: `package main - -import ( - "fmt" - "time" -) - -func main() { - fmt.Println(time.Unix(now, 0).UTC( // this comment must not introduce an illegal linebreak - )) -} -`, - }, -} diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go index 8e54314d1..d33b69fdd 100644 --- a/src/cmd/fix/typecheck.go +++ b/src/cmd/fix/typecheck.go @@ -17,7 +17,7 @@ import ( // // The fact that it is partial is very important: the input is // an AST and a description of some type information to -// assume about one or more packages, but not all the +// assume about one or more packages, but not all the // packages that the program imports. The checker is // expected to do as much as it can with what it has been // given. There is not enough information supplied to do @@ -395,9 +395,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a // Field or method. name := n.Sel.Name if t := typeof[n.X]; t != "" { - if strings.HasPrefix(t, "*") { - t = t[1:] // implicit * - } + t = strings.TrimPrefix(t, "*") // implicit * if typ := cfg.Type[t]; typ != nil { if t := typ.dot(cfg, name); t != "" { typeof[n] = t diff --git a/src/cmd/fix/url.go b/src/cmd/fix/url.go deleted file mode 100644 index 49aac739b..000000000 --- a/src/cmd/fix/url.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(urlFix) -} - -var urlFix = fix{ - "url", - "2011-08-17", - url, - `Move the URL pieces of package http into a new package, url. - -http://codereview.appspot.com/4893043 -`, -} - -var urlRenames = []struct{ in, out string }{ - {"URL", "URL"}, - {"ParseURL", "Parse"}, - {"ParseURLReference", "ParseWithReference"}, - {"ParseQuery", "ParseQuery"}, - {"Values", "Values"}, - {"URLEscape", "QueryEscape"}, - {"URLUnescape", "QueryUnescape"}, - {"URLError", "Error"}, - {"URLEscapeError", "EscapeError"}, -} - -func url(f *ast.File) bool { - if imports(f, "url") || !imports(f, "http") { - return false - } - - fixed := false - - // Update URL code. - urlWalk := func(n interface{}) { - // Is it an identifier? - if ident, ok := n.(*ast.Ident); ok && ident.Name == "url" { - ident.Name = "url_" - return - } - // Parameter and result names. - if fn, ok := n.(*ast.FuncType); ok { - fixed = urlDoFields(fn.Params) || fixed - fixed = urlDoFields(fn.Results) || fixed - } - } - - // Fix up URL code and add import, at most once. - fix := func() { - if fixed { - return - } - addImport(f, "url") - walkBeforeAfter(f, urlWalk, nop) - fixed = true - } - - walk(f, func(n interface{}) { - // Rename functions and methods. - if expr, ok := n.(ast.Expr); ok { - for _, s := range urlRenames { - if isPkgDot(expr, "http", s.in) { - fix() - expr.(*ast.SelectorExpr).X.(*ast.Ident).Name = "url" - expr.(*ast.SelectorExpr).Sel.Name = s.out - return - } - } - } - }) - - // Remove the http import if no longer needed. - if fixed && !usesImport(f, "http") { - deleteImport(f, "http") - } - - return fixed -} - -func urlDoFields(list *ast.FieldList) (fixed bool) { - if list == nil { - return - } - for _, field := range list.List { - for _, ident := range field.Names { - if ident.Name == "url" { - fixed = true - ident.Name = "url_" - } - } - } - return -} diff --git a/src/cmd/fix/url2.go b/src/cmd/fix/url2.go deleted file mode 100644 index 5fd05ad2a..000000000 --- a/src/cmd/fix/url2.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(url2Fix) -} - -var url2Fix = fix{ - "url2", - "2012-02-16", - url2, - `Rename some functions in net/url. - -http://codereview.appspot.com/5671061 -`, -} - -func url2(f *ast.File) bool { - if !imports(f, "net/url") { - return false - } - - fixed := false - - walk(f, func(n interface{}) { - // Rename functions and methods. - sel, ok := n.(*ast.SelectorExpr) - if !ok { - return - } - if !isTopName(sel.X, "url") { - return - } - if sel.Sel.Name == "ParseWithReference" { - sel.Sel.Name = "ParseWithFragment" - fixed = true - } - }) - - return fixed -} diff --git a/src/cmd/fix/url2_test.go b/src/cmd/fix/url2_test.go deleted file mode 100644 index c68dd88f1..000000000 --- a/src/cmd/fix/url2_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(url2Tests, url2) -} - -var url2Tests = []testCase{ - { - Name: "url2.0", - In: `package main - -import "net/url" - -func f() { - url.ParseWithReference("foo") -} -`, - Out: `package main - -import "net/url" - -func f() { - url.ParseWithFragment("foo") -} -`, - }, -} diff --git a/src/cmd/fix/url_test.go b/src/cmd/fix/url_test.go deleted file mode 100644 index 39827f780..000000000 --- a/src/cmd/fix/url_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(urlTests, url) -} - -var urlTests = []testCase{ - { - Name: "url.0", - In: `package main - -import ( - "http" -) - -func f() { - var _ http.URL - http.ParseURL(a) - http.ParseURLReference(a) - http.ParseQuery(a) - m := http.Values{a: b} - http.URLEscape(a) - http.URLUnescape(a) - var x http.URLError - var y http.URLEscapeError -} -`, - Out: `package main - -import "url" - -func f() { - var _ url.URL - url.Parse(a) - url.ParseWithReference(a) - url.ParseQuery(a) - m := url.Values{a: b} - url.QueryEscape(a) - url.QueryUnescape(a) - var x url.Error - var y url.EscapeError -} -`, - }, - { - Name: "url.1", - In: `package main - -import ( - "http" -) - -func f() { - http.ParseURL(a) - var x http.Request -} -`, - Out: `package main - -import ( - "http" - "url" -) - -func f() { - url.Parse(a) - var x http.Request -} -`, - }, - { - Name: "url.2", - In: `package main - -import ( - "http" -) - -type U struct{ url int } -type M map[int]int - -func f() { - http.ParseURL(a) - var url = 23 - url, x := 45, y - _ = U{url: url} - _ = M{url + 1: url} -} - -func g(url string) string { - return url -} - -func h() (url string) { - return url -} -`, - Out: `package main - -import "url" - -type U struct{ url_ int } -type M map[int]int - -func f() { - url.Parse(a) - var url_ = 23 - url_, x := 45, y - _ = U{url_: url_} - _ = M{url_ + 1: url_} -} - -func g(url_ string) string { - return url_ -} - -func h() (url_ string) { - return url_ -} -`, - }, - { - Name: "url.3", - In: `package main - -import "http" - -type U struct{ url string } - -func f() { - var u U - u.url = "x" -} - -func (url *T) m() string { - return url -} -`, - Out: `package main - -import "http" - -type U struct{ url string } - -func f() { - var u U - u.url = "x" -} - -func (url *T) m() string { - return url -} -`, - }, -} diff --git a/src/cmd/fix/xmlapi.go b/src/cmd/fix/xmlapi.go deleted file mode 100644 index e74425914..000000000 --- a/src/cmd/fix/xmlapi.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(xmlapiFix) -} - -var xmlapiFix = fix{ - "xmlapi", - "2012-01-23", - xmlapi, - ` - Make encoding/xml's API look more like the rest of the encoding packages. - -http://codereview.appspot.com/5574053 -`, -} - -var xmlapiTypeConfig = &TypeConfig{ - Func: map[string]string{ - "xml.NewParser": "*xml.Parser", - "os.Open": "*os.File", - "os.OpenFile": "*os.File", - "bytes.NewBuffer": "*bytes.Buffer", - "bytes.NewBufferString": "*bytes.Buffer", - "bufio.NewReader": "*bufio.Reader", - "bufio.NewReadWriter": "*bufio.ReadWriter", - }, -} - -var isReader = map[string]bool{ - "*os.File": true, - "*bytes.Buffer": true, - "*bufio.Reader": true, - "*bufio.ReadWriter": true, - "io.Reader": true, -} - -func xmlapi(f *ast.File) bool { - if !imports(f, "encoding/xml") { - return false - } - - typeof, _ := typecheck(xmlapiTypeConfig, f) - - fixed := false - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - if ok && typeof[s.X] == "*xml.Parser" && s.Sel.Name == "Unmarshal" { - s.Sel.Name = "DecodeElement" - fixed = true - return - } - if ok && isPkgDot(s, "xml", "Parser") { - s.Sel.Name = "Decoder" - fixed = true - return - } - - call, ok := n.(*ast.CallExpr) - if !ok { - return - } - switch { - case len(call.Args) == 2 && isPkgDot(call.Fun, "xml", "Marshal"): - *call = xmlMarshal(call.Args) - fixed = true - case len(call.Args) == 2 && isPkgDot(call.Fun, "xml", "Unmarshal"): - if isReader[typeof[call.Args[0]]] { - *call = xmlUnmarshal(call.Args) - fixed = true - } - case len(call.Args) == 1 && isPkgDot(call.Fun, "xml", "NewParser"): - sel := call.Fun.(*ast.SelectorExpr).Sel - sel.Name = "NewDecoder" - fixed = true - } - }) - return fixed -} - -func xmlMarshal(args []ast.Expr) ast.CallExpr { - return xmlCallChain("NewEncoder", "Encode", args) -} - -func xmlUnmarshal(args []ast.Expr) ast.CallExpr { - return xmlCallChain("NewDecoder", "Decode", args) -} - -func xmlCallChain(first, second string, args []ast.Expr) ast.CallExpr { - return ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent("xml"), - Sel: ast.NewIdent(first), - }, - Args: args[:1], - }, - Sel: ast.NewIdent(second), - }, - Args: args[1:2], - } -} diff --git a/src/cmd/fix/xmlapi_test.go b/src/cmd/fix/xmlapi_test.go deleted file mode 100644 index 6486c8124..000000000 --- a/src/cmd/fix/xmlapi_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(xmlapiTests, xmlapi) -} - -var xmlapiTests = []testCase{ - { - Name: "xmlapi.0", - In: `package main - -import "encoding/xml" - -func f() { - xml.Marshal(a, b) - xml.Unmarshal(a, b) - - var buf1 bytes.Buffer - buf2 := &bytes.Buffer{} - buf3 := bytes.NewBuffer(data) - buf4 := bytes.NewBufferString(data) - buf5 := bufio.NewReader(r) - xml.Unmarshal(&buf1, v) - xml.Unmarshal(buf2, v) - xml.Unmarshal(buf3, v) - xml.Unmarshal(buf4, v) - xml.Unmarshal(buf5, v) - - f := os.Open("foo.xml") - xml.Unmarshal(f, v) - - p1 := xml.NewParser(stream) - p1.Unmarshal(v, start) - - var p2 *xml.Parser - p2.Unmarshal(v, start) -} - -func g(r io.Reader, f *os.File, b []byte) { - xml.Unmarshal(r, v) - xml.Unmarshal(f, v) - xml.Unmarshal(b, v) -} -`, - Out: `package main - -import "encoding/xml" - -func f() { - xml.NewEncoder(a).Encode(b) - xml.Unmarshal(a, b) - - var buf1 bytes.Buffer - buf2 := &bytes.Buffer{} - buf3 := bytes.NewBuffer(data) - buf4 := bytes.NewBufferString(data) - buf5 := bufio.NewReader(r) - xml.NewDecoder(&buf1).Decode(v) - xml.NewDecoder(buf2).Decode(v) - xml.NewDecoder(buf3).Decode(v) - xml.NewDecoder(buf4).Decode(v) - xml.NewDecoder(buf5).Decode(v) - - f := os.Open("foo.xml") - xml.NewDecoder(f).Decode(v) - - p1 := xml.NewDecoder(stream) - p1.DecodeElement(v, start) - - var p2 *xml.Decoder - p2.DecodeElement(v, start) -} - -func g(r io.Reader, f *os.File, b []byte) { - xml.NewDecoder(r).Decode(v) - xml.NewDecoder(f).Decode(v) - xml.Unmarshal(b, v) -} -`, - }, -} diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index 6982bbe56..be9f552f6 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -15,8 +15,8 @@ static int defercalc; -uint32 -rnd(uint32 o, uint32 r) +vlong +rnd(vlong o, vlong r) { if(r < 1 || r > 8 || (r&(r-1)) != 0) fatal("rnd"); @@ -54,6 +54,11 @@ widstruct(Type *errtype, Type *t, vlong o, int flag) for(f=t->type; f!=T; f=f->down) { if(f->etype != TFIELD) fatal("widstruct: not TFIELD: %lT", f); + if(f->type == T) { + // broken field, just skip it so that other valid fields + // get a width. + continue; + } dowidth(f->type); if(f->type->align > maxalign) maxalign = f->type->align; @@ -248,8 +253,12 @@ dowidth(Type *t) checkwidth(t->type); t->align = widthptr; } - else if(t->bound == -100) - yyerror("use of [...] array outside of array literal"); + else if(t->bound == -100) { + if(!t->broke) { + yyerror("use of [...] array outside of array literal"); + t->broke = 1; + } + } else fatal("dowidth %T", t); // probably [...]T break; @@ -607,7 +616,7 @@ typeinit(void) fatal("typeinit: %s already defined", s->name); t = typ(etype); - t->sym = s; + t->sym = s1; dowidth(t); types[etype] = t; @@ -615,12 +624,12 @@ typeinit(void) } Array_array = rnd(0, widthptr); - Array_nel = rnd(Array_array+widthptr, types[TUINT32]->width); - Array_cap = rnd(Array_nel+types[TUINT32]->width, types[TUINT32]->width); - sizeof_Array = rnd(Array_cap+types[TUINT32]->width, widthptr); + Array_nel = rnd(Array_array+widthptr, widthint); + Array_cap = rnd(Array_nel+widthint, widthint); + sizeof_Array = rnd(Array_cap+widthint, widthptr); // string is same as slice wo the cap - sizeof_String = rnd(Array_nel+types[TUINT32]->width, widthptr); + sizeof_String = rnd(Array_nel+widthint, widthptr); dowidth(types[TSTRING]); dowidth(idealstring); diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors index 0f865d086..8886a8e52 100755 --- a/src/cmd/gc/bisonerrors +++ b/src/cmd/gc/bisonerrors @@ -41,9 +41,9 @@ grammar && NF>0 { } # In state dumps, record shift/reduce actions. -bison && /^state 0/ { grammar = 0; states = 1 } +bison && /^[Ss]tate 0/ { grammar = 0; states = 1 } -states && /^state / { state = $2 } +states && /^[Ss]tate / { state = $2 } states { statetext[state] = statetext[state] $0 "\n" } states && / shift/ { diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c index ca3d6670d..7de448d1e 100644 --- a/src/cmd/gc/builtin.c +++ b/src/cmd/gc/builtin.c @@ -1,118 +1,119 @@ char *runtimeimport = "package runtime\n" "import runtime \"runtime\"\n" - "func @\"\".new(@\"\".typ *byte) (? *any)\n" - "func @\"\".panicindex()\n" - "func @\"\".panicslice()\n" - "func @\"\".throwreturn()\n" - "func @\"\".throwinit()\n" - "func @\"\".panicwrap(? string, ? string, ? string)\n" - "func @\"\".panic(? interface {})\n" - "func @\"\".recover(? *int32) (? interface {})\n" - "func @\"\".printbool(? bool)\n" - "func @\"\".printfloat(? float64)\n" - "func @\"\".printint(? int64)\n" - "func @\"\".printuint(? uint64)\n" - "func @\"\".printcomplex(? complex128)\n" - "func @\"\".printstring(? string)\n" - "func @\"\".printpointer(? any)\n" - "func @\"\".printiface(? any)\n" - "func @\"\".printeface(? any)\n" - "func @\"\".printslice(? any)\n" - "func @\"\".printnl()\n" - "func @\"\".printsp()\n" - "func @\"\".goprintf()\n" - "func @\"\".concatstring()\n" - "func @\"\".append()\n" - "func @\"\".appendslice(@\"\".typ *byte, @\"\".x any, @\"\".y []any) (? any)\n" - "func @\"\".appendstr(@\"\".typ *byte, @\"\".x []byte, @\"\".y string) (? []byte)\n" - "func @\"\".cmpstring(? string, ? string) (? int)\n" - "func @\"\".slicestring(? string, ? int, ? int) (? string)\n" - "func @\"\".slicestring1(? string, ? int) (? string)\n" - "func @\"\".intstring(? int64) (? string)\n" - "func @\"\".slicebytetostring(? []byte) (? string)\n" - "func @\"\".slicerunetostring(? []rune) (? string)\n" - "func @\"\".stringtoslicebyte(? string) (? []byte)\n" - "func @\"\".stringtoslicerune(? string) (? []rune)\n" - "func @\"\".stringiter(? string, ? int) (? int)\n" - "func @\"\".stringiter2(? string, ? int) (@\"\".retk int, @\"\".retv rune)\n" - "func @\"\".copy(@\"\".to any, @\"\".fr any, @\"\".wid uint32) (? int)\n" - "func @\"\".slicestringcopy(@\"\".to any, @\"\".fr any) (? int)\n" - "func @\"\".convI2E(@\"\".elem any) (@\"\".ret any)\n" - "func @\"\".convI2I(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n" - "func @\"\".convT2E(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n" - "func @\"\".convT2I(@\"\".typ *byte, @\"\".typ2 *byte, @\"\".elem any) (@\"\".ret any)\n" - "func @\"\".assertE2E(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertE2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertE2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertE2I2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertE2T(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertE2T2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertI2E(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertI2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertI2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertI2I2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertI2T(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertI2T2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".ifaceeq(@\"\".i1 any, @\"\".i2 any) (@\"\".ret bool)\n" - "func @\"\".efaceeq(@\"\".i1 any, @\"\".i2 any) (@\"\".ret bool)\n" - "func @\"\".ifacethash(@\"\".i1 any) (@\"\".ret uint32)\n" - "func @\"\".efacethash(@\"\".i1 any) (@\"\".ret uint32)\n" - "func @\"\".equal(@\"\".typ *byte, @\"\".x1 any, @\"\".x2 any) (@\"\".ret bool)\n" - "func @\"\".makemap(@\"\".mapType *byte, @\"\".hint int64) (@\"\".hmap map[any]any)\n" - "func @\"\".mapaccess1(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any) (@\"\".val any)\n" - "func @\"\".mapaccess2(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any) (@\"\".val any, @\"\".pres bool)\n" - "func @\"\".mapassign1(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any, @\"\".val any)\n" - "func @\"\".mapassign2(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any, @\"\".val any, @\"\".pres bool)\n" - "func @\"\".mapiterinit(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".hiter *any)\n" - "func @\"\".mapdelete(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any)\n" - "func @\"\".mapiternext(@\"\".hiter *any)\n" - "func @\"\".mapiter1(@\"\".hiter *any) (@\"\".key any)\n" - "func @\"\".mapiter2(@\"\".hiter *any) (@\"\".key any, @\"\".val any)\n" - "func @\"\".makechan(@\"\".chanType *byte, @\"\".hint int64) (@\"\".hchan chan any)\n" - "func @\"\".chanrecv1(@\"\".chanType *byte, @\"\".hchan <-chan any) (@\"\".elem any)\n" - "func @\"\".chanrecv2(@\"\".chanType *byte, @\"\".hchan <-chan any) (@\"\".elem any, @\"\".received bool)\n" - "func @\"\".chansend1(@\"\".chanType *byte, @\"\".hchan chan<- any, @\"\".elem any)\n" - "func @\"\".closechan(@\"\".hchan any)\n" - "func @\"\".selectnbsend(@\"\".chanType *byte, @\"\".hchan chan<- any, @\"\".elem any) (? bool)\n" - "func @\"\".selectnbrecv(@\"\".chanType *byte, @\"\".elem *any, @\"\".hchan <-chan any) (? bool)\n" - "func @\"\".selectnbrecv2(@\"\".chanType *byte, @\"\".elem *any, @\"\".received *bool, @\"\".hchan <-chan any) (? bool)\n" - "func @\"\".newselect(@\"\".size int) (@\"\".sel *byte)\n" - "func @\"\".selectsend(@\"\".sel *byte, @\"\".hchan chan<- any, @\"\".elem *any) (@\"\".selected bool)\n" - "func @\"\".selectrecv(@\"\".sel *byte, @\"\".hchan <-chan any, @\"\".elem *any) (@\"\".selected bool)\n" - "func @\"\".selectrecv2(@\"\".sel *byte, @\"\".hchan <-chan any, @\"\".elem *any, @\"\".received *bool) (@\"\".selected bool)\n" - "func @\"\".selectdefault(@\"\".sel *byte) (@\"\".selected bool)\n" - "func @\"\".selectgo(@\"\".sel *byte)\n" - "func @\"\".block()\n" - "func @\"\".makeslice(@\"\".typ *byte, @\"\".nel int64, @\"\".cap int64) (@\"\".ary []any)\n" - "func @\"\".growslice(@\"\".typ *byte, @\"\".old []any, @\"\".n int64) (@\"\".ary []any)\n" - "func @\"\".sliceslice1(@\"\".old []any, @\"\".lb uint64, @\"\".width uint64) (@\"\".ary []any)\n" - "func @\"\".sliceslice(@\"\".old []any, @\"\".lb uint64, @\"\".hb uint64, @\"\".width uint64) (@\"\".ary []any)\n" - "func @\"\".slicearray(@\"\".old *any, @\"\".nel uint64, @\"\".lb uint64, @\"\".hb uint64, @\"\".width uint64) (@\"\".ary []any)\n" - "func @\"\".closure()\n" - "func @\"\".memequal(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal8(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal16(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal32(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal64(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal128(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".int64div(? int64, ? int64) (? int64)\n" - "func @\"\".uint64div(? uint64, ? uint64) (? uint64)\n" - "func @\"\".int64mod(? int64, ? int64) (? int64)\n" - "func @\"\".uint64mod(? uint64, ? uint64) (? uint64)\n" - "func @\"\".float64toint64(? float64) (? int64)\n" - "func @\"\".float64touint64(? float64) (? uint64)\n" - "func @\"\".int64tofloat64(? int64) (? float64)\n" - "func @\"\".uint64tofloat64(? uint64) (? float64)\n" - "func @\"\".complex128div(@\"\".num complex128, @\"\".den complex128) (@\"\".quo complex128)\n" + "func @\"\".new (@\"\".typ·2 *byte) (? *any)\n" + "func @\"\".panicindex ()\n" + "func @\"\".panicslice ()\n" + "func @\"\".throwreturn ()\n" + "func @\"\".throwinit ()\n" + "func @\"\".panicwrap (? string, ? string, ? string)\n" + "func @\"\".panic (? interface {})\n" + "func @\"\".recover (? *int32) (? interface {})\n" + "func @\"\".printbool (? bool)\n" + "func @\"\".printfloat (? float64)\n" + "func @\"\".printint (? int64)\n" + "func @\"\".printuint (? uint64)\n" + "func @\"\".printcomplex (? complex128)\n" + "func @\"\".printstring (? string)\n" + "func @\"\".printpointer (? any)\n" + "func @\"\".printiface (? any)\n" + "func @\"\".printeface (? any)\n" + "func @\"\".printslice (? any)\n" + "func @\"\".printnl ()\n" + "func @\"\".printsp ()\n" + "func @\"\".goprintf ()\n" + "func @\"\".concatstring ()\n" + "func @\"\".append ()\n" + "func @\"\".appendslice (@\"\".typ·2 *byte, @\"\".x·3 any, @\"\".y·4 []any) (? any)\n" + "func @\"\".appendstr (@\"\".typ·2 *byte, @\"\".x·3 []byte, @\"\".y·4 string) (? []byte)\n" + "func @\"\".cmpstring (? string, ? string) (? int)\n" + "func @\"\".eqstring (? string, ? string) (? bool)\n" + "func @\"\".intstring (? int64) (? string)\n" + "func @\"\".slicebytetostring (? []byte) (? string)\n" + "func @\"\".slicerunetostring (? []rune) (? string)\n" + "func @\"\".stringtoslicebyte (? string) (? []byte)\n" + "func @\"\".stringtoslicerune (? string) (? []rune)\n" + "func @\"\".stringiter (? string, ? int) (? int)\n" + "func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n" + "func @\"\".copy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n" + "func @\"\".slicestringcopy (@\"\".to·2 any, @\"\".fr·3 any) (? int)\n" + "func @\"\".typ2Itab (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte) (@\"\".ret·1 *byte)\n" + "func @\"\".convI2E (@\"\".elem·2 any) (@\"\".ret·1 any)\n" + "func @\"\".convI2I (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n" + "func @\"\".convT2E (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n" + "func @\"\".convT2I (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte, @\"\".elem·5 any) (@\"\".ret·1 any)\n" + "func @\"\".assertE2E (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertE2E2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertE2I (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertE2I2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertE2T (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertE2T2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertI2E (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertI2E2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertI2I (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertI2I2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertI2T (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertI2T2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertI2TOK (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ok·1 bool)\n" + "func @\"\".assertE2TOK (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ok·1 bool)\n" + "func @\"\".ifaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + "func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + "func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + "func @\"\".equal (@\"\".typ·2 *byte, @\"\".x1·3 any, @\"\".x2·4 any) (@\"\".ret·1 bool)\n" + "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".hmap·1 map[any]any)\n" + "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 any)\n" + "func @\"\".mapaccess2 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 any, @\"\".pres·2 bool)\n" + "func @\"\".mapassign1 (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 any, @\"\".val·4 any)\n" + "func @\"\".mapiterinit (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".hiter·3 *any)\n" + "func @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 any)\n" + "func @\"\".mapiternext (@\"\".hiter·1 *any)\n" + "func @\"\".mapiter1 (@\"\".hiter·2 *any) (@\"\".key·1 any)\n" + "func @\"\".mapiter2 (@\"\".hiter·3 *any) (@\"\".key·1 any, @\"\".val·2 any)\n" + "func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n" + "func @\"\".chanrecv1 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any) (@\"\".elem·1 any)\n" + "func @\"\".chanrecv2 (@\"\".chanType·3 *byte, @\"\".hchan·4 <-chan any) (@\"\".elem·1 any, @\"\".received·2 bool)\n" + "func @\"\".chansend1 (@\"\".chanType·1 *byte, @\"\".hchan·2 chan<- any, @\"\".elem·3 any)\n" + "func @\"\".closechan (@\"\".hchan·1 any)\n" + "func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 any) (? bool)\n" + "func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n" + "func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n" + "func @\"\".newselect (@\"\".size·2 int32) (@\"\".sel·1 *byte)\n" + "func @\"\".selectsend (@\"\".sel·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n" + "func @\"\".selectrecv (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n" + "func @\"\".selectrecv2 (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any, @\"\".received·5 *bool) (@\"\".selected·1 bool)\n" + "func @\"\".selectdefault (@\"\".sel·2 *byte) (@\"\".selected·1 bool)\n" + "func @\"\".selectgo (@\"\".sel·1 *byte)\n" + "func @\"\".block ()\n" + "func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n" + "func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int64) (@\"\".ary·1 []any)\n" + "func @\"\".memequal (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal8 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal16 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal32 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal64 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal128 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".int64div (? int64, ? int64) (? int64)\n" + "func @\"\".uint64div (? uint64, ? uint64) (? uint64)\n" + "func @\"\".int64mod (? int64, ? int64) (? int64)\n" + "func @\"\".uint64mod (? uint64, ? uint64) (? uint64)\n" + "func @\"\".float64toint64 (? float64) (? int64)\n" + "func @\"\".float64touint64 (? float64) (? uint64)\n" + "func @\"\".int64tofloat64 (? int64) (? float64)\n" + "func @\"\".uint64tofloat64 (? uint64) (? float64)\n" + "func @\"\".complex128div (@\"\".num·2 complex128, @\"\".den·3 complex128) (@\"\".quo·1 complex128)\n" + "func @\"\".racefuncenter (? uintptr)\n" + "func @\"\".racefuncexit ()\n" + "func @\"\".raceread (? uintptr)\n" + "func @\"\".racewrite (? uintptr)\n" "\n" "$$\n"; char *unsafeimport = "package unsafe\n" "import runtime \"runtime\"\n" "type @\"\".Pointer uintptr\n" - "func @\"\".Offsetof(? any) (? uintptr)\n" - "func @\"\".Sizeof(? any) (? uintptr)\n" - "func @\"\".Alignof(? any) (? uintptr)\n" + "func @\"\".Offsetof (? any) (? uintptr)\n" + "func @\"\".Sizeof (? any) (? uintptr)\n" + "func @\"\".Alignof (? any) (? uintptr)\n" "\n" "$$\n"; diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index fa44e40fa..4e029ef83 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -75,6 +75,8 @@ closurebody(NodeList *body) return func; } +static Node* makeclosure(Node *func, int nowrap); + void typecheckclosure(Node *func, int top) { @@ -85,12 +87,12 @@ typecheckclosure(Node *func, int top) oldfn = curfn; typecheck(&func->ntype, Etype); func->type = func->ntype->type; - if(curfn == nil) { - xtop = list(xtop, func); - return; - } - - if(func->type != T) { + + // Type check the body now, but only if we're inside a function. + // At top level (in a variable initialization: curfn==nil) we're not + // ready to type check code yet; we'll check it later, because the + // underlying closure function we create is added to xtop. + if(curfn && func->type != T) { curfn = func; typechecklist(func->nbody, Etop); curfn = oldfn; @@ -120,26 +122,43 @@ typecheckclosure(Node *func, int top) func->enter = list(func->enter, v->heapaddr); v->heapaddr = N; } + + // Create top-level function + xtop = list(xtop, makeclosure(func, func->cvars==nil || (top&Ecall))); } static Node* -makeclosure(Node *func, NodeList **init, int nowrap) +makeclosure(Node *func, int nowrap) { - Node *xtype, *v, *addr, *xfunc; - NodeList *l; + Node *xtype, *v, *addr, *xfunc, *cv; + NodeList *l, *body; static int closgen; char *p; - - USED(init); + int offset; /* * wrap body in external function - * with extra closure parameters. + * that begins by reading closure parameters. */ xtype = nod(OTFUNC, N, N); + xtype->list = func->list; + xtype->rlist = func->rlist; - // each closure variable has a corresponding - // address parameter. + // create the function + xfunc = nod(ODCLFUNC, N, N); + snprint(namebuf, sizeof namebuf, "func·%.3d", ++closgen); + xfunc->nname = newname(lookup(namebuf)); + xfunc->nname->sym->flags |= SymExported; // disable export + xfunc->nname->ntype = xtype; + xfunc->nname->defn = xfunc; + declare(xfunc->nname, PFUNC); + xfunc->nname->funcdepth = func->funcdepth; + xfunc->funcdepth = func->funcdepth; + + // declare variables holding addresses taken from closure + // and initialize in entry prologue. + body = nil; + offset = widthptr; for(l=func->cvars; l; l=l->next) { v = l->n; if(v->op == 0) @@ -149,38 +168,35 @@ makeclosure(Node *func, NodeList **init, int nowrap) addr->sym = lookup(p); free(p); addr->ntype = nod(OIND, typenod(v->type), N); - addr->class = PPARAM; + addr->class = PAUTO; addr->addable = 1; addr->ullman = 1; - + addr->used = 1; + addr->curfn = xfunc; + xfunc->dcl = list(xfunc->dcl, addr); v->heapaddr = addr; - - xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype)); + cv = nod(OCLOSUREVAR, N, N); + cv->type = ptrto(v->type); + cv->xoffset = offset; + body = list(body, nod(OAS, addr, cv)); + offset += widthptr; } + typechecklist(body, Etop); + walkstmtlist(body); + xfunc->enter = body; - // then a dummy arg where the closure's caller pc sits - if (!nowrap) - xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); - - // then the function arguments - xtype->list = concat(xtype->list, func->list); - xtype->rlist = concat(xtype->rlist, func->rlist); - - // create the function - xfunc = nod(ODCLFUNC, N, N); - snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen); - xfunc->nname = newname(lookup(namebuf)); - xfunc->nname->ntype = xtype; - xfunc->nname->defn = xfunc; - declare(xfunc->nname, PFUNC); - xfunc->nname->funcdepth = func->funcdepth; - xfunc->funcdepth = func->funcdepth; xfunc->nbody = func->nbody; - xfunc->dcl = func->dcl; + xfunc->dcl = concat(func->dcl, xfunc->dcl); if(xfunc->nbody == nil) fatal("empty body - won't generate any code"); typecheck(&xfunc, Etop); - closures = list(closures, xfunc); + + xfunc->closure = func; + func->closure = xfunc; + + func->nbody = nil; + func->list = nil; + func->rlist = nil; return xfunc; } @@ -188,51 +204,55 @@ makeclosure(Node *func, NodeList **init, int nowrap) Node* walkclosure(Node *func, NodeList **init) { + Node *clos, *typ; + NodeList *l; + char buf[20]; int narg; - Node *xtype, *xfunc, *call, *clos; - NodeList *l, *in; - // no closure vars, don't bother wrapping + // If no closure vars, don't bother wrapping. if(func->cvars == nil) - return makeclosure(func, init, 1)->nname; + return func->closure->nname; + + // Create closure in the form of a composite literal. + // supposing the closure captures an int i and a string s + // and has one float64 argument and no results, + // the generated code looks like: + // + // clos = &struct{F uintptr; A0 *int; A1 *string}{func·001, &i, &s} + // + // The use of the struct provides type information to the garbage + // collector so that it can walk the closure. We could use (in this case) + // [3]unsafe.Pointer instead, but that would leave the gc in the dark. + // The information appears in the binary in the form of type descriptors; + // the struct is unnamed so that closures in multiple packages with the + // same struct type can share the descriptor. - /* - * wrap body in external function - * with extra closure parameters. - */ - - // create the function - xfunc = makeclosure(func, init, 0); - xtype = xfunc->nname->ntype; - - // prepare call of sys.closure that turns external func into func literal value. - clos = syslook("closure", 1); - clos->type = T; - clos->ntype = nod(OTFUNC, N, N); - in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // siz - in = list(in, nod(ODCLFIELD, N, xtype)); narg = 0; + typ = nod(OTSTRUCT, N, N); + typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR]))); for(l=func->cvars; l; l=l->next) { if(l->n->op == 0) continue; - narg++; - in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype)); + snprint(buf, sizeof buf, "A%d", narg++); + typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup(buf)), l->n->heapaddr->ntype)); } - clos->ntype->list = in; - clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type))); + + clos = nod(OCOMPLIT, N, nod(OIND, typ, N)); + clos->esc = func->esc; + clos->right->implicit = 1; + clos->list = concat(list1(nod(OCFUNC, func->closure->nname, N)), func->enter); + + // Force type conversion from *struct to the func type. + clos = nod(OCONVNOP, clos, N); + clos->type = func->type; + typecheck(&clos, Erv); + // typecheck will insert a PTRLIT node under CONVNOP, + // tag it with escape analysis result. + clos->left->esc = func->esc; + walkexpr(&clos, init); - call = nod(OCALL, clos, N); - if(narg*widthptr > 100) - yyerror("closure needs too many variables; runtime will reject it"); - in = list1(nodintconst(narg*widthptr)); - in = list(in, xfunc->nname); - in = concat(in, func->enter); - call->list = in; - - typecheck(&call, Erv); - walkexpr(&call, init); - return call; + return clos; } // Special case for closures that get called in place. @@ -242,6 +262,7 @@ walkclosure(Node *func, NodeList **init) void walkcallclosure(Node *n, NodeList **init) { + USED(init); if (n->op != OCALLFUNC || n->left->op != OCLOSURE) { dump("walkcallclosure", n); fatal("abuse of walkcallclosure"); @@ -250,7 +271,7 @@ walkcallclosure(Node *n, NodeList **init) // New arg list for n. First the closure-args // and then the original parameter list. n->list = concat(n->left->enter, n->list); - n->left = makeclosure(n->left, init, 1)->nname; + n->left = n->left->closure->nname; dowidth(n->left->type); n->type = getoutargx(n->left->type); // for a single valued function, pull the field type out of the struct diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index e27c88338..4f1ff6778 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -87,8 +87,12 @@ convlit1(Node **np, Type *t, int explicit) switch(n->op) { default: - if(n->type == idealbool) - n->type = types[TBOOL]; + if(n->type == idealbool) { + if(t->etype == TBOOL) + n->type = t; + else + n->type = types[TBOOL]; + } if(n->type->etype == TIDEAL) { convlit(&n->left, t); convlit(&n->right, t); @@ -162,6 +166,16 @@ convlit1(Node **np, Type *t, int explicit) case TFUNC: case TUNSAFEPTR: break; + + case TUINTPTR: + // A nil literal may be converted to uintptr + // if it is an unsafe.Pointer + if(n->type->etype == TUNSAFEPTR) { + n->val.u.xval = mal(sizeof(*n->val.u.xval)); + mpmovecfix(n->val.u.xval, 0); + n->val.ctype = CTINT; + } else + goto bad; } break; @@ -230,7 +244,8 @@ convlit1(Node **np, Type *t, int explicit) bad: if(!n->diag) { - yyerror("cannot convert %N to type %T", n, t); + if(!t->broke) + yyerror("cannot convert %N to type %T", n, t); n->diag = 1; } if(isideal(n->type)) { @@ -348,13 +363,9 @@ toint(Val v) return v; } -void -overflow(Val v, Type *t) +int +doesoverflow(Val v, Type *t) { - // v has already been converted - // to appropriate form for t. - if(t == T || t->etype == TIDEAL) - return; switch(v.ctype) { case CTINT: case CTRUNE: @@ -362,14 +373,14 @@ overflow(Val v, Type *t) fatal("overflow: %T integer constant", t); if(mpcmpfixfix(v.u.xval, minintval[t->etype]) < 0 || mpcmpfixfix(v.u.xval, maxintval[t->etype]) > 0) - yyerror("constant %B overflows %T", v.u.xval, t); + return 1; break; case CTFLT: if(!isfloat[t->etype]) fatal("overflow: %T floating-point constant", t); if(mpcmpfltflt(v.u.fval, minfltval[t->etype]) <= 0 || mpcmpfltflt(v.u.fval, maxfltval[t->etype]) >= 0) - yyerror("constant %#F overflows %T", v.u.fval, t); + return 1; break; case CTCPLX: if(!iscomplex[t->etype]) @@ -378,7 +389,33 @@ overflow(Val v, Type *t) mpcmpfltflt(&v.u.cval->real, maxfltval[t->etype]) >= 0 || mpcmpfltflt(&v.u.cval->imag, minfltval[t->etype]) <= 0 || mpcmpfltflt(&v.u.cval->imag, maxfltval[t->etype]) >= 0) - yyerror("constant %#F overflows %T", v.u.fval, t); + return 1; + break; + } + return 0; +} + +void +overflow(Val v, Type *t) +{ + // v has already been converted + // to appropriate form for t. + if(t == T || t->etype == TIDEAL) + return; + + if(!doesoverflow(v, t)) + return; + + switch(v.ctype) { + case CTINT: + case CTRUNE: + yyerror("constant %B overflows %T", v.u.xval, t); + break; + case CTFLT: + yyerror("constant %#F overflows %T", v.u.fval, t); + break; + case CTCPLX: + yyerror("constant %#F overflows %T", v.u.fval, t); break; } } @@ -437,6 +474,20 @@ isconst(Node *n, int ct) return t == ct || (ct == CTINT && t == CTRUNE); } +static Node* +saveorig(Node *n) +{ + Node *n1; + + if(n == n->orig) { + // duplicate node for n->orig. + n1 = nod(OLITERAL, N, N); + n->orig = n1; + *n1 = *n; + } + return n->orig; +} + /* * if n is constant, rewrite as OLITERAL node. */ @@ -902,12 +953,7 @@ unary: } ret: - if(n == n->orig) { - // duplicate node for n->orig. - norig = nod(OLITERAL, N, N); - *norig = *n; - } else - norig = n->orig; + norig = saveorig(n); *n = *nl; // restore value of n->orig. n->orig = norig; @@ -924,11 +970,15 @@ ret: return; settrue: + norig = saveorig(n); *n = *nodbool(1); + n->orig = norig; return; setfalse: + norig = saveorig(n); *n = *nodbool(0); + n->orig = norig; return; } @@ -984,79 +1034,88 @@ nodcplxlit(Val r, Val i) return n; } -// TODO(rsc): combine with convlit +// idealkind returns a constant kind like consttype +// but for an arbitrary "ideal" expression. +static int +idealkind(Node *n) +{ + int k1, k2; + + if(n == N || !isideal(n->type)) + return CTxxx; + + switch(n->op) { + default: + return CTxxx; + case OLITERAL: + return n->val.ctype; + case OADD: + case OAND: + case OANDNOT: + case OCOM: + case ODIV: + case OMINUS: + case OMOD: + case OMUL: + case OSUB: + case OXOR: + case OOR: + case OPLUS: + // numeric kinds. + k1 = idealkind(n->left); + k2 = idealkind(n->right); + if(k1 > k2) + return k1; + else + return k2; + case OADDSTR: + return CTSTR; + case OANDAND: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLT: + case ONE: + case ONOT: + case OOROR: + case OCMPSTR: + case OCMPIFACE: + return CTBOOL; + case OLSH: + case ORSH: + // shifts (beware!). + return idealkind(n->left); + } +} + void defaultlit(Node **np, Type *t) { int lno; + int ctype; Node *n, *nn; + Type *t1; n = *np; if(n == N || !isideal(n->type)) return; - switch(n->op) { - case OLITERAL: + if(n->op == OLITERAL) { nn = nod(OXXX, N, N); *nn = *n; n = nn; *np = n; - break; - case OLSH: - case ORSH: - defaultlit(&n->left, t); - t = n->left->type; - if(t != T && !isint[t->etype]) { - yyerror("invalid operation: %N (shift of type %T)", n, t); - t = T; - } - n->type = t; - return; - case ONOT: - defaultlit(&n->left, t); - n->type = n->left->type; - return; - default: - if(n->left == N) { - dump("defaultlit", n); - fatal("defaultlit"); - } - // n is ideal, so left and right must both be ideal. - // n has not been computed as a constant value, - // so either left or right must not be constant. - // The only 'ideal' non-constant expressions are shifts. Ugh. - // If one of these is a shift and the other is not, use that type. - // When compiling x := 1<right->op == OLSH || n->right->op == ORSH)) { - defaultlit(&n->left, T); - defaultlit(&n->right, n->left->type); - } else if(t == T && (n->left->op == OLSH || n->left->op == ORSH)) { - defaultlit(&n->right, T); - defaultlit(&n->left, n->right->type); - } else if(iscmp[n->op]) { - defaultlit2(&n->left, &n->right, 1); - } else { - defaultlit(&n->left, t); - defaultlit(&n->right, t); - } - if(n->type == idealbool || n->type == idealstring) { - if(t != T && t->etype == n->type->etype) - n->type = t; - else - n->type = types[n->type->etype]; - } else - n->type = n->left->type; - return; } lno = setlineno(n); - switch(n->val.ctype) { + ctype = idealkind(n); + t1 = T; + switch(ctype) { default: if(t != T) { convlit(np, t); - break; + return; } if(n->val.ctype == CTNIL) { lineno = lno; @@ -1065,46 +1124,52 @@ defaultlit(Node **np, Type *t) break; } if(n->val.ctype == CTSTR) { - n->type = types[TSTRING]; + t1 = types[TSTRING]; + convlit(np, t1); break; } yyerror("defaultlit: unknown literal: %N", n); break; + case CTxxx: + fatal("defaultlit: idealkind is CTxxx: %+N", n); + break; case CTBOOL: - n->type = types[TBOOL]; + t1 = types[TBOOL]; if(t != T && t->etype == TBOOL) - n->type = t; + t1 = t; + convlit(np, t1); break; case CTINT: - n->type = types[TINT]; + t1 = types[TINT]; goto num; case CTRUNE: - n->type = runetype; + t1 = runetype; goto num; case CTFLT: - n->type = types[TFLOAT64]; + t1 = types[TFLOAT64]; goto num; case CTCPLX: - n->type = types[TCOMPLEX128]; + t1 = types[TCOMPLEX128]; goto num; num: if(t != T) { if(isint[t->etype]) { - n->type = t; + t1 = t; n->val = toint(n->val); } else if(isfloat[t->etype]) { - n->type = t; + t1 = t; n->val = toflt(n->val); } else if(iscomplex[t->etype]) { - n->type = t; + t1 = t; n->val = tocplx(n->val); } } - overflow(n->val, n->type); + overflow(n->val, t1); + convlit(np, t1); break; } lineno = lno; @@ -1206,6 +1271,7 @@ smallintconst(Node *n) case TIDEAL: case TINT64: case TUINT64: + case TPTR64: if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0 || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0) break; @@ -1392,3 +1458,132 @@ cmplxdiv(Mpcplx *v, Mpcplx *rv) mpsubfltflt(&v->imag, &ad); // bc-ad mpdivfltflt(&v->imag, &cc_plus_dd); // (bc+ad)/(cc+dd) } + +static int hascallchan(Node*); + +// Is n a Go language constant (as opposed to a compile-time constant)? +// Expressions derived from nil, like string([]byte(nil)), while they +// may be known at compile time, are not Go language constants. +// Only called for expressions known to evaluated to compile-time +// constants. +int +isgoconst(Node *n) +{ + Node *l; + Type *t; + + if(n->orig != N) + n = n->orig; + + switch(n->op) { + case OADD: + case OADDSTR: + case OAND: + case OANDAND: + case OANDNOT: + case OCOM: + case ODIV: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLSH: + case OLT: + case OMINUS: + case OMOD: + case OMUL: + case ONE: + case ONOT: + case OOR: + case OOROR: + case OPLUS: + case ORSH: + case OSUB: + case OXOR: + case OCONV: + case OIOTA: + case OCOMPLEX: + case OREAL: + case OIMAG: + if(isgoconst(n->left) && (n->right == N || isgoconst(n->right))) + return 1; + break; + + case OLEN: + case OCAP: + l = n->left; + if(isgoconst(l)) + return 1; + // Special case: len/cap is constant when applied to array or + // pointer to array when the expression does not contain + // function calls or channel receive operations. + t = l->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t) && !hascallchan(l)) + return 1; + break; + + case OLITERAL: + if(n->val.ctype != CTNIL) + return 1; + break; + + case ONAME: + l = n->sym->def; + if(l->op == OLITERAL && n->val.ctype != CTNIL) + return 1; + break; + + case ONONAME: + if(n->sym->def != N && n->sym->def->op == OIOTA) + return 1; + break; + + case OCALL: + // Only constant calls are unsafe.Alignof, Offsetof, and Sizeof. + l = n->left; + while(l->op == OPAREN) + l = l->left; + if(l->op != ONAME || l->sym->pkg != unsafepkg) + break; + if(strcmp(l->sym->name, "Alignof") == 0 || + strcmp(l->sym->name, "Offsetof") == 0 || + strcmp(l->sym->name, "Sizeof") == 0) + return 1; + break; + } + + //dump("nonconst", n); + return 0; +} + +static int +hascallchan(Node *n) +{ + NodeList *l; + + if(n == N) + return 0; + switch(n->op) { + case OCALL: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + case ORECV: + return 1; + } + + if(hascallchan(n->left) || + hascallchan(n->right)) + return 1; + + for(l=n->list; l; l=l->next) + if(hascallchan(l->n)) + return 1; + for(l=n->rlist; l; l=l->next) + if(hascallchan(l->n)) + return 1; + + return 0; +} diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c index dea7bc3bb..e0127fc59 100644 --- a/src/cmd/gc/cplx.c +++ b/src/cmd/gc/cplx.c @@ -271,7 +271,7 @@ complexgen(Node *n, Node *res) } void -complexbool(int op, Node *nl, Node *nr, int true, Prog *to) +complexbool(int op, Node *nl, Node *nr, int true, int likely, Prog *to) { Node tnl, tnr; Node n1, n2, n3, n4; @@ -323,7 +323,7 @@ complexbool(int op, Node *nl, Node *nr, int true, Prog *to) if(op == ONE) true = !true; - bgen(&na, true, to); + bgen(&na, true, likely, to); } void diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index 32f334b71..aa2489d9a 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -150,16 +150,35 @@ testdclstack(void) void redeclare(Sym *s, char *where) { - if(s->lastlineno == 0) - yyerror("%S redeclared %s\n" - "\tprevious declaration during import", - s, where); - else + Strlit *pkgstr; + int line1, line2; + + if(s->lastlineno == 0) { + pkgstr = s->origpkg ? s->origpkg->path : s->pkg->path; yyerror("%S redeclared %s\n" + "\tprevious declaration during import \"%Z\"", + s, where, pkgstr); + } else { + line1 = parserline(); + line2 = s->lastlineno; + + // When an import and a declaration collide in separate files, + // present the import as the "redeclared", because the declaration + // is visible where the import is, but not vice versa. + // See issue 4510. + if(s->def == N) { + line2 = line1; + line1 = s->lastlineno; + } + + yyerrorl(line1, "%S redeclared %s\n" "\tprevious declaration at %L", - s, where, s->lastlineno); + s, where, line2); + } } +static int vargen; + /* * declare individual names - var, typ, const */ @@ -168,7 +187,10 @@ declare(Node *n, int ctxt) { Sym *s; int gen; - static int typegen, vargen; + static int typegen; + + if(ctxt == PDISCARD) + return; if(isblank(n)) return; @@ -180,6 +202,9 @@ declare(Node *n, int ctxt) if(importpkg == nil && !typecheckok && s->pkg != localpkg) yyerror("cannot declare name %S", s); + if(ctxt == PEXTERN && strcmp(s->name, "init") == 0) + yyerror("cannot declare init - must be func", s); + gen = 0; if(ctxt == PEXTERN) { externdcl = list(externdcl, n); @@ -192,7 +217,7 @@ declare(Node *n, int ctxt) curfn->dcl = list(curfn->dcl, n); if(n->op == OTYPE) gen = ++typegen; - else if(n->op == ONAME) + else if(n->op == ONAME && ctxt == PAUTO && strstr(s->name, "·") == nil) gen = ++vargen; pushdcl(s); n->curfn = curfn; @@ -237,7 +262,7 @@ variter(NodeList *vl, Node *t, NodeList *el) init = nil; doexpr = el != nil; - + if(count(el) == 1 && count(vl) > 1) { e = el->n; as2 = nod(OAS2, N, N); @@ -464,7 +489,7 @@ colasdefn(NodeList *left, Node *defn) if(isblank(n)) continue; if(!colasname(n)) { - yyerror("non-name %N on left side of :=", n); + yyerrorl(defn->lineno, "non-name %N on left side of :=", n); nerr++; continue; } @@ -479,11 +504,11 @@ colasdefn(NodeList *left, Node *defn) l->n = n; } if(nnew == 0 && nerr == 0) - yyerror("no new variables on left side of :="); + yyerrorl(defn->lineno, "no new variables on left side of :="); } Node* -colas(NodeList *left, NodeList *right) +colas(NodeList *left, NodeList *right, int32 lno) { Node *as; @@ -491,6 +516,7 @@ colas(NodeList *left, NodeList *right) as->list = left; as->rlist = right; as->colas = 1; + as->lineno = lno; colasdefn(left, as); // make the tree prettier; not necessary @@ -515,7 +541,7 @@ ifacedcl(Node *n) if(n->op != ODCLFIELD || n->right == N) fatal("ifacedcl"); - dclcontext = PAUTO; + dclcontext = PPARAM; markdcl(); funcdepth++; n->outer = curfn; @@ -526,6 +552,7 @@ ifacedcl(Node *n) // seen the body of a function but since an interface // field declaration does not have a body, we must // call it now to pop the current declaration context. + dclcontext = PAUTO; funcbody(n); } @@ -567,6 +594,11 @@ funcargs(Node *nt) if(nt->op != OTFUNC) fatal("funcargs %O", nt->op); + // re-start the variable generation number + // we want to use small numbers for the return variables, + // so let them have the chunk starting at 1. + vargen = count(nt->rlist); + // declare the receiver and in arguments. // no n->defn because type checking of func header // will not fill in the types until later @@ -578,6 +610,8 @@ funcargs(Node *nt) n->left->op = ONAME; n->left->ntype = n->right; declare(n->left, PPARAM); + if(dclcontext == PAUTO) + n->left->vargen = ++vargen; } } for(l=nt->list; l; l=l->next) { @@ -588,29 +622,44 @@ funcargs(Node *nt) n->left->op = ONAME; n->left->ntype = n->right; declare(n->left, PPARAM); + if(dclcontext == PAUTO) + n->left->vargen = ++vargen; } } // declare the out arguments. - gen = 0; + gen = count(nt->list); + int i = 0; for(l=nt->rlist; l; l=l->next) { n = l->n; + if(n->op != ODCLFIELD) fatal("funcargs out %O", n->op); - if(n->left != N) { - n->left->op = ONAME; - n->left->ntype = n->right; - if(isblank(n->left)) { - // Give it a name so we can assign to it during return. - // preserve the original in ->orig - nn = nod(OXXX, N, N); - *nn = *n->left; - n->left = nn; - snprint(namebuf, sizeof(namebuf), ".anon%d", gen++); - n->left->sym = lookup(namebuf); - } - declare(n->left, PPARAMOUT); + + if(n->left == N) { + // give it a name so escape analysis has nodes to work with + snprint(namebuf, sizeof(namebuf), "~anon%d", gen++); + n->left = newname(lookup(namebuf)); + // TODO: n->left->missing = 1; + } + + n->left->op = ONAME; + + if(isblank(n->left)) { + // Give it a name so we can assign to it during return. + // preserve the original in ->orig + nn = nod(OXXX, N, N); + *nn = *n->left; + n->left = nn; + + snprint(namebuf, sizeof(namebuf), "~anon%d", gen++); + n->left->sym = lookup(namebuf); } + + n->left->ntype = n->right; + declare(n->left, PPARAMOUT); + if(dclcontext == PAUTO) + n->left->vargen = ++i; } } @@ -978,8 +1027,11 @@ embedded(Sym *s) *utfrune(name, CenterDot) = 0; } - if(exportname(name) || s->pkg == builtinpkg) // old behaviour, tests pass, but is it correct? + if(exportname(name)) n = newname(lookup(name)); + else if(s->pkg == builtinpkg && importpkg != nil) + // The name of embedded builtins during imports belongs to importpkg. + n = newname(pkglookup(name, importpkg)); else n = newname(pkglookup(name, s->pkg)); n = nod(ODCLFIELD, n, oldname(s)); @@ -1124,6 +1176,7 @@ functype(Node *this, NodeList *in, NodeList *out) { Type *t; NodeList *rcvr; + Sym *s; t = typ(TFUNC); @@ -1141,7 +1194,12 @@ functype(Node *this, NodeList *in, NodeList *out) t->thistuple = 1; t->outtuple = count(out); t->intuple = count(in); - t->outnamed = t->outtuple > 0 && out->n->left != N; + t->outnamed = 0; + if(t->outtuple > 0 && out->n->left != N && out->n->left->orig != N) { + s = out->n->left->orig->sym; + if(s != S && s->name[0] != '~') + t->outnamed = 1; + } return t; } @@ -1249,7 +1307,7 @@ methodname1(Node *n, Node *t) * n is fieldname, pa is base type, t is function type */ void -addmethod(Sym *sf, Type *t, int local) +addmethod(Sym *sf, Type *t, int local, int nointerface) { Type *f, *d, *pa; Node *n; @@ -1332,6 +1390,7 @@ addmethod(Sym *sf, Type *t, int local) } f = structfield(n); + f->nointerface = nointerface; // during import unexported method names should be in the type's package if(importpkg && f->sym && !exportname(f->sym->name) && f->sym->pkg != structpkg) @@ -1382,3 +1441,20 @@ funccompile(Node *n, int isclosure) funcdepth = 0; dclcontext = PEXTERN; } + +Sym* +funcsym(Sym *s) +{ + char *p; + Sym *s1; + + p = smprint("%s·f", s->name); + s1 = pkglookup(p, s->pkg); + free(p); + if(s1->def == N) { + s1->def = newname(s1); + s1->def->shortname = newname(s); + funcsyms = list(funcsyms, s1->def); + } + return s1; +} diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go index 8d8f8967b..791967708 100644 --- a/src/cmd/gc/doc.go +++ b/src/cmd/gc/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* Gc is the generic label for the family of Go compilers @@ -25,6 +27,8 @@ other packages. It is therefore not necessary when compiling client C of package P to read the files of P's dependencies, only the compiled output of P. +Command Line + Usage: go tool 6g [flags] file... The specified files must be Go source files and all part of the same package. @@ -48,15 +52,40 @@ Flags: disable optimizations -S write assembly language text to standard output (code only) - -SS + -S -S write assembly language text to standard output (code and data) -u disallow importing packages not marked as safe -V print the compiler version + -race + compile with race detection enabled There are also a number of debugging flags; run the command with no arguments to get a usage message. +Compiler Directives + +The compiler accepts two compiler directives in the form of // comments at the +beginning of a line. To distinguish them from non-directive comments, the directives +require no space between the slashes and the name of the directive. However, since +they are comments, tools unaware of the directive convention or of a particular +directive can skip over a directive like any other comment. + + //line path/to/file:linenumber + +The //line directive specifies that the source line that follows should be recorded +as having come from the given file path and line number. Successive lines are +recorded using increasing line numbers, until the next directive. This directive +typically appears in machine-generated code, so that compilers and debuggers +will show lines in the original input to the generator. + + //go:noescape + +The //go:noescape directive specifies that the next declaration in the file, which +must be a func without a body (meaning that it has an implementation not written +in Go) does not allow any of the pointers passed as arguments to escape into the +heap or into the values returned from the function. This information can be used as +during the compiler's escape analysis of Go code calling the function. */ -package documentation +package main diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index 8a265ce59..46c06d10e 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -1,8 +1,162 @@ // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// + // Escape analysis. + +#include +#include +#include "go.h" + +// Run analysis on minimal sets of mutually recursive functions +// or single non-recursive functions, bottom up. +// +// Finding these sets is finding strongly connected components +// in the static call graph. The algorithm for doing that is taken +// from Sedgewick, Algorithms, Second Edition, p. 482, with two +// adaptations. +// +// First, a hidden closure function (n->curfn != N) cannot be the +// root of a connected component. Refusing to use it as a root +// forces it into the component of the function in which it appears. +// The analysis assumes that closures and the functions in which they +// appear are analyzed together, so that the aliasing between their +// variables can be modeled more precisely. +// +// Second, each function becomes two virtual nodes in the graph, +// with numbers n and n+1. We record the function's node number as n +// but search from node n+1. If the search tells us that the component +// number (min) is n+1, we know that this is a trivial component: one function +// plus its closures. If the search tells us that the component number is +// n, then there was a path from node n+1 back to node n, meaning that +// the function set is mutually recursive. The escape analysis can be +// more precise when analyzing a single non-recursive function than +// when analyzing a set of mutually recursive functions. + +static NodeList *stack; +static uint32 visitgen; +static uint32 visit(Node*); +static uint32 visitcode(Node*, uint32); +static uint32 visitcodelist(NodeList*, uint32); + +static void analyze(NodeList*, int); + +enum +{ + EscFuncUnknown = 0, + EscFuncPlanned, + EscFuncStarted, + EscFuncTagged, +}; + +void +escapes(NodeList *all) +{ + NodeList *l; + + for(l=all; l; l=l->next) + l->n->walkgen = 0; + + visitgen = 0; + for(l=all; l; l=l->next) + if(l->n->op == ODCLFUNC && l->n->curfn == N) + visit(l->n); + + for(l=all; l; l=l->next) + l->n->walkgen = 0; +} + +static uint32 +visit(Node *n) +{ + uint32 min, recursive; + NodeList *l, *block; + + if(n->walkgen > 0) { + // already visited + return n->walkgen; + } + + visitgen++; + n->walkgen = visitgen; + visitgen++; + min = visitgen; + + l = mal(sizeof *l); + l->next = stack; + l->n = n; + stack = l; + min = visitcodelist(n->nbody, min); + if((min == n->walkgen || min == n->walkgen+1) && n->curfn == N) { + // This node is the root of a strongly connected component. + + // The original min passed to visitcodelist was n->walkgen+1. + // If visitcodelist found its way back to n->walkgen, then this + // block is a set of mutually recursive functions. + // Otherwise it's just a lone function that does not recurse. + recursive = min == n->walkgen; + + // Remove connected component from stack. + // Mark walkgen so that future visits return a large number + // so as not to affect the caller's min. + block = stack; + for(l=stack; l->n != n; l=l->next) + l->n->walkgen = (uint32)~0U; + n->walkgen = (uint32)~0U; + stack = l->next; + l->next = nil; + + // Run escape analysis on this set of functions. + analyze(block, recursive); + } + + return min; +} + +static uint32 +visitcodelist(NodeList *l, uint32 min) +{ + for(; l; l=l->next) + min = visitcode(l->n, min); + return min; +} + +static uint32 +visitcode(Node *n, uint32 min) +{ + Node *fn; + uint32 m; + + if(n == N) + return min; + + min = visitcodelist(n->ninit, min); + min = visitcode(n->left, min); + min = visitcode(n->right, min); + min = visitcodelist(n->list, min); + min = visitcode(n->ntest, min); + min = visitcode(n->nincr, min); + min = visitcodelist(n->nbody, min); + min = visitcodelist(n->nelse, min); + min = visitcodelist(n->rlist, min); + + if(n->op == OCALLFUNC || n->op == OCALLMETH) { + fn = n->left; + if(n->op == OCALLMETH) + fn = n->left->right->sym->def; + if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody) + if((m = visit(fn->defn)) < min) + min = m; + } + + if(n->op == OCLOSURE) + if((m = visit(n->closure)) < min) + min = m; + + return min; +} + +// An escape analysis pass for a set of functions. // // First escfunc, esc and escassign recurse over the ast of each // function to dig out flow(dst,src) edges between any @@ -22,75 +176,123 @@ // not escape, then new(T) can be rewritten into a stack allocation. // The same is true of slice literals. // -// If escape analysis is disabled (-s), this code is not used. +// If optimizations are disabled (-N), this code is not used. // Instead, the compiler assumes that any value whose address // is taken without being immediately dereferenced // needs to be moved to the heap, and new(T) and slice // literals are always real allocations. -#include -#include -#include "go.h" +typedef struct EscState EscState; + +static void escfunc(EscState*, Node *func); +static void esclist(EscState*, NodeList *l); +static void esc(EscState*, Node *n); +static void escloopdepthlist(EscState*, NodeList *l); +static void escloopdepth(EscState*, Node *n); +static void escassign(EscState*, Node *dst, Node *src); +static void esccall(EscState*, Node*); +static void escflows(EscState*, Node *dst, Node *src); +static void escflood(EscState*, Node *dst); +static void escwalk(EscState*, int level, Node *dst, Node *src); +static void esctag(EscState*, Node *func); + +struct EscState { + // Fake node that all + // - return values and output variables + // - parameters on imported functions not marked 'safe' + // - assignments to global variables + // flow to. + Node theSink; + + NodeList* dsts; // all dst nodes + int loopdepth; // for detecting nested loop scopes + int pdepth; // for debug printing in recursions. + int dstcount, edgecount; // diagnostic + NodeList* noesc; // list of possible non-escaping nodes, for printing + int recursive; // recursive function or group of mutually recursive functions. +}; + +static Strlit *tags[16]; + +static Strlit* +mktag(int mask) +{ + Strlit *s; + char buf[40]; + + switch(mask&EscMask) { + case EscNone: + case EscReturn: + break; + default: + fatal("escape mktag"); + } -static void escfunc(Node *func); -static void esclist(NodeList *l); -static void esc(Node *n); -static void escloopdepthlist(NodeList *l); -static void escloopdepth(Node *n); -static void escassign(Node *dst, Node *src); -static void esccall(Node*); -static void escflows(Node *dst, Node *src); -static void escflood(Node *dst); -static void escwalk(int level, Node *dst, Node *src); -static void esctag(Node *func); - -// Fake node that all -// - return values and output variables -// - parameters on imported functions not marked 'safe' -// - assignments to global variables -// flow to. -static Node theSink; - -static NodeList* dsts; // all dst nodes -static int loopdepth; // for detecting nested loop scopes -static int pdepth; // for debug printing in recursions. -static Strlit* safetag; // gets slapped on safe parameters' field types for export -static int dstcount, edgecount; // diagnostic -static NodeList* noesc; // list of possible non-escaping nodes, for printing + mask >>= EscBits; -void -escapes(NodeList *all) + if(mask < nelem(tags) && tags[mask] != nil) + return tags[mask]; + + snprint(buf, sizeof buf, "esc:0x%x", mask); + s = strlit(buf); + if(mask < nelem(tags)) + tags[mask] = s; + return s; +} + +static int +parsetag(Strlit *note) { - NodeList *l; + int em; + + if(note == nil) + return EscUnknown; + if(strncmp(note->s, "esc:", 4) != 0) + return EscUnknown; + em = atoi(note->s + 4); + if (em == 0) + return EscNone; + return EscReturn | (em << EscBits); +} - theSink.op = ONAME; - theSink.orig = &theSink; - theSink.class = PEXTERN; - theSink.sym = lookup(".sink"); - theSink.escloopdepth = -1; +static void +analyze(NodeList *all, int recursive) +{ + NodeList *l; + EscState es, *e; + + memset(&es, 0, sizeof es); + e = &es; + e->theSink.op = ONAME; + e->theSink.orig = &e->theSink; + e->theSink.class = PEXTERN; + e->theSink.sym = lookup(".sink"); + e->theSink.escloopdepth = -1; + e->recursive = recursive; - safetag = strlit("noescape"); - noesc = nil; + for(l=all; l; l=l->next) + if(l->n->op == ODCLFUNC) + l->n->esc = EscFuncPlanned; // flow-analyze functions for(l=all; l; l=l->next) - if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) - escfunc(l->n); + if(l->n->op == ODCLFUNC) + escfunc(e, l->n); - // print("escapes: %d dsts, %d edges\n", dstcount, edgecount); + // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount); - // visit the updstream of each dst, mark address nodes with + // visit the upstream of each dst, mark address nodes with // addrescapes, mark parameters unsafe - for(l = dsts; l; l=l->next) - escflood(l->n); + for(l = e->dsts; l; l=l->next) + escflood(e, l->n); // for all top level functions, tag the typenodes corresponding to the param nodes for(l=all; l; l=l->next) if(l->n->op == ODCLFUNC) - esctag(l->n); + esctag(e, l->n); if(debug['m']) { - for(l=noesc; l; l=l->next) + for(l=e->noesc; l; l=l->next) if(l->n->esc == EscNone) warnl(l->n->lineno, "%S %hN does not escape", (l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S, @@ -100,14 +302,20 @@ escapes(NodeList *all) static void -escfunc(Node *func) +escfunc(EscState *e, Node *func) { - Node *savefn, *n; + Node *savefn; NodeList *ll; int saveld; - saveld = loopdepth; - loopdepth = 1; +// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":""); + + if(func->esc != 1) + fatal("repeat escfunc %N", func->nname); + func->esc = EscFuncStarted; + + saveld = e->loopdepth; + e->loopdepth = 1; savefn = curfn; curfn = func; @@ -116,63 +324,54 @@ escfunc(Node *func) continue; switch (ll->n->class) { case PPARAMOUT: - // output parameters flow to the sink - escflows(&theSink, ll->n); - ll->n->escloopdepth = loopdepth; + // out params are in a loopdepth between the sink and all local variables + ll->n->escloopdepth = 0; break; case PPARAM: if(ll->n->type && !haspointers(ll->n->type)) break; - ll->n->esc = EscNone; // prime for escflood later - noesc = list(noesc, ll->n); - ll->n->escloopdepth = loopdepth; + if(curfn->nbody == nil && !curfn->noescape) + ll->n->esc = EscHeap; + else + ll->n->esc = EscNone; // prime for escflood later + e->noesc = list(e->noesc, ll->n); + ll->n->escloopdepth = 1; break; } } - // walk will take the address of cvar->closure later and assign it to cvar. - // linking a fake oaddr node directly to the closure handles the case - // of the closure itself leaking. Following the flow of the value to th - // paramref is done in escflow, because if we did that here, it would look - // like the original is assigned out of its loop depth, whereas it's just - // assigned to something in an inner function. A paramref itself is never - // moved to the heap, only its original. - for(ll=curfn->cvars; ll; ll=ll->next) { - if(ll->n->op == OXXX) // see dcl.c:398 - continue; - - n = nod(OADDR, ll->n->closure, N); - n->lineno = ll->n->lineno; - typecheck(&n, Erv); - escassign(curfn, n); - } + // in a mutually recursive group we lose track of the return values + if(e->recursive) + for(ll=curfn->dcl; ll; ll=ll->next) + if(ll->n->op == ONAME && ll->n->class == PPARAMOUT) + escflows(e, &e->theSink, ll->n); - escloopdepthlist(curfn->nbody); - esclist(curfn->nbody); + escloopdepthlist(e, curfn->nbody); + esclist(e, curfn->nbody); curfn = savefn; - loopdepth = saveld; + e->loopdepth = saveld; } -// Mark labels that have no backjumps to them as not increasing loopdepth. +// Mark labels that have no backjumps to them as not increasing e->loopdepth. // Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat // and set it to one of the following two. Then in esc we'll clear it again. static Label looping; static Label nonlooping; static void -escloopdepthlist(NodeList *l) +escloopdepthlist(EscState *e, NodeList *l) { for(; l; l=l->next) - escloopdepth(l->n); + escloopdepth(e, l->n); } static void -escloopdepth(Node *n) +escloopdepth(EscState *e, Node *n) { if(n == N) return; - escloopdepthlist(n->ninit); + escloopdepthlist(e, n->ninit); switch(n->op) { case OLABEL: @@ -194,29 +393,30 @@ escloopdepth(Node *n) break; } - escloopdepth(n->left); - escloopdepth(n->right); - escloopdepthlist(n->list); - escloopdepth(n->ntest); - escloopdepth(n->nincr); - escloopdepthlist(n->nbody); - escloopdepthlist(n->nelse); - escloopdepthlist(n->rlist); + escloopdepth(e, n->left); + escloopdepth(e, n->right); + escloopdepthlist(e, n->list); + escloopdepth(e, n->ntest); + escloopdepth(e, n->nincr); + escloopdepthlist(e, n->nbody); + escloopdepthlist(e, n->nelse); + escloopdepthlist(e, n->rlist); } static void -esclist(NodeList *l) +esclist(EscState *e, NodeList *l) { for(; l; l=l->next) - esc(l->n); + esc(e, l->n); } static void -esc(Node *n) +esc(EscState *e, Node *n) { int lno; NodeList *ll, *lr; + Node *a; if(n == N) return; @@ -224,33 +424,30 @@ esc(Node *n) lno = setlineno(n); if(n->op == OFOR || n->op == ORANGE) - loopdepth++; - - if(n->op == OCLOSURE) { - escfunc(n); - } else { - esc(n->left); - esc(n->right); - esc(n->ntest); - esc(n->nincr); - esclist(n->ninit); - esclist(n->nbody); - esclist(n->nelse); - esclist(n->list); - esclist(n->rlist); - } + e->loopdepth++; + + esc(e, n->left); + esc(e, n->right); + esc(e, n->ntest); + esc(e, n->nincr); + esclist(e, n->ninit); + esclist(e, n->nbody); + esclist(e, n->nelse); + esclist(e, n->list); + esclist(e, n->rlist); + if(n->op == OFOR || n->op == ORANGE) - loopdepth--; + e->loopdepth--; if(debug['m'] > 1) - print("%L:[%d] %S esc: %N\n", lineno, loopdepth, + print("%L:[%d] %S esc: %N\n", lineno, e->loopdepth, (curfn && curfn->nname) ? curfn->nname->sym : S, n); switch(n->op) { case ODCL: // Record loop depth at declaration. if(n->left) - n->left->escloopdepth = loopdepth; + n->left->escloopdepth = e->loopdepth; break; case OLABEL: @@ -260,18 +457,19 @@ esc(Node *n) } else if(n->left->sym->label == &looping) { if(debug['m'] > 1) print("%L: %N looping label\n", lineno, n); - loopdepth++; + e->loopdepth++; } // See case OLABEL in escloopdepth above // else if(n->left->sym->label == nil) - // fatal("escape anaylysis missed or messed up a label: %+N", n); + // fatal("escape analysis missed or messed up a label: %+N", n); n->left->sym->label = nil; + break; case ORANGE: // Everything but fixed array is a dereference. if(isfixedarray(n->type) && n->list->next) - escassign(n->list->next->n, n->right); + escassign(e, n->list->next->n, n->right); break; case OSWITCH: @@ -279,123 +477,157 @@ esc(Node *n) for(ll=n->list; ll; ll=ll->next) { // cases // ntest->right is the argument of the .(type), // ll->n->nname is the variable per case - escassign(ll->n->nname, n->ntest->right); + escassign(e, ll->n->nname, n->ntest->right); } } break; case OAS: case OASOP: - escassign(n->left, n->right); + escassign(e, n->left, n->right); break; case OAS2: // x,y = a,b if(count(n->list) == count(n->rlist)) for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next) - escassign(ll->n, lr->n); + escassign(e, ll->n, lr->n); break; case OAS2RECV: // v, ok = <-ch case OAS2MAPR: // v, ok = m[k] case OAS2DOTTYPE: // v, ok = x.(type) - escassign(n->list->n, n->rlist->n); + escassign(e, n->list->n, n->rlist->n); break; case OSEND: // ch <- x - escassign(&theSink, n->right); + escassign(e, &e->theSink, n->right); break; case ODEFER: - if(loopdepth == 1) // top level + if(e->loopdepth == 1) // top level break; // arguments leak out of scope // TODO: leak to a dummy node instead // fallthrough case OPROC: // go f(x) - f and x escape - escassign(&theSink, n->left->left); - escassign(&theSink, n->left->right); // ODDDARG for call + escassign(e, &e->theSink, n->left->left); + escassign(e, &e->theSink, n->left->right); // ODDDARG for call for(ll=n->left->list; ll; ll=ll->next) - escassign(&theSink, ll->n); + escassign(e, &e->theSink, ll->n); + break; + + case OCALLMETH: + case OCALLFUNC: + case OCALLINTER: + esccall(e, n); + break; + + case OAS2FUNC: // x,y = f() + // esccall already done on n->rlist->n. tie it's escretval to n->list + lr=n->rlist->n->escretval; + for(ll=n->list; lr && ll; lr=lr->next, ll=ll->next) + escassign(e, ll->n, lr->n); + if(lr || ll) + fatal("esc oas2func"); break; case ORETURN: - for(ll=n->list; ll; ll=ll->next) - escassign(&theSink, ll->n); + ll=n->list; + if(count(n->list) == 1 && curfn->type->outtuple > 1) { + // OAS2FUNC in disguise + // esccall already done on n->list->n + // tie n->list->n->escretval to curfn->dcl PPARAMOUT's + ll = n->list->n->escretval; + } + + for(lr = curfn->dcl; lr && ll; lr=lr->next) { + if (lr->n->op != ONAME || lr->n->class != PPARAMOUT) + continue; + escassign(e, lr->n, ll->n); + ll = ll->next; + } + if (ll != nil) + fatal("esc return list"); break; case OPANIC: // Argument could leak through recover. - escassign(&theSink, n->left); + escassign(e, &e->theSink, n->left); break; case OAPPEND: if(!n->isddd) for(ll=n->list->next; ll; ll=ll->next) - escassign(&theSink, ll->n); // lose track of assign to dereference - break; - - case OCALLMETH: - case OCALLFUNC: - case OCALLINTER: - esccall(n); + escassign(e, &e->theSink, ll->n); // lose track of assign to dereference break; case OCONV: case OCONVNOP: case OCONVIFACE: - escassign(n, n->left); + escassign(e, n, n->left); break; case OARRAYLIT: if(isslice(n->type)) { n->esc = EscNone; // until proven otherwise - noesc = list(noesc, n); - n->escloopdepth = loopdepth; + e->noesc = list(e->noesc, n); + n->escloopdepth = e->loopdepth; // Values make it to memory, lose track. for(ll=n->list; ll; ll=ll->next) - escassign(&theSink, ll->n->right); + escassign(e, &e->theSink, ll->n->right); } else { // Link values to array. for(ll=n->list; ll; ll=ll->next) - escassign(n, ll->n->right); + escassign(e, n, ll->n->right); } break; case OSTRUCTLIT: // Link values to struct. for(ll=n->list; ll; ll=ll->next) - escassign(n, ll->n->right); + escassign(e, n, ll->n->right); break; case OPTRLIT: n->esc = EscNone; // until proven otherwise - noesc = list(noesc, n); - n->escloopdepth = loopdepth; + e->noesc = list(e->noesc, n); + n->escloopdepth = e->loopdepth; // Contents make it to memory, lose track. - escassign(&theSink, n->left); + escassign(e, &e->theSink, n->left); break; case OMAPLIT: n->esc = EscNone; // until proven otherwise - noesc = list(noesc, n); - n->escloopdepth = loopdepth; + e->noesc = list(e->noesc, n); + n->escloopdepth = e->loopdepth; // Keys and values make it to memory, lose track. for(ll=n->list; ll; ll=ll->next) { - escassign(&theSink, ll->n->left); - escassign(&theSink, ll->n->right); + escassign(e, &e->theSink, ll->n->left); + escassign(e, &e->theSink, ll->n->right); } break; case OCLOSURE: + // Link addresses of captured variables to closure. + for(ll=n->cvars; ll; ll=ll->next) { + if(ll->n->op == OXXX) // unnamed out argument; see dcl.c:/^funcargs + continue; + a = nod(OADDR, ll->n->closure, N); + a->lineno = ll->n->lineno; + a->escloopdepth = e->loopdepth; + typecheck(&a, Erv); + escassign(e, n, a); + } + // fallthrough case OADDR: case OMAKECHAN: case OMAKEMAP: case OMAKESLICE: case ONEW: - n->escloopdepth = loopdepth; + n->escloopdepth = e->loopdepth; n->esc = EscNone; // until proven otherwise - noesc = list(noesc, n); + e->noesc = list(e->noesc, n); break; } @@ -407,21 +639,22 @@ esc(Node *n) // evaluated in curfn. For expr==nil, dst must still be examined for // evaluations inside it (e.g *f(x) = y) static void -escassign(Node *dst, Node *src) +escassign(EscState *e, Node *dst, Node *src) { int lno; + NodeList *ll; if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX) return; if(debug['m'] > 1) - print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, loopdepth, + print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, e->loopdepth, (curfn && curfn->nname) ? curfn->nname->sym : S, dst, dst, src, src); setlineno(dst); // Analyze lhs of assignment. - // Replace dst with theSink if we can't track it. + // Replace dst with e->theSink if we can't track it. switch(dst->op) { default: dump("dst", dst); @@ -438,31 +671,31 @@ escassign(Node *dst, Node *src) case ONAME: if(dst->class == PEXTERN) - dst = &theSink; + dst = &e->theSink; break; case ODOT: // treat "dst.x = src" as "dst = src" - escassign(dst->left, src); + escassign(e, dst->left, src); return; case OINDEX: if(isfixedarray(dst->left->type)) { - escassign(dst->left, src); + escassign(e, dst->left, src); return; } - dst = &theSink; // lose track of dereference + dst = &e->theSink; // lose track of dereference break; case OIND: case ODOTPTR: - dst = &theSink; // lose track of dereference + dst = &e->theSink; // lose track of dereference break; case OINDEXMAP: // lose track of key and value - escassign(&theSink, dst->right); - dst = &theSink; + escassign(e, &e->theSink, dst->right); + dst = &e->theSink; break; } lno = setlineno(src); - pdepth++; + e->pdepth++; switch(src->op) { case OADDR: // dst = &x @@ -480,7 +713,16 @@ escassign(Node *dst, Node *src) case OMAKESLICE: case ONEW: case OCLOSURE: - escflows(dst, src); + escflows(e, dst, src); + break; + + case OCALLMETH: + case OCALLFUNC: + case OCALLINTER: + // Flowing multiple returns to a single dst happens when + // analyzing "go f(g())": here g() flows to sink (issue 4529). + for(ll=src->escretval; ll; ll=ll->next) + escflows(e, dst, ll->n); break; case ODOT: @@ -498,18 +740,18 @@ escassign(Node *dst, Node *src) case OSLICE: case OSLICEARR: // Conversions, field access, slice all preserve the input value. - escassign(dst, src->left); + escassign(e, dst, src->left); break; case OAPPEND: // Append returns first argument. - escassign(dst, src->list->n); + escassign(e, dst, src->list->n); break; case OINDEX: // Index of array preserves input value. if(isfixedarray(src->left->type)) - escassign(dst, src->left); + escassign(e, dst, src->left); break; case OADD: @@ -529,29 +771,49 @@ escassign(Node *dst, Node *src) // Might be pointer arithmetic, in which case // the operands flow into the result. // TODO(rsc): Decide what the story is here. This is unsettling. - escassign(dst, src->left); - escassign(dst, src->right); + escassign(e, dst, src->left); + escassign(e, dst, src->right); break; - } - pdepth--; + e->pdepth--; lineno = lno; } +static void +escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src) +{ + int em; + + em = parsetag(note); + + if(em == EscUnknown) { + escassign(e, &e->theSink, src); + return; + } + + for(em >>= EscBits; em && dsts; em >>= 1, dsts=dsts->next) + if(em & 1) + escassign(e, dsts->n, src); + + if (em != 0 && dsts == nil) + fatal("corrupt esc tag %Z or messed up escretval list\n", note); +} -// This is a bit messier than fortunate, pulled out of escassign's big +// This is a bit messier than fortunate, pulled out of esc's big // switch for clarity. We either have the paramnodes, which may be -// connected to other things throug flows or we have the parameter type +// connected to other things through flows or we have the parameter type // nodes, which may be marked "noescape". Navigating the ast is slightly // different for methods vs plain functions and for imported vs // this-package static void -esccall(Node *n) +esccall(EscState *e, Node *n) { NodeList *ll, *lr; Node *a, *fn, *src; Type *t, *fntype; + char buf[40]; + int i; fn = N; switch(n->op) { @@ -579,74 +841,96 @@ esccall(Node *n) ll = n->list; if(n->list != nil && n->list->next == nil) { a = n->list->n; - if(a->type->etype == TSTRUCT && a->type->funarg) { - // f(g()). - // Since f's arguments are g's results and - // all function results escape, we're done. - ll = nil; - } + if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()). + ll = a->escretval; } - if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype) { - // Local function. Incorporate into flow graph. + if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) { + // function in same mutually recursive group. Incorporate into flow graph. +// print("esc local fn: %N\n", fn->ntype); + if(fn->defn->esc == EscFuncUnknown || n->escretval != nil) + fatal("graph inconsistency"); + + // set up out list on this call node + for(lr=fn->ntype->rlist; lr; lr=lr->next) + n->escretval = list(n->escretval, lr->n->left); // type.rlist -> dclfield -> ONAME (PPARAMOUT) // Receiver. if(n->op != OCALLFUNC) - escassign(fn->ntype->left->left, n->left->left); + escassign(e, fn->ntype->left->left, n->left->left); for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) { src = ll->n; if(lr->n->isddd && !n->isddd) { // Introduce ODDDARG node to represent ... allocation. src = nod(ODDDARG, N, N); - src->escloopdepth = loopdepth; + src->escloopdepth = e->loopdepth; src->lineno = n->lineno; src->esc = EscNone; // until we find otherwise - noesc = list(noesc, src); + e->noesc = list(e->noesc, src); n->right = src; } if(lr->n->left != N) - escassign(lr->n->left, src); + escassign(e, lr->n->left, src); if(src != ll->n) break; } // "..." arguments are untracked for(; ll; ll=ll->next) - escassign(&theSink, ll->n); + escassign(e, &e->theSink, ll->n); + return; } - // Imported function. Use the escape tags. - if(n->op != OCALLFUNC) { - t = getthisx(fntype)->type; - if(!t->note || strcmp(t->note->s, safetag->s) != 0) - escassign(&theSink, n->left->left); + // Imported or completely analyzed function. Use the escape tags. + if(n->escretval != nil) + fatal("esc already decorated call %+N\n", n); + + // set up out list on this call node with dummy auto ONAMES in the current (calling) function. + i = 0; + for(t=getoutargx(fntype)->type; t; t=t->down) { + src = nod(ONAME, N, N); + snprint(buf, sizeof buf, ".dum%d", i++); + src->sym = lookup(buf); + src->type = t->type; + src->class = PAUTO; + src->curfn = curfn; + src->escloopdepth = e->loopdepth; + src->used = 1; + src->lineno = n->lineno; + n->escretval = list(n->escretval, src); } + +// print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); + + // Receiver. + if(n->op != OCALLFUNC) + escassignfromtag(e, getthisx(fntype)->type->note, n->escretval, n->left->left); + for(t=getinargx(fntype)->type; ll; ll=ll->next) { src = ll->n; if(t->isddd && !n->isddd) { // Introduce ODDDARG node to represent ... allocation. src = nod(ODDDARG, N, N); - src->escloopdepth = loopdepth; + src->escloopdepth = e->loopdepth; src->lineno = n->lineno; src->esc = EscNone; // until we find otherwise - noesc = list(noesc, src); + e->noesc = list(e->noesc, src); n->right = src; } - if(!t->note || strcmp(t->note->s, safetag->s) != 0) - escassign(&theSink, src); + escassignfromtag(e, t->note, n->escretval, src); if(src != ll->n) break; t = t->down; } // "..." arguments are untracked for(; ll; ll=ll->next) - escassign(&theSink, ll->n); + escassign(e, &e->theSink, ll->n); } // Store the link src->dst in dst, throwing out some quick wins. static void -escflows(Node *dst, Node *src) +escflows(EscState *e, Node *dst, Node *src) { if(dst == nil || src == nil || dst == src) return; @@ -659,10 +943,10 @@ escflows(Node *dst, Node *src) print("%L::flows:: %hN <- %hN\n", lineno, dst, src); if(dst->escflowsrc == nil) { - dsts = list(dsts, dst); - dstcount++; + e->dsts = list(e->dsts, dst); + e->dstcount++; } - edgecount++; + e->edgecount++; dst->escflowsrc = list(dst->escflowsrc, src); } @@ -673,11 +957,11 @@ escflows(Node *dst, Node *src) // so this address doesn't leak (yet). // If level == 0, it means the /value/ of this node can reach the root of this flood. // so if this node is an OADDR, it's argument should be marked as escaping iff -// it's currfn/loopdepth are different from the flood's root. +// it's currfn/e->loopdepth are different from the flood's root. // Once an object has been moved to the heap, all of it's upstream should be considered // escaping to the global scope. static void -escflood(Node *dst) +escflood(EscState *e, Node *dst) { NodeList *l; @@ -696,45 +980,71 @@ escflood(Node *dst) for(l = dst->escflowsrc; l; l=l->next) { walkgen++; - escwalk(0, dst, l->n); + escwalk(e, 0, dst, l->n); } } +// There appear to be some loops in the escape graph, causing +// arbitrary recursion into deeper and deeper levels. +// Cut this off safely by making minLevel sticky: once you +// get that deep, you cannot go down any further but you also +// cannot go up any further. This is a conservative fix. +// Making minLevel smaller (more negative) would handle more +// complex chains of indirections followed by address-of operations, +// at the cost of repeating the traversal once for each additional +// allowed level when a loop is encountered. Using -2 suffices to +// pass all the tests we have written so far, which we assume matches +// the level of complexity we want the escape analysis code to handle. +#define MinLevel (-2) + static void -escwalk(int level, Node *dst, Node *src) +escwalk(EscState *e, int level, Node *dst, Node *src) { NodeList *ll; - int leaks; + int leaks, newlevel; - if(src->walkgen == walkgen) + if(src->walkgen == walkgen && src->esclevel <= level) return; src->walkgen = walkgen; + src->esclevel = level; if(debug['m']>1) print("escwalk: level:%d depth:%d %.*s %hN(%hJ) scope:%S[%d]\n", - level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src, + level, e->pdepth, e->pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src, (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth); - pdepth++; + e->pdepth++; + + // Input parameter flowing to output parameter? + if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen <= 20) { + if(src->op == ONAME && src->class == PPARAM && level == 0 && src->curfn == dst->curfn) { + if(src->esc != EscScope && src->esc != EscHeap) { + if(debug['m']) + warnl(src->lineno, "leaking param: %hN to result %S", src, dst->sym); + if((src->esc&EscMask) != EscReturn) + src->esc = EscReturn; + src->esc |= 1<<((dst->vargen-1) + EscBits); + goto recurse; + } + } + } leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth); switch(src->op) { case ONAME: - if(src->class == PPARAM && leaks && src->esc == EscNone) { + if(src->class == PPARAM && leaks && src->esc != EscHeap) { src->esc = EscScope; if(debug['m']) warnl(src->lineno, "leaking param: %hN", src); } - // handle the missing flow ref <- orig - // a paramref is automagically dereferenced, and taking its - // address produces the address of the original, so all we have to do here - // is keep track of the value flow, so level is unchanged. - // alternatively, we could have substituted PPARAMREFs with their ->closure in esc/escassign/flow, + + // Treat a PPARAMREF closure variable as equivalent to the + // original variable. if(src->class == PPARAMREF) { if(leaks && debug['m']) warnl(src->lineno, "leaking closure reference %hN", src); - escwalk(level, dst, src->closure); + escwalk(e, level, dst, src->closure); } break; @@ -746,7 +1056,10 @@ escwalk(int level, Node *dst, Node *src) if(debug['m']) warnl(src->lineno, "%hN escapes to heap", src); } - escwalk(level-1, dst, src->left); + newlevel = level; + if(level > MinLevel) + newlevel--; + escwalk(e, newlevel, dst, src->left); break; case OARRAYLIT: @@ -767,32 +1080,53 @@ escwalk(int level, Node *dst, Node *src) } break; + case ODOT: + escwalk(e, level, dst, src->left); + break; + case OINDEX: - if(isfixedarray(src->type)) + if(isfixedarray(src->left->type)) { + escwalk(e, level, dst, src->left); break; + } // fall through case OSLICE: case ODOTPTR: case OINDEXMAP: case OIND: - escwalk(level+1, dst, src->left); + newlevel = level; + if(level > MinLevel) + newlevel++; + escwalk(e, newlevel, dst, src->left); } +recurse: for(ll=src->escflowsrc; ll; ll=ll->next) - escwalk(level, dst, ll->n); + escwalk(e, level, dst, ll->n); - pdepth--; + e->pdepth--; } static void -esctag(Node *func) +esctag(EscState *e, Node *func) { Node *savefn; NodeList *ll; + Type *t; + + USED(e); + func->esc = EscFuncTagged; - // External functions must be assumed unsafe. - if(func->nbody == nil) + // External functions are assumed unsafe, + // unless //go:noescape is given before the declaration. + if(func->nbody == nil) { + if(func->noescape) { + for(t=getinargx(func->type)->type; t; t=t->down) + if(haspointers(t->type)) + t->note = mktag(EscNone); + } return; + } savefn = curfn; curfn = func; @@ -801,10 +1135,12 @@ esctag(Node *func) if(ll->n->op != ONAME || ll->n->class != PPARAM) continue; - switch (ll->n->esc) { + switch (ll->n->esc&EscMask) { case EscNone: // not touched by escflood + case EscReturn: if(haspointers(ll->n->type)) // don't bother tagging for scalars - ll->n->paramfld->note = safetag; + ll->n->paramfld->note = mktag(ll->n->esc); + break; case EscHeap: // touched by escflood, moved to heap case EscScope: // touched by escflood, value leaves scope break; diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index bbed8ae36..b7311665a 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -22,22 +22,8 @@ exportsym(Node *n) } n->sym->flags |= SymExport; - exportlist = list(exportlist, n); -} - -// Mark n's symbol as package-local -static void -packagesym(Node *n) -{ - if(n == N || n->sym == S) - return; - if(n->sym->flags & (SymExport|SymPackage)) { - if(n->sym->flags & SymExport) - yyerror("export/package mismatch: %S", n->sym); - return; - } - n->sym->flags |= SymPackage; - + if(debug['E']) + print("export symbol %S\n", n->sym); exportlist = list(exportlist, n); } @@ -58,6 +44,18 @@ initname(char *s) return strcmp(s, "init") == 0; } +// exportedsym returns whether a symbol will be visible +// to files that import our package. +static int +exportedsym(Sym *sym) +{ + // Builtins are visible everywhere. + if(sym->pkg == builtinpkg || sym->origpkg == builtinpkg) + return 1; + + return sym->pkg == localpkg && exportname(sym->name); +} + void autoexport(Node *n, int ctxt) { @@ -67,10 +65,9 @@ autoexport(Node *n, int ctxt) return; if(n->ntype && n->ntype->op == OTFUNC && n->ntype->left) // method return; - if(exportname(n->sym->name) || initname(n->sym->name)) + // -A is for cmd/gc/mkbuiltin script, so export everything + if(debug['A'] || exportname(n->sym->name) || initname(n->sym->name)) exportsym(n); - else - packagesym(n); } static void @@ -104,36 +101,60 @@ reexportdep(Node *n) if(!n) return; -// print("reexportdep %+hN\n", n); + //print("reexportdep %+hN\n", n); switch(n->op) { case ONAME: switch(n->class&~PHEAP) { case PFUNC: // methods will be printed along with their type + // nodes for T.Method expressions + if(n->left && n->left->op == OTYPE) + break; + // nodes for method calls. if(!n->type || n->type->thistuple > 0) break; // fallthrough case PEXTERN: - if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg) + if(n->sym && !exportedsym(n->sym)) { + if(debug['E']) + print("reexport name %S\n", n->sym); exportlist = list(exportlist, n); + } } break; + case ODCL: + // Local variables in the bodies need their type. + t = n->left->type; + if(t != types[t->etype] && t != idealbool && t != idealstring) { + if(isptr[t->etype]) + t = t->type; + if(t && t->sym && t->sym->def && !exportedsym(t->sym)) { + if(debug['E']) + print("reexport type %S from declaration\n", t->sym); + exportlist = list(exportlist, t->sym->def); + } + } + break; case OLITERAL: t = n->type; if(t != types[n->type->etype] && t != idealbool && t != idealstring) { if(isptr[t->etype]) t = t->type; - if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) { -// print("reexport literal type %+hN\n", t->sym->def); + if(t && t->sym && t->sym->def && !exportedsym(t->sym)) { + if(debug['E']) + print("reexport literal type %S\n", t->sym); exportlist = list(exportlist, t->sym->def); } } // fallthrough case OTYPE: - if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg) + if(n->sym && !exportedsym(n->sym)) { + if(debug['E']) + print("reexport literal/type %S\n", n->sym); exportlist = list(exportlist, n); + } break; // for operations that need a type when rendered, put the type on the export list. @@ -141,13 +162,15 @@ reexportdep(Node *n) case OCONVIFACE: case OCONVNOP: case ODOTTYPE: + case ODOTTYPE2: case OSTRUCTLIT: case OPTRLIT: t = n->type; if(!t->sym && t->type) t = t->type; - if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) { -// print("reexport convnop %+hN\n", t->sym->def); + if(t && t->sym && t->sym->def && !exportedsym(t->sym)) { + if(debug['E']) + print("reexport type for convnop %S\n", t->sym); exportlist = list(exportlist, t->sym->def); } break; @@ -207,10 +230,11 @@ dumpexportvar(Sym *s) // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package if(debug['l'] < 2) typecheckinl(n); - Bprint(bout, "\tfunc %#S%#hT { %#H }\n", s, t, n->inl); + // NOTE: The space after %#S here is necessary for ld's export data parser. + Bprint(bout, "\tfunc %#S %#hT { %#H }\n", s, t, n->inl); reexportdeplist(n->inl); } else - Bprint(bout, "\tfunc %#S%#hT\n", s, t); + Bprint(bout, "\tfunc %#S %#hT\n", s, t); } else Bprint(bout, "\tvar %#S %#T\n", s, t); } @@ -262,15 +286,17 @@ dumpexporttype(Type *t) Bprint(bout, "\ttype %#S %#lT\n", t->sym, t); for(i=0; inointerface) + Bprint(bout, "\t//go:nointerface\n"); if (f->type->nname && f->type->nname->inl) { // nname was set by caninl // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package if(debug['l'] < 2) typecheckinl(f->type->nname); - Bprint(bout, "\tfunc (%#T) %#hhS%#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl); + Bprint(bout, "\tfunc (%#T) %#hhS %#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl); reexportdeplist(f->type->nname->inl); } else - Bprint(bout, "\tfunc (%#T) %#hhS%#hT\n", getthisx(f->type)->type, f->sym, f->type); + Bprint(bout, "\tfunc (%#T) %#hhS %#hT\n", getthisx(f->type)->type, f->sym, f->type); } } @@ -349,8 +375,12 @@ dumpexport(void) Sym* importsym(Sym *s, int op) { - if(s->def != N && s->def->op != op) - redeclare(s, "during import"); + char *pkgstr; + + if(s->def != N && s->def->op != op) { + pkgstr = smprint("during import \"%Z\"", importpkg->path); + redeclare(s, pkgstr); + } // mark the symbol so it is not reexported if(s->def == N) { @@ -389,6 +419,8 @@ importimport(Sym *s, Strlit *z) // human-readable messages. Pkg *p; + if(isbadimport(z)) + errorexit(); p = mkpkg(z); if(p->name == nil) { p->name = s->name; diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index 5672c0010..ab81e6c88 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -228,6 +228,7 @@ goopnames[] = [ORANGE] = "range", [OREAL] = "real", [ORECV] = "<-", + [ORECOVER] = "recover", [ORETURN] = "return", [ORSH] = ">>", [OSELECT] = "select", @@ -289,7 +290,7 @@ Jconv(Fmt *fp) fmtprint(fp, " l(%d)", n->lineno); if(!c && n->xoffset != BADWIDTH) - fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta); + fmtprint(fp, " x(%lld%+lld)", n->xoffset, n->stkdelta); if(n->class != 0) { s = ""; @@ -361,6 +362,8 @@ Vconv(Fmt *fp) switch(v->ctype) { case CTINT: + if((fp->flags & FmtSharp) || fmtmode == FExp) + return fmtprint(fp, "%#B", v->u.xval); return fmtprint(fp, "%B", v->u.xval); case CTRUNE: x = mpgetfix(v->u.xval); @@ -377,8 +380,14 @@ Vconv(Fmt *fp) return fmtprint(fp, "%#F", v->u.fval); case CTCPLX: if((fp->flags & FmtSharp) || fmtmode == FExp) - return fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag); - return fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag); + return fmtprint(fp, "(%F+%Fi)", &v->u.cval->real, &v->u.cval->imag); + if(mpcmpfltc(&v->u.cval->real, 0) == 0) + return fmtprint(fp, "%#Fi", &v->u.cval->imag); + if(mpcmpfltc(&v->u.cval->imag, 0) == 0) + return fmtprint(fp, "%#F", &v->u.cval->real); + if(mpcmpfltc(&v->u.cval->imag, 0) < 0) + return fmtprint(fp, "(%#F%#Fi)", &v->u.cval->real, &v->u.cval->imag); + return fmtprint(fp, "(%#F+%#Fi)", &v->u.cval->real, &v->u.cval->imag); case CTSTR: return fmtprint(fp, "\"%Z\"", v->u.sval); case CTBOOL: @@ -388,7 +397,7 @@ Vconv(Fmt *fp) case CTNIL: return fmtstrcpy(fp, "nil"); } - return fmtprint(fp, "<%d>", v->ctype); + return fmtprint(fp, "", v->ctype); } // Fmt "%Z": escaped string literals @@ -516,6 +525,8 @@ symfmt(Fmt *fp, Sym *s) return fmtprint(fp, "%s.%s", s->pkg->name, s->name); // dcommontype, typehash return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name); // (methodsym), typesym, weaksym case FExp: + if(s->name && s->name[0] == '.') + fatal("exporting synthetic symbol %s", s->name); if(s->pkg != builtinpkg) return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name); } @@ -711,12 +722,21 @@ typefmt(Fmt *fp, Type *t) case TFIELD: if(!(fp->flags&FmtShort)) { s = t->sym; - // Take the name from the original, lest we substituted it with .anon%d - if (t->nname && (fmtmode == FErr || fmtmode == FExp)) - s = t->nname->orig->sym; + + // Take the name from the original, lest we substituted it with ~anon%d + if ((fmtmode == FErr || fmtmode == FExp) && t->nname != N) { + if(t->nname->orig != N) { + s = t->nname->orig->sym; + if(s != S && s->name[0] == '~') + s = S; + } else + s = S; + } if(s != S && !t->embedded) { - if(fp->flags&FmtLong) + if(t->funarg) + fmtprint(fp, "%N ", t->nname); + else if(fp->flags&FmtLong) fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg) else fmtprint(fp, "%S ", s); @@ -794,6 +814,15 @@ stmtfmt(Fmt *f, Node *n) switch(n->op){ case ODCL: + if(fmtmode == FExp) { + switch(n->left->class&~PHEAP) { + case PPARAM: + case PPARAMOUT: + case PAUTO: + fmtprint(f, "var %N %T", n->left, n->left->type); + goto ret; + } + } fmtprint(f, "var %S %T", n->left->sym, n->left->type); break; @@ -805,6 +834,12 @@ stmtfmt(Fmt *f, Node *n) break; case OAS: + // Don't export "v = " initializing statements, hope they're always + // preceded by the DCL which will be re-parsed and typecheck to reproduce + // the "v = " again. + if(fmtmode == FExp && n->right == N) + break; + if(n->colas && !complexinit) fmtprint(f, "%N := %N", n->left, n->right); else @@ -925,6 +960,7 @@ stmtfmt(Fmt *f, Node *n) break; } +ret: if(extrablock) fmtstrcpy(f, "}"); @@ -962,7 +998,6 @@ static int opprec[] = { [OPAREN] = 8, [OPRINTN] = 8, [OPRINT] = 8, - [ORECV] = 8, [ORUNESTR] = 8, [OSTRARRAYBYTE] = 8, [OSTRARRAYRUNE] = 8, @@ -994,6 +1029,7 @@ static int opprec[] = { [OMINUS] = 7, [OADDR] = 7, [OIND] = 7, + [ORECV] = 7, [OMUL] = 6, [ODIV] = 6, @@ -1055,8 +1091,8 @@ static int exprfmt(Fmt *f, Node *n, int prec) { int nprec; + int ptrlit; NodeList *l; - Type *t; while(n && n->implicit && (n->op == OIND || n->op == OADDR)) n = n->left; @@ -1084,9 +1120,9 @@ exprfmt(Fmt *f, Node *n, int prec) case OLITERAL: // this is a bit of a mess if(fmtmode == FErr && n->sym != S) return fmtprint(f, "%S", n->sym); - if(n->val.ctype == CTNIL) - n = n->orig; // if this node was a nil decorated with at type, print the original naked nil - if(n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) { + if(n->val.ctype == CTNIL && n->orig != N && n->orig != n) + return exprfmt(f, n->orig, prec); + if(n->type != T && n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) { // Need parens when type begins with what might // be misinterpreted as a unary operator: * or <-. if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv)) @@ -1097,6 +1133,15 @@ exprfmt(Fmt *f, Node *n, int prec) return fmtprint(f, "%V", &n->val); case ONAME: + // Special case: name used as local variable in export. + switch(n->class&~PHEAP){ + case PAUTO: + case PPARAM: + case PPARAMOUT: + if(fmtmode == FExp && n->sym && !isblanksym(n->sym) && n->vargen > 0) + return fmtprint(f, "%S·%d", n->sym, n->vargen); + } + // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, // but for export, this should be rendered as (*pkg.T).meth. // These nodes have the special property that they are names with a left OTYPE and a right ONAME. @@ -1152,12 +1197,25 @@ exprfmt(Fmt *f, Node *n, int prec) case OCLOSURE: if(fmtmode == FErr) return fmtstrcpy(f, "func literal"); - return fmtprint(f, "%T { %H }", n->type, n->nbody); + if(n->nbody) + return fmtprint(f, "%T { %H }", n->type, n->nbody); + return fmtprint(f, "%T { %H }", n->type, n->closure->nbody); case OCOMPLIT: - if(fmtmode == FErr) + ptrlit = n->right != N && n->right->implicit && n->right->type && isptr[n->right->type->etype]; + if(fmtmode == FErr) { + if(n->right != N && n->right->type != T && !n->implicit) { + if(ptrlit) + return fmtprint(f, "&%T literal", n->right->type->type); + else + return fmtprint(f, "%T literal", n->right->type); + } return fmtstrcpy(f, "composite literal"); - return fmtprint(f, "%N{ %,H }", n->right, n->list); + } + if(fmtmode == FExp && ptrlit) + // typecheck has overwritten OIND by OTYPE with pointer type. + return fmtprint(f, "&%T{ %,H }", n->right->type->type, n->list); + return fmtprint(f, "(%N{ %,H })", n->right, n->list); case OPTRLIT: if(fmtmode == FExp && n->left->implicit) @@ -1168,24 +1226,18 @@ exprfmt(Fmt *f, Node *n, int prec) if(fmtmode == FExp) { // requires special handling of field names if(n->implicit) fmtstrcpy(f, "{"); - else - fmtprint(f, "%T{", n->type); + else + fmtprint(f, "(%T{", n->type); for(l=n->list; l; l=l->next) { - // another special case: if n->left is an embedded field of builtin type, - // it needs to be non-qualified. Can't figure that out in %S, so do it here - if(l->n->left->type->embedded) { - t = l->n->left->type->type; - if(t->sym == S) - t = t->type; - fmtprint(f, " %T:%N", t, l->n->right); - } else - fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right); + fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right); if(l->next) fmtstrcpy(f, ","); else fmtstrcpy(f, " "); } + if(!n->implicit) + return fmtstrcpy(f, "})"); return fmtstrcpy(f, "}"); } // fallthrough @@ -1196,11 +1248,16 @@ exprfmt(Fmt *f, Node *n, int prec) return fmtprint(f, "%T literal", n->type); if(fmtmode == FExp && n->implicit) return fmtprint(f, "{ %,H }", n->list); - return fmtprint(f, "%T{ %,H }", n->type, n->list); + return fmtprint(f, "(%T{ %,H })", n->type, n->list); case OKEY: - if(n->left && n->right) - return fmtprint(f, "%N:%N", n->left, n->right); + if(n->left && n->right) { + if(fmtmode == FExp && n->left->type && n->left->type->etype == TFIELD) { + // requires special handling of field names + return fmtprint(f, "%hhS:%N", n->left->sym, n->right); + } else + return fmtprint(f, "%N:%N", n->left, n->right); + } if(!n->left && n->right) return fmtprint(f, ":%N", n->right); if(n->left && !n->right) @@ -1260,6 +1317,7 @@ exprfmt(Fmt *f, Node *n, int prec) case OMAKE: case ONEW: case OPANIC: + case ORECOVER: case OPRINT: case OPRINTN: if(n->left) @@ -1408,6 +1466,7 @@ nodedump(Fmt *fp, Node *n) fmtprint(fp, "%O%J", n->op, n); break; case OREGISTER: + case OINDREG: fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n); break; case OLITERAL: @@ -1419,6 +1478,10 @@ nodedump(Fmt *fp, Node *n) fmtprint(fp, "%O-%S%J", n->op, n->sym, n); else fmtprint(fp, "%O%J", n->op, n); + if(recur && n->type == T && n->ntype) { + indent(fp); + fmtprint(fp, "%O-ntype%N", n->op, n->ntype); + } break; case OASOP: fmtprint(fp, "%O-%O%J", n->op, n->etype, n); @@ -1629,11 +1692,11 @@ fmtinstallgo(void) void dumplist(char *s, NodeList *l) { - print("%s\n%+H\n", s, l); + print("%s%+H\n", s, l); } void dump(char *s, Node *n) { - print("%s [%p]\n%+N\n", s, n, n); + print("%s [%p]%+N\n", s, n, n); } diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 96e7b526c..5f03d9476 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -266,6 +266,8 @@ gen(Node *n) Label *lab; int32 wasregalloc; +//dump("gen", n); + lno = setlineno(n); wasregalloc = anyregalloc(); @@ -279,7 +281,7 @@ gen(Node *n) switch(n->op) { default: - fatal("gen: unknown op %N", n); + fatal("gen: unknown op %+hN", n); break; case OCASE: @@ -394,7 +396,7 @@ gen(Node *n) } gen(n->nincr); // contin: incr patch(p1, pc); // test: - bgen(n->ntest, 0, breakpc); // if(!test) goto break + bgen(n->ntest, 0, -1, breakpc); // if(!test) goto break genlist(n->nbody); // body gjmp(continpc); patch(breakpc, pc); // done: @@ -410,7 +412,7 @@ gen(Node *n) p1 = gjmp(P); // goto test p2 = gjmp(P); // p2: goto else patch(p1, pc); // test: - bgen(n->ntest, 0, p2); // if(!test) goto p2 + bgen(n->ntest, 0, -n->likely, p2); // if(!test) goto p2 genlist(n->nbody); // then p3 = gjmp(P); // goto done patch(p2, pc); // else: @@ -509,22 +511,24 @@ ret: void cgen_callmeth(Node *n, int proc) { + Node n2; Node *l; - // generate a rewrite for method call + // generate a rewrite in n2 for the method call // (p.f)(...) goes to (f)(p,...) l = n->left; if(l->op != ODOTMETH) fatal("cgen_callmeth: not dotmethod: %N"); - n->op = OCALLFUNC; - n->left = n->left->right; - n->left->type = l->type; + n2 = *n; + n2.op = OCALLFUNC; + n2.left = l->right; + n2.left->type = l->type; - if(n->left->op == ONAME) - n->left->class = PFUNC; - cgen_call(n, proc); + if(n2.left->op == ONAME) + n2.left->class = PFUNC; + cgen_call(&n2, proc); } /* @@ -630,6 +634,67 @@ cgen_discard(Node *nr) } } +/* + * clearslim generates code to zero a slim node. + */ +void +clearslim(Node *n) +{ + Node z; + Mpflt zero; + + memset(&z, 0, sizeof(z)); + z.op = OLITERAL; + z.type = n->type; + z.addable = 1; + + switch(simtype[n->type->etype]) { + case TCOMPLEX64: + case TCOMPLEX128: + z.val.u.cval = mal(sizeof(*z.val.u.cval)); + mpmovecflt(&z.val.u.cval->real, 0.0); + mpmovecflt(&z.val.u.cval->imag, 0.0); + break; + + case TFLOAT32: + case TFLOAT64: + mpmovecflt(&zero, 0.0); + z.val.ctype = CTFLT; + z.val.u.fval = &zero; + break; + + case TPTR32: + case TPTR64: + case TCHAN: + case TMAP: + z.val.ctype = CTNIL; + break; + + case TBOOL: + z.val.ctype = CTBOOL; + break; + + case TINT8: + case TINT16: + case TINT32: + case TINT64: + case TUINT8: + case TUINT16: + case TUINT32: + case TUINT64: + z.val.ctype = CTINT; + z.val.u.xval = mal(sizeof(*z.val.u.xval)); + mpmovecfix(z.val.u.xval, 0); + break; + + default: + fatal("clearslim called on type %T", n->type); + } + + ullmancalc(&z); + cgen(&z, n); +} + /* * generate assignment: * nl = nr @@ -638,9 +703,7 @@ cgen_discard(Node *nr) void cgen_as(Node *nl, Node *nr) { - Node nc; Type *tl; - int iszer; if(debug['g']) { dump("cgen_as", nl); @@ -655,7 +718,6 @@ cgen_as(Node *nl, Node *nr) return; } - iszer = 0; if(nr == N || isnil(nr)) { // externals and heaps should already be clear if(nr == N) { @@ -670,59 +732,10 @@ cgen_as(Node *nl, Node *nr) return; if(isfat(tl)) { clearfat(nl); - goto ret; - } - - /* invent a "zero" for the rhs */ - iszer = 1; - nr = &nc; - memset(nr, 0, sizeof(*nr)); - switch(simtype[tl->etype]) { - default: - fatal("cgen_as: tl %T", tl); - break; - - case TINT8: - case TUINT8: - case TINT16: - case TUINT16: - case TINT32: - case TUINT32: - case TINT64: - case TUINT64: - nr->val.u.xval = mal(sizeof(*nr->val.u.xval)); - mpmovecfix(nr->val.u.xval, 0); - nr->val.ctype = CTINT; - break; - - case TFLOAT32: - case TFLOAT64: - nr->val.u.fval = mal(sizeof(*nr->val.u.fval)); - mpmovecflt(nr->val.u.fval, 0.0); - nr->val.ctype = CTFLT; - break; - - case TBOOL: - nr->val.u.bval = 0; - nr->val.ctype = CTBOOL; - break; - - case TPTR32: - case TPTR64: - nr->val.ctype = CTNIL; - break; - - case TCOMPLEX64: - case TCOMPLEX128: - nr->val.u.cval = mal(sizeof(*nr->val.u.cval)); - mpmovecflt(&nr->val.u.cval->real, 0.0); - mpmovecflt(&nr->val.u.cval->imag, 0.0); - break; + return; } - nr->op = OLITERAL; - nr->type = tl; - nr->addable = 1; - ullmancalc(nr); + clearslim(nl); + return; } tl = nl->type; @@ -730,11 +743,88 @@ cgen_as(Node *nl, Node *nr) return; cgen(nr, nl); - if(iszer && nl->addable) - gused(nl); +} -ret: - ; +/* + * generate: + * res = iface{typ, data} + * n->left is typ + * n->right is data + */ +void +cgen_eface(Node *n, Node *res) +{ + /* + * the right node of an eface may contain function calls that uses res as an argument, + * so it's important that it is done first + */ + Node dst; + dst = *res; + dst.type = types[tptr]; + dst.xoffset += widthptr; + cgen(n->right, &dst); + dst.xoffset -= widthptr; + cgen(n->left, &dst); +} + +/* + * generate: + * res = s[lo, hi]; + * n->left is s + * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)]) + * caller (cgen) guarantees res is an addable ONAME. + */ +void +cgen_slice(Node *n, Node *res) +{ + Node src, dst, *cap, *len, *offs, *add; + + cap = n->list->n; + len = n->list->next->n; + offs = N; + if(n->list->next->next) + offs = n->list->next->next->n; + + // dst.len = hi [ - lo ] + dst = *res; + dst.xoffset += Array_nel; + dst.type = types[simtype[TUINT]]; + cgen(len, &dst); + + if(n->op != OSLICESTR) { + // dst.cap = cap [ - lo ] + dst = *res; + dst.xoffset += Array_cap; + dst.type = types[simtype[TUINT]]; + cgen(cap, &dst); + } + + // dst.array = src.array [ + lo *width ] + dst = *res; + dst.xoffset += Array_array; + dst.type = types[TUINTPTR]; + + if(n->op == OSLICEARR) { + if(!isptr[n->left->type->etype]) + fatal("slicearr is supposed to work on pointer: %+N\n", n); + checkref(n->left); + } + + if(isnil(n->left)) { + tempname(&src, n->left->type); + cgen(n->left, &src); + } else + src = *n->left; + src.xoffset += Array_array; + src.type = types[TUINTPTR]; + + if(offs == N) { + cgen(&src, &dst); + } else { + add = nod(OADD, &src, offs); + typecheck(&add, Erv); + cgen(add, &dst); + } } /* diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors index e29cfff5b..68a5e5af3 100644 --- a/src/cmd/gc/go.errors +++ b/src/cmd/gc/go.errors @@ -65,6 +65,9 @@ static struct { % loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';' "need trailing comma before newline in composite literal", + % loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';' + "need trailing comma before newline in composite literal", + % loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME "nested func not allowed", diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 43bd68793..f86c152f2 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -137,6 +137,7 @@ typedef struct Label Label; struct Type { uchar etype; + uchar nointerface; uchar chan; uchar trecur; // to detect loops uchar printed; @@ -146,7 +147,7 @@ struct Type uchar copyany; uchar local; // created in this file uchar deferwidth; - uchar broke; + uchar broke; // broken type definition. uchar isddd; // TFIELD is ... argument uchar align; @@ -170,21 +171,25 @@ struct Type vlong argwid; // most nodes - Type* type; - vlong width; // offset in TFIELD, width in all others + Type* type; // actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx + vlong width; // offset in TFIELD, width in all others // TFIELD - Type* down; // also used in TMAP + Type* down; // next struct field, also key type in TMAP + Type* outer; // outer struct Strlit* note; // literal string annotation // TARRAY - int32 bound; // negative is dynamic array + vlong bound; // negative is dynamic array int32 maplineno; // first use of TFORW as map key int32 embedlineno; // first use of TFORW as embedded type // for TFORW, where to copy the eventual value to NodeList *copyto; + + // for usefield + Node *lastfn; }; #define T ((Type*)0) @@ -215,7 +220,10 @@ enum EscHeap, EscScope, EscNone, + EscReturn, EscNever, + EscBits = 4, + EscMask = (1<= y + OGT, // x > y + OIND, // *p + OINDEX, // a[i] + OINDEXMAP, // m[s] + OKEY, // The x:3 in t{x:3, y:4}, the 1:2 in a[1:2], the 2:20 in [3]int{2:20}, etc. + OPARAM, // The on-stack copy of a parameter or return value that escapes. + OLEN, // len + OMAKE, // make, typechecking may convert to a more specfic OMAKEXXX. + OMAKECHAN, // make(chan int) + OMAKEMAP, // make(map[string]int) + OMAKESLICE, // make([]int, 0) + OMUL, // x * y + ODIV, // x / y + OMOD, // x % y + OLSH, // x << u + ORSH, // x >> u + OAND, // x & y + OANDNOT, // x &^ y + ONEW, // new + ONOT, // !b + OCOM, // ^x + OPLUS, // +x + OMINUS, // -y + OOROR, // b1 || b2 + OPANIC, // panic + OPRINT, // print + OPRINTN, // println + OPAREN, // (x) + OSEND, // c <- x + OSLICE, // v[1:2], typechecking may convert to a more specfic OSLICEXXX. + OSLICEARR, // a[1:2] + OSLICESTR, // s[1:2] + ORECOVER, // recover + ORECV, // <-c + ORUNESTR, // string(i) + OSELRECV, // case x = <-c: + OSELRECV2, // case x, ok = <-c: + OIOTA, // iota + OREAL, // real + OIMAG, // imag + OCOMPLEX, // complex + + // statements + OBLOCK, // block of code + OBREAK, // break + OCASE, // case, after being verified by swt.c's casebody. + OXCASE, // case, before verification. + OCONTINUE, // continue + ODEFER, // defer + OEMPTY, // no-op + OFALL, // fallthrough, after being verified by swt.c's casebody. + OXFALL, // fallthrough, before verification. + OFOR, // for + OGOTO, // goto + OIF, // if + OLABEL, // label: + OPROC, // go + ORANGE, // range + ORETURN, // return + OSELECT, // select + OSWITCH, // switch x + OTYPESW, // switch err.(type) // types - OTCHAN, - OTMAP, - OTSTRUCT, - OTINTER, - OTFUNC, - OTARRAY, - OTPAREN, + OTCHAN, // chan int + OTMAP, // map[string]int + OTSTRUCT, // struct{} + OTINTER, // interface{} + OTFUNC, // func() + OTARRAY, // []int, [8]int, [N]int or [...]int + OTPAREN, // (T) // misc - ODDD, - ODDDARG, - OINLCALL, // intermediary representation of an inlined call - OITAB, // itable word of interface value - - // for back ends - OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG, + ODDD, // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}. + ODDDARG, // func f(args ...int), introduced by escape analysis. + OINLCALL, // intermediary representation of an inlined call. + OEFACE, // itable and data words of an empty-interface value. + OITAB, // itable word of an interface value. + OCLOSUREVAR, // variable reference at beginning of closure function + OCFUNC, // reference to c function pointer (not go func value) + + // arch-specific registers + OREGISTER, // a register, such as AX. + OINDREG, // offset plus indirect of a register, such as 8(SP). + + // 386/amd64-specific opcodes + OCMP, // compare: ACMP. + ODEC, // decrement: ADEC. + OINC, // increment: AINC. + OEXTEND, // extend: ACWD/ACDQ/ACQO. + OHMUL, // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both). + OLROT, // left rotate: AROL. + ORROTC, // right rotate-carry: ARCR. OEND, }; + enum { Txxx, // 0 @@ -539,6 +624,7 @@ enum NTYPE, }; + enum { CTxxx, @@ -562,18 +648,21 @@ enum Cboth = Crecv | Csend, }; +// declaration context enum { Pxxx, - PEXTERN, // declaration context - PAUTO, - PPARAM, - PPARAMOUT, - PPARAMREF, // param passed by reference - PFUNC, + PEXTERN, // global variable + PAUTO, // local variables + PPARAM, // input arguments + PPARAMOUT, // output results + PPARAMREF, // closure variable reference + PFUNC, // global function - PHEAP = 1<<7, + PDISCARD, // discard during parse of duplicate import + + PHEAP = 1<<7, // an extra bit to identify an escaped variable }; enum @@ -606,7 +695,6 @@ typedef struct Var Var; struct Var { vlong offset; - Sym* gotype; Node* node; int width; char name; @@ -756,7 +844,7 @@ EXTERN int safemode; EXTERN char namebuf[NSYMB]; EXTERN char lexbuf[NSYMB]; EXTERN char litbuf[NSYMB]; -EXTERN char debug[256]; +EXTERN int debug[256]; EXTERN Sym* hash[NHASH]; EXTERN Sym* importmyname; // my name for package EXTERN Pkg* localpkg; // package being compiled @@ -764,11 +852,15 @@ EXTERN Pkg* importpkg; // package being imported EXTERN Pkg* structpkg; // package that declared struct, during import EXTERN Pkg* builtinpkg; // fake package for builtins EXTERN Pkg* gostringpkg; // fake pkg for Go strings +EXTERN Pkg* itabpkg; // fake pkg for itab cache EXTERN Pkg* runtimepkg; // package runtime +EXTERN Pkg* racepkg; // package runtime/race EXTERN Pkg* stringpkg; // fake package for C strings -EXTERN Pkg* typepkg; // fake package for runtime type info +EXTERN Pkg* typepkg; // fake package for runtime type info (headers) +EXTERN Pkg* typelinkpkg; // fake package for runtime type info (data) EXTERN Pkg* weaktypepkg; // weak references to runtime type info EXTERN Pkg* unsafepkg; // package unsafe +EXTERN Pkg* trackpkg; // fake package for field tracking EXTERN Pkg* phash[128]; EXTERN int tptr; // either TPTR32 or TPTR64 extern char* runtimeimport; @@ -815,6 +907,7 @@ EXTERN NodeList* externdcl; EXTERN NodeList* closures; EXTERN NodeList* exportlist; EXTERN NodeList* importlist; // imported functions and methods with inlinable bodies +EXTERN NodeList* funcsyms; EXTERN int dclcontext; // PEXTERN/PAUTO EXTERN int incannedimport; EXTERN int statuniqgen; // name generator for static temps @@ -823,8 +916,8 @@ EXTERN int loophack; EXTERN int32 iota; EXTERN NodeList* lastconst; EXTERN Node* lasttype; -EXTERN int32 maxarg; -EXTERN int32 stksize; // stack size for current frame +EXTERN vlong maxarg; +EXTERN vlong stksize; // stack size for current frame EXTERN int32 blockgen; // max block number EXTERN int32 block; // current block number EXTERN int hasdefer; // flag that curfn has defer statetment @@ -832,12 +925,14 @@ EXTERN int hasdefer; // flag that curfn has defer statetment EXTERN Node* curfn; EXTERN int widthptr; +EXTERN int widthint; EXTERN Node* typesw; EXTERN Node* nblank; extern int thechar; extern char* thestring; +EXTERN int use_sse; EXTERN char* hunk; EXTERN int32 nhunk; @@ -846,6 +941,14 @@ EXTERN int32 thunk; EXTERN int funcdepth; EXTERN int typecheckok; EXTERN int compiling_runtime; +EXTERN int compiling_wrappers; +EXTERN int pure_go; +EXTERN int flag_race; +EXTERN int flag_largemodel; +EXTERN int noescape; + +EXTERN int nointerface; +EXTERN int fieldtrack_enabled; /* * y.tab.c @@ -860,7 +963,7 @@ void checkwidth(Type *t); void defercheckwidth(void); void dowidth(Type *t); void resumecheckwidth(void); -uint32 rnd(uint32 o, uint32 r); +vlong rnd(vlong o, vlong r); void typeinit(void); /* @@ -898,9 +1001,11 @@ void defaultlit(Node **np, Type *t); void defaultlit2(Node **lp, Node **rp, int force); void evconst(Node *n); int isconst(Node *n, int ct); +int isgoconst(Node *n); Node* nodcplxlit(Val r, Val i); Node* nodlit(Val v); long nonnegconst(Node *n); +int doesoverflow(Val v, Type *t); void overflow(Val v, Type *t); int smallintconst(Node *n); Val toint(Val v); @@ -910,7 +1015,7 @@ Mpflt* truncfltlit(Mpflt *oldv, Type *t); * cplx.c */ void complexadd(int op, Node *nl, Node *nr, Node *res); -void complexbool(int op, Node *nl, Node *nr, int true, Prog *to); +void complexbool(int op, Node *nl, Node *nr, int true, int likely, Prog *to); void complexgen(Node *n, Node *res); void complexminus(Node *nl, Node *res); void complexmove(Node *f, Node *t); @@ -921,10 +1026,10 @@ void nodfconst(Node *n, Type *t, Mpflt* fval); /* * dcl.c */ -void addmethod(Sym *sf, Type *t, int local); +void addmethod(Sym *sf, Type *t, int local, int nointerface); void addvar(Node *n, Type *t, int ctxt); NodeList* checkarglist(NodeList *all, int input); -Node* colas(NodeList *left, NodeList *right); +Node* colas(NodeList *left, NodeList *right, int32 lno); void colasdefn(NodeList *left, Node *defn); NodeList* constiter(NodeList *vl, Node *t, NodeList *cl); Node* dclname(Sym *s); @@ -954,6 +1059,7 @@ Node* typedcl0(Sym *s); Node* typedcl1(Node *n, Node *t, int local); Node* typenod(Type *t); NodeList* variter(NodeList *vl, Node *t, NodeList *el); +Sym* funcsym(Sym*); /* * esc.c @@ -987,6 +1093,8 @@ void dumplist(char *s, NodeList *l); void addrescapes(Node *n); void cgen_as(Node *nl, Node *nr); void cgen_callmeth(Node *n, int proc); +void cgen_eface(Node* n, Node* res); +void cgen_slice(Node* n, Node* res); void clearlabels(void); void checklabels(void); int dotoffset(Node *n, int *oary, Node **nn); @@ -1115,8 +1223,11 @@ void dumptypestructs(void); Type* methodfunc(Type *f, Type*); Node* typename(Type *t); Sym* typesym(Type *t); +Sym* typenamesym(Type *t); +Sym* tracksym(Type *t); Sym* typesymprefix(char *prefix, Type *t); int haspointers(Type *t); +void usefield(Node*); /* * select.c @@ -1214,6 +1325,7 @@ Node* safeexpr(Node *n, NodeList **init); void saveerrors(void); Node* cheapexpr(Node *n, NodeList **init); Node* localexpr(Node *n, Type *t, NodeList **init); +void saveorignode(Node *n); int32 setlineno(Node *n); void setmaxarg(Type *t); Type* shallow(Type *t); @@ -1274,6 +1386,7 @@ void walkexprlistsafe(NodeList *l, NodeList **init); void walkstmt(Node **np); void walkstmtlist(NodeList *l); Node* conv(Node*, Type*); +int candiscard(Node*); /* * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c @@ -1302,7 +1415,8 @@ EXTERN Node* nodfp; int anyregalloc(void); void betypeinit(void); -void bgen(Node *n, int true, Prog *to); +void bgen(Node *n, int true, int likely, Prog *to); +void checkref(Node*); void cgen(Node*, Node*); void cgen_asop(Node *n); void cgen_call(Node *n, int proc); @@ -1323,8 +1437,8 @@ void gdata(Node*, Node*, int); void gdatacomplex(Node*, Mpcplx*); void gdatastring(Node*, Strlit*); void genembedtramp(Type*, Type*, Sym*, int iface); -void ggloblnod(Node *nam, int32 width); -void ggloblsym(Sym *s, int32 width, int dupok); +void ggloblnod(Node *nam); +void ggloblsym(Sym *s, int32 width, int dupok, int rodata); Prog* gjmp(Prog*); void gused(Node*); int isfat(Type*); @@ -1364,3 +1478,8 @@ void zname(Biobuf *b, Sym *s, int t); #pragma varargck type "V" Val* #pragma varargck type "Y" char* #pragma varargck type "Z" Strlit* + +/* + * racewalk.c + */ +void racewalk(Node *fn); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index f95058721..794961e8e 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -37,8 +37,8 @@ static void fixlbrace(int); // |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /' %token LLITERAL -%token LASOP -%token LBREAK LCASE LCHAN LCOLAS LCONST LCONTINUE LDDD +%token LASOP LCOLAS +%token LBREAK LCASE LCHAN LCONST LCONTINUE LDDD %token LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO %token LIF LIMPORT LINTERFACE LMAP LNAME %token LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH @@ -54,10 +54,10 @@ static void fixlbrace(int); %type stmt ntype %type arg_type %type case caseblock -%type compound_stmt dotname embed expr complitexpr +%type compound_stmt dotname embed expr complitexpr bare_complitexpr %type expr_or_type %type fndcl hidden_fndcl fnliteral -%type for_body for_header for_stmt if_header if_stmt else non_dcl_stmt +%type for_body for_header for_stmt if_header if_stmt non_dcl_stmt %type interfacedcl keyval labelname name %type name_or_type non_expr_type %type new_name dcl_name oexpr typedclname @@ -70,7 +70,7 @@ static void fixlbrace(int); %type xdcl fnbody fnres loop_body dcl_name_list %type new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list -%type oexpr_list caseblock_list stmt_list oarg_type_list_ocomma arg_type_list +%type oexpr_list caseblock_list elseif elseif_list else stmt_list oarg_type_list_ocomma arg_type_list %type interfacedcl_list vardcl vardcl_list structdcl structdcl_list %type common_dcl constdcl constdcl1 constdcl_list typedcl_list @@ -405,6 +405,20 @@ simple_stmt: expr { $$ = $1; + + // These nodes do not carry line numbers. + // Since a bare name used as an expression is an error, + // introduce a wrapper node to give the correct line. + switch($$->op) { + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + $$ = nod(OPAREN, $$, N); + $$->implicit = 1; + break; + } } | expr LASOP expr { @@ -437,7 +451,7 @@ simple_stmt: $$->left = dclname($1->n->sym); // it's a colas, so must not re-use an oldname. break; } - $$ = colas($1, $3); + $$ = colas($1, $3, $2); } | expr LINC { @@ -496,7 +510,7 @@ case: // done in casebody() markdcl(); $$ = nod(OXCASE, N, N); - $$->list = list1(colas($2, list1($4))); + $$->list = list1(colas($2, list1($4), $3)); } | LDEFAULT ':' { @@ -661,25 +675,56 @@ if_stmt: { $3->nbody = $5; } - else + elseif_list else { - popdcl(); + Node *n; + NodeList *nn; + $$ = $3; - if($7 != N) - $$->nelse = list1($7); + n = $3; + popdcl(); + for(nn = concat($7, $8); nn; nn = nn->next) { + if(nn->n->op == OIF) + popdcl(); + n->nelse = list1(nn->n); + n = nn->n; + } } -else: +elseif: + LELSE LIF { - $$ = N; + markdcl(); } -| LELSE if_stmt + if_header loop_body { - $$ = $2; + if($4->ntest == N) + yyerror("missing condition in if statement"); + $4->nbody = $5; + $$ = list1($4); + } + +elseif_list: + { + $$ = nil; + } +| elseif_list elseif + { + $$ = concat($1, $2); + } + +else: + { + $$ = nil; } | LELSE compound_stmt { - $$ = $2; + NodeList *node; + + node = mal(sizeof *node); + node->n = $2; + node->end = node; + $$ = node; } switch_stmt: @@ -902,7 +947,7 @@ pexpr_no_paren: $$ = nod(OSLICE, $1, nod(OKEY, $3, $5)); } | pseudocall -| convtype '(' expr ')' +| convtype '(' expr ocomma ')' { // conversion $$ = nod(OCALL, $1, N); @@ -943,6 +988,30 @@ keyval: $$ = nod(OKEY, $1, $3); } +bare_complitexpr: + expr + { + // These nodes do not carry line numbers. + // Since a composite literal commonly spans several lines, + // the line number on errors may be misleading. + // Introduce a wrapper node to give the correct line. + $$ = $1; + switch($$->op) { + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + $$ = nod(OPAREN, $$, N); + $$->implicit = 1; + } + } +| '{' start_complit braced_keyval_list '}' + { + $$ = $2; + $$->list = $3; + } + complitexpr: expr | '{' start_complit braced_keyval_list '}' @@ -966,6 +1035,7 @@ pexpr: case OPACK: case OTYPE: case OLITERAL: + case OTYPESW: $$ = nod(OPAREN, $$, N); } } @@ -1030,10 +1100,16 @@ sym: hidden_importsym: '@' LLITERAL '.' LNAME { + Pkg *p; + if($2.u.sval->len == 0) - $$ = pkglookup($4->name, importpkg); - else - $$ = pkglookup($4->name, mkpkg($2.u.sval)); + p = importpkg; + else { + if(isbadimport($2.u.sval)) + errorexit(); + p = mkpkg($2.u.sval); + } + $$ = pkglookup($4->name, p); } name: @@ -1201,8 +1277,11 @@ xfndcl: $$ = $2; if($$ == N) break; + if(noescape && $3 != nil) + yyerror("can only use //go:noescape with external func implementations"); $$->nbody = $3; $$->endlineno = lineno; + $$->noescape = noescape; funcbody($$); } @@ -1269,6 +1348,7 @@ fndcl: $$->nname = methodname1($$->shortname, rcvr->right); $$->nname->defn = $$; $$->nname->ntype = t; + $$->nname->nointerface = nointerface; declare($$->nname, PFUNC); funchdr($$); @@ -1287,8 +1367,10 @@ hidden_fndcl: importsym(s, ONAME); if(s->def != N && s->def->op == ONAME) { - if(eqtype(t, s->def->type)) + if(eqtype(t, s->def->type)) { + dclcontext = PDISCARD; // since we skip funchdr below break; + } yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t); } @@ -1304,7 +1386,8 @@ hidden_fndcl: $$->type = functype($2->n, $6, $8); checkwidth($$->type); - addmethod($4, $$->type, 0); + addmethod($4, $$->type, 0, nointerface); + nointerface = 0; funchdr($$); // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as @@ -1381,6 +1464,8 @@ xdcl_list: $$ = concat($1, $2); if(nsyntaxerrors == 0) testdclstack(); + nointerface = 0; + noescape = 0; } vardcl_list: @@ -1719,7 +1804,7 @@ keyval_list: { $$ = list1($1); } -| complitexpr +| bare_complitexpr { $$ = list1($1); } @@ -1727,7 +1812,7 @@ keyval_list: { $$ = list($1, $3); } -| keyval_list ',' complitexpr +| keyval_list ',' bare_complitexpr { $$ = list($1, $3); } @@ -1818,8 +1903,10 @@ hidden_import: } | LFUNC hidden_fndcl fnbody ';' { - if($2 == N) + if($2 == N) { + dclcontext = PEXTERN; // since we skip the funcbody below break; + } $2->inl = $3; @@ -1828,7 +1915,7 @@ hidden_import: if(debug['E']) { print("import [%Z] func %lN \n", importpkg->path, $2); - if(debug['l'] > 2 && $2->inl) + if(debug['m'] > 2 && $2->inl) print("inl body:%+H\n", $2->inl); } } @@ -2039,6 +2126,8 @@ hidden_constant: mpaddfixfix($2->val.u.xval, $4->val.u.xval, 0); break; } + $4->val.u.cval->real = $4->val.u.cval->imag; + mpmovecflt(&$4->val.u.cval->imag, 0.0); $$ = nodcplxlit($2->val, $4->val); } diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c index be402cc0c..918d37180 100644 --- a/src/cmd/gc/init.c +++ b/src/cmd/gc/init.c @@ -55,6 +55,10 @@ anyinit(NodeList *n) case ODCLTYPE: case OEMPTY: break; + case OAS: + if(isblank(l->n->left) && candiscard(l->n->right)) + break; + // fall through default: return 1; } diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c index b2b1faff7..1cc13a304 100644 --- a/src/cmd/gc/inl.c +++ b/src/cmd/gc/inl.c @@ -13,7 +13,7 @@ // 0: disabled // 1: 40-nodes leaf functions, oneliners, lazy typechecking (default) // 2: early typechecking of all imported bodies -// 3: +// 3: allow variadic functions // 4: allow non-leaf functions , (breaks runtime.Caller) // 5: transitive inlining // @@ -39,9 +39,10 @@ static int ishairylist(NodeList *ll, int *budget); // Used by inlcalls static void inlnodelist(NodeList *l); static void inlnode(Node **np); -static void mkinlcall(Node **np, Node *fn); +static void mkinlcall(Node **np, Node *fn, int isddd); static Node* inlvar(Node *n); static Node* retvar(Type *n, int i); +static Node* argvar(Type *n, int i); static Node* newlabel(void); static Node* inlsubst(Node *n); static NodeList* inlsubstlist(NodeList *l); @@ -82,20 +83,18 @@ typecheckinl(Node *fn) Pkg *pkg; int save_safemode, lno; - if(fn->typecheck) - return; - lno = setlineno(fn); - if (debug['m']>2) - print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl); - - // typecheckinl is only used for imported functions; + // typecheckinl is only for imported functions; // their bodies may refer to unsafe as long as the package // was marked safe during import (which was checked then). + // the ->inl of a local function has been typechecked before caninl copied it. pkg = fnpkg(fn); if (pkg == localpkg || pkg == nil) - fatal("typecheckinl on local function %lN", fn); + return; // typecheckinl on local function + + if (debug['m']>2) + print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl); save_safemode = safemode; safemode = 0; @@ -103,7 +102,6 @@ typecheckinl(Node *fn) savefn = curfn; curfn = fn; typechecklist(fn->inl, Etop); - fn->typecheck = 1; curfn = savefn; safemode = save_safemode; @@ -111,10 +109,9 @@ typecheckinl(Node *fn) lineno = lno; } -// Caninl determines whether fn is inlineable. Currently that means: -// fn is exactly 1 statement, either a return or an assignment, and -// some temporary constraints marked TODO. If fn is inlineable, saves -// fn->nbody in fn->inl and substitutes it with a copy. +// Caninl determines whether fn is inlineable. +// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy. +// fn and ->nbody will already have been typechecked. void caninl(Node *fn) { @@ -131,10 +128,14 @@ caninl(Node *fn) if(fn->nbody == nil) return; + if(fn->typecheck == 0) + fatal("caninl on non-typechecked function %N", fn); + // can't handle ... args yet - for(t=fn->type->type->down->down->type; t; t=t->down) - if(t->isddd) - return; + if(debug['l'] < 3) + for(t=fn->type->type->down->down->type; t; t=t->down) + if(t->isddd) + return; budget = 40; // allowed hairyness if(ishairylist(fn->nbody, &budget)) @@ -145,8 +146,6 @@ caninl(Node *fn) fn->nname->inl = fn->nbody; fn->nbody = inlcopylist(fn->nname->inl); - // nbody will have been typechecked, so we can set this: - fn->typecheck = 1; // hack, TODO, check for better way to link method nodes back to the thing with the ->inl // this is so export can find the body of a method @@ -195,19 +194,11 @@ ishairy(Node *n, int *budget) case OSWITCH: case OPROC: case ODEFER: - case ODCL: // declares locals as globals b/c of @"". qualification case ODCLTYPE: // can't print yet case ODCLCONST: // can't print yet return 1; break; - case OAS: - // x = zero initializing assignments aren't representible in export yet. - // alternatively we may just skip them in printing and hope their DCL printed - // as a var will regenerate it - if(n->right == N) - return 1; - break; } (*budget)--; @@ -366,8 +357,8 @@ inlnode(Node **np) } case OCLOSURE: - // TODO do them here instead of in lex.c phase 6b, so escape analysis - // can avoid more heapmoves. + // TODO do them here (or earlier) instead of in walkcallclosure, + // so escape analysis can avoid more heapmoves. return; } @@ -464,10 +455,10 @@ inlnode(Node **np) if(debug['m']>3) print("%L:call to func %+N\n", n->lineno, n->left); if(n->left->inl) // normal case - mkinlcall(np, n->left); + mkinlcall(np, n->left, n->isddd); else if(n->left->op == ONAME && n->left->left && n->left->left->op == OTYPE && n->left->right && n->left->right->op == ONAME) // methods called as functions if(n->left->sym->def) - mkinlcall(np, n->left->sym->def); + mkinlcall(np, n->left->sym->def, n->isddd); break; case OCALLMETH: @@ -480,7 +471,7 @@ inlnode(Node **np) if(n->left->type->nname == N) fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type); - mkinlcall(np, n->left->type->nname); + mkinlcall(np, n->left->type->nname, n->isddd); break; } @@ -488,10 +479,10 @@ inlnode(Node **np) lineno = lno; } -static void mkinlcall1(Node **np, Node *fn); +static void mkinlcall1(Node **np, Node *fn, int isddd); static void -mkinlcall(Node **np, Node *fn) +mkinlcall(Node **np, Node *fn, int isddd) { int save_safemode; Pkg *pkg; @@ -503,7 +494,7 @@ mkinlcall(Node **np, Node *fn) pkg = fnpkg(fn); if(pkg != localpkg && pkg != nil) safemode = 0; - mkinlcall1(np, fn); + mkinlcall1(np, fn, isddd); safemode = save_safemode; } @@ -519,17 +510,25 @@ tinlvar(Type *t) return nblank; } +static int inlgen; + // if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL. // On return ninit has the parameter assignments, the nbody is the // inlined function body and list, rlist contain the input, output // parameters. static void -mkinlcall1(Node **np, Node *fn) +mkinlcall1(Node **np, Node *fn, int isddd) { int i; + int chkargcount; Node *n, *call, *saveinlfn, *as, *m; NodeList *dcl, *ll, *ninit, *body; Type *t; + // For variadic fn. + int variadic, varargcount, multiret; + Node *vararg; + NodeList *varargs; + Type *varargtype, *vararrtype; if (fn->inl == nil) return; @@ -556,6 +555,8 @@ mkinlcall1(Node **np, Node *fn) ninit = n->ninit; +//dumplist("ninit pre", ninit); + if (fn->defn) // local function dcl = fn->defn->dcl; else // imported function @@ -567,7 +568,10 @@ mkinlcall1(Node **np, Node *fn) for(ll = dcl; ll; ll=ll->next) if(ll->n->op == ONAME) { ll->n->inlvar = inlvar(ll->n); - ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs + // Typecheck because inlvar is not necessarily a function parameter. + typecheck(&ll->n->inlvar, Erv); + if ((ll->n->class&~PHEAP) != PAUTO) + ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs if (ll->n->class == PPARAMOUT) // we rely on the order being correct here inlretvars = list(inlretvars, ll->n->inlvar); } @@ -580,49 +584,118 @@ mkinlcall1(Node **np, Node *fn) inlretvars = list(inlretvars, m); } - // assign arguments to the parameters' temp names - as = N; - if(fn->type->thistuple) { + // assign receiver. + if(fn->type->thistuple && n->left->op == ODOTMETH) { + // method call with a receiver. t = getthisx(fn->type)->type; if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) fatal("missing inlvar for %N\n", t->nname); - - if(n->left->op == ODOTMETH) { - if(!n->left->left) - fatal("method call without receiver: %+N", n); - if(t == T) - fatal("method call unknown receiver type: %+N", n); - as = nod(OAS, tinlvar(t), n->left->left); - } else { // non-method call to method - if(!n->list) - fatal("non-method call to method without first arg: %+N", n); - if(t != T) - as = nod(OAS, tinlvar(t), n->list->n); - } - + if(!n->left->left) + fatal("method call without receiver: %+N", n); + if(t == T) + fatal("method call unknown receiver type: %+N", n); + as = nod(OAS, tinlvar(t), n->left->left); if(as != N) { typecheck(&as, Etop); ninit = list(ninit, as); } } + // check if inlined function is variadic. + variadic = 0; + varargtype = T; + varargcount = 0; + for(t=fn->type->type->down->down->type; t; t=t->down) { + if(t->isddd) { + variadic = 1; + varargtype = t->type; + } + } + // but if argument is dotted too forget about variadicity. + if(variadic && isddd) + variadic = 0; + + // check if argument is actually a returned tuple from call. + multiret = 0; + if(n->list && !n->list->next) { + switch(n->list->n->op) { + case OCALL: + case OCALLFUNC: + case OCALLINTER: + case OCALLMETH: + if(n->list->n->left->type->outtuple > 1) + multiret = n->list->n->left->type->outtuple-1; + } + } + + if(variadic) { + varargcount = count(n->list) + multiret; + if(n->left->op != ODOTMETH) + varargcount -= fn->type->thistuple; + varargcount -= fn->type->intuple - 1; + } + + // assign arguments to the parameters' temp names as = nod(OAS2, N, N); - if(fn->type->intuple > 1 && n->list && !n->list->next) { - // TODO check that n->list->n is a call? - // TODO: non-method call to T.meth(f()) where f returns t, args... - as->rlist = n->list; - for(t = getinargx(fn->type)->type; t; t=t->down) - as->list = list(as->list, tinlvar(t)); - } else { - ll = n->list; - if(fn->type->thistuple && n->left->op != ODOTMETH) // non method call to method - ll=ll->next; // was handled above in if(thistuple) + as->rlist = n->list; + ll = n->list; + + // TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call? + if(fn->type->thistuple && n->left->op != ODOTMETH) { + // non-method call to method + if(!n->list) + fatal("non-method call to method without first arg: %+N", n); + // append receiver inlvar to LHS. + t = getthisx(fn->type)->type; + if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) + fatal("missing inlvar for %N\n", t->nname); + if(t == T) + fatal("method call unknown receiver type: %+N", n); + as->list = list(as->list, tinlvar(t)); + ll = ll->next; // track argument count. + } - for(t = getinargx(fn->type)->type; t && ll; t=t->down) { + // append ordinary arguments to LHS. + chkargcount = n->list && n->list->next; + vararg = N; // the slice argument to a variadic call + varargs = nil; // the list of LHS names to put in vararg. + if(!chkargcount) { + // 0 or 1 expression on RHS. + for(t = getinargx(fn->type)->type; t; t=t->down) { + if(variadic && t->isddd) { + vararg = tinlvar(t); + for(i=0; ilist = list(as->list, m); + } + break; + } + as->list = list(as->list, tinlvar(t)); + } + } else { + // match arguments except final variadic (unless the call is dotted itself) + for(t = getinargx(fn->type)->type; t;) { + if(!ll) + break; + if(variadic && t->isddd) + break; as->list = list(as->list, tinlvar(t)); - as->rlist = list(as->rlist, ll->n); + t=t->down; ll=ll->next; } + // match varargcount arguments with variadic parameters. + if(variadic && t && t->isddd) { + vararg = tinlvar(t); + for(i=0; ilist = list(as->list, m); + ll=ll->next; + } + if(i==varargcount) + t=t->down; + } if(ll || t) fatal("arg count mismatch: %#T vs %,H\n", getinargx(fn->type), n->list); } @@ -632,6 +705,25 @@ mkinlcall1(Node **np, Node *fn) ninit = list(ninit, as); } + // turn the variadic args into a slice. + if(variadic) { + as = nod(OAS, vararg, N); + if(!varargcount) { + as->right = nodnil(); + as->right->type = varargtype; + } else { + vararrtype = typ(TARRAY); + vararrtype->type = varargtype->type; + vararrtype->bound = varargcount; + + as->right = nod(OCOMPLIT, N, typenod(varargtype)); + as->right->list = varargs; + as->right = nod(OSLICE, as->right, nod(OKEY, N, N)); + } + typecheck(&as, Etop); + ninit = list(ninit, as); + } + // zero the outparams for(ll = inlretvars; ll; ll=ll->next) { as = nod(OAS, ll->n, N); @@ -640,12 +732,14 @@ mkinlcall1(Node **np, Node *fn) } inlretlabel = newlabel(); + inlgen++; body = inlsubstlist(fn->inl); body = list(body, nod(OGOTO, inlretlabel, N)); // avoid 'not used' when function doesnt have return body = list(body, nod(OLABEL, inlretlabel, N)); typechecklist(body, Etop); +//dumplist("ninit post", ninit); call = nod(OINLCALL, N, N); call->ninit = ninit; @@ -655,6 +749,7 @@ mkinlcall1(Node **np, Node *fn) call->typecheck = 1; setlno(call, n->lineno); +//dumplist("call body", body); *np = call; @@ -705,7 +800,24 @@ retvar(Type *t, int i) { Node *n; - snprint(namebuf, sizeof(namebuf), ".r%d", i); + snprint(namebuf, sizeof(namebuf), "~r%d", i); + n = newname(lookup(namebuf)); + n->type = t->type; + n->class = PAUTO; + n->used = 1; + n->curfn = curfn; // the calling function, not the called one + curfn->dcl = list(curfn->dcl, n); + return n; +} + +// Synthesize a variable to store the inlined function's arguments +// when they come from a multiple return call. +static Node* +argvar(Type *t, int i) +{ + Node *n; + + snprint(namebuf, sizeof(namebuf), "~arg%d", i); n = newname(lookup(namebuf)); n->type = t->type; n->class = PAUTO; @@ -746,6 +858,7 @@ inlsubstlist(NodeList *ll) static Node* inlsubst(Node *n) { + char *p; Node *m, *as; NodeList *ll; @@ -788,6 +901,16 @@ inlsubst(Node *n) typecheck(&m, Etop); // dump("Return after substitution", m); return m; + + case OGOTO: + case OLABEL: + m = nod(OXXX, N, N); + *m = *n; + m->ninit = nil; + p = smprint("%s·%d", n->left->sym->name, inlgen); + m->left = newname(lookup(p)); + free(p); + return m; } diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 624dfb0b4..68ae6864d 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -30,6 +30,8 @@ static void addidir(char*); static int getlinepragma(void); static char *goos, *goarch, *goroot; +#define BOM 0xFEFF + // Compiler experiments. // These are controlled by the GOEXPERIMENT environment // variable recorded when the compiler is built. @@ -38,6 +40,7 @@ static struct { int *val; } exper[] = { // {"rune32", &rune32}, + {"fieldtrack", &fieldtrack_enabled}, {nil, nil}, }; @@ -96,7 +99,7 @@ yy_isdigit(int c) static int yy_isspace(int c) { - return c >= 0 && c <= 0xFF && isspace(c); + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } static int @@ -130,42 +133,8 @@ enum void usage(void) { - print("gc: usage: %cg [flags] file.go...\n", thechar); - print("flags:\n"); - // -A allow use of "any" type, for bootstrapping - // -B disable bounds checking - // -E print imported declarations - // -K warn when lineno is zero - // -M print arguments to gmove - // -P print peephole diagnostics - // -R print optimizer diagnostics - // -g print code generation diagnostics - // -i print line history - // -j print variables to be initialized at runtime - // -r print generated helper functions - // -s print redundant types in composite literals - // -v print more information with -P or -R - // -y print declarations in cannedimports (used with -d) - // -% print non-static initializers - // -+ indicate that the runtime is being compiled - print(" -D PATH interpret local imports relative to this import path\n"); - print(" -I DIR search for packages in DIR\n"); - print(" -L show full path in file:line prints\n"); - print(" -N disable optimizations\n"); - print(" -S print the assembly language\n"); - print(" -V print the compiler version\n"); - print(" -W print the parse tree after typing\n"); - print(" -d print declarations\n"); - print(" -e no limit on number of errors printed\n"); - print(" -f print stack frame structure\n"); - print(" -h panic on an error\n"); - print(" -l disable inlining\n"); - print(" -m print optimization decisions\n"); - print(" -o file specify output file\n"); - print(" -p assumed import path for this code\n"); - print(" -u disable package unsafe\n"); - print(" -w print type checking details\n"); - print(" -x print lex tokens\n"); + print("usage: %cg [options] file.go...\n", thechar); + flagprint(1); exits("usage"); } @@ -183,11 +152,23 @@ fault(int s) fatal("fault"); } +void +doversion(void) +{ + char *p; + + p = expstring(); + if(strcmp(p, "X:none") == 0) + p = ""; + print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p); + exits(0); +} + int main(int argc, char *argv[]) { - int i, c; - NodeList *l, *batch; + int i; + NodeList *l; char *p; #ifdef SIGBUS @@ -197,25 +178,41 @@ main(int argc, char *argv[]) localpkg = mkpkg(strlit("")); localpkg->prefix = "\"\""; - + + // pseudo-package, for scoping builtinpkg = mkpkg(strlit("go.builtin")); + // pseudo-package, accessed by import "unsafe" + unsafepkg = mkpkg(strlit("unsafe")); + unsafepkg->name = "unsafe"; + + // real package, referred to by generated runtime calls + runtimepkg = mkpkg(strlit("runtime")); + runtimepkg->name = "runtime"; + + // pseudo-packages used in symbol tables gostringpkg = mkpkg(strlit("go.string")); gostringpkg->name = "go.string"; gostringpkg->prefix = "go.string"; // not go%2estring - runtimepkg = mkpkg(strlit("runtime")); - runtimepkg->name = "runtime"; + itabpkg = mkpkg(strlit("go.itab")); + itabpkg->name = "go.itab"; + itabpkg->prefix = "go.itab"; // not go%2eitab - typepkg = mkpkg(strlit("type")); - typepkg->name = "type"; + weaktypepkg = mkpkg(strlit("go.weak.type")); + weaktypepkg->name = "go.weak.type"; + weaktypepkg->prefix = "go.weak.type"; // not go%2eweak%2etype + + typelinkpkg = mkpkg(strlit("go.typelink")); + typelinkpkg->name = "go.typelink"; + typelinkpkg->prefix = "go.typelink"; // not go%2etypelink - weaktypepkg = mkpkg(strlit("weak.type")); - weaktypepkg->name = "weak.type"; - weaktypepkg->prefix = "weak.type"; // not weak%2etype + trackpkg = mkpkg(strlit("go.track")); + trackpkg->name = "go.track"; + trackpkg->prefix = "go.track"; // not go%2etrack - unsafepkg = mkpkg(strlit("unsafe")); - unsafepkg->name = "unsafe"; + typepkg = mkpkg(strlit("type")); + typepkg->name = "type"; goroot = getgoroot(); goos = getgoos(); @@ -224,41 +221,55 @@ main(int argc, char *argv[]) setexp(); outfile = nil; - ARGBEGIN { - default: - c = ARGC(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - - case 'o': - outfile = EARGF(usage()); - break; - - case 'p': - myimportpath = EARGF(usage()); - break; + flagcount("+", "compiling runtime", &compiling_runtime); + flagcount("%", "debug non-static initializers", &debug['%']); + flagcount("A", "for bootstrapping, allow 'any' type", &debug['A']); + flagcount("B", "disable bounds checking", &debug['B']); + flagstr("D", "path: set relative path for local imports", &localimport); + flagcount("E", "debug symbol export", &debug['E']); + flagfn1("I", "dir: add dir to import search path", addidir); + flagcount("K", "debug missing line numbers", &debug['K']); + flagcount("L", "use full (long) path in error messages", &debug['L']); + flagcount("M", "debug move generation", &debug['M']); + flagcount("N", "disable optimizations", &debug['N']); + flagcount("P", "debug peephole optimizer", &debug['P']); + flagcount("R", "debug register optimizer", &debug['R']); + flagcount("S", "print assembly listing", &debug['S']); + flagfn0("V", "print compiler version", doversion); + flagcount("W", "debug parse tree after type checking", &debug['W']); + flagcount("complete", "compiling complete package (no C or assembly)", &pure_go); + flagcount("d", "debug declarations", &debug['d']); + flagcount("e", "no limit on number of errors reported", &debug['e']); + flagcount("f", "debug stack frames", &debug['f']); + flagcount("g", "debug code generation", &debug['g']); + flagcount("h", "halt on error", &debug['h']); + flagcount("i", "debug line number stack", &debug['i']); + flagcount("j", "debug runtime-initialized variables", &debug['j']); + flagcount("l", "disable inlining", &debug['l']); + flagcount("m", "print optimization decisions", &debug['m']); + flagstr("o", "obj: set output file", &outfile); + flagstr("p", "path: set expected package import path", &myimportpath); + flagcount("r", "debug generated wrappers", &debug['r']); + flagcount("race", "enable race detector", &flag_race); + flagcount("s", "warn about composite literals that can be simplified", &debug['s']); + flagcount("u", "reject unsafe code", &safemode); + flagcount("v", "increase debug verbosity", &debug['v']); + flagcount("w", "debug type checking", &debug['w']); + flagcount("x", "debug lexer", &debug['x']); + flagcount("y", "debug declarations in canned imports (with -d)", &debug['y']); + if(thechar == '6') + flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel); + + flagparse(&argc, &argv, usage); - case 'u': - safemode = 1; - break; + if(argc < 1) + usage(); - case 'D': - localimport = EARGF(usage()); - break; + if(flag_race) { + racepkg = mkpkg(strlit("runtime/race")); + racepkg->name = "race"; + } - case 'I': - addidir(EARGF(usage())); - break; - - case 'V': - p = expstring(); - if(strcmp(p, "X:none") == 0) - p = ""; - print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p); - exits(0); - } ARGEND - // enable inlining. for now: // default: inlining on. (debug['l'] == 1) // -l: inlining off (debug['l'] == 0) @@ -266,11 +277,15 @@ main(int argc, char *argv[]) if(debug['l'] <= 1) debug['l'] = 1 - debug['l']; - if(argc < 1) - usage(); - - // special flag to detect compilation of package runtime - compiling_runtime = debug['+']; + if(thechar == '8') { + p = getgo386(); + if(strcmp(p, "387") == 0) + use_sse = 0; + else if(strcmp(p, "sse2") == 0) + use_sse = 1; + else + sysfatal("unsupported setting GO386=%s", p); + } pathname = mal(1000); if(getwd(pathname, 999) == 0) @@ -315,6 +330,10 @@ main(int argc, char *argv[]) curio.peekc1 = 0; curio.nlsemi = 0; + // Skip initial BOM if present. + if(Bgetrune(curio.bin) != BOM) + Bungetrune(curio.bin); + block = 1; iota = -1000000; @@ -335,6 +354,7 @@ main(int argc, char *argv[]) frame(1); // Process top-level declarations in phases. + // Phase 1: const, type, and names and types of funcs. // This will gather all the information about types // and methods but doesn't depend on any of it. @@ -367,7 +387,7 @@ main(int argc, char *argv[]) errorexit(); // Phase 4: Inlining - if (debug['l'] > 1) { + if(debug['l'] > 1) { // Typecheck imported function bodies if debug['l'] > 1, // otherwise lazily when used or re-exported. for(l=importlist; l; l=l->next) @@ -380,7 +400,7 @@ main(int argc, char *argv[]) errorexit(); } - if (debug['l']) { + if(debug['l']) { // Find functions that can be inlined and clone them before walk expands them. for(l=xtop; l; l=l->next) if(l->n->op == ODCLFUNC) @@ -392,7 +412,7 @@ main(int argc, char *argv[]) inlcalls(l->n); } - // Phase 5: escape analysis. + // Phase 5: Escape analysis. if(!debug['N']) escapes(xtop); @@ -404,21 +424,7 @@ main(int argc, char *argv[]) if(nsavederrors+nerrors == 0) fninit(xtop); - // Phase 6b: Compile all closures. - // Can generate more closures, so run in batches. - while(closures) { - batch = closures; - closures = nil; - if(debug['l']) - for(l=batch; l; l=l->next) - inlcalls(l->n); - if(!debug['N']) - escapes(batch); - for(l=batch; l; l=l->next) - funccompile(l->n, 1); - } - - // Phase 7: check external declarations. + // Phase 7: Check external declarations. for(l=externdcl; l; l=l->next) if(l->n->op == ONAME) typecheck(&l->n, Erv); @@ -483,7 +489,7 @@ skiptopkgdef(Biobuf *b) if(memcmp(p, "!\n", 8) != 0) return 0; /* symbol table is first; skip it */ - sz = arsize(b, "__.SYMDEF"); + sz = arsize(b, "__.GOSYMDEF"); if(sz < 0) return 0; Bseek(b, sz, 1); @@ -533,7 +539,7 @@ static int findpkg(Strlit *name) { Idir *p; - char *q; + char *q, *race; if(islocalname(name)) { if(safemode) @@ -571,10 +577,13 @@ findpkg(Strlit *name) return 1; } if(goroot != nil) { - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.a", goroot, goos, goarch, name); + race = ""; + if(flag_race) + race = "_race"; + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.a", goroot, goos, goarch, race, name); if(access(namebuf, 0) >= 0) return 1; - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.%c", goroot, goos, goarch, name, thechar); + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.%c", goroot, goos, goarch, race, name, thechar); if(access(namebuf, 0) >= 0) return 1; } @@ -659,6 +668,11 @@ importfile(Val *f, int line) strcat(cleanbuf, path->s); cleanname(cleanbuf); path = strlit(cleanbuf); + + if(isbadimport(path)) { + fakeimport(); + return; + } } if(!findpkg(path)) { @@ -667,6 +681,16 @@ importfile(Val *f, int line) } importpkg = mkpkg(path); + // If we already saw that package, feed a dummy statement + // to the lexer to avoid parsing export data twice. + if(importpkg->imported) { + file = strdup(namebuf); + p = smprint("package %s\n$$\n", importpkg->name); + cannedimports(file, p); + return; + } + importpkg->imported = 1; + imp = Bopen(namebuf, OREAD); if(imp == nil) { yyerror("can't open import: \"%Z\": %r", f->u.sval); @@ -767,12 +791,8 @@ static int isfrog(int c) { // complain about possibly invisible control characters - if(c < 0) - return 1; if(c < ' ') { - if(c == '\n' || c== '\r' || c == '\t') // good white space - return 0; - return 1; + return !yy_isspace(c); // exclude good white space } if(0x7f <= c && c <= 0xa0) // DEL, unicode block including unbreakable space. return 1; @@ -982,6 +1002,7 @@ l0: c1 = getc(); if(c1 == '=') { c = LCOLAS; + yylval.i = lexlineno; goto lx; } break; @@ -1145,6 +1166,11 @@ l0: case '[': if(loophack || lstk != nil) { h = malloc(sizeof *h); + if(h == nil) { + flusherrors(); + yyerror("out of memory"); + errorexit(); + } h->v = loophack; h->next = lstk; lstk = h; @@ -1208,7 +1234,7 @@ talph: rune = getr(); // 0xb7 · is used for internal names if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7)) - yyerror("invalid identifier character 0x%ux", rune); + yyerror("invalid identifier character U+%04x", rune); cp += runetochar(cp, &rune); } else if(!yy_isalnum(c) && c != '_') break; @@ -1270,12 +1296,14 @@ tnum: continue; if(cp == lexbuf+2) yyerror("malformed hex constant"); + if(c == 'p') + goto caseep; goto ncu; } } if(c == 'p') // 0p begins floating point zero - goto casep; + goto caseep; c1 = 0; for(;;) { @@ -1293,7 +1321,7 @@ tnum: if(c == '.') goto casedot; if(c == 'e' || c == 'E') - goto casee; + goto caseep; if(c == 'i') goto casei; if(c1) @@ -1303,10 +1331,8 @@ tnum: dc: if(c == '.') goto casedot; - if(c == 'e' || c == 'E') - goto casee; - if(c == 'p' || c == 'P') - goto casep; + if(c == 'e' || c == 'E' || c == 'p' || c == 'P') + goto caseep; if(c == 'i') goto casei; @@ -1342,29 +1368,8 @@ casedot: if(c != 'e' && c != 'E') goto caseout; -casee: - *cp++ = 'e'; - c = getc(); - if(c == '+' || c == '-') { - *cp++ = c; - c = getc(); - } - if(!yy_isdigit(c)) - yyerror("malformed fp constant exponent"); - while(yy_isdigit(c)) { - if(cp+10 >= ep) { - yyerror("identifier too long"); - errorexit(); - } - *cp++ = c; - c = getc(); - } - if(c == 'i') - goto casei; - goto caseout; - -casep: - *cp++ = 'p'; +caseep: + *cp++ = c; c = getc(); if(c == '+' || c == '-') { *cp++ = c; @@ -1430,7 +1435,12 @@ getlinepragma(void) char *cp, *ep, *linep; Hist *h; - for(i=0; i<5; i++) { + c = getr(); + if(c == 'g') + goto go; + if(c != 'l') + goto out; + for(i=1; i<5; i++) { c = getr(); if(c != "line "[i]) goto out; @@ -1478,7 +1488,35 @@ getlinepragma(void) } } linehist(strdup(lexbuf), n, 0); + goto out; + +go: + cp = lexbuf; + ep = lexbuf+sizeof(lexbuf)-5; + *cp++ = 'g'; // already read + for(;;) { + c = getr(); + if(c == EOF || c >= Runeself) + goto out; + if(c == '\n') + break; + if(cp < ep) + *cp++ = c; + } + *cp = 0; + ep = strchr(lexbuf, ' '); + if(ep != nil) + *ep = 0; + if(strcmp(lexbuf, "go:nointerface") == 0 && fieldtrack_enabled) { + nointerface = 1; + goto out; + } + if(strcmp(lexbuf, "go:noescape") == 0) { + noescape = 1; + goto out; + } + out: return c; } @@ -1524,24 +1562,35 @@ yylex(void) static int getc(void) { - int c; + int c, c1, c2; c = curio.peekc; if(c != 0) { curio.peekc = curio.peekc1; curio.peekc1 = 0; - if(c == '\n' && pushedio.bin == nil) - lexlineno++; - return c; + goto check; } if(curio.bin == nil) { c = *curio.cp & 0xff; if(c != 0) curio.cp++; - } else + } else { + loop: c = Bgetc(curio.bin); + if(c == 0xef) { + c1 = Bgetc(curio.bin); + c2 = Bgetc(curio.bin); + if(c1 == 0xbb && c2 == 0xbf) { + yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file"); + goto loop; + } + Bungetc(curio.bin); + Bungetc(curio.bin); + } + } +check: switch(c) { case 0: if(curio.bin != nil) { @@ -1820,16 +1869,16 @@ lexinit(void) if(etype != Txxx) { if(etype < 0 || etype >= nelem(types)) fatal("lexinit: %s bad etype", s->name); + s1 = pkglookup(syms[i].name, builtinpkg); t = types[etype]; if(t == T) { t = typ(etype); - t->sym = s; + t->sym = s1; if(etype != TANY && etype != TSTRING) dowidth(t); types[etype] = t; } - s1 = pkglookup(syms[i].name, builtinpkg); s1->lexical = LNAME; s1->def = typenod(t); continue; @@ -1959,8 +2008,10 @@ lexfini(void) s->lexical = lex; etype = syms[i].etype; - if(etype != Txxx && (etype != TANY || debug['A']) && s->def == N) + if(etype != Txxx && (etype != TANY || debug['A']) && s->def == N) { s->def = typenod(types[etype]); + s->origpkg = builtinpkg; + } etype = syms[i].op; if(etype != OXXX && s->def == N) { @@ -1968,54 +2019,68 @@ lexfini(void) s->def->sym = s; s->def->etype = etype; s->def->builtin = 1; + s->origpkg = builtinpkg; } } + // backend-specific builtin types (e.g. int). for(i=0; typedefs[i].name; i++) { s = lookup(typedefs[i].name); - if(s->def == N) + if(s->def == N) { s->def = typenod(types[typedefs[i].etype]); + s->origpkg = builtinpkg; + } } // there's only so much table-driven we can handle. // these are special cases. s = lookup("byte"); - if(s->def == N) + if(s->def == N) { s->def = typenod(bytetype); - + s->origpkg = builtinpkg; + } + s = lookup("error"); - if(s->def == N) + if(s->def == N) { s->def = typenod(errortype); + s->origpkg = builtinpkg; + } s = lookup("rune"); - if(s->def == N) + if(s->def == N) { s->def = typenod(runetype); + s->origpkg = builtinpkg; + } s = lookup("nil"); if(s->def == N) { v.ctype = CTNIL; s->def = nodlit(v); s->def->sym = s; + s->origpkg = builtinpkg; } - + s = lookup("iota"); if(s->def == N) { s->def = nod(OIOTA, N, N); s->def->sym = s; + s->origpkg = builtinpkg; } s = lookup("true"); if(s->def == N) { s->def = nodbool(1); s->def->sym = s; + s->origpkg = builtinpkg; } s = lookup("false"); if(s->def == N) { s->def = nodbool(0); s->def->sym = s; + s->origpkg = builtinpkg; } - + nodfp = nod(ONAME, N, N); nodfp->type = types[TINT32]; nodfp->xoffset = 0; @@ -2179,7 +2244,7 @@ mkpackage(char* pkgname) { Sym *s; int32 h; - char *p; + char *p, *q; if(localpkg->name == nil) { if(strcmp(pkgname, "_") == 0) @@ -2219,6 +2284,11 @@ mkpackage(char* pkgname) if(outfile == nil) { p = strrchr(infile, '/'); + if(windows) { + q = strrchr(infile, '\\'); + if(q > p) + p = q; + } if(p == nil) p = infile; else diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c index 33fa90e2e..e25044a8b 100644 --- a/src/cmd/gc/mparith1.c +++ b/src/cmd/gc/mparith1.c @@ -43,10 +43,10 @@ mpcmpfixfix(Mpint *a, Mpint *b) int mpcmpfixc(Mpint *b, vlong c) { - Mpint a; + Mpint c1; - mpmovecfix(&a, c); - return mpcmpfixfix(&a, b); + mpmovecfix(&c1, c); + return mpcmpfixfix(b, &c1); } int @@ -232,16 +232,78 @@ mppow10flt(Mpflt *a, int p) mpmulcflt(a, 10); } +static void +mphextofix(Mpint *a, char *s, int n) +{ + char *hexdigitp, *end, c; + long d; + int bit; + + while(*s == '0') { + s++; + n--; + } + + // overflow + if(4*n > Mpscale*Mpprec) { + a->ovf = 1; + return; + } + + end = s+n-1; + for(hexdigitp=end; hexdigitp>=s; hexdigitp--) { + c = *hexdigitp; + if(c >= '0' && c <= '9') + d = c-'0'; + else if(c >= 'A' && c <= 'F') + d = c-'A'+10; + else + d = c-'a'+10; + + bit = 4*(end - hexdigitp); + while(d > 0) { + if(d & 1) + a->a[bit/Mpscale] |= (long)1 << (bit%Mpscale); + bit++; + d = d >> 1; + } + } +} + // // floating point input -// required syntax is [+-]d*[.]d*[e[+-]d*] +// required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*] // void mpatoflt(Mpflt *a, char *as) { Mpflt b; - int dp, c, f, ef, ex, eb; - char *s; + int dp, c, f, ef, ex, eb, base; + char *s, *start; + + while(*as == ' ' || *as == '\t') + as++; + + /* determine base */ + s = as; + base = -1; + while(base == -1) { + switch(*s++) { + case '-': + case '+': + break; + + case '0': + if(*s == 'x') + base = 16; + else + base = 10; + break; + + default: + base = 10; + } + } s = as; dp = 0; /* digits after decimal point */ @@ -250,6 +312,37 @@ mpatoflt(Mpflt *a, char *as) eb = 0; /* binary point */ mpmovecflt(a, 0.0); + if(base == 16) { + start = nil; + for(;;) { + c = *s; + if(c == '-') { + f = 1; + s++; + } + else if(c == '+') { + s++; + } + else if(c == '0' && s[1] == 'x') { + s += 2; + start = s; + } + else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + s++; + } + else { + break; + } + } + if(start == nil) + goto bad; + + mphextofix(&a->val, start, s-start); + if(a->val.ovf) + goto bad; + a->exp = 0; + mpnorm(a); + } for(;;) { switch(c = *s++) { default: @@ -259,11 +352,13 @@ mpatoflt(Mpflt *a, char *as) f = 1; case ' ': - case '\t': - case '+': + case '\t': + case '+': continue; case '.': + if(base == 16) + goto bad; dp = 1; continue; @@ -355,7 +450,7 @@ void mpatofix(Mpint *a, char *as) { int c, f; - char *s; + char *s, *s0; s = as; f = 0; @@ -402,28 +497,19 @@ oct: goto out; hex: - c = *s++; + s0 = s; + c = *s; while(c) { - if(c >= '0' && c <= '9') { - mpmulcfix(a, 16); - mpaddcfix(a, c-'0'); - c = *s++; - continue; - } - if(c >= 'a' && c <= 'f') { - mpmulcfix(a, 16); - mpaddcfix(a, c+10-'a'); - c = *s++; - continue; - } - if(c >= 'A' && c <= 'F') { - mpmulcfix(a, 16); - mpaddcfix(a, c+10-'A'); - c = *s++; + if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + s++; + c = *s; continue; } goto bad; } + mphextofix(a, s0, s-s0); + if(a->ovf) + goto bad; out: if(f) @@ -439,8 +525,8 @@ int Bconv(Fmt *fp) { char buf[500], *p; - Mpint *xval, q, r, ten; - int f; + Mpint *xval, q, r, ten, sixteen; + int f, digit; xval = va_arg(fp->args, Mpint*); mpmovefixfix(&q, xval); @@ -449,15 +535,33 @@ Bconv(Fmt *fp) f = 1; mpnegfix(&q); } - mpmovecfix(&ten, 10); p = &buf[sizeof(buf)]; *--p = 0; - for(;;) { - mpdivmodfixfix(&q, &r, &q, &ten); - *--p = mpgetfix(&r) + '0'; - if(mptestfix(&q) <= 0) - break; + if(fp->flags & FmtSharp) { + // Hexadecimal + mpmovecfix(&sixteen, 16); + for(;;) { + mpdivmodfixfix(&q, &r, &q, &sixteen); + digit = mpgetfix(&r); + if(digit < 10) + *--p = digit + '0'; + else + *--p = digit - 10 + 'A'; + if(mptestfix(&q) <= 0) + break; + } + *--p = 'x'; + *--p = '0'; + } else { + // Decimal + mpmovecfix(&ten, 10); + for(;;) { + mpdivmodfixfix(&q, &r, &q, &ten); + *--p = mpgetfix(&r) + '0'; + if(mptestfix(&q) <= 0) + break; + } } if(f) *--p = '-'; @@ -501,10 +605,10 @@ Fconv(Fmt *fp) } if(fv.exp >= 0) { - snprint(buf, sizeof(buf), "%Bp+%d", &fv.val, fv.exp); + snprint(buf, sizeof(buf), "%#Bp+%d", &fv.val, fv.exp); goto out; } - snprint(buf, sizeof(buf), "%Bp-%d", &fv.val, -fv.exp); + snprint(buf, sizeof(buf), "%#Bp-%d", &fv.val, -fv.exp); out: return fmtstrcpy(fp, buf); diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index e45b4e0d4..b87d35b7b 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -59,7 +59,13 @@ dumpglobls(void) continue; dowidth(n->type); - ggloblnod(n, n->type->width); + ggloblnod(n); + } + + for(l=funcsyms; l; l=l->next) { + n = l->n; + dsymptr(n->sym, 0, n->sym->def->shortname->sym, 0); + ggloblsym(n->sym, widthptr, 1, 1); } } @@ -302,8 +308,8 @@ stringsym(char *s, int len) off = 0; // string header - off = dsymptr(sym, off, sym, widthptr+4); - off = duint32(sym, off, len); + off = dsymptr(sym, off, sym, widthptr+widthint); + off = duintxx(sym, off, len, widthint); // string data for(n=0; nleft); orderexprinplace(&r->ntest); - orderexpr(&r->right->left, out); + orderexpr(&r->right->left, &l->n->ninit); break; case OSEND: - orderexpr(&r->left, out); - orderexpr(&r->right, out); + orderexpr(&r->left, &l->n->ninit); + orderexpr(&r->right, &l->n->ninit); break; } } diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index f2b75d61b..df8903baf 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -14,11 +14,12 @@ compile(Node *fn) { Plist *pl; Node nod1, *n; - Prog *ptxt; + Prog *plocals, *ptxt, *p, *p1; int32 lno; Type *t; Iter save; vlong oldstksize; + NodeList *l; if(newproc == N) { newproc = sysfunc("newproc"); @@ -29,16 +30,19 @@ compile(Node *fn) throwreturn = sysfunc("throwreturn"); } - if(fn->nbody == nil) - return; + lno = setlineno(fn); + + if(fn->nbody == nil) { + if(pure_go || memcmp(fn->nname->sym->name, "init·", 6) == 0) + yyerror("missing function body", fn); + goto ret; + } saveerrors(); // set up domain for labels clearlabels(); - lno = setlineno(fn); - curfn = fn; dowidth(curfn->type); @@ -63,6 +67,10 @@ compile(Node *fn) walk(curfn); if(nerrors != 0) goto ret; + if(flag_race) + racewalk(curfn); + if(nerrors != 0) + goto ret; continpc = P; breakpc = P; @@ -76,15 +84,34 @@ compile(Node *fn) ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1); if(fn->dupok) ptxt->TEXTFLAG = DUPOK; - afunclit(&ptxt->from); + afunclit(&ptxt->from, curfn->nname); ginit(); + + plocals = gins(ALOCALS, N, N); + + for(t=curfn->paramfld; t; t=t->down) + gtrack(tracksym(t->type)); + + for(l=fn->dcl; l; l=l->next) { + n = l->n; + if(n->op != ONAME) // might be OTYPE or OLITERAL + continue; + switch(n->class) { + case PAUTO: + case PPARAM: + case PPARAMOUT: + nodconst(&nod1, types[TUINTPTR], l->n->type->width); + p = gins(ATYPE, l->n, &nod1); + p->from.gotype = ngotype(l->n); + break; + } + } + genlist(curfn->enter); retpc = nil; if(hasdefer || curfn->exit) { - Prog *p1; - p1 = gjmp(nil); retpc = gjmp(nil); patch(p1, pc); @@ -111,6 +138,7 @@ compile(Node *fn) gclean(); if(nerrors != 0) goto ret; + pc->as = ARET; // overwrite AEND pc->lineno = lineno; @@ -120,6 +148,10 @@ compile(Node *fn) oldstksize = stksize; allocauto(ptxt); + + plocals->to.type = D_CONST; + plocals->to.offset = stksize; + if(0) print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize); @@ -177,8 +209,10 @@ allocauto(Prog* ptxt) ll = curfn->dcl; n = ll->n; if (n->class == PAUTO && n->op == ONAME && !n->used) { + // No locals used at all curfn->dcl = nil; stksize = 0; + fixautoused(ptxt); return; } diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c new file mode 100644 index 000000000..bae98ec1b --- /dev/null +++ b/src/cmd/gc/racewalk.c @@ -0,0 +1,579 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The racewalk pass modifies the code tree for the function as follows: +// +// 1. It inserts a call to racefuncenter at the beginning of each function. +// 2. It inserts a call to racefuncexit at the end of each function. +// 3. It inserts a call to raceread before each memory read. +// 4. It inserts a call to racewrite before each memory write. +// +// The rewriting is not yet complete. Certain nodes are not rewritten +// but should be. + +#include +#include +#include "go.h" + +// TODO(dvyukov): do not instrument initialization as writes: +// a := make([]int, 10) + +static void racewalklist(NodeList *l, NodeList **init); +static void racewalknode(Node **np, NodeList **init, int wr, int skip); +static int callinstr(Node **n, NodeList **init, int wr, int skip); +static Node* uintptraddr(Node *n); +static Node* basenod(Node *n); +static void foreach(Node *n, void(*f)(Node*, void*), void *c); +static void hascallspred(Node *n, void *c); +static Node* detachexpr(Node *n, NodeList **init); + +// Do not instrument the following packages at all, +// at best instrumentation would cause infinite recursion. +static const char *omit_pkgs[] = {"runtime", "runtime/race"}; +// Only insert racefuncenter/racefuncexit into the following packages. +// Memory accesses in the packages are either uninteresting or will cause false positives. +static const char *noinst_pkgs[] = {"sync", "sync/atomic"}; + +static int +ispkgin(const char **pkgs, int n) +{ + int i; + + if(myimportpath) { + for(i=0; inbody, nil); + // nothing interesting for race detector in fn->enter + racewalklist(fn->exit, nil); + } + + // nodpc is the PC of the caller as extracted by + // getcallerpc. We use -widthptr(FP) for x86. + // BUG: this will not work on arm. + nodpc = nod(OXXX, nil, nil); + *nodpc = *nodfp; + nodpc->type = types[TUINTPTR]; + nodpc->xoffset = -widthptr; + nd = mkcall("racefuncenter", T, nil, nodpc); + fn->enter = concat(list1(nd), fn->enter); + nd = mkcall("racefuncexit", T, nil); + fn->exit = list(fn->exit, nd); + + if(debug['W']) { + snprint(s, sizeof(s), "after racewalk %S", fn->nname->sym); + dumplist(s, fn->nbody); + snprint(s, sizeof(s), "enter %S", fn->nname->sym); + dumplist(s, fn->enter); + snprint(s, sizeof(s), "exit %S", fn->nname->sym); + dumplist(s, fn->exit); + } +} + +static void +racewalklist(NodeList *l, NodeList **init) +{ + NodeList *instr; + + for(; l; l = l->next) { + instr = nil; + racewalknode(&l->n, &instr, 0, 0); + if(init == nil) + l->n->ninit = concat(l->n->ninit, instr); + else + *init = concat(*init, instr); + } +} + +// walkexpr and walkstmt combined +// walks the tree and adds calls to the +// instrumentation code to top-level (statement) nodes' init +static void +racewalknode(Node **np, NodeList **init, int wr, int skip) +{ + Node *n, *n1; + NodeList *l; + NodeList *fini; + + n = *np; + + if(n == N) + return; + + if(debug['w'] > 1) + dump("racewalk-before", n); + setlineno(n); + if(init == nil || init == &n->ninit) + fatal("racewalk: bad init list"); + + racewalklist(n->ninit, nil); + + switch(n->op) { + default: + fatal("racewalk: unknown node type %O", n->op); + + case OASOP: + case OAS: + case OAS2: + case OAS2DOTTYPE: + case OAS2RECV: + case OAS2FUNC: + case OAS2MAPR: + racewalknode(&n->left, init, 1, 0); + racewalknode(&n->right, init, 0, 0); + goto ret; + + case OCFUNC: + // can't matter + goto ret; + + case OBLOCK: + if(n->list == nil) + goto ret; + + switch(n->list->n->op) { + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + // Blocks are used for multiple return function calls. + // x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]} + // We don't want to instrument between the statements because it will + // smash the results. + racewalknode(&n->list->n, &n->ninit, 0, 0); + fini = nil; + racewalklist(n->list->next, &fini); + n->list = concat(n->list, fini); + break; + + default: + // Ordinary block, for loop initialization or inlined bodies. + racewalklist(n->list, nil); + break; + } + goto ret; + + case ODEFER: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case OPROC: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case OCALLINTER: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case OCALLFUNC: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case OSWITCH: + if(n->ntest->op == OTYPESW) + // TODO(dvyukov): the expression can contain calls or reads. + return; + goto ret; + + case ONOT: + case OMINUS: + case OPLUS: + case OREAL: + case OIMAG: + racewalknode(&n->left, init, wr, 0); + goto ret; + + case ODOTINTER: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case ODOT: + racewalknode(&n->left, init, 0, 1); + callinstr(&n, init, wr, skip); + goto ret; + + case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND + racewalknode(&n->left, init, 0, 0); + callinstr(&n, init, wr, skip); + goto ret; + + case OIND: // *p + racewalknode(&n->left, init, 0, 0); + callinstr(&n, init, wr, skip); + goto ret; + + case OLEN: + case OCAP: + racewalknode(&n->left, init, 0, 0); + if(istype(n->left->type, TMAP)) { + // crashes on len(m[0]) or len(f()) + SET(n1); + USED(n1); + /* + n1 = nod(OADDR, n->left, N); + n1 = conv(n1, types[TUNSAFEPTR]); + n1 = conv(n1, ptrto(ptrto(types[TINT8]))); + n1 = nod(OIND, n1, N); + n1 = nod(OIND, n1, N); + typecheck(&n1, Erv); + callinstr(&n1, init, 0, skip); + */ + } + goto ret; + + case OLSH: + case ORSH: + case OAND: + case OANDNOT: + case OOR: + case OXOR: + case OSUB: + case OMUL: + case OEQ: + case ONE: + case OLT: + case OLE: + case OGE: + case OGT: + case OADD: + case OCOMPLEX: + racewalknode(&n->left, init, wr, 0); + racewalknode(&n->right, init, wr, 0); + goto ret; + + case OANDAND: + case OOROR: + racewalknode(&n->left, init, wr, 0); + // It requires more complex tree transformation, + // because we don't know whether it will be executed or not. + //racewalknode(&n->right, init, wr, 0); + goto ret; + + case ONAME: + callinstr(&n, init, wr, skip); + goto ret; + + case OCONV: + racewalknode(&n->left, init, wr, 0); + goto ret; + + case OCONVNOP: + racewalknode(&n->left, init, wr, 0); + goto ret; + + case ODIV: + case OMOD: + // TODO(dvyukov): add a test for this + racewalknode(&n->left, init, wr, 0); + racewalknode(&n->right, init, wr, 0); + goto ret; + + case OINDEX: + if(!isfixedarray(n->left->type)) + racewalknode(&n->left, init, 0, 0); + else if(!islvalue(n->left)) { + // index of unaddressable array, like Map[k][i]. + racewalknode(&n->left, init, wr, 0); + racewalknode(&n->right, init, 0, 0); + goto ret; + } + racewalknode(&n->right, init, 0, 0); + if(n->left->type->etype != TSTRING) + callinstr(&n, init, wr, skip); + goto ret; + + case OSLICE: + case OSLICEARR: + // Seems to only lead to double instrumentation. + //racewalknode(&n->left, init, 0, 0); + goto ret; + + case OADDR: + racewalknode(&n->left, init, 0, 1); + goto ret; + + case OEFACE: + racewalknode(&n->left, init, 0, 0); + racewalknode(&n->right, init, 0, 0); + goto ret; + + // should not appear in AST by now + case OSEND: + case ORECV: + case OCLOSE: + case ONEW: + case OXCASE: + case OXFALL: + case OCASE: + case OPANIC: + case ORECOVER: + case OCONVIFACE: + yyerror("racewalk: %O must be lowered by now", n->op); + goto ret; + + // just do generic traversal + case OFOR: + case OIF: + case OCALLMETH: + case ORETURN: + case OSELECT: + case OEMPTY: + goto ret; + + // does not require instrumentation + case OINDEXMAP: // implemented in runtime + case OPRINT: // don't bother instrumenting it + case OPRINTN: // don't bother instrumenting it + case OPARAM: // it appears only in fn->exit to copy heap params back + goto ret; + + // unimplemented + case OCMPSTR: + case OADDSTR: + case OSLICESTR: + case OAPPEND: + case OCOPY: + case OMAKECHAN: + case OMAKEMAP: + case OMAKESLICE: + case ORUNESTR: + case OARRAYBYTESTR: + case OARRAYRUNESTR: + case OSTRARRAYBYTE: + case OSTRARRAYRUNE: + case OCMPIFACE: + case OARRAYLIT: + case OMAPLIT: + case OSTRUCTLIT: + case OCLOSURE: + case ODOTTYPE: + case ODOTTYPE2: + case OCALL: + case OBREAK: + case ODCL: + case OCONTINUE: + case OFALL: + case OGOTO: + case OLABEL: + case ODCLCONST: + case ODCLTYPE: + case OLITERAL: + case ORANGE: + case OTYPE: + case ONONAME: + case OINDREG: + case OCOM: + case ODOTMETH: + case OITAB: + case OEXTEND: + case OHMUL: + case OLROT: + case ORROTC: + goto ret; + } + +ret: + if(n->op != OBLOCK) // OBLOCK is handled above in a special way. + racewalklist(n->list, init); + l = nil; + racewalknode(&n->ntest, &l, 0, 0); + n->ninit = concat(n->ninit, l); + l = nil; + racewalknode(&n->nincr, &l, 0, 0); + n->ninit = concat(n->ninit, l); + racewalklist(n->nbody, nil); + racewalklist(n->nelse, nil); + racewalklist(n->rlist, nil); + + *np = n; +} + +static int +isartificial(Node *n) +{ + // compiler-emitted artificial things that we do not want to instrument, + // cant' possibly participate in a data race. + if(n->op == ONAME && n->sym != S && n->sym->name != nil) { + if(strcmp(n->sym->name, "_") == 0) + return 1; + // autotmp's are always local + if(strncmp(n->sym->name, "autotmp_", sizeof("autotmp_")-1) == 0) + return 1; + // statictmp's are read-only + if(strncmp(n->sym->name, "statictmp_", sizeof("statictmp_")-1) == 0) + return 1; + // go.itab is accessed only by the compiler and runtime (assume safe) + if(n->sym->pkg && n->sym->pkg->name && strcmp(n->sym->pkg->name, "go.itab") == 0) + return 1; + } + return 0; +} + +static int +callinstr(Node **np, NodeList **init, int wr, int skip) +{ + Node *f, *b, *n; + Type *t, *t1; + int class, res, hascalls; + + n = *np; + //print("callinstr for %+N [ %O ] etype=%E class=%d\n", + // n, n->op, n->type ? n->type->etype : -1, n->class); + + if(skip || n->type == T || n->type->etype >= TIDEAL) + return 0; + t = n->type; + if(isartificial(n)) + return 0; + if(t->etype == TSTRUCT) { + // PARAMs w/o PHEAP are not interesting. + if(n->class == PPARAM || n->class == PPARAMOUT) + return 0; + res = 0; + hascalls = 0; + foreach(n, hascallspred, &hascalls); + if(hascalls) { + n = detachexpr(n, init); + *np = n; + } + for(t1=t->type; t1; t1=t1->down) { + if(t1->sym && strcmp(t1->sym->name, "_")) { + n = treecopy(n); + f = nod(OXDOT, n, newname(t1->sym)); + f->type = t1; + if(f->type->etype == TFIELD) + f->type = f->type->type; + if(callinstr(&f, init, wr, 0)) { + typecheck(&f, Erv); + res = 1; + } + } + } + return res; + } + + b = basenod(n); + // it skips e.g. stores to ... parameter array + if(isartificial(b)) + return 0; + class = b->class; + // BUG: we _may_ want to instrument PAUTO sometimes + // e.g. if we've got a local variable/method receiver + // that has got a pointer inside. Whether it points to + // the heap or not is impossible to know at compile time + if((class&PHEAP) || class == PPARAMREF || class == PEXTERN + || b->type->etype == TARRAY || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) { + hascalls = 0; + foreach(n, hascallspred, &hascalls); + if(hascalls) { + n = detachexpr(n, init); + *np = n; + } + n = treecopy(n); + f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n)); + *init = list(*init, f); + return 1; + } + return 0; +} + +static Node* +uintptraddr(Node *n) +{ + Node *r; + + r = nod(OADDR, n, N); + r = conv(r, types[TUNSAFEPTR]); + r = conv(r, types[TUINTPTR]); + return r; +} + +static Node* +basenod(Node *n) +{ + for(;;) { + if(n->op == ODOT || n->op == OXDOT || n->op == OCONVNOP || n->op == OCONV || n->op == OPAREN) { + n = n->left; + continue; + } + if(n->op == OINDEX) { + n = n->left; + continue; + } + break; + } + return n; +} + +static Node* +detachexpr(Node *n, NodeList **init) +{ + Node *addr, *as, *ind, *l; + + addr = nod(OADDR, n, N); + l = temp(ptrto(n->type)); + as = nod(OAS, l, addr); + typecheck(&as, Etop); + walkexpr(&as, init); + *init = list(*init, as); + ind = nod(OIND, l, N); + typecheck(&ind, Erv); + walkexpr(&ind, init); + return ind; +} + +static void +foreachnode(Node *n, void(*f)(Node*, void*), void *c) +{ + if(n) + f(n, c); +} + +static void +foreachlist(NodeList *l, void(*f)(Node*, void*), void *c) +{ + for(; l; l = l->next) + foreachnode(l->n, f, c); +} + +static void +foreach(Node *n, void(*f)(Node*, void*), void *c) +{ + foreachlist(n->ninit, f, c); + foreachnode(n->left, f, c); + foreachnode(n->right, f, c); + foreachlist(n->list, f, c); + foreachnode(n->ntest, f, c); + foreachnode(n->nincr, f, c); + foreachlist(n->nbody, f, c); + foreachlist(n->nelse, f, c); + foreachlist(n->rlist, f, c); +} + +static void +hascallspred(Node *n, void *c) +{ + switch(n->op) { + case OCALL: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + (*(int*)c)++; + } +} diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index d301e4e4f..50c4617c0 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -72,6 +72,15 @@ typecheckrange(Node *n) if(n->list->next) v2 = n->list->next->n; + // this is not only a optimization but also a requirement in the spec. + // "if the second iteration variable is the blank identifier, the range + // clause is equivalent to the same clause with only the first variable + // present." + if(isblank(v2)) { + n->list = list1(v1); + v2 = N; + } + if(v1->defn == n) v1->type = t1; else if(v1->type != T && assignop(t1, v1->type, &why) == 0) @@ -145,7 +154,7 @@ walkrange(Node *n) if(v2) { hp = temp(ptrto(n->type->type)); tmp = nod(OINDEX, ha, nodintconst(0)); - tmp->etype = 1; // no bounds check + tmp->bounded = 1; init = list(init, nod(OAS, hp, nod(OADDR, tmp, N))); } diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 62759bcba..b8eb79938 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -5,6 +5,7 @@ #include #include #include "go.h" +#include "../../pkg/runtime/mgc0.h" /* * runtime interface and reflection data structures @@ -14,12 +15,13 @@ static NodeList* signatlist; static Sym* dtypesym(Type*); static Sym* weaktypesym(Type*); static Sym* dalgsym(Type*); +static Sym* dgcsym(Type*); static int sigcmp(Sig *a, Sig *b) { int i; - + i = strcmp(a->name, b->name); if(i != 0) return i; @@ -130,7 +132,12 @@ methodfunc(Type *f, Type *receiver) out = list(out, d); } - return functype(N, in, out); + t = functype(N, in, out); + if(f->nname) { + // Link to name of original method function. + t->nname = f->nname; + } + return t; } /* @@ -165,6 +172,8 @@ methods(Type *t) fatal("non-method on %T method %S %T\n", mt, f->sym, f); if (!getthisx(f->type)->type) fatal("receiver with no type on %T method %S %T\n", mt, f->sym, f); + if(f->nointerface) + continue; method = f->sym; if(method == nil) @@ -203,22 +212,26 @@ methods(Type *t) // but we can generate more efficient code // using genembedtramp if all that is necessary // is a pointer adjustment and a JMP. + compiling_wrappers = 1; if(isptr[it->etype] && isptr[this->etype] && f->embedded && !isifacemethod(f->type)) genembedtramp(it, f, a->isym, 1); else genwrapper(it, f, a->isym, 1); + compiling_wrappers = 0; } } if(!(a->tsym->flags & SymSiggen)) { a->tsym->flags |= SymSiggen; if(!eqtype(this, t)) { + compiling_wrappers = 1; if(isptr[t->etype] && isptr[this->etype] && f->embedded && !isifacemethod(f->type)) genembedtramp(t, f, a->tsym, 0); else genwrapper(t, f, a->tsym, 0); + compiling_wrappers = 0; } } } @@ -262,12 +275,12 @@ imethods(Type *t) else last->link = a; last = a; - + // Compiler can only refer to wrappers for // named interface types. if(t->sym == S) continue; - + // NOTE(rsc): Perhaps an oversight that // IfaceType.Method is not in the reflect data. // Generate the method body, so that compiled @@ -287,7 +300,7 @@ dimportpath(Pkg *p) static Pkg *gopkg; char *nam; Node *n; - + if(p->pathsym != S) return; @@ -303,9 +316,9 @@ dimportpath(Pkg *p) n->class = PEXTERN; n->xoffset = 0; p->pathsym = n->sym; - + gdatastring(n, p->path); - ggloblsym(n->sym, types[TSTRING]->width, 1); + ggloblsym(n->sym, types[TSTRING]->width, 1, 1); } static int @@ -319,7 +332,7 @@ dgopkgpath(Sym *s, int ot, Pkg *pkg) // that imports this one directly defines the symbol. if(pkg == localpkg) { static Sym *ns; - + if(ns == nil) ns = pkglookup("importpath.\"\".", mkpkg(strlit("go"))); return dsymptr(s, ot, ns, 0); @@ -343,7 +356,7 @@ dextratype(Sym *sym, int off, Type *t, int ptroff) m = methods(t); if(t->sym == nil && m == nil) return off; - + // fill in *extraType pointer in header dsymptr(sym, ptroff, sym, off); @@ -367,9 +380,9 @@ dextratype(Sym *sym, int off, Type *t, int ptroff) } // slice header - ot = dsymptr(s, ot, s, ot + widthptr + 2*4); - ot = duint32(s, ot, n); - ot = duint32(s, ot, n); + ot = dsymptr(s, ot, s, ot + widthptr + 2*widthint); + ot = duintxx(s, ot, n, widthint); + ot = duintxx(s, ot, n, widthint); // methods for(a=m; a; a=a->link) { @@ -419,7 +432,7 @@ enum { KindString, KindStruct, KindUnsafePointer, - + KindNoPointers = 1<<7, }; @@ -454,19 +467,6 @@ kinds[] = [TUNSAFEPTR] = KindUnsafePointer, }; -static Sym* -typestruct(Type *t) -{ - // We use a weak reference to the reflect type - // to avoid requiring package reflect in every binary. - // If package reflect is available, the interface{} holding - // a runtime type will contain a *reflect.commonType. - // Otherwise it will use a nil type word but still be usable - // by package runtime (because we always use the memory - // after the interface value, not the interface value itself). - return pkglookup("*reflect.commonType", weaktypepkg); -} - int haspointers(Type *t) { @@ -521,6 +521,9 @@ dcommontype(Sym *s, int ot, Type *t) Sym *sptr, *algsym; static Sym *algarray; char *p; + + if(ot != 0) + fatal("dcommontype %d", ot); sizeofAlg = 4*widthptr; if(algarray == nil) @@ -536,31 +539,34 @@ dcommontype(Sym *s, int ot, Type *t) else sptr = weaktypesym(ptrto(t)); - // empty interface pointing at this type. - // all the references that we emit are *interface{}; - // they point here. - ot = rnd(ot, widthptr); - ot = dsymptr(s, ot, typestruct(t), 0); - ot = dsymptr(s, ot, s, 2*widthptr); - // ../../pkg/reflect/type.go:/^type.commonType // actual type structure // type commonType struct { - // size uintptr; - // hash uint32; - // alg uint8; - // align uint8; - // fieldAlign uint8; - // kind uint8; - // string *string; - // *extraType; - // ptrToThis *Type + // size uintptr + // hash uint32 + // _ uint8 + // align uint8 + // fieldAlign uint8 + // kind uint8 + // alg unsafe.Pointer + // gc unsafe.Pointer + // string *string + // *extraType + // ptrToThis *Type // } ot = duintptr(s, ot, t->width); ot = duint32(s, ot, typehash(t)); ot = duint8(s, ot, 0); // unused + + // runtime (and common sense) expects alignment to be a power of two. + i = t->align; + if(i == 0) + i = 1; + if((i&(i-1)) != 0) + fatal("invalid alignment %d for %T", t->align, t); ot = duint8(s, ot, t->align); // align ot = duint8(s, ot, t->align); // fieldAlign + i = kinds[t->etype]; if(t->etype == TARRAY && t->bound < 0) i = KindSlice; @@ -571,11 +577,12 @@ dcommontype(Sym *s, int ot, Type *t) ot = dsymptr(s, ot, algarray, alg*sizeofAlg); else ot = dsymptr(s, ot, algsym, 0); + ot = dsymptr(s, ot, dgcsym(t), 0); // gc p = smprint("%-uT", t); //print("dcommontype: %s\n", p); ot = dgostringptr(s, ot, p); // string free(p); - + // skip pointer to extraType, // which follows the rest of this type structure. // caller will fill in if needed. @@ -599,6 +606,39 @@ typesym(Type *t) return s; } +Sym* +tracksym(Type *t) +{ + char *p; + Sym *s; + + p = smprint("%-T.%s", t->outer, t->sym->name); + s = pkglookup(p, trackpkg); + free(p); + return s; +} + +Sym* +typelinksym(Type *t) +{ + char *p; + Sym *s; + + // %-uT is what the generated Type's string field says. + // It uses (ambiguous) package names instead of import paths. + // %-T is the complete, unambiguous type name. + // We want the types to end up sorted by string field, + // so use that first in the name, and then add :%-T to + // disambiguate. The names are a little long but they are + // discarded by the linker and do not end up in the symbol + // table of the final binary. + p = smprint("%-uT/%-T", t, t); + s = pkglookup(p, typelinkpkg); + //print("typelinksym: %s -> %+S\n", p, s); + free(p); + return s; +} + Sym* typesymprefix(char *prefix, Type *t) { @@ -612,8 +652,8 @@ typesymprefix(char *prefix, Type *t) return s; } -Node* -typename(Type *t) +Sym* +typenamesym(Type *t) { Sym *s; Node *n; @@ -634,7 +674,16 @@ typename(Type *t) signatlist = list(signatlist, typenod(t)); } + return s->def->sym; +} + +Node* +typename(Type *t) +{ + Sym *s; + Node *n; + s = typenamesym(t); n = nod(OADDR, s->def, N); n->type = ptrto(s->def->type); n->addable = 1; @@ -660,10 +709,16 @@ static Sym* dtypesym(Type *t) { int ot, xt, n, isddd, dupok; - Sym *s, *s1, *s2; + Sym *s, *s1, *s2, *slink; Sig *a, *m; Type *t1, *tbase, *t2; + // Replace byte, rune aliases with real type. + // They've been separate internally to make error messages + // better, but we have to merge them in the reflect tables. + if(t == bytetype || t == runetype) + t = types[t->etype]; + if(isideal(t)) fatal("dtypesym %T", t); @@ -680,7 +735,7 @@ dtypesym(Type *t) tbase = t->type; dupok = tbase->sym == S; - if(compiling_runtime && + if(compiling_runtime && (tbase == types[tbase->etype] || tbase == bytetype || tbase == runetype || @@ -751,13 +806,13 @@ ok: // two slice headers: in and out. ot = rnd(ot, widthptr); - ot = dsymptr(s, ot, s, ot+2*(widthptr+2*4)); + ot = dsymptr(s, ot, s, ot+2*(widthptr+2*widthint)); n = t->thistuple + t->intuple; - ot = duint32(s, ot, n); - ot = duint32(s, ot, n); - ot = dsymptr(s, ot, s, ot+1*(widthptr+2*4)+n*widthptr); - ot = duint32(s, ot, t->outtuple); - ot = duint32(s, ot, t->outtuple); + ot = duintxx(s, ot, n, widthint); + ot = duintxx(s, ot, n, widthint); + ot = dsymptr(s, ot, s, ot+1*(widthptr+2*widthint)+n*widthptr); + ot = duintxx(s, ot, t->outtuple, widthint); + ot = duintxx(s, ot, t->outtuple, widthint); // slice data for(t1=getthisx(t)->type; t1; t1=t1->down, n++) @@ -779,9 +834,9 @@ ok: // ../../pkg/runtime/type.go:/InterfaceType ot = dcommontype(s, ot, t); xt = ot - 2*widthptr; - ot = dsymptr(s, ot, s, ot+widthptr+2*4); - ot = duint32(s, ot, n); - ot = duint32(s, ot, n); + ot = dsymptr(s, ot, s, ot+widthptr+2*widthint); + ot = duintxx(s, ot, n, widthint); + ot = duintxx(s, ot, n, widthint); for(a=m; a; a=a->link) { // ../../pkg/runtime/type.go:/imethod ot = dgostringptr(s, ot, a->name); @@ -824,9 +879,9 @@ ok: } ot = dcommontype(s, ot, t); xt = ot - 2*widthptr; - ot = dsymptr(s, ot, s, ot+widthptr+2*4); - ot = duint32(s, ot, n); - ot = duint32(s, ot, n); + ot = dsymptr(s, ot, s, ot+widthptr+2*widthint); + ot = duintxx(s, ot, n, widthint); + ot = duintxx(s, ot, n, widthint); for(t1=t->type; t1!=T; t1=t1->down) { // ../../pkg/runtime/type.go:/structField if(t1->sym && !t1->embedded) { @@ -837,7 +892,10 @@ ok: ot = dgopkgpath(s, ot, t1->sym->pkg); } else { ot = dgostringptr(s, ot, nil); - ot = dgostringptr(s, ot, nil); + if(t1->type->sym != S && t1->type->sym->pkg == builtinpkg) + ot = dgopkgpath(s, ot, localpkg); + else + ot = dgostringptr(s, ot, nil); } ot = dsymptr(s, ot, dtypesym(t1->type), 0); ot = dgostrlitptr(s, ot, t1->note); @@ -846,7 +904,24 @@ ok: break; } ot = dextratype(s, ot, t, xt); - ggloblsym(s, ot, dupok); + ggloblsym(s, ot, dupok, 1); + + // generate typelink.foo pointing at s = type.foo. + // The linker will leave a table of all the typelinks for + // types in the binary, so reflect can find them. + // We only need the link for unnamed composites that + // we want be able to find. + if(t->sym == S) { + switch(t->etype) { + case TARRAY: + case TCHAN: + case TMAP: + slink = typelinksym(t); + dsymptr(slink, 0, s, 0); + ggloblsym(slink, widthptr, dupok, 1); + } + } + return s; } @@ -899,12 +974,14 @@ dumptypestructs(void) // emit type structs for error and func(error) string. // The latter is the type of an auto-generated wrapper. dtypesym(ptrto(errortype)); - dtypesym(functype(nil, + dtypesym(functype(nil, list1(nod(ODCLFIELD, N, typenod(errortype))), list1(nod(ODCLFIELD, N, typenod(types[TSTRING]))))); - + // add paths for runtime and main, which 6l imports implicitly. dimportpath(runtimepkg); + if(flag_race) + dimportpath(racepkg); dimportpath(mkpkg(strlit("main"))); } } @@ -944,7 +1021,180 @@ dalgsym(Type *t) break; } - ggloblsym(s, ot, 1); + ggloblsym(s, ot, 1, 1); return s; } +static int +dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size) +{ + Type *t1; + vlong o, off2, fieldoffset; + + if(t->align > 0 && (*off % t->align) != 0) + fatal("dgcsym1: invalid initial alignment, %T", t); + + switch(t->etype) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TINT: + case TUINT: + case TUINTPTR: + case TBOOL: + case TFLOAT32: + case TFLOAT64: + case TCOMPLEX64: + case TCOMPLEX128: + *off += t->width; + break; + + case TPTR32: + case TPTR64: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(!haspointers(t->type) || t->type->etype == TUINT8) { + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + } else { + ot = duintptr(s, ot, GC_PTR); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dgcsym(t->type), 0); + } + *off += t->width; + break; + + case TCHAN: + case TUNSAFEPTR: + case TFUNC: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + *off += t->width; + break; + + // struct Hmap* + case TMAP: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_MAP_PTR); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dtypesym(t), 0); + *off += t->width; + break; + + // struct { byte *str; int32 len; } + case TSTRING: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_STRING); + ot = duintptr(s, ot, *off); + *off += t->width; + break; + + // struct { Itab* tab; void* data; } + // struct { Type* type; void* data; } // When isnilinter(t)==true + case TINTER: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(isnilinter(t)) { + ot = duintptr(s, ot, GC_EFACE); + ot = duintptr(s, ot, *off); + } else { + ot = duintptr(s, ot, GC_IFACE); + ot = duintptr(s, ot, *off); + } + *off += t->width; + break; + + case TARRAY: + if(t->bound < -1) + fatal("dgcsym1: invalid bound, %T", t); + if(isslice(t)) { + // struct { byte* array; uint32 len; uint32 cap; } + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(t->type->width != 0) { + ot = duintptr(s, ot, GC_SLICE); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dgcsym(t->type), 0); + } else { + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + } + *off += t->width; + } else { + if(t->bound < 1 || !haspointers(t->type)) { + *off += t->width; + } else if(t->bound == 1) { + ot = dgcsym1(s, ot, t->type, off, stack_size); // recursive call of dgcsym1 + } else { + if(stack_size < GC_STACK_CAPACITY) { + ot = duintptr(s, ot, GC_ARRAY_START); // a stack push during GC + ot = duintptr(s, ot, *off); + ot = duintptr(s, ot, t->bound); + ot = duintptr(s, ot, t->type->width); + off2 = 0; + ot = dgcsym1(s, ot, t->type, &off2, stack_size+1); // recursive call of dgcsym1 + ot = duintptr(s, ot, GC_ARRAY_NEXT); // a stack pop during GC + } else { + ot = duintptr(s, ot, GC_REGION); + ot = duintptr(s, ot, *off); + ot = duintptr(s, ot, t->width); + ot = dsymptr(s, ot, dgcsym(t), 0); + } + *off += t->width; + } + } + break; + + case TSTRUCT: + o = 0; + for(t1=t->type; t1!=T; t1=t1->down) { + fieldoffset = t1->width; + *off += fieldoffset - o; + ot = dgcsym1(s, ot, t1->type, off, stack_size); // recursive call of dgcsym1 + o = fieldoffset + t1->type->width; + } + *off += t->width - o; + break; + + default: + fatal("dgcsym1: unexpected type %T", t); + } + + return ot; +} + +static Sym* +dgcsym(Type *t) +{ + int ot; + vlong off; + Sym *s; + + s = typesymprefix(".gc", t); + if(s->flags & SymGcgen) + return s; + s->flags |= SymGcgen; + + ot = 0; + off = 0; + ot = duintptr(s, ot, t->width); + ot = dgcsym1(s, ot, t, &off, 0); + ot = duintptr(s, ot, GC_END); + ggloblsym(s, ot, 1, 1); + + if(t->align > 0) + off = rnd(off, t->align); + if(off != t->width) + fatal("dgcsym: off=%lld, size=%lld, type %T", off, t->width, t); + + return s; +} diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 15a61d9ef..c49d05c5c 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -45,8 +45,7 @@ func appendslice(typ *byte, x any, y []any) any func appendstr(typ *byte, x []byte, y string) []byte func cmpstring(string, string) int -func slicestring(string, int, int) string -func slicestring1(string, int) string +func eqstring(string, string) bool func intstring(int64) string func slicebytetostring([]byte) string func slicerunetostring([]rune) string @@ -54,14 +53,15 @@ func stringtoslicebyte(string) []byte func stringtoslicerune(string) []rune func stringiter(string, int) int func stringiter2(string, int) (retk int, retv rune) -func copy(to any, fr any, wid uint32) int +func copy(to any, fr any, wid uintptr) int func slicestringcopy(to any, fr any) int // interface conversions +func typ2Itab(typ *byte, typ2 *byte, cache **byte) (ret *byte) func convI2E(elem any) (ret any) func convI2I(typ *byte, elem any) (ret any) func convT2E(typ *byte, elem any) (ret any) -func convT2I(typ *byte, typ2 *byte, elem any) (ret any) +func convT2I(typ *byte, typ2 *byte, cache **byte, elem any) (ret any) // interface type assertions x.(T) func assertE2E(typ *byte, iface any) (ret any) @@ -76,6 +76,8 @@ func assertI2I(typ *byte, iface any) (ret any) func assertI2I2(typ *byte, iface any) (ret any, ok bool) func assertI2T(typ *byte, iface any) (ret any) func assertI2T2(typ *byte, iface any) (ret any, ok bool) +func assertI2TOK(typ *byte, iface any) (ok bool) +func assertE2TOK(typ *byte, iface any) (ok bool) func ifaceeq(i1 any, i2 any) (ret bool) func efaceeq(i1 any, i2 any) (ret bool) @@ -89,7 +91,6 @@ func makemap(mapType *byte, hint int64) (hmap map[any]any) func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any) func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool) func mapassign1(mapType *byte, hmap map[any]any, key any, val any) -func mapassign2(mapType *byte, hmap map[any]any, key any, val any, pres bool) func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) func mapdelete(mapType *byte, hmap map[any]any, key any) func mapiternext(hiter *any) @@ -107,7 +108,7 @@ func selectnbsend(chanType *byte, hchan chan<- any, elem any) bool func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool -func newselect(size int) (sel *byte) +func newselect(size int32) (sel *byte) func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool) func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool) func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool) @@ -117,11 +118,6 @@ func block() func makeslice(typ *byte, nel int64, cap int64) (ary []any) func growslice(typ *byte, old []any, n int64) (ary []any) -func sliceslice1(old []any, lb uint64, width uint64) (ary []any) -func sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) -func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary []any) - -func closure() // has args, but compiler fills in func memequal(eq *bool, size uintptr, x, y *any) func memequal8(eq *bool, size uintptr, x, y *any) @@ -141,3 +137,9 @@ func int64tofloat64(int64) float64 func uint64tofloat64(uint64) float64 func complex128div(num complex128, den complex128) (quo complex128) + +// race detection +func racefuncenter(uintptr) +func racefuncexit() +func raceread(uintptr) +func racewrite(uintptr) diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 8ace1d4ee..cd3de8c7b 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -62,7 +62,7 @@ typecheckselect(Node *sel) case OAS2RECV: // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok - if(n->right->op != ORECV) { + if(n->rlist->n->op != ORECV) { yyerror("select assignment must have receive on right hand side"); break; } @@ -70,6 +70,7 @@ typecheckselect(Node *sel) n->left = n->list->n; n->ntest = n->list->next->n; n->right = n->rlist->n; + n->rlist = nil; break; case ORECV: @@ -146,7 +147,7 @@ walkselect(Node *sel) a = nod(OAS2, N, N); a->list = n->list; - a->rlist = n->rlist; + a->rlist = list1(n->right); n = a; typecheck(&n, Etop); break; @@ -296,15 +297,15 @@ walkselect(Node *sel) setlineno(cas); n = cas->left; r = nod(OIF, N, N); - r->nbody = cas->ninit; + r->ninit = cas->ninit; cas->ninit = nil; if(n != nil) { - r->nbody = concat(r->nbody, n->ninit); + r->ninit = concat(r->ninit, n->ninit); n->ninit = nil; } if(n == nil) { // selectdefault(sel *byte); - r->ntest = mkcall("selectdefault", types[TBOOL], &init, var); + r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var); } else { switch(n->op) { default: @@ -312,25 +313,25 @@ walkselect(Node *sel) case OSEND: // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); - n->left = safeexpr(n->left, &r->ninit); + n->left = localexpr(safeexpr(n->left, &r->ninit), n->left->type, &r->ninit); n->right = localexpr(n->right, n->left->type->type, &r->ninit); n->right = nod(OADDR, n->right, N); n->right->etype = 1; // pointer does not escape typecheck(&n->right, Erv); r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], - &init, var, n->left, n->right); + &r->ninit, var, n->left, n->right); break; case OSELRECV: // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL], - &init, var, n->right->left, n->left); + &r->ninit, var, n->right->left, n->left); break; case OSELRECV2: // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool); r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL], - &init, var, n->right->left, n->left, n->ntest); + &r->ninit, var, n->right->left, n->left, n->ntest); break; } } diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index c8796f8b7..353fc00ce 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -37,6 +37,12 @@ init1(Node *n, NodeList **out) for(l=n->list; l; l=l->next) init1(l->n, out); + if(n->left && n->type && n->left->op == OTYPE && n->class == PFUNC) { + // Methods called as Type.Method(receiver, ...). + // Definitions for method expressions are stored in type->nname. + init1(n->type->nname, out); + } + if(n->op != ONAME) return; switch(n->class) { @@ -78,6 +84,11 @@ init1(Node *n, NodeList **out) } n->initorder = InitPending; l = malloc(sizeof *l); + if(l == nil) { + flusherrors(); + yyerror("out of memory"); + errorexit(); + } l->next = initlist; l->n = n; l->end = nil; @@ -97,6 +108,13 @@ init1(Node *n, NodeList **out) case OAS: if(n->defn->left != n) goto bad; + if(isblank(n->defn->left) && candiscard(n->defn->right)) { + n->defn->op = OEMPTY; + n->defn->left = N; + n->defn->right = N; + break; + } + /* n->defn->dodata = 1; init1(n->defn->right, out); @@ -167,6 +185,11 @@ init2(Node *n, NodeList **out) init2list(n->rlist, out); init2list(n->nbody, out); init2list(n->nelse, out); + + if(n->op == OCLOSURE) + init2list(n->closure->nbody, out); + if(n->op == ODOTMETH) + init2(n->type->nname, out); } static void @@ -291,9 +314,9 @@ staticcopy(Node *l, Node *r, NodeList **out) n1.xoffset = l->xoffset + Array_array; gdata(&n1, nod(OADDR, a, N), widthptr); n1.xoffset = l->xoffset + Array_nel; - gdata(&n1, r->right, 4); + gdata(&n1, r->right, widthint); n1.xoffset = l->xoffset + Array_cap; - gdata(&n1, r->right, 4); + gdata(&n1, r->right, widthint); return 1; } // fall through @@ -394,9 +417,9 @@ staticassign(Node *l, Node *r, NodeList **out) n1.xoffset = l->xoffset + Array_array; gdata(&n1, nod(OADDR, a, N), widthptr); n1.xoffset = l->xoffset + Array_nel; - gdata(&n1, r->right, 4); + gdata(&n1, r->right, widthint); n1.xoffset = l->xoffset + Array_cap; - gdata(&n1, r->right, 4); + gdata(&n1, r->right, widthint); // Fall through to init underlying array. l = a; } @@ -747,7 +770,7 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) index = r->left; value = r->right; a = nod(OINDEX, var, index); - a->etype = 1; // no bounds checking + a->bounded = 1; // TODO need to check bounds? switch(value->op) { @@ -879,11 +902,11 @@ ctxt = 0; index = temp(types[TINT]); a = nod(OINDEX, vstat, index); - a->etype = 1; // no bounds checking + a->bounded = 1; a = nod(ODOT, a, newname(symb)); r = nod(OINDEX, vstat, index); - r->etype = 1; // no bounds checking + r->bounded = 1; r = nod(ODOT, r, newname(syma)); r = nod(OINDEX, var, r); @@ -930,7 +953,7 @@ void anylit(int ctxt, Node *n, Node *var, NodeList **init) { Type *t; - Node *a, *vstat; + Node *a, *vstat, *r; t = n->type; switch(n->op) { @@ -941,7 +964,14 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init) if(!isptr[t->etype]) fatal("anylit: not ptr"); - a = nod(OAS, var, callnew(t->type)); + r = nod(ONEW, N, N); + r->typecheck = 1; + r->type = t; + r->esc = n->esc; + walkexpr(&r, init); + + a = nod(OAS, var, r); + typecheck(&a, Etop); *init = list(*init, a); @@ -1223,11 +1253,11 @@ slice: gdata(&nam, nl, types[tptr]->width); nam.xoffset += Array_nel-Array_array; - nodconst(&nod1, types[TINT32], nr->type->bound); - gdata(&nam, &nod1, types[TINT32]->width); + nodconst(&nod1, types[TINT], nr->type->bound); + gdata(&nam, &nod1, widthint); nam.xoffset += Array_cap-Array_nel; - gdata(&nam, &nod1, types[TINT32]->width); + gdata(&nam, &nod1, widthint); goto yes; diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index bd53520df..c53eaf285 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -142,18 +142,32 @@ yyerror(char *fmt, ...) if(debug['x']) print("yyerror: yystate=%d yychar=%d\n", yystate, yychar); + // An unexpected EOF caused a syntax error. Use the previous + // line number since getc generated a fake newline character. + if(curio.eofnl) + lexlineno = prevlineno; + // only one syntax error per line if(lastsyntax == lexlineno) return; lastsyntax = lexlineno; - - if(strstr(fmt, "{ or {")) { + + if(strstr(fmt, "{ or {") || strstr(fmt, " or ?") || strstr(fmt, " or @")) { // The grammar has { and LBRACE but both show up as {. // Rewrite syntax error referring to "{ or {" to say just "{". strecpy(buf, buf+sizeof buf, fmt); p = strstr(buf, "{ or {"); if(p) memmove(p+1, p+6, strlen(p+6)+1); + + // The grammar has ? and @ but only for reading imports. + // Silence them in ordinary errors. + p = strstr(buf, " or ?"); + if(p) + memmove(p, p+5, strlen(p+5)+1); + p = strstr(buf, " or @"); + if(p) + memmove(p, p+5, strlen(p+5)+1); fmt = buf; } @@ -214,6 +228,8 @@ warnl(int line, char *fmt, ...) va_start(arg, fmt); adderr(line, fmt, arg); va_end(arg); + if(debug['m']) + flusherrors(); } void @@ -377,6 +393,7 @@ importdot(Pkg *opkg, Node *pack) Sym *s, *s1; uint32 h; int n; + char *pkgerror; n = 0; for(h=0; hname); if(s1->def != N) { - redeclare(s1, "during import"); + pkgerror = smprint("during import \"%Z\"", opkg->path); + redeclare(s1, pkgerror); continue; } s1->def = s->def; s1->block = s->block; s1->def->pack = pack; + s1->origpkg = opkg; n++; } } @@ -494,6 +513,31 @@ nod(int op, Node *nleft, Node *nright) return n; } +void +saveorignode(Node *n) +{ + Node *norig; + + if(n->orig != N) + return; + norig = nod(n->op, N, N); + *norig = *n; + n->orig = norig; +} + +// ispaddedfield returns whether the given field +// is followed by padding. For the case where t is +// the last field, total gives the size of the enclosing struct. +static int +ispaddedfield(Type *t, vlong total) +{ + if(t->etype != TFIELD) + fatal("ispaddedfield called non-field %T", t); + if(t->down == T) + return t->width + t->type->width != total; + return t->width + t->type->width != t->down->width; +} + int algtype1(Type *t, Type **bad) { @@ -571,8 +615,12 @@ algtype1(Type *t, Type **bad) } ret = AMEM; for(t1=t->type; t1!=T; t1=t1->down) { - if(isblanksym(t1->sym)) + // Blank fields and padding must be ignored, + // so need special compare. + if(isblanksym(t1->sym) || ispaddedfield(t1, t->width)) { + ret = -1; continue; + } a = algtype1(t1->type, bad); if(a == ANOEQ) return ANOEQ; // not comparable @@ -813,6 +861,7 @@ treecopy(Node *n) default: m = nod(OXXX, N, N); *m = *n; + m->orig = m; m->left = treecopy(n->left); m->right = treecopy(n->right); m->list = listtreecopy(n->list); @@ -1214,7 +1263,7 @@ assignop(Type *src, Type *dst, char **why) return 0; } if(src->etype == TINTER && dst->etype != TBLANK) { - if(why != nil) + if(why != nil && implements(dst, src, &missing, &have, &ptr)) *why = ": need type assertion"; return 0; } @@ -1379,6 +1428,7 @@ assignconv(Node *n, Type *t, char *context) r->type = t; r->typecheck = 1; r->implicit = 1; + r->orig = n->orig; return r; } @@ -2029,11 +2079,13 @@ cheapexpr(Node *n, NodeList **init) /* * return n in a local variable of type t if it is not already. + * the value is guaranteed not to change except by direct + * assignment to it. */ Node* localexpr(Node *n, Type *t, NodeList **init) { - if(n->op == ONAME && + if(n->op == ONAME && !n->addrtaken && (n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) && convertop(n->type, t, nil) == OCONVNOP) return n; @@ -2508,6 +2560,9 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) funcbody(fn); curfn = fn; + // wrappers where T is anonymous (struct{ NamedType }) can be duplicated. + if(rcvr->etype == TSTRUCT || isptr[rcvr->etype] && rcvr->type->etype == TSTRUCT) + fn->dupok = 1; typecheck(&fn, Etop); typechecklist(fn->nbody, Etop); curfn = nil; @@ -2591,7 +2646,7 @@ genhash(Sym *sym, Type *t) Node *hashel; Type *first, *t1; int old_safemode; - int64 size, mul; + int64 size, mul, offend; if(debug['r']) print("genhash %S %T\n", sym, t); @@ -2664,7 +2719,7 @@ genhash(Sym *sym, Type *t) call->list = list(call->list, nh); call->list = list(call->list, nodintconst(t->type->width)); nx = nod(OINDEX, np, ni); - nx->etype = 1; // no bounds check + nx->bounded = 1; na = nod(OADDR, nx, N); na->etype = 1; // no escape to heap call->list = list(call->list, na); @@ -2677,22 +2732,21 @@ genhash(Sym *sym, Type *t) // Walk the struct using memhash for runs of AMEM // and calling specific hash functions for the others. first = T; + offend = 0; for(t1=t->type;; t1=t1->down) { - if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) { - if(first == T && !isblanksym(t1->sym)) + if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) { + offend = t1->width + t1->type->width; + if(first == T) first = t1; - continue; + // If it's a memory field but it's padded, stop here. + if(ispaddedfield(t1, t->width)) + t1 = t1->down; + else + continue; } // Run memhash for fields up to this one. - while(first != T && isblanksym(first->sym)) - first = first->down; if(first != T) { - if(first->down == t1) - size = first->type->width; - else if(t1 == T) - size = t->width - first->width; // first->width is offset - else - size = t1->width - first->width; // both are offsets + size = offend - first->width; // first->width is offset hashel = hashmem(first->type); // hashel(h, size, &p.first) call = nod(OCALL, hashel, N); @@ -2708,6 +2762,8 @@ genhash(Sym *sym, Type *t) } if(t1 == T) break; + if(isblanksym(t1->sym)) + continue; // Run hash for this field. hashel = hashfor(t1->type); @@ -2721,6 +2777,8 @@ genhash(Sym *sym, Type *t) call->list = list(call->list, na); fn->nbody = list(fn->nbody, call); } + // make sure body is not empty. + fn->nbody = list(fn->nbody, nod(ORETURN, N, N)); break; } @@ -2822,6 +2880,7 @@ geneq(Sym *sym, Type *t) Type *t1, *first; int old_safemode; int64 size; + int64 offend; if(debug['r']) print("geneq %S %T\n", sym, t); @@ -2875,9 +2934,9 @@ geneq(Sym *sym, Type *t) // if p[i] != q[i] { *eq = false; return } nx = nod(OINDEX, np, ni); - nx->etype = 1; // no bounds check + nx->bounded = 1; ny = nod(OINDEX, nq, ni); - ny->etype = 1; // no bounds check + ny->bounded = 1; nif = nod(OIF, N, N); nif->ntest = nod(ONE, nx, ny); @@ -2893,18 +2952,23 @@ geneq(Sym *sym, Type *t) case TSTRUCT: // Walk the struct using memequal for runs of AMEM // and calling specific equality tests for the others. + // Skip blank-named fields. first = T; + offend = 0; for(t1=t->type;; t1=t1->down) { - if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) { - if(first == T && !isblanksym(t1->sym)) + if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) { + offend = t1->width + t1->type->width; + if(first == T) first = t1; - continue; + // If it's a memory field but it's padded, stop here. + if(ispaddedfield(t1, t->width)) + t1 = t1->down; + else + continue; } // Run memequal for fields up to this one. // TODO(rsc): All the calls to newname are wrong for // cross-package unexported fields. - while(first != T && isblanksym(first->sym)) - first = first->down; if(first != T) { if(first->down == t1) { fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); @@ -2915,16 +2979,15 @@ geneq(Sym *sym, Type *t) fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); } else { // More than two fields: use memequal. - if(t1 == T) - size = t->width - first->width; // first->width is offset - else - size = t1->width - first->width; // both are offsets + size = offend - first->width; // first->width is offset fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq)); } first = T; } if(t1 == T) break; + if(isblanksym(t1->sym)) + continue; // Check this field, which is not just memory. fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq)); @@ -3031,7 +3094,7 @@ implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr) for(im=iface->type; im; im=im->down) { imtype = methodfunc(im->type, 0); tm = ifacelookdot(im->sym, t, &followptr, 0); - if(tm == T || !eqtype(methodfunc(tm->type, 0), imtype)) { + if(tm == T || tm->nointerface || !eqtype(methodfunc(tm->type, 0), imtype)) { if(tm == T) tm = ifacelookdot(im->sym, t, &followptr, 1); *m = im; @@ -3504,11 +3567,8 @@ umagic(Magic *m) Sym* ngotype(Node *n) { - if(n->sym != S && n->realtype != T) - if(strncmp(n->sym->name, "autotmp_", 8) != 0) - if(strncmp(n->sym->name, "statictmp_", 8) != 0) - return typename(n->realtype)->left->sym; - + if(n->type != T) + return typenamesym(n->type); return S; } @@ -3564,9 +3624,6 @@ mkpkg(Strlit *path) Pkg *p; int h; - if(isbadimport(path)) - errorexit(); - h = stringhash(path->s) & (nelem(phash)-1); for(p=phash[h]; p; p=p->link) if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0) @@ -3615,9 +3672,15 @@ addinit(Node **np, NodeList *init) n->ullman = UINF; } +static char* reservedimports[] = { + "go", + "type", +}; + int isbadimport(Strlit *path) { + int i; char *s; Rune r; @@ -3625,6 +3688,13 @@ isbadimport(Strlit *path) yyerror("import path contains NUL"); return 1; } + + for(i=0; is, reservedimports[i]) == 0) { + yyerror("import path \"%s\" is reserved and cannot be used", path->s); + return 1; + } + } s = path->s; while(*s) { diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c index f1a95587f..a497b8622 100644 --- a/src/cmd/gc/swt.c +++ b/src/cmd/gc/swt.c @@ -117,12 +117,15 @@ exprcmp(Case *c1, Case *c2) n1 = c1->node->left; n2 = c2->node->left; + // sort by type (for switches on interface) ct = n1->val.ctype; - if(ct != n2->val.ctype) { - // invalid program, but return a sort - // order so that we can give a better - // error later. + if(ct != n2->val.ctype) return ct - n2->val.ctype; + if(!eqtype(n1->type, n2->type)) { + if(n1->type->vargen > n2->type->vargen) + return +1; + else + return -1; } // sort by constant value @@ -259,10 +262,11 @@ casebody(Node *sw, Node *typeswvar) Node *go, *br; int32 lno, needvar; - lno = setlineno(sw); if(sw->list == nil) return; + lno = setlineno(sw); + cas = nil; // cases stat = nil; // statements def = N; // defaults @@ -270,7 +274,7 @@ casebody(Node *sw, Node *typeswvar) for(l=sw->list; l; l=l->next) { n = l->n; - lno = setlineno(n); + setlineno(n); if(n->op != OXCASE) fatal("casebody %O", n->op); n->op = OCASE; @@ -378,6 +382,7 @@ mkcaselist(Node *sw, int arg) case Strue: case Sfalse: c->type = Texprvar; + c->hash = typehash(n->left->type); switch(consttype(n->left)) { case CTFLT: case CTINT: @@ -442,6 +447,10 @@ exprbsw(Case *c0, int ncase, int arg) n = c0->node; lno = setlineno(n); + if(assignop(n->left->type, exprname->type, nil) == OCONVIFACE || + assignop(exprname->type, n->left->type, nil) == OCONVIFACE) + goto snorm; + switch(arg) { case Strue: a = nod(OIF, N, N); @@ -457,6 +466,7 @@ exprbsw(Case *c0, int ncase, int arg) break; default: + snorm: a = nod(OIF, N, N); a->ntest = nod(OEQ, exprname, n->left); // if name == val typecheck(&a->ntest, Erv); @@ -520,6 +530,8 @@ exprswitch(Node *sw) exprname = temp(sw->ntest->type); cas = list1(nod(OAS, exprname, sw->ntest)); typechecklist(cas, Etop); + } else { + exprname = nodbool(arg == Strue); } c0 = mkcaselist(sw, arg); @@ -786,7 +798,6 @@ typeswitch(Node *sw) void walkswitch(Node *sw) { - /* * reorder the body into (OLIST, cases, statements) * cases have OGOTO into statements. @@ -813,7 +824,7 @@ typecheckswitch(Node *n) { int top, lno, ptr; char *nilonly; - Type *t, *missing, *have; + Type *t, *badtype, *missing, *have; NodeList *l, *ll; Node *ncase, *nvar; Node *def; @@ -839,10 +850,14 @@ typecheckswitch(Node *n) } else t = types[TBOOL]; if(t) { - if(!okforeq[t->etype] || isfixedarray(t)) + if(!okforeq[t->etype]) yyerror("cannot switch on %lN", n->ntest); - else if(t->etype == TARRAY) + else if(t->etype == TARRAY && !isfixedarray(t)) nilonly = "slice"; + else if(t->etype == TARRAY && isfixedarray(t) && algtype1(t, nil) == ANOEQ) + yyerror("cannot switch on %lN", n->ntest); + else if(t->etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ) + yyerror("cannot switch on %lN (struct containing %T cannot be compared)", n->ntest, badtype); else if(t->etype == TFUNC) nilonly = "func"; else if(t->etype == TMAP) @@ -889,7 +904,7 @@ typecheckswitch(Node *n) yyerror("%lN is not a type", ll->n); // reset to original type ll->n = n->ntest->right; - } else if(ll->n->type->etype != TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) { + } else if(ll->n->type->etype != TINTER && t->etype == TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) { if(have && !missing->broke && !have->broke) yyerror("impossible type switch case: %lN cannot have dynamic type %T" " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT", diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index cc4faf5a7..fbab85d03 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -31,6 +31,8 @@ static void checkassign(Node*); static void checkassignlist(NodeList*); static void stringtoarraylit(Node**); static Node* resolve(Node*); +static void checkdefergo(Node*); +static int checkmake(Type*, char*, Node*); static NodeList* typecheckdefstack; @@ -105,6 +107,27 @@ typekind(Type *t) return buf; } +/* + * sprint_depchain prints a dependency chain + * of nodes into fmt. + * It is used by typecheck in the case of OLITERAL nodes + * to print constant definition loops. + */ +static void +sprint_depchain(Fmt *fmt, NodeList *stack, Node *cur, Node *first) +{ + NodeList *l; + + for(l = stack; l; l=l->next) { + if(l->n->op == cur->op) { + if(l->n != first) + sprint_depchain(fmt, l->next, l->n, first); + fmtprint(fmt, "\n\t%L: %N uses %N", l->n->lineno, l->n, cur); + return; + } + } +} + /* * type check node *np. * replaces *np with a new pointer in some cases. @@ -155,6 +178,24 @@ typecheck(Node **np, int top) } if(n->typecheck == 2) { + // Typechecking loop. Trying printing a meaningful message, + // otherwise a stack trace of typechecking. + switch(n->op) { + case ONAME: + // We can already diagnose variables used as types. + if((top & (Erv|Etype)) == Etype) + yyerror("%N is not a type", n); + break; + case OLITERAL: + if((top & (Erv|Etype)) == Etype) { + yyerror("%N is not a type", n); + break; + } + fmtstrinit(&fmt); + sprint_depchain(&fmt, tcstack, n, n); + yyerrorl(n->lineno, "constant definition loop%s", fmtstrflush(&fmt)); + break; + } if(nsavederrors+nerrors == 0) { fmtstrinit(&fmt); for(l=tcstack; l; l=l->next) @@ -165,7 +206,7 @@ typecheck(Node **np, int top) return n; } n->typecheck = 2; - + if(tcfree != nil) { l = tcfree; tcfree = l->next; @@ -206,6 +247,12 @@ callrecv(Node *n) case OCALLINTER: case OCALLFUNC: case ORECV: + case OCAP: + case OLEN: + case OCOPY: + case ONEW: + case OAPPEND: + case ODELETE: return 1; } @@ -249,7 +296,6 @@ typecheck1(Node **np, int top) } typecheckdef(n); - n->realtype = n->type; if(n->op == ONONAME) goto error; } @@ -331,8 +377,11 @@ reswitch: t->bound = -1; // slice } else if(l->op == ODDD) { t->bound = -100; // to be filled in - if(!(top&Ecomplit)) + if(!(top&Ecomplit) && !n->diag) { + t->broke = 1; + n->diag = 1; yyerror("use of [...] array outside of array literal"); + } } else { l = typecheck(&n->left, Erv); switch(consttype(l)) { @@ -351,8 +400,10 @@ reswitch: if(t->bound < 0) { yyerror("array bound must be non-negative"); goto error; - } else - overflow(v, types[TINT]); + } else if(doesoverflow(v, types[TINT])) { + yyerror("array bound is too large"); + goto error; + } } typecheck(&r, Etype); if(r->type == T) @@ -396,7 +447,7 @@ reswitch: ok |= Etype; n->op = OTYPE; n->type = tostruct(n->list); - if(n->type == T) + if(n->type == T || n->type->broke) goto error; n->list = nil; break; @@ -436,8 +487,11 @@ reswitch: goto ret; } if(!isptr[t->etype]) { - yyerror("invalid indirect of %lN", n->left); - goto error; + if(top & (Erv | Etop)) { + yyerror("invalid indirect of %lN", n->left); + goto error; + } + goto ret; } ok |= Erv; n->type = t->type; @@ -567,7 +621,10 @@ reswitch: n->left = l; n->right = r; } - } + // non-comparison operators on ideal bools should make them lose their ideal-ness + } else if(t == idealbool) + t = types[TBOOL]; + if(et == TSTRING) { if(iscmp[n->op]) { n->etype = n->op; @@ -587,6 +644,13 @@ reswitch: n->op = OCMPIFACE; } } + + if((op == ODIV || op == OMOD) && isconst(r, CTINT)) + if(mpcmpfixc(r->val.u.xval, 0) == 0) { + yyerror("division by zero"); + goto error; + } + n->type = t; goto ret; @@ -697,6 +761,10 @@ reswitch: n->op = ODOTPTR; checkwidth(t); } + if(isblank(n->right)) { + yyerror("cannot refer to blank field or method"); + goto error; + } if(!lookdot(n, t, 0)) { if(lookdot(n, t, 1)) yyerror("%N undefined (cannot refer to unexported field or method %S)", n, n->right->sym); @@ -735,14 +803,20 @@ reswitch: } if(n->type != T && n->type->etype != TINTER) if(!implements(n->type, t, &missing, &have, &ptr)) { - if(have) - yyerror("impossible type assertion: %lN cannot have dynamic type %T" - " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT", - l, n->type, missing->sym, have->sym, have->type, - missing->sym, missing->type); + if(have && have->sym == missing->sym) + yyerror("impossible type assertion:\n\t%T does not implement %T (wrong type for %S method)\n" + "\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym, + have->sym, have->type, missing->sym, missing->type); + else if(ptr) + yyerror("impossible type assertion:\n\t%T does not implement %T (%S method requires pointer receiver)", + n->type, t, missing->sym); + else if(have) + yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)\n" + "\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym, + have->sym, have->type, missing->sym, missing->type); else - yyerror("impossible type assertion: %lN cannot have dynamic type %T" - " (missing %S method)", l, n->type, missing->sym); + yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)", + n->type, t, missing->sym); goto error; } goto ret; @@ -762,11 +836,35 @@ reswitch: yyerror("invalid operation: %N (index of type %T)", n, t); goto error; + + case TSTRING: case TARRAY: defaultlit(&n->right, T); - if(n->right->type != T && !isint[n->right->type->etype]) - yyerror("non-integer array index %N", n->right); - n->type = t->type; + if(t->etype == TSTRING) + n->type = types[TUINT8]; + else + n->type = t->type; + why = "string"; + if(t->etype == TARRAY) { + if(isfixedarray(t)) + why = "array"; + else + why = "slice"; + } + if(n->right->type != T && !isint[n->right->type->etype]) { + yyerror("non-integer %s index %N", why, n->right); + break; + } + if(n->right->op == OLITERAL) { + if(mpgetfix(n->right->val.u.xval) < 0) + yyerror("invalid %s index %N (index must be non-negative)", why, n->right); + else if(isfixedarray(t) && t->bound > 0 && mpgetfix(n->right->val.u.xval) >= t->bound) + yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound); + else if(isconst(n->left, CTSTR) && mpgetfix(n->right->val.u.xval) >= n->left->val.u.sval->len) + yyerror("invalid string index %N (out of bounds for %d-byte string)", n->right, n->left->val.u.sval->len); + else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) + yyerror("invalid %s index %N (index too large)", why, n->right); + } break; case TMAP: @@ -777,13 +875,6 @@ reswitch: n->type = t->type; n->op = OINDEXMAP; break; - - case TSTRING: - defaultlit(&n->right, types[TUINT]); - if(n->right->type != T && !isint[n->right->type->etype]) - yyerror("non-integer string index %N", n->right); - n->type = types[TUINT8]; - break; } goto ret; @@ -843,7 +934,8 @@ reswitch: defaultlit(&n->left, T); defaultlit(&n->right->left, T); defaultlit(&n->right->right, T); - if(isfixedarray(n->left->type)) { + l = n->left; + if(isfixedarray(l->type)) { if(!islvalue(n->left)) { yyerror("invalid operation %N (slice of unaddressable value)", n); goto error; @@ -851,6 +943,26 @@ reswitch: n->left = nod(OADDR, n->left, N); n->left->implicit = 1; typecheck(&n->left, Erv); + l = n->left; + } + if((t = l->type) == T) + goto error; + tp = nil; + if(istype(t, TSTRING)) { + n->type = t; + n->op = OSLICESTR; + } else if(isptr[t->etype] && isfixedarray(t->type)) { + tp = t->type; + n->type = typ(TARRAY); + n->type->type = tp->type; + n->type->bound = -1; + dowidth(n->type); + n->op = OSLICEARR; + } else if(isslice(t)) { + n->type = t; + } else { + yyerror("cannot slice %N (type %T)", l, t); + goto error; } if(n->right->left != N) { if((t = n->right->left->type) == T) @@ -859,6 +971,18 @@ reswitch: yyerror("invalid slice index %N (type %T)", n->right->left, t); goto error; } + if(n->right->left->op == OLITERAL) { + if(mpgetfix(n->right->left->val.u.xval) < 0) { + yyerror("invalid slice index %N (index must be non-negative)", n->right->left); + goto error; + } else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->left->val.u.xval) > tp->bound) { + yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->left, tp->bound); + goto error; + } else if(mpcmpfixfix(n->right->left->val.u.xval, maxintval[TINT]) > 0) { + yyerror("invalid slice index %N (index too large)", n->right->left); + goto error; + } + } } if(n->right->right != N) { if((t = n->right->right->type) == T) @@ -867,29 +991,28 @@ reswitch: yyerror("invalid slice index %N (type %T)", n->right->right, t); goto error; } + if(n->right->right->op == OLITERAL) { + if(mpgetfix(n->right->right->val.u.xval) < 0) { + yyerror("invalid slice index %N (index must be non-negative)", n->right->right); + goto error; + } else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->right->val.u.xval) > tp->bound) { + yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->right, tp->bound); + goto error; + } else if(mpcmpfixfix(n->right->right->val.u.xval, maxintval[TINT]) > 0) { + yyerror("invalid slice index %N (index too large)", n->right->right); + goto error; + } + } } - l = n->left; - if((t = l->type) == T) + if(n->right->left != N + && n->right->right != N + && n->right->left->op == OLITERAL + && n->right->right->op == OLITERAL + && mpcmpfixfix(n->right->left->val.u.xval, n->right->right->val.u.xval) > 0) { + yyerror("inverted slice index %N > %N", n->right->left, n->right->right); goto error; - if(istype(t, TSTRING)) { - n->type = t; - n->op = OSLICESTR; - goto ret; - } - if(isptr[t->etype] && isfixedarray(t->type)) { - n->type = typ(TARRAY); - n->type->type = t->type->type; - n->type->bound = -1; - dowidth(n->type); - n->op = OSLICEARR; - goto ret; - } - if(isslice(t)) { - n->type = t; - goto ret; } - yyerror("cannot slice %N (type %T)", l, t); - goto error; + goto ret; /* * call and call like @@ -916,8 +1039,11 @@ reswitch: defaultlit(&n->left, T); l = n->left; if(l->op == OTYPE) { - if(n->isddd || l->type->bound == -100) - yyerror("invalid use of ... in type conversion", l); + if(n->isddd || l->type->bound == -100) { + if(!l->type->broke) + yyerror("invalid use of ... in type conversion", l); + n->diag = 1; + } // pick off before type-checking arguments ok |= Erv; // turn CALL(type, arg) into CONV(arg) w/ type @@ -929,7 +1055,7 @@ reswitch: goto doconv; } - if(count(n->list) == 1) + if(count(n->list) == 1 && !n->isddd) typecheck(&n->list->n, Erv | Efnstruct); else typechecklist(n->list, Erv); @@ -1011,10 +1137,12 @@ reswitch: if(!iscomplex[t->etype]) goto badcall1; if(isconst(l, CTCPLX)){ + r = n; if(n->op == OREAL) n = nodfltconst(&l->val.u.cval->real); else n = nodfltconst(&l->val.u.cval->imag); + n->orig = r; } n->type = types[cplxsubtype(t->etype)]; goto ret; @@ -1074,7 +1202,9 @@ reswitch: } if(l->op == OLITERAL && r->op == OLITERAL) { // make it a complex literal - n = nodcplxlit(l->val, r->val); + r = nodcplxlit(l->val, r->val); + r->orig = n; + n = r; } n->type = t; goto ret; @@ -1214,17 +1344,25 @@ reswitch: case OCONV: doconv: ok |= Erv; + saveorignode(n); typecheck(&n->left, Erv | (top & (Eindir | Eiota))); convlit1(&n->left, n->type, 1); if((t = n->left->type) == T || n->type == T) goto error; if((n->op = convertop(t, n->type, &why)) == 0) { - yyerror("cannot convert %lN to type %T%s", n->left, n->type, why); + if(!n->diag && !n->type->broke) { + yyerror("cannot convert %lN to type %T%s", n->left, n->type, why); + n->diag = 1; + } n->op = OCONV; } switch(n->op) { case OCONVNOP: if(n->left->op == OLITERAL) { + r = nod(OXXX, N, N); + n->op = OCONV; + n->orig = r; + *r = *n; n->op = OLITERAL; n->val = n->left->val; } @@ -1267,22 +1405,20 @@ reswitch: l = args->n; args = args->next; typecheck(&l, Erv); - defaultlit(&l, types[TINT]); r = N; if(args != nil) { r = args->n; args = args->next; typecheck(&r, Erv); - defaultlit(&r, types[TINT]); } if(l->type == T || (r && r->type == T)) goto error; - if(!isint[l->type->etype]) { - yyerror("non-integer len argument to make(%T)", t); + et = checkmake(t, "len", l) < 0; + et |= r && checkmake(t, "cap", r) < 0; + if(et) goto error; - } - if(r && !isint[r->type->etype]) { - yyerror("non-integer cap argument to make(%T)", t); + if(isconst(l, CTINT) && r && isconst(r, CTINT) && mpcmpfixfix(l->val.u.xval, r->val.u.xval) > 0) { + yyerror("len larger than cap in make(%T)", t); goto error; } n->left = l; @@ -1298,10 +1434,8 @@ reswitch: defaultlit(&l, types[TINT]); if(l->type == T) goto error; - if(!isint[l->type->etype]) { - yyerror("non-integer size argument to make(%T)", t); + if(checkmake(t, "size", l) < 0) goto error; - } n->left = l; } else n->left = nodintconst(0); @@ -1317,10 +1451,8 @@ reswitch: defaultlit(&l, types[TINT]); if(l->type == T) goto error; - if(!isint[l->type->etype]) { - yyerror("non-integer buffer argument to make(%T)", t); + if(checkmake(t, "buffer", l) < 0) goto error; - } n->left = l; } else n->left = nodintconst(0); @@ -1402,6 +1534,21 @@ reswitch: fatal("OITAB of %T", t); n->type = ptrto(types[TUINTPTR]); goto ret; + + case OCLOSUREVAR: + ok |= Erv; + goto ret; + + case OCFUNC: + ok |= Erv; + typecheck(&n->left, Erv); + n->type = types[TUINTPTR]; + goto ret; + + case OCONVNOP: + ok |= Erv; + typecheck(&n->left, Erv); + goto ret; /* * statements @@ -1428,12 +1575,15 @@ reswitch: case ODEFER: ok |= Etop; - typecheck(&n->left, Etop); + typecheck(&n->left, Etop|Erv); + if(!n->left->diag) + checkdefergo(n); goto ret; case OPROC: ok |= Etop; - typecheck(&n->left, Etop|Eproc); + typecheck(&n->left, Etop|Eproc|Erv); + checkdefergo(n); goto ret; case OFOR: @@ -1530,9 +1680,7 @@ ret: } } - // TODO(rsc): should not need to check importpkg, - // but reflect mentions unsafe.Pointer. - if(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR) + if(safemode && !incannedimport && !importpkg && !compiling_wrappers && t && t->etype == TUNSAFEPTR) yyerror("cannot use unsafe.Pointer"); evconst(n); @@ -1555,7 +1703,7 @@ ret: } if((top & Etop) && !(top & (Ecall|Erv|Etype)) && !(ok & Etop)) { if(n->diag == 0) { - yyerror("%N not used", n); + yyerror("%N evaluated but not used", n); n->diag = 1; } goto error; @@ -1578,6 +1726,56 @@ out: *np = n; } +static void +checkdefergo(Node *n) +{ + char *what; + + what = "defer"; + if(n->op == OPROC) + what = "go"; + + switch(n->left->op) { + case OCALLINTER: + case OCALLMETH: + case OCALLFUNC: + case OCLOSE: + case OCOPY: + case ODELETE: + case OPANIC: + case OPRINT: + case OPRINTN: + case ORECOVER: + // ok + break; + case OAPPEND: + case OCAP: + case OCOMPLEX: + case OIMAG: + case OLEN: + case OMAKE: + case OMAKESLICE: + case OMAKECHAN: + case OMAKEMAP: + case ONEW: + case OREAL: + case OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof + if(n->left->orig != N && n->left->orig->op == OCONV) + goto conv; + yyerror("%s discards result of %N", what, n->left); + break; + default: + conv: + if(!n->diag) { + // The syntax made sure it was a call, so this must be + // a conversion. + n->diag = 1; + yyerror("%s requires function call, not conversion", what); + } + break; + } +} + static void implicitstar(Node **nn) { @@ -1682,7 +1880,7 @@ lookdot1(Node *errnode, Sym *s, Type *t, Type *f, int dostrcmp) static int looktypedot(Node *n, Type *t, int dostrcmp) { - Type *f1, *f2, *tt; + Type *f1, *f2; Sym *s; s = n->right->sym; @@ -1692,8 +1890,6 @@ looktypedot(Node *n, Type *t, int dostrcmp) if(f1 == T) return 0; - if(f1->width == BADWIDTH) - fatal("lookdot badwidth %T %p", f1, f1); n->right = methodname(n->right, t); n->xoffset = f1->width; n->type = f1->type; @@ -1701,11 +1897,9 @@ looktypedot(Node *n, Type *t, int dostrcmp) return 1; } - tt = t; - if(t->sym == S && isptr[t->etype]) - tt = t->type; - - f2 = methtype(tt, 0); + // Find the base type: methtype will fail if t + // is not of the form T or *T. + f2 = methtype(t, 0); if(f2 == T) return 0; @@ -1769,6 +1963,7 @@ lookdot(Node *n, Type *t, int dostrcmp) fatal("lookdot badwidth %T %p", f1, f1); n->xoffset = f1->width; n->type = f1->type; + n->paramfld = f1; if(t->etype == TINTER) { if(isptr[n->left->type->etype]) { n->left = nod(OIND, n->left, N); // implicitstar @@ -2129,10 +2324,10 @@ static void typecheckcomplit(Node **np) { int bad, i, len, nerr; - Node *l, *n, *r, **hash; + Node *l, *n, *norig, *r, **hash; NodeList *ll; Type *t, *f; - Sym *s; + Sym *s, *s1; int32 lno; ulong nhash; Node *autohash[101]; @@ -2146,14 +2341,18 @@ typecheckcomplit(Node **np) yyerror("missing type in composite literal"); goto error; } - + + // Save original node (including n->right) + norig = nod(n->op, N, N); + *norig = *n; + setlineno(n->right); l = typecheck(&n->right /* sic */, Etype|Ecomplit); if((t = l->type) == T) goto error; nerr = nerrors; n->type = t; - + if(isptr[t->etype]) { // For better or worse, we don't allow pointers as the composite literal type, // except when using the &T syntax, which sets implicit on the OIND. @@ -2161,13 +2360,12 @@ typecheckcomplit(Node **np) yyerror("invalid pointer type %T for composite literal (use &%T instead)", t, t->type); goto error; } - // Also, the underlying type must be a struct, map, slice, or array. if(!iscomptype(t)) { yyerror("invalid pointer type %T for composite literal", t); goto error; } - t = t->type; + t = t->type; } switch(t->etype) { @@ -2298,9 +2496,11 @@ typecheckcomplit(Node **np) // Sym might have resolved to name in other top-level // package, because of import dot. Redirect to correct sym // before we do the lookup. - if(s->pkg != localpkg && exportname(s->name)) - s = lookup(s->name); - + if(s->pkg != localpkg && exportname(s->name)) { + s1 = lookup(s->name); + if(s1->origpkg == s->pkg) + s = s1; + } f = lookdot1(nil, s, t, t->type, 0); if(f == nil) { yyerror("unknown %T field '%S' in struct literal", t, s); @@ -2323,6 +2523,7 @@ typecheckcomplit(Node **np) if(nerr != nerrors) goto error; + n->orig = norig; if(isptr[n->type->etype]) { n = nod(OPTRLIT, n, N); n->typecheck = 1; @@ -2331,6 +2532,7 @@ typecheckcomplit(Node **np) n->left->typecheck = 1; } + n->orig = norig; *np = n; lineno = lno; return; @@ -2527,7 +2729,6 @@ typecheckas2(Node *n) goto common; case ORECV: n->op = OAS2RECV; - n->right = n->rlist->n; goto common; case ODOTTYPE: n->op = OAS2DOTTYPE; @@ -2572,7 +2773,7 @@ typecheckfunc(Node *n) t->nname = n->nname; rcvr = getthisx(t)->type; if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) - addmethod(n->shortname->sym, t, 1); + addmethod(n->shortname->sym, t, 1, n->nname->nointerface); } static void @@ -2715,6 +2916,7 @@ typecheckdeftype(Node *n) typecheck(&n->ntype, Etype); if((t = n->ntype->type) == T) { n->diag = 1; + n->type = T; goto ret; } if(n->type == T) { @@ -2828,14 +3030,14 @@ typecheckdef(Node *n) yyerror("xxx"); } typecheck(&e, Erv | Eiota); - if(e->type != T && e->op != OLITERAL) { - yyerror("const initializer must be constant"); - goto ret; - } if(isconst(e, CTNIL)) { yyerror("const initializer cannot be nil"); goto ret; } + if(e->type != T && e->op != OLITERAL || !isgoconst(e)) { + yyerror("const initializer %N is not a constant", e); + goto ret; + } t = n->type; if(t != T) { if(!okforconst[t->etype]) { @@ -2912,3 +3114,33 @@ ret: n->walkdef = 1; return n; } + +static int +checkmake(Type *t, char *arg, Node *n) +{ + if(n->op == OLITERAL) { + n->val = toint(n->val); + if(mpcmpfixc(n->val.u.xval, 0) < 0) { + yyerror("negative %s argument in make(%T)", arg, t); + return -1; + } + if(mpcmpfixfix(n->val.u.xval, maxintval[TINT]) > 0) { + yyerror("%s argument too large in make(%T)", arg, t); + return -1; + } + + // Delay defaultlit until after we've checked range, to avoid + // a redundant "constant NNN overflows int" error. + defaultlit(&n, types[TINT]); + return 0; + } + + // Defaultlit still necessary for non-constant: n might be 1<type->etype]) { + yyerror("non-integer %s argument in make(%T) - %T", arg, t, n->type); + return -1; + } + return 0; +} diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index a4edc9062..de2105ed3 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -21,7 +21,13 @@ static NodeList* reorder3(NodeList*); static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); +static Node* sliceany(Node*, NodeList**); static void walkcompare(Node**, NodeList**); +static void walkrotate(Node**); +static void walkmul(Node**, NodeList**); +static void walkdiv(Node**, NodeList**); +static int bounded(Node*, int64); +static Mpint mpzero; // can this code branch reach the end // without an unconditional RETURN @@ -178,8 +184,8 @@ walkstmt(Node **np) dump("nottop", n); break; - case OASOP: case OAS: + case OASOP: case OAS2: case OAS2DOTTYPE: case OAS2RECV: @@ -368,9 +374,10 @@ walkexpr(Node **np, NodeList **init) NodeList *ll, *lr, *lpost; Type *t; int et; - int64 v, v1, v2, len; + int64 v; int32 lno; - Node *n, *fn; + Node *n, *fn, *n1, *n2; + Sym *sym; char buf[100], *p; n = *np; @@ -409,7 +416,7 @@ walkexpr(Node **np, NodeList **init) switch(n->op) { default: dump("walk", n); - fatal("walkexpr: switch 1 unknown op %N", n); + fatal("walkexpr: switch 1 unknown op %+hN", n); break; case OTYPE: @@ -424,14 +431,23 @@ walkexpr(Node **np, NodeList **init) case OCOM: case OREAL: case OIMAG: - case ODOT: - case ODOTPTR: case ODOTMETH: case ODOTINTER: case OIND: walkexpr(&n->left, init); goto ret; + case ODOT: + case ODOTPTR: + usefield(n); + walkexpr(&n->left, init); + goto ret; + + case OEFACE: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + goto ret; + case OITAB: walkexpr(&n->left, init); goto ret; @@ -454,19 +470,34 @@ walkexpr(Node **np, NodeList **init) case OLSH: case ORSH: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + shiftwalked: + t = n->left->type; + n->bounded = bounded(n->right, 8*t->width); + if(debug['m'] && n->etype && !isconst(n->right, CTINT)) + warn("shift bounds check elided"); + goto ret; + case OAND: - case OOR: - case OXOR: case OSUB: - case OMUL: + case OHMUL: case OLT: case OLE: case OGE: case OGT: case OADD: case OCOMPLEX: + case OLROT: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + goto ret; + + case OOR: + case OXOR: walkexpr(&n->left, init); walkexpr(&n->right, init); + walkrotate(&n); goto ret; case OEQ: @@ -505,6 +536,11 @@ walkexpr(Node **np, NodeList **init) n->addable = 1; goto ret; + case OCLOSUREVAR: + case OCFUNC: + n->addable = 1; + goto ret; + case ONAME: if(!(n->class & PHEAP) && n->class != PPARAMREF) n->addable = 1; @@ -525,10 +561,12 @@ walkexpr(Node **np, NodeList **init) if(n->list && n->list->n->op == OAS) goto ret; + /* if(n->left->op == OCLOSURE) { walkcallclosure(n, init); t = n->left->type; } + */ walkexpr(&n->left, init); walkexprlist(n->list, init); @@ -656,6 +694,31 @@ walkexpr(Node **np, NodeList **init) n->ninit = nil; r = n->rlist->n; walkexprlistsafe(n->list, init); + if(isblank(n->list->n) && !isinter(r->type)) { + strcpy(buf, "assert"); + p = buf+strlen(buf); + if(isnilinter(r->left->type)) + *p++ = 'E'; + else + *p++ = 'I'; + *p++ = '2'; + *p++ = 'T'; + *p++ = 'O'; + *p++ = 'K'; + *p = '\0'; + + fn = syslook(buf, 1); + ll = list1(typename(r->type)); + ll = list(ll, r->left); + argtype(fn, r->left->type); + n1 = nod(OCALL, fn, N); + n1->list = ll; + n = nod(OAS, n->list->next->n, n1); + typecheck(&n, Etop); + walkexpr(&n, init); + goto ret; + } + r->op = ODOTTYPE2; walkexpr(&r, init); ll = ascompatet(n->op, n->list, &r->type, 0, init); @@ -694,10 +757,22 @@ walkexpr(Node **np, NodeList **init) goto ret; case OCONVIFACE: + walkexpr(&n->left, init); + + // Optimize convT2E as a two-word copy when T is uintptr-shaped. + if(!isinter(n->left->type) && isnilinter(n->type) && + (n->left->type->width == widthptr) && + isint[simsimtype(n->left->type)]) { + l = nod(OEFACE, typename(n->left->type), n->left); + l->type = n->type; + l->typecheck = n->typecheck; + n = l; + goto ret; + } + // Build name of function: convI2E etc. // Not all names are possible // (e.g., we'll never generate convE2E or convE2I). - walkexpr(&n->left, init); strcpy(buf, "conv"); p = buf+strlen(buf); if(isnilinter(n->left->type)) @@ -719,6 +794,60 @@ walkexpr(Node **np, NodeList **init) ll = list(ll, typename(n->left->type)); if(!isnilinter(n->type)) ll = list(ll, typename(n->type)); + if(!isinter(n->left->type) && !isnilinter(n->type)){ + sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg); + if(sym->def == N) { + l = nod(ONAME, N, N); + l->sym = sym; + l->type = ptrto(types[TUINT8]); + l->addable = 1; + l->class = PEXTERN; + l->xoffset = 0; + sym->def = l; + ggloblsym(sym, widthptr, 1, 0); + } + l = nod(OADDR, sym->def, N); + l->addable = 1; + ll = list(ll, l); + + if(n->left->type->width == widthptr && + isint[simsimtype(n->left->type)]) { + /* For pointer types, we can make a special form of optimization + * + * These statements are put onto the expression init list: + * Itab *tab = atomicloadtype(&cache); + * if(tab == nil) + * tab = typ2Itab(type, itype, &cache); + * + * The CONVIFACE expression is replaced with this: + * OEFACE{tab, ptr}; + */ + l = temp(ptrto(types[TUINT8])); + + n1 = nod(OAS, l, sym->def); + typecheck(&n1, Etop); + *init = list(*init, n1); + + fn = syslook("typ2Itab", 1); + n1 = nod(OCALL, fn, N); + n1->list = ll; + typecheck(&n1, Erv); + walkexpr(&n1, init); + + n2 = nod(OIF, N, N); + n2->ntest = nod(OEQ, l, nodnil()); + n2->nbody = list1(nod(OAS, l, n1)); + n2->likely = -1; + typecheck(&n2, Etop); + *init = list(*init, n2); + + l = nod(OEFACE, l, n->left); + l->typecheck = n->typecheck; + l->type = n->type; + n = l; + goto ret; + } + } ll = list(ll, n->left); argtype(fn, n->left->type); argtype(fn, n->type); @@ -772,7 +901,7 @@ walkexpr(Node **np, NodeList **init) * on 386, rewrite float ops into l = l op r. * everywhere, rewrite map ops into l = l op r. * everywhere, rewrite string += into l = l op r. - * everywhere, rewrite complex /= into l = l op r. + * everywhere, rewrite integer/complex /= into l = l op r. * TODO(rsc): Maybe this rewrite should be done always? */ et = n->left->type->etype; @@ -780,7 +909,8 @@ walkexpr(Node **np, NodeList **init) (thechar == '8' && isfloat[et]) || l->op == OINDEXMAP || et == TSTRING || - (iscomplex[et] && n->etype == ODIV)) { + (!isfloat[et] && n->etype == ODIV) || + n->etype == OMOD) { l = safeexpr(n->left, init); a = l; if(a->op == OINDEXMAP) { @@ -794,15 +924,24 @@ walkexpr(Node **np, NodeList **init) typecheck(&r, Etop); walkexpr(&r, init); n = r; + goto ret; } + if(n->etype == OLSH || n->etype == ORSH) + goto shiftwalked; goto ret; case OANDNOT: walkexpr(&n->left, init); - walkexpr(&n->right, init); n->op = OAND; n->right = nod(OCOM, n->right, N); typecheck(&n->right, Erv); + walkexpr(&n->right, init); + goto ret; + + case OMUL: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + walkmul(&n, init); goto ret; case ODIV: @@ -821,63 +960,80 @@ walkexpr(Node **np, NodeList **init) n = conv(n, t); goto ret; } + // Nothing to do for float divisions. + if(isfloat[et]) + goto ret; + + // Try rewriting as shifts or magic multiplies. + walkdiv(&n, init); + /* - * rewrite div and mod into function calls + * rewrite 64-bit div and mod into function calls * on 32-bit architectures. */ - if(widthptr > 4 || (et != TUINT64 && et != TINT64)) - goto ret; - if(et == TINT64) - strcpy(namebuf, "int64"); - else - strcpy(namebuf, "uint64"); - if(n->op == ODIV) - strcat(namebuf, "div"); - else - strcat(namebuf, "mod"); - n = mkcall(namebuf, n->type, init, - conv(n->left, types[et]), conv(n->right, types[et])); + switch(n->op) { + case OMOD: + case ODIV: + if(widthptr > 4 || (et != TUINT64 && et != TINT64)) + goto ret; + if(et == TINT64) + strcpy(namebuf, "int64"); + else + strcpy(namebuf, "uint64"); + if(n->op == ODIV) + strcat(namebuf, "div"); + else + strcat(namebuf, "mod"); + n = mkcall(namebuf, n->type, init, + conv(n->left, types[et]), conv(n->right, types[et])); + break; + default: + break; + } goto ret; case OINDEX: walkexpr(&n->left, init); + // save the original node for bounds checking elision. + // If it was a ODIV/OMOD walk might rewrite it. + r = n->right; walkexpr(&n->right, init); // if range of type cannot exceed static array bound, - // disable bounds check - if(isfixedarray(n->left->type)) - if(!issigned[n->right->type->etype]) - if(n->right->type->width < 4) - if((1<<(8*n->right->type->width)) <= n->left->type->bound) - n->etype = 1; - - if(isconst(n->left, CTSTR)) - if(!issigned[n->right->type->etype]) - if(n->right->type->width < 4) - if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len) - n->etype = 1; - - // check for static out of bounds - if(isconst(n->right, CTINT) && !n->etype) { - v = mpgetfix(n->right->val.u.xval); - len = 1LL<<60; - t = n->left->type; - if(isconst(n->left, CTSTR)) - len = n->left->val.u.sval->len; - if(t != T && isptr[t->etype]) - t = t->type; - if(isfixedarray(t)) - len = t->bound; - if(v < 0 || v >= (1LL<<31) || v >= len) + // disable bounds check. + if(n->bounded) + goto ret; + t = n->left->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) { + n->bounded = bounded(r, t->bound); + if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) + warn("index bounds check elided"); + if(smallintconst(n->right) && !n->bounded) yyerror("index out of bounds"); - else if(isconst(n->left, CTSTR)) { - // replace "abc"[2] with 'b'. - // delayed until now because "abc"[2] is not - // an ideal constant. - nodconst(n, n->type, n->left->val.u.sval->s[v]); - n->typecheck = 1; + } else if(isconst(n->left, CTSTR)) { + n->bounded = bounded(r, n->left->val.u.sval->len); + if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) + warn("index bounds check elided"); + if(smallintconst(n->right)) { + if(!n->bounded) + yyerror("index out of bounds"); + else { + // replace "abc"[2] with 'b'. + // delayed until now because "abc"[2] is not + // an ideal constant. + v = mpgetfix(n->right->val.u.xval); + nodconst(n, n->type, n->left->val.u.sval->s[v]); + n->typecheck = 1; + } } } + + if(isconst(n->right, CTINT)) + if(mpcmpfixfix(n->right->val.u.xval, &mpzero) < 0 || + mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) + yyerror("index out of bounds"); goto ret; case OINDEXMAP: @@ -895,95 +1051,29 @@ walkexpr(Node **np, NodeList **init) goto ret; case OSLICE: + if(n->right != N && n->right->left == N && n->right->right == N) { // noop + walkexpr(&n->left, init); + n = n->left; + goto ret; + } + // fallthrough case OSLICEARR: + case OSLICESTR: + if(n->right == N) // already processed + goto ret; + walkexpr(&n->left, init); - n->left = safeexpr(n->left, init); + // cgen_slice can't handle string literals as source + // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] + if((n->op == OSLICESTR && n->left->op == OLITERAL) || (n->left->op == OINDEX)) + n->left = copyexpr(n->left, n->left->type, init); + else + n->left = safeexpr(n->left, init); walkexpr(&n->right->left, init); n->right->left = safeexpr(n->right->left, init); walkexpr(&n->right->right, init); n->right->right = safeexpr(n->right->right, init); - - len = 1LL<<60; - t = n->left->type; - if(t != T && isptr[t->etype]) - t = t->type; - if(isfixedarray(t)) - len = t->bound; - - // check for static out of bounds - // NOTE: v > len not v >= len. - v1 = -1; - v2 = -1; - if(isconst(n->right->left, CTINT)) { - v1 = mpgetfix(n->right->left->val.u.xval); - if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) { - yyerror("slice index out of bounds"); - v1 = -1; - } - } - if(isconst(n->right->right, CTINT)) { - v2 = mpgetfix(n->right->right->val.u.xval); - if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) { - yyerror("slice index out of bounds"); - v2 = -1; - } - } - if(v1 >= 0 && v2 >= 0 && v1 > v2) - yyerror("inverted slice range"); - - if(n->op == OSLICEARR) - goto slicearray; - - // dynamic slice - // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) - // sliceslice1(old []any, lb uint64, width uint64) (ary []any) - t = n->type; - et = n->etype; - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TUINT64]); - if(n->right->right != N) { - fn = syslook("sliceslice", 1); - argtype(fn, t->type); // any-1 - argtype(fn, t->type); // any-2 - n = mkcall1(fn, t, init, - n->left, - l, - conv(n->right->right, types[TUINT64]), - nodintconst(t->type->width)); - } else { - fn = syslook("sliceslice1", 1); - argtype(fn, t->type); // any-1 - argtype(fn, t->type); // any-2 - n = mkcall1(fn, t, init, - n->left, - l, - nodintconst(t->type->width)); - } - n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call. - goto ret; - - slicearray: - // static slice - // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any) - t = n->type; - fn = syslook("slicearray", 1); - argtype(fn, n->left->type->type); // any-1 - argtype(fn, t->type); // any-2 - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TUINT64]); - if(n->right->right == N) - r = nodintconst(n->left->type->type->bound); - else - r = conv(n->right->right, types[TUINT64]); - n = mkcall1(fn, t, init, - n->left, nodintconst(n->left->type->type->bound), - l, - r, - nodintconst(t->type->width)); + n = sliceany(n, init); // chops n->right, sets n->list goto ret; case OADDR: @@ -1031,27 +1121,34 @@ walkexpr(Node **np, NodeList **init) goto ret; } - // prepare for rewrite below if(n->etype == OEQ || n->etype == ONE) { + // prepare for rewrite below n->left = cheapexpr(n->left, init); n->right = cheapexpr(n->right, init); - } - // sys_cmpstring(s1, s2) :: 0 - r = mkcall("cmpstring", types[TINT], init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TSTRING])); - r = nod(n->etype, r, nodintconst(0)); + r = mkcall("eqstring", types[TBOOL], init, + conv(n->left, types[TSTRING]), + conv(n->right, types[TSTRING])); - // quick check of len before full compare for == or != - if(n->etype == OEQ || n->etype == ONE) { - if(n->etype == OEQ) + // quick check of len before full compare for == or != + if(n->etype == OEQ) { + // len(left) == len(right) && eqstring(left, right) r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); - else + } else { + // len(left) != len(right) || !eqstring(left, right) + r = nod(ONOT, r, N); r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + } typecheck(&r, Erv); walkexpr(&r, nil); + } else { + // sys_cmpstring(s1, s2) :: 0 + r = mkcall("cmpstring", types[TINT], init, + conv(n->left, types[TSTRING]), + conv(n->right, types[TSTRING])); + r = nod(n->etype, r, nodintconst(0)); } + typecheck(&r, Erv); if(n->type->etype != TBOOL) fatal("cmp %T", n->type); r->type = n->type; @@ -1061,25 +1158,7 @@ walkexpr(Node **np, NodeList **init) case OADDSTR: n = addstr(n, init); goto ret; - - case OSLICESTR: - // sys_slicestring(s, lb, hb) - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TINT]); - if(n->right->right) { - n = mkcall("slicestring", n->type, init, - conv(n->left, types[TSTRING]), - l, - conv(n->right->right, types[TINT])); - } else { - n = mkcall("slicestring1", n->type, init, - conv(n->left, types[TSTRING]), - l); - } - goto ret; - + case OAPPEND: if(n->isddd) { if(istype(n->type->type, TUINT8) && istype(n->list->next->n->type, TSTRING)) @@ -1177,6 +1256,9 @@ walkexpr(Node **np, NodeList **init) fn = syslook("efaceeq", 1); else fn = syslook("ifaceeq", 1); + + n->right = cheapexpr(n->right, init); + n->left = cheapexpr(n->left, init); argtype(fn, n->right->type); argtype(fn, n->left->type); r = mkcall1(fn, n->type, init, n->left, n->right); @@ -1189,7 +1271,7 @@ walkexpr(Node **np, NodeList **init) else r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); typecheck(&r, Erv); - walkexpr(&r, nil); + walkexpr(&r, init); r->type = n->type; n = r; goto ret; @@ -1226,9 +1308,16 @@ ret: static Node* ascompatee1(int op, Node *l, Node *r, NodeList **init) { + Node *n; USED(op); + + // convas will turn map assigns into function calls, + // making it impossible for reorder3 to work. + n = nod(OAS, l, r); + if(l->op == OINDEXMAP) + return n; - return convas(nod(OAS, l, r), init); + return convas(n, init); } static NodeList* @@ -1249,12 +1338,16 @@ ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) lr->n = safeexpr(lr->n, init); nn = nil; - for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) + for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) { + // Do not generate 'x = x' during return. See issue 4014. + if(op == ORETURN && ll->n == lr->n) + continue; nn = list(nn, ascompatee1(op, ll->n, lr->n, init)); + } // cannot happen: caller checked that lists had same length if(ll || lr) - yyerror("error in shape across %+H %O %+H", nl, op, nr); + yyerror("error in shape across %+H %O %+H / %d %d [%s]", nl, op, nr, count(nl), count(nr), curfn->nname->sym->name); return nn; } @@ -1826,13 +1919,14 @@ static int aliased(Node*, NodeList*, NodeList*); static NodeList* reorder3(NodeList *all) { - NodeList *list, *early; + NodeList *list, *early, *mapinit; Node *l; // If a needed expression may be affected by an // earlier assignment, make an early copy of that // expression and use the copy instead. early = nil; + mapinit = nil; for(list=all; list; list=list->next) { l = list->n->left; @@ -1856,8 +1950,11 @@ reorder3(NodeList *all) case ONAME: break; case OINDEX: + case OINDEXMAP: reorder3save(&l->left, all, list, &early); reorder3save(&l->right, all, list, &early); + if(l->op == OINDEXMAP) + list->n = convas(list->n, &mapinit); break; case OIND: case ODOTPTR: @@ -1868,6 +1965,7 @@ reorder3(NodeList *all) reorder3save(&list->n->right, all, list, &early); } + early = concat(mapinit, early); return concat(early, all); } @@ -2119,6 +2217,8 @@ paramstoheap(Type **argin, int out) nn = nil; for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { v = t->nname; + if(v && v->sym && v->sym->name[0] == '~') + v = N; if(v == N && out && hasdefer) { // Defer might stop a panic and show the // return values as they exist at the time of panic. @@ -2395,12 +2495,12 @@ append(Node *n, NodeList **init) l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc] - nx->etype = 1; // disable bounds check + nx->bounded = 1; l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc] for (a = n->list->next; a != nil; a = a->next) { nx = nod(OINDEX, ns, nn); // s[n] ... - nx->etype = 1; // disable bounds check + nx->bounded = 1; l = list(l, nod(OAS, nx, a->n)); // s[n] = arg if (a->next != nil) l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1 @@ -2412,6 +2512,151 @@ append(Node *n, NodeList **init) return ns; } + +// Generate frontend part for OSLICE[ARR|STR] +// +static Node* +sliceany(Node* n, NodeList **init) +{ + int bounded; + Node *src, *lb, *hb, *bound, *chk, *chk1, *chk2; + int64 lbv, hbv, bv, w; + Type *bt; + +// print("before sliceany: %+N\n", n); + + src = n->left; + lb = n->right->left; + hb = n->right->right; + + bounded = n->etype; + + if(n->op == OSLICESTR) + bound = nod(OLEN, src, N); + else + bound = nod(OCAP, src, N); + + typecheck(&bound, Erv); + walkexpr(&bound, init); // if src is an array, bound will be a const now. + + // static checks if possible + bv = 1LL<<50; + if(isconst(bound, CTINT)) { + if(!smallintconst(bound)) + yyerror("array len too large"); + else + bv = mpgetfix(bound->val.u.xval); + } + + if(isconst(hb, CTINT)) { + hbv = mpgetfix(hb->val.u.xval); + if(hbv < 0 || hbv > bv) { + yyerror("slice index out of bounds"); + hbv = -1; + } + } + if(isconst(lb, CTINT)) { + lbv = mpgetfix(lb->val.u.xval); + if(lbv < 0 || lbv > bv) { + yyerror("slice index out of bounds"); + lbv = -1; + } + if(lbv == 0) + lb = N; + } + + // dynamic checks convert all bounds to unsigned to save us the bound < 0 comparison + // generate + // if hb > bound || lb > hb { panicslice() } + chk = N; + chk1 = N; + chk2 = N; + + bt = types[simtype[TUINT]]; + if(hb != N && hb->type->width > 4) + bt = types[TUINT64]; + if(lb != N && lb->type->width > 4) + bt = types[TUINT64]; + + bound = cheapexpr(conv(bound, bt), init); + + if(hb != N) { + hb = cheapexpr(conv(hb, bt), init); + if(!bounded) + chk1 = nod(OLT, bound, hb); + } else if(n->op == OSLICEARR) { + hb = bound; + } else { + hb = nod(OLEN, src, N); + typecheck(&hb, Erv); + walkexpr(&hb, init); + hb = cheapexpr(conv(hb, bt), init); + } + + if(lb != N) { + lb = cheapexpr(conv(lb, bt), init); + if(!bounded) + chk2 = nod(OLT, hb, lb); + } + + if(chk1 != N || chk2 != N) { + chk = nod(OIF, N, N); + chk->nbody = list1(mkcall("panicslice", T, init)); + if(chk1 != N) + chk->ntest = chk1; + if(chk2 != N) { + if(chk->ntest == N) + chk->ntest = chk2; + else + chk->ntest = nod(OOROR, chk->ntest, chk2); + } + typecheck(&chk, Etop); + walkstmt(&chk); + *init = concat(*init, chk->ninit); + chk->ninit = nil; + *init = list(*init, chk); + } + + // prepare new cap, len and offs for backend cgen_slice + // cap = bound [ - lo ] + n->right = N; + n->list = nil; + if(lb == N) + bound = conv(bound, types[simtype[TUINT]]); + else + bound = nod(OSUB, conv(bound, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); + typecheck(&bound, Erv); + walkexpr(&bound, init); + n->list = list(n->list, bound); + + // len = hi [ - lo] + if(lb == N) + hb = conv(hb, types[simtype[TUINT]]); + else + hb = nod(OSUB, conv(hb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); + typecheck(&hb, Erv); + walkexpr(&hb, init); + n->list = list(n->list, hb); + + // offs = [width *] lo, but omit if zero + if(lb != N) { + if(n->op == OSLICESTR) + w = 1; + else + w = n->type->type->width; + lb = conv(lb, types[TUINTPTR]); + if(w > 1) + lb = nod(OMUL, nodintconst(w), lb); + typecheck(&lb, Erv); + walkexpr(&lb, init); + n->list = list(n->list, lb); + } + +// print("after sliceany: %+N\n", n); + + return n; +} + static Node* eqfor(Type *t) { @@ -2531,6 +2776,8 @@ walkcompare(Node **np, NodeList **init) // Struct of four or fewer fields. // Inline comparisons. for(t1=t->type; t1; t1=t1->down) { + if(isblanksym(t1->sym)) + continue; li = nod(OXDOT, l, newname(t1->sym)); ri = nod(OXDOT, r, newname(t1->sym)); a = nod(n->op, li, ri); @@ -2596,3 +2843,591 @@ hard: *np = r; return; } + +static int +samecheap(Node *a, Node *b) +{ + if(a == N || b == N || a->op != b->op) + return 0; + + switch(a->op) { + case ONAME: + return a == b; + // TODO: Could do more here, but maybe this is enough. + // It's all cheapexpr does. + } + return 0; +} + +static void +walkrotate(Node **np) +{ + int w, sl, sr, s; + Node *l, *r; + Node *n; + + n = *np; + + // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value. + l = n->left; + r = n->right; + if((n->op != OOR && n->op != OXOR) || + (l->op != OLSH && l->op != ORSH) || + (r->op != OLSH && r->op != ORSH) || + n->type == T || issigned[n->type->etype] || + l->op == r->op) { + return; + } + + // Want same, side effect-free expression on lhs of both shifts. + if(!samecheap(l->left, r->left)) + return; + + // Constants adding to width? + w = l->type->width * 8; + if(smallintconst(l->right) && smallintconst(r->right)) { + if((sl=mpgetfix(l->right->val.u.xval)) >= 0 && (sr=mpgetfix(r->right->val.u.xval)) >= 0 && sl+sr == w) + goto yes; + return; + } + + // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31). + return; + +yes: + // Rewrite left shift half to left rotate. + if(l->op == OLSH) + n = l; + else + n = r; + n->op = OLROT; + + // Remove rotate 0 and rotate w. + s = mpgetfix(n->right->val.u.xval); + if(s == 0 || s == w) + n = n->left; + + *np = n; + return; +} + +/* + * walkmul rewrites integer multiplication by powers of two as shifts. + */ +static void +walkmul(Node **np, NodeList **init) +{ + Node *n, *nl, *nr; + int pow, neg, w; + + n = *np; + if(!isint[n->type->etype]) + return; + + if(n->right->op == OLITERAL) { + nl = n->left; + nr = n->right; + } else if(n->left->op == OLITERAL) { + nl = n->right; + nr = n->left; + } else + return; + + neg = 0; + + // x*0 is 0 (and side effects of x). + if(mpgetfix(nr->val.u.xval) == 0) { + cheapexpr(nl, init); + nodconst(n, n->type, 0); + goto ret; + } + + // nr is a constant. + pow = powtwo(nr); + if(pow < 0) + return; + if(pow >= 1000) { + // negative power of 2, like -16 + neg = 1; + pow -= 1000; + } + + w = nl->type->width*8; + if(pow+1 >= w)// too big, shouldn't happen + return; + + nl = cheapexpr(nl, init); + + if(pow == 0) { + // x*1 is x + n = nl; + goto ret; + } + + n = nod(OLSH, nl, nodintconst(pow)); + +ret: + if(neg) + n = nod(OMINUS, n, N); + + typecheck(&n, Erv); + walkexpr(&n, init); + *np = n; +} + +/* + * walkdiv rewrites division by a constant as less expensive + * operations. + */ +static void +walkdiv(Node **np, NodeList **init) +{ + Node *n, *nl, *nr, *nc; + Node *n1, *n2, *n3, *n4; + int pow; // if >= 0, nr is 1<right->op != OLITERAL) + return; + // nr is a constant. + nl = cheapexpr(n->left, init); + nr = n->right; + + // special cases of mod/div + // by a constant + w = nl->type->width*8; + s = 0; + pow = powtwo(nr); + if(pow >= 1000) { + // negative power of 2 + s = 1; + pow -= 1000; + } + + if(pow+1 >= w) { + // divisor too large. + return; + } + if(pow < 0) { + goto divbymul; + } + + switch(pow) { + case 0: + if(n->op == OMOD) { + // nl % 1 is zero. + nodconst(n, n->type, 0); + } else if(s) { + // divide by -1 + n->op = OMINUS; + n->right = N; + } else { + // divide by 1 + n = nl; + } + break; + default: + if(issigned[n->type->etype]) { + if(n->op == OMOD) { + // signed modulo 2^pow is like ANDing + // with the last pow bits, but if nl < 0, + // nl & (2^pow-1) is (nl+1)%2^pow - 1. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-1); + n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. + if(pow == 1) { + typecheck(&n1, Erv); + n1 = cheapexpr(n1, init); + // n = (nl+ε)&1 -ε where ε=1 iff nl<0. + n2 = nod(OSUB, nl, n1); + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, 1); + n3 = nod(OAND, n2, nc); + n = nod(OADD, n3, n1); + } else { + // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0. + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, (1LL<= 0, nl >> n == nl / nr + // if nl < 0, we want to add 2^n-1 first. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-1); + n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. + if(pow == 1) { + // nl+1 is nl-(-1) + n->left = nod(OSUB, nl, n1); + } else { + // Do a logical right right on -1 to keep pow bits. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-pow); + n2 = nod(ORSH, conv(n1, tounsigned(nl->type)), nc); + n->left = nod(OADD, nl, conv(n2, nl->type)); + } + // n = (nl + 2^pow-1) >> pow + n->op = ORSH; + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], pow); + n->right = nc; + n->typecheck = 0; + } + if(s) + n = nod(OMINUS, n, N); + break; + } + nc = nod(OXXX, N, N); + if(n->op == OMOD) { + // n = nl & (nr-1) + n->op = OAND; + nodconst(nc, nl->type, mpgetfix(nr->val.u.xval)-1); + } else { + // n = nl >> pow + n->op = ORSH; + nodconst(nc, types[simtype[TUINT]], pow); + } + n->typecheck = 0; + n->right = nc; + break; + } + goto ret; + +divbymul: + // try to do division by multiply by (2^w)/d + // see hacker's delight chapter 10 + // TODO: support 64-bit magic multiply here. + m.w = w; + if(issigned[nl->type->etype]) { + m.sd = mpgetfix(nr->val.u.xval); + smagic(&m); + } else { + m.ud = mpgetfix(nr->val.u.xval); + umagic(&m); + } + if(m.bad) + return; + + // We have a quick division method so use it + // for modulo too. + if(n->op == OMOD) + goto longmod; + + switch(simtype[nl->type->etype]) { + default: + return; + + case TUINT8: + case TUINT16: + case TUINT32: + // n1 = nl * magic >> w (HMUL) + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, m.um); + n1 = nod(OMUL, nl, nc); + typecheck(&n1, Erv); + n1->op = OHMUL; + if(m.ua) { + // Select a Go type with (at least) twice the width. + switch(simtype[nl->type->etype]) { + default: + return; + case TUINT8: + case TUINT16: + twide = types[TUINT32]; + break; + case TUINT32: + twide = types[TUINT64]; + break; + case TINT8: + case TINT16: + twide = types[TINT32]; + break; + case TINT32: + twide = types[TINT64]; + break; + } + + // add numerator (might overflow). + // n2 = (n1 + nl) + n2 = nod(OADD, conv(n1, twide), conv(nl, twide)); + + // shift by m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n = conv(nod(ORSH, n2, nc), nl->type); + } else { + // n = n1 >> m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n = nod(ORSH, n1, nc); + } + break; + + case TINT8: + case TINT16: + case TINT32: + // n1 = nl * magic >> w + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, m.sm); + n1 = nod(OMUL, nl, nc); + typecheck(&n1, Erv); + n1->op = OHMUL; + if(m.sm < 0) { + // add the numerator. + n1 = nod(OADD, n1, nl); + } + // shift by m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n2 = conv(nod(ORSH, n1, nc), nl->type); + // add 1 iff n1 is negative. + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], w-1); + n3 = nod(ORSH, nl, nc); // n4 = -1 iff n1 is negative. + n = nod(OSUB, n2, n3); + // apply sign. + if(m.sd < 0) + n = nod(OMINUS, n, N); + break; + } + goto ret; + +longmod: + // rewrite as A%B = A - (A/B*B). + n1 = nod(ODIV, nl, nr); + n2 = nod(OMUL, n1, nr); + n = nod(OSUB, nl, n2); + goto ret; + +ret: + typecheck(&n, Erv); + walkexpr(&n, init); + *np = n; +} + +// return 1 if integer n must be in range [0, max), 0 otherwise +static int +bounded(Node *n, int64 max) +{ + int64 v; + int32 bits; + int sign; + + if(n->type == T || !isint[n->type->etype]) + return 0; + + sign = issigned[n->type->etype]; + bits = 8*n->type->width; + + if(smallintconst(n)) { + v = mpgetfix(n->val.u.xval); + return 0 <= v && v < max; + } + + switch(n->op) { + case OAND: + v = -1; + if(smallintconst(n->left)) { + v = mpgetfix(n->left->val.u.xval); + } else if(smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + } + if(0 <= v && v < max) + return 1; + break; + + case OMOD: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + if(0 <= v && v <= max) + return 1; + } + break; + + case ODIV: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + while(bits > 0 && v >= 2) { + bits--; + v >>= 1; + } + } + break; + + case ORSH: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + if(v > bits) + return 1; + bits -= v; + } + break; + } + + if(!sign && bits <= 62 && (1LL<op) { + default: + fatal("usefield %O", n->op); + case ODOT: + case ODOTPTR: + break; + } + + field = n->paramfld; + if(field == T) + fatal("usefield %T %S without paramfld", n->left->type, n->right->sym); + if(field->note == nil || strstr(field->note->s, "go:\"track\"") == nil) + return; + + // dedup on list + if(field->lastfn == curfn) + return; + field->lastfn = curfn; + field->outer = n->left->type; + if(isptr[field->outer->etype]) + field->outer = field->outer->type; + if(field->outer->sym == S) + yyerror("tracked field must be in named struct type"); + if(!exportname(field->sym->name)) + yyerror("tracked field must be exported (upper case)"); + + l = typ(0); + l->type = field; + l->down = curfn->paramfld; + curfn->paramfld = l; +} + +static int +candiscardlist(NodeList *l) +{ + for(; l; l=l->next) + if(!candiscard(l->n)) + return 0; + return 1; +} + +int +candiscard(Node *n) +{ + if(n == N) + return 1; + + switch(n->op) { + default: + return 0; + + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + case OADD: + case OSUB: + case OOR: + case OXOR: + case OADDSTR: + case OADDR: + case OANDAND: + case OARRAYBYTESTR: + case OARRAYRUNESTR: + case OSTRARRAYBYTE: + case OSTRARRAYRUNE: + case OCAP: + case OCMPIFACE: + case OCMPSTR: + case OCOMPLIT: + case OMAPLIT: + case OSTRUCTLIT: + case OARRAYLIT: + case OPTRLIT: + case OCONV: + case OCONVIFACE: + case OCONVNOP: + case ODOT: + case OEQ: + case ONE: + case OLT: + case OLE: + case OGT: + case OGE: + case OKEY: + case OLEN: + case OMUL: + case OLSH: + case ORSH: + case OAND: + case OANDNOT: + case ONEW: + case ONOT: + case OCOM: + case OPLUS: + case OMINUS: + case OOROR: + case OPAREN: + case ORUNESTR: + case OREAL: + case OIMAG: + case OCOMPLEX: + // Discardable as long as the subpieces are. + break; + + case ODIV: + case OMOD: + // Discardable as long as we know it's not division by zero. + if(isconst(n->right, CTINT) && mpcmpfixc(n->right->val.u.xval, 0) != 0) + break; + if(isconst(n->right, CTFLT) && mpcmpfltc(n->right->val.u.fval, 0) != 0) + break; + return 0; + + case OMAKECHAN: + case OMAKEMAP: + // Discardable as long as we know it won't fail because of a bad size. + if(isconst(n->left, CTINT) && mpcmpfixc(n->left->val.u.xval, 0) == 0) + break; + return 0; + + case OMAKESLICE: + // Difficult to tell what sizes are okay. + return 0; + } + + if(!candiscard(n->left) || + !candiscard(n->right) || + !candiscard(n->ntest) || + !candiscard(n->nincr) || + !candiscardlist(n->ninit) || + !candiscardlist(n->nbody) || + !candiscardlist(n->nelse) || + !candiscardlist(n->list) || + !candiscardlist(n->rlist)) { + return 0; + } + + return 1; +} diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c index 97bf233eb..75175455e 100644 --- a/src/cmd/gc/y.tab.c +++ b/src/cmd/gc/y.tab.c @@ -1,21 +1,24 @@ -/* A Bison parser, made by GNU Bison 2.5. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - + the Free Software Foundation; either version 2, or (at your option) + any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -26,7 +29,7 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ @@ -44,7 +47,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.5" +#define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -52,52 +55,11 @@ /* Pure parsers. */ #define YYPURE 0 -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - /* Using locations. */ #define YYLSP_NEEDED 0 -/* Copy the first part of user declarations. */ - -/* Line 268 of yacc.c */ -#line 20 "go.y" - -#include -#include /* if we don't, bison will, and go.h re-#defines getc */ -#include -#include "go.h" - -static void fixlbrace(int); - - -/* Line 268 of yacc.c */ -#line 81 "y.tab.c" - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 1 -#endif - -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif - - /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -106,10 +68,10 @@ static void fixlbrace(int); enum yytokentype { LLITERAL = 258, LASOP = 259, - LBREAK = 260, - LCASE = 261, - LCHAN = 262, - LCOLAS = 263, + LCOLAS = 260, + LBREAK = 261, + LCASE = 262, + LCHAN = 263, LCONST = 264, LCONTINUE = 265, LDDD = 266, @@ -158,10 +120,10 @@ static void fixlbrace(int); /* Tokens. */ #define LLITERAL 258 #define LASOP 259 -#define LBREAK 260 -#define LCASE 261 -#define LCHAN 262 -#define LCOLAS 263 +#define LCOLAS 260 +#define LBREAK 261 +#define LCASE 262 +#define LCHAN 263 #define LCONST 264 #define LCONTINUE 265 #define LDDD 266 @@ -209,36 +171,61 @@ static void fixlbrace(int); +/* Copy the first part of user declarations. */ +#line 20 "go.y" + +#include +#include /* if we don't, bison will, and go.h re-#defines getc */ +#include +#include "go.h" + +static void fixlbrace(int); + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -{ - -/* Line 293 of yacc.c */ #line 28 "go.y" - +{ Node* node; NodeList* list; Type* type; Sym* sym; struct Val val; int i; - - - -/* Line 293 of yacc.c */ -#line 230 "y.tab.c" -} YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 +} +/* Line 193 of yacc.c. */ +#line 216 "y.tab.c" + YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 #endif + /* Copy the second part of user declarations. */ -/* Line 343 of yacc.c */ -#line 242 "y.tab.c" +/* Line 216 of yacc.c. */ +#line 229 "y.tab.c" #ifdef short # undef short @@ -313,14 +300,14 @@ typedef short int yytype_int16; #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int -YYID (int yyi) +YYID (int i) #else static int -YYID (yyi) - int yyi; +YYID (i) + int i; #endif { - return yyi; + return i; } #endif @@ -341,11 +328,11 @@ YYID (yyi) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 +# ifndef _STDLIB_H +# define _STDLIB_H 1 # endif # endif # endif @@ -368,24 +355,24 @@ YYID (yyi) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined EXIT_SUCCESS \ +# if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 +# ifndef _STDLIB_H +# define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -401,9 +388,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss_alloc; - YYSTYPE yyvs_alloc; -}; + yytype_int16 yyss; + YYSTYPE yyvs; + }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) @@ -414,27 +401,6 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -# define YYCOPY_NEEDED 1 - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY @@ -452,21 +418,38 @@ union yyalloc while (YYID (0)) # endif # endif -#endif /* !YYCOPY_NEEDED */ + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 4 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 2131 +#define YYLAST 2194 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 76 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 138 +#define YYNNTS 142 /* YYNRULES -- Number of rules. */ -#define YYNRULES 344 +#define YYNRULES 349 /* YYNRULES -- Number of states. */ -#define YYNSTATES 653 +#define YYNSTATES 663 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 @@ -524,148 +507,150 @@ static const yytype_uint16 yyprhs[] = 162, 164, 168, 172, 176, 179, 182, 186, 192, 198, 201, 202, 207, 208, 212, 213, 216, 217, 222, 227, 232, 238, 240, 242, 245, 246, 250, 252, 256, 257, - 258, 259, 267, 268, 271, 274, 275, 276, 284, 285, - 291, 293, 297, 301, 305, 309, 313, 317, 321, 325, - 329, 333, 337, 341, 345, 349, 353, 357, 361, 365, - 369, 373, 375, 378, 381, 384, 387, 390, 393, 396, - 399, 403, 409, 416, 418, 420, 424, 430, 436, 441, - 448, 450, 455, 461, 467, 475, 477, 478, 482, 484, - 489, 491, 495, 497, 499, 501, 503, 505, 507, 509, - 510, 512, 514, 516, 518, 523, 525, 527, 529, 532, - 534, 536, 538, 540, 542, 546, 548, 550, 552, 555, - 557, 559, 561, 563, 567, 569, 571, 573, 575, 577, - 579, 581, 583, 585, 589, 594, 599, 602, 606, 612, - 614, 616, 619, 623, 629, 633, 639, 643, 647, 653, - 662, 668, 677, 683, 684, 688, 689, 691, 695, 697, - 702, 705, 706, 710, 712, 716, 718, 722, 724, 728, - 730, 734, 736, 740, 744, 747, 752, 756, 762, 768, - 770, 774, 776, 779, 781, 785, 790, 792, 795, 798, - 800, 802, 806, 807, 810, 811, 813, 815, 817, 819, - 821, 823, 825, 827, 829, 830, 835, 837, 840, 843, - 846, 849, 852, 855, 857, 861, 863, 867, 869, 873, - 875, 879, 881, 885, 887, 889, 893, 897, 898, 901, - 902, 904, 905, 907, 908, 910, 911, 913, 914, 916, - 917, 919, 920, 922, 923, 925, 926, 928, 933, 938, - 944, 951, 956, 961, 963, 965, 967, 969, 971, 973, - 975, 977, 979, 983, 988, 994, 999, 1004, 1007, 1010, - 1015, 1019, 1023, 1029, 1033, 1038, 1042, 1048, 1050, 1051, - 1053, 1057, 1059, 1061, 1064, 1066, 1068, 1074, 1075, 1078, - 1080, 1084, 1086, 1090, 1092 + 258, 259, 268, 269, 275, 276, 279, 280, 283, 284, + 285, 293, 294, 300, 302, 306, 310, 314, 318, 322, + 326, 330, 334, 338, 342, 346, 350, 354, 358, 362, + 366, 370, 374, 378, 382, 384, 387, 390, 393, 396, + 399, 402, 405, 408, 412, 418, 425, 427, 429, 433, + 439, 445, 450, 457, 459, 465, 471, 477, 485, 487, + 488, 492, 494, 499, 501, 506, 508, 512, 514, 516, + 518, 520, 522, 524, 526, 527, 529, 531, 533, 535, + 540, 542, 544, 546, 549, 551, 553, 555, 557, 559, + 563, 565, 567, 569, 572, 574, 576, 578, 580, 584, + 586, 588, 590, 592, 594, 596, 598, 600, 602, 606, + 611, 616, 619, 623, 629, 631, 633, 636, 640, 646, + 650, 656, 660, 664, 670, 679, 685, 694, 700, 701, + 705, 706, 708, 712, 714, 719, 722, 723, 727, 729, + 733, 735, 739, 741, 745, 747, 751, 753, 757, 761, + 764, 769, 773, 779, 785, 787, 791, 793, 796, 798, + 802, 807, 809, 812, 815, 817, 819, 823, 824, 827, + 828, 830, 832, 834, 836, 838, 840, 842, 844, 846, + 847, 852, 854, 857, 860, 863, 866, 869, 872, 874, + 878, 880, 884, 886, 890, 892, 896, 898, 902, 904, + 906, 910, 914, 915, 918, 919, 921, 922, 924, 925, + 927, 928, 930, 931, 933, 934, 936, 937, 939, 940, + 942, 943, 945, 950, 955, 961, 968, 973, 978, 980, + 982, 984, 986, 988, 990, 992, 994, 996, 1000, 1005, + 1011, 1016, 1021, 1024, 1027, 1032, 1036, 1040, 1046, 1050, + 1055, 1059, 1065, 1067, 1068, 1070, 1074, 1076, 1078, 1081, + 1083, 1085, 1091, 1092, 1095, 1097, 1101, 1103, 1107, 1109 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int16 yyrhs[] = { - 77, 0, -1, 79, 78, 81, 162, -1, -1, 25, - 137, 62, -1, -1, 80, 86, 88, -1, -1, 81, - 82, 62, -1, 21, 83, -1, 21, 59, 84, 186, + 77, 0, -1, 79, 78, 81, 166, -1, -1, 25, + 141, 62, -1, -1, 80, 86, 88, -1, -1, 81, + 82, 62, -1, 21, 83, -1, 21, 59, 84, 190, 60, -1, 21, 59, 60, -1, 85, 86, 88, -1, 85, 88, -1, 83, -1, 84, 62, 83, -1, 3, - -1, 137, 3, -1, 63, 3, -1, 25, 24, 87, - 62, -1, -1, 24, -1, -1, 89, 210, 64, 64, - -1, -1, 91, -1, 154, -1, 177, -1, 1, -1, - 32, 93, -1, 32, 59, 163, 186, 60, -1, 32, - 59, 60, -1, 92, 94, -1, 92, 59, 94, 186, - 60, -1, 92, 59, 94, 62, 164, 186, 60, -1, - 92, 59, 60, -1, 31, 97, -1, 31, 59, 165, - 186, 60, -1, 31, 59, 60, -1, 9, -1, 181, - 142, -1, 181, 142, 65, 182, -1, 181, 65, 182, - -1, 181, 142, 65, 182, -1, 181, 65, 182, -1, - 94, -1, 181, 142, -1, 181, -1, 137, -1, 96, - 142, -1, 123, -1, 123, 4, 123, -1, 182, 65, - 182, -1, 182, 8, 182, -1, 123, 42, -1, 123, - 37, -1, 6, 183, 66, -1, 6, 183, 65, 123, - 66, -1, 6, 183, 8, 123, 66, -1, 12, 66, - -1, -1, 67, 101, 179, 68, -1, -1, 99, 103, - 179, -1, -1, 104, 102, -1, -1, 35, 106, 179, - 68, -1, 182, 65, 26, 123, -1, 182, 8, 26, - 123, -1, 190, 62, 190, 62, 190, -1, 190, -1, + -1, 141, 3, -1, 63, 3, -1, 25, 24, 87, + 62, -1, -1, 24, -1, -1, 89, 214, 64, 64, + -1, -1, 91, -1, 158, -1, 181, -1, 1, -1, + 32, 93, -1, 32, 59, 167, 190, 60, -1, 32, + 59, 60, -1, 92, 94, -1, 92, 59, 94, 190, + 60, -1, 92, 59, 94, 62, 168, 190, 60, -1, + 92, 59, 60, -1, 31, 97, -1, 31, 59, 169, + 190, 60, -1, 31, 59, 60, -1, 9, -1, 185, + 146, -1, 185, 146, 65, 186, -1, 185, 65, 186, + -1, 185, 146, 65, 186, -1, 185, 65, 186, -1, + 94, -1, 185, 146, -1, 185, -1, 141, -1, 96, + 146, -1, 126, -1, 126, 4, 126, -1, 186, 65, + 186, -1, 186, 5, 186, -1, 126, 42, -1, 126, + 37, -1, 7, 187, 66, -1, 7, 187, 65, 126, + 66, -1, 7, 187, 5, 126, 66, -1, 12, 66, + -1, -1, 67, 101, 183, 68, -1, -1, 99, 103, + 183, -1, -1, 104, 102, -1, -1, 35, 106, 183, + 68, -1, 186, 65, 26, 126, -1, 186, 5, 26, + 126, -1, 194, 62, 194, 62, 194, -1, 194, -1, 107, -1, 108, 105, -1, -1, 16, 111, 109, -1, - 190, -1, 190, 62, 190, -1, -1, -1, -1, 20, - 114, 112, 115, 105, 116, 117, -1, -1, 14, 113, - -1, 14, 100, -1, -1, -1, 30, 119, 112, 120, - 35, 104, 68, -1, -1, 28, 122, 35, 104, 68, - -1, 124, -1, 123, 47, 123, -1, 123, 33, 123, - -1, 123, 38, 123, -1, 123, 46, 123, -1, 123, - 45, 123, -1, 123, 43, 123, -1, 123, 39, 123, - -1, 123, 40, 123, -1, 123, 49, 123, -1, 123, - 50, 123, -1, 123, 51, 123, -1, 123, 52, 123, - -1, 123, 53, 123, -1, 123, 54, 123, -1, 123, - 55, 123, -1, 123, 56, 123, -1, 123, 34, 123, - -1, 123, 44, 123, -1, 123, 48, 123, -1, 123, - 36, 123, -1, 130, -1, 53, 124, -1, 56, 124, - -1, 49, 124, -1, 50, 124, -1, 69, 124, -1, - 70, 124, -1, 52, 124, -1, 36, 124, -1, 130, - 59, 60, -1, 130, 59, 183, 187, 60, -1, 130, - 59, 183, 11, 187, 60, -1, 3, -1, 139, -1, - 130, 63, 137, -1, 130, 63, 59, 131, 60, -1, - 130, 63, 59, 31, 60, -1, 130, 71, 123, 72, - -1, 130, 71, 188, 66, 188, 72, -1, 125, -1, - 145, 59, 123, 60, -1, 146, 133, 127, 185, 68, - -1, 126, 67, 127, 185, 68, -1, 59, 131, 60, - 67, 127, 185, 68, -1, 161, -1, -1, 123, 66, - 129, -1, 123, -1, 67, 127, 185, 68, -1, 126, - -1, 59, 131, 60, -1, 123, -1, 143, -1, 142, - -1, 35, -1, 67, -1, 137, -1, 137, -1, -1, - 134, -1, 24, -1, 138, -1, 73, -1, 74, 3, - 63, 24, -1, 137, -1, 134, -1, 11, -1, 11, - 142, -1, 151, -1, 157, -1, 149, -1, 150, -1, - 148, -1, 59, 142, 60, -1, 151, -1, 157, -1, - 149, -1, 53, 143, -1, 157, -1, 149, -1, 150, - -1, 148, -1, 59, 142, 60, -1, 157, -1, 149, - -1, 149, -1, 151, -1, 157, -1, 149, -1, 150, - -1, 148, -1, 139, -1, 139, 63, 137, -1, 71, - 188, 72, 142, -1, 71, 11, 72, 142, -1, 7, - 144, -1, 7, 36, 142, -1, 23, 71, 142, 72, - 142, -1, 152, -1, 153, -1, 53, 142, -1, 36, - 7, 142, -1, 29, 133, 166, 186, 68, -1, 29, - 133, 68, -1, 22, 133, 167, 186, 68, -1, 22, - 133, 68, -1, 17, 155, 158, -1, 137, 59, 175, - 60, 159, -1, 59, 175, 60, 137, 59, 175, 60, - 159, -1, 196, 59, 191, 60, 206, -1, 59, 211, - 60, 137, 59, 191, 60, 206, -1, 17, 59, 175, - 60, 159, -1, -1, 67, 179, 68, -1, -1, 147, - -1, 59, 175, 60, -1, 157, -1, 160, 133, 179, - 68, -1, 160, 1, -1, -1, 162, 90, 62, -1, - 93, -1, 163, 62, 93, -1, 95, -1, 164, 62, - 95, -1, 97, -1, 165, 62, 97, -1, 168, -1, - 166, 62, 168, -1, 171, -1, 167, 62, 171, -1, - 180, 142, 194, -1, 170, 194, -1, 59, 170, 60, - 194, -1, 53, 170, 194, -1, 59, 53, 170, 60, - 194, -1, 53, 59, 170, 60, 194, -1, 24, -1, - 24, 63, 137, -1, 169, -1, 134, 172, -1, 169, - -1, 59, 169, 60, -1, 59, 175, 60, 159, -1, - 132, -1, 137, 132, -1, 137, 141, -1, 141, -1, - 173, -1, 174, 75, 173, -1, -1, 174, 187, -1, - -1, 100, -1, 91, -1, 177, -1, 1, -1, 98, - -1, 110, -1, 118, -1, 121, -1, 113, -1, -1, - 140, 66, 178, 176, -1, 15, -1, 5, 136, -1, - 10, 136, -1, 18, 125, -1, 13, 125, -1, 19, - 134, -1, 27, 189, -1, 176, -1, 179, 62, 176, - -1, 134, -1, 180, 75, 134, -1, 135, -1, 181, - 75, 135, -1, 123, -1, 182, 75, 123, -1, 131, - -1, 183, 75, 131, -1, 128, -1, 129, -1, 184, - 75, 128, -1, 184, 75, 129, -1, -1, 184, 187, - -1, -1, 62, -1, -1, 75, -1, -1, 123, -1, - -1, 182, -1, -1, 98, -1, -1, 211, -1, -1, - 212, -1, -1, 213, -1, -1, 3, -1, 21, 24, - 3, 62, -1, 32, 196, 198, 62, -1, 9, 196, - 65, 209, 62, -1, 9, 196, 198, 65, 209, 62, - -1, 31, 197, 198, 62, -1, 17, 156, 158, 62, - -1, 138, -1, 196, -1, 200, -1, 201, -1, 202, - -1, 200, -1, 202, -1, 138, -1, 24, -1, 71, - 72, 198, -1, 71, 3, 72, 198, -1, 23, 71, - 198, 72, 198, -1, 29, 67, 192, 68, -1, 22, - 67, 193, 68, -1, 53, 198, -1, 7, 199, -1, - 7, 59, 201, 60, -1, 7, 36, 198, -1, 36, - 7, 198, -1, 17, 59, 191, 60, 206, -1, 137, - 198, 194, -1, 137, 11, 198, 194, -1, 137, 198, - 194, -1, 137, 59, 191, 60, 206, -1, 198, -1, - -1, 207, -1, 59, 191, 60, -1, 198, -1, 3, - -1, 50, 3, -1, 137, -1, 208, -1, 59, 208, - 49, 208, 60, -1, -1, 210, 195, -1, 203, -1, - 211, 75, 203, -1, 204, -1, 212, 62, 204, -1, - 205, -1, 213, 62, 205, -1 + 194, -1, 194, 62, 194, -1, -1, -1, -1, 20, + 114, 112, 115, 105, 116, 119, 120, -1, -1, 14, + 20, 118, 112, 105, -1, -1, 119, 117, -1, -1, + 14, 100, -1, -1, -1, 30, 122, 112, 123, 35, + 104, 68, -1, -1, 28, 125, 35, 104, 68, -1, + 127, -1, 126, 47, 126, -1, 126, 33, 126, -1, + 126, 38, 126, -1, 126, 46, 126, -1, 126, 45, + 126, -1, 126, 43, 126, -1, 126, 39, 126, -1, + 126, 40, 126, -1, 126, 49, 126, -1, 126, 50, + 126, -1, 126, 51, 126, -1, 126, 52, 126, -1, + 126, 53, 126, -1, 126, 54, 126, -1, 126, 55, + 126, -1, 126, 56, 126, -1, 126, 34, 126, -1, + 126, 44, 126, -1, 126, 48, 126, -1, 126, 36, + 126, -1, 134, -1, 53, 127, -1, 56, 127, -1, + 49, 127, -1, 50, 127, -1, 69, 127, -1, 70, + 127, -1, 52, 127, -1, 36, 127, -1, 134, 59, + 60, -1, 134, 59, 187, 191, 60, -1, 134, 59, + 187, 11, 191, 60, -1, 3, -1, 143, -1, 134, + 63, 141, -1, 134, 63, 59, 135, 60, -1, 134, + 63, 59, 31, 60, -1, 134, 71, 126, 72, -1, + 134, 71, 192, 66, 192, 72, -1, 128, -1, 149, + 59, 126, 191, 60, -1, 150, 137, 130, 189, 68, + -1, 129, 67, 130, 189, 68, -1, 59, 135, 60, + 67, 130, 189, 68, -1, 165, -1, -1, 126, 66, + 133, -1, 126, -1, 67, 130, 189, 68, -1, 126, + -1, 67, 130, 189, 68, -1, 129, -1, 59, 135, + 60, -1, 126, -1, 147, -1, 146, -1, 35, -1, + 67, -1, 141, -1, 141, -1, -1, 138, -1, 24, + -1, 142, -1, 73, -1, 74, 3, 63, 24, -1, + 141, -1, 138, -1, 11, -1, 11, 146, -1, 155, + -1, 161, -1, 153, -1, 154, -1, 152, -1, 59, + 146, 60, -1, 155, -1, 161, -1, 153, -1, 53, + 147, -1, 161, -1, 153, -1, 154, -1, 152, -1, + 59, 146, 60, -1, 161, -1, 153, -1, 153, -1, + 155, -1, 161, -1, 153, -1, 154, -1, 152, -1, + 143, -1, 143, 63, 141, -1, 71, 192, 72, 146, + -1, 71, 11, 72, 146, -1, 8, 148, -1, 8, + 36, 146, -1, 23, 71, 146, 72, 146, -1, 156, + -1, 157, -1, 53, 146, -1, 36, 8, 146, -1, + 29, 137, 170, 190, 68, -1, 29, 137, 68, -1, + 22, 137, 171, 190, 68, -1, 22, 137, 68, -1, + 17, 159, 162, -1, 141, 59, 179, 60, 163, -1, + 59, 179, 60, 141, 59, 179, 60, 163, -1, 200, + 59, 195, 60, 210, -1, 59, 215, 60, 141, 59, + 195, 60, 210, -1, 17, 59, 179, 60, 163, -1, + -1, 67, 183, 68, -1, -1, 151, -1, 59, 179, + 60, -1, 161, -1, 164, 137, 183, 68, -1, 164, + 1, -1, -1, 166, 90, 62, -1, 93, -1, 167, + 62, 93, -1, 95, -1, 168, 62, 95, -1, 97, + -1, 169, 62, 97, -1, 172, -1, 170, 62, 172, + -1, 175, -1, 171, 62, 175, -1, 184, 146, 198, + -1, 174, 198, -1, 59, 174, 60, 198, -1, 53, + 174, 198, -1, 59, 53, 174, 60, 198, -1, 53, + 59, 174, 60, 198, -1, 24, -1, 24, 63, 141, + -1, 173, -1, 138, 176, -1, 173, -1, 59, 173, + 60, -1, 59, 179, 60, 163, -1, 136, -1, 141, + 136, -1, 141, 145, -1, 145, -1, 177, -1, 178, + 75, 177, -1, -1, 178, 191, -1, -1, 100, -1, + 91, -1, 181, -1, 1, -1, 98, -1, 110, -1, + 121, -1, 124, -1, 113, -1, -1, 144, 66, 182, + 180, -1, 15, -1, 6, 140, -1, 10, 140, -1, + 18, 128, -1, 13, 128, -1, 19, 138, -1, 27, + 193, -1, 180, -1, 183, 62, 180, -1, 138, -1, + 184, 75, 138, -1, 139, -1, 185, 75, 139, -1, + 126, -1, 186, 75, 126, -1, 135, -1, 187, 75, + 135, -1, 131, -1, 132, -1, 188, 75, 131, -1, + 188, 75, 132, -1, -1, 188, 191, -1, -1, 62, + -1, -1, 75, -1, -1, 126, -1, -1, 186, -1, + -1, 98, -1, -1, 215, -1, -1, 216, -1, -1, + 217, -1, -1, 3, -1, 21, 24, 3, 62, -1, + 32, 200, 202, 62, -1, 9, 200, 65, 213, 62, + -1, 9, 200, 202, 65, 213, 62, -1, 31, 201, + 202, 62, -1, 17, 160, 162, 62, -1, 142, -1, + 200, -1, 204, -1, 205, -1, 206, -1, 204, -1, + 206, -1, 142, -1, 24, -1, 71, 72, 202, -1, + 71, 3, 72, 202, -1, 23, 71, 202, 72, 202, + -1, 29, 67, 196, 68, -1, 22, 67, 197, 68, + -1, 53, 202, -1, 8, 203, -1, 8, 59, 205, + 60, -1, 8, 36, 202, -1, 36, 8, 202, -1, + 17, 59, 195, 60, 210, -1, 141, 202, 198, -1, + 141, 11, 202, 198, -1, 141, 202, 198, -1, 141, + 59, 195, 60, 210, -1, 202, -1, -1, 211, -1, + 59, 195, 60, -1, 202, -1, 3, -1, 50, 3, + -1, 141, -1, 212, -1, 59, 212, 49, 212, 60, + -1, -1, 214, 199, -1, 207, -1, 215, 75, 207, + -1, 208, -1, 216, 62, 208, -1, 209, -1, 217, + 62, 209, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -676,36 +661,36 @@ static const yytype_uint16 yyrline[] = 259, 260, 267, 267, 280, 284, 285, 289, 294, 300, 304, 308, 312, 318, 324, 330, 335, 339, 343, 349, 355, 359, 363, 369, 373, 379, 380, 384, 390, 399, - 405, 409, 414, 426, 442, 447, 454, 474, 492, 501, - 520, 519, 531, 530, 561, 564, 571, 570, 581, 587, - 596, 607, 613, 616, 624, 623, 634, 640, 652, 656, - 661, 651, 673, 676, 680, 687, 691, 686, 709, 708, - 724, 725, 729, 733, 737, 741, 745, 749, 753, 757, - 761, 765, 769, 773, 777, 781, 785, 789, 793, 797, - 802, 808, 809, 813, 824, 828, 832, 836, 841, 845, - 855, 859, 864, 872, 876, 877, 888, 892, 896, 900, - 904, 905, 911, 918, 924, 931, 934, 941, 947, 948, - 955, 956, 974, 975, 978, 981, 985, 996, 1005, 1011, - 1014, 1017, 1024, 1025, 1031, 1040, 1048, 1060, 1065, 1071, - 1072, 1073, 1074, 1075, 1076, 1082, 1083, 1084, 1085, 1091, - 1092, 1093, 1094, 1095, 1101, 1102, 1105, 1108, 1109, 1110, - 1111, 1112, 1115, 1116, 1129, 1133, 1138, 1143, 1148, 1152, - 1153, 1156, 1162, 1169, 1175, 1182, 1188, 1199, 1210, 1239, - 1278, 1301, 1318, 1327, 1330, 1338, 1342, 1346, 1353, 1359, - 1364, 1376, 1379, 1387, 1388, 1394, 1395, 1401, 1405, 1411, - 1412, 1418, 1422, 1428, 1451, 1456, 1462, 1468, 1475, 1484, - 1493, 1508, 1514, 1519, 1523, 1530, 1543, 1544, 1550, 1556, - 1559, 1563, 1569, 1572, 1581, 1584, 1585, 1589, 1590, 1596, - 1597, 1598, 1599, 1600, 1602, 1601, 1616, 1621, 1625, 1629, - 1633, 1637, 1642, 1661, 1667, 1675, 1679, 1685, 1689, 1695, - 1699, 1705, 1709, 1718, 1722, 1726, 1730, 1736, 1739, 1747, - 1748, 1750, 1751, 1754, 1757, 1760, 1763, 1766, 1769, 1772, - 1775, 1778, 1781, 1784, 1787, 1790, 1793, 1799, 1803, 1807, - 1811, 1815, 1819, 1837, 1844, 1855, 1856, 1857, 1860, 1861, - 1864, 1868, 1878, 1882, 1886, 1890, 1894, 1898, 1902, 1908, - 1914, 1922, 1930, 1936, 1943, 1959, 1977, 1981, 1987, 1990, - 1993, 1997, 2007, 2011, 2026, 2034, 2035, 2045, 2046, 2049, - 2053, 2059, 2063, 2069, 2073 + 405, 423, 428, 440, 456, 461, 468, 488, 506, 515, + 534, 533, 545, 544, 575, 578, 585, 584, 595, 601, + 610, 621, 627, 630, 638, 637, 648, 654, 666, 670, + 675, 665, 696, 695, 708, 711, 717, 720, 732, 736, + 731, 754, 753, 769, 770, 774, 778, 782, 786, 790, + 794, 798, 802, 806, 810, 814, 818, 822, 826, 830, + 834, 838, 842, 847, 853, 854, 858, 869, 873, 877, + 881, 886, 890, 900, 904, 909, 917, 921, 922, 933, + 937, 941, 945, 949, 950, 956, 963, 969, 976, 979, + 986, 992, 1009, 1016, 1017, 1024, 1025, 1044, 1045, 1048, + 1051, 1055, 1066, 1075, 1081, 1084, 1087, 1094, 1095, 1101, + 1116, 1124, 1136, 1141, 1147, 1148, 1149, 1150, 1151, 1152, + 1158, 1159, 1160, 1161, 1167, 1168, 1169, 1170, 1171, 1177, + 1178, 1181, 1184, 1185, 1186, 1187, 1188, 1191, 1192, 1205, + 1209, 1214, 1219, 1224, 1228, 1229, 1232, 1238, 1245, 1251, + 1258, 1264, 1275, 1289, 1318, 1358, 1383, 1401, 1410, 1413, + 1421, 1425, 1429, 1436, 1442, 1447, 1459, 1462, 1472, 1473, + 1479, 1480, 1486, 1490, 1496, 1497, 1503, 1507, 1513, 1536, + 1541, 1547, 1553, 1560, 1569, 1578, 1593, 1599, 1604, 1608, + 1615, 1628, 1629, 1635, 1641, 1644, 1648, 1654, 1657, 1666, + 1669, 1670, 1674, 1675, 1681, 1682, 1683, 1684, 1685, 1687, + 1686, 1701, 1706, 1710, 1714, 1718, 1722, 1727, 1746, 1752, + 1760, 1764, 1770, 1774, 1780, 1784, 1790, 1794, 1803, 1807, + 1811, 1815, 1821, 1824, 1832, 1833, 1835, 1836, 1839, 1842, + 1845, 1848, 1851, 1854, 1857, 1860, 1863, 1866, 1869, 1872, + 1875, 1878, 1884, 1888, 1892, 1896, 1900, 1904, 1924, 1931, + 1942, 1943, 1944, 1947, 1948, 1951, 1955, 1965, 1969, 1973, + 1977, 1981, 1985, 1989, 1995, 2001, 2009, 2017, 2023, 2030, + 2046, 2064, 2068, 2074, 2077, 2080, 2084, 2094, 2098, 2113, + 2121, 2122, 2134, 2135, 2138, 2142, 2148, 2152, 2158, 2162 }; #endif @@ -714,8 +699,8 @@ static const yytype_uint16 yyrline[] = First, the terminals, then, starting at YYNTOKENS, nonterminals. */ const char *yytname[] = { - "$end", "error", "$undefined", "LLITERAL", "LASOP", "LBREAK", "LCASE", - "LCHAN", "LCOLAS", "LCONST", "LCONTINUE", "LDDD", "LDEFAULT", "LDEFER", + "$end", "error", "$undefined", "LLITERAL", "LASOP", "LCOLAS", "LBREAK", + "LCASE", "LCHAN", "LCONST", "LCONTINUE", "LDDD", "LDEFAULT", "LDEFER", "LELSE", "LFALL", "LFOR", "LFUNC", "LGO", "LGOTO", "LIF", "LIMPORT", "LINTERFACE", "LMAP", "LNAME", "LPACKAGE", "LRANGE", "LRETURN", "LSELECT", "LSTRUCT", "LSWITCH", "LTYPE", "LVAR", "LANDAND", "LANDNOT", @@ -724,30 +709,31 @@ const char *yytname[] = "'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'", "PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'", "'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file", - "package", "loadsys", "$@1", "imports", "import", "import_stmt", + "package", "loadsys", "@1", "imports", "import", "import_stmt", "import_stmt_list", "import_here", "import_package", "import_safety", - "import_there", "$@2", "xdcl", "common_dcl", "lconst", "vardcl", + "import_there", "@2", "xdcl", "common_dcl", "lconst", "vardcl", "constdcl", "constdcl1", "typedclname", "typedcl", "simple_stmt", "case", - "compound_stmt", "$@3", "caseblock", "$@4", "caseblock_list", - "loop_body", "$@5", "range_stmt", "for_header", "for_body", "for_stmt", - "$@6", "if_header", "if_stmt", "$@7", "$@8", "$@9", "else", - "switch_stmt", "$@10", "$@11", "select_stmt", "$@12", "expr", "uexpr", - "pseudocall", "pexpr_no_paren", "start_complit", "keyval", "complitexpr", - "pexpr", "expr_or_type", "name_or_type", "lbrace", "new_name", - "dcl_name", "onew_name", "sym", "hidden_importsym", "name", "labelname", - "dotdotdot", "ntype", "non_expr_type", "non_recvchantype", "convtype", - "comptype", "fnret_type", "dotname", "othertype", "ptrtype", - "recvchantype", "structtype", "interfacetype", "xfndcl", "fndcl", - "hidden_fndcl", "fntype", "fnbody", "fnres", "fnlitdcl", "fnliteral", - "xdcl_list", "vardcl_list", "constdcl_list", "typedcl_list", - "structdcl_list", "interfacedcl_list", "structdcl", "packname", "embed", - "interfacedcl", "indcl", "arg_type", "arg_type_list", - "oarg_type_list_ocomma", "stmt", "non_dcl_stmt", "$@13", "stmt_list", - "new_name_list", "dcl_name_list", "expr_list", "expr_or_type_list", - "keyval_list", "braced_keyval_list", "osemi", "ocomma", "oexpr", - "oexpr_list", "osimple_stmt", "ohidden_funarg_list", - "ohidden_structdcl_list", "ohidden_interfacedcl_list", "oliteral", - "hidden_import", "hidden_pkg_importsym", "hidden_pkgtype", "hidden_type", + "compound_stmt", "@3", "caseblock", "@4", "caseblock_list", "loop_body", + "@5", "range_stmt", "for_header", "for_body", "for_stmt", "@6", + "if_header", "if_stmt", "@7", "@8", "@9", "elseif", "@10", "elseif_list", + "else", "switch_stmt", "@11", "@12", "select_stmt", "@13", "expr", + "uexpr", "pseudocall", "pexpr_no_paren", "start_complit", "keyval", + "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type", + "name_or_type", "lbrace", "new_name", "dcl_name", "onew_name", "sym", + "hidden_importsym", "name", "labelname", "dotdotdot", "ntype", + "non_expr_type", "non_recvchantype", "convtype", "comptype", + "fnret_type", "dotname", "othertype", "ptrtype", "recvchantype", + "structtype", "interfacetype", "xfndcl", "fndcl", "hidden_fndcl", + "fntype", "fnbody", "fnres", "fnlitdcl", "fnliteral", "xdcl_list", + "vardcl_list", "constdcl_list", "typedcl_list", "structdcl_list", + "interfacedcl_list", "structdcl", "packname", "embed", "interfacedcl", + "indcl", "arg_type", "arg_type_list", "oarg_type_list_ocomma", "stmt", + "non_dcl_stmt", "@14", "stmt_list", "new_name_list", "dcl_name_list", + "expr_list", "expr_or_type_list", "keyval_list", "braced_keyval_list", + "osemi", "ocomma", "oexpr", "oexpr_list", "osimple_stmt", + "ohidden_funarg_list", "ohidden_structdcl_list", + "ohidden_interfacedcl_list", "oliteral", "hidden_import", + "hidden_pkg_importsym", "hidden_pkgtype", "hidden_type", "hidden_type_non_recv_chan", "hidden_type_misc", "hidden_type_recv_chan", "hidden_type_func", "hidden_funarg", "hidden_structdcl", "hidden_interfacedcl", "ohidden_funres", "hidden_funres", @@ -784,33 +770,33 @@ static const yytype_uint8 yyr1[] = 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 101, 100, 103, 102, 104, 104, 106, 105, 107, 107, 108, 108, 108, 109, 111, 110, 112, 112, 114, 115, - 116, 113, 117, 117, 117, 119, 120, 118, 122, 121, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 127, 128, 129, 129, - 130, 130, 131, 131, 132, 133, 133, 134, 135, 136, - 136, 137, 137, 137, 138, 139, 140, 141, 141, 142, - 142, 142, 142, 142, 142, 143, 143, 143, 143, 144, - 144, 144, 144, 144, 145, 145, 146, 147, 147, 147, - 147, 147, 148, 148, 149, 149, 149, 149, 149, 149, - 149, 150, 151, 152, 152, 153, 153, 154, 155, 155, - 156, 156, 157, 158, 158, 159, 159, 159, 160, 161, - 161, 162, 162, 163, 163, 164, 164, 165, 165, 166, - 166, 167, 167, 168, 168, 168, 168, 168, 168, 169, - 169, 170, 171, 171, 171, 172, 173, 173, 173, 173, - 174, 174, 175, 175, 176, 176, 176, 176, 176, 177, - 177, 177, 177, 177, 178, 177, 177, 177, 177, 177, - 177, 177, 177, 179, 179, 180, 180, 181, 181, 182, - 182, 183, 183, 184, 184, 184, 184, 185, 185, 186, - 186, 187, 187, 188, 188, 189, 189, 190, 190, 191, - 191, 192, 192, 193, 193, 194, 194, 195, 195, 195, - 195, 195, 195, 196, 197, 198, 198, 198, 199, 199, - 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, - 200, 201, 202, 203, 203, 204, 205, 205, 206, 206, - 207, 207, 208, 208, 208, 209, 209, 210, 210, 211, - 211, 212, 212, 213, 213 + 116, 113, 118, 117, 119, 119, 120, 120, 122, 123, + 121, 125, 124, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 128, 128, 128, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 130, + 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, + 137, 137, 138, 139, 140, 140, 141, 141, 141, 142, + 143, 144, 145, 145, 146, 146, 146, 146, 146, 146, + 147, 147, 147, 147, 148, 148, 148, 148, 148, 149, + 149, 150, 151, 151, 151, 151, 151, 152, 152, 153, + 153, 153, 153, 153, 153, 153, 154, 155, 156, 156, + 157, 157, 158, 159, 159, 160, 160, 161, 162, 162, + 163, 163, 163, 164, 165, 165, 166, 166, 167, 167, + 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, + 172, 172, 172, 172, 173, 173, 174, 175, 175, 175, + 176, 177, 177, 177, 177, 178, 178, 179, 179, 180, + 180, 180, 180, 180, 181, 181, 181, 181, 181, 182, + 181, 181, 181, 181, 181, 181, 181, 181, 183, 183, + 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, + 188, 188, 189, 189, 190, 190, 191, 191, 192, 192, + 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, + 198, 198, 199, 199, 199, 199, 199, 199, 200, 201, + 202, 202, 202, 203, 203, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 205, 206, 207, 207, + 208, 209, 209, 210, 210, 211, 211, 212, 212, 212, + 213, 213, 214, 214, 215, 215, 216, 216, 217, 217 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -824,663 +810,674 @@ static const yytype_uint8 yyr2[] = 1, 3, 3, 3, 2, 2, 3, 5, 5, 2, 0, 4, 0, 3, 0, 2, 0, 4, 4, 4, 5, 1, 1, 2, 0, 3, 1, 3, 0, 0, - 0, 7, 0, 2, 2, 0, 0, 7, 0, 5, - 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 8, 0, 5, 0, 2, 0, 2, 0, 0, + 7, 0, 5, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 1, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 5, 6, 1, 1, 3, 5, 5, 4, 6, - 1, 4, 5, 5, 7, 1, 0, 3, 1, 4, - 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 4, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 3, 1, 1, 1, 2, 1, - 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 3, 4, 4, 2, 3, 5, 1, - 1, 2, 3, 5, 3, 5, 3, 3, 5, 8, - 5, 8, 5, 0, 3, 0, 1, 3, 1, 4, - 2, 0, 3, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 3, 3, 2, 4, 3, 5, 5, 1, - 3, 1, 2, 1, 3, 4, 1, 2, 2, 1, - 1, 3, 0, 2, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 4, 1, 2, 2, 2, - 2, 2, 2, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 3, 1, 1, 3, 3, 0, 2, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 4, 4, 5, - 6, 4, 4, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 3, 4, 5, 4, 4, 2, 2, 4, - 3, 3, 5, 3, 4, 3, 5, 1, 0, 1, - 3, 1, 1, 2, 1, 1, 5, 0, 2, 1, - 3, 1, 3, 1, 3 + 3, 3, 3, 3, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 5, 6, 1, 1, 3, 5, + 5, 4, 6, 1, 5, 5, 5, 7, 1, 0, + 3, 1, 4, 1, 4, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 4, + 1, 1, 1, 2, 1, 1, 1, 1, 1, 3, + 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 4, + 4, 2, 3, 5, 1, 1, 2, 3, 5, 3, + 5, 3, 3, 5, 8, 5, 8, 5, 0, 3, + 0, 1, 3, 1, 4, 2, 0, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 3, 2, + 4, 3, 5, 5, 1, 3, 1, 2, 1, 3, + 4, 1, 2, 2, 1, 1, 3, 0, 2, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 4, 1, 2, 2, 2, 2, 2, 2, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 1, + 3, 3, 0, 2, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 4, 4, 5, 6, 4, 4, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 4, 5, + 4, 4, 2, 2, 4, 3, 3, 5, 3, 4, + 3, 5, 1, 0, 1, 3, 1, 1, 2, 1, + 1, 5, 0, 2, 1, 3, 1, 3, 1, 3 }; -/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint16 yydefact[] = { - 5, 0, 3, 0, 1, 0, 7, 0, 22, 151, - 153, 0, 0, 152, 211, 20, 6, 337, 0, 4, + 5, 0, 3, 0, 1, 0, 7, 0, 22, 156, + 158, 0, 0, 157, 216, 20, 6, 342, 0, 4, 0, 0, 0, 21, 0, 0, 0, 16, 0, 0, - 9, 22, 0, 8, 28, 123, 149, 0, 39, 149, - 0, 256, 74, 0, 0, 0, 78, 0, 0, 285, - 88, 0, 85, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 283, 0, 25, 0, 249, 250, - 253, 251, 252, 50, 90, 130, 140, 111, 156, 155, - 124, 0, 0, 0, 176, 189, 190, 26, 208, 0, - 135, 27, 0, 19, 0, 0, 0, 0, 0, 0, - 338, 154, 11, 14, 279, 18, 22, 13, 17, 150, - 257, 147, 0, 0, 0, 0, 155, 182, 186, 172, - 170, 171, 169, 258, 130, 0, 287, 242, 0, 203, - 130, 261, 287, 145, 146, 0, 0, 269, 286, 262, - 0, 0, 287, 0, 0, 36, 48, 0, 29, 267, - 148, 0, 119, 114, 115, 118, 112, 113, 0, 0, - 142, 0, 143, 167, 165, 166, 116, 117, 0, 284, - 0, 212, 0, 32, 0, 0, 0, 0, 0, 55, + 9, 22, 0, 8, 28, 126, 154, 0, 39, 154, + 0, 261, 74, 0, 0, 0, 78, 0, 0, 290, + 91, 0, 88, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 288, 0, 25, 0, 254, 255, + 258, 256, 257, 50, 93, 133, 145, 114, 161, 160, + 127, 0, 0, 0, 181, 194, 195, 26, 213, 0, + 138, 27, 0, 19, 0, 0, 0, 0, 0, 0, + 343, 159, 11, 14, 284, 18, 22, 13, 17, 155, + 262, 152, 0, 0, 0, 0, 160, 187, 191, 177, + 175, 176, 174, 263, 133, 0, 292, 247, 0, 208, + 133, 266, 292, 150, 151, 0, 0, 274, 291, 267, + 0, 0, 292, 0, 0, 36, 48, 0, 29, 272, + 153, 0, 122, 117, 118, 121, 115, 116, 0, 0, + 147, 0, 148, 172, 170, 171, 119, 120, 0, 289, + 0, 217, 0, 32, 0, 0, 0, 0, 0, 55, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 136, 0, - 0, 283, 254, 0, 136, 210, 0, 0, 0, 0, - 303, 0, 0, 203, 0, 0, 304, 0, 0, 23, - 280, 0, 12, 242, 0, 0, 187, 163, 161, 162, - 159, 160, 191, 0, 0, 288, 72, 0, 75, 0, - 71, 157, 236, 155, 239, 144, 240, 281, 0, 242, - 0, 197, 79, 76, 151, 0, 196, 0, 279, 233, - 221, 0, 64, 0, 0, 194, 265, 279, 219, 231, - 295, 0, 86, 38, 217, 279, 49, 31, 213, 279, - 0, 0, 40, 0, 168, 141, 0, 0, 35, 279, - 0, 0, 51, 92, 107, 110, 93, 97, 98, 96, - 108, 95, 94, 91, 109, 99, 100, 101, 102, 103, - 104, 105, 106, 277, 120, 271, 281, 0, 125, 284, - 0, 0, 0, 277, 248, 60, 246, 245, 263, 247, - 0, 53, 52, 270, 0, 0, 0, 0, 311, 0, - 0, 0, 0, 0, 310, 0, 305, 306, 307, 0, - 339, 0, 0, 289, 0, 0, 0, 15, 10, 0, - 0, 0, 173, 183, 66, 73, 0, 0, 287, 158, - 237, 238, 282, 243, 205, 0, 0, 0, 287, 0, - 229, 0, 242, 232, 280, 0, 0, 0, 0, 295, - 0, 0, 280, 0, 296, 224, 0, 295, 0, 280, - 0, 280, 0, 42, 268, 0, 0, 0, 192, 163, - 161, 162, 160, 136, 185, 184, 280, 0, 44, 0, - 136, 138, 273, 274, 281, 0, 281, 282, 0, 0, - 0, 128, 283, 255, 131, 0, 0, 0, 209, 0, - 0, 318, 308, 309, 289, 293, 0, 291, 0, 317, - 332, 0, 0, 334, 335, 0, 0, 0, 0, 0, - 295, 0, 0, 302, 0, 290, 297, 301, 298, 205, - 164, 0, 0, 0, 0, 241, 242, 155, 206, 181, - 179, 180, 177, 178, 202, 205, 204, 80, 77, 230, - 234, 0, 222, 195, 188, 0, 0, 89, 62, 65, - 0, 226, 0, 295, 220, 193, 266, 223, 64, 218, - 37, 214, 30, 41, 0, 277, 45, 215, 279, 47, - 33, 43, 277, 0, 282, 278, 133, 282, 0, 272, - 121, 127, 126, 0, 132, 0, 264, 320, 0, 0, - 311, 0, 310, 0, 327, 343, 294, 0, 0, 0, - 341, 292, 321, 333, 0, 299, 0, 312, 0, 295, - 323, 0, 340, 328, 0, 69, 68, 287, 0, 242, - 198, 82, 205, 0, 59, 0, 295, 295, 225, 0, - 164, 0, 280, 0, 46, 0, 138, 137, 275, 276, - 122, 129, 61, 319, 328, 289, 316, 0, 0, 295, - 315, 0, 0, 313, 300, 324, 289, 289, 331, 200, - 329, 67, 70, 207, 0, 0, 81, 235, 0, 0, - 56, 0, 63, 228, 227, 87, 134, 216, 34, 139, - 322, 0, 344, 314, 325, 342, 0, 0, 0, 205, - 84, 83, 0, 0, 328, 336, 328, 330, 199, 58, - 57, 326, 201 + 0, 0, 0, 0, 0, 0, 0, 0, 139, 0, + 0, 288, 259, 0, 139, 215, 0, 0, 0, 0, + 308, 0, 0, 208, 0, 0, 309, 0, 0, 23, + 285, 0, 12, 247, 0, 0, 192, 168, 166, 167, + 164, 165, 196, 0, 0, 293, 72, 0, 75, 0, + 71, 162, 241, 160, 244, 149, 245, 286, 0, 247, + 0, 202, 79, 76, 156, 0, 201, 0, 284, 238, + 226, 0, 64, 0, 0, 199, 270, 284, 224, 236, + 300, 0, 89, 38, 222, 284, 49, 31, 218, 284, + 0, 0, 40, 0, 173, 146, 0, 0, 35, 284, + 0, 0, 51, 95, 110, 113, 96, 100, 101, 99, + 111, 98, 97, 94, 112, 102, 103, 104, 105, 106, + 107, 108, 109, 282, 123, 276, 286, 0, 128, 289, + 0, 0, 286, 282, 253, 60, 251, 250, 268, 252, + 0, 53, 52, 275, 0, 0, 0, 0, 316, 0, + 0, 0, 0, 0, 315, 0, 310, 311, 312, 0, + 344, 0, 0, 294, 0, 0, 0, 15, 10, 0, + 0, 0, 178, 188, 66, 73, 0, 0, 292, 163, + 242, 243, 287, 248, 210, 0, 0, 0, 292, 0, + 234, 0, 247, 237, 285, 0, 0, 0, 0, 300, + 0, 0, 285, 0, 301, 229, 0, 300, 0, 285, + 0, 285, 0, 42, 273, 0, 0, 0, 197, 168, + 166, 167, 165, 139, 190, 189, 285, 0, 44, 0, + 139, 141, 278, 279, 286, 0, 286, 287, 0, 0, + 0, 131, 288, 260, 287, 0, 0, 0, 0, 214, + 0, 0, 323, 313, 314, 294, 298, 0, 296, 0, + 322, 337, 0, 0, 339, 340, 0, 0, 0, 0, + 0, 300, 0, 0, 307, 0, 295, 302, 306, 303, + 210, 169, 0, 0, 0, 0, 246, 247, 160, 211, + 186, 184, 185, 182, 183, 207, 210, 209, 80, 77, + 235, 239, 0, 227, 200, 193, 0, 0, 92, 62, + 65, 0, 231, 0, 300, 225, 198, 271, 228, 64, + 223, 37, 219, 30, 41, 0, 282, 45, 220, 284, + 47, 33, 43, 282, 0, 287, 283, 136, 0, 277, + 124, 130, 129, 0, 134, 135, 0, 269, 325, 0, + 0, 316, 0, 315, 0, 332, 348, 299, 0, 0, + 0, 346, 297, 326, 338, 0, 304, 0, 317, 0, + 300, 328, 0, 345, 333, 0, 69, 68, 292, 0, + 247, 203, 84, 210, 0, 59, 0, 300, 300, 230, + 0, 169, 0, 285, 0, 46, 0, 139, 143, 140, + 280, 281, 125, 132, 61, 324, 333, 294, 321, 0, + 0, 300, 320, 0, 0, 318, 305, 329, 294, 294, + 336, 205, 334, 67, 70, 212, 0, 86, 240, 0, + 0, 56, 0, 63, 233, 232, 90, 137, 221, 34, + 142, 282, 327, 0, 349, 319, 330, 347, 0, 0, + 0, 210, 0, 85, 81, 0, 0, 0, 333, 341, + 333, 335, 204, 82, 87, 58, 57, 144, 331, 206, + 292, 0, 83 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 1, 6, 2, 3, 14, 21, 30, 104, 31, - 8, 24, 16, 17, 65, 326, 67, 148, 516, 517, - 144, 145, 68, 498, 327, 436, 499, 575, 387, 365, - 471, 236, 237, 238, 69, 126, 252, 70, 132, 377, - 571, 616, 71, 142, 398, 72, 140, 73, 74, 75, - 76, 313, 422, 423, 77, 315, 242, 135, 78, 149, - 110, 116, 13, 80, 81, 244, 245, 162, 118, 82, - 83, 478, 227, 84, 229, 230, 85, 86, 87, 129, - 213, 88, 251, 484, 89, 90, 22, 279, 518, 275, - 267, 258, 268, 269, 270, 260, 383, 246, 247, 248, - 328, 329, 321, 330, 271, 151, 92, 316, 424, 425, - 221, 373, 170, 139, 253, 464, 549, 543, 395, 100, - 211, 217, 608, 441, 346, 347, 348, 350, 550, 545, - 609, 610, 454, 455, 25, 465, 551, 546 + 8, 24, 16, 17, 65, 326, 67, 148, 517, 518, + 144, 145, 68, 499, 327, 437, 500, 576, 387, 365, + 472, 236, 237, 238, 69, 126, 252, 70, 132, 377, + 572, 643, 660, 617, 644, 71, 142, 398, 72, 140, + 73, 74, 75, 76, 313, 422, 423, 589, 77, 315, + 242, 135, 78, 149, 110, 116, 13, 80, 81, 244, + 245, 162, 118, 82, 83, 479, 227, 84, 229, 230, + 85, 86, 87, 129, 213, 88, 251, 485, 89, 90, + 22, 279, 519, 275, 267, 258, 268, 269, 270, 260, + 383, 246, 247, 248, 328, 329, 321, 330, 271, 151, + 92, 316, 424, 425, 221, 373, 170, 139, 253, 465, + 550, 544, 395, 100, 211, 217, 610, 442, 346, 347, + 348, 350, 551, 546, 611, 612, 455, 456, 25, 466, + 552, 547 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -551 +#define YYPACT_NINF -485 static const yytype_int16 yypact[] = { - -551, 46, 48, 121, -551, 58, -551, 135, -551, -551, - -551, 65, 72, -551, 147, 156, -551, -551, 112, -551, - 50, 134, 904, -551, 162, 463, 209, -551, 54, 237, - -551, 121, 245, -551, -551, -551, 58, 1666, -551, 58, - 288, -551, -551, 34, 288, 58, -551, 36, 184, 1467, - -551, 36, -551, 316, 462, 1467, 1467, 1467, 1467, 1467, - 1467, 1522, 1467, 1467, 965, 198, -551, 506, -551, -551, - -551, -551, -551, 796, -551, -551, 195, 1, -551, 199, - -551, 207, 216, 36, 221, -551, -551, -551, 226, 53, - -551, -551, 108, -551, 214, 110, 269, 214, 214, 233, - -551, -551, -551, -551, 238, -551, -551, -551, -551, -551, - -551, -551, 242, 1691, 1691, 1691, -551, 240, -551, -551, - -551, -551, -551, -551, 154, 1, 1467, 1658, 247, 246, - 290, -551, 1467, -551, -551, 419, 1691, 2004, 229, -551, - 272, 333, 1467, 258, 1691, -551, -551, 485, -551, -551, - -551, 656, -551, -551, -551, -551, -551, -551, 1577, 1522, - 2004, 255, -551, 10, -551, 51, -551, -551, 251, 2004, - 256, -551, 508, -551, 1632, 1467, 1467, 1467, 1467, -551, - 1467, 1467, 1467, -551, 1467, 1467, 1467, 1467, 1467, 1467, - 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, -551, 1192, - 552, 1467, -551, 1467, -551, -551, 1134, 1467, 1467, 1467, - -551, 1203, 58, 246, 260, 332, -551, 1838, 1838, -551, - 76, 282, -551, 1658, 344, 1691, -551, -551, -551, -551, - -551, -551, -551, 293, 58, -551, -551, 328, -551, 178, - 303, 1691, -551, 1658, -551, -551, -551, 295, 308, 1658, - 1134, -551, -551, 309, 271, 364, -551, 334, 337, -551, - -551, 330, -551, 11, 66, -551, -551, 342, -551, -551, - 397, 664, -551, -551, -551, 351, -551, -551, -551, 352, - 1467, 58, 343, 1719, -551, 349, 1691, 1691, -551, 356, - 1467, 357, 2004, 2075, -551, 2028, 692, 692, 692, 692, - -551, 692, 692, 2052, -551, 593, 593, 593, 593, -551, - -551, -551, -551, 1247, -551, -551, 22, 1302, -551, 1877, - 355, 1060, 1979, 1247, -551, -551, -551, -551, -551, -551, - 86, 229, 229, 2004, 1777, 367, 361, 359, -551, 366, - 427, 1838, 52, 29, -551, 370, -551, -551, -551, 784, - -551, 118, 379, 58, 396, 400, 401, -551, -551, 404, - 1691, 409, -551, -551, -551, -551, 1357, 1412, 1467, -551, - -551, -551, 1658, -551, 1744, 414, 109, 328, 1467, 58, - 416, 423, 1658, -551, 561, 417, 1691, 44, 364, 397, - 364, 428, 451, 421, -551, -551, 58, 397, 456, 58, - 436, 58, 438, 229, -551, 1467, 1752, 1691, -551, 24, - 171, 287, 338, -551, -551, -551, 58, 439, 229, 1467, - -551, 1907, -551, -551, 425, 435, 430, 1522, 446, 448, - 453, -551, 1467, -551, -551, 443, 1134, 1060, -551, 1838, - 479, -551, -551, -551, 58, 1805, 1838, 58, 1838, -551, - -551, 514, 161, -551, -551, 460, 454, 1838, 52, 1838, - 397, 58, 58, -551, 458, 459, -551, -551, -551, 1744, - -551, 1134, 1467, 1467, 467, -551, 1658, 472, -551, -551, - -551, -551, -551, -551, -551, 1744, -551, -551, -551, -551, - -551, 477, -551, -551, -551, 1522, 474, -551, -551, -551, - 482, -551, 500, 397, -551, -551, -551, -551, -551, -551, - -551, -551, -551, 229, 501, 1247, -551, -551, 505, 1632, - -551, 229, 1247, 1247, 1247, -551, -551, -551, 503, -551, - -551, -551, -551, 511, -551, 137, -551, -551, 518, 528, - 532, 534, 535, 530, -551, -551, 537, 533, 1838, 536, - -551, 538, -551, -551, 560, -551, 1838, -551, 551, 397, - -551, 557, -551, 1830, 151, 2004, 2004, 1467, 558, 1658, - -551, 605, 1744, 125, -551, 1060, 397, 397, -551, 75, - 360, 554, 58, 563, 357, 556, 2004, -551, -551, -551, - -551, -551, -551, -551, 1830, 58, -551, 1805, 1838, 397, - -551, 58, 161, -551, -551, -551, 58, 58, -551, -551, - -551, -551, -551, -551, 570, 78, -551, -551, 1467, 1467, - -551, 1522, 569, -551, -551, -551, -551, -551, -551, -551, - -551, 576, -551, -551, -551, -551, 578, 582, 584, 1744, - -551, -551, 1931, 1955, 1830, -551, 1830, -551, -551, -551, - -551, -551, -551 + -485, 67, 35, 55, -485, 44, -485, 64, -485, -485, + -485, 96, 38, -485, 77, 85, -485, -485, 66, -485, + 34, 84, 1059, -485, 86, 294, 147, -485, 165, 210, + -485, 55, 221, -485, -485, -485, 44, 1762, -485, 44, + 290, -485, -485, 442, 290, 44, -485, 80, 69, 1608, + -485, 80, -485, 450, 452, 1608, 1608, 1608, 1608, 1608, + 1608, 1651, 1608, 1608, 920, 157, -485, 460, -485, -485, + -485, -485, -485, 718, -485, -485, 167, 344, -485, 176, + -485, 180, 193, 80, 206, -485, -485, -485, 218, 91, + -485, -485, 76, -485, 205, 10, 260, 205, 205, 223, + -485, -485, -485, -485, 230, -485, -485, -485, -485, -485, + -485, -485, 237, 1770, 1770, 1770, -485, 236, -485, -485, + -485, -485, -485, -485, 220, 344, 1608, 990, 241, 235, + 262, -485, 1608, -485, -485, 405, 1770, 2090, 254, -485, + 297, 444, 1608, 61, 1770, -485, -485, 271, -485, -485, + -485, 671, -485, -485, -485, -485, -485, -485, 1694, 1651, + 2090, 291, -485, 181, -485, 60, -485, -485, 287, 2090, + 301, -485, 496, -485, 912, 1608, 1608, 1608, 1608, -485, + 1608, 1608, 1608, -485, 1608, 1608, 1608, 1608, 1608, 1608, + 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, -485, 1290, + 468, 1608, -485, 1608, -485, -485, 1221, 1608, 1608, 1608, + -485, 573, 44, 235, 275, 347, -485, 1301, 1301, -485, + 113, 302, -485, 990, 358, 1770, -485, -485, -485, -485, + -485, -485, -485, 316, 44, -485, -485, 340, -485, 78, + 318, 1770, -485, 990, -485, -485, -485, 307, 325, 990, + 1221, -485, -485, 324, 117, 365, -485, 343, 337, -485, + -485, 333, -485, 32, 23, -485, -485, 350, -485, -485, + 406, 1737, -485, -485, -485, 351, -485, -485, -485, 352, + 1608, 44, 354, 1796, -485, 353, 1770, 1770, -485, 359, + 1608, 357, 2090, 1928, -485, 2114, 1212, 1212, 1212, 1212, + -485, 1212, 1212, 2138, -485, 566, 566, 566, 566, -485, + -485, -485, -485, 1345, -485, -485, 31, 1400, -485, 1988, + 360, 1147, 1955, 1345, -485, -485, -485, -485, -485, -485, + 95, 254, 254, 2090, 1857, 368, 361, 371, -485, 363, + 427, 1301, 247, 51, -485, 374, -485, -485, -485, 1890, + -485, 36, 382, 44, 384, 385, 387, -485, -485, 391, + 1770, 395, -485, -485, -485, -485, 1455, 1510, 1608, -485, + -485, -485, 990, -485, 1823, 399, 135, 340, 1608, 44, + 397, 403, 990, -485, 542, 407, 1770, 278, 365, 406, + 365, 411, 364, 413, -485, -485, 44, 406, 430, 44, + 423, 44, 425, 254, -485, 1608, 1849, 1770, -485, 216, + 219, 274, 288, -485, -485, -485, 44, 426, 254, 1608, + -485, 2018, -485, -485, 414, 422, 416, 1651, 433, 434, + 436, -485, 1608, -485, -485, 439, 437, 1221, 1147, -485, + 1301, 466, -485, -485, -485, 44, 1882, 1301, 44, 1301, + -485, -485, 504, 207, -485, -485, 446, 438, 1301, 247, + 1301, 406, 44, 44, -485, 453, 455, -485, -485, -485, + 1823, -485, 1221, 1608, 1608, 467, -485, 990, 472, -485, + -485, -485, -485, -485, -485, -485, 1823, -485, -485, -485, + -485, -485, 475, -485, -485, -485, 1651, 470, -485, -485, + -485, 490, -485, 493, 406, -485, -485, -485, -485, -485, + -485, -485, -485, -485, 254, 495, 1345, -485, -485, 498, + 912, -485, 254, 1345, 1553, 1345, -485, -485, 497, -485, + -485, -485, -485, 486, -485, -485, 143, -485, -485, 501, + 502, 473, 508, 513, 505, -485, -485, 515, 503, 1301, + 511, -485, 518, -485, -485, 533, -485, 1301, -485, 522, + 406, -485, 526, -485, 1916, 144, 2090, 2090, 1608, 527, + 990, -485, -485, 1823, 39, -485, 1147, 406, 406, -485, + 315, 293, 521, 44, 548, 357, 525, -485, 2090, -485, + -485, -485, -485, -485, -485, -485, 1916, 44, -485, 1882, + 1301, 406, -485, 44, 207, -485, -485, -485, 44, 44, + -485, -485, -485, -485, -485, -485, 551, 572, -485, 1608, + 1608, -485, 1651, 550, -485, -485, -485, -485, -485, -485, + -485, 1345, -485, 558, -485, -485, -485, -485, 563, 564, + 565, 1823, 46, -485, -485, 2042, 2066, 559, 1916, -485, + 1916, -485, -485, -485, -485, -485, -485, -485, -485, -485, + 1608, 340, -485 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -551, -551, -551, -551, -551, -551, -551, -6, -551, -551, - 608, -551, -11, -551, -551, 623, -551, -134, -25, 68, - -551, -135, -121, -551, 39, -551, -551, -551, 145, 278, - -551, -551, -551, -551, -551, -551, 515, 41, -551, -551, - -551, -551, -551, -551, -551, -551, -551, 579, 493, 45, - -551, -192, 138, -246, 192, -47, 415, 200, -20, 380, - 626, -5, 449, 346, -551, 426, 95, 509, -551, -551, - -551, -551, -33, 38, -31, -18, -551, -551, -551, -551, - -551, 43, 457, -467, -551, -551, -551, -551, -551, -551, - -551, -551, 280, -126, -227, 292, -551, 302, -551, -220, - -297, 662, -551, -248, -551, -66, 18, 194, -551, -295, - -228, -289, -191, -551, -119, -403, -551, -551, -305, -551, - -32, -551, 127, -551, 362, 250, 363, 232, 90, 98, - -550, -551, -426, 241, -551, 486, -551, -551 + -485, -485, -485, -485, -485, -485, -485, -6, -485, -485, + 597, -485, -3, -485, -485, 608, -485, -131, -28, 50, + -485, -135, -106, -485, -7, -485, -485, -485, 125, -370, + -485, -485, -485, -485, -485, -485, -138, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + 665, 15, 116, -485, -190, 111, 112, -485, 164, -59, + 398, 137, 14, 367, 603, -5, 454, 432, -485, 402, + -50, 491, -485, -485, -485, -485, -36, 18, -34, -9, + -485, -485, -485, -485, -485, 257, 441, -445, -485, -485, + -485, -485, -485, -485, -485, -485, 259, -116, -218, 265, + -485, 284, -485, -217, -286, 636, -485, -237, -485, -62, + -24, 166, -485, -314, -246, -265, -177, -485, -115, -415, + -485, -485, -379, -485, -8, -485, 435, -485, 326, 225, + 327, 204, 65, 70, -484, -485, -426, 211, -485, 462, + -485, -485 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -270 + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -275 static const yytype_int16 yytable[] = { - 12, 174, 376, 359, 119, 235, 121, 240, 274, 259, - 320, 235, 323, 278, 161, 32, 109, 79, 570, 109, - 107, 235, 103, 32, 433, 131, 554, 428, 435, 375, - 385, 111, 456, 426, 111, 380, 389, 391, 128, 393, - 111, 539, 173, 164, 630, -176, 4, 400, 146, 150, - 495, 402, -208, 27, 205, 450, 496, 27, 9, -172, - 199, 417, 150, 214, 200, 216, 218, 138, 18, -175, - 388, 133, 201, 5, 9, 120, 9, -176, 9, 27, - 122, 495, 9, -172, 501, 124, -208, 496, 133, 130, - 380, -172, 507, 127, 651, 222, 652, 427, 46, 163, - 9, 457, 451, 134, 165, 617, 174, 10, 11, 28, - -174, 452, 497, 29, 102, 257, 207, 29, -208, 390, - 134, 266, 243, 10, 11, 10, 11, 10, 11, 381, - 111, 10, 11, 618, 19, 525, 111, 528, 146, 29, - 536, 164, 150, 625, 239, 325, 7, 289, 437, 10, - 11, 228, 228, 228, 438, 560, 231, 231, 231, 15, - -260, 500, 491, 502, 450, 228, -260, 150, 20, 212, - 231, 437, 648, 208, 228, 26, 636, 486, 461, 231, - 23, 164, 228, 209, 11, 9, 366, 231, 535, 228, - 619, 620, 631, 462, 231, 318, 33, 163, 578, 437, - 621, 79, 165, 637, 638, 592, -170, 349, 226, 232, - 233, 451, 228, 437, 357, 32, -260, 231, 243, 611, - 581, 515, -260, 564, 93, 331, 332, 585, 522, 363, - -170, 261, 125, 101, 10, 11, 125, 163, -170, 276, - 105, 533, 165, 367, 243, 79, 282, 235, 108, 474, - 409, 141, 411, 209, 605, 136, 568, 235, 259, 488, - 171, 228, 198, 228, 509, -147, 231, 511, 231, 291, - 430, 623, 624, 202, -229, 203, 150, 587, 589, 228, - -175, 228, 9, 204, 231, -174, 231, 228, 11, 206, - 583, 35, 231, 215, 634, 37, -259, 219, 403, 164, - 220, 223, -259, 234, 209, 112, 249, 262, 418, 228, - 47, 48, 9, 250, 231, 285, 79, 51, 273, 353, - 361, 410, -171, 286, 228, 228, 412, 622, 287, 231, - 231, 10, 11, -229, 379, 354, 369, 453, 345, -229, - 9, 479, 358, 481, 355, 356, -171, 61, 349, 614, - 519, 360, -259, 362, -171, 163, 482, 254, -259, 64, - 165, 10, 11, 364, 257, 368, 397, 243, 374, 477, - 372, 378, 266, -169, 489, 143, 506, 243, 408, 111, - 529, 414, 415, 117, 331, 332, 263, 111, 380, 10, - 11, 111, 264, 382, 146, -173, 150, -169, 228, 384, - 394, 265, 386, 231, 392, -169, 10, 11, 405, 164, - 228, 150, 480, 399, 401, 231, 413, 483, 416, -173, - 228, 432, 419, 513, 228, 231, 444, -173, 445, 231, - 446, 79, 79, 447, 448, 458, 479, 521, 481, 349, - 541, 463, 548, 254, 228, 228, 235, 453, 612, 231, - 231, 482, 479, 453, 481, 408, 561, 349, 466, 117, - 117, 117, 467, 468, 469, 163, 79, 482, 449, 470, - 165, 243, 94, 117, 485, 254, 460, 164, 255, 379, - 95, 494, 117, 490, 96, 493, 9, 256, 503, 505, - 117, 508, 10, 11, 97, 98, 510, 117, 512, 520, - 524, 226, 514, 526, 263, 527, 530, 480, 531, 9, - 264, 534, 483, 532, 228, 340, 519, 553, 563, 231, - 117, 147, 555, 480, 10, 11, 556, 99, 483, 567, - 9, 569, 9, 163, 462, 10, 11, 572, 165, 479, - 574, 481, 576, 210, 210, 277, 210, 210, 152, 153, - 154, 155, 156, 157, 482, 166, 167, 228, 10, 11, - 577, 580, 231, 590, 243, 172, 537, 582, 288, 117, - 79, 117, 544, 547, 529, 552, 9, 150, 593, 10, - 11, 10, 11, 591, 557, 254, 559, 117, 594, 117, - 349, -151, 541, 595, -152, 117, 548, 453, 596, 597, - 601, 349, 349, 164, 600, 598, 479, 228, 481, 602, - 480, 317, 231, 604, 584, 483, 606, 117, 613, 615, - 255, 482, 626, 628, 629, 10, 11, 177, 137, 117, - 639, 437, 117, 117, 10, 11, 644, 185, 645, 106, - 160, 189, 646, 169, 647, 66, 194, 195, 196, 197, - 627, 152, 156, 579, 640, 487, 641, 272, 370, 163, - 344, 404, 588, 37, 165, 123, 344, 344, 284, 371, - 352, 37, 504, 112, 475, 599, 492, 480, 47, 48, - 9, 112, 483, 603, 91, 51, 47, 48, 9, 573, - 538, 635, 224, 51, 562, 632, 442, 443, 351, 558, - 224, 0, 0, 0, 0, 0, 117, 0, 0, 114, - 0, 0, 0, 0, 0, 225, 0, 114, 117, 0, - 117, 280, 0, 225, 544, 633, 177, 64, 117, 10, - 11, 281, 117, 0, 0, 64, 185, 10, 11, 396, + 12, 119, 161, 121, 272, 174, 359, 488, 274, 436, + 502, 240, 385, 376, 323, 32, 278, 79, 508, 259, + 235, 393, 103, 32, 320, 138, 235, 555, 107, 400, + 540, 111, 375, 402, 111, 433, 235, 27, 128, 173, + 111, 571, 426, 417, 619, 389, 391, 380, 146, 150, + 109, 428, 164, 109, 457, 120, 380, 435, 9, 131, + 5, -213, 150, 226, 232, 233, 653, 4, 9, 212, + 152, 153, 154, 155, 156, 157, 390, 166, 167, 163, + 7, 207, 561, 366, 11, 9, 261, 214, 15, 216, + 218, 388, 205, 28, 276, -213, 462, 29, 20, 18, + 19, 282, 239, 222, 620, 621, 427, 10, 11, 23, + 174, 463, 632, 325, 622, 133, 27, 10, 11, -179, + -234, 273, 243, 458, 291, 579, 133, -213, 618, 26, + 111, 228, 228, 228, 10, 11, 111, 9, 146, 381, + 136, 208, 150, 367, 289, 228, 33, 134, 93, 257, + 164, 209, 537, 209, 228, 266, 124, 438, 134, 526, + 130, 528, 228, 439, 658, 492, 659, 150, 27, 228, + 501, 101, 503, 152, 156, 361, 29, 163, 638, -234, + 379, 607, 633, 331, 332, -234, 10, 11, 141, 9, + 164, 369, 228, 639, 640, 318, 652, 438, 624, 625, + 536, 79, 582, 487, 125, 438, 438, 349, 125, 586, + 451, 594, 613, 105, 357, 32, -181, 163, 243, 171, + 204, 397, 636, 516, 108, 102, 206, -265, 29, 363, + 523, 9, -265, 408, 198, 565, 414, 415, 10, 11, + -180, 228, -152, 228, 243, 79, 202, 409, -181, 411, + 451, -177, 203, 475, -175, 533, 403, 452, 430, 228, + 569, 228, 235, 489, 510, -180, 418, 228, 259, -264, + 512, 9, 235, 584, -264, -177, 150, -179, -175, 11, + 10, 11, -265, -177, 215, 496, -175, 219, -265, 228, + 497, 662, 220, 35, 122, 9, 223, 452, 37, 234, + 249, 410, 250, 94, 228, 228, 453, 112, 164, -176, + 408, 95, 47, 48, 9, 96, 79, 647, 165, 51, + 10, 11, 496, -174, -264, 97, 98, 497, -178, 209, + -264, 277, 262, -176, 353, 163, 495, 454, 480, 623, + 482, -176, 331, 332, 10, 11, 498, -174, 349, 61, + 354, 285, -178, 616, 520, -174, 226, 515, 99, 286, + -178, 64, 358, 10, 11, 483, 360, 243, 529, 478, + 231, 231, 231, 287, 490, 364, 362, 243, 228, 111, + 368, 514, 372, 626, 231, 374, 378, 111, 254, 380, + 228, 111, 481, 231, 146, 522, 150, 631, 257, 384, + 228, 231, 382, 199, 228, 386, 266, 200, 231, 394, + 507, 150, 392, 399, 401, 201, 165, 263, 164, 405, + 413, 416, 419, 264, 228, 228, 432, 445, 446, 254, + 448, 231, 79, 79, 480, 449, 482, 10, 11, 459, + 349, 542, 447, 549, 464, 163, 467, 468, 454, 469, + 480, 470, 482, 614, 454, 471, 165, 562, 349, 486, + 379, 483, 235, 491, 255, 509, 9, 79, 254, 117, + 585, 504, 243, 256, 9, 494, 9, 483, 10, 11, + 231, 506, 231, 511, 9, 513, 521, 164, 481, 525, + 527, 434, 9, 530, 531, 228, 532, 263, 231, 534, + 231, 127, 340, 264, 481, 535, 231, 554, 556, 143, + 557, 147, 265, 564, 163, 10, 11, 10, 11, 172, + 9, 520, 661, 10, 11, 10, 11, 317, 231, 568, + 463, 570, -156, 10, 11, 573, 575, 480, 228, 482, + 412, 10, 11, 231, 231, 117, 117, 117, 210, 210, + 577, 210, 210, 578, 235, 581, 288, 592, 593, 117, + 583, 595, 596, 529, 483, 243, 254, 597, 117, 10, + 11, 79, -157, 598, 165, 600, 117, 599, 150, 602, + 603, 334, 604, 117, 606, 608, 642, 615, 228, 627, + 335, 481, 349, 630, 542, 336, 337, 338, 549, 454, + 177, 255, 339, 349, 349, 480, 117, 482, 629, 340, + 185, 641, 438, 164, 189, 10, 11, 231, 648, 194, + 195, 196, 197, 649, 650, 651, 341, 657, 106, 231, + 66, 484, 483, 628, 580, 654, 590, 591, 342, 231, + 163, 370, 123, 231, 343, 371, 345, 11, 404, 493, + 284, 505, 355, 356, 352, 117, 476, 117, 91, 481, + 443, 444, 574, 231, 231, 344, 539, 563, 637, 634, + 559, 344, 344, 117, 351, 117, 0, 0, 0, 37, + 0, 117, 0, 0, 165, 0, 0, 0, 112, 0, + 0, 0, 0, 47, 48, 9, 0, 0, 0, 0, + 51, 0, 0, 117, 0, 0, 0, 224, 0, 0, + 0, 0, 0, 0, 137, 117, 0, 0, 117, 117, + 0, 0, 175, -274, 114, 0, 160, 484, 0, 169, + 225, 0, 0, 0, 231, 0, 280, 0, 0, 0, + 0, 0, 64, 484, 10, 11, 281, 0, 0, 0, + 0, 176, 177, 165, 178, 179, 180, 181, 182, 0, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 0, 450, 231, 0, 0, + 0, 0, 0, -274, 461, 0, 0, 0, 344, 0, + 0, 0, 117, -274, 0, 344, 0, 0, 0, 0, + 0, 0, 0, 344, 117, 0, 117, 0, 0, 0, + 0, 0, 0, 0, 117, 0, 0, 0, 117, 0, + 0, 0, 0, 0, 0, 0, 0, 231, 0, 0, + 484, 0, 0, 0, 0, 0, 0, 0, 117, 117, + 292, 293, 294, 295, 0, 296, 297, 298, 0, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 0, 160, 0, 319, 0, 322, 0, + 0, 0, 137, 137, 333, 538, 0, 0, 0, 165, + 0, 545, 548, 0, 553, 0, 0, 0, 0, 0, + 0, 0, 0, 558, 344, 560, 0, 0, 484, 0, + 543, 344, 117, 344, 0, 0, 0, 0, 0, 117, + 0, 0, 344, 0, 344, 0, 0, 0, 117, 0, + 37, 0, 0, 35, 0, 0, 0, 0, 37, 112, + 0, 168, 0, 0, 47, 48, 9, 112, 0, 0, + 0, 51, 47, 48, 9, 137, 0, 0, 224, 51, + 0, 0, 117, 0, 0, 137, 55, 0, 0, 0, + 0, 0, 0, 0, 0, 114, 0, 0, 0, 56, + 57, 225, 58, 59, 0, 0, 60, 290, 421, 61, + 0, 0, 160, 64, 601, 10, 11, 281, 421, 62, + 63, 64, 605, 10, 11, 0, 0, 0, 37, 0, + 0, 241, 117, 344, 0, 117, 0, 112, 0, 0, + 0, 344, 47, 48, 9, 0, 0, 0, 344, 51, + 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, + 0, 137, 137, 0, 545, 635, 0, 0, 0, 0, + 0, 0, 0, 114, 0, 0, 0, 0, 0, 225, + 344, 0, 0, 543, 344, 0, 0, 0, 0, -2, + 34, 64, 35, 10, 11, 36, 0, 37, 38, 39, + 137, 0, 40, 117, 41, 42, 43, 44, 45, 46, + 0, 47, 48, 9, 137, 0, 49, 50, 51, 52, + 53, 54, 160, 0, 0, 55, 0, 169, 0, 0, + 0, 0, 344, 0, 344, 0, 0, 0, 56, 57, + 0, 58, 59, 0, 0, 60, 0, 0, 61, 0, + 0, -24, 0, 0, 0, 0, 0, 0, 62, 63, + 64, 0, 10, 11, 0, 0, 0, 0, 566, 567, + 0, 0, 0, 0, 0, 0, 0, 0, 324, 0, + 35, 0, 0, 36, -249, 37, 38, 39, 0, -249, + 40, 160, 41, 42, 112, 44, 45, 46, 0, 47, + 48, 9, 0, 0, 49, 50, 51, 52, 53, 54, + 0, 421, 0, 55, 0, 0, 0, 0, 421, 588, + 421, 0, 0, 0, 0, 0, 56, 57, 0, 58, + 59, 0, 0, 60, 0, 0, 61, 0, 0, -249, + 0, 0, 0, 0, 325, -249, 62, 63, 64, 0, + 10, 11, 324, 0, 35, 0, 0, 36, 0, 37, + 38, 39, 0, 0, 40, 0, 41, 42, 112, 44, + 45, 46, 0, 47, 48, 9, 177, 0, 49, 50, + 51, 52, 53, 54, 0, 0, 185, 55, 0, 0, 189, 190, 191, 192, 193, 194, 195, 196, 197, 0, - 0, 0, 117, 117, 292, 293, 294, 295, 0, 296, - 297, 298, 0, 299, 300, 301, 302, 303, 304, 305, - 306, 307, 308, 309, 310, 311, 312, 0, 160, 0, - 319, 0, 322, 344, 0, 0, 137, 137, 333, 0, - 344, 334, 0, 0, 0, 459, 0, 0, 344, 0, - 175, 335, 0, 0, -269, 0, 336, 337, 338, 0, - 0, 0, 0, 339, 0, 117, 0, 0, 0, 0, - 340, 0, 117, 0, 0, 0, 0, 0, 0, 176, - 177, 117, 178, 179, 180, 181, 182, 341, 183, 184, - 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, - 195, 196, 197, 0, 0, 343, 0, 0, 11, 137, - 0, -269, 0, 0, 0, 117, 0, 0, 0, 137, - 0, -269, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 344, 0, - 0, 0, 421, 0, 542, 344, 160, 344, 0, 0, - 0, 0, 421, 0, -2, 34, 344, 35, 344, 36, - 0, 37, 0, 38, 39, 117, 0, 40, 117, 41, - 42, 43, 44, 45, 46, 0, 47, 48, 9, 0, - 0, 49, 50, 51, 52, 53, 54, 0, 0, 0, - 55, 0, 0, 0, 0, 137, 137, 0, 0, 0, - 0, 0, 0, 56, 57, 0, 58, 59, 0, 0, - 60, 0, 0, 61, 0, 0, -24, 0, 35, 0, - 0, 0, 37, 62, 63, 64, 168, 10, 11, 0, - 0, 0, 112, 0, 137, 117, 0, 47, 48, 9, - 0, 0, 0, 0, 51, 0, 0, 344, 137, 0, - 0, 55, 0, 0, 0, 344, 160, 0, 0, 0, - 0, 169, 344, 0, 56, 57, 0, 58, 59, 0, - 0, 60, 0, 0, 61, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 62, 63, 64, 0, 10, 11, - 0, 0, 0, 344, 0, 0, 542, 344, 0, 0, - 0, 565, 566, 0, 0, 0, 0, 0, 0, 0, - 0, 324, 0, 35, 0, 36, -244, 37, 0, 38, - 39, 0, -244, 40, 160, 41, 42, 112, 44, 45, - 46, 0, 47, 48, 9, 0, 0, 49, 50, 51, - 52, 53, 54, 344, 421, 344, 55, 0, 0, 0, - 0, 421, 586, 421, 0, 0, 0, 0, 0, 56, - 57, 0, 58, 59, 0, 0, 60, 0, 0, 61, - 0, 0, -244, 0, 0, 0, 0, 325, -244, 62, - 63, 64, 0, 10, 11, 324, 0, 35, 0, 36, - 0, 37, 0, 38, 39, 0, 0, 40, 0, 41, - 42, 112, 44, 45, 46, 0, 47, 48, 9, 0, - 0, 49, 50, 51, 52, 53, 54, 0, 0, 0, - 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 56, 57, 0, 58, 59, 0, 0, - 60, 0, 0, 61, 0, 35, -244, 642, 643, 37, - 160, 325, -244, 62, 63, 64, 0, 10, 11, 112, - 334, 0, 0, 0, 47, 48, 9, 0, 0, 0, - 335, 51, 0, 0, 0, 336, 337, 338, 158, 0, - 0, 0, 339, 0, 0, 0, 0, 0, 0, 340, - 0, 56, 57, 0, 58, 159, 0, 0, 60, 0, - 35, 61, 314, 0, 37, 0, 341, 0, 0, 0, - 0, 62, 63, 64, 112, 10, 11, 0, 342, 47, - 48, 9, 0, 0, 343, 0, 51, 11, 0, 0, - 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 35, 61, 0, 0, 37, - 0, 0, 0, 0, 420, 0, 62, 63, 64, 112, - 10, 11, 0, 0, 47, 48, 9, 0, 0, 0, - 0, 51, 0, 429, 0, 0, 0, 0, 158, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 56, 57, 0, 58, 159, 0, 0, 60, 0, - 35, 61, 0, 0, 37, 0, 0, 0, 0, 0, - 0, 62, 63, 64, 112, 10, 11, 0, 0, 47, - 48, 9, 0, 472, 0, 0, 51, 0, 0, 0, - 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 35, 61, 0, 0, 37, - 0, 0, 0, 0, 0, 0, 62, 63, 64, 112, - 10, 11, 0, 0, 47, 48, 9, 0, 473, 0, - 0, 51, 0, 0, 0, 0, 0, 0, 55, 0, + 56, 57, 0, 58, 59, 0, 0, 60, 0, 0, + 61, 0, 0, -249, 645, 646, 0, 160, 325, -249, + 62, 63, 64, 35, 10, 11, 421, 0, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 112, 0, 334, + 0, 0, 47, 48, 9, 0, 0, 0, 335, 51, + 0, 0, 0, 336, 337, 338, 158, 0, 0, 0, + 339, 0, 0, 0, 0, 0, 0, 340, 0, 56, + 57, 0, 58, 159, 0, 0, 60, 0, 35, 61, + 314, 0, 0, 37, 341, 0, 0, 0, 0, 62, + 63, 64, 112, 10, 11, 0, 0, 47, 48, 9, + 0, 0, 343, 0, 51, 11, 0, 0, 0, 0, + 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 57, 0, 58, 59, 0, + 0, 60, 0, 35, 61, 0, 0, 0, 37, 0, + 0, 0, 420, 0, 62, 63, 64, 112, 10, 11, + 0, 0, 47, 48, 9, 0, 0, 0, 0, 51, + 0, 429, 0, 0, 0, 0, 158, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, + 57, 0, 58, 159, 0, 0, 60, 0, 35, 61, + 0, 0, 0, 37, 0, 0, 0, 0, 0, 62, + 63, 64, 112, 10, 11, 0, 0, 47, 48, 9, + 0, 473, 0, 0, 51, 0, 0, 0, 0, 0, + 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 57, 0, 58, 59, 0, + 0, 60, 0, 35, 61, 0, 0, 0, 37, 0, + 0, 0, 0, 0, 62, 63, 64, 112, 10, 11, + 0, 0, 47, 48, 9, 0, 474, 0, 0, 51, + 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 56, + 57, 37, 58, 59, 0, 0, 60, 0, 0, 61, + 112, 0, 0, 0, 0, 47, 48, 9, 0, 62, + 63, 64, 51, 10, 11, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 56, 57, 0, 58, 59, 0, 0, 60, 0, - 35, 61, 0, 0, 37, 0, 0, 0, 0, 0, - 0, 62, 63, 64, 112, 10, 11, 0, 0, 47, - 48, 9, 0, 0, 0, 0, 51, 0, 0, 0, - 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 35, 61, 0, 0, 37, - 0, 0, 0, 0, 0, 0, 62, 63, 64, 112, - 10, 11, 0, 0, 47, 48, 9, 0, 0, 0, - 0, 51, 0, 0, 0, 0, 0, 0, 158, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 56, 57, 0, 58, 159, 0, 0, 60, 0, - 35, 61, 0, 0, 283, 0, 0, 0, 0, 0, - 0, 62, 63, 64, 112, 10, 11, 0, 0, 47, - 48, 9, 0, 0, 0, 0, 51, 0, 0, 0, - 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 0, 61, 0, 0, 37, - 0, 0, 0, 0, 0, 0, 62, 63, 64, 112, - 10, 11, 0, 0, 47, 48, 9, 0, 0, 0, - 0, 51, 0, 0, 0, 37, 0, 0, 224, 241, - 0, 0, 0, 37, 0, 112, 0, 0, 0, 0, - 47, 48, 9, 112, 0, 114, 0, 51, 47, 48, - 9, 225, 0, 0, 224, 51, 0, 290, 37, 0, - 0, 0, 113, 64, 0, 10, 11, 281, 112, 0, - 0, 114, 0, 47, 48, 9, 0, 225, 0, 114, - 51, 0, 0, 0, 0, 115, 37, 224, 0, 64, - 0, 10, 11, 0, 0, 0, 112, 64, 0, 10, - 11, 47, 48, 9, 114, 0, 0, 0, 51, 0, - 225, 37, 0, 0, 0, 406, 0, 0, 0, 283, - 0, 112, 64, 0, 10, 11, 47, 48, 9, 112, - 0, 0, 114, 51, 47, 48, 9, 0, 407, 0, - 224, 51, 0, 0, 334, 0, 0, 0, 224, 0, - 64, 0, 10, 11, 335, 0, 0, 114, 0, 336, - 337, 338, 0, 476, 0, 114, 339, 0, 0, 0, - 0, 225, 334, 439, 0, 64, 0, 10, 11, 0, - 0, 0, 335, 64, 0, 10, 11, 336, 337, 540, - 341, 0, 0, 0, 339, 0, 440, 334, 0, 0, - 0, 340, 0, 0, 0, 334, 0, 335, 343, 0, - 0, 11, 336, 337, 338, 335, 0, 0, 341, 339, - 336, 337, 338, 0, 0, 0, 340, 339, 0, 0, - 0, 0, 0, 0, 340, 0, 343, 0, 10, 11, - 0, 0, 0, 341, 0, 0, 0, 0, 0, 607, - 0, 341, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 343, 0, 0, 11, 0, 0, 0, 0, 343, - 176, 177, 11, 178, 0, 180, 181, 182, 0, 0, - 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, - 194, 195, 196, 197, 0, 0, 0, 0, 0, 0, - 176, 177, 0, 178, 0, 180, 181, 182, 0, 431, - 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, - 194, 195, 196, 197, 176, 177, 0, 178, 0, 180, - 181, 182, 0, 523, 184, 185, 186, 187, 188, 189, - 190, 191, 192, 193, 194, 195, 196, 197, 176, 177, - 0, 178, 0, 180, 181, 182, 0, 649, 184, 185, + 0, 0, 56, 57, 0, 58, 59, 0, 0, 60, + 0, 35, 61, 0, 0, 0, 37, 0, 0, 0, + 587, 0, 62, 63, 64, 112, 10, 11, 0, 0, + 47, 48, 9, 0, 0, 0, 0, 51, 0, 0, + 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 56, 57, 37, + 58, 59, 0, 0, 60, 0, 0, 61, 112, 0, + 0, 0, 0, 47, 48, 9, 0, 62, 63, 64, + 51, 10, 11, 0, 0, 0, 0, 158, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 56, 57, 283, 58, 159, 0, 0, 60, 0, 0, + 61, 112, 0, 0, 0, 0, 47, 48, 9, 0, + 62, 63, 64, 51, 10, 11, 0, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 56, 57, 37, 58, 59, 0, 0, + 60, 0, 0, 61, 112, 0, 0, 0, 0, 47, + 48, 9, 0, 62, 63, 64, 51, 10, 11, 0, + 37, 0, 0, 224, 0, 0, 0, 0, 37, 112, + 0, 0, 0, 0, 47, 48, 9, 112, 0, 0, + 114, 51, 47, 48, 9, 0, 225, 0, 113, 51, + 0, 0, 0, 0, 37, 0, 224, 0, 64, 0, + 10, 11, 396, 112, 0, 114, 0, 0, 47, 48, + 9, 115, 0, 114, 0, 51, 0, 0, 0, 225, + 0, 37, 406, 64, 0, 10, 11, 0, 0, 0, + 112, 64, 0, 10, 11, 47, 48, 9, 0, 114, + 0, 0, 51, 0, 0, 407, 0, 283, 0, 224, + 0, 0, 0, 0, 0, 334, 112, 64, 0, 10, + 11, 47, 48, 9, 335, 0, 114, 0, 51, 336, + 337, 338, 477, 0, 0, 224, 339, 0, 0, 0, + 334, 0, 0, 440, 64, 0, 10, 11, 334, 335, + 0, 460, 114, 0, 336, 337, 541, 335, 225, 0, + 341, 339, 336, 337, 338, 0, 441, 0, 340, 339, + 64, 0, 10, 11, 334, 0, 340, 0, 343, 0, + 0, 11, 0, 335, 0, 341, 0, 0, 336, 337, + 338, 0, 0, 341, 0, 339, 0, 0, 0, 0, + 0, 0, 340, 343, 0, 10, 11, 0, 0, 0, + 0, 343, 177, 0, 11, 0, 180, 181, 182, 341, + 0, 184, 185, 186, 187, 609, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 0, 0, 343, 176, 177, + 11, 178, 0, 180, 181, 182, 0, 0, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, - 196, 197, 176, 177, 0, 178, 0, 180, 181, 182, - 0, 650, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 0, 176, 177, 434, - 178, 0, 180, 181, 182, 0, 0, 184, 185, 186, + 196, 197, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 177, 0, 178, 0, 180, 181, 182, 0, + 434, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 0, 0, 0, 0, 0, + 0, 176, 177, 0, 178, 0, 180, 181, 182, 0, + 431, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 176, 177, 0, 178, 0, + 180, 181, 182, 0, 524, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 176, + 177, 0, 178, 0, 180, 181, 182, 0, 655, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 176, 177, 0, 178, 0, 180, 181, + 182, 0, 656, 184, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 176, 177, 0, + 0, 0, 180, 181, 182, 0, 0, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 176, 177, 0, 0, 0, 180, 181, 182, 0, - 0, 184, 185, 186, 187, 188, 189, 190, 191, 192, - 193, 194, 195, 196, 197, 176, 177, 0, 0, 0, - 180, 181, 182, 0, 0, 184, 185, 186, 187, 0, - 189, 190, 191, 192, 193, 194, 195, 196, 197, 177, - 0, 0, 0, 180, 181, 182, 0, 0, 184, 185, - 186, 187, 0, 189, 190, 191, 192, 193, 194, 195, - 196, 197 + 0, 184, 185, 186, 187, 0, 189, 190, 191, 192, + 193, 194, 195, 196, 197 }; -#define yypact_value_is_default(yystate) \ - ((yystate) == (-551)) - -#define yytable_value_is_error(yytable_value) \ - YYID (0) - static const yytype_int16 yycheck[] = { - 5, 67, 250, 223, 37, 126, 37, 126, 143, 135, - 201, 132, 204, 147, 61, 20, 36, 22, 485, 39, - 31, 142, 28, 28, 321, 45, 452, 316, 323, 249, - 258, 36, 3, 11, 39, 24, 263, 264, 43, 267, - 45, 444, 67, 61, 594, 35, 0, 275, 53, 54, - 6, 279, 1, 3, 1, 3, 12, 3, 24, 35, - 59, 289, 67, 95, 63, 97, 98, 49, 3, 59, - 59, 35, 71, 25, 24, 37, 24, 67, 24, 3, - 37, 6, 24, 59, 389, 40, 35, 12, 35, 44, - 24, 67, 397, 59, 644, 106, 646, 75, 20, 61, - 24, 72, 50, 67, 61, 572, 172, 73, 74, 59, - 59, 59, 68, 63, 60, 135, 8, 63, 67, 53, - 67, 141, 127, 73, 74, 73, 74, 73, 74, 255, - 135, 73, 74, 8, 62, 424, 141, 426, 143, 63, - 437, 159, 147, 68, 126, 67, 25, 172, 62, 73, - 74, 113, 114, 115, 68, 460, 113, 114, 115, 24, - 6, 388, 382, 390, 3, 127, 12, 172, 21, 59, - 127, 62, 639, 65, 136, 63, 602, 68, 60, 136, - 24, 199, 144, 75, 74, 24, 8, 144, 436, 151, - 65, 66, 595, 75, 151, 200, 62, 159, 503, 62, - 75, 206, 159, 606, 607, 68, 35, 212, 113, 114, - 115, 50, 174, 62, 220, 220, 62, 174, 223, 68, - 515, 413, 68, 471, 62, 207, 208, 522, 420, 234, - 59, 136, 40, 24, 73, 74, 44, 199, 67, 144, - 3, 432, 199, 65, 249, 250, 151, 368, 3, 368, - 283, 51, 283, 75, 559, 71, 476, 378, 384, 378, - 62, 223, 67, 225, 399, 66, 223, 401, 225, 174, - 317, 576, 577, 66, 3, 59, 281, 523, 524, 241, - 59, 243, 24, 83, 241, 59, 243, 249, 74, 89, - 518, 3, 249, 24, 599, 7, 6, 64, 280, 317, - 62, 59, 12, 63, 75, 17, 59, 35, 290, 271, - 22, 23, 24, 67, 271, 60, 321, 29, 60, 59, - 225, 283, 35, 72, 286, 287, 283, 575, 72, 286, - 287, 73, 74, 62, 63, 3, 241, 342, 211, 68, - 24, 374, 60, 374, 217, 218, 59, 59, 353, 569, - 416, 7, 62, 60, 67, 317, 374, 24, 68, 71, - 317, 73, 74, 35, 384, 62, 271, 372, 60, 374, - 75, 62, 392, 35, 379, 59, 396, 382, 283, 384, - 427, 286, 287, 37, 366, 367, 53, 392, 24, 73, - 74, 396, 59, 59, 399, 35, 401, 59, 360, 62, - 3, 68, 72, 360, 62, 67, 73, 74, 65, 427, - 372, 416, 374, 62, 62, 372, 67, 374, 62, 59, - 382, 66, 65, 405, 386, 382, 59, 67, 67, 386, - 71, 436, 437, 67, 7, 65, 469, 419, 469, 444, - 445, 62, 447, 24, 406, 407, 567, 452, 567, 406, - 407, 469, 485, 458, 485, 360, 461, 462, 62, 113, - 114, 115, 62, 62, 60, 427, 471, 485, 341, 60, - 427, 476, 9, 127, 60, 24, 349, 495, 59, 63, - 17, 386, 136, 60, 21, 68, 24, 68, 60, 68, - 144, 35, 73, 74, 31, 32, 60, 151, 60, 60, - 75, 406, 407, 68, 53, 75, 60, 469, 60, 24, - 59, 68, 469, 60, 476, 36, 582, 3, 60, 476, - 174, 59, 62, 485, 73, 74, 72, 64, 485, 62, - 24, 59, 24, 495, 75, 73, 74, 60, 495, 572, - 66, 572, 60, 94, 95, 60, 97, 98, 55, 56, - 57, 58, 59, 60, 572, 62, 63, 519, 73, 74, - 60, 60, 519, 60, 569, 59, 439, 62, 60, 223, - 575, 225, 445, 446, 621, 448, 24, 582, 60, 73, - 74, 73, 74, 72, 457, 24, 459, 241, 60, 243, - 595, 59, 597, 59, 59, 249, 601, 602, 68, 62, - 62, 606, 607, 621, 68, 72, 639, 569, 639, 49, - 572, 59, 569, 62, 519, 572, 59, 271, 60, 14, - 59, 639, 68, 60, 68, 73, 74, 34, 49, 283, - 60, 62, 286, 287, 73, 74, 60, 44, 60, 31, - 61, 48, 60, 64, 60, 22, 53, 54, 55, 56, - 582, 158, 159, 508, 615, 377, 615, 142, 243, 621, - 211, 281, 524, 7, 621, 39, 217, 218, 159, 243, - 213, 7, 392, 17, 372, 548, 384, 639, 22, 23, - 24, 17, 639, 556, 22, 29, 22, 23, 24, 495, - 440, 601, 36, 29, 462, 597, 334, 334, 212, 458, - 36, -1, -1, -1, -1, -1, 360, -1, -1, 53, - -1, -1, -1, -1, -1, 59, -1, 53, 372, -1, - 374, 65, -1, 59, 597, 598, 34, 71, 382, 73, - 74, 75, 386, -1, -1, 71, 44, 73, 74, 75, + 5, 37, 61, 37, 142, 67, 223, 377, 143, 323, + 389, 126, 258, 250, 204, 20, 147, 22, 397, 135, + 126, 267, 28, 28, 201, 49, 132, 453, 31, 275, + 445, 36, 249, 279, 39, 321, 142, 3, 43, 67, + 45, 486, 11, 289, 5, 263, 264, 24, 53, 54, + 36, 316, 61, 39, 3, 37, 24, 322, 24, 45, + 25, 1, 67, 113, 114, 115, 20, 0, 24, 59, + 55, 56, 57, 58, 59, 60, 53, 62, 63, 61, + 25, 5, 461, 5, 74, 24, 136, 95, 24, 97, + 98, 59, 1, 59, 144, 35, 60, 63, 21, 3, + 62, 151, 126, 106, 65, 66, 75, 73, 74, 24, + 172, 75, 596, 67, 75, 35, 3, 73, 74, 59, + 3, 60, 127, 72, 174, 504, 35, 67, 573, 63, + 135, 113, 114, 115, 73, 74, 141, 24, 143, 255, + 71, 65, 147, 65, 172, 127, 62, 67, 62, 135, + 159, 75, 438, 75, 136, 141, 40, 62, 67, 424, + 44, 426, 144, 68, 648, 382, 650, 172, 3, 151, + 388, 24, 390, 158, 159, 225, 63, 159, 604, 62, + 63, 560, 597, 207, 208, 68, 73, 74, 51, 24, + 199, 241, 174, 608, 609, 200, 641, 62, 577, 578, + 437, 206, 516, 68, 40, 62, 62, 212, 44, 523, + 3, 68, 68, 3, 220, 220, 35, 199, 223, 62, + 83, 271, 601, 413, 3, 60, 89, 7, 63, 234, + 420, 24, 12, 283, 67, 472, 286, 287, 73, 74, + 59, 223, 66, 225, 249, 250, 66, 283, 67, 283, + 3, 35, 59, 368, 35, 432, 280, 50, 317, 241, + 477, 243, 368, 378, 399, 59, 290, 249, 384, 7, + 401, 24, 378, 519, 12, 59, 281, 59, 59, 74, + 73, 74, 62, 67, 24, 7, 67, 64, 68, 271, + 12, 661, 62, 3, 37, 24, 59, 50, 8, 63, + 59, 283, 67, 9, 286, 287, 59, 17, 317, 35, + 360, 17, 22, 23, 24, 21, 321, 631, 61, 29, + 73, 74, 7, 35, 62, 31, 32, 12, 35, 75, + 68, 60, 35, 59, 59, 317, 386, 342, 374, 576, + 374, 67, 366, 367, 73, 74, 68, 59, 353, 59, + 3, 60, 59, 570, 416, 67, 406, 407, 64, 72, + 67, 71, 60, 73, 74, 374, 8, 372, 427, 374, + 113, 114, 115, 72, 379, 35, 60, 382, 360, 384, + 62, 405, 75, 68, 127, 60, 62, 392, 24, 24, + 372, 396, 374, 136, 399, 419, 401, 587, 384, 62, + 382, 144, 59, 59, 386, 72, 392, 63, 151, 3, + 396, 416, 62, 62, 62, 71, 159, 53, 427, 65, + 67, 62, 65, 59, 406, 407, 66, 59, 67, 24, + 67, 174, 437, 438, 470, 8, 470, 73, 74, 65, + 445, 446, 71, 448, 62, 427, 62, 62, 453, 62, + 486, 60, 486, 568, 459, 60, 199, 462, 463, 60, + 63, 470, 568, 60, 59, 35, 24, 472, 24, 37, + 520, 60, 477, 68, 24, 68, 24, 486, 73, 74, + 223, 68, 225, 60, 24, 60, 60, 496, 470, 75, + 68, 75, 24, 60, 60, 477, 60, 53, 241, 60, + 243, 59, 36, 59, 486, 68, 249, 3, 62, 59, + 72, 59, 68, 60, 496, 73, 74, 73, 74, 59, + 24, 583, 660, 73, 74, 73, 74, 59, 271, 62, + 75, 59, 59, 73, 74, 60, 66, 573, 520, 573, + 283, 73, 74, 286, 287, 113, 114, 115, 94, 95, + 60, 97, 98, 60, 660, 60, 60, 60, 72, 127, + 62, 60, 60, 622, 573, 570, 24, 59, 136, 73, + 74, 576, 59, 68, 317, 72, 144, 62, 583, 68, + 62, 8, 49, 151, 62, 59, 14, 60, 570, 68, + 17, 573, 597, 68, 599, 22, 23, 24, 603, 604, + 34, 59, 29, 608, 609, 641, 174, 641, 60, 36, + 44, 60, 62, 622, 48, 73, 74, 360, 60, 53, + 54, 55, 56, 60, 60, 60, 53, 68, 31, 372, + 22, 374, 641, 583, 509, 642, 525, 525, 65, 382, + 622, 243, 39, 386, 71, 243, 211, 74, 281, 384, + 159, 392, 217, 218, 213, 223, 372, 225, 22, 641, + 334, 334, 496, 406, 407, 211, 441, 463, 603, 599, + 459, 217, 218, 241, 212, 243, -1, -1, -1, 8, + -1, 249, -1, -1, 427, -1, -1, -1, 17, -1, + -1, -1, -1, 22, 23, 24, -1, -1, -1, -1, + 29, -1, -1, 271, -1, -1, -1, 36, -1, -1, + -1, -1, -1, -1, 49, 283, -1, -1, 286, 287, + -1, -1, 4, 5, 53, -1, 61, 470, -1, 64, + 59, -1, -1, -1, 477, -1, 65, -1, -1, -1, + -1, -1, 71, 486, 73, 74, 75, -1, -1, -1, + -1, 33, 34, 496, 36, 37, 38, 39, 40, -1, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, -1, 341, 520, -1, -1, + -1, -1, -1, 65, 349, -1, -1, -1, 334, -1, + -1, -1, 360, 75, -1, 341, -1, -1, -1, -1, + -1, -1, -1, 349, 372, -1, 374, -1, -1, -1, + -1, -1, -1, -1, 382, -1, -1, -1, 386, -1, + -1, -1, -1, -1, -1, -1, -1, 570, -1, -1, + 573, -1, -1, -1, -1, -1, -1, -1, 406, 407, + 175, 176, 177, 178, -1, 180, 181, 182, -1, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, -1, 199, -1, 201, -1, 203, -1, + -1, -1, 207, 208, 209, 440, -1, -1, -1, 622, + -1, 446, 447, -1, 449, -1, -1, -1, -1, -1, + -1, -1, -1, 458, 440, 460, -1, -1, 641, -1, + 446, 447, 470, 449, -1, -1, -1, -1, -1, 477, + -1, -1, 458, -1, 460, -1, -1, -1, 486, -1, + 8, -1, -1, 3, -1, -1, -1, -1, 8, 17, + -1, 11, -1, -1, 22, 23, 24, 17, -1, -1, + -1, 29, 22, 23, 24, 280, -1, -1, 36, 29, + -1, -1, 520, -1, -1, 290, 36, -1, -1, -1, + -1, -1, -1, -1, -1, 53, -1, -1, -1, 49, + 50, 59, 52, 53, -1, -1, 56, 65, 313, 59, + -1, -1, 317, 71, 549, 73, 74, 75, 323, 69, + 70, 71, 557, 73, 74, -1, -1, -1, 8, -1, + -1, 11, 570, 549, -1, 573, -1, 17, -1, -1, + -1, 557, 22, 23, 24, -1, -1, -1, 564, 29, + -1, -1, -1, -1, -1, -1, 36, -1, -1, -1, + -1, 366, 367, -1, 599, 600, -1, -1, -1, -1, + -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, + 596, -1, -1, 599, 600, -1, -1, -1, -1, 0, + 1, 71, 3, 73, 74, 6, -1, 8, 9, 10, + 405, -1, 13, 641, 15, 16, 17, 18, 19, 20, + -1, 22, 23, 24, 419, -1, 27, 28, 29, 30, + 31, 32, 427, -1, -1, 36, -1, 432, -1, -1, + -1, -1, 648, -1, 650, -1, -1, -1, 49, 50, + -1, 52, 53, -1, -1, 56, -1, -1, 59, -1, + -1, 62, -1, -1, -1, -1, -1, -1, 69, 70, + 71, -1, 73, 74, -1, -1, -1, -1, 473, 474, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + 3, -1, -1, 6, 7, 8, 9, 10, -1, 12, + 13, 496, 15, 16, 17, 18, 19, 20, -1, 22, + 23, 24, -1, -1, 27, 28, 29, 30, 31, 32, + -1, 516, -1, 36, -1, -1, -1, -1, 523, 524, + 525, -1, -1, -1, -1, -1, 49, 50, -1, 52, + 53, -1, -1, 56, -1, -1, 59, -1, -1, 62, + -1, -1, -1, -1, 67, 68, 69, 70, 71, -1, + 73, 74, 1, -1, 3, -1, -1, 6, -1, 8, + 9, 10, -1, -1, 13, -1, 15, 16, 17, 18, + 19, 20, -1, 22, 23, 24, 34, -1, 27, 28, + 29, 30, 31, 32, -1, -1, 44, 36, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, - -1, -1, 406, 407, 175, 176, 177, 178, -1, 180, - 181, 182, -1, 184, 185, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 196, 197, -1, 199, -1, - 201, -1, 203, 334, -1, -1, 207, 208, 209, -1, - 341, 7, -1, -1, -1, 11, -1, -1, 349, -1, - 4, 17, -1, -1, 8, -1, 22, 23, 24, -1, - -1, -1, -1, 29, -1, 469, -1, -1, -1, -1, - 36, -1, 476, -1, -1, -1, -1, -1, -1, 33, - 34, 485, 36, 37, 38, 39, 40, 53, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, -1, -1, 71, -1, -1, 74, 280, - -1, 65, -1, -1, -1, 519, -1, -1, -1, 290, - -1, 75, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 439, -1, - -1, -1, 313, -1, 445, 446, 317, 448, -1, -1, - -1, -1, 323, -1, 0, 1, 457, 3, 459, 5, - -1, 7, -1, 9, 10, 569, -1, 13, 572, 15, - 16, 17, 18, 19, 20, -1, 22, 23, 24, -1, - -1, 27, 28, 29, 30, 31, 32, -1, -1, -1, - 36, -1, -1, -1, -1, 366, 367, -1, -1, -1, - -1, -1, -1, 49, 50, -1, 52, 53, -1, -1, - 56, -1, -1, 59, -1, -1, 62, -1, 3, -1, - -1, -1, 7, 69, 70, 71, 11, 73, 74, -1, - -1, -1, 17, -1, 405, 639, -1, 22, 23, 24, - -1, -1, -1, -1, 29, -1, -1, 548, 419, -1, - -1, 36, -1, -1, -1, 556, 427, -1, -1, -1, - -1, 432, 563, -1, 49, 50, -1, 52, 53, -1, - -1, 56, -1, -1, 59, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 69, 70, 71, -1, 73, 74, - -1, -1, -1, 594, -1, -1, 597, 598, -1, -1, - -1, 472, 473, -1, -1, -1, -1, -1, -1, -1, - -1, 1, -1, 3, -1, 5, 6, 7, -1, 9, - 10, -1, 12, 13, 495, 15, 16, 17, 18, 19, - 20, -1, 22, 23, 24, -1, -1, 27, 28, 29, - 30, 31, 32, 644, 515, 646, 36, -1, -1, -1, - -1, 522, 523, 524, -1, -1, -1, -1, -1, 49, - 50, -1, 52, 53, -1, -1, 56, -1, -1, 59, - -1, -1, 62, -1, -1, -1, -1, 67, 68, 69, - 70, 71, -1, 73, 74, 1, -1, 3, -1, 5, - -1, 7, -1, 9, 10, -1, -1, 13, -1, 15, - 16, 17, 18, 19, 20, -1, 22, 23, 24, -1, - -1, 27, 28, 29, 30, 31, 32, -1, -1, -1, - 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 49, 50, -1, 52, 53, -1, -1, - 56, -1, -1, 59, -1, 3, 62, 618, 619, 7, - 621, 67, 68, 69, 70, 71, -1, 73, 74, 17, - 7, -1, -1, -1, 22, 23, 24, -1, -1, -1, - 17, 29, -1, -1, -1, 22, 23, 24, 36, -1, - -1, -1, 29, -1, -1, -1, -1, -1, -1, 36, - -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, - 3, 59, 60, -1, 7, -1, 53, -1, -1, -1, - -1, 69, 70, 71, 17, 73, 74, -1, 65, 22, - 23, 24, -1, -1, 71, -1, 29, 74, -1, -1, - -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, 3, 59, -1, -1, 7, - -1, -1, -1, -1, 67, -1, 69, 70, 71, 17, - 73, 74, -1, -1, 22, 23, 24, -1, -1, -1, - -1, 29, -1, 31, -1, -1, -1, -1, 36, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, - 3, 59, -1, -1, 7, -1, -1, -1, -1, -1, - -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, - 23, 24, -1, 26, -1, -1, 29, -1, -1, -1, - -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, 3, 59, -1, -1, 7, - -1, -1, -1, -1, -1, -1, 69, 70, 71, 17, - 73, 74, -1, -1, 22, 23, 24, -1, 26, -1, - -1, 29, -1, -1, -1, -1, -1, -1, 36, -1, + 49, 50, -1, 52, 53, -1, -1, 56, -1, -1, + 59, -1, -1, 62, 619, 620, -1, 622, 67, 68, + 69, 70, 71, 3, 73, 74, 631, -1, 8, -1, + -1, -1, -1, -1, -1, -1, -1, 17, -1, 8, + -1, -1, 22, 23, 24, -1, -1, -1, 17, 29, + -1, -1, -1, 22, 23, 24, 36, -1, -1, -1, + 29, -1, -1, -1, -1, -1, -1, 36, -1, 49, + 50, -1, 52, 53, -1, -1, 56, -1, 3, 59, + 60, -1, -1, 8, 53, -1, -1, -1, -1, 69, + 70, 71, 17, 73, 74, -1, -1, 22, 23, 24, + -1, -1, 71, -1, 29, 74, -1, -1, -1, -1, + -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 49, 50, -1, 52, 53, -1, + -1, 56, -1, 3, 59, -1, -1, -1, 8, -1, + -1, -1, 67, -1, 69, 70, 71, 17, 73, 74, + -1, -1, 22, 23, 24, -1, -1, -1, -1, 29, + -1, 31, -1, -1, -1, -1, 36, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 49, + 50, -1, 52, 53, -1, -1, 56, -1, 3, 59, + -1, -1, -1, 8, -1, -1, -1, -1, -1, 69, + 70, 71, 17, 73, 74, -1, -1, 22, 23, 24, + -1, 26, -1, -1, 29, -1, -1, -1, -1, -1, + -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 49, 50, -1, 52, 53, -1, + -1, 56, -1, 3, 59, -1, -1, -1, 8, -1, + -1, -1, -1, -1, 69, 70, 71, 17, 73, 74, + -1, -1, 22, 23, 24, -1, 26, -1, -1, 29, + -1, -1, -1, -1, -1, -1, 36, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 3, -1, -1, 49, + 50, 8, 52, 53, -1, -1, 56, -1, -1, 59, + 17, -1, -1, -1, -1, 22, 23, 24, -1, 69, + 70, 71, 29, 73, 74, -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, - 3, 59, -1, -1, 7, -1, -1, -1, -1, -1, - -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, - 23, 24, -1, -1, -1, -1, 29, -1, -1, -1, - -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, 3, 59, -1, -1, 7, - -1, -1, -1, -1, -1, -1, 69, 70, 71, 17, - 73, 74, -1, -1, 22, 23, 24, -1, -1, -1, - -1, 29, -1, -1, -1, -1, -1, -1, 36, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, - 3, 59, -1, -1, 7, -1, -1, -1, -1, -1, - -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, - 23, 24, -1, -1, -1, -1, 29, -1, -1, -1, - -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, -1, 59, -1, -1, 7, - -1, -1, -1, -1, -1, -1, 69, 70, 71, 17, - 73, 74, -1, -1, 22, 23, 24, -1, -1, -1, - -1, 29, -1, -1, -1, 7, -1, -1, 36, 11, - -1, -1, -1, 7, -1, 17, -1, -1, -1, -1, - 22, 23, 24, 17, -1, 53, -1, 29, 22, 23, - 24, 59, -1, -1, 36, 29, -1, 65, 7, -1, - -1, -1, 36, 71, -1, 73, 74, 75, 17, -1, - -1, 53, -1, 22, 23, 24, -1, 59, -1, 53, - 29, -1, -1, -1, -1, 59, 7, 36, -1, 71, - -1, 73, 74, -1, -1, -1, 17, 71, -1, 73, - 74, 22, 23, 24, 53, -1, -1, -1, 29, -1, - 59, 7, -1, -1, -1, 36, -1, -1, -1, 7, - -1, 17, 71, -1, 73, 74, 22, 23, 24, 17, - -1, -1, 53, 29, 22, 23, 24, -1, 59, -1, - 36, 29, -1, -1, 7, -1, -1, -1, 36, -1, - 71, -1, 73, 74, 17, -1, -1, 53, -1, 22, - 23, 24, -1, 59, -1, 53, 29, -1, -1, -1, - -1, 59, 7, 36, -1, 71, -1, 73, 74, -1, - -1, -1, 17, 71, -1, 73, 74, 22, 23, 24, - 53, -1, -1, -1, 29, -1, 59, 7, -1, -1, - -1, 36, -1, -1, -1, 7, -1, 17, 71, -1, - -1, 74, 22, 23, 24, 17, -1, -1, 53, 29, - 22, 23, 24, -1, -1, -1, 36, 29, -1, -1, - -1, -1, -1, -1, 36, -1, 71, -1, 73, 74, - -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, - -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 71, -1, -1, 74, -1, -1, -1, -1, 71, - 33, 34, 74, 36, -1, 38, 39, 40, -1, -1, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - 53, 54, 55, 56, -1, -1, -1, -1, -1, -1, - 33, 34, -1, 36, -1, 38, 39, 40, -1, 72, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - 53, 54, 55, 56, 33, 34, -1, 36, -1, 38, - 39, 40, -1, 66, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 33, 34, - -1, 36, -1, 38, 39, 40, -1, 66, 43, 44, + -1, -1, 49, 50, -1, 52, 53, -1, -1, 56, + -1, 3, 59, -1, -1, -1, 8, -1, -1, -1, + 67, -1, 69, 70, 71, 17, 73, 74, -1, -1, + 22, 23, 24, -1, -1, -1, -1, 29, -1, -1, + -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 3, -1, -1, 49, 50, 8, + 52, 53, -1, -1, 56, -1, -1, 59, 17, -1, + -1, -1, -1, 22, 23, 24, -1, 69, 70, 71, + 29, 73, 74, -1, -1, -1, -1, 36, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, + 49, 50, 8, 52, 53, -1, -1, 56, -1, -1, + 59, 17, -1, -1, -1, -1, 22, 23, 24, -1, + 69, 70, 71, 29, 73, 74, -1, -1, -1, -1, + 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 49, 50, 8, 52, 53, -1, -1, + 56, -1, -1, 59, 17, -1, -1, -1, -1, 22, + 23, 24, -1, 69, 70, 71, 29, 73, 74, -1, + 8, -1, -1, 36, -1, -1, -1, -1, 8, 17, + -1, -1, -1, -1, 22, 23, 24, 17, -1, -1, + 53, 29, 22, 23, 24, -1, 59, -1, 36, 29, + -1, -1, -1, -1, 8, -1, 36, -1, 71, -1, + 73, 74, 75, 17, -1, 53, -1, -1, 22, 23, + 24, 59, -1, 53, -1, 29, -1, -1, -1, 59, + -1, 8, 36, 71, -1, 73, 74, -1, -1, -1, + 17, 71, -1, 73, 74, 22, 23, 24, -1, 53, + -1, -1, 29, -1, -1, 59, -1, 8, -1, 36, + -1, -1, -1, -1, -1, 8, 17, 71, -1, 73, + 74, 22, 23, 24, 17, -1, 53, -1, 29, 22, + 23, 24, 59, -1, -1, 36, 29, -1, -1, -1, + 8, -1, -1, 36, 71, -1, 73, 74, 8, 17, + -1, 11, 53, -1, 22, 23, 24, 17, 59, -1, + 53, 29, 22, 23, 24, -1, 59, -1, 36, 29, + 71, -1, 73, 74, 8, -1, 36, -1, 71, -1, + -1, 74, -1, 17, -1, 53, -1, -1, 22, 23, + 24, -1, -1, 53, -1, 29, -1, -1, -1, -1, + -1, -1, 36, 71, -1, 73, 74, -1, -1, -1, + -1, 71, 34, -1, 74, -1, 38, 39, 40, 53, + -1, 43, 44, 45, 46, 59, 48, 49, 50, 51, + 52, 53, 54, 55, 56, -1, -1, 71, 33, 34, + 74, 36, -1, 38, 39, 40, -1, -1, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 33, 34, -1, 36, -1, 38, 39, 40, - -1, 66, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, -1, 33, 34, 60, - 36, -1, 38, 39, 40, -1, -1, 43, 44, 45, + 55, 56, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, 34, -1, 36, -1, 38, 39, 40, -1, + 75, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, -1, -1, -1, -1, -1, + -1, 33, 34, -1, 36, -1, 38, 39, 40, -1, + 72, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 33, 34, -1, 36, -1, + 38, 39, 40, -1, 66, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 33, + 34, -1, 36, -1, 38, 39, 40, -1, 66, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 33, 34, -1, 36, -1, 38, 39, + 40, -1, 66, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 33, 34, -1, + -1, -1, 38, 39, 40, -1, -1, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 33, 34, -1, -1, -1, 38, 39, 40, -1, - -1, 43, 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 33, 34, -1, -1, -1, - 38, 39, 40, -1, -1, 43, 44, 45, 46, -1, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 34, - -1, -1, -1, 38, 39, 40, -1, -1, 43, 44, - 45, 46, -1, 48, 49, 50, 51, 52, 53, 54, - 55, 56 + -1, 43, 44, 45, 46, -1, 48, 49, 50, 51, + 52, 53, 54, 55, 56 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing @@ -1488,71 +1485,72 @@ static const yytype_int16 yycheck[] = static const yytype_uint8 yystos[] = { 0, 77, 79, 80, 0, 25, 78, 25, 86, 24, - 73, 74, 137, 138, 81, 24, 88, 89, 3, 62, - 21, 82, 162, 24, 87, 210, 63, 3, 59, 63, - 83, 85, 137, 62, 1, 3, 5, 7, 9, 10, + 73, 74, 141, 142, 81, 24, 88, 89, 3, 62, + 21, 82, 166, 24, 87, 214, 63, 3, 59, 63, + 83, 85, 141, 62, 1, 3, 6, 8, 9, 10, 13, 15, 16, 17, 18, 19, 20, 22, 23, 27, 28, 29, 30, 31, 32, 36, 49, 50, 52, 53, 56, 59, 69, 70, 71, 90, 91, 92, 98, 110, - 113, 118, 121, 123, 124, 125, 126, 130, 134, 137, - 139, 140, 145, 146, 149, 152, 153, 154, 157, 160, - 161, 177, 182, 62, 9, 17, 21, 31, 32, 64, - 195, 24, 60, 83, 84, 3, 86, 88, 3, 134, - 136, 137, 17, 36, 53, 59, 137, 139, 144, 148, - 149, 150, 157, 136, 125, 130, 111, 59, 137, 155, - 125, 134, 114, 35, 67, 133, 71, 123, 182, 189, - 122, 133, 119, 59, 96, 97, 137, 59, 93, 135, - 137, 181, 124, 124, 124, 124, 124, 124, 36, 53, - 123, 131, 143, 149, 151, 157, 124, 124, 11, 123, - 188, 62, 59, 94, 181, 4, 33, 34, 36, 37, + 113, 121, 124, 126, 127, 128, 129, 134, 138, 141, + 143, 144, 149, 150, 153, 156, 157, 158, 161, 164, + 165, 181, 186, 62, 9, 17, 21, 31, 32, 64, + 199, 24, 60, 83, 84, 3, 86, 88, 3, 138, + 140, 141, 17, 36, 53, 59, 141, 143, 148, 152, + 153, 154, 161, 140, 128, 134, 111, 59, 141, 159, + 128, 138, 114, 35, 67, 137, 71, 126, 186, 193, + 125, 137, 122, 59, 96, 97, 141, 59, 93, 139, + 141, 185, 127, 127, 127, 127, 127, 127, 36, 53, + 126, 135, 147, 153, 155, 161, 127, 127, 11, 126, + 192, 62, 59, 94, 185, 4, 33, 34, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 67, 59, - 63, 71, 66, 59, 133, 1, 133, 8, 65, 75, - 138, 196, 59, 156, 196, 24, 196, 197, 196, 64, - 62, 186, 88, 59, 36, 59, 142, 148, 149, 150, - 151, 157, 142, 142, 63, 98, 107, 108, 109, 182, - 190, 11, 132, 137, 141, 142, 173, 174, 175, 59, - 67, 158, 112, 190, 24, 59, 68, 134, 167, 169, - 171, 142, 35, 53, 59, 68, 134, 166, 168, 169, - 170, 180, 112, 60, 97, 165, 142, 60, 93, 163, - 65, 75, 142, 7, 143, 60, 72, 72, 60, 94, - 65, 142, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 127, 60, 131, 183, 59, 137, 123, - 188, 178, 123, 127, 1, 67, 91, 100, 176, 177, - 179, 182, 182, 123, 7, 17, 22, 23, 24, 29, - 36, 53, 65, 71, 138, 198, 200, 201, 202, 137, - 203, 211, 158, 59, 3, 198, 198, 83, 60, 175, - 7, 142, 60, 137, 35, 105, 8, 65, 62, 142, - 132, 141, 75, 187, 60, 175, 179, 115, 62, 63, - 24, 169, 59, 172, 62, 186, 72, 104, 59, 170, - 53, 170, 62, 186, 3, 194, 75, 142, 120, 62, - 186, 62, 186, 182, 135, 65, 36, 59, 142, 148, - 149, 150, 157, 67, 142, 142, 62, 186, 182, 65, - 67, 123, 128, 129, 184, 185, 11, 75, 187, 31, - 131, 72, 66, 176, 60, 185, 101, 62, 68, 36, - 59, 199, 200, 202, 59, 67, 71, 67, 7, 198, - 3, 50, 59, 137, 208, 209, 3, 72, 65, 11, - 198, 60, 75, 62, 191, 211, 62, 62, 62, 60, - 60, 106, 26, 26, 190, 173, 59, 137, 147, 148, - 149, 150, 151, 157, 159, 60, 68, 105, 190, 137, - 60, 175, 171, 68, 142, 6, 12, 68, 99, 102, - 170, 194, 170, 60, 168, 68, 134, 194, 35, 97, - 60, 93, 60, 182, 142, 127, 94, 95, 164, 181, - 60, 182, 127, 66, 75, 187, 68, 75, 187, 131, - 60, 60, 60, 188, 68, 179, 176, 198, 201, 191, - 24, 137, 138, 193, 198, 205, 213, 198, 137, 192, - 204, 212, 198, 3, 208, 62, 72, 198, 209, 198, - 194, 137, 203, 60, 179, 123, 123, 62, 175, 59, - 159, 116, 60, 183, 66, 103, 60, 60, 194, 104, - 60, 185, 62, 186, 142, 185, 123, 129, 128, 129, - 60, 72, 68, 60, 60, 59, 68, 62, 72, 198, - 68, 62, 49, 198, 62, 194, 59, 59, 198, 206, - 207, 68, 190, 60, 175, 14, 117, 159, 8, 65, - 66, 75, 179, 194, 194, 68, 68, 95, 60, 68, - 206, 191, 205, 198, 194, 204, 208, 191, 191, 60, - 100, 113, 123, 123, 60, 60, 60, 60, 159, 66, - 66, 206, 206 + 63, 71, 66, 59, 137, 1, 137, 5, 65, 75, + 142, 200, 59, 160, 200, 24, 200, 201, 200, 64, + 62, 190, 88, 59, 36, 59, 146, 152, 153, 154, + 155, 161, 146, 146, 63, 98, 107, 108, 109, 186, + 194, 11, 136, 141, 145, 146, 177, 178, 179, 59, + 67, 162, 112, 194, 24, 59, 68, 138, 171, 173, + 175, 146, 35, 53, 59, 68, 138, 170, 172, 173, + 174, 184, 112, 60, 97, 169, 146, 60, 93, 167, + 65, 75, 146, 8, 147, 60, 72, 72, 60, 94, + 65, 146, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 130, 60, 135, 187, 59, 141, 126, + 192, 182, 126, 130, 1, 67, 91, 100, 180, 181, + 183, 186, 186, 126, 8, 17, 22, 23, 24, 29, + 36, 53, 65, 71, 142, 202, 204, 205, 206, 141, + 207, 215, 162, 59, 3, 202, 202, 83, 60, 179, + 8, 146, 60, 141, 35, 105, 5, 65, 62, 146, + 136, 145, 75, 191, 60, 179, 183, 115, 62, 63, + 24, 173, 59, 176, 62, 190, 72, 104, 59, 174, + 53, 174, 62, 190, 3, 198, 75, 146, 123, 62, + 190, 62, 190, 186, 139, 65, 36, 59, 146, 152, + 153, 154, 161, 67, 146, 146, 62, 190, 186, 65, + 67, 126, 131, 132, 188, 189, 11, 75, 191, 31, + 135, 72, 66, 180, 75, 191, 189, 101, 62, 68, + 36, 59, 203, 204, 206, 59, 67, 71, 67, 8, + 202, 3, 50, 59, 141, 212, 213, 3, 72, 65, + 11, 202, 60, 75, 62, 195, 215, 62, 62, 62, + 60, 60, 106, 26, 26, 194, 177, 59, 141, 151, + 152, 153, 154, 155, 161, 163, 60, 68, 105, 194, + 141, 60, 179, 175, 68, 146, 7, 12, 68, 99, + 102, 174, 198, 174, 60, 172, 68, 138, 198, 35, + 97, 60, 93, 60, 186, 146, 130, 94, 95, 168, + 185, 60, 186, 130, 66, 75, 191, 68, 191, 135, + 60, 60, 60, 192, 60, 68, 183, 180, 202, 205, + 195, 24, 141, 142, 197, 202, 209, 217, 202, 141, + 196, 208, 216, 202, 3, 212, 62, 72, 202, 213, + 202, 198, 141, 207, 60, 183, 126, 126, 62, 179, + 59, 163, 116, 60, 187, 66, 103, 60, 60, 198, + 104, 60, 189, 62, 190, 146, 189, 67, 126, 133, + 131, 132, 60, 72, 68, 60, 60, 59, 68, 62, + 72, 202, 68, 62, 49, 202, 62, 198, 59, 59, + 202, 210, 211, 68, 194, 60, 179, 119, 163, 5, + 65, 66, 75, 183, 198, 198, 68, 68, 95, 60, + 68, 130, 210, 195, 209, 202, 198, 208, 212, 195, + 195, 60, 14, 117, 120, 126, 126, 189, 60, 60, + 60, 60, 163, 20, 100, 66, 66, 68, 210, 210, + 118, 112, 105 }; #define yyerrok (yyerrstatus = 0) @@ -1567,18 +1565,9 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. However, - YYFAIL appears to be in use. Nevertheless, it is formally deprecated - in Bison 2.4.2's NEWS entry, where a plan to phase it out is - discussed. */ + Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab -#if defined YYFAIL - /* This is here to suppress warnings from the GCC cpp's - -Wunused-macros. Normally we don't worry about that warning, but - some users do, and we want to make it easy for users to remove - YYFAIL uses, which will produce warnings from Bison 2.5. */ -#endif #define YYRECOVERING() (!!yyerrstatus) @@ -1588,6 +1577,7 @@ do \ { \ yychar = (Token); \ yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ @@ -1629,10 +1619,19 @@ while (YYID (0)) #endif -/* This macro is provided for backward compatibility. */ +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif #endif @@ -1736,20 +1735,17 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void -yy_stack_print (yybottom, yytop) - yytype_int16 *yybottom; - yytype_int16 *yytop; +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) - { - int yybot = *yybottom; - YYFPRINTF (stderr, " %d", yybot); - } + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } @@ -1783,11 +1779,11 @@ yy_reduce_print (yyvsp, yyrule) /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { - YYFPRINTF (stderr, " $%d = ", yyi + 1); + fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); - YYFPRINTF (stderr, "\n"); + fprintf (stderr, "\n"); } } @@ -1824,6 +1820,7 @@ int yydebug; # define YYMAXDEPTH 10000 #endif + #if YYERROR_VERBOSE @@ -1926,142 +1923,115 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message - about the unexpected token YYTOKEN for the state stack whose top is - YYSSP. - - Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is - not large enough to hold the message. In that case, also set - *YYMSG_ALLOC to the required number of bytes. Return 2 if the - required number of bytes is too large to store. */ -static int -yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, - yytype_int16 *yyssp, int yytoken) +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) { - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - /* Internationalized format string. */ - const char *yyformat = 0; - /* Arguments of yyformat. */ - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - /* Number of reported tokens (one for the "unexpected", one per - "expected"). */ - int yycount = 0; - - /* There are many possibilities here to consider: - - Assume YYFAIL is not used. It's too flawed to consider. See - - for details. YYERROR is fine as it does not invoke this - function. - - If this state is a consistent state with a default action, then - the only way this function was invoked is if the default action - is an error action. In that case, don't check for expected - tokens because there are none. - - The only way there can be no lookahead present (in yychar) is if - this state is a consistent state with a default action. Thus, - detecting the absence of a lookahead is sufficient to determine - that there is no unexpected or expected token to report. In that - case, just report a simple "syntax error". - - Don't assume there isn't a lookahead just because this state is a - consistent state with a default action. There might have been a - previous inconsistent state, consistent state with a non-default - action, or user semantic action that manipulated yychar. - - Of course, the expected token list depends on states to have - correct lookahead information, and it depends on the parser not - to perform extra reductions after fetching a lookahead from the - scanner and before detecting a syntax error. Thus, state merging - (from LALR or IELR) and default reductions corrupt the expected - token list. However, the list is correct for canonical LR with - one exception: it will still contain any token that will not be - accepted due to an error action in a later state. - */ - if (yytoken != YYEMPTY) - { - int yyn = yypact[*yyssp]; - yyarg[yycount++] = yytname[yytoken]; - if (!yypact_value_is_default (yyn)) - { - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. In other words, skip the first -YYN actions for - this state because they are default actions. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yyx; - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR - && !yytable_value_is_error (yytable[yyx + yyn])) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - if (! (yysize <= yysize1 - && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; - } - } - } + int yyn = yypact[yystate]; - switch (yycount) - { -# define YYCASE_(N, S) \ - case N: \ - yyformat = S; \ - break - YYCASE_(0, YY_("syntax error")); - YYCASE_(1, YY_("syntax error, unexpected %s")); - YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); - YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); - YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); - YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); -# undef YYCASE_ - } + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } - yysize1 = yysize + yystrlen (yyformat); - if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; - if (*yymsg_alloc < yysize) - { - *yymsg_alloc = 2 * yysize; - if (! (yysize <= *yymsg_alloc - && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) - *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; - return 1; - } + if (yysize_overflow) + return YYSIZE_MAXIMUM; - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - { - char *yyp = *yymsg; - int yyi = 0; - while ((*yyp = *yyformat) != '\0') - if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyformat += 2; - } - else - { - yyp++; - yyformat++; - } - } - return 0; + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } } #endif /* YYERROR_VERBOSE */ + /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -2093,9 +2063,10 @@ yydestruct (yymsg, yytype, yyvaluep) break; } } - + /* Prevent warnings from -Wmissing-prototypes. */ + #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); @@ -2111,16 +2082,18 @@ int yyparse (); #endif /* ! YYPARSE_PARAM */ -/* The lookahead symbol. */ + +/* The look-ahead symbol. */ int yychar, yystate; -/* The semantic value of the lookahead symbol. */ +/* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; + /*----------. | yyparse. | `----------*/ @@ -2147,36 +2120,13 @@ yyparse () #endif #endif { - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - - /* The stacks and their tools: - `yyss': related to states. - `yyvs': related to semantic values. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss; - yytype_int16 *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs; - YYSTYPE *yyvsp; - - YYSIZE_T yystacksize; - + int yyn; int yyresult; - /* Lookahead token as an internal (translated) token number. */ - int yytoken; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; @@ -2184,28 +2134,51 @@ yyparse () YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; - yytoken = 0; - yyss = yyssa; - yyvs = yyvsa; - yystacksize = YYINITDEPTH; - YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ + yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ + yyssp = yyss; yyvsp = yyvs; @@ -2235,6 +2208,7 @@ yyparse () YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; + /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might @@ -2242,6 +2216,7 @@ yyparse () yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); yyss = yyss1; @@ -2264,8 +2239,9 @@ yyparse () (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); @@ -2276,6 +2252,7 @@ yyparse () yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; + YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); @@ -2285,9 +2262,6 @@ yyparse () YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - if (yystate == YYFINAL) - YYACCEPT; - goto yybackup; /*-----------. @@ -2296,16 +2270,16 @@ yyparse () yybackup: /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ + look-ahead token if we need one and don't already have one. */ - /* First try to decide what to do without reference to lookahead token. */ + /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; - if (yypact_value_is_default (yyn)) + if (yyn == YYPACT_NINF) goto yydefault; - /* Not known => get a lookahead token if don't already have one. */ + /* Not known => get a look-ahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -2331,22 +2305,26 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yytable_value_is_error (yyn)) - goto yyerrlab; + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; yyn = -yyn; goto yyreduce; } + if (yyn == YYFINAL) + YYACCEPT; + /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; - /* Shift the lookahead token. */ + /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - /* Discard the shifted token. */ - yychar = YYEMPTY; + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; @@ -2386,8 +2364,6 @@ yyreduce: switch (yyn) { case 2: - -/* Line 1806 of yacc.c */ #line 128 "go.y" { xtop = concat(xtop, (yyvsp[(4) - (4)].list)); @@ -2395,8 +2371,6 @@ yyreduce: break; case 3: - -/* Line 1806 of yacc.c */ #line 134 "go.y" { prevlineno = lineno; @@ -2407,8 +2381,6 @@ yyreduce: break; case 4: - -/* Line 1806 of yacc.c */ #line 141 "go.y" { mkpackage((yyvsp[(2) - (3)].sym)->name); @@ -2416,8 +2388,6 @@ yyreduce: break; case 5: - -/* Line 1806 of yacc.c */ #line 151 "go.y" { importpkg = runtimepkg; @@ -2431,8 +2401,6 @@ yyreduce: break; case 6: - -/* Line 1806 of yacc.c */ #line 162 "go.y" { importpkg = nil; @@ -2440,8 +2408,6 @@ yyreduce: break; case 12: - -/* Line 1806 of yacc.c */ #line 176 "go.y" { Pkg *ipkg; @@ -2478,8 +2444,6 @@ yyreduce: break; case 13: - -/* Line 1806 of yacc.c */ #line 209 "go.y" { // When an invalid import path is passed to importfile, @@ -2492,8 +2456,6 @@ yyreduce: break; case 16: - -/* Line 1806 of yacc.c */ #line 224 "go.y" { // import with original name @@ -2504,8 +2466,6 @@ yyreduce: break; case 17: - -/* Line 1806 of yacc.c */ #line 231 "go.y" { // import with given name @@ -2516,8 +2476,6 @@ yyreduce: break; case 18: - -/* Line 1806 of yacc.c */ #line 238 "go.y" { // import into my name space @@ -2528,8 +2486,6 @@ yyreduce: break; case 19: - -/* Line 1806 of yacc.c */ #line 247 "go.y" { if(importpkg->name == nil) { @@ -2545,8 +2501,6 @@ yyreduce: break; case 21: - -/* Line 1806 of yacc.c */ #line 261 "go.y" { if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0) @@ -2555,8 +2509,6 @@ yyreduce: break; case 22: - -/* Line 1806 of yacc.c */ #line 267 "go.y" { defercheckwidth(); @@ -2564,8 +2516,6 @@ yyreduce: break; case 23: - -/* Line 1806 of yacc.c */ #line 271 "go.y" { resumecheckwidth(); @@ -2574,8 +2524,6 @@ yyreduce: break; case 24: - -/* Line 1806 of yacc.c */ #line 280 "go.y" { yyerror("empty top-level declaration"); @@ -2584,8 +2532,6 @@ yyreduce: break; case 26: - -/* Line 1806 of yacc.c */ #line 286 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); @@ -2593,8 +2539,6 @@ yyreduce: break; case 27: - -/* Line 1806 of yacc.c */ #line 290 "go.y" { yyerror("non-declaration statement outside function body"); @@ -2603,8 +2547,6 @@ yyreduce: break; case 28: - -/* Line 1806 of yacc.c */ #line 295 "go.y" { (yyval.list) = nil; @@ -2612,8 +2554,6 @@ yyreduce: break; case 29: - -/* Line 1806 of yacc.c */ #line 301 "go.y" { (yyval.list) = (yyvsp[(2) - (2)].list); @@ -2621,8 +2561,6 @@ yyreduce: break; case 30: - -/* Line 1806 of yacc.c */ #line 305 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2630,8 +2568,6 @@ yyreduce: break; case 31: - -/* Line 1806 of yacc.c */ #line 309 "go.y" { (yyval.list) = nil; @@ -2639,8 +2575,6 @@ yyreduce: break; case 32: - -/* Line 1806 of yacc.c */ #line 313 "go.y" { (yyval.list) = (yyvsp[(2) - (2)].list); @@ -2650,8 +2584,6 @@ yyreduce: break; case 33: - -/* Line 1806 of yacc.c */ #line 319 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2661,8 +2593,6 @@ yyreduce: break; case 34: - -/* Line 1806 of yacc.c */ #line 325 "go.y" { (yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list)); @@ -2672,8 +2602,6 @@ yyreduce: break; case 35: - -/* Line 1806 of yacc.c */ #line 331 "go.y" { (yyval.list) = nil; @@ -2682,8 +2610,6 @@ yyreduce: break; case 36: - -/* Line 1806 of yacc.c */ #line 336 "go.y" { (yyval.list) = list1((yyvsp[(2) - (2)].node)); @@ -2691,8 +2617,6 @@ yyreduce: break; case 37: - -/* Line 1806 of yacc.c */ #line 340 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2700,8 +2624,6 @@ yyreduce: break; case 38: - -/* Line 1806 of yacc.c */ #line 344 "go.y" { (yyval.list) = nil; @@ -2709,8 +2631,6 @@ yyreduce: break; case 39: - -/* Line 1806 of yacc.c */ #line 350 "go.y" { iota = 0; @@ -2718,8 +2638,6 @@ yyreduce: break; case 40: - -/* Line 1806 of yacc.c */ #line 356 "go.y" { (yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil); @@ -2727,8 +2645,6 @@ yyreduce: break; case 41: - -/* Line 1806 of yacc.c */ #line 360 "go.y" { (yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list)); @@ -2736,8 +2652,6 @@ yyreduce: break; case 42: - -/* Line 1806 of yacc.c */ #line 364 "go.y" { (yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list)); @@ -2745,8 +2659,6 @@ yyreduce: break; case 43: - -/* Line 1806 of yacc.c */ #line 370 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list)); @@ -2754,8 +2666,6 @@ yyreduce: break; case 44: - -/* Line 1806 of yacc.c */ #line 374 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list)); @@ -2763,8 +2673,6 @@ yyreduce: break; case 46: - -/* Line 1806 of yacc.c */ #line 381 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil); @@ -2772,8 +2680,6 @@ yyreduce: break; case 47: - -/* Line 1806 of yacc.c */ #line 385 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil); @@ -2781,8 +2687,6 @@ yyreduce: break; case 48: - -/* Line 1806 of yacc.c */ #line 391 "go.y" { // different from dclname because the name @@ -2793,8 +2697,6 @@ yyreduce: break; case 49: - -/* Line 1806 of yacc.c */ #line 400 "go.y" { (yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1); @@ -2802,18 +2704,28 @@ yyreduce: break; case 50: - -/* Line 1806 of yacc.c */ #line 406 "go.y" { (yyval.node) = (yyvsp[(1) - (1)].node); + + // These nodes do not carry line numbers. + // Since a bare name used as an expression is an error, + // introduce a wrapper node to give the correct line. + switch((yyval.node)->op) { + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + (yyval.node) = nod(OPAREN, (yyval.node), N); + (yyval.node)->implicit = 1; + break; + } } break; case 51: - -/* Line 1806 of yacc.c */ -#line 410 "go.y" +#line 424 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); (yyval.node)->etype = (yyvsp[(2) - (3)].i); // rathole to pass opcode @@ -2821,9 +2733,7 @@ yyreduce: break; case 52: - -/* Line 1806 of yacc.c */ -#line 415 "go.y" +#line 429 "go.y" { if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) { // simple @@ -2838,9 +2748,7 @@ yyreduce: break; case 53: - -/* Line 1806 of yacc.c */ -#line 427 "go.y" +#line 441 "go.y" { if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) { (yyval.node) = nod(OTYPESW, N, (yyvsp[(3) - (3)].list)->n->right); @@ -2854,14 +2762,12 @@ yyreduce: (yyval.node)->left = dclname((yyvsp[(1) - (3)].list)->n->sym); // it's a colas, so must not re-use an oldname. break; } - (yyval.node) = colas((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); + (yyval.node) = colas((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list), (yyvsp[(2) - (3)].i)); } break; case 54: - -/* Line 1806 of yacc.c */ -#line 443 "go.y" +#line 457 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); (yyval.node)->etype = OADD; @@ -2869,9 +2775,7 @@ yyreduce: break; case 55: - -/* Line 1806 of yacc.c */ -#line 448 "go.y" +#line 462 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); (yyval.node)->etype = OSUB; @@ -2879,9 +2783,7 @@ yyreduce: break; case 56: - -/* Line 1806 of yacc.c */ -#line 455 "go.y" +#line 469 "go.y" { Node *n, *nn; @@ -2904,9 +2806,7 @@ yyreduce: break; case 57: - -/* Line 1806 of yacc.c */ -#line 475 "go.y" +#line 489 "go.y" { Node *n; @@ -2927,23 +2827,19 @@ yyreduce: break; case 58: - -/* Line 1806 of yacc.c */ -#line 493 "go.y" +#line 507 "go.y" { // will be converted to OCASE // right will point to next case // done in casebody() markdcl(); (yyval.node) = nod(OXCASE, N, N); - (yyval.node)->list = list1(colas((yyvsp[(2) - (5)].list), list1((yyvsp[(4) - (5)].node)))); + (yyval.node)->list = list1(colas((yyvsp[(2) - (5)].list), list1((yyvsp[(4) - (5)].node)), (yyvsp[(3) - (5)].i))); } break; case 59: - -/* Line 1806 of yacc.c */ -#line 502 "go.y" +#line 516 "go.y" { Node *n, *nn; @@ -2962,18 +2858,14 @@ yyreduce: break; case 60: - -/* Line 1806 of yacc.c */ -#line 520 "go.y" +#line 534 "go.y" { markdcl(); } break; case 61: - -/* Line 1806 of yacc.c */ -#line 524 "go.y" +#line 538 "go.y" { (yyval.node) = liststmt((yyvsp[(3) - (4)].list)); popdcl(); @@ -2981,9 +2873,7 @@ yyreduce: break; case 62: - -/* Line 1806 of yacc.c */ -#line 531 "go.y" +#line 545 "go.y" { // If the last token read by the lexer was consumed // as part of the case, clear it (parser has cleared yychar). @@ -2996,9 +2886,7 @@ yyreduce: break; case 63: - -/* Line 1806 of yacc.c */ -#line 541 "go.y" +#line 555 "go.y" { int last; @@ -3020,36 +2908,28 @@ yyreduce: break; case 64: - -/* Line 1806 of yacc.c */ -#line 561 "go.y" +#line 575 "go.y" { (yyval.list) = nil; } break; case 65: - -/* Line 1806 of yacc.c */ -#line 565 "go.y" +#line 579 "go.y" { (yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node)); } break; case 66: - -/* Line 1806 of yacc.c */ -#line 571 "go.y" +#line 585 "go.y" { markdcl(); } break; case 67: - -/* Line 1806 of yacc.c */ -#line 575 "go.y" +#line 589 "go.y" { (yyval.list) = (yyvsp[(3) - (4)].list); popdcl(); @@ -3057,9 +2937,7 @@ yyreduce: break; case 68: - -/* Line 1806 of yacc.c */ -#line 582 "go.y" +#line 596 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -3068,9 +2946,7 @@ yyreduce: break; case 69: - -/* Line 1806 of yacc.c */ -#line 588 "go.y" +#line 602 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -3080,9 +2956,7 @@ yyreduce: break; case 70: - -/* Line 1806 of yacc.c */ -#line 597 "go.y" +#line 611 "go.y" { // init ; test ; incr if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0) @@ -3096,9 +2970,7 @@ yyreduce: break; case 71: - -/* Line 1806 of yacc.c */ -#line 608 "go.y" +#line 622 "go.y" { // normal test (yyval.node) = nod(OFOR, N, N); @@ -3107,9 +2979,7 @@ yyreduce: break; case 73: - -/* Line 1806 of yacc.c */ -#line 617 "go.y" +#line 631 "go.y" { (yyval.node) = (yyvsp[(1) - (2)].node); (yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list)); @@ -3117,18 +2987,14 @@ yyreduce: break; case 74: - -/* Line 1806 of yacc.c */ -#line 624 "go.y" +#line 638 "go.y" { markdcl(); } break; case 75: - -/* Line 1806 of yacc.c */ -#line 628 "go.y" +#line 642 "go.y" { (yyval.node) = (yyvsp[(3) - (3)].node); popdcl(); @@ -3136,9 +3002,7 @@ yyreduce: break; case 76: - -/* Line 1806 of yacc.c */ -#line 635 "go.y" +#line 649 "go.y" { // test (yyval.node) = nod(OIF, N, N); @@ -3147,9 +3011,7 @@ yyreduce: break; case 77: - -/* Line 1806 of yacc.c */ -#line 641 "go.y" +#line 655 "go.y" { // init ; test (yyval.node) = nod(OIF, N, N); @@ -3160,18 +3022,14 @@ yyreduce: break; case 78: - -/* Line 1806 of yacc.c */ -#line 652 "go.y" +#line 666 "go.y" { markdcl(); } break; case 79: - -/* Line 1806 of yacc.c */ -#line 656 "go.y" +#line 670 "go.y" { if((yyvsp[(3) - (3)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3179,66 +3037,89 @@ yyreduce: break; case 80: - -/* Line 1806 of yacc.c */ -#line 661 "go.y" +#line 675 "go.y" { (yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list); } break; case 81: - -/* Line 1806 of yacc.c */ -#line 665 "go.y" +#line 679 "go.y" { + Node *n; + NodeList *nn; + + (yyval.node) = (yyvsp[(3) - (8)].node); + n = (yyvsp[(3) - (8)].node); popdcl(); - (yyval.node) = (yyvsp[(3) - (7)].node); - if((yyvsp[(7) - (7)].node) != N) - (yyval.node)->nelse = list1((yyvsp[(7) - (7)].node)); + for(nn = concat((yyvsp[(7) - (8)].list), (yyvsp[(8) - (8)].list)); nn; nn = nn->next) { + if(nn->n->op == OIF) + popdcl(); + n->nelse = list1(nn->n); + n = nn->n; + } } break; case 82: - -/* Line 1806 of yacc.c */ -#line 673 "go.y" +#line 696 "go.y" { - (yyval.node) = N; + markdcl(); } break; case 83: - -/* Line 1806 of yacc.c */ -#line 677 "go.y" +#line 700 "go.y" { - (yyval.node) = (yyvsp[(2) - (2)].node); + if((yyvsp[(4) - (5)].node)->ntest == N) + yyerror("missing condition in if statement"); + (yyvsp[(4) - (5)].node)->nbody = (yyvsp[(5) - (5)].list); + (yyval.list) = list1((yyvsp[(4) - (5)].node)); } break; case 84: - -/* Line 1806 of yacc.c */ -#line 681 "go.y" +#line 708 "go.y" { - (yyval.node) = (yyvsp[(2) - (2)].node); + (yyval.list) = nil; } break; case 85: - -/* Line 1806 of yacc.c */ -#line 687 "go.y" +#line 712 "go.y" { - markdcl(); + (yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list)); } break; case 86: +#line 717 "go.y" + { + (yyval.list) = nil; + } + break; + + case 87: +#line 721 "go.y" + { + NodeList *node; + + node = mal(sizeof *node); + node->n = (yyvsp[(2) - (2)].node); + node->end = node; + (yyval.list) = node; + } + break; + + case 88: +#line 732 "go.y" + { + markdcl(); + } + break; -/* Line 1806 of yacc.c */ -#line 691 "go.y" + case 89: +#line 736 "go.y" { Node *n; n = (yyvsp[(3) - (3)].node)->ntest; @@ -3248,10 +3129,8 @@ yyreduce: } break; - case 87: - -/* Line 1806 of yacc.c */ -#line 699 "go.y" + case 90: +#line 744 "go.y" { (yyval.node) = (yyvsp[(3) - (7)].node); (yyval.node)->op = OSWITCH; @@ -3261,19 +3140,15 @@ yyreduce: } break; - case 88: - -/* Line 1806 of yacc.c */ -#line 709 "go.y" + case 91: +#line 754 "go.y" { typesw = nod(OXXX, typesw, N); } break; - case 89: - -/* Line 1806 of yacc.c */ -#line 713 "go.y" + case 92: +#line 758 "go.y" { (yyval.node) = nod(OSELECT, N, N); (yyval.node)->lineno = typesw->lineno; @@ -3282,199 +3157,155 @@ yyreduce: } break; - case 91: - -/* Line 1806 of yacc.c */ -#line 726 "go.y" + case 94: +#line 771 "go.y" { (yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 92: - -/* Line 1806 of yacc.c */ -#line 730 "go.y" + case 95: +#line 775 "go.y" { (yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 93: - -/* Line 1806 of yacc.c */ -#line 734 "go.y" + case 96: +#line 779 "go.y" { (yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 94: - -/* Line 1806 of yacc.c */ -#line 738 "go.y" + case 97: +#line 783 "go.y" { (yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 95: - -/* Line 1806 of yacc.c */ -#line 742 "go.y" + case 98: +#line 787 "go.y" { (yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 96: - -/* Line 1806 of yacc.c */ -#line 746 "go.y" + case 99: +#line 791 "go.y" { (yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 97: - -/* Line 1806 of yacc.c */ -#line 750 "go.y" + case 100: +#line 795 "go.y" { (yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 98: - -/* Line 1806 of yacc.c */ -#line 754 "go.y" + case 101: +#line 799 "go.y" { (yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 99: - -/* Line 1806 of yacc.c */ -#line 758 "go.y" + case 102: +#line 803 "go.y" { (yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 100: - -/* Line 1806 of yacc.c */ -#line 762 "go.y" + case 103: +#line 807 "go.y" { (yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 101: - -/* Line 1806 of yacc.c */ -#line 766 "go.y" + case 104: +#line 811 "go.y" { (yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 102: - -/* Line 1806 of yacc.c */ -#line 770 "go.y" + case 105: +#line 815 "go.y" { (yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 103: - -/* Line 1806 of yacc.c */ -#line 774 "go.y" + case 106: +#line 819 "go.y" { (yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 104: - -/* Line 1806 of yacc.c */ -#line 778 "go.y" + case 107: +#line 823 "go.y" { (yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 105: - -/* Line 1806 of yacc.c */ -#line 782 "go.y" + case 108: +#line 827 "go.y" { (yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 106: - -/* Line 1806 of yacc.c */ -#line 786 "go.y" + case 109: +#line 831 "go.y" { (yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 107: - -/* Line 1806 of yacc.c */ -#line 790 "go.y" + case 110: +#line 835 "go.y" { (yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 108: - -/* Line 1806 of yacc.c */ -#line 794 "go.y" + case 111: +#line 839 "go.y" { (yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 109: - -/* Line 1806 of yacc.c */ -#line 798 "go.y" + case 112: +#line 843 "go.y" { (yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 110: - -/* Line 1806 of yacc.c */ -#line 803 "go.y" + case 113: +#line 848 "go.y" { (yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 112: - -/* Line 1806 of yacc.c */ -#line 810 "go.y" + case 115: +#line 855 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; - case 113: - -/* Line 1806 of yacc.c */ -#line 814 "go.y" + case 116: +#line 859 "go.y" { if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) { // Special case for &T{...}: turn into (*T){...}. @@ -3487,84 +3318,66 @@ yyreduce: } break; - case 114: - -/* Line 1806 of yacc.c */ -#line 825 "go.y" + case 117: +#line 870 "go.y" { (yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N); } break; - case 115: - -/* Line 1806 of yacc.c */ -#line 829 "go.y" + case 118: +#line 874 "go.y" { (yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N); } break; - case 116: - -/* Line 1806 of yacc.c */ -#line 833 "go.y" + case 119: +#line 878 "go.y" { (yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N); } break; - case 117: - -/* Line 1806 of yacc.c */ -#line 837 "go.y" + case 120: +#line 882 "go.y" { yyerror("the bitwise complement operator is ^"); (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); } break; - case 118: - -/* Line 1806 of yacc.c */ -#line 842 "go.y" + case 121: +#line 887 "go.y" { (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); } break; - case 119: - -/* Line 1806 of yacc.c */ -#line 846 "go.y" + case 122: +#line 891 "go.y" { (yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N); } break; - case 120: - -/* Line 1806 of yacc.c */ -#line 856 "go.y" + case 123: +#line 901 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N); } break; - case 121: - -/* Line 1806 of yacc.c */ -#line 860 "go.y" + case 124: +#line 905 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); (yyval.node)->list = (yyvsp[(3) - (5)].list); } break; - case 122: - -/* Line 1806 of yacc.c */ -#line 865 "go.y" + case 125: +#line 910 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N); (yyval.node)->list = (yyvsp[(3) - (6)].list); @@ -3572,19 +3385,15 @@ yyreduce: } break; - case 123: - -/* Line 1806 of yacc.c */ -#line 873 "go.y" + case 126: +#line 918 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; - case 125: - -/* Line 1806 of yacc.c */ -#line 878 "go.y" + case 128: +#line 923 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3597,57 +3406,45 @@ yyreduce: } break; - case 126: - -/* Line 1806 of yacc.c */ -#line 889 "go.y" + case 129: +#line 934 "go.y" { (yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node)); } break; - case 127: - -/* Line 1806 of yacc.c */ -#line 893 "go.y" + case 130: +#line 938 "go.y" { (yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node)); } break; - case 128: - -/* Line 1806 of yacc.c */ -#line 897 "go.y" + case 131: +#line 942 "go.y" { (yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); } break; - case 129: - -/* Line 1806 of yacc.c */ -#line 901 "go.y" + case 132: +#line 946 "go.y" { (yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node))); } break; - case 131: - -/* Line 1806 of yacc.c */ -#line 906 "go.y" + case 134: +#line 951 "go.y" { // conversion - (yyval.node) = nod(OCALL, (yyvsp[(1) - (4)].node), N); - (yyval.node)->list = list1((yyvsp[(3) - (4)].node)); + (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); + (yyval.node)->list = list1((yyvsp[(3) - (5)].node)); } break; - case 132: - -/* Line 1806 of yacc.c */ -#line 912 "go.y" + case 135: +#line 957 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3656,10 +3453,8 @@ yyreduce: } break; - case 133: - -/* Line 1806 of yacc.c */ -#line 919 "go.y" + case 136: +#line 964 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3667,10 +3462,8 @@ yyreduce: } break; - case 134: - -/* Line 1806 of yacc.c */ -#line 925 "go.y" + case 137: +#line 970 "go.y" { yyerror("cannot parenthesize type in composite literal"); (yyval.node) = (yyvsp[(5) - (7)].node); @@ -3679,10 +3472,8 @@ yyreduce: } break; - case 136: - -/* Line 1806 of yacc.c */ -#line 934 "go.y" + case 139: +#line 979 "go.y" { // composite expression. // make node early so we get the right line number. @@ -3690,29 +3481,51 @@ yyreduce: } break; - case 137: - -/* Line 1806 of yacc.c */ -#line 942 "go.y" + case 140: +#line 987 "go.y" { (yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 139: + case 141: +#line 993 "go.y" + { + // These nodes do not carry line numbers. + // Since a composite literal commonly spans several lines, + // the line number on errors may be misleading. + // Introduce a wrapper node to give the correct line. + (yyval.node) = (yyvsp[(1) - (1)].node); + switch((yyval.node)->op) { + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + (yyval.node) = nod(OPAREN, (yyval.node), N); + (yyval.node)->implicit = 1; + } + } + break; -/* Line 1806 of yacc.c */ -#line 949 "go.y" + case 142: +#line 1010 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); } break; - case 141: + case 144: +#line 1018 "go.y" + { + (yyval.node) = (yyvsp[(2) - (4)].node); + (yyval.node)->list = (yyvsp[(3) - (4)].list); + } + break; -/* Line 1806 of yacc.c */ -#line 957 "go.y" + case 146: +#line 1026 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -3725,33 +3538,28 @@ yyreduce: case OPACK: case OTYPE: case OLITERAL: + case OTYPESW: (yyval.node) = nod(OPAREN, (yyval.node), N); } } break; - case 145: - -/* Line 1806 of yacc.c */ -#line 982 "go.y" + case 150: +#line 1052 "go.y" { (yyval.i) = LBODY; } break; - case 146: - -/* Line 1806 of yacc.c */ -#line 986 "go.y" + case 151: +#line 1056 "go.y" { (yyval.i) = '{'; } break; - case 147: - -/* Line 1806 of yacc.c */ -#line 997 "go.y" + case 152: +#line 1067 "go.y" { if((yyvsp[(1) - (1)].sym) == S) (yyval.node) = N; @@ -3760,28 +3568,22 @@ yyreduce: } break; - case 148: - -/* Line 1806 of yacc.c */ -#line 1006 "go.y" + case 153: +#line 1076 "go.y" { (yyval.node) = dclname((yyvsp[(1) - (1)].sym)); } break; - case 149: - -/* Line 1806 of yacc.c */ -#line 1011 "go.y" + case 154: +#line 1081 "go.y" { (yyval.node) = N; } break; - case 151: - -/* Line 1806 of yacc.c */ -#line 1018 "go.y" + case 156: +#line 1088 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); // during imports, unqualified non-exported identifiers are from builtinpkg @@ -3790,31 +3592,31 @@ yyreduce: } break; - case 153: - -/* Line 1806 of yacc.c */ -#line 1026 "go.y" + case 158: +#line 1096 "go.y" { (yyval.sym) = S; } break; - case 154: - -/* Line 1806 of yacc.c */ -#line 1032 "go.y" + case 159: +#line 1102 "go.y" { + Pkg *p; + if((yyvsp[(2) - (4)].val).u.sval->len == 0) - (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, importpkg); - else - (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, mkpkg((yyvsp[(2) - (4)].val).u.sval)); + p = importpkg; + else { + if(isbadimport((yyvsp[(2) - (4)].val).u.sval)) + errorexit(); + p = mkpkg((yyvsp[(2) - (4)].val).u.sval); + } + (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, p); } break; - case 155: - -/* Line 1806 of yacc.c */ -#line 1041 "go.y" + case 160: +#line 1117 "go.y" { (yyval.node) = oldname((yyvsp[(1) - (1)].sym)); if((yyval.node)->pack != N) @@ -3822,56 +3624,44 @@ yyreduce: } break; - case 157: - -/* Line 1806 of yacc.c */ -#line 1061 "go.y" + case 162: +#line 1137 "go.y" { yyerror("final argument in variadic function missing type"); (yyval.node) = nod(ODDD, typenod(typ(TINTER)), N); } break; - case 158: - -/* Line 1806 of yacc.c */ -#line 1066 "go.y" + case 163: +#line 1142 "go.y" { (yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N); } break; - case 164: - -/* Line 1806 of yacc.c */ -#line 1077 "go.y" + case 169: +#line 1153 "go.y" { (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N); } break; - case 168: - -/* Line 1806 of yacc.c */ -#line 1086 "go.y" + case 173: +#line 1162 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; - case 173: - -/* Line 1806 of yacc.c */ -#line 1096 "go.y" + case 178: +#line 1172 "go.y" { (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N); } break; - case 183: - -/* Line 1806 of yacc.c */ -#line 1117 "go.y" + case 188: +#line 1193 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3884,77 +3674,61 @@ yyreduce: } break; - case 184: - -/* Line 1806 of yacc.c */ -#line 1130 "go.y" + case 189: +#line 1206 "go.y" { (yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node)); } break; - case 185: - -/* Line 1806 of yacc.c */ -#line 1134 "go.y" + case 190: +#line 1210 "go.y" { // array literal of nelem (yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node)); } break; - case 186: - -/* Line 1806 of yacc.c */ -#line 1139 "go.y" + case 191: +#line 1215 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N); (yyval.node)->etype = Cboth; } break; - case 187: - -/* Line 1806 of yacc.c */ -#line 1144 "go.y" + case 192: +#line 1220 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Csend; } break; - case 188: - -/* Line 1806 of yacc.c */ -#line 1149 "go.y" + case 193: +#line 1225 "go.y" { (yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); } break; - case 191: - -/* Line 1806 of yacc.c */ -#line 1157 "go.y" + case 196: +#line 1233 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; - case 192: - -/* Line 1806 of yacc.c */ -#line 1163 "go.y" + case 197: +#line 1239 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Crecv; } break; - case 193: - -/* Line 1806 of yacc.c */ -#line 1170 "go.y" + case 198: +#line 1246 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3962,20 +3736,16 @@ yyreduce: } break; - case 194: - -/* Line 1806 of yacc.c */ -#line 1176 "go.y" + case 199: +#line 1252 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); fixlbrace((yyvsp[(2) - (3)].i)); } break; - case 195: - -/* Line 1806 of yacc.c */ -#line 1183 "go.y" + case 200: +#line 1259 "go.y" { (yyval.node) = nod(OTINTER, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3983,34 +3753,31 @@ yyreduce: } break; - case 196: - -/* Line 1806 of yacc.c */ -#line 1189 "go.y" + case 201: +#line 1265 "go.y" { (yyval.node) = nod(OTINTER, N, N); fixlbrace((yyvsp[(2) - (3)].i)); } break; - case 197: - -/* Line 1806 of yacc.c */ -#line 1200 "go.y" + case 202: +#line 1276 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); if((yyval.node) == N) break; + if(noescape && (yyvsp[(3) - (3)].list) != nil) + yyerror("can only use //go:noescape with external func implementations"); (yyval.node)->nbody = (yyvsp[(3) - (3)].list); (yyval.node)->endlineno = lineno; + (yyval.node)->noescape = noescape; funcbody((yyval.node)); } break; - case 198: - -/* Line 1806 of yacc.c */ -#line 1211 "go.y" + case 203: +#line 1290 "go.y" { Node *t; @@ -4041,10 +3808,8 @@ yyreduce: } break; - case 199: - -/* Line 1806 of yacc.c */ -#line 1240 "go.y" + case 204: +#line 1319 "go.y" { Node *rcvr, *t; @@ -4077,16 +3842,15 @@ yyreduce: (yyval.node)->nname = methodname1((yyval.node)->shortname, rcvr->right); (yyval.node)->nname->defn = (yyval.node); (yyval.node)->nname->ntype = t; + (yyval.node)->nname->nointerface = nointerface; declare((yyval.node)->nname, PFUNC); funchdr((yyval.node)); } break; - case 200: - -/* Line 1806 of yacc.c */ -#line 1279 "go.y" + case 205: +#line 1359 "go.y" { Sym *s; Type *t; @@ -4098,8 +3862,10 @@ yyreduce: importsym(s, ONAME); if(s->def != N && s->def->op == ONAME) { - if(eqtype(t, s->def->type)) + if(eqtype(t, s->def->type)) { + dclcontext = PDISCARD; // since we skip funchdr below break; + } yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t); } @@ -4111,16 +3877,15 @@ yyreduce: } break; - case 201: - -/* Line 1806 of yacc.c */ -#line 1302 "go.y" + case 206: +#line 1384 "go.y" { (yyval.node) = methodname1(newname((yyvsp[(4) - (8)].sym)), (yyvsp[(2) - (8)].list)->n->right); (yyval.node)->type = functype((yyvsp[(2) - (8)].list)->n, (yyvsp[(6) - (8)].list), (yyvsp[(8) - (8)].list)); checkwidth((yyval.node)->type); - addmethod((yyvsp[(4) - (8)].sym), (yyval.node)->type, 0); + addmethod((yyvsp[(4) - (8)].sym), (yyval.node)->type, 0, nointerface); + nointerface = 0; funchdr((yyval.node)); // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as @@ -4131,10 +3896,8 @@ yyreduce: } break; - case 202: - -/* Line 1806 of yacc.c */ -#line 1319 "go.y" + case 207: +#line 1402 "go.y" { (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1); (yyval.node) = nod(OTFUNC, N, N); @@ -4143,19 +3906,15 @@ yyreduce: } break; - case 203: - -/* Line 1806 of yacc.c */ -#line 1327 "go.y" + case 208: +#line 1410 "go.y" { (yyval.list) = nil; } break; - case 204: - -/* Line 1806 of yacc.c */ -#line 1331 "go.y" + case 209: +#line 1414 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); if((yyval.list) == nil) @@ -4163,149 +3922,119 @@ yyreduce: } break; - case 205: - -/* Line 1806 of yacc.c */ -#line 1339 "go.y" + case 210: +#line 1422 "go.y" { (yyval.list) = nil; } break; - case 206: - -/* Line 1806 of yacc.c */ -#line 1343 "go.y" + case 211: +#line 1426 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node))); } break; - case 207: - -/* Line 1806 of yacc.c */ -#line 1347 "go.y" + case 212: +#line 1430 "go.y" { (yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0); (yyval.list) = (yyvsp[(2) - (3)].list); } break; - case 208: - -/* Line 1806 of yacc.c */ -#line 1354 "go.y" + case 213: +#line 1437 "go.y" { closurehdr((yyvsp[(1) - (1)].node)); } break; - case 209: - -/* Line 1806 of yacc.c */ -#line 1360 "go.y" + case 214: +#line 1443 "go.y" { (yyval.node) = closurebody((yyvsp[(3) - (4)].list)); fixlbrace((yyvsp[(2) - (4)].i)); } break; - case 210: - -/* Line 1806 of yacc.c */ -#line 1365 "go.y" + case 215: +#line 1448 "go.y" { (yyval.node) = closurebody(nil); } break; - case 211: - -/* Line 1806 of yacc.c */ -#line 1376 "go.y" + case 216: +#line 1459 "go.y" { (yyval.list) = nil; } break; - case 212: - -/* Line 1806 of yacc.c */ -#line 1380 "go.y" + case 217: +#line 1463 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list)); if(nsyntaxerrors == 0) testdclstack(); + nointerface = 0; + noescape = 0; } break; - case 214: - -/* Line 1806 of yacc.c */ -#line 1389 "go.y" + case 219: +#line 1474 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 216: - -/* Line 1806 of yacc.c */ -#line 1396 "go.y" + case 221: +#line 1481 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 217: - -/* Line 1806 of yacc.c */ -#line 1402 "go.y" + case 222: +#line 1487 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 218: - -/* Line 1806 of yacc.c */ -#line 1406 "go.y" + case 223: +#line 1491 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 220: - -/* Line 1806 of yacc.c */ -#line 1413 "go.y" + case 225: +#line 1498 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 221: - -/* Line 1806 of yacc.c */ -#line 1419 "go.y" + case 226: +#line 1504 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 222: - -/* Line 1806 of yacc.c */ -#line 1423 "go.y" + case 227: +#line 1508 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 223: - -/* Line 1806 of yacc.c */ -#line 1429 "go.y" + case 228: +#line 1514 "go.y" { NodeList *l; @@ -4330,20 +4059,16 @@ yyreduce: } break; - case 224: - -/* Line 1806 of yacc.c */ -#line 1452 "go.y" + case 229: +#line 1537 "go.y" { (yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val); (yyval.list) = list1((yyvsp[(1) - (2)].node)); } break; - case 225: - -/* Line 1806 of yacc.c */ -#line 1457 "go.y" + case 230: +#line 1542 "go.y" { (yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val); (yyval.list) = list1((yyvsp[(2) - (4)].node)); @@ -4351,10 +4076,8 @@ yyreduce: } break; - case 226: - -/* Line 1806 of yacc.c */ -#line 1463 "go.y" + case 231: +#line 1548 "go.y" { (yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N); (yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val); @@ -4362,10 +4085,8 @@ yyreduce: } break; - case 227: - -/* Line 1806 of yacc.c */ -#line 1469 "go.y" + case 232: +#line 1554 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4374,10 +4095,8 @@ yyreduce: } break; - case 228: - -/* Line 1806 of yacc.c */ -#line 1476 "go.y" + case 233: +#line 1561 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4386,10 +4105,8 @@ yyreduce: } break; - case 229: - -/* Line 1806 of yacc.c */ -#line 1485 "go.y" + case 234: +#line 1570 "go.y" { Node *n; @@ -4400,10 +4117,8 @@ yyreduce: } break; - case 230: - -/* Line 1806 of yacc.c */ -#line 1494 "go.y" + case 235: +#line 1579 "go.y" { Pkg *pkg; @@ -4418,48 +4133,38 @@ yyreduce: } break; - case 231: - -/* Line 1806 of yacc.c */ -#line 1509 "go.y" + case 236: +#line 1594 "go.y" { (yyval.node) = embedded((yyvsp[(1) - (1)].sym)); } break; - case 232: - -/* Line 1806 of yacc.c */ -#line 1515 "go.y" + case 237: +#line 1600 "go.y" { (yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); ifacedcl((yyval.node)); } break; - case 233: - -/* Line 1806 of yacc.c */ -#line 1520 "go.y" + case 238: +#line 1605 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym))); } break; - case 234: - -/* Line 1806 of yacc.c */ -#line 1524 "go.y" + case 239: +#line 1609 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym))); yyerror("cannot parenthesize embedded type"); } break; - case 235: - -/* Line 1806 of yacc.c */ -#line 1531 "go.y" + case 240: +#line 1616 "go.y" { // without func keyword (yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1); @@ -4469,10 +4174,8 @@ yyreduce: } break; - case 237: - -/* Line 1806 of yacc.c */ -#line 1545 "go.y" + case 242: +#line 1630 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4480,10 +4183,8 @@ yyreduce: } break; - case 238: - -/* Line 1806 of yacc.c */ -#line 1551 "go.y" + case 243: +#line 1636 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4491,83 +4192,65 @@ yyreduce: } break; - case 240: - -/* Line 1806 of yacc.c */ -#line 1560 "go.y" + case 245: +#line 1645 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 241: - -/* Line 1806 of yacc.c */ -#line 1564 "go.y" + case 246: +#line 1649 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 242: - -/* Line 1806 of yacc.c */ -#line 1569 "go.y" + case 247: +#line 1654 "go.y" { (yyval.list) = nil; } break; - case 243: - -/* Line 1806 of yacc.c */ -#line 1573 "go.y" + case 248: +#line 1658 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; - case 244: - -/* Line 1806 of yacc.c */ -#line 1581 "go.y" + case 249: +#line 1666 "go.y" { (yyval.node) = N; } break; - case 246: - -/* Line 1806 of yacc.c */ -#line 1586 "go.y" + case 251: +#line 1671 "go.y" { (yyval.node) = liststmt((yyvsp[(1) - (1)].list)); } break; - case 248: - -/* Line 1806 of yacc.c */ -#line 1591 "go.y" + case 253: +#line 1676 "go.y" { (yyval.node) = N; } break; - case 254: - -/* Line 1806 of yacc.c */ -#line 1602 "go.y" + case 259: +#line 1687 "go.y" { (yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N); (yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions } break; - case 255: - -/* Line 1806 of yacc.c */ -#line 1607 "go.y" + case 260: +#line 1692 "go.y" { NodeList *l; @@ -4579,66 +4262,52 @@ yyreduce: } break; - case 256: - -/* Line 1806 of yacc.c */ -#line 1617 "go.y" + case 261: +#line 1702 "go.y" { // will be converted to OFALL (yyval.node) = nod(OXFALL, N, N); } break; - case 257: - -/* Line 1806 of yacc.c */ -#line 1622 "go.y" + case 262: +#line 1707 "go.y" { (yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N); } break; - case 258: - -/* Line 1806 of yacc.c */ -#line 1626 "go.y" + case 263: +#line 1711 "go.y" { (yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N); } break; - case 259: - -/* Line 1806 of yacc.c */ -#line 1630 "go.y" + case 264: +#line 1715 "go.y" { (yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N); } break; - case 260: - -/* Line 1806 of yacc.c */ -#line 1634 "go.y" + case 265: +#line 1719 "go.y" { (yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N); } break; - case 261: - -/* Line 1806 of yacc.c */ -#line 1638 "go.y" + case 266: +#line 1723 "go.y" { (yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N); (yyval.node)->sym = dclstack; // context, for goto restrictions } break; - case 262: - -/* Line 1806 of yacc.c */ -#line 1643 "go.y" + case 267: +#line 1728 "go.y" { (yyval.node) = nod(ORETURN, N, N); (yyval.node)->list = (yyvsp[(2) - (2)].list); @@ -4657,10 +4326,8 @@ yyreduce: } break; - case 263: - -/* Line 1806 of yacc.c */ -#line 1662 "go.y" + case 268: +#line 1747 "go.y" { (yyval.list) = nil; if((yyvsp[(1) - (1)].node) != N) @@ -4668,10 +4335,8 @@ yyreduce: } break; - case 264: - -/* Line 1806 of yacc.c */ -#line 1668 "go.y" + case 269: +#line 1753 "go.y" { (yyval.list) = (yyvsp[(1) - (3)].list); if((yyvsp[(3) - (3)].node) != N) @@ -4679,247 +4344,195 @@ yyreduce: } break; - case 265: - -/* Line 1806 of yacc.c */ -#line 1676 "go.y" + case 270: +#line 1761 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 266: - -/* Line 1806 of yacc.c */ -#line 1680 "go.y" + case 271: +#line 1765 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 267: - -/* Line 1806 of yacc.c */ -#line 1686 "go.y" + case 272: +#line 1771 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 268: - -/* Line 1806 of yacc.c */ -#line 1690 "go.y" + case 273: +#line 1775 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 269: - -/* Line 1806 of yacc.c */ -#line 1696 "go.y" + case 274: +#line 1781 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 270: - -/* Line 1806 of yacc.c */ -#line 1700 "go.y" + case 275: +#line 1785 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 271: - -/* Line 1806 of yacc.c */ -#line 1706 "go.y" + case 276: +#line 1791 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 272: - -/* Line 1806 of yacc.c */ -#line 1710 "go.y" + case 277: +#line 1795 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 273: - -/* Line 1806 of yacc.c */ -#line 1719 "go.y" + case 278: +#line 1804 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 274: - -/* Line 1806 of yacc.c */ -#line 1723 "go.y" + case 279: +#line 1808 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 275: - -/* Line 1806 of yacc.c */ -#line 1727 "go.y" + case 280: +#line 1812 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 276: - -/* Line 1806 of yacc.c */ -#line 1731 "go.y" + case 281: +#line 1816 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 277: - -/* Line 1806 of yacc.c */ -#line 1736 "go.y" + case 282: +#line 1821 "go.y" { (yyval.list) = nil; } break; - case 278: - -/* Line 1806 of yacc.c */ -#line 1740 "go.y" + case 283: +#line 1825 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; - case 283: - -/* Line 1806 of yacc.c */ -#line 1754 "go.y" + case 288: +#line 1839 "go.y" { (yyval.node) = N; } break; - case 285: - -/* Line 1806 of yacc.c */ -#line 1760 "go.y" + case 290: +#line 1845 "go.y" { (yyval.list) = nil; } break; - case 287: - -/* Line 1806 of yacc.c */ -#line 1766 "go.y" + case 292: +#line 1851 "go.y" { (yyval.node) = N; } break; - case 289: - -/* Line 1806 of yacc.c */ -#line 1772 "go.y" + case 294: +#line 1857 "go.y" { (yyval.list) = nil; } break; - case 291: - -/* Line 1806 of yacc.c */ -#line 1778 "go.y" + case 296: +#line 1863 "go.y" { (yyval.list) = nil; } break; - case 293: - -/* Line 1806 of yacc.c */ -#line 1784 "go.y" + case 298: +#line 1869 "go.y" { (yyval.list) = nil; } break; - case 295: - -/* Line 1806 of yacc.c */ -#line 1790 "go.y" + case 300: +#line 1875 "go.y" { (yyval.val).ctype = CTxxx; } break; - case 297: - -/* Line 1806 of yacc.c */ -#line 1800 "go.y" + case 302: +#line 1885 "go.y" { importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval); } break; - case 298: - -/* Line 1806 of yacc.c */ -#line 1804 "go.y" + case 303: +#line 1889 "go.y" { importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type)); } break; - case 299: - -/* Line 1806 of yacc.c */ -#line 1808 "go.y" + case 304: +#line 1893 "go.y" { importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node)); } break; - case 300: - -/* Line 1806 of yacc.c */ -#line 1812 "go.y" + case 305: +#line 1897 "go.y" { importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node)); } break; - case 301: - -/* Line 1806 of yacc.c */ -#line 1816 "go.y" + case 306: +#line 1901 "go.y" { importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type)); } break; - case 302: - -/* Line 1806 of yacc.c */ -#line 1820 "go.y" + case 307: +#line 1905 "go.y" { - if((yyvsp[(2) - (4)].node) == N) + if((yyvsp[(2) - (4)].node) == N) { + dclcontext = PEXTERN; // since we skip the funcbody below break; + } (yyvsp[(2) - (4)].node)->inl = (yyvsp[(3) - (4)].list); @@ -4928,45 +4541,37 @@ yyreduce: if(debug['E']) { print("import [%Z] func %lN \n", importpkg->path, (yyvsp[(2) - (4)].node)); - if(debug['l'] > 2 && (yyvsp[(2) - (4)].node)->inl) + if(debug['m'] > 2 && (yyvsp[(2) - (4)].node)->inl) print("inl body:%+H\n", (yyvsp[(2) - (4)].node)->inl); } } break; - case 303: - -/* Line 1806 of yacc.c */ -#line 1838 "go.y" + case 308: +#line 1925 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); structpkg = (yyval.sym)->pkg; } break; - case 304: - -/* Line 1806 of yacc.c */ -#line 1845 "go.y" + case 309: +#line 1932 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); importsym((yyvsp[(1) - (1)].sym), OTYPE); } break; - case 310: - -/* Line 1806 of yacc.c */ -#line 1865 "go.y" + case 315: +#line 1952 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); } break; - case 311: - -/* Line 1806 of yacc.c */ -#line 1869 "go.y" + case 316: +#line 1956 "go.y" { // predefined name like uint8 (yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg); @@ -4978,64 +4583,50 @@ yyreduce: } break; - case 312: - -/* Line 1806 of yacc.c */ -#line 1879 "go.y" + case 317: +#line 1966 "go.y" { (yyval.type) = aindex(N, (yyvsp[(3) - (3)].type)); } break; - case 313: - -/* Line 1806 of yacc.c */ -#line 1883 "go.y" + case 318: +#line 1970 "go.y" { (yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type)); } break; - case 314: - -/* Line 1806 of yacc.c */ -#line 1887 "go.y" + case 319: +#line 1974 "go.y" { (yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type)); } break; - case 315: - -/* Line 1806 of yacc.c */ -#line 1891 "go.y" + case 320: +#line 1978 "go.y" { (yyval.type) = tostruct((yyvsp[(3) - (4)].list)); } break; - case 316: - -/* Line 1806 of yacc.c */ -#line 1895 "go.y" + case 321: +#line 1982 "go.y" { (yyval.type) = tointerface((yyvsp[(3) - (4)].list)); } break; - case 317: - -/* Line 1806 of yacc.c */ -#line 1899 "go.y" + case 322: +#line 1986 "go.y" { (yyval.type) = ptrto((yyvsp[(2) - (2)].type)); } break; - case 318: - -/* Line 1806 of yacc.c */ -#line 1903 "go.y" + case 323: +#line 1990 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(2) - (2)].type); @@ -5043,10 +4634,8 @@ yyreduce: } break; - case 319: - -/* Line 1806 of yacc.c */ -#line 1909 "go.y" + case 324: +#line 1996 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (4)].type); @@ -5054,10 +4643,8 @@ yyreduce: } break; - case 320: - -/* Line 1806 of yacc.c */ -#line 1915 "go.y" + case 325: +#line 2002 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -5065,10 +4652,8 @@ yyreduce: } break; - case 321: - -/* Line 1806 of yacc.c */ -#line 1923 "go.y" + case 326: +#line 2010 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -5076,19 +4661,15 @@ yyreduce: } break; - case 322: - -/* Line 1806 of yacc.c */ -#line 1931 "go.y" + case 327: +#line 2018 "go.y" { (yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)); } break; - case 323: - -/* Line 1806 of yacc.c */ -#line 1937 "go.y" + case 328: +#line 2024 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type))); if((yyvsp[(1) - (3)].sym)) @@ -5097,10 +4678,8 @@ yyreduce: } break; - case 324: - -/* Line 1806 of yacc.c */ -#line 1944 "go.y" + case 329: +#line 2031 "go.y" { Type *t; @@ -5116,10 +4695,8 @@ yyreduce: } break; - case 325: - -/* Line 1806 of yacc.c */ -#line 1960 "go.y" + case 330: +#line 2047 "go.y" { Sym *s; @@ -5137,64 +4714,50 @@ yyreduce: } break; - case 326: - -/* Line 1806 of yacc.c */ -#line 1978 "go.y" + case 331: +#line 2065 "go.y" { (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)))); } break; - case 327: - -/* Line 1806 of yacc.c */ -#line 1982 "go.y" + case 332: +#line 2069 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))); } break; - case 328: - -/* Line 1806 of yacc.c */ -#line 1987 "go.y" + case 333: +#line 2074 "go.y" { (yyval.list) = nil; } break; - case 330: - -/* Line 1806 of yacc.c */ -#line 1994 "go.y" + case 335: +#line 2081 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); } break; - case 331: - -/* Line 1806 of yacc.c */ -#line 1998 "go.y" + case 336: +#line 2085 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)))); } break; - case 332: - -/* Line 1806 of yacc.c */ -#line 2008 "go.y" + case 337: +#line 2095 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; - case 333: - -/* Line 1806 of yacc.c */ -#line 2012 "go.y" + case 338: +#line 2099 "go.y" { (yyval.node) = nodlit((yyvsp[(2) - (2)].val)); switch((yyval.node)->val.ctype){ @@ -5211,10 +4774,8 @@ yyreduce: } break; - case 334: - -/* Line 1806 of yacc.c */ -#line 2027 "go.y" + case 339: +#line 2114 "go.y" { (yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg)); if((yyval.node)->op != OLITERAL) @@ -5222,91 +4783,67 @@ yyreduce: } break; - case 336: - -/* Line 1806 of yacc.c */ -#line 2036 "go.y" + case 341: +#line 2123 "go.y" { if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) { (yyval.node) = (yyvsp[(2) - (5)].node); mpaddfixfix((yyvsp[(2) - (5)].node)->val.u.xval, (yyvsp[(4) - (5)].node)->val.u.xval, 0); break; } + (yyvsp[(4) - (5)].node)->val.u.cval->real = (yyvsp[(4) - (5)].node)->val.u.cval->imag; + mpmovecflt(&(yyvsp[(4) - (5)].node)->val.u.cval->imag, 0.0); (yyval.node) = nodcplxlit((yyvsp[(2) - (5)].node)->val, (yyvsp[(4) - (5)].node)->val); } break; - case 339: - -/* Line 1806 of yacc.c */ -#line 2050 "go.y" + case 344: +#line 2139 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 340: - -/* Line 1806 of yacc.c */ -#line 2054 "go.y" + case 345: +#line 2143 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 341: - -/* Line 1806 of yacc.c */ -#line 2060 "go.y" + case 346: +#line 2149 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 342: - -/* Line 1806 of yacc.c */ -#line 2064 "go.y" + case 347: +#line 2153 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 343: - -/* Line 1806 of yacc.c */ -#line 2070 "go.y" + case 348: +#line 2159 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 344: - -/* Line 1806 of yacc.c */ -#line 2074 "go.y" + case 349: +#line 2163 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - -/* Line 1806 of yacc.c */ -#line 5298 "y.tab.c" +/* Line 1267 of yacc.c. */ +#line 4846 "y.tab.c" default: break; } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -5315,6 +4852,7 @@ yyreduce: *++yyvsp = yyval; + /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ @@ -5334,10 +4872,6 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); - /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -5345,36 +4879,37 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else -# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ - yyssp, yytoken) { - char *yymsgp = YY_("syntax error"); - int yysyntax_error_status; - yysyntax_error_status = YYSYNTAX_ERROR; - if (yysyntax_error_status == 0) - yymsgp = yymsg; - else if (yysyntax_error_status == 1) - { - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); - if (!yymsg) - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - yysyntax_error_status = 2; - } - else - { - yysyntax_error_status = YYSYNTAX_ERROR; - yymsgp = yymsg; - } - } - yyerror (yymsgp); - if (yysyntax_error_status == 2) - goto yyexhaustedlab; + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } } -# undef YYSYNTAX_ERROR #endif } @@ -5382,7 +4917,7 @@ yyerrlab: if (yyerrstatus == 3) { - /* If just tried and failed to reuse lookahead token after an + /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) @@ -5399,7 +4934,7 @@ yyerrlab: } } - /* Else will try to reuse lookahead token after shifting the error + /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; @@ -5433,7 +4968,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (!yypact_value_is_default (yyn)) + if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -5456,6 +4991,9 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } + if (yyn == YYFINAL) + YYACCEPT; + *++yyvsp = yylval; @@ -5480,7 +5018,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -5491,14 +5029,9 @@ yyexhaustedlab: #endif yyreturn: - if (yychar != YYEMPTY) - { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE (yychar); - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - } + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); @@ -5522,9 +5055,7 @@ yyreturn: } - -/* Line 2067 of yacc.c */ -#line 2078 "go.y" +#line 2167 "go.y" static void diff --git a/src/cmd/gc/y.tab.h b/src/cmd/gc/y.tab.h index bc6c47d6d..d01fbe198 100644 --- a/src/cmd/gc/y.tab.h +++ b/src/cmd/gc/y.tab.h @@ -1,21 +1,24 @@ -/* A Bison parser, made by GNU Bison 2.5. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Bison interface for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - + the Free Software Foundation; either version 2, or (at your option) + any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -26,11 +29,10 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ - /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -39,10 +41,10 @@ enum yytokentype { LLITERAL = 258, LASOP = 259, - LBREAK = 260, - LCASE = 261, - LCHAN = 262, - LCOLAS = 263, + LCOLAS = 260, + LBREAK = 261, + LCASE = 262, + LCHAN = 263, LCONST = 264, LCONTINUE = 265, LDDD = 266, @@ -91,10 +93,10 @@ /* Tokens. */ #define LLITERAL 258 #define LASOP 259 -#define LBREAK 260 -#define LCASE 261 -#define LCHAN 262 -#define LCOLAS 263 +#define LCOLAS 260 +#define LBREAK 261 +#define LCASE 262 +#define LCHAN 263 #define LCONST 264 #define LCONTINUE 265 #define LDDD 266 @@ -144,28 +146,22 @@ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -{ - -/* Line 2068 of yacc.c */ #line 28 "go.y" - +{ Node* node; NodeList* list; Type* type; Sym* sym; struct Val val; int i; - - - -/* Line 2068 of yacc.c */ -#line 163 "y.tab.h" -} YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 +} +/* Line 1529 of yacc.c. */ +#line 160 "y.tab.h" + YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE yylval; - diff --git a/src/cmd/gc/yerr.h b/src/cmd/gc/yerr.h index 588890d0e..e7eb6516c 100644 --- a/src/cmd/gc/yerr.h +++ b/src/cmd/gc/yerr.h @@ -26,7 +26,7 @@ static struct { 237, ';', "unexpected semicolon or newline before {", - 474, LBODY, + 475, LBODY, "unexpected semicolon or newline before {", 22, '{', @@ -44,7 +44,7 @@ static struct { 37, ',', "unexpected comma in channel type", - 437, LELSE, + 438, LELSE, "unexpected semicolon or newline before else", 257, ',', @@ -65,9 +65,12 @@ static struct { 425, ';', "need trailing comma before newline in composite literal", + 436, ';', + "need trailing comma before newline in composite literal", + 112, LNAME, "nested func not allowed", - 615, ';', + 642, ';', "else must be followed by if or statement block" }; diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 767ddfd40..38fc43ef1 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -8,6 +8,7 @@ import ( "bytes" "container/heap" "errors" + "flag" "fmt" "go/build" "io" @@ -59,7 +60,12 @@ The build flags are shared by the build, install, run, and test commands: do not delete it when exiting. -x print the commands. + -race + enable data race detection. + Supported only on linux/amd64, darwin/amd64 and windows/amd64. + -ccflags 'arg list' + arguments to pass on each 5c, 6c, or 8c compiler invocation -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc) -gccgoflags 'arg list' @@ -73,6 +79,9 @@ The build flags are shared by the build, install, run, and test commands: See the documentation for the go/build package for more information about build tags. +The list flags accept a space-separated list of strings. To embed spaces +in an element in the list, surround it with either single or double quotes. + For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, see 'go help gopath'. @@ -99,8 +108,10 @@ var buildX bool // -x flag var buildO = cmdBuild.Flag.String("o", "", "output file") var buildWork bool // -work flag var buildGcflags []string // -gcflags flag +var buildCcflags []string // -ccflags flag var buildLdflags []string // -ldflags flag var buildGccgoflags []string // -gccgoflags flag +var buildRace bool // -race flag var buildContext = build.Default var buildToolchain toolchain = noToolchain{} @@ -115,7 +126,7 @@ func (c buildCompiler) Set(value string) error { case "gc": buildToolchain = gcToolchain{} case "gccgo": - buildToolchain = gccgcToolchain{} + buildToolchain = gccgoToolchain{} default: return fmt.Errorf("unknown compiler %q", value) } @@ -132,7 +143,7 @@ func init() { case "gc": buildToolchain = gcToolchain{} case "gccgo": - buildToolchain = gccgcToolchain{} + buildToolchain = gccgoToolchain{} } } @@ -146,10 +157,12 @@ func addBuildFlags(cmd *Command) { cmd.Flag.BoolVar(&buildX, "x", false, "") cmd.Flag.BoolVar(&buildWork, "work", false, "") cmd.Flag.Var((*stringsFlag)(&buildGcflags), "gcflags", "") + cmd.Flag.Var((*stringsFlag)(&buildCcflags), "ccflags", "") cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "") cmd.Flag.Var((*stringsFlag)(&buildGccgoflags), "gccgoflags", "") cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") cmd.Flag.Var(buildCompiler{}, "compiler", "") + cmd.Flag.BoolVar(&buildRace, "race", false, "") } func addBuildFlagsNX(cmd *Command) { @@ -157,11 +170,52 @@ func addBuildFlagsNX(cmd *Command) { cmd.Flag.BoolVar(&buildX, "x", false, "") } +func isSpaceByte(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} + type stringsFlag []string func (v *stringsFlag) Set(s string) error { - *v = strings.Fields(s) - return nil + var err error + *v, err = splitQuotedFields(s) + return err +} + +func splitQuotedFields(s string) ([]string, error) { + // Split fields allowing '' or "" around elements. + // Quotes further inside the string do not count. + var f []string + for len(s) > 0 { + for len(s) > 0 && isSpaceByte(s[0]) { + s = s[1:] + } + if len(s) == 0 { + break + } + // Accepted quoted string. No unescaping inside. + if s[0] == '"' || s[0] == '\'' { + quote := s[0] + s = s[1:] + i := 0 + for i < len(s) && s[i] != quote { + i++ + } + if i >= len(s) { + return nil, fmt.Errorf("unterminated %c string", quote) + } + f = append(f, s[:i]) + s = s[i+1:] + continue + } + i := 0 + for i < len(s) && !isSpaceByte(s[i]) { + i++ + } + f = append(f, s[:i]) + s = s[i:] + } + return f, nil } func (v *stringsFlag) String() string { @@ -169,6 +223,7 @@ func (v *stringsFlag) String() string { } func runBuild(cmd *Command, args []string) { + raceInit() var b builder b.init() @@ -179,6 +234,21 @@ func runBuild(cmd *Command, args []string) { *buildO += exeSuffix } + // sanity check some often mis-used options + switch buildContext.Compiler { + case "gccgo": + if len(buildGcflags) != 0 { + fmt.Println("go build: when using gccgo toolchain, please pass compiler flags using -gccgoflags, not -gcflags") + } + if len(buildLdflags) != 0 { + fmt.Println("go build: when using gccgo toolchain, please pass linker flags using -gccgoflags, not -ldflags") + } + case "gc": + if len(buildGccgoflags) != 0 { + fmt.Println("go build: when using gc toolchain, please pass compile flags using -gcflags, and linker flags using -ldflags") + } + } + if *buildO != "" { if len(pkgs) > 1 { fatalf("go build: cannot use -o with multiple packages") @@ -213,11 +283,12 @@ See also: go build, go get, go clean. } func runInstall(cmd *Command, args []string) { + raceInit() pkgs := packagesForBuild(args) for _, p := range pkgs { if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { - errorf("go install: no install location for %s", p.ImportPath) + errorf("go install: no install location for directory %s outside GOPATH", p.Dir) } } exitIfErrors() @@ -437,7 +508,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action // using cgo, to make sure we do not overwrite the binary while // a package is using it. If this is a cross-build, then the cgo we // are writing is not the cgo we need to use. - if goos == runtime.GOOS && goarch == runtime.GOARCH { + if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace { if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" { var stk importStack p1 := loadPackage("cmd/cgo", &stk) @@ -456,7 +527,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action return a } // gccgo standard library is "fake" too. - if _, ok := buildToolchain.(gccgcToolchain); ok { + if _, ok := buildToolchain.(gccgoToolchain); ok { // the target name is needed for cgo. a.target = p.target return a @@ -487,9 +558,18 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action a.f = (*builder).build a.target = a.objpkg if a.link { - // An executable file. - // (This is the name of a temporary file.) - a.target = a.objdir + "a.out" + exeSuffix + // An executable file. (This is the name of a temporary file.) + // Because we run the temporary file in 'go run' and 'go test', + // the name will show up in ps listings. If the caller has specified + // a name, use that instead of a.out. The binary is generated + // in an otherwise empty subdirectory named exe to avoid + // naming conflicts. The only possible conflict is if we were + // to create a top-level package named exe. + name := "a.out" + if p.exeName != "" { + name = p.exeName + } + a.target = a.objdir + filepath.Join("exe", name) + exeSuffix } } @@ -535,7 +615,6 @@ func (b *builder) do(root *action) { } b.readySema = make(chan bool, len(all)) - done := make(chan bool) // Initialize per-action execution state. for _, a := range all { @@ -583,10 +662,11 @@ func (b *builder) do(root *action) { if a == root { close(b.readySema) - done <- true } } + var wg sync.WaitGroup + // Kick off goroutines according to parallelism. // If we are using the -n flag (just printing commands) // drop the parallelism to 1, both to make the output @@ -596,19 +676,40 @@ func (b *builder) do(root *action) { par = 1 } for i := 0; i < par; i++ { + wg.Add(1) go func() { - for _ = range b.readySema { - // Receiving a value from b.sema entitles - // us to take from the ready queue. - b.exec.Lock() - a := b.ready.pop() - b.exec.Unlock() - handle(a) + defer wg.Done() + for { + select { + case _, ok := <-b.readySema: + if !ok { + return + } + // Receiving a value from b.readySema entitles + // us to take from the ready queue. + b.exec.Lock() + a := b.ready.pop() + b.exec.Unlock() + handle(a) + case <-interrupted: + setExitStatus(1) + return + } } }() } - <-done + wg.Wait() +} + +// hasString reports whether s appears in the list of strings. +func hasString(strings []string, s string) bool { + for _, t := range strings { + if s == t { + return true + } + } + return false } // build is the action for building a single package or command. @@ -631,12 +732,25 @@ func (b *builder) build(a *action) (err error) { fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath) } + if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && + !hasString(a.p.HFiles, "zasm_"+buildContext.GOOS+"_"+buildContext.GOARCH+".h") { + return fmt.Errorf("%s/%s must be bootstrapped using make.bash", buildContext.GOOS, buildContext.GOARCH) + } + // Make build directory. obj := a.objdir if err := b.mkdir(obj); err != nil { return err } + // make target directory + dir, _ := filepath.Split(a.target) + if dir != "" { + if err := b.mkdir(dir); err != nil { + return err + } + } + var gofiles, cfiles, sfiles, objects, cgoObjects []string gofiles = append(gofiles, a.p.GoFiles...) cfiles = append(cfiles, a.p.CFiles...) @@ -680,16 +794,37 @@ func (b *builder) build(a *action) (err error) { gofiles = append(gofiles, outGo...) } + // Run SWIG. + if a.p.usesSwig() { + // In a package using SWIG, any .c or .s files are + // compiled with gcc. + gccfiles := append(cfiles, sfiles...) + cfiles = nil + sfiles = nil + outGo, outObj, err := b.swig(a.p, obj, gccfiles) + if err != nil { + return err + } + cgoObjects = append(cgoObjects, outObj...) + gofiles = append(gofiles, outGo...) + } + // Prepare Go import path list. inc := b.includeArgs("-I", a.deps) // Compile Go. if len(gofiles) > 0 { - if out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles); err != nil { + ofile, out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles) + if len(out) > 0 { + b.showOutput(a.p.Dir, a.p.ImportPath, b.processOutput(out)) + if err != nil { + return errPrintedOutput + } + } + if err != nil { return err - } else { - objects = append(objects, out) } + objects = append(objects, ofile) } // Copy .h files named for goos or goarch or goos_goarch @@ -718,8 +853,13 @@ func (b *builder) build(a *action) (err error) { } } + objExt := archChar + if _, ok := buildToolchain.(gccgoToolchain); ok { + objExt = "o" + } + for _, file := range cfiles { - out := file[:len(file)-len(".c")] + "." + archChar + out := file[:len(file)-len(".c")] + "." + objExt if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil { return err } @@ -728,7 +868,7 @@ func (b *builder) build(a *action) (err error) { // Assemble .s files. for _, file := range sfiles { - out := file[:len(file)-len(".s")] + "." + archChar + out := file[:len(file)-len(".s")] + "." + objExt if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil { return err } @@ -795,6 +935,20 @@ func (b *builder) install(a *action) (err error) { defer os.Remove(a1.target) } + if a.p.usesSwig() { + for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) { + dir = a.p.swigDir(&buildContext) + if err := b.mkdir(dir); err != nil { + return err + } + soname := a.p.swigSoname(f) + target := filepath.Join(dir, soname) + if err = b.copyFile(a, target, soname, perm); err != nil { + return err + } + } + } + return b.copyFile(a, a.target, a1.target, perm) } @@ -826,10 +980,13 @@ func (b *builder) includeArgs(flag string, all []*action) []string { for _, a1 := range all { if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] { incMap[dir] = true - if _, ok := buildToolchain.(gccgcToolchain); ok { - dir = filepath.Join(dir, "gccgo") + if _, ok := buildToolchain.(gccgoToolchain); ok { + dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch) } else { dir = filepath.Join(dir, goos+"_"+goarch) + if buildRace { + dir += "_race" + } } inc = append(inc, flag, dir) } @@ -905,6 +1062,8 @@ var objectMagic = [][]byte{ {0xCE, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 32-bit {0xCF, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 64-bit {0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00}, // PE (Windows) as generated by 6l/8l + {0x00, 0x00, 0x01, 0xEB}, // Plan 9 i386 + {0x00, 0x00, 0x8a, 0x97}, // Plan 9 amd64 } func isObject(s string) bool { @@ -1024,19 +1183,18 @@ func relPaths(paths []string) []string { // print this error. var errPrintedOutput = errors.New("already printed output - no need to show error") +var cgoLine = regexp.MustCompile(`\[[^\[\]]+\.cgo1\.go:[0-9]+\]`) + // run runs the command given by cmdline in the directory dir. // If the command fails, run prints information about the failure // and returns a non-nil error. func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error { out, err := b.runOut(dir, desc, cmdargs...) if len(out) > 0 { - if out[len(out)-1] != '\n' { - out = append(out, '\n') - } if desc == "" { desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " ")) } - b.showOutput(dir, desc, string(out)) + b.showOutput(dir, desc, b.processOutput(out)) if err != nil { err = errPrintedOutput } @@ -1044,6 +1202,23 @@ func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error { return err } +// processOutput prepares the output of runOut to be output to the console. +func (b *builder) processOutput(out []byte) string { + if out[len(out)-1] != '\n' { + out = append(out, '\n') + } + messages := string(out) + // Fix up output referring to cgo-generated code to be more readable. + // Replace x.go:19[/tmp/.../x.cgo1.go:18] with x.go:19. + // Replace _Ctype_foo with C.foo. + // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite. + if !buildX && cgoLine.MatchString(messages) { + messages = cgoLine.ReplaceAllString(messages, "") + messages = strings.Replace(messages, "type _Ctype_", "type C.", -1) + } + return messages +} + // runOut runs the command given by cmdline in the directory dir. // It returns the command output and any errors that occurred. func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byte, error) { @@ -1062,7 +1237,7 @@ func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byt cmd.Stdout = &buf cmd.Stderr = &buf cmd.Dir = dir - // TODO: cmd.Env + cmd.Env = envForDir(cmd.Dir) err := cmd.Run() // cmd.Run will fail on Unix if some other process has the binary @@ -1077,7 +1252,7 @@ func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byt // not worry about other processes inheriting the fd accidentally. // The answer is that running a command is fork and exec. // A child forked while the cgo fd is open inherits that fd. - // Until the child has called exec, it holds the fd open and the + // Until the child has called exec, it holds the fd open and the // kernel will not let us run cgo. Even if the child were to close // the fd explicitly, it would still be open from the time of the fork // until the time of the explicit close, and the race would remain. @@ -1161,7 +1336,7 @@ type toolchain interface { // gc runs the compiler in a specific directory on a set of files // and returns the name of the generated output file. // The compiler runs in the directory dir. - gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) + gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error) // cc runs the toolchain's C compiler in a directory on a C file // to produce an output file. cc(b *builder, p *Package, objdir, ofile, cfile string) error @@ -1198,8 +1373,8 @@ func (noToolchain) linker() string { return "" } -func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { - return "", noCompiler() +func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error) { + return "", nil, noCompiler() } func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { @@ -1234,7 +1409,7 @@ func (gcToolchain) linker() string { return tool(archChar + "l") } -func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { +func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { out := "_go_." + archChar ofile = obj + out gcargs := []string{"-p", p.ImportPath} @@ -1244,16 +1419,33 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g gcargs = append(gcargs, "-+") } + // If we're giving the compiler the entire package (no C etc files), tell it that, + // so that it can give good error messages about forward declarations. + // Exceptions: a few standard packages have forward declarations for + // pieces supplied behind-the-scenes by package runtime. + extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) + if p.Standard { + switch p.ImportPath { + case "os", "runtime/pprof", "sync", "time": + extFiles++ + } + } + if extFiles == 0 { + gcargs = append(gcargs, "-complete") + } + args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs) for _, f := range gofiles { args = append(args, mkAbs(p.Dir, f)) } - return ofile, b.run(p.Dir, p.ImportPath, args) + + output, err = b.runOut(p.Dir, p.ImportPath, args) + return ofile, output, err } func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { sfile = mkAbs(p.Dir, sfile) - return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile) + return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile) } func (gcToolchain) pkgpath(basedir string, p *Package) string { @@ -1266,62 +1458,86 @@ func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s for _, f := range ofiles { absOfiles = append(absOfiles, mkAbs(objDir, f)) } - return b.run(p.Dir, p.ImportPath, tool("pack"), "grc", mkAbs(objDir, afile), absOfiles) + return b.run(p.Dir, p.ImportPath, tool("pack"), "grcP", b.work, mkAbs(objDir, afile), absOfiles) } func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { importArgs := b.includeArgs("-L", allactions) - return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, buildLdflags, mainpkg) + swigDirs := make(map[string]bool) + swigArg := []string{} + for _, a := range allactions { + if a.p != nil && a.p.usesSwig() { + sd := a.p.swigDir(&buildContext) + if len(swigArg) == 0 { + swigArg = []string{"-r", sd} + } else if !swigDirs[sd] { + swigArg[1] += ":" + swigArg[1] += sd + } + swigDirs[sd] = true + } + } + return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, swigArg, buildLdflags, mainpkg) } func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) cfile = mkAbs(p.Dir, cfile) - return b.run(p.Dir, p.ImportPath, tool(archChar+"c"), "-FVw", - "-I", objdir, "-I", inc, "-o", ofile, - "-DGOOS_"+goos, "-DGOARCH_"+goarch, cfile) + args := stringList(tool(archChar+"c"), "-F", "-V", "-w", "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile) + return b.run(p.Dir, p.ImportPath, args) } // The Gccgo toolchain. -type gccgcToolchain struct{} +type gccgoToolchain struct{} var gccgoBin, _ = exec.LookPath("gccgo") -func (gccgcToolchain) compiler() string { +func (gccgoToolchain) compiler() string { return gccgoBin } -func (gccgcToolchain) linker() string { +func (gccgoToolchain) linker() string { return gccgoBin } -func (gccgcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { +func (gccgoToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { out := p.Name + ".o" ofile = obj + out gcargs := []string{"-g"} - if prefix := gccgoPrefix(p); prefix != "" { - gcargs = append(gcargs, "-fgo-prefix="+gccgoPrefix(p)) + gcargs = append(gcargs, b.gccArchArgs()...) + if pkgpath := gccgoPkgpath(p); pkgpath != "" { + gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath) + } + if p.localPrefix != "" { + gcargs = append(gcargs, "-fgo-relative-import-path="+p.localPrefix) } args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags) for _, f := range gofiles { args = append(args, mkAbs(p.Dir, f)) } - return ofile, b.run(p.Dir, p.ImportPath, args) + + output, err = b.runOut(p.Dir, p.ImportPath, args) + return ofile, output, err } -func (gccgcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { +func (gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { sfile = mkAbs(p.Dir, sfile) - return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) + } + defs = append(defs, b.gccArchArgs()...) + return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, defs, sfile) } -func (gccgcToolchain) pkgpath(basedir string, p *Package) string { +func (gccgoToolchain) pkgpath(basedir string, p *Package) string { end := filepath.FromSlash(p.ImportPath + ".a") afile := filepath.Join(basedir, end) // add "lib" to the final element return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile)) } -func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { +func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { var absOfiles []string for _, f := range ofiles { absOfiles = append(absOfiles, mkAbs(objDir, f)) @@ -1329,12 +1545,14 @@ func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles return b.run(p.Dir, p.ImportPath, "ar", "cru", mkAbs(objDir, afile), absOfiles) } -func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { // gccgo needs explicit linking with all package dependencies, // and all LDFLAGS from cgo dependencies. afiles := make(map[*Package]string) - ldflags := []string{} + sfiles := make(map[*Package][]string) + ldflags := b.gccArchArgs() cgoldflags := []string{} + usesCgo := false for _, a := range allactions { if a.p != nil { if !a.p.Standard { @@ -1343,31 +1561,99 @@ func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions [] } } cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...) + if len(a.p.CgoFiles) > 0 { + usesCgo = true + } + if a.p.usesSwig() { + sd := a.p.swigDir(&buildContext) + for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) { + soname := a.p.swigSoname(f) + sfiles[a.p] = append(sfiles[a.p], filepath.Join(sd, soname)) + } + usesCgo = true + } } } for _, afile := range afiles { ldflags = append(ldflags, afile) } + for _, sfiles := range sfiles { + ldflags = append(ldflags, sfiles...) + } ldflags = append(ldflags, cgoldflags...) - return b.run(".", p.ImportPath, "gccgo", "-o", out, buildGccgoflags, ofiles, "-Wl,-(", ldflags, "-Wl,-)") + if usesCgo && goos == "linux" { + ldflags = append(ldflags, "-Wl,-E") + } + return b.run(".", p.ImportPath, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags) } -func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { +func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) cfile = mkAbs(p.Dir, cfile) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + defs = append(defs, b.gccArchArgs()...) + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) + } + // TODO: Support using clang here (during gccgo build)? return b.run(p.Dir, p.ImportPath, "gcc", "-Wall", "-g", - "-I", objdir, "-I", inc, "-o", ofile, - "-DGOOS_"+goos, "-DGOARCH_"+goarch, "-c", cfile) + "-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) } -func gccgoPrefix(p *Package) string { - switch { - case p.build.IsCommand() && !p.forceLibrary: +func gccgoPkgpath(p *Package) string { + if p.build.IsCommand() && !p.forceLibrary { return "" - case p.fake: - return "fake_" + p.ImportPath } - return "go_" + p.ImportPath + return p.ImportPath +} + +func gccgoCleanPkgpath(p *Package) string { + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + return strings.Map(clean, gccgoPkgpath(p)) +} + +// libgcc returns the filename for libgcc, as determined by invoking gcc with +// the -print-libgcc-file-name option. +func (b *builder) libgcc(p *Package) (string, error) { + var buf bytes.Buffer + + gccCmd := b.gccCmd(p.Dir) + + prev := b.print + if buildN { + // In -n mode we temporarily swap out the builder's + // print function to capture the command-line. This + // let's us assign it to $LIBGCC and produce a valid + // buildscript for cgo packages. + b.print = func(a ...interface{}) (n int, err error) { + return fmt.Fprint(&buf, a...) + } + } + f, err := b.runOut(p.Dir, p.ImportPath, gccCmd, "-print-libgcc-file-name") + if err != nil { + return "", fmt.Errorf("gcc -print-libgcc-file-name: %v (%s)", err, f) + } + if buildN { + s := fmt.Sprintf("LIBGCC=$(%s)\n", buf.Next(buf.Len()-1)) + b.print = prev + b.print(s) + return "$LIBGCC", nil + } + + // clang might not be able to find libgcc, and in that case, + // it will simply return "libgcc.a", which is of no use to us. + if strings.Contains(gccCmd[0], "clang") && !filepath.IsAbs(string(f)) { + return "", nil + } + + return strings.Trim(string(f), "\r\n"), nil } // gcc runs the gcc C compiler to create an object from a single C file. @@ -1386,20 +1672,19 @@ func (b *builder) gccCmd(objdir string) []string { // NOTE: env.go's mkEnv knows that the first three // strings returned are "gcc", "-I", objdir (and cuts them off). - // TODO: HOST_CC? - a := []string{"gcc", "-I", objdir, "-g", "-O2"} + gcc := strings.Fields(os.Getenv("CC")) + if len(gcc) == 0 { + gcc = append(gcc, "gcc") + } + a := []string{gcc[0], "-I", objdir, "-g", "-O2"} + a = append(a, gcc[1:]...) // Definitely want -fPIC but on Windows gcc complains // "-fPIC ignored for target (all code is position independent)" if goos != "windows" { a = append(a, "-fPIC") } - switch archChar { - case "8": - a = append(a, "-m32") - case "6": - a = append(a, "-m64") - } + a = append(a, b.gccArchArgs()...) // gcc-4.5 and beyond require explicit "-pthread" flag // for multithreading with pthread library. if buildContext.CgoEnabled { @@ -1411,6 +1696,11 @@ func (b *builder) gccCmd(objdir string) []string { } } + // clang is too smart about command-line arguments + if strings.Contains(a[0], "clang") { + a = append(a, "-Qunused-arguments") + } + // On OS X, some of the compilers behave as if -fno-common // is always set, and the Mach-O linker in 6l/8l assumes this. // See http://golang.org/issue/3253. @@ -1421,12 +1711,31 @@ func (b *builder) gccCmd(objdir string) []string { return a } +// gccArchArgs returns arguments to pass to gcc based on the architecture. +func (b *builder) gccArchArgs() []string { + switch archChar { + case "8": + return []string{"-m32"} + case "6": + return []string{"-m64"} + case "5": + return []string{"-marm"} // not thumb + } + return nil +} + func envList(key string) []string { return strings.Fields(os.Getenv(key)) } var cgoRe = regexp.MustCompile(`[/\\:]`) +var ( + cgoLibGccFile string + cgoLibGccErr error + cgoLibGccFileOnce sync.Once +) + func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) { if goos != toolGOOS { return nil, nil, errors.New("cannot use cgo when compiling for a different operating system") @@ -1478,10 +1787,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-import_runtime_cgo=false") } - if _, ok := buildToolchain.(gccgcToolchain); ok { + if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/cgo") { + cgoflags = append(cgoflags, "-import_syscall=false") + } + + if _, ok := buildToolchain.(gccgoToolchain); ok { cgoflags = append(cgoflags, "-gccgo") - if prefix := gccgoPrefix(p); prefix != "" { - cgoflags = append(cgoflags, "-gccgoprefix="+gccgoPrefix(p)) + if pkgpath := gccgoPkgpath(p); pkgpath != "" { + cgoflags = append(cgoflags, "-gccgopkgpath="+pkgpath) } objExt = "o" } @@ -1499,6 +1812,36 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, // gcc var linkobj []string + + var bareLDFLAGS []string + // filter out -lsomelib, and -framework X if on Darwin + for i := 0; i < len(cgoLDFLAGS); i++ { + f := cgoLDFLAGS[i] + if !strings.HasPrefix(f, "-l") { + if goos == "darwin" && f == "-framework" { // skip the -framework X + i += 1 + continue + } + bareLDFLAGS = append(bareLDFLAGS, f) + } + } + + cgoLibGccFileOnce.Do(func() { + cgoLibGccFile, cgoLibGccErr = b.libgcc(p) + }) + if cgoLibGccFile == "" && cgoLibGccErr != nil { + return nil, nil, err + } + + var staticLibs []string + if goos == "windows" { + // libmingw32 and libmingwex might also use libgcc, so libgcc must come last + staticLibs = []string{"-lmingwex", "-lmingw32"} + } + if cgoLibGccFile != "" { + staticLibs = append(staticLibs, cgoLibGccFile) + } + for _, cfile := range cfiles { ofile := obj + cfile[:len(cfile)-1] + "o" if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil { @@ -1517,19 +1860,30 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } + linkobj = append(linkobj, p.SysoFiles...) dynobj := obj + "_cgo_.o" + if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym + cgoLDFLAGS = append(cgoLDFLAGS, "-pie") + } if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil { return nil, nil, err } + if goarch == "arm" && goos == "linux" { // but we don't need -pie for normal cgo programs + cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1] + } - if _, ok := buildToolchain.(gccgcToolchain); ok { + if _, ok := buildToolchain.(gccgoToolchain); ok { // we don't use dynimport when using gccgo. return outGo, outObj, nil } // cgo -dynimport importC := obj + "_cgo_import.c" - if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil { + cgoflags = []string{} + if p.Standard && p.ImportPath == "runtime/cgo" { + cgoflags = append(cgoflags, "-dynlinker") // record path to dynamic linker + } + if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC, cgoflags); err != nil { return nil, nil, err } @@ -1539,14 +1893,162 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, return nil, nil, err } + ofile := obj + "_all.o" + var gccObjs, nonGccObjs []string + for _, f := range outObj { + if strings.HasSuffix(f, ".o") { + gccObjs = append(gccObjs, f) + } else { + nonGccObjs = append(nonGccObjs, f) + } + } + if err := b.gccld(p, ofile, stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs), gccObjs); err != nil { + return nil, nil, err + } + // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows // must be processed before the gcc-generated objects. // Put it first. http://golang.org/issue/2601 - outObj = append([]string{importObj}, outObj...) + outObj = stringList(importObj, nonGccObjs, ofile) return outGo, outObj, nil } +// Run SWIG on all SWIG input files. +func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) { + + intgosize, err := b.swigIntSize(obj) + if err != nil { + return nil, nil, err + } + + for _, f := range p.SwigFiles { + goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize) + if err != nil { + return nil, nil, err + } + if goFile != "" { + outGo = append(outGo, goFile) + } + if objFile != "" { + outObj = append(outObj, objFile) + } + } + for _, f := range p.SwigCXXFiles { + goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize) + if err != nil { + return nil, nil, err + } + if goFile != "" { + outGo = append(outGo, goFile) + } + if objFile != "" { + outObj = append(outObj, objFile) + } + } + return outGo, outObj, nil +} + +// This code fails to build if sizeof(int) <= 32 +const swigIntSizeCode = ` +package main +const i int = 1 << 32 +` + +// Determine the size of int on the target system for the -intgosize option +// of swig >= 2.0.9 +func (b *builder) swigIntSize(obj string) (intsize string, err error) { + src := filepath.Join(b.work, "swig_intsize.go") + if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil { + return + } + srcs := []string{src} + + p := goFilesPackage(srcs) + + if _, _, e := buildToolchain.gc(b, p, obj, nil, srcs); e != nil { + return "32", nil + } + return "64", nil +} + +// Run SWIG on one SWIG input file. +func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj string, err error) { + n := 5 // length of ".swig" + if cxx { + n = 8 // length of ".swigcxx" + } + base := file[:len(file)-n] + goFile := base + ".go" + cBase := base + "_gc." + gccBase := base + "_wrap." + gccExt := "c" + if cxx { + gccExt = "cxx" + } + soname := p.swigSoname(file) + + _, gccgo := buildToolchain.(gccgoToolchain) + + // swig + args := []string{ + "-go", + "-intgosize", intgosize, + "-module", base, + "-soname", soname, + "-o", obj + gccBase + gccExt, + "-outdir", obj, + } + if gccgo { + args = append(args, "-gccgo") + } + if cxx { + args = append(args, "-c++") + } + + if out, err := b.runOut(p.Dir, p.ImportPath, "swig", args, file); err != nil { + if len(out) > 0 { + if bytes.Contains(out, []byte("Unrecognized option -intgosize")) { + return "", "", errors.New("must have SWIG version >= 2.0.9\n") + } + b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) + return "", "", errPrintedOutput + } + return "", "", err + } + + var cObj string + if !gccgo { + // cc + cObj = obj + cBase + archChar + if err := buildToolchain.cc(b, p, obj, cObj, obj+cBase+"c"); err != nil { + return "", "", err + } + } + + // gcc + gccObj := obj + gccBase + "o" + if err := b.gcc(p, gccObj, []string{"-g", "-fPIC", "-O2"}, obj+gccBase+gccExt); err != nil { + return "", "", err + } + + // create shared library + osldflags := map[string][]string{ + "darwin": {"-dynamiclib", "-Wl,-undefined,dynamic_lookup"}, + "freebsd": {"-shared", "-lpthread", "-lm"}, + "linux": {"-shared", "-lpthread", "-lm"}, + "windows": {"-shared", "-lm", "-mthreads"}, + } + var cxxlib []string + if cxx { + cxxlib = []string{"-lstdc++"} + } + ldflags := stringList(osldflags[goos], cxxlib) + b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-o", soname, gccObj, ldflags) + + return obj + goFile, cObj, nil +} + // An actionQueue is a priority queue of actions. type actionQueue []*action @@ -1569,3 +2071,18 @@ func (q *actionQueue) push(a *action) { func (q *actionQueue) pop() *action { return heap.Pop(q).(*action) } + +func raceInit() { + if !buildRace { + return + } + if goarch != "amd64" || goos != "linux" && goos != "darwin" && goos != "windows" { + fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) + os.Exit(2) + } + buildGcflags = append(buildGcflags, "-race") + buildLdflags = append(buildLdflags, "-race") + buildCcflags = append(buildCcflags, "-D", "RACE") + buildContext.InstallTag = "race" + buildContext.BuildTags = append(buildContext.BuildTags, "race") +} diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go index 773951826..ba600d3bb 100644 --- a/src/cmd/go/clean.go +++ b/src/cmd/go/clean.go @@ -34,6 +34,7 @@ source directories corresponding to the import paths: DIR(.exe) from go build DIR.test(.exe) from go test -c MAINFILE(.exe) from go build MAINFILE.go + *.so from SWIG In the list, DIR represents the final path element of the directory, and MAINFILE is the base name of any Go source @@ -93,11 +94,12 @@ var cleanFile = map[string]bool{ } var cleanExt = map[string]bool{ - ".5": true, - ".6": true, - ".8": true, - ".a": true, - ".o": true, + ".5": true, + ".6": true, + ".8": true, + ".a": true, + ".o": true, + ".so": true, } func clean(p *Package) { @@ -168,7 +170,9 @@ func clean(p *Package) { continue } } - os.RemoveAll(filepath.Join(p.Dir, name)) + if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil { + errorf("go clean: %v", err) + } } continue } @@ -178,7 +182,7 @@ func clean(p *Package) { } if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] { - os.Remove(filepath.Join(p.Dir, name)) + removeFile(filepath.Join(p.Dir, name)) } } @@ -187,7 +191,21 @@ func clean(p *Package) { b.showcmd("", "rm -f %s", p.target) } if !cleanN { - os.Remove(p.target) + removeFile(p.target) + } + } + + if cleanI && p.usesSwig() { + for _, f := range stringList(p.SwigFiles, p.SwigCXXFiles) { + dir := p.swigDir(&buildContext) + soname := p.swigSoname(f) + target := filepath.Join(dir, soname) + if cleanN || cleanX { + b.showcmd("", "rm -f %s", target) + } + if !cleanN { + removeFile(target) + } } } @@ -197,3 +215,11 @@ func clean(p *Package) { } } } + +// removeFile tries to remove file f, if error other than file doesn't exist +// occurs, it will report the error. +func removeFile(f string) { + if err := os.Remove(f); err != nil && !os.IsNotExist(err) { + errorf("go clean: %v", err) + } +} diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 5e7b10692..0297b7602 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -76,7 +76,12 @@ The build flags are shared by the build, install, run, and test commands: do not delete it when exiting. -x print the commands. + -race + enable data race detection. + Supported only on linux/amd64, darwin/amd64 and windows/amd64. + -ccflags 'arg list' + arguments to pass on each 5c, 6c, or 8c compiler invocation -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc) -gccgoflags 'arg list' @@ -90,6 +95,9 @@ The build flags are shared by the build, install, run, and test commands: See the documentation for the go/build package for more information about build tags. +The list flags accept a space-separated list of strings. To embed spaces +in an element in the list, surround it with either single or double quotes. + For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, see 'go help gopath'. @@ -121,6 +129,7 @@ source directories corresponding to the import paths: DIR(.exe) from go build DIR.test(.exe) from go test -c MAINFILE(.exe) from go build MAINFILE.go + *.so from SWIG In the list, DIR represents the final path element of the directory, and MAINFILE is the base name of any Go source @@ -215,14 +224,11 @@ Download and install packages and dependencies Usage: - go get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages] + go get [-d] [-fix] [-u] [build flags] [packages] Get downloads and installs the packages named by the import paths, along with their dependencies. -The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build' -and 'go install'. See 'go help build'. - The -d flag instructs get to stop after downloading the packages; that is, it instructs get not to install the packages. @@ -233,6 +239,9 @@ The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages. +Get also accepts all the flags in the 'go build' and 'go install' commands, +to control the installation. See 'go help build'. + When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get @@ -266,7 +275,7 @@ List packages Usage: - go list [-e] [-f format] [-json] [packages] + go list [-e] [-f format] [-json] [-tags 'tag list'] [packages] List lists the packages named by the import paths, one per line. @@ -276,10 +285,10 @@ The default output shows the package import path: code.google.com/p/goauth2/oauth code.google.com/p/sqlite -The -f flag specifies an alternate format for the list, -using the syntax of package template. The default output -is equivalent to -f '{{.ImportPath}}'. The struct -being passed to the template is: +The -f flag specifies an alternate format for the list, using the +syntax of package template. The default output is equivalent to -f +'{{.ImportPath}}'. One extra template function is available, "join", +which calls strings.Join. The struct being passed to the template is: type Package struct { Dir string // directory containing package sources @@ -293,12 +302,15 @@ being passed to the template is: Root string // Go root or Go path dir containing this package // Source files - GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string // .go sources files that import "C" - CFiles []string // .c source files - HFiles []string // .h source files - SFiles []string // .s source files - SysoFiles []string // .syso object files to add to archive + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go sources files that import "C" + IgnoredGoFiles []string // .go sources ignored due to build constraints + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + SysoFiles []string // .syso object files to add to archive + SwigFiles []string // .swig files + SwigCXXFiles []string // .swigcxx files // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler @@ -333,6 +345,9 @@ printing. Erroneous packages will have a non-empty ImportPath and a non-nil Error field; other information may or may not be missing (zeroed). +The -tags flag specifies a list of build tags, like in the 'go build' +command. + For more about specifying packages, see 'go help packages'. @@ -368,6 +383,7 @@ followed by detailed output for each failed package. 'Go test' recompiles each package along with any files with names matching the file pattern "*_test.go". These additional files can contain test functions, benchmark functions, and example functions. See 'go help testfunc' for more. +Each listed package causes the execution of a separate test binary. By default, go test needs no arguments. It compiles and tests the package with source in the current directory, including tests, and runs the tests. @@ -445,7 +461,7 @@ On Unix, the value is a colon-separated string. On Windows, the value is a semicolon-separated string. On Plan 9, the value is a list. -GOPATH must be set to build and install packages outside the +GOPATH must be set to get, build and install packages outside the standard Go tree. Each directory listed in GOPATH must have a prescribed structure: @@ -491,7 +507,7 @@ Here's an example directory layout: bar.a (installed package object) Go searches each directory listed in GOPATH to find source code, -but new packages are always downloaded into the first directory +but new packages are always downloaded into the first directory in the list. @@ -509,13 +525,13 @@ denotes the package in that directory. Otherwise, the import path P denotes the package found in the directory DIR/src/P for some DIR listed in the GOPATH -environment variable (see 'go help gopath'). +environment variable (see 'go help gopath'). If no import paths are given, the action applies to the package in the current directory. The special import path "all" expands to all package directories -found in all the GOPATH trees. For example, 'go list all' +found in all the GOPATH trees. For example, 'go list all' lists all the packages on the local system. The special import path "std" is like all but expands to just the @@ -623,7 +639,7 @@ The meta tag has the form: -The import-prefix is the import path correponding to the repository +The import-prefix is the import path corresponding to the repository root. It must be a prefix or an exact match of the package being fetched with "go get". If it's not an exact match, another http request is made at the prefix to verify the tags match. @@ -663,28 +679,47 @@ Description of testing flags The 'go test' command takes both flags that apply to 'go test' itself and flags that apply to the resulting test binary. -The test binary, called pkg.test, where pkg is the name of the -directory containing the package sources, has its own flags: - - -test.v - Verbose output: log all tests as they are run. +The following flags are recognized by the 'go test' command and +control the execution of any test: - -test.run pattern - Run only those tests and examples matching the regular - expression. - - -test.bench pattern + -bench regexp Run benchmarks matching the regular expression. - By default, no benchmarks run. + By default, no benchmarks run. To run all benchmarks, + use '-bench .' or '-bench=.'. + + -benchmem + Print memory allocation statistics for benchmarks. + + -benchtime t + Run enough iterations of each benchmark to take t, specified + as a time.Duration (for example, -benchtime 1h30s). + The default is 1 second (1s). + + -blockprofile block.out + Write a goroutine blocking profile to the specified file + when all tests are complete. + + -blockprofilerate n + Control the detail provided in goroutine blocking profiles by setting + runtime.BlockProfileRate to n. See 'godoc runtime BlockProfileRate'. + The profiler aims to sample, on average, one blocking event every + n nanoseconds the program spends blocked. By default, + if -test.blockprofile is set without this flag, all blocking events + are recorded, equivalent to -test.blockprofilerate=1. + + -cpu 1,2,4 + Specify a list of GOMAXPROCS values for which the tests or + benchmarks should be executed. The default is the current value + of GOMAXPROCS. - -test.cpuprofile cpu.out + -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. - -test.memprofile mem.out + -memprofile mem.out Write a memory profile to the specified file when all tests are complete. - -test.memprofilerate n + -memprofilerate n Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate. See 'godoc runtime MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1 @@ -692,32 +727,35 @@ directory containing the package sources, has its own flags: garbage collector, provided the test can run in the available memory without garbage collection. - -test.parallel n + -parallel n Allow parallel execution of test functions that call t.Parallel. The value of this flag is the maximum number of tests to run simultaneously; by default, it is set to the value of GOMAXPROCS. - -test.short + -run regexp + Run only those tests and examples matching the regular + expression. + + -short Tell long-running tests to shorten their run time. It is off by default but set during all.bash so that installing the Go tree can run a sanity check but not spend time running exhaustive tests. - -test.timeout t + -timeout t If a test runs longer than t, panic. - -test.benchtime n - Run enough iterations of each benchmark to take n seconds. - The default is 1 second. + -v + Verbose output: log all tests as they are run. - -test.cpu 1,2,4 - Specify a list of GOMAXPROCS values for which the tests or - benchmarks should be executed. The default is the current value - of GOMAXPROCS. +The test binary, called pkg.test where pkg is the name of the +directory containing the package sources, can be invoked directly +after building it with 'go test -c'. When invoking the test binary +directly, each of the standard flag names must be prefixed with 'test.', +as in -test.run=TestMyFunc or -test.v. -For convenience, each of these -test.X flags of the test binary is -also available as the flag -X in 'go test' itself. Flags not listed -here are passed through unaltered. For instance, the command +When running 'go test', flags not listed above are passed through +unaltered. For instance, the command go test -x -v -cpuprofile=prof.out -dir=testdata -update @@ -725,6 +763,11 @@ will compile the test binary and then run it as pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update +The test flags that generate profiles also leave the test binary in pkg.test +for use when analyzing the profiles. + +Flags not recognized by 'go test' must be placed after any specified packages. + Description of testing functions @@ -740,8 +783,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature, func BenchmarkXXX(b *testing.B) { ... } -An example function is similar to a test function but, instead of using *testing.T -to report success or failure, prints output to os.Stdout and os.Stderr. +An example function is similar to a test function but, instead of using +*testing.T to report success or failure, prints output to os.Stdout. That output is compared against the function's "Output:" comment, which must be the last comment in the function body (see example below). An example with no such comment, or with no text after "Output:" is compiled @@ -769,6 +812,6 @@ See the documentation of the testing package for more information. */ -package documentation +package main // NOTE: cmdDoc is in fmt.go. diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go index d5b034809..6cab37b48 100644 --- a/src/cmd/go/env.go +++ b/src/cmd/go/env.go @@ -6,6 +6,7 @@ package main import ( "fmt" + "os" "runtime" "strings" ) @@ -33,16 +34,22 @@ func mkEnv() []envVar { b.init() env := []envVar{ - {"GOROOT", goroot}, - {"GOBIN", gobin}, {"GOARCH", goarch}, + {"GOBIN", gobin}, {"GOCHAR", archChar}, - {"GOOS", goos}, {"GOEXE", exeSuffix}, {"GOHOSTARCH", runtime.GOARCH}, {"GOHOSTOS", runtime.GOOS}, + {"GOOS", goos}, + {"GOPATH", os.Getenv("GOPATH")}, + {"GOROOT", goroot}, {"GOTOOLDIR", toolDir}, - {"GOGCCFLAGS", strings.Join(b.gccCmd(".")[3:], " ")}, + } + + if goos != "plan9" { + cmd := b.gccCmd(".") + env = append(env, envVar{"CC", cmd[0]}) + env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")}) } if buildContext.CgoEnabled { diff --git a/src/cmd/go/fix.go b/src/cmd/go/fix.go index ef02b5739..8736cce3e 100644 --- a/src/cmd/go/fix.go +++ b/src/cmd/go/fix.go @@ -25,6 +25,6 @@ func runFix(cmd *Command, args []string) { // Use pkg.gofiles instead of pkg.Dir so that // the command only applies to this package, // not to packages in subdirectories. - run(stringList(tool("fix"), relPaths(pkg.gofiles))) + run(stringList(tool("fix"), relPaths(pkg.allgofiles))) } } diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go index b1aba32f3..9d3c911dd 100644 --- a/src/cmd/go/fmt.go +++ b/src/cmd/go/fmt.go @@ -34,7 +34,7 @@ func runFmt(cmd *Command, args []string) { // Use pkg.gofiles instead of pkg.Dir so that // the command only applies to this package, // not to packages in subdirectories. - run(stringList("gofmt", "-l", "-w", relPaths(pkg.gofiles))) + run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles))) } } diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index fe45697e2..8c08ab261 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -18,15 +18,12 @@ import ( ) var cmdGet = &Command{ - UsageLine: "get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages]", + UsageLine: "get [-d] [-fix] [-u] [build flags] [packages]", Short: "download and install packages and dependencies", Long: ` Get downloads and installs the packages named by the import paths, along with their dependencies. -The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build' -and 'go install'. See 'go help build'. - The -d flag instructs get to stop after downloading the packages; that is, it instructs get not to install the packages. @@ -37,6 +34,9 @@ The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages. +Get also accepts all the flags in the 'go build' and 'go install' commands, +to control the installation. See 'go help build'. + When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get @@ -93,7 +93,7 @@ func runGet(cmd *Command, args []string) { runInstall(cmd, args) } -// downloadPath prepares the list of paths to pass to download. +// downloadPaths prepares the list of paths to pass to download. // It expands ... patterns that can be expanded. If there is no match // for a particular pattern, downloadPaths leaves it in the result list, // in the hope that we can figure out the repository from the @@ -105,7 +105,7 @@ func downloadPaths(args []string) []string { if strings.Contains(a, "...") { var expand []string // Use matchPackagesInFS to avoid printing - // warnings. They will be printed by the + // warnings. They will be printed by the // eventual call to importPaths instead. if build.IsLocalImport(a) { expand = matchPackagesInFS(a) @@ -204,7 +204,7 @@ func download(arg string, stk *importStack) { // due to wildcard expansion. for _, p := range pkgs { if *getFix { - run(stringList(tool("fix"), relPaths(p.gofiles))) + run(stringList(tool("fix"), relPaths(p.allgofiles))) // The imports might have changed, so reload again. p = reloadPackage(arg, stk) @@ -247,16 +247,17 @@ func downloadPackage(p *Package) error { } if p.build.SrcRoot == "" { - // Package not found. Put in first directory of $GOPATH or else $GOROOT. - // Guard against people setting GOPATH=$GOROOT. We have to use - // $GOROOT's directory hierarchy (src/pkg, not just src) in that case. - if list := filepath.SplitList(buildContext.GOPATH); len(list) > 0 && list[0] != goroot { - p.build.SrcRoot = filepath.Join(list[0], "src") - p.build.PkgRoot = filepath.Join(list[0], "pkg") - } else { - p.build.SrcRoot = filepath.Join(goroot, "src", "pkg") - p.build.PkgRoot = filepath.Join(goroot, "pkg") + // Package not found. Put in first directory of $GOPATH. + list := filepath.SplitList(buildContext.GOPATH) + if len(list) == 0 { + return fmt.Errorf("cannot download, $GOPATH not set. For more details see: go help gopath") + } + // Guard against people setting GOPATH=$GOROOT. + if list[0] == goroot { + return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath") } + p.build.SrcRoot = filepath.Join(list[0], "src") + p.build.PkgRoot = filepath.Join(list[0], "pkg") } root := filepath.Join(p.build.SrcRoot, rootPath) // If we've considered this repository already, don't do it again. diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index 7539753af..6d2bd7dbb 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -186,7 +186,7 @@ On Unix, the value is a colon-separated string. On Windows, the value is a semicolon-separated string. On Plan 9, the value is a list. -GOPATH must be set to build and install packages outside the +GOPATH must be set to get, build and install packages outside the standard Go tree. Each directory listed in GOPATH must have a prescribed structure: diff --git a/src/cmd/go/http.go b/src/cmd/go/http.go index 6de9a3e1e..107b820f2 100644 --- a/src/cmd/go/http.go +++ b/src/cmd/go/http.go @@ -20,9 +20,13 @@ import ( "net/url" ) +// httpClient is the default HTTP client, but a variable so it can be +// changed by tests, without modifying http.DefaultClient. +var httpClient = http.DefaultClient + // httpGET returns the data from an HTTP GET request for the given URL. func httpGET(url string) ([]byte, error) { - resp, err := http.Get(url) + resp, err := httpClient.Get(url) if err != nil { return nil, err } @@ -37,10 +41,6 @@ func httpGET(url string) ([]byte, error) { return b, nil } -// httpClient is the default HTTP client, but a variable so it can be -// changed by tests, without modifying http.DefaultClient. -var httpClient = http.DefaultClient - // httpsOrHTTP returns the body of either the importPath's // https resource or, if unavailable, the http resource. func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) { diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go index edb59aa79..2d23d077e 100644 --- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -9,11 +9,12 @@ import ( "encoding/json" "io" "os" + "strings" "text/template" ) var cmdList = &Command{ - UsageLine: "list [-e] [-f format] [-json] [packages]", + UsageLine: "list [-e] [-f format] [-json] [-tags 'tag list'] [packages]", Short: "list packages", Long: ` List lists the packages named by the import paths, one per line. @@ -24,10 +25,10 @@ The default output shows the package import path: code.google.com/p/goauth2/oauth code.google.com/p/sqlite -The -f flag specifies an alternate format for the list, -using the syntax of package template. The default output -is equivalent to -f '{{.ImportPath}}'. The struct -being passed to the template is: +The -f flag specifies an alternate format for the list, using the +syntax of package template. The default output is equivalent to -f +'{{.ImportPath}}'. One extra template function is available, "join", +which calls strings.Join. The struct being passed to the template is: type Package struct { Dir string // directory containing package sources @@ -41,12 +42,15 @@ being passed to the template is: Root string // Go root or Go path dir containing this package // Source files - GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string // .go sources files that import "C" - CFiles []string // .c source files - HFiles []string // .h source files - SFiles []string // .s source files - SysoFiles []string // .syso object files to add to archive + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go sources files that import "C" + IgnoredGoFiles []string // .go sources ignored due to build constraints + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + SysoFiles []string // .syso object files to add to archive + SwigFiles []string // .swig files + SwigCXXFiles []string // .swigcxx files // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler @@ -81,6 +85,9 @@ printing. Erroneous packages will have a non-empty ImportPath and a non-nil Error field; other information may or may not be missing (zeroed). +The -tags flag specifies a list of build tags, like in the 'go build' +command. + For more about specifying packages, see 'go help packages'. `, } @@ -88,6 +95,7 @@ For more about specifying packages, see 'go help packages'. func init() { cmdList.Run = runList // break init cycle cmdList.Flag.Var(buildCompiler{}, "compiler", "") + cmdList.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") } var listE = cmdList.Flag.Bool("e", false, "") @@ -96,7 +104,7 @@ var listJson = cmdList.Flag.Bool("json", false, "") var nl = []byte{'\n'} func runList(cmd *Command, args []string) { - out := newCountingWriter(os.Stdout) + out := newTrackingWriter(os.Stdout) defer out.w.Flush() var do func(*Package) @@ -111,18 +119,17 @@ func runList(cmd *Command, args []string) { out.Write(nl) } } else { - tmpl, err := template.New("main").Parse(*listFmt) + tmpl, err := template.New("main").Funcs(template.FuncMap{"join": strings.Join}).Parse(*listFmt) if err != nil { fatalf("%s", err) } do = func(p *Package) { - out.Reset() if err := tmpl.Execute(out, p); err != nil { out.Flush() fatalf("%s", err) } - if out.Count() > 0 { - out.w.WriteRune('\n') + if out.NeedNL() { + out.Write([]byte{'\n'}) } } } @@ -137,32 +144,33 @@ func runList(cmd *Command, args []string) { } } -// CountingWriter counts its data, so we can avoid appending a newline -// if there was no actual output. -type CountingWriter struct { - w *bufio.Writer - count int64 +// TrackingWriter tracks the last byte written on every write so +// we can avoid printing a newline if one was already written or +// if there is no output at all. +type TrackingWriter struct { + w *bufio.Writer + last byte } -func newCountingWriter(w io.Writer) *CountingWriter { - return &CountingWriter{ - w: bufio.NewWriter(w), +func newTrackingWriter(w io.Writer) *TrackingWriter { + return &TrackingWriter{ + w: bufio.NewWriter(w), + last: '\n', } } -func (cw *CountingWriter) Write(p []byte) (n int, err error) { - cw.count += int64(len(p)) - return cw.w.Write(p) -} - -func (cw *CountingWriter) Flush() { - cw.w.Flush() +func (t *TrackingWriter) Write(p []byte) (n int, err error) { + n, err = t.w.Write(p) + if n > 0 { + t.last = p[n-1] + } + return } -func (cw *CountingWriter) Reset() { - cw.count = 0 +func (t *TrackingWriter) Flush() { + t.w.Flush() } -func (cw *CountingWriter) Count() int64 { - return cw.count +func (t *TrackingWriter) NeedNL() bool { + return t.last != '\n' } diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 20585d1be..bf1dad40f 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -127,6 +127,17 @@ func main() { // which is not what most people want when they do it. if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) + } else { + for _, p := range filepath.SplitList(gopath) { + if strings.Contains(p, "~") && runtime.GOOS != "windows" { + fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot contain shell metacharacter '~': %q\n", p) + os.Exit(2) + } + if build.IsLocalImport(p) { + fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p) + os.Exit(2) + } + } } for _, cmd := range commands { @@ -189,7 +200,7 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser {{end}}*/ -package documentation +package main // NOTE: cmdDoc is in fmt.go. ` @@ -368,6 +379,25 @@ func runOut(dir string, cmdargs ...interface{}) []byte { return out } +// envForDir returns a copy of the environment +// suitable for running in the given directory. +// The environment is the current process's environment +// but with an updated $PWD, so that an os.Getwd in the +// child will be faster. +func envForDir(dir string) []string { + env := os.Environ() + for i, kv := range env { + if strings.HasPrefix(kv, "PWD=") { + env[i] = "PWD=" + dir + return env + } + } + // Internally we only use rooted paths, so dir is rooted. + // Even if dir is not rooted, no harm done. + env = append(env, "PWD="+dir) + return env +} + // matchPattern(pattern)(name) reports whether // name matches pattern. Pattern is a limited glob // pattern in which '...' means 'any string' and there @@ -423,19 +453,20 @@ func matchPackages(pattern string) []string { return filepath.SkipDir } - _, err = build.ImportDir(path, 0) - if err != nil { - return nil - } - // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. name = "cmd/" + name - if !have[name] { - have[name] = true - if match(name) { - pkgs = append(pkgs, name) - } + if have[name] { + return nil } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + return nil + } + pkgs = append(pkgs, name) return nil }) @@ -463,14 +494,14 @@ func matchPackages(pattern string) []string { return nil } have[name] = true - - _, err = build.ImportDir(path, 0) - if err != nil && strings.Contains(err.Error(), "no Go source files") { + if !match(name) { return nil } - if match(name) { - pkgs = append(pkgs, name) + _, err = buildContext.ImportDir(path, 0) + if err != nil && strings.Contains(err.Error(), "no Go source files") { + return nil } + pkgs = append(pkgs, name) return nil }) } @@ -559,3 +590,60 @@ func stringList(args ...interface{}) []string { } return x } + +// toFold returns a string with the property that +// strings.EqualFold(s, t) iff toFold(s) == toFold(t) +// This lets us test a large set of strings for fold-equivalent +// duplicates without making a quadratic number of calls +// to EqualFold. Note that strings.ToUpper and strings.ToLower +// have the desired property in some corner cases. +func toFold(s string) string { + // Fast path: all ASCII, no upper case. + // Most paths look like this already. + for i := 0; i < len(s); i++ { + c := s[i] + if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { + goto Slow + } + } + return s + +Slow: + var buf bytes.Buffer + for _, r := range s { + // SimpleFold(x) cycles to the next equivalent rune > x + // or wraps around to smaller values. Iterate until it wraps, + // and we've found the minimum value. + for { + r0 := r + r = unicode.SimpleFold(r0) + if r <= r0 { + break + } + } + // Exception to allow fast path above: A-Z => a-z + if 'A' <= r && r <= 'Z' { + r += 'a' - 'A' + } + buf.WriteRune(r) + } + return buf.String() +} + +// foldDup reports a pair of strings from the list that are +// equal according to strings.EqualFold. +// It returns "", "" if there are no such strings. +func foldDup(list []string) (string, string) { + clash := map[string]string{} + for _, s := range list { + fold := toFold(s) + if t := clash[fold]; t != "" { + if s > t { + s, t = t, s + } + return s, t + } + clash[fold] = s + } + return "", "" +} diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 30bbfad55..7fc61fd86 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -36,12 +36,15 @@ type Package struct { Root string `json:",omitempty"` // Go root or Go path dir containing this package // Source files - GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string `json:",omitempty"` // .go sources files that import "C" - CFiles []string `json:",omitempty"` // .c source files - HFiles []string `json:",omitempty"` // .h source files - SFiles []string `json:",omitempty"` // .s source files - SysoFiles []string `json:",omitempty"` // .syso system object files added to package + GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string `json:",omitempty"` // .go sources files that import "C" + IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints + CFiles []string `json:",omitempty"` // .c source files + HFiles []string `json:",omitempty"` // .h source files + SFiles []string `json:",omitempty"` // .s source files + SysoFiles []string `json:",omitempty"` // .syso system object files added to package + SwigFiles []string `json:",omitempty"` // .swig files + SwigCXXFiles []string `json:",omitempty"` // .swigcxx files // Cgo directives CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler @@ -69,12 +72,14 @@ type Package struct { imports []*Package deps []*Package gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths + allgofiles []string // gofiles + IgnoredGoFiles, absolute paths target string // installed file for this package (may be executable) fake bool // synthesized package forceBuild bool // this package must be rebuilt forceLibrary bool // this package is a library (even if named "main") local bool // imported via local path (./ or ../) localPrefix string // interpret ./ and ../ imports relative to this prefix + exeName string // desired name for temporary executable } func (p *Package) copyBuild(pp *build.Package) { @@ -90,10 +95,13 @@ func (p *Package) copyBuild(pp *build.Package) { p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") p.GoFiles = pp.GoFiles p.CgoFiles = pp.CgoFiles + p.IgnoredGoFiles = pp.IgnoredGoFiles p.CFiles = pp.CFiles p.HFiles = pp.HFiles p.SFiles = pp.SFiles p.SysoFiles = pp.SysoFiles + p.SwigFiles = pp.SwigFiles + p.SwigCXXFiles = pp.SwigCXXFiles p.CgoCFLAGS = pp.CgoCFLAGS p.CgoLDFLAGS = pp.CgoLDFLAGS p.CgoPkgConfig = pp.CgoPkgConfig @@ -117,6 +125,9 @@ func (p *PackageError) Error() string { // is the most important thing. return p.Pos + ": " + p.Err } + if len(p.ImportStack) == 0 { + return p.Err + } return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err } @@ -246,7 +257,7 @@ func reusePackage(p *Package, stk *importStack) *Package { if p.Error == nil { p.Error = &PackageError{ ImportStack: stk.copy(), - Err: "import loop", + Err: "import cycle not allowed", } } p.Incomplete = true @@ -260,13 +271,11 @@ func reusePackage(p *Package, stk *importStack) *Package { // isGoTool is the list of directories for Go programs that are installed in // $GOROOT/pkg/tool. var isGoTool = map[string]bool{ - "cmd/api": true, - "cmd/cgo": true, - "cmd/fix": true, - "cmd/vet": true, - "cmd/yacc": true, - "exp/gotype": true, - "exp/ebnflint": true, + "cmd/api": true, + "cmd/cgo": true, + "cmd/fix": true, + "cmd/vet": true, + "cmd/yacc": true, } // expandScanner expands a scanner.List error into all the errors in the list. @@ -316,11 +325,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Install cross-compiled binaries to subdirectories of bin. elem = full } - p.target = filepath.Join(p.build.BinDir, elem) - if p.Goroot && isGoTool[p.ImportPath] { + if p.build.BinDir != "" { + p.target = filepath.Join(p.build.BinDir, elem) + } + if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) { p.target = filepath.Join(gorootPkg, "tool", full) } - if buildContext.GOOS == "windows" { + if p.target != "" && buildContext.GOOS == "windows" { p.target += ".exe" } } else if p.local { @@ -340,6 +351,11 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Everything depends on runtime, except runtime and unsafe. if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { importPaths = append(importPaths, "runtime") + // When race detection enabled everything depends on runtime/race. + // Exclude runtime/cgo and cmd/cgo to avoid circular dependencies. + if buildRace && (!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo")) { + importPaths = append(importPaths, "runtime/race") + } } // Build list of full paths to all Go files in the package, @@ -350,6 +366,38 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } sort.Strings(p.gofiles) + p.allgofiles = stringList(p.IgnoredGoFiles) + for i := range p.allgofiles { + p.allgofiles[i] = filepath.Join(p.Dir, p.allgofiles[i]) + } + p.allgofiles = append(p.allgofiles, p.gofiles...) + sort.Strings(p.allgofiles) + + // Check for case-insensitive collision of input files. + // To avoid problems on case-insensitive files, we reject any package + // where two different input files have equal names under a case-insensitive + // comparison. + f1, f2 := foldDup(stringList( + p.GoFiles, + p.CgoFiles, + p.IgnoredGoFiles, + p.CFiles, + p.HFiles, + p.SFiles, + p.SysoFiles, + p.SwigFiles, + p.SwigCXXFiles, + p.TestGoFiles, + p.XTestGoFiles, + )) + if f1 != "" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("case-insensitive file name collision: %q and %q", f1, f2), + } + return p + } + // Build list of imported packages and full dependency list. imports := make([]*Package, 0, len(p.Imports)) deps := make(map[string]bool) @@ -403,11 +451,47 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { p.target = "" } - p.Target = p.target + + // In the absence of errors lower in the dependency tree, + // check for case-insensitive collisions of import paths. + if len(p.DepsErrors) == 0 { + dep1, dep2 := foldDup(p.Deps) + if dep1 != "" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("case-insensitive import collision: %q and %q", dep1, dep2), + } + return p + } + } + return p } +// usesSwig returns whether the package needs to run SWIG. +func (p *Package) usesSwig() bool { + return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0 +} + +// swigSoname returns the name of the shared library we create for a +// SWIG input file. +func (p *Package) swigSoname(file string) string { + return strings.Replace(p.ImportPath, "/", "-", -1) + "-" + strings.Replace(file, ".", "-", -1) + ".so" +} + +// swigDir returns the name of the shared SWIG directory for a +// package. +func (p *Package) swigDir(ctxt *build.Context) string { + dir := p.build.PkgRoot + if ctxt.Compiler == "gccgo" { + dir = filepath.Join(dir, "gccgo_"+ctxt.GOOS+"_"+ctxt.GOARCH) + } else { + dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH) + } + return filepath.Join(dir, "swig") +} + // packageList returns the list of packages in the dag rooted at roots // as visited in a depth-first post-order traversal. func packageList(roots []*Package) []*Package { @@ -459,7 +543,7 @@ func isStale(p *Package, topRoot map[string]bool) bool { // distributions of Go packages, although such binaries are // only useful with the specific version of the toolchain that // created them. - if len(p.gofiles) == 0 { + if len(p.gofiles) == 0 && !p.usesSwig() { return false } @@ -491,14 +575,19 @@ func isStale(p *Package, topRoot map[string]bool) bool { // As a courtesy to developers installing new versions of the compiler // frequently, define that packages are stale if they are // older than the compiler, and commands if they are older than - // the linker. This heuristic will not work if the binaries are back-dated, - // as some binary distributions may do, but it does handle a very - // common case. See issue 3036. - if olderThan(buildToolchain.compiler()) { - return true - } - if p.build.IsCommand() && olderThan(buildToolchain.linker()) { - return true + // the linker. This heuristic will not work if the binaries are + // back-dated, as some binary distributions may do, but it does handle + // a very common case. + // See issue 3036. + // Assume code in $GOROOT is up to date, since it may not be writeable. + // See issue 4106. + if p.Root != goroot { + if olderThan(buildToolchain.compiler()) { + return true + } + if p.build.IsCommand() && olderThan(buildToolchain.linker()) { + return true + } } // Have installed copy, probably built using current compilers, @@ -522,6 +611,21 @@ func isStale(p *Package, topRoot map[string]bool) bool { } } + for _, src := range stringList(p.SwigFiles, p.SwigCXXFiles) { + if olderThan(filepath.Join(p.Dir, src)) { + return true + } + soname := p.swigSoname(src) + fi, err := os.Stat(soname) + if err != nil { + return true + } + fiSrc, err := os.Stat(src) + if err != nil || fiSrc.ModTime().After(fi.ModTime()) { + return true + } + } + return false } @@ -546,13 +650,24 @@ func loadPackage(arg string, stk *importStack) *Package { arg = sub } } - if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") { + if strings.HasPrefix(arg, "cmd/") { if p := cmdCache[arg]; p != nil { return p } stk.push(arg) defer stk.pop() - bp, err := build.ImportDir(filepath.Join(gorootSrc, arg), 0) + + if strings.Contains(arg[4:], "/") { + p := &Package{ + Error: &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"), + }, + } + return p + } + + bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0) bp.ImportPath = arg bp.Goroot = true bp.BinDir = gorootBin @@ -580,7 +695,7 @@ func loadPackage(arg string, stk *importStack) *Package { // referring to io/ioutil rather than a hypothetical import of // "./ioutil". if build.IsLocalImport(arg) { - bp, _ := build.ImportDir(filepath.Join(cwd, arg), build.FindOnly) + bp, _ := buildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } @@ -621,10 +736,14 @@ func packagesAndErrors(args []string) []*Package { args = importPaths(args) var pkgs []*Package var stk importStack + var set = make(map[string]bool) + for _, arg := range args { - pkgs = append(pkgs, loadPackage(arg, &stk)) + if !set[arg] { + pkgs = append(pkgs, loadPackage(arg, &stk)) + set[arg] = true + } } - computeStale(pkgs...) return pkgs diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go index 6043b7e20..b50569363 100644 --- a/src/cmd/go/run.go +++ b/src/cmd/go/run.go @@ -34,6 +34,7 @@ func printStderr(args ...interface{}) (int, error) { } func runRun(cmd *Command, args []string) { + raceInit() var b builder b.init() b.print = printStderr @@ -45,6 +46,13 @@ func runRun(cmd *Command, args []string) { if len(files) == 0 { fatalf("go run: no go files listed") } + for _, file := range files { + if strings.HasSuffix(file, "_test.go") { + // goFilesPackage is going to assign this to TestGoFiles. + // Reject since it won't be part of the build. + fatalf("go run: cannot run *_test.go files (%s)", file) + } + } p := goFilesPackage(files) if p.Error != nil { fatalf("%s", p.Error) @@ -57,6 +65,21 @@ func runRun(cmd *Command, args []string) { fatalf("go run: cannot run non-main package") } p.target = "" // must build - not up to date + var src string + if len(p.GoFiles) > 0 { + src = p.GoFiles[0] + } else if len(p.CgoFiles) > 0 { + src = p.CgoFiles[0] + } else { + // this case could only happen if the provided source uses cgo + // while cgo is disabled. + hint := "" + if !buildContext.CgoEnabled { + hint = " (cgo is disabled)" + } + fatalf("go run: no suitable source files%s", hint) + } + p.exeName = src[:len(src)-len(".go")] // name temporary executable for first go file a1 := b.action(modeBuild, modeBuild, p) a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}} b.do(a) @@ -83,6 +106,7 @@ func runStdin(cmdargs ...interface{}) { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + startSigHandlers() if err := cmd.Run(); err != nil { errorf("%v", err) } diff --git a/src/cmd/go/signal.go b/src/cmd/go/signal.go new file mode 100644 index 000000000..e8ba0d365 --- /dev/null +++ b/src/cmd/go/signal.go @@ -0,0 +1,31 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "os/signal" + "sync" +) + +// interrupted is closed, if go process is interrupted. +var interrupted = make(chan struct{}) + +// processSignals setups signal handler. +func processSignals() { + sig := make(chan os.Signal) + signal.Notify(sig, signalsToIgnore...) + go func() { + <-sig + close(interrupted) + }() +} + +var onceProcessSignals sync.Once + +// startSigHandlers start signal handlers. +func startSigHandlers() { + onceProcessSignals.Do(processSignals) +} diff --git a/src/cmd/go/signal_notunix.go b/src/cmd/go/signal_notunix.go new file mode 100644 index 000000000..ef13c1919 --- /dev/null +++ b/src/cmd/go/signal_notunix.go @@ -0,0 +1,13 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build plan9 windows + +package main + +import ( + "os" +) + +var signalsToIgnore = []os.Signal{os.Interrupt} diff --git a/src/cmd/go/signal_unix.go b/src/cmd/go/signal_unix.go new file mode 100644 index 000000000..489a73b83 --- /dev/null +++ b/src/cmd/go/signal_unix.go @@ -0,0 +1,14 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package main + +import ( + "os" + "syscall" +) + +var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT} diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index fe186d4bb..460061a11 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -76,6 +76,12 @@ if ! ./testgo test ./testdata/testimport; then ok=false fi +# Test installation with relative imports. +if ! ./testgo test -i ./testdata/testimport; then + echo "go test -i ./testdata/testimport failed" + ok=false +fi + # Test tests with relative imports in packages synthesized # from Go files named on the command line. if ! ./testgo test ./testdata/testimport/*.go; then @@ -119,6 +125,208 @@ elif ! test -x testdata/bin1/helloworld; then ok=false fi +# Reject relative paths in GOPATH. +if GOPATH=. ./testgo build testdata/src/go-cmd-test/helloworld.go; then + echo 'GOPATH="." go build should have failed, did not' + ok=false +fi + +if GOPATH=:$(pwd)/testdata:. ./testgo build go-cmd-test; then + echo 'GOPATH=":$(pwd)/testdata:." go build should have failed, did not' + ok=false +fi + +# issue 4104 +if [ $(./testgo test fmt fmt fmt fmt fmt | wc -l) -ne 1 ] ; then + echo 'go test fmt fmt fmt fmt fmt tested the same package multiple times' + ok=false +fi + +# ensure that output of 'go list' is consistent between runs +./testgo list std > test_std.list +if ! ./testgo list std | cmp -s test_std.list - ; then + echo "go list std ordering is inconsistent" + ok=false +fi +rm -f test_std.list + +# issue 4096. Validate the output of unsucessful go install foo/quxx +if [ $(./testgo install 'foo/quxx' 2>&1 | grep -c 'cannot find package "foo/quxx" in any of') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of' + ok=false +fi +# test GOROOT search failure is reported +if [ $(./testgo install 'foo/quxx' 2>&1 | egrep -c 'foo/quxx \(from \$GOROOT\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*foo/quxx (from $GOROOT)' + ok=false +fi +# test multiple GOPATH entries are reported separately +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/./src/foo/quxx') -ne 2 ] ; then + echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)\n.*testdata/b/src/foo/quxx' + ok=false +fi +# test (from $GOPATH) annotation is reported for the first GOPATH entry +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/a/src/foo/quxx \(from \$GOPATH\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)' + ok=false +fi +# but not on the second +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/b/src/foo/quxx$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*testdata/b/src/foo/quxx' + ok=false +fi +# test missing GOPATH is reported +if [ $(GOPATH= ./testgo install 'foo/quxx' 2>&1 | egrep -c '\(\$GOPATH not set\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: ($GOPATH not set)' + ok=false +fi + +# issue 4186. go get cannot be used to download packages to $GOROOT +# Test that without GOPATH set, go get should fail +d=$(mktemp -d -t testgo) +mkdir -p $d/src/pkg +if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then + echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset' + ok=false +fi +rm -rf $d +# Test that with GOPATH=$GOROOT, go get should fail +d=$(mktemp -d -t testgo) +mkdir -p $d/src/pkg +if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then + echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT' + ok=false +fi +rm -rf $d + +# issue 3941: args with spaces +d=$(mktemp -d -t testgo) +cat >$d/main.go<hello.out +if ! grep -q '^hello world' hello.out; then + echo "ldflags -X main.extern 'hello world' failed. Output:" + cat hello.out + ok=false +fi +rm -rf $d + +# test that go test -cpuprofile leaves binary behind +./testgo test -cpuprofile strings.prof strings || ok=false +if [ ! -x strings.test ]; then + echo "go test -cpuprofile did not create strings.test" + ok=false +fi +rm -f strings.prof strings.test + +# issue 4568. test that symlinks don't screw things up too badly. +old=$(pwd) +d=$(mktemp -d -t testgo) +mkdir -p $d/src +( + ln -s $d $d/src/dir1 + cd $d/src/dir1 + echo package p >p.go + export GOPATH=$d + if [ "$($old/testgo list -f '{{.Root}}' .)" != "$d" ]; then + echo got lost in symlink tree: + pwd + env|grep WD + $old/testgo list -json . dir1 + touch $d/failed + fi +) +if [ -f $d/failed ]; then + ok=false +fi +rm -rf $d + +# issue 4515. +d=$(mktemp -d -t testgo) +mkdir -p $d/src/example/a $d/src/example/b $d/bin +cat >$d/src/example/a/main.go <$d/src/example/b/main.go <$d/src/example/a/a.go <$d/out; then + echo go list example/a should have failed, did not. + ok=false +elif ! grep "case-insensitive import collision" $d/out >/dev/null; then + echo go list example/a did not report import collision. + ok=false +fi +cat >$d/src/example/b/file.go <$d/src/example/b/FILE.go <$d/out; then + echo go list example/b should have failed, did not. + ok=false +elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then + echo go list example/b did not report file name collision. + ok=false +fi +unset GOPATH +rm -rf $d + +# Only succeeds if source order is preserved. +./testgo test testdata/example[12]_test.go + +# clean up +rm -rf testdata/bin testdata/bin1 +rm -f testgo + if $ok; then echo PASS else diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 870ab190f..3132ab210 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -48,6 +48,7 @@ followed by detailed output for each failed package. 'Go test' recompiles each package along with any files with names matching the file pattern "*_test.go". These additional files can contain test functions, benchmark functions, and example functions. See 'go help testfunc' for more. +Each listed package causes the execution of a separate test binary. By default, go test needs no arguments. It compiles and tests the package with source in the current directory, including tests, and runs the tests. @@ -80,28 +81,47 @@ var helpTestflag = &Command{ The 'go test' command takes both flags that apply to 'go test' itself and flags that apply to the resulting test binary. -The test binary, called pkg.test, where pkg is the name of the -directory containing the package sources, has its own flags: +The following flags are recognized by the 'go test' command and +control the execution of any test: - -test.v - Verbose output: log all tests as they are run. - - -test.run pattern - Run only those tests and examples matching the regular - expression. - - -test.bench pattern + -bench regexp Run benchmarks matching the regular expression. - By default, no benchmarks run. + By default, no benchmarks run. To run all benchmarks, + use '-bench .' or '-bench=.'. + + -benchmem + Print memory allocation statistics for benchmarks. + + -benchtime t + Run enough iterations of each benchmark to take t, specified + as a time.Duration (for example, -benchtime 1h30s). + The default is 1 second (1s). + + -blockprofile block.out + Write a goroutine blocking profile to the specified file + when all tests are complete. + + -blockprofilerate n + Control the detail provided in goroutine blocking profiles by setting + runtime.BlockProfileRate to n. See 'godoc runtime BlockProfileRate'. + The profiler aims to sample, on average, one blocking event every + n nanoseconds the program spends blocked. By default, + if -test.blockprofile is set without this flag, all blocking events + are recorded, equivalent to -test.blockprofilerate=1. + + -cpu 1,2,4 + Specify a list of GOMAXPROCS values for which the tests or + benchmarks should be executed. The default is the current value + of GOMAXPROCS. - -test.cpuprofile cpu.out + -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. - -test.memprofile mem.out + -memprofile mem.out Write a memory profile to the specified file when all tests are complete. - -test.memprofilerate n + -memprofilerate n Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate. See 'godoc runtime MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1 @@ -109,38 +129,46 @@ directory containing the package sources, has its own flags: garbage collector, provided the test can run in the available memory without garbage collection. - -test.parallel n + -parallel n Allow parallel execution of test functions that call t.Parallel. The value of this flag is the maximum number of tests to run simultaneously; by default, it is set to the value of GOMAXPROCS. - -test.short + -run regexp + Run only those tests and examples matching the regular + expression. + + -short Tell long-running tests to shorten their run time. It is off by default but set during all.bash so that installing the Go tree can run a sanity check but not spend time running exhaustive tests. - -test.timeout t + -timeout t If a test runs longer than t, panic. - -test.benchtime n - Run enough iterations of each benchmark to take n seconds. - The default is 1 second. + -v + Verbose output: log all tests as they are run. - -test.cpu 1,2,4 - Specify a list of GOMAXPROCS values for which the tests or - benchmarks should be executed. The default is the current value - of GOMAXPROCS. +The test binary, called pkg.test where pkg is the name of the +directory containing the package sources, can be invoked directly +after building it with 'go test -c'. When invoking the test binary +directly, each of the standard flag names must be prefixed with 'test.', +as in -test.run=TestMyFunc or -test.v. -For convenience, each of these -test.X flags of the test binary is -also available as the flag -X in 'go test' itself. Flags not listed -here are passed through unaltered. For instance, the command +When running 'go test', flags not listed above are passed through +unaltered. For instance, the command go test -x -v -cpuprofile=prof.out -dir=testdata -update will compile the test binary and then run it as pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update + +The test flags that generate profiles also leave the test binary in pkg.test +for use when analyzing the profiles. + +Flags not recognized by 'go test' must be placed after any specified packages. `, } @@ -160,8 +188,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature, func BenchmarkXXX(b *testing.B) { ... } -An example function is similar to a test function but, instead of using *testing.T -to report success or failure, prints output to os.Stdout and os.Stderr. +An example function is similar to a test function but, instead of using +*testing.T to report success or failure, prints output to os.Stdout. That output is compared against the function's "Output:" comment, which must be the last comment in the function body (see example below). An example with no such comment, or with no text after "Output:" is compiled @@ -191,6 +219,7 @@ See the documentation of the testing package for more information. var ( testC bool // -c flag + testProfile bool // some profiling flag testI bool // -i flag testV bool // -v flag testFiles []string // -file flag(s) TODO: not respected @@ -207,6 +236,7 @@ func runTest(cmd *Command, args []string) { var pkgArgs []string pkgArgs, testArgs = testFlags(args) + raceInit() pkgs := packagesForBuild(pkgArgs) if len(pkgs) == 0 { fatalf("no packages to test") @@ -215,12 +245,15 @@ func runTest(cmd *Command, args []string) { if testC && len(pkgs) != 1 { fatalf("cannot use -c flag with multiple packages") } + if testProfile && len(pkgs) != 1 { + fatalf("cannot use test profile flag with multiple packages") + } // If a test timeout was given and is parseable, set our kill timeout // to that timeout plus one minute. This is a backup alarm in case // the test wedges with a goroutine spinning and its background // timer does not get a chance to fire. - if dt, err := time.ParseDuration(testTimeout); err == nil { + if dt, err := time.ParseDuration(testTimeout); err == nil && dt > 0 { testKillTimeout = dt + 1*time.Minute } @@ -276,7 +309,9 @@ func runTest(cmd *Command, args []string) { all := []string{} for path := range deps { - all = append(all, path) + if !build.IsLocalImport(path) { + all = append(all, path) + } } sort.Strings(all) @@ -361,7 +396,11 @@ func runTest(cmd *Command, args []string) { if args != "" { args = " " + args } - fmt.Fprintf(os.Stderr, "installing these packages with 'go test -i%s' will speed future tests.\n\n", args) + extraOpts := "" + if buildRace { + extraOpts = "-race " + } + fmt.Fprintf(os.Stderr, "installing these packages with 'go test %s-i%s' will speed future tests.\n\n", extraOpts, args) } b.do(root) @@ -405,7 +444,14 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, // Use last element of import path, not package name. // They differ when package name is "main". - _, elem := path.Split(p.ImportPath) + // But if the import path is "command-line-arguments", + // like it is during 'go run', use the package name. + var elem string + if p.ImportPath == "command-line-arguments" { + elem = p.Name + } else { + _, elem = path.Split(p.ImportPath) + } testBinary := elem + ".test" // The ptest package needs to be importable under the @@ -532,14 +578,17 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, a.target = filepath.Join(testDir, testBinary) + exeSuffix pmainAction := a - if testC { - // -c flag: create action to copy binary to ./test.out. + if testC || testProfile { + // -c or profiling flag: create action to copy binary to ./test.out. runAction = &action{ f: (*builder).install, deps: []*action{pmainAction}, p: pmain, - target: testBinary + exeSuffix, + target: filepath.Join(cwd, testBinary+exeSuffix), } + pmainAction = runAction // in case we are running the test + } + if testC { printAction = &action{p: p, deps: []*action{runAction}} // nop } else { // run test @@ -586,6 +635,7 @@ func (b *builder) runTest(a *action) error { cmd := exec.Command(args[0], args[1:]...) cmd.Dir = a.p.Dir + cmd.Env = envForDir(cmd.Dir) var buf bytes.Buffer if testStreamOutput { cmd.Stdout = os.Stdout @@ -595,14 +645,34 @@ func (b *builder) runTest(a *action) error { cmd.Stderr = &buf } + // If there are any local SWIG dependencies, we want to load + // the shared library from the build directory. + if a.p.usesSwig() { + env := cmd.Env + found := false + prefix := "LD_LIBRARY_PATH=" + for i, v := range env { + if strings.HasPrefix(v, prefix) { + env[i] = v + ":." + found = true + break + } + } + if !found { + env = append(env, "LD_LIBRARY_PATH=.") + } + cmd.Env = env + } + t0 := time.Now() err := cmd.Start() // This is a last-ditch deadline to detect and // stop wedged test binaries, to keep the builders // running. - tick := time.NewTimer(testKillTimeout) if err == nil { + tick := time.NewTimer(testKillTimeout) + startSigHandlers() done := make(chan error) go func() { done <- cmd.Wait() @@ -613,13 +683,12 @@ func (b *builder) runTest(a *action) error { case <-tick.C: cmd.Process.Kill() err = <-done - fmt.Fprintf(&buf, "*** Test killed: ran too long.\n") + fmt.Fprintf(&buf, "*** Test killed: ran too long (%v).\n", testKillTimeout) } tick.Stop() } out := buf.Bytes() - t1 := time.Now() - t := fmt.Sprintf("%.3fs", t1.Sub(t0).Seconds()) + t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds()) if err == nil { if testShowPass { a.testOutput.Write(out) @@ -750,8 +819,10 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error { *seen = true } } - for _, e := range doc.Examples(f) { - if e.Output == "" { + ex := doc.Examples(f) + sort.Sort(byOrder(ex)) + for _, e := range ex { + if e.Output == "" && !e.EmptyOutput { // Don't run examples with no output. continue } @@ -761,6 +832,12 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error { return nil } +type byOrder []*doc.Example + +func (x byOrder) Len() int { return len(x) } +func (x byOrder) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byOrder) Less(i, j int) bool { return x[i].Order < x[j].Order } + var testmainTmpl = template.Must(template.New("main").Parse(` package main diff --git a/src/cmd/go/testdata/example1_test.go b/src/cmd/go/testdata/example1_test.go new file mode 100644 index 000000000..ec7092e97 --- /dev/null +++ b/src/cmd/go/testdata/example1_test.go @@ -0,0 +1,23 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure that go test runs Example_Z before Example_A, preserving source order. + +package p + +import "fmt" + +var n int + +func Example_Z() { + n++ + fmt.Println(n) + // Output: 1 +} + +func Example_A() { + n++ + fmt.Println(n) + // Output: 2 +} diff --git a/src/cmd/go/testdata/example2_test.go b/src/cmd/go/testdata/example2_test.go new file mode 100644 index 000000000..1e0e80b80 --- /dev/null +++ b/src/cmd/go/testdata/example2_test.go @@ -0,0 +1,21 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure that go test runs Example_Y before Example_B, preserving source order. + +package p + +import "fmt" + +func Example_Y() { + n++ + fmt.Println(n) + // Output: 3 +} + +func Example_B() { + n++ + fmt.Println(n) + // Output: 4 +} diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go index ecf5bf456..b2ca66b09 100644 --- a/src/cmd/go/testflag.go +++ b/src/cmd/go/testflag.go @@ -25,11 +25,14 @@ var usageMessage = `Usage of go test: // These flags can be passed with or without a "test." prefix: -v or -test.v. -bench="": passes -test.bench to test - -benchtime=1: passes -test.benchtime to test + -benchmem=false: print memory allocation statistics for benchmarks + -benchtime=1s: passes -test.benchtime to test -cpu="": passes -test.cpu to test -cpuprofile="": passes -test.cpuprofile to test -memprofile="": passes -test.memprofile to test -memprofilerate=0: passes -test.memprofilerate to test + -blockprofile="": pases -test.blockprofile to test + -blockprofilerate=0: passes -test.blockprofilerate to test -parallel=0: passes -test.parallel to test -run="": passes -test.run to test -short=false: passes -test.short to test @@ -71,14 +74,18 @@ var testFlagDefn = []*testFlagSpec{ {name: "gccgoflags"}, {name: "tags"}, {name: "compiler"}, + {name: "race", boolVar: &buildRace}, // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. {name: "bench", passToTest: true}, + {name: "benchmem", boolVar: new(bool), passToTest: true}, {name: "benchtime", passToTest: true}, {name: "cpu", passToTest: true}, {name: "cpuprofile", passToTest: true}, {name: "memprofile", passToTest: true}, {name: "memprofilerate", passToTest: true}, + {name: "blockprofile", passToTest: true}, + {name: "blockprofilerate", passToTest: true}, {name: "parallel", passToTest: true}, {name: "run", passToTest: true}, {name: "short", boolVar: new(bool), passToTest: true}, @@ -117,7 +124,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { f, value, extraWord := testFlag(args, i) if f == nil { // This is a flag we do not know; we must assume - // that any args we see after this might be flag + // that any args we see after this might be flag // arguments, not package names. inPkg = false if packageNames == nil { @@ -127,18 +134,28 @@ func testFlags(args []string) (packageNames, passToTest []string) { passToTest = append(passToTest, args[i]) continue } + var err error switch f.name { // bool flags. - case "a", "c", "i", "n", "x", "v", "work": + case "a", "c", "i", "n", "x", "v", "work", "race": setBoolFlag(f.boolVar, value) case "p": setIntFlag(&buildP, value) case "gcflags": - buildGcflags = strings.Fields(value) + buildGcflags, err = splitQuotedFields(value) + if err != nil { + fatalf("invalid flag argument for -%s: %v", f.name, err) + } case "ldflags": - buildLdflags = strings.Fields(value) + buildLdflags, err = splitQuotedFields(value) + if err != nil { + fatalf("invalid flag argument for -%s: %v", f.name, err) + } case "gccgoflags": - buildGccgoflags = strings.Fields(value) + buildGccgoflags, err = splitQuotedFields(value) + if err != nil { + fatalf("invalid flag argument for -%s: %v", f.name, err) + } case "tags": buildContext.BuildTags = strings.Fields(value) case "compiler": @@ -150,6 +167,8 @@ func testFlags(args []string) (packageNames, passToTest []string) { testBench = true case "timeout": testTimeout = value + case "blockprofile", "cpuprofile", "memprofile": + testProfile = true } if extraWord { i++ @@ -176,9 +195,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) } name := arg[1:] // If there's already "test.", drop it for now. - if strings.HasPrefix(name, "test.") { - name = name[5:] - } + name = strings.TrimPrefix(name, "test.") equals := strings.Index(name, "=") if equals >= 0 { value = name[equals+1:] diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go index cb463a2e7..299b94cb3 100644 --- a/src/cmd/go/tool.go +++ b/src/cmd/go/tool.go @@ -47,7 +47,7 @@ const toolWindowsExtension = ".exe" func tool(name string) string { p := filepath.Join(toolDir, name) - if toolIsWindows { + if toolIsWindows && name != "pprof" { p += toolWindowsExtension } return p @@ -76,6 +76,16 @@ func runTool(cmd *Command, args []string) { setExitStatus(3) return } + if toolIsWindows && toolName == "pprof" { + args = append([]string{"perl", toolPath}, args[1:]...) + var err error + toolPath, err = exec.LookPath("perl") + if err != nil { + fmt.Fprintf(os.Stderr, "go tool: perl not found\n") + setExitStatus(3) + return + } + } if toolN { fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " ")) @@ -90,7 +100,14 @@ func runTool(cmd *Command, args []string) { } err := toolCmd.Run() if err != nil { - fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err) + // Only print about the exit status if the command + // didn't even run (not an ExitError) or it didn't exit cleanly + // or we're printing command lines too (-x mode). + // Assume if command exited cleanly (even with non-zero status) + // it printed any messages it wanted to print. + if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || buildX { + fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err) + } setExitStatus(1) return } diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 1c121672f..b99579441 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -81,7 +81,7 @@ var vcsHg = &vcsCmd{ tagSyncCmd: "update -r {tag}", tagSyncDefault: "update default", - scheme: []string{"https", "http"}, + scheme: []string{"https", "http", "ssh"}, pingCmd: "identify {scheme}://{repo}", } @@ -180,8 +180,17 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) args[i] = expand(m, arg) } + _, err := exec.LookPath(v.cmd) + if err != nil { + fmt.Fprintf(os.Stderr, + "go: missing %s command. See http://golang.org/s/gogetcmd\n", + v.name) + return nil, err + } + cmd := exec.Command(v.cmd, args...) cmd.Dir = dir + cmd.Env = envForDir(cmd.Dir) if buildX { fmt.Printf("cd %s\n", dir) fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " ")) @@ -189,7 +198,7 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) var buf bytes.Buffer cmd.Stdout = &buf cmd.Stderr = &buf - err := cmd.Run() + err = cmd.Run() out := buf.Bytes() if err != nil { if verbose || buildV { diff --git a/src/cmd/go/version.go b/src/cmd/go/version.go index 09e2f1633..a41f4a736 100644 --- a/src/cmd/go/version.go +++ b/src/cmd/go/version.go @@ -21,5 +21,5 @@ func runVersion(cmd *Command, args []string) { cmd.Usage() } - fmt.Printf("go version %s\n", runtime.Version()) + fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH) } diff --git a/src/cmd/godoc/appinit.go b/src/cmd/godoc/appinit.go index 70da00110..996b2b850 100644 --- a/src/cmd/godoc/appinit.go +++ b/src/cmd/godoc/appinit.go @@ -17,9 +17,12 @@ import ( ) func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) { - contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path! w.WriteHeader(http.StatusNotFound) - servePage(w, relpath, "File "+relpath, "", "", contents) + servePage(w, Page{ + Title: "File " + relpath, + Subtitle: relpath, + Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path! + }) } func init() { @@ -34,6 +37,7 @@ func init() { *indexFiles = indexFilenames *maxResults = 100 // reduce latency by limiting the number of fulltext search results *indexThrottle = 0.3 // in case *indexFiles is empty (and thus the indexer is run) + *showPlayground = true // read .zip file and set up file systems const zipfile = zipFilename @@ -48,6 +52,7 @@ func init() { readTemplates() initHandlers() registerPublicHandlers(http.DefaultServeMux) + registerPlaygroundHandlers(http.DefaultServeMux) // initialize default directory tree with corresponding timestamp. initFSTree() diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go index f7f51d0a0..e68c0fa6b 100644 --- a/src/cmd/godoc/codewalk.go +++ b/src/cmd/godoc/codewalk.go @@ -68,8 +68,11 @@ func codewalk(w http.ResponseWriter, r *http.Request) { return } - b := applyTemplate(codewalkHTML, "codewalk", cw) - servePage(w, cw.Title, "Codewalk: "+cw.Title, "", "", b) + servePage(w, Page{ + Title: "Codewalk: " + cw.Title, + Tabtitle: cw.Title, + Body: applyTemplate(codewalkHTML, "codewalk", cw), + }) } // A Codewalk represents a single codewalk read from an XML file. @@ -199,8 +202,10 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string } } - b := applyTemplate(codewalkdirHTML, "codewalkdir", v) - servePage(w, "", "Codewalks", "", "", b) + servePage(w, Page{ + Title: "Codewalks", + Body: applyTemplate(codewalkdirHTML, "codewalkdir", v), + }) } // codewalkFileprint serves requests with ?fileprint=f&lo=lo&hi=hi. diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go index b9b529f87..fda7adce5 100644 --- a/src/cmd/godoc/dirtrees.go +++ b/src/cmd/godoc/dirtrees.go @@ -74,7 +74,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i // determine number of subdirectories and if there are package files ndirs := 0 hasPkgFiles := false - var synopses [4]string // prioritized package documentation (0 == highest priority) + var synopses [3]string // prioritized package documentation (0 == highest priority) for _, d := range list { switch { case isPkgDir(d): @@ -95,12 +95,10 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i switch file.Name.Name { case name: i = 0 // normal case: directory name matches package name - case fakePkgName: - i = 1 // synopses for commands case "main": - i = 2 // directory contains a main package + i = 1 // directory contains a main package default: - i = 3 // none of the above + i = 2 // none of the above } if 0 <= i && i < len(synopses) && synopses[i] == "" { synopses[i] = doc.Synopsis(file.Doc.Text()) @@ -229,9 +227,7 @@ func (dir *Directory) lookupLocal(name string) *Directory { } func splitPath(p string) []string { - if strings.HasPrefix(p, "/") { - p = p[1:] - } + p = strings.TrimPrefix(p, "/") if p == "" { return nil } @@ -264,7 +260,7 @@ type DirEntry struct { Height int // = DirList.MaxHeight - Depth, > 0 Path string // directory path; includes Name, relative to DirList root Name string // directory name - HasPkg bool // true if the directory contains at least one package + HasPkg bool // true if the directory contains at least one package Synopsis string // package documentation, if any } @@ -310,14 +306,9 @@ func (root *Directory) listing(skipRoot bool) *DirList { // the path is relative to root.Path - remove the root.Path // prefix (the prefix should always be present but avoid // crashes and check) - path := d.Path - if strings.HasPrefix(d.Path, root.Path) { - path = d.Path[len(root.Path):] - } + path := strings.TrimPrefix(d.Path, root.Path) // remove leading separator if any - path must be relative - if len(path) > 0 && path[0] == '/' { - path = path[1:] - } + path = strings.TrimPrefix(path, "/") p.Path = path p.Name = d.Name p.HasPkg = d.HasPkg diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go index 39ecc6e63..ddb6d2687 100644 --- a/src/cmd/godoc/doc.go +++ b/src/cmd/godoc/doc.go @@ -67,8 +67,6 @@ The flags are: -maxresults=10000 maximum number of full text search results shown (no full text index is built if maxresults <= 0) - -path="" - additional package directories (colon-separated) -html print HTML in command-line mode -goroot=$GOROOT @@ -88,20 +86,8 @@ The flags are: zip file providing the file system to serve; disabled if empty By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set). -Additional directories may be specified via the -path flag which accepts a list -of colon-separated paths; unrooted paths are relative to the current working -directory. Each path is considered as an additional root for packages in order -of appearance. The last (absolute) path element is the prefix for the package -path. For instance, given the flag value: - - path=".:/home/bar:/public" - -for a godoc started in /home/user/godoc, absolute paths are mapped to package paths -as follows: - - /home/user/godoc/x -> godoc/x - /home/bar/x -> bar/x - /public/x -> public/x +This behavior can be altered by providing an alternative $GOROOT with the -goroot +flag. When godoc runs as a web server and -index is set, a search index is maintained. The index is created at startup. @@ -141,4 +127,4 @@ See "Godoc: documenting Go code" for how to write good comments for godoc: http://golang.org/doc/articles/godoc_documenting_go_code.html */ -package documentation +package main diff --git a/src/cmd/godoc/filesystem.go b/src/cmd/godoc/filesystem.go index 09d7b2463..0309d7cab 100644 --- a/src/cmd/godoc/filesystem.go +++ b/src/cmd/godoc/filesystem.go @@ -41,7 +41,7 @@ import ( // paths can assume they are slash-separated and should be using // package path (often imported as pathpkg) to manipulate them, // even on Windows. -// +// var fs = nameSpace{} // the underlying file system for godoc // Setting debugNS = true will enable debugging prints about @@ -138,7 +138,7 @@ func hasPathPrefix(x, y string) bool { // but we want to be able to mount multiple file systems on a single // mount point and have the system behave as if the union of those // file systems were present at the mount point. -// For example, if the OS file system has a Go installation in +// For example, if the OS file system has a Go installation in // c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then // this name space creates the view we want for the godoc server: // @@ -179,7 +179,7 @@ func hasPathPrefix(x, y string) bool { // OS(`d:\Work1').ReadDir("/src/code") // OS(`d:\Work2').ReadDir("/src/code") // -// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by +// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by // just "/src" in the final two calls. // // OS is itself an implementation of a file system: it implements @@ -459,9 +459,7 @@ func (ns nameSpace) ReadDir(path string) ([]os.FileInfo, error) { if hasPathPrefix(old, path) && old != path { // Find next element after path in old. elem := old[len(path):] - if strings.HasPrefix(elem, "/") { - elem = elem[1:] - } + elem = strings.TrimPrefix(elem, "/") if i := strings.Index(elem, "/"); i >= 0 { elem = elem[:i] } diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go index 3b1b9a822..122ddc7d6 100644 --- a/src/cmd/godoc/format.go +++ b/src/cmd/godoc/format.go @@ -54,6 +54,8 @@ type SegmentWriter func(w io.Writer, text []byte, selections int) // Selection is ignored. // func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) { + // If we have a link writer, make the links + // selection the last entry in selections if lw != nil { selections = append(selections, links) } @@ -108,8 +110,8 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, break } // determine the kind of segment change - if index == len(selections)-1 { - // we have a link segment change: + if lw != nil && index == len(selections)-1 { + // we have a link segment change (see start of this function): // format the previous selection segment, write the // link tag and start a new selection segment segment(offs) @@ -119,7 +121,7 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, } else { // we have a selection change: // format the previous selection segment, determine - // the new selection bitset and start a new segment + // the new selection bitset and start a new segment segment(offs) lastOffs = offs mask := 1 << uint(index) diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index f6dc678b4..872b0dc1e 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -12,8 +12,10 @@ import ( "go/ast" "go/build" "go/doc" + "go/format" "go/printer" "go/token" + htmlpkg "html" "io" "io/ioutil" "log" @@ -57,12 +59,13 @@ var ( // TODO(gri) consider the invariant that goroot always end in '/' goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)") - pkgPath = flag.String("path", "", "additional package directories (colon-separated)") // layout control tabwidth = flag.Int("tabwidth", 4, "tab width") showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings") templateDir = flag.String("templates", "", "directory containing alternate template files") + showPlayground = flag.Bool("play", false, "enable playground in web interface") + showExamples = flag.Bool("ex", false, "show examples in command line mode") // search index indexEnabled = flag.Bool("index", false, "enable search index") @@ -80,22 +83,19 @@ var ( fileServer http.Handler // default file server cmdHandler docServer pkgHandler docServer + + // which code 'Notes' to show + notes = flag.String("notes", "BUG", "comma separated list of Note markers as per pkg:go/doc") + // list of 'Notes' to show + notesToShow []string ) func initHandlers() { - // Add named directories in -path argument as - // subdirectories of src/pkg. - for _, p := range filepath.SplitList(*pkgPath) { - _, elem := filepath.Split(p) - if elem == "" { - log.Fatalf("invalid -path argument: %q has no final element", p) - } - fs.Bind("/src/pkg/"+elem, OS(p), "/", bindReplace) - } + notesToShow = strings.Split(*notes, ",") fileServer = http.FileServer(&httpFS{fs}) - cmdHandler = docServer{"/cmd/", "/src/cmd", false} - pkgHandler = docServer{"/pkg/", "/src/pkg", true} + cmdHandler = docServer{"/cmd/", "/src/cmd"} + pkgHandler = docServer{"/pkg/", "/src/pkg"} } func registerPublicHandlers(mux *http.ServeMux) { @@ -326,18 +326,62 @@ func startsWithUppercase(s string) bool { var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`) -func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string { +// stripExampleSuffix strips lowercase braz in Foo_braz or Foo_Bar_braz from name +// while keeping uppercase Braz in Foo_Braz. +func stripExampleSuffix(name string) string { + if i := strings.LastIndex(name, "_"); i != -1 { + if i < len(name)-1 && !startsWithUppercase(name[i+1:]) { + name = name[:i] + } + } + return name +} + +func example_textFunc(funcName string, examples []*doc.Example, fset *token.FileSet, indent string) string { + if !*showExamples { + return "" + } + var buf bytes.Buffer + first := true for _, eg := range examples { - name := eg.Name + name := stripExampleSuffix(eg.Name) + if name != funcName { + continue + } - // strip lowercase braz in Foo_braz or Foo_Bar_braz from name - // while keeping uppercase Braz in Foo_Braz - if i := strings.LastIndex(name, "_"); i != -1 { - if i < len(name)-1 && !startsWithUppercase(name[i+1:]) { - name = name[:i] - } + if !first { + buf.WriteString("\n") } + first = false + + // print code + cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments} + var buf1 bytes.Buffer + writeNode(&buf1, fset, cnode) + code := buf1.String() + // Additional formatting if this is a function body. + if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' { + // remove surrounding braces + code = code[1 : n-1] + // unindent + code = strings.Replace(code, "\n ", "\n", -1) + } + code = strings.Trim(code, "\n") + code = strings.Replace(code, "\n", "\n\t", -1) + + buf.WriteString(indent) + buf.WriteString("Example:\n\t") + buf.WriteString(code) + buf.WriteString("\n") + } + return buf.String() +} + +func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string { + var buf bytes.Buffer + for _, eg := range examples { + name := stripExampleSuffix(eg.Name) if name != funcName { continue @@ -347,9 +391,11 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments} code := node_htmlFunc(cnode, fset) out := eg.Output + wholeFile := true - // additional formatting if this is a function body + // Additional formatting if this is a function body. if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' { + wholeFile = false // remove surrounding braces code = code[1 : n-1] // unindent @@ -358,14 +404,28 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File if loc := exampleOutputRx.FindStringIndex(code); loc != nil { code = strings.TrimSpace(code[:loc[0]]) } - } else { - // drop output, as the output comment will appear in the code + } + + // Write out the playground code in standard Go style + // (use tabs, no comment highlight, etc). + play := "" + if eg.Play != nil && *showPlayground { + var buf bytes.Buffer + if err := format.Node(&buf, fset, eg.Play); err != nil { + log.Print(err) + } else { + play = buf.String() + } + } + + // Drop output, as the output comment will appear in the code. + if wholeFile && play == "" { out = "" } err := exampleHTML.Execute(&buf, struct { - Name, Doc, Code, Output string - }{eg.Name, eg.Doc, code, out}) + Name, Doc, Code, Play, Output string + }{eg.Name, eg.Doc, code, play, out}) if err != nil { log.Print(err) } @@ -393,6 +453,10 @@ func example_suffixFunc(name string) string { return suffix } +func noteTitle(note string) string { + return strings.Title(strings.ToLower(note)) +} + func splitExampleName(s string) (name, suffix string) { i := strings.LastIndex(s, "_") if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) { @@ -408,9 +472,7 @@ func pkgLinkFunc(path string) string { relpath := path[1:] // because of the irregular mapping under goroot // we need to correct certain relative paths - if strings.HasPrefix(relpath, "src/pkg/") { - relpath = relpath[len("src/pkg/"):] - } + relpath = strings.TrimPrefix(relpath, "src/pkg/") return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL } @@ -485,8 +547,12 @@ var fmap = template.FuncMap{ // formatting of Examples "example_html": example_htmlFunc, + "example_text": example_textFunc, "example_name": example_nameFunc, "example_suffix": example_suffixFunc, + + // formatting of Notes + "noteTitle": noteTitle, } func readTemplate(name string) *template.Template { @@ -538,31 +604,28 @@ func readTemplates() { // ---------------------------------------------------------------------------- // Generic HTML wrapper -func servePage(w http.ResponseWriter, tabtitle, title, subtitle, query string, content []byte) { - if tabtitle == "" { - tabtitle = title - } - d := struct { - Tabtitle string - Title string - Subtitle string - SearchBox bool - Query string - Version string - Menu []byte - Content []byte - }{ - tabtitle, - title, - subtitle, - *indexEnabled, - query, - runtime.Version(), - nil, - content, - } - - if err := godocHTML.Execute(w, &d); err != nil { +// Page describes the contents of the top-level godoc webpage. +type Page struct { + Title string + Tabtitle string + Subtitle string + Query string + Body []byte + + // filled in by servePage + SearchBox bool + Playground bool + Version string +} + +func servePage(w http.ResponseWriter, page Page) { + if page.Tabtitle == "" { + page.Tabtitle = page.Title + } + page.SearchBox = *indexEnabled + page.Playground = *showPlayground + page.Version = runtime.Version() + if err := godocHTML.Execute(w, page); err != nil { log.Printf("godocHTML.Execute: %s", err) } } @@ -627,7 +690,11 @@ func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath strin src = buf.Bytes() } - servePage(w, "", meta.Title, meta.Subtitle, "", src) + servePage(w, Page{ + Title: meta.Title, + Subtitle: meta.Subtitle, + Body: src, + }) } func applyTemplate(t *template.Template, name string, data interface{}) []byte { @@ -640,11 +707,25 @@ func applyTemplate(t *template.Template, name string, data interface{}) []byte { func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { canonical := pathpkg.Clean(r.URL.Path) - if !strings.HasSuffix("/", canonical) { + if !strings.HasSuffix(canonical, "/") { canonical += "/" } if r.URL.Path != canonical { - http.Redirect(w, r, canonical, http.StatusMovedPermanently) + url := *r.URL + url.Path = canonical + http.Redirect(w, r, url.String(), http.StatusMovedPermanently) + redirected = true + } + return +} + +func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) { + c := pathpkg.Clean(r.URL.Path) + c = strings.TrimRight(c, "/") + if r.URL.Path != c { + url := *r.URL + url.Path = c + http.Redirect(w, r, url.String(), http.StatusMovedPermanently) redirected = true } return @@ -658,12 +739,22 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit return } + if r.FormValue("m") == "text" { + serveText(w, src) + return + } + var buf bytes.Buffer buf.WriteString("
")
 	FormatText(&buf, src, 1, pathpkg.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s")))
 	buf.WriteString("
") + fmt.Fprintf(&buf, `

View as plain text

`, htmlpkg.EscapeString(relpath)) - servePage(w, relpath, title+" "+relpath, "", "", buf.Bytes()) + servePage(w, Page{ + Title: title + " " + relpath, + Tabtitle: relpath, + Body: buf.Bytes(), + }) } func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) { @@ -677,8 +768,11 @@ func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath str return } - contents := applyTemplate(dirlistHTML, "dirlistHTML", list) - servePage(w, relpath, "Directory "+relpath, "", "", contents) + servePage(w, Page{ + Title: "Directory " + relpath, + Tabtitle: relpath, + Body: applyTemplate(dirlistHTML, "dirlistHTML", list), + }) } func serveFile(w http.ResponseWriter, r *http.Request) { @@ -734,6 +828,9 @@ func serveFile(w http.ResponseWriter, r *http.Request) { } if isTextFile(abspath) { + if redirectFile(w, r) { + return + } serveTextFile(w, r, abspath, relpath, "Text file") return } @@ -754,10 +851,6 @@ func serveSearchDesc(w http.ResponseWriter, r *http.Request) { // ---------------------------------------------------------------------------- // Packages -// Fake package file and name for commands. Contains the command documentation. -const fakePkgFile = "doc.go" -const fakePkgName = "documentation" - // Fake relative package path for built-ins. Documentation for all globals // (not just exported ones) will be shown for packages in this directory. const builtinPkgPath = "builtin" @@ -814,17 +907,21 @@ func remoteSearchURL(query string, html bool) string { } type PageInfo struct { - Dirname string // directory containing the package - PList []string // list of package names found - FSet *token.FileSet // corresponding file set - PAst *ast.File // nil if no single AST with package exports - PDoc *doc.Package // nil if no single package documentation - Examples []*doc.Example // nil if no example code - Dirs *DirList // nil if no directory information - DirTime time.Time // directory time stamp - DirFlat bool // if set, show directory in a flat (non-indented) manner - IsPkg bool // false if this is not documenting a real package - Err error // I/O error or nil + Dirname string // directory containing the package + Err error // error or nil + + // package info + FSet *token.FileSet // nil if no package documentation + PDoc *doc.Package // nil if no package documentation + Examples []*doc.Example // nil if no example code + Notes map[string][]string // nil if no package Notes + PAst *ast.File // nil if no AST with package exports + IsMain bool // true for package main + + // directory info + Dirs *DirList // nil if no directory information + DirTime time.Time // directory time stamp + DirFlat bool // if set, show directory in a flat (non-indented) manner } func (info *PageInfo) IsEmpty() bool { @@ -834,30 +931,97 @@ func (info *PageInfo) IsEmpty() bool { type docServer struct { pattern string // url pattern; e.g. "/pkg/" fsRoot string // file system root to which the pattern is mapped - isPkg bool // true if this handler serves real package documentation (as opposed to command documentation) } // fsReadDir implements ReadDir for the go/build package. func fsReadDir(dir string) ([]os.FileInfo, error) { - return fs.ReadDir(dir) + return fs.ReadDir(filepath.ToSlash(dir)) } // fsOpenFile implements OpenFile for the go/build package. func fsOpenFile(name string) (r io.ReadCloser, err error) { - data, err := ReadFile(fs, name) + data, err := ReadFile(fs, filepath.ToSlash(name)) if err != nil { return nil, err } return ioutil.NopCloser(bytes.NewReader(data)), nil } -func inList(name string, list []string) bool { - for _, l := range list { - if name == l { - return true +// packageExports is a local implementation of ast.PackageExports +// which correctly updates each package file's comment list. +// (The ast.PackageExports signature is frozen, hence the local +// implementation). +// +func packageExports(fset *token.FileSet, pkg *ast.Package) { + for _, src := range pkg.Files { + cmap := ast.NewCommentMap(fset, src, src.Comments) + ast.FileExports(src) + src.Comments = cmap.Filter(src).Comments() + } +} + +// addNames adds the names declared by decl to the names set. +// Method names are added in the form ReceiverTypeName_Method. +func addNames(names map[string]bool, decl ast.Decl) { + switch d := decl.(type) { + case *ast.FuncDecl: + name := d.Name.Name + if d.Recv != nil { + var typeName string + switch r := d.Recv.List[0].Type.(type) { + case *ast.StarExpr: + typeName = r.X.(*ast.Ident).Name + case *ast.Ident: + typeName = r.Name + } + name = typeName + "_" + name + } + names[name] = true + case *ast.GenDecl: + for _, spec := range d.Specs { + switch s := spec.(type) { + case *ast.TypeSpec: + names[s.Name.Name] = true + case *ast.ValueSpec: + for _, id := range s.Names { + names[id.Name] = true + } + } + } + } +} + +// globalNames returns a set of the names declared by all package-level +// declarations. Method names are returned in the form Receiver_Method. +func globalNames(pkg *ast.Package) map[string]bool { + names := make(map[string]bool) + for _, file := range pkg.Files { + for _, decl := range file.Decls { + addNames(names, decl) } } - return false + return names +} + +// collectExamples collects examples for pkg from testfiles. +func collectExamples(pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Example { + var files []*ast.File + for _, f := range testfiles { + files = append(files, f) + } + + var examples []*doc.Example + globals := globalNames(pkg) + for _, e := range doc.Examples(files...) { + name := stripExampleSuffix(e.Name) + if name == "" || globals[name] { + examples = append(examples, e) + } else { + log.Printf("skipping example 'Example%s' because '%s' is not a known function or type", e.Name, e.Name) + } + } + + return examples } // getPageInfo returns the PageInfo for a package directory abspath. If the @@ -865,132 +1029,56 @@ func inList(name string, list []string) bool { // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) // is extracted from the AST. If there is no corresponding package in the // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- -// directories, PageInfo.Dirs is nil. If a directory read error occurred, -// PageInfo.Err is set to the respective error but the error is not logged. +// directories, PageInfo.Dirs is nil. If an error occurred, PageInfo.Err is +// set to the respective error but the error is not logged. // -func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo { - var pkgFiles []string +func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (info PageInfo) { + info.Dirname = abspath - // If we're showing the default package, restrict to the ones - // that would be used when building the package on this - // system. This makes sure that if there are separate - // implementations for, say, Windows vs Unix, we don't + // Restrict to the package files that would be used when building + // the package on this system. This makes sure that if there are + // separate implementations for, say, Windows vs Unix, we don't // jumble them all together. - if pkgname == "" { - // Note: Uses current binary's GOOS/GOARCH. - // To use different pair, such as if we allowed the user - // to choose, set ctxt.GOOS and ctxt.GOARCH before - // calling ctxt.ScanDir. - ctxt := build.Default - ctxt.IsAbsPath = pathpkg.IsAbs - ctxt.ReadDir = fsReadDir - ctxt.OpenFile = fsOpenFile - dir, err := ctxt.ImportDir(abspath, 0) - if err == nil { - pkgFiles = append(dir.GoFiles, dir.CgoFiles...) - } - } - - // filter function to select the desired .go files - filter := func(d os.FileInfo) bool { - // Only Go files. - if !isPkgFile(d) { - return false - } - // If we are looking at cmd documentation, only accept - // the special fakePkgFile containing the documentation. - if !h.isPkg { - return d.Name() == fakePkgFile - } - // Also restrict file list to pkgFiles. - return pkgFiles == nil || inList(d.Name(), pkgFiles) - } - - // get package ASTs - fset := token.NewFileSet() - pkgs, err := parseDir(fset, abspath, filter) - if err != nil && pkgs == nil { - // only report directory read errors, ignore parse errors - // (may be able to extract partial package information) - return PageInfo{Dirname: abspath, Err: err} - } - - // select package - var pkg *ast.Package // selected package - var plist []string // list of other package (names), if any - if len(pkgs) == 1 { - // Exactly one package - select it. - for _, p := range pkgs { - pkg = p - } - - } else if len(pkgs) > 1 { - // Multiple packages - select the best matching package: The - // 1st choice is the package with pkgname, the 2nd choice is - // the package with dirname, and the 3rd choice is a package - // that is not called "main" if there is exactly one such - // package. Otherwise, don't select a package. - dirpath, dirname := pathpkg.Split(abspath) - - // If the dirname is "go" we might be in a sub-directory for - // .go files - use the outer directory name instead for better - // results. - if dirname == "go" { - _, dirname = pathpkg.Split(pathpkg.Clean(dirpath)) - } - - var choice3 *ast.Package - loop: - for _, p := range pkgs { - switch { - case p.Name == pkgname: - pkg = p - break loop // 1st choice; we are done - case p.Name == dirname: - pkg = p // 2nd choice - case p.Name != "main": - choice3 = p - } - } - if pkg == nil && len(pkgs) == 2 { - pkg = choice3 - } - - // Compute the list of other packages - // (excluding the selected package, if any). - plist = make([]string, len(pkgs)) - i := 0 - for name := range pkgs { - if pkg == nil || name != pkg.Name { - plist[i] = name - i++ - } - } - plist = plist[0:i] - sort.Strings(plist) + // Note: Uses current binary's GOOS/GOARCH. + // To use different pair, such as if we allowed the user to choose, + // set ctxt.GOOS and ctxt.GOARCH before calling ctxt.ImportDir. + ctxt := build.Default + ctxt.IsAbsPath = pathpkg.IsAbs + ctxt.ReadDir = fsReadDir + ctxt.OpenFile = fsOpenFile + pkginfo, err := ctxt.ImportDir(abspath, 0) + // continue if there are no Go source files; we still want the directory info + if _, nogo := err.(*build.NoGoError); err != nil && !nogo { + info.Err = err + return } - // get examples from *_test.go files - var examples []*doc.Example - filter = func(d os.FileInfo) bool { - return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go") - } - if testpkgs, err := parseDir(fset, abspath, filter); err != nil { - log.Println("parsing test files:", err) - } else { - for _, testpkg := range testpkgs { - var files []*ast.File - for _, f := range testpkg.Files { - files = append(files, f) - } - examples = append(examples, doc.Examples(files...)...) + // collect package files + pkgname := pkginfo.Name + pkgfiles := append(pkginfo.GoFiles, pkginfo.CgoFiles...) + if len(pkgfiles) == 0 { + // Commands written in C have no .go files in the build. + // Instead, documentation may be found in an ignored file. + // The file may be ignored via an explicit +build ignore + // constraint (recommended), or by defining the package + // documentation (historic). + pkgname = "main" // assume package main since pkginfo.Name == "" + pkgfiles = pkginfo.IgnoredGoFiles + } + + // get package information, if any + if len(pkgfiles) > 0 { + // build package AST + fset := token.NewFileSet() + files, err := parseFiles(fset, abspath, pkgfiles) + if err != nil { + info.Err = err + return } - } + pkg := &ast.Package{Name: pkgname, Files: files} - // compute package documentation - var past *ast.File - var pdoc *doc.Package - if pkg != nil { + // extract package documentation + info.FSet = fset if mode&showSource == 0 { // show extracted documentation var m doc.Mode @@ -1000,19 +1088,39 @@ func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoM if mode&allMethods != 0 { m |= doc.AllMethods } - pdoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath + info.PDoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath + + // collect examples + testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...) + files, err = parseFiles(fset, abspath, testfiles) + if err != nil { + log.Println("parsing examples:", err) + } + info.Examples = collectExamples(pkg, files) + + // collect any notes that we want to show + if info.PDoc.Notes != nil { + info.Notes = make(map[string][]string) + for _, m := range notesToShow { + if n := info.PDoc.Notes[m]; n != nil { + info.Notes[m] = n + } + } + } + } else { // show source code // TODO(gri) Consider eliminating export filtering in this mode, // or perhaps eliminating the mode altogether. if mode&noFiltering == 0 { - ast.PackageExports(pkg) + packageExports(fset, pkg) } - past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments) + info.PAst = ast.MergePackageFiles(pkg, 0) } + info.IsMain = pkgname == "main" } - // get directory information + // get directory information, if any var dir *Directory var timestamp time.Time if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil { @@ -1030,20 +1138,11 @@ func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoM dir = newDirectory(abspath, 1) timestamp = time.Now() } + info.Dirs = dir.listing(true) + info.DirTime = timestamp + info.DirFlat = mode&flatDir != 0 - return PageInfo{ - Dirname: abspath, - PList: plist, - FSet: fset, - PAst: past, - PDoc: pdoc, - Examples: examples, - Dirs: dir.listing(true), - DirTime: timestamp, - DirFlat: mode&flatDir != 0, - IsPkg: h.isPkg, - Err: nil, - } + return } func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -1057,7 +1156,7 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { if relpath == builtinPkgPath { mode = noFiltering } - info := h.getPageInfo(abspath, relpath, r.FormValue("p"), mode) + info := h.getPageInfo(abspath, relpath, mode) if info.Err != nil { log.Print(info.Err) serveError(w, r, relpath, info.Err) @@ -1065,8 +1164,7 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if mode&noHtml != 0 { - contents := applyTemplate(packageText, "packageText", info) - serveText(w, contents) + serveText(w, applyTemplate(packageText, "packageText", info)) return } @@ -1074,26 +1172,25 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch { case info.PAst != nil: tabtitle = info.PAst.Name.Name - title = "Package " + tabtitle case info.PDoc != nil: - if info.PDoc.Name == fakePkgName { - // assume that the directory name is the command name - _, tabtitle = pathpkg.Split(relpath) - } else { - tabtitle = info.PDoc.Name - } - if info.IsPkg { - title = "Package " + tabtitle - } else { - title = "Command " + tabtitle - } + tabtitle = info.PDoc.Name default: tabtitle = info.Dirname - title = "Directory " + tabtitle + title = "Directory " if *showTimestamps { subtitle = "Last update: " + info.DirTime.String() } } + if title == "" { + if info.IsMain { + // assume that the directory name is the command name + _, tabtitle = pathpkg.Split(relpath) + title = "Command " + } else { + title = "Package " + } + } + title += tabtitle // special cases for top-level package/command directories switch tabtitle { @@ -1103,8 +1200,12 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { tabtitle = "Commands" } - contents := applyTemplate(packageHTML, "packageHTML", info) - servePage(w, tabtitle, title, subtitle, "", contents) + servePage(w, Page{ + Title: title, + Tabtitle: tabtitle, + Subtitle: subtitle, + Body: applyTemplate(packageHTML, "packageHTML", info), + }) } // ---------------------------------------------------------------------------- @@ -1181,8 +1282,7 @@ func search(w http.ResponseWriter, r *http.Request) { result := lookup(query) if getPageInfoMode(r)&noHtml != 0 { - contents := applyTemplate(searchText, "searchText", result) - serveText(w, contents) + serveText(w, applyTemplate(searchText, "searchText", result)) return } @@ -1193,8 +1293,12 @@ func search(w http.ResponseWriter, r *http.Request) { title = fmt.Sprintf(`No results found for query %q`, query) } - contents := applyTemplate(searchHTML, "searchHTML", result) - servePage(w, query, title, "", query, contents) + servePage(w, Page{ + Title: title, + Tabtitle: query, + Query: query, + Body: applyTemplate(searchHTML, "searchHTML", result), + }) } // ---------------------------------------------------------------------------- diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index 1bef79693..91c56461a 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -148,7 +148,7 @@ func init() { // sanity check: if nKinds is too large, the SpotInfo // accessor functions may need to be updated if nKinds > 8 { - panic("nKinds > 8") + panic("internal error: nKinds > 8") } } @@ -457,12 +457,6 @@ func (x *Indexer) addSnippet(s *Snippet) int { return index } -func (x *Indexer) visitComment(c *ast.CommentGroup) { - if c != nil { - ast.Walk(x, c) - } -} - func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { if id != nil { lists, found := x.words[id.Name] @@ -486,20 +480,24 @@ func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { } } -func (x *Indexer) visitSpec(spec ast.Spec, isVarDecl bool) { +func (x *Indexer) visitFieldList(kind SpotKind, list *ast.FieldList) { + for _, f := range list.List { + x.decl = nil // no snippets for fields + for _, name := range f.Names { + x.visitIdent(kind, name) + } + ast.Walk(x, f.Type) + // ignore tag - not indexed at the moment + } +} + +func (x *Indexer) visitSpec(kind SpotKind, spec ast.Spec) { switch n := spec.(type) { case *ast.ImportSpec: - x.visitComment(n.Doc) x.visitIdent(ImportDecl, n.Name) - ast.Walk(x, n.Path) - x.visitComment(n.Comment) + // ignore path - not indexed at the moment case *ast.ValueSpec: - x.visitComment(n.Doc) - kind := ConstDecl - if isVarDecl { - kind = VarDecl - } for _, n := range n.Names { x.visitIdent(kind, n) } @@ -507,57 +505,51 @@ func (x *Indexer) visitSpec(spec ast.Spec, isVarDecl bool) { for _, v := range n.Values { ast.Walk(x, v) } - x.visitComment(n.Comment) case *ast.TypeSpec: - x.visitComment(n.Doc) x.visitIdent(TypeDecl, n.Name) ast.Walk(x, n.Type) - x.visitComment(n.Comment) + } +} + +func (x *Indexer) visitGenDecl(decl *ast.GenDecl) { + kind := VarDecl + if decl.Tok == token.CONST { + kind = ConstDecl + } + x.decl = decl + for _, s := range decl.Specs { + x.visitSpec(kind, s) } } func (x *Indexer) Visit(node ast.Node) ast.Visitor { - // TODO(gri): methods in interface types are categorized as VarDecl switch n := node.(type) { case nil: - return nil + // nothing to do case *ast.Ident: x.visitIdent(Use, n) - case *ast.Field: - x.decl = nil // no snippets for fields - x.visitComment(n.Doc) - for _, m := range n.Names { - x.visitIdent(VarDecl, m) - } - ast.Walk(x, n.Type) - ast.Walk(x, n.Tag) - x.visitComment(n.Comment) + case *ast.FieldList: + x.visitFieldList(VarDecl, n) + + case *ast.InterfaceType: + x.visitFieldList(MethodDecl, n.Methods) case *ast.DeclStmt: + // local declarations should only be *ast.GenDecls; + // ignore incorrect ASTs if decl, ok := n.Decl.(*ast.GenDecl); ok { - // local declarations can only be *ast.GenDecls x.decl = nil // no snippets for local declarations - x.visitComment(decl.Doc) - for _, s := range decl.Specs { - x.visitSpec(s, decl.Tok == token.VAR) - } - } else { - // handle error case gracefully - ast.Walk(x, n.Decl) + x.visitGenDecl(decl) } case *ast.GenDecl: x.decl = n - x.visitComment(n.Doc) - for _, s := range n.Specs { - x.visitSpec(s, n.Tok == token.VAR) - } + x.visitGenDecl(n) case *ast.FuncDecl: - x.visitComment(n.Doc) kind := FuncDecl if n.Recv != nil { kind = MethodDecl @@ -571,15 +563,11 @@ func (x *Indexer) Visit(node ast.Node) ast.Visitor { } case *ast.File: - x.visitComment(n.Doc) x.decl = nil x.visitIdent(PackageClause, n.Name) for _, d := range n.Decls { ast.Walk(x, d) } - // don't visit package level comments for now - // to avoid duplicate visiting from individual - // nodes default: return x @@ -622,7 +610,7 @@ func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast * // the file set implementation changed or we have another error. base := x.fset.Base() if x.sources.Len() != base { - panic("internal error - file base incorrect") + panic("internal error: file base incorrect") } // append file contents (src) to x.sources diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 2e2889ed3..134410090 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -36,6 +36,7 @@ import ( "fmt" "go/ast" "go/build" + "go/printer" "io" "log" "net/http" @@ -73,9 +74,12 @@ var ( ) func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) { - contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path! w.WriteHeader(http.StatusNotFound) - servePage(w, relpath, "File "+relpath, "", "", contents) + servePage(w, Page{ + Title: "File " + relpath, + Subtitle: relpath, + Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path! + }) } func usage() { @@ -221,6 +225,8 @@ func main() { // Print content that would be served at the URL *urlFlag. if *urlFlag != "" { registerPublicHandlers(http.DefaultServeMux) + initFSTree() + updateMetadata() // Try up to 10 fetches, following redirects. urlstr := *urlFlag for i := 0; i < 10; i++ { @@ -278,10 +284,7 @@ func main() { } registerPublicHandlers(http.DefaultServeMux) - - // Playground handlers are not available in local godoc. - http.HandleFunc("/compile", disabledHandler) - http.HandleFunc("/share", disabledHandler) + registerPlaygroundHandlers(http.DefaultServeMux) // Initialize default directory tree with corresponding timestamp. // (Do it in a goroutine so that launch is quick.) @@ -344,7 +347,7 @@ func main() { fs.Bind(target, OS(path), "/", bindReplace) abspath = target } else if strings.HasPrefix(path, cmdPrefix) { - path = path[len(cmdPrefix):] + path = strings.TrimPrefix(path, cmdPrefix) forceCmd = true } else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" { fs.Bind(target, OS(bp.Dir), "/", bindReplace) @@ -369,13 +372,11 @@ func main() { } mode |= showSource } - // TODO(gri): Provide a mechanism (flag?) to select a package - // if there are multiple packages in a directory. // first, try as package unless forced as command var info PageInfo if !forceCmd { - info = pkgHandler.getPageInfo(abspath, relpath, "", mode) + info = pkgHandler.getPageInfo(abspath, relpath, mode) } // second, try as command unless the path is absolute @@ -383,7 +384,7 @@ func main() { var cinfo PageInfo if !filepath.IsAbs(path) { abspath = pathpkg.Join(cmdHandler.fsRoot, path) - cinfo = cmdHandler.getPageInfo(abspath, relpath, "", mode) + cinfo = cmdHandler.getPageInfo(abspath, relpath, mode) } // determine what to use @@ -421,20 +422,24 @@ func main() { filter := func(s string) bool { return rx.MatchString(s) } switch { case info.PAst != nil: + cmap := ast.NewCommentMap(info.FSet, info.PAst, info.PAst.Comments) ast.FilterFile(info.PAst, filter) // Special case: Don't use templates for printing // so we only get the filtered declarations without // package clause or extra whitespace. for i, d := range info.PAst.Decls { + // determine the comments associated with d only + comments := cmap.Filter(d).Comments() + cn := &printer.CommentedNode{Node: d, Comments: comments} if i > 0 { fmt.Println() } if *html { var buf bytes.Buffer - writeNode(&buf, info.FSet, d) + writeNode(&buf, info.FSet, cn) FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil) } else { - writeNode(os.Stdout, info.FSet, d) + writeNode(os.Stdout, info.FSet, cn) } fmt.Println() } @@ -459,9 +464,3 @@ type httpWriter struct { func (w *httpWriter) Header() http.Header { return w.h } func (w *httpWriter) WriteHeader(code int) { w.code = code } - -// disabledHandler serves a 501 "Not Implemented" response. -func disabledHandler(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) - fmt.Fprint(w, "This functionality is not available via local godoc.") -} diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go index c6b7c2dc8..42a5d2d98 100644 --- a/src/cmd/godoc/parser.go +++ b/src/cmd/godoc/parser.go @@ -2,10 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file contains support functions for parsing .go files. -// Similar functionality is found in package go/parser but the -// functions here operate using godoc's file system fs instead -// of calling the OS's file operations directly. +// This file contains support functions for parsing .go files +// accessed via godoc's file system fs. package main @@ -13,7 +11,6 @@ import ( "go/ast" "go/parser" "go/token" - "os" pathpkg "path" ) @@ -25,44 +22,16 @@ func parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.Fil return parser.ParseFile(fset, filename, src, mode) } -func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.Package, first error) { - pkgs = make(map[string]*ast.Package) - for _, filename := range filenames { - file, err := parseFile(fset, filename, parser.ParseComments) +func parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) { + files := make(map[string]*ast.File) + for _, f := range localnames { + absname := pathpkg.Join(abspath, f) + file, err := parseFile(fset, absname, parser.ParseComments) if err != nil { - if first == nil { - first = err - } - continue - } - - name := file.Name.Name - pkg, found := pkgs[name] - if !found { - // TODO(gri) Use NewPackage here; reconsider ParseFiles API. - pkg = &ast.Package{Name: name, Files: make(map[string]*ast.File)} - pkgs[name] = pkg - } - pkg.Files[filename] = file - } - return -} - -func parseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool) (map[string]*ast.Package, error) { - list, err := fs.ReadDir(path) - if err != nil { - return nil, err - } - - filenames := make([]string, len(list)) - i := 0 - for _, d := range list { - if filter == nil || filter(d) { - filenames[i] = pathpkg.Join(path, d.Name()) - i++ + return nil, err } + files[absname] = file } - filenames = filenames[0:i] - return parseFiles(fset, filenames) + return files, nil } diff --git a/src/cmd/godoc/play-appengine.go b/src/cmd/godoc/play-appengine.go new file mode 100644 index 000000000..9e351d1a2 --- /dev/null +++ b/src/cmd/godoc/play-appengine.go @@ -0,0 +1,35 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// App Engine godoc Playground functionality. + +// +build appengine + +package main + +import ( + "io" + "net/http" + + "appengine" + "appengine/urlfetch" +) + +func bounceToPlayground(w http.ResponseWriter, req *http.Request) { + c := appengine.NewContext(req) + client := urlfetch.Client(c) + url := playgroundBaseURL + req.URL.Path + defer req.Body.Close() + resp, err := client.Post(url, req.Header.Get("Content-type"), req.Body) + if err != nil { + http.Error(w, "Internal Server Error", 500) + c.Errorf("making POST request: %v", err) + return + } + defer resp.Body.Close() + if _, err := io.Copy(w, resp.Body); err != nil { + http.Error(w, "Internal Server Error", 500) + c.Errorf("making POST request: %v", err) + } +} diff --git a/src/cmd/godoc/play-local.go b/src/cmd/godoc/play-local.go new file mode 100644 index 000000000..637ce5e1a --- /dev/null +++ b/src/cmd/godoc/play-local.go @@ -0,0 +1,41 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Stand-alone godoc Playground functionality. + +// +build !appengine + +package main + +import ( + "io" + "net/http" + "net/url" +) + +var playgroundScheme, playgroundHost string + +func init() { + u, err := url.Parse(playgroundBaseURL) + if err != nil { + panic(err) + } + playgroundScheme = u.Scheme + playgroundHost = u.Host +} + +// bounceToPlayground forwards the request to play.golang.org. +func bounceToPlayground(w http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + req.URL.Scheme = playgroundScheme + req.URL.Host = playgroundHost + resp, err := http.Post(req.URL.String(), req.Header.Get("Content-type"), req.Body) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + w.WriteHeader(resp.StatusCode) + io.Copy(w, resp.Body) + resp.Body.Close() +} diff --git a/src/cmd/godoc/play.go b/src/cmd/godoc/play.go new file mode 100644 index 000000000..47a11f6c0 --- /dev/null +++ b/src/cmd/godoc/play.go @@ -0,0 +1,52 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Common Playground functionality. + +package main + +import ( + "encoding/json" + "fmt" + "go/format" + "net/http" +) + +// The server that will service compile and share requests. +const playgroundBaseURL = "http://play.golang.org" + +func registerPlaygroundHandlers(mux *http.ServeMux) { + if *showPlayground { + mux.HandleFunc("/compile", bounceToPlayground) + mux.HandleFunc("/share", bounceToPlayground) + } else { + mux.HandleFunc("/compile", disabledHandler) + mux.HandleFunc("/share", disabledHandler) + } + http.HandleFunc("/fmt", fmtHandler) +} + +type fmtResponse struct { + Body string + Error string +} + +// fmtHandler takes a Go program in its "body" form value, formats it with +// standard gofmt formatting, and writes a fmtResponse as a JSON object. +func fmtHandler(w http.ResponseWriter, r *http.Request) { + resp := new(fmtResponse) + body, err := format.Source([]byte(r.FormValue("body"))) + if err != nil { + resp.Error = err.Error() + } else { + resp.Body = string(body) + } + json.NewEncoder(w).Encode(resp) +} + +// disabledHandler serves a 501 "Not Implemented" response. +func disabledHandler(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) + fmt.Fprint(w, "This functionality is not available via local godoc.") +} diff --git a/src/cmd/godoc/setup-godoc-app.bash b/src/cmd/godoc/setup-godoc-app.bash old mode 100644 new mode 100755 index b8dc4dcf9..792e0d450 --- a/src/cmd/godoc/setup-godoc-app.bash +++ b/src/cmd/godoc/setup-godoc-app.bash @@ -4,13 +4,14 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# This script creates the .zip, index, and configuration files for running -# godoc on app-engine. +# This script creates a complete godoc app in $APPDIR. +# It copies the cmd/godoc and src/pkg/go/... sources from GOROOT, +# synthesizes an app.yaml file, and creates the .zip, index, and +# configuration files. # # If an argument is provided it is assumed to be the app-engine godoc directory. -# Without an argument, $APPDIR is used instead. If GOROOT is not set, the -# current working directory is assumed to be $GOROOT. Various sanity checks -# prevent accidents. +# Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env" +# is consulted to find the $GOROOT. # # The script creates a .zip file representing the $GOROOT file system # and computes the correspondig search index files. These files are then @@ -29,8 +30,8 @@ error() { getArgs() { if [ -z $GOROOT ]; then - GOROOT=$(pwd) - echo "GOROOT not set, using cwd instead" + GOROOT=$(go env GOROOT) + echo "GOROOT not set explicitly, using $GOROOT instead" fi if [ -z $APPDIR ]; then if [ $# == 0 ]; then @@ -47,14 +48,8 @@ getArgs() { if [ ! -x $GOROOT/bin/godoc ]; then error "$GOROOT/bin/godoc does not exist or is not executable" fi - if [ ! -d $APPDIR ]; then - error "$APPDIR is not a directory" - fi - if [ ! -e $APPDIR/app.yaml ]; then - error "$APPDIR is not an app-engine directory; missing file app.yaml" - fi - if [ ! -d $APPDIR/godoc ]; then - error "$APPDIR is missing directory godoc" + if [ -e $APPDIR ]; then + error "$APPDIR exists; check and remove it before trying again" fi # reporting @@ -62,12 +57,32 @@ getArgs() { echo "APPDIR = $APPDIR" } -cleanup() { - echo "*** cleanup $APPDIR" - rm $APPDIR/$ZIPFILE - rm $APPDIR/$INDEXFILE - rm $APPDIR/$SPLITFILES* - rm $APPDIR/$CONFIGFILE +copyGodoc() { + echo "*** copy $GOROOT/src/cmd/godoc to $APPDIR/godoc" + cp -r $GOROOT/src/cmd/godoc $APPDIR/godoc +} + +copyGoPackages() { + echo "*** copy $GOROOT/src/pkg/go to $APPDIR/newgo and rewrite imports" + cp -r $GOROOT/src/pkg/go $APPDIR/newgo + find $APPDIR/newgo -type d -name testdata | xargs rm -r + gofiles=$(find $APPDIR -name '*.go') + sed -i '' 's_^\(."\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles + sed -i '' 's_^\(import "\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles +} + +makeAppYaml() { + echo "*** make $APPDIR/app.yaml" + cat > $APPDIR/app.yaml < α[β:]' -w $GOROOT/src/pkg */ -package documentation +package main // BUG(rsc): The implementation of -r is a bit slow. diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 0bc385b5b..861ff9390 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -29,7 +29,7 @@ var ( rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')") simplifyAST = flag.Bool("s", false, "simplify code") doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") - allErrors = flag.Bool("e", false, "print all (including spurious) errors") + allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)") // layout control comments = flag.Bool("comments", true, "print comments") @@ -65,7 +65,7 @@ func initParserMode() { parserMode |= parser.ParseComments } if *allErrors { - parserMode |= parser.SpuriousErrors + parserMode |= parser.AllErrors } } diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go index 4b2805009..202d0a50c 100644 --- a/src/cmd/gofmt/gofmt_test.go +++ b/src/cmd/gofmt/gofmt_test.go @@ -56,31 +56,37 @@ func runTest(t *testing.T, in, out, flags string) { return } - if got := buf.Bytes(); bytes.Compare(got, expected) != 0 { + if got := buf.Bytes(); !bytes.Equal(got, expected) { t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in) d, err := diff(expected, got) if err == nil { t.Errorf("%s", d) } - ioutil.WriteFile(in+".gofmt", got, 0666) + if err := ioutil.WriteFile(in+".gofmt", got, 0666); err != nil { + t.Error(err) + } } } -// TODO(gri) Add more test cases! var tests = []struct { in, flags string }{ {"gofmt.go", ""}, {"gofmt_test.go", ""}, {"testdata/composites.input", "-s"}, + {"testdata/slices1.input", "-s"}, + {"testdata/slices2.input", "-s"}, {"testdata/old.input", ""}, {"testdata/rewrite1.input", "-r=Foo->Bar"}, {"testdata/rewrite2.input", "-r=int->bool"}, {"testdata/rewrite3.input", "-r=x->x"}, {"testdata/rewrite4.input", "-r=(x)->x"}, + {"testdata/rewrite5.input", "-r=x+x->2*x"}, {"testdata/stdin*.input", "-stdin"}, {"testdata/comments.input", ""}, {"testdata/import.input", ""}, + {"testdata/crlf.input", ""}, // test case for issue 3961; see also TestCRLF + {"testdata/typeswitch.input", ""}, // test case for issue 4470 } func TestRewrite(t *testing.T) { @@ -103,3 +109,24 @@ func TestRewrite(t *testing.T) { } } } + +func TestCRLF(t *testing.T) { + const input = "testdata/crlf.input" // must contain CR/LF's + const golden = "testdata/crlf.golden" // must not contain any CR's + + data, err := ioutil.ReadFile(input) + if err != nil { + t.Error(err) + } + if bytes.Index(data, []byte("\r\n")) < 0 { + t.Errorf("%s contains no CR/LF's", input) + } + + data, err = ioutil.ReadFile(golden) + if err != nil { + t.Error(err) + } + if bytes.Index(data, []byte("\r")) >= 0 { + t.Errorf("%s contains CR's", golden) + } +} diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go index edbce606a..862e9d987 100644 --- a/src/cmd/gofmt/long_test.go +++ b/src/cmd/gofmt/long_test.go @@ -84,7 +84,7 @@ func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) { } // the first and 2nd result should be identical - if bytes.Compare(b1.Bytes(), b2.Bytes()) != 0 { + if !bytes.Equal(b1.Bytes(), b2.Bytes()) { t.Errorf("gofmt %s not idempotent", filename) } } diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index 3c7861f0d..dfabb6198 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -55,6 +55,7 @@ func dump(msg string, val reflect.Value) { // rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file. func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { + cmap := ast.NewCommentMap(fileSet, p, p.Comments) m := make(map[string]reflect.Value) pat := reflect.ValueOf(pattern) repl := reflect.ValueOf(replace) @@ -73,7 +74,9 @@ func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { } return val } - return apply(f, reflect.ValueOf(p)).Interface().(*ast.File) + r := apply(f, reflect.ValueOf(p)).Interface().(*ast.File) + r.Comments = cmap.Filter(r).Comments() // recreate comments list + return r } // setValue is a wrapper for x.SetValue(y); it protects diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go index 470c00625..e9a67a73a 100644 --- a/src/cmd/gofmt/simplify.go +++ b/src/cmd/gofmt/simplify.go @@ -10,7 +10,9 @@ import ( "reflect" ) -type simplifier struct{} +type simplifier struct { + hasDotImport bool // package file contains: import . "some/import/path" +} func (s *simplifier) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { @@ -34,7 +36,7 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor { x = t.Value px = &t.Value } - simplify(x) + ast.Walk(s, x) // simplify x // if the element is a composite literal and its literal type // matches the outer literal's element type exactly, the inner // literal type may be omitted @@ -62,20 +64,54 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor { return nil } + case *ast.SliceExpr: + // a slice expression of the form: s[a:len(s)] + // can be simplified to: s[a:] + // if s is "simple enough" (for now we only accept identifiers) + if s.hasDotImport { + // if dot imports are present, we cannot be certain that an + // unresolved "len" identifier refers to the predefined len() + break + } + if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil { + // the array/slice object is a single, resolved identifier + if call, _ := n.High.(*ast.CallExpr); call != nil && len(call.Args) == 1 && !call.Ellipsis.IsValid() { + // the high expression is a function call with a single argument + if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" && fun.Obj == nil { + // the function called is "len" and it is not locally defined; and + // because we don't have dot imports, it must be the predefined len() + if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Obj == s.Obj { + // the len argument is the array/slice object + n.High = nil + } + } + } + } + // Note: We could also simplify slice expressions of the form s[0:b] to s[:b] + // but we leave them as is since sometimes we want to be very explicit + // about the lower bound. + case *ast.RangeStmt: - // range of the form: for x, _ = range v {...} + // a range of the form: for x, _ = range v {...} // can be simplified to: for x = range v {...} - if n.Value != nil { - if ident, ok := n.Value.(*ast.Ident); ok && ident.Name == "_" { - n.Value = nil - } + if ident, _ := n.Value.(*ast.Ident); ident != nil && ident.Name == "_" { + n.Value = nil } } return s } -func simplify(node ast.Node) { +func simplify(f *ast.File) { var s simplifier - ast.Walk(&s, node) + + // determine if f contains dot imports + for _, imp := range f.Imports { + if imp.Name != nil && imp.Name.Name == "." { + s.hasDotImport = true + break + } + } + + ast.Walk(&s, f) } diff --git a/src/cmd/gofmt/testdata/crlf.golden b/src/cmd/gofmt/testdata/crlf.golden new file mode 100644 index 000000000..57679f770 --- /dev/null +++ b/src/cmd/gofmt/testdata/crlf.golden @@ -0,0 +1,12 @@ +/* + Source containing CR/LF line endings. + The gofmt'ed output must only have LF + line endings. +*/ +package main + +func main() { + // line comment + println("hello, world!") // another line comment + println() +} diff --git a/src/cmd/gofmt/testdata/crlf.input b/src/cmd/gofmt/testdata/crlf.input new file mode 100644 index 000000000..61a1aa0b4 --- /dev/null +++ b/src/cmd/gofmt/testdata/crlf.input @@ -0,0 +1,12 @@ +/* + Source containing CR/LF line endings. + The gofmt'ed output must only have LF + line endings. +*/ +package main + +func main() { + // line comment + println("hello, world!") // another line comment + println() +} diff --git a/src/cmd/gofmt/testdata/rewrite5.golden b/src/cmd/gofmt/testdata/rewrite5.golden new file mode 100644 index 000000000..5a448a63d --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite5.golden @@ -0,0 +1,15 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Rewriting of expressions containing nodes with associated comments to +// expressions without those nodes must also eliminate the associated +// comments. + +package p + +func f(x int) int { + _ = 2 * x // this comment remains in the rewrite + _ = 2 * x + return 2 * x +} diff --git a/src/cmd/gofmt/testdata/rewrite5.input b/src/cmd/gofmt/testdata/rewrite5.input new file mode 100644 index 000000000..0d759e69b --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite5.input @@ -0,0 +1,15 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Rewriting of expressions containing nodes with associated comments to +// expressions without those nodes must also eliminate the associated +// comments. + +package p + +func f(x int) int { + _ = x + x // this comment remains in the rewrite + _ = x /* this comment must not be in the rewrite */ + x + return x /* this comment must not be in the rewrite */ + x +} diff --git a/src/cmd/gofmt/testdata/slices1.golden b/src/cmd/gofmt/testdata/slices1.golden new file mode 100644 index 000000000..61e074f68 --- /dev/null +++ b/src/cmd/gofmt/testdata/slices1.golden @@ -0,0 +1,58 @@ +// Test cases for slice expression simplification. +package p + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:] + _ = a[3:(len(a))] + _ = a[len(a) : len(a)-1] + _ = a[0:len(b)] + + _ = a[:] + _ = a[:10] + _ = a[:] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + + _ = s[:] + _ = s[:10] + _ = s[:] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] +) + +func _() { + s := s[0:] + _ = s +} diff --git a/src/cmd/gofmt/testdata/slices1.input b/src/cmd/gofmt/testdata/slices1.input new file mode 100644 index 000000000..4d2cbfff4 --- /dev/null +++ b/src/cmd/gofmt/testdata/slices1.input @@ -0,0 +1,58 @@ +// Test cases for slice expression simplification. +package p + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:len(a)] + _ = a[3:(len(a))] + _ = a[len(a) : len(a)-1] + _ = a[0:len(b)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] +) + +func _() { + s := s[0:len(s)] + _ = s +} diff --git a/src/cmd/gofmt/testdata/slices2.golden b/src/cmd/gofmt/testdata/slices2.golden new file mode 100644 index 000000000..433788e1e --- /dev/null +++ b/src/cmd/gofmt/testdata/slices2.golden @@ -0,0 +1,61 @@ +// Test cases for slice expression simplification. +// Because of a dot import, these slices must remain untouched. +package p + +import . "math" + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:len(a)] + _ = a[3:(len(a))] + _ = a[len(a) : len(a)-1] + _ = a[0:len(b)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] +) + +func _() { + s := s[0:len(s)] + _ = s +} diff --git a/src/cmd/gofmt/testdata/slices2.input b/src/cmd/gofmt/testdata/slices2.input new file mode 100644 index 000000000..433788e1e --- /dev/null +++ b/src/cmd/gofmt/testdata/slices2.input @@ -0,0 +1,61 @@ +// Test cases for slice expression simplification. +// Because of a dot import, these slices must remain untouched. +package p + +import . "math" + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:len(a)] + _ = a[3:(len(a))] + _ = a[len(a) : len(a)-1] + _ = a[0:len(b)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] +) + +func _() { + s := s[0:len(s)] + _ = s +} diff --git a/src/cmd/gofmt/testdata/typeswitch.golden b/src/cmd/gofmt/testdata/typeswitch.golden new file mode 100644 index 000000000..87e916181 --- /dev/null +++ b/src/cmd/gofmt/testdata/typeswitch.golden @@ -0,0 +1,60 @@ +/* + Parenthesized type switch expressions originally + accepted by gofmt must continue to be rewritten + into the correct unparenthesized form. + + Only type-switches that didn't declare a variable + in the the type switch type assertion and which + contained only "expression-like" (named) types in their + cases were permitted to have their type assertion parenthesized + by go/parser (due to a weak predicate in the parser). All others + were rejected always, either with a syntax error in the + type switch header or in the case. + + See also issue 4470. +*/ +package p + +func f() { + var x interface{} + switch x.(type) { // should remain the same + } + switch x.(type) { // should become: switch x.(type) { + } + + switch x.(type) { // should remain the same + case int: + } + switch x.(type) { // should become: switch x.(type) { + case int: + } + + switch x.(type) { // should remain the same + case []int: + } + + // Parenthesized (x.(type)) in type switches containing cases + // with unnamed (literal) types were never permitted by gofmt; + // thus there won't be any code in the wild using this style if + // the code was gofmt-ed. + /* + switch (x.(type)) { + case []int: + } + */ + + switch t := x.(type) { // should remain the same + default: + _ = t + } + + // Parenthesized (x.(type)) in type switches declaring a variable + // were never permitted by gofmt; thus there won't be any code in + // the wild using this style if the code was gofmt-ed. + /* + switch t := (x.(type)) { + default: + _ = t + } + */ +} diff --git a/src/cmd/gofmt/testdata/typeswitch.input b/src/cmd/gofmt/testdata/typeswitch.input new file mode 100644 index 000000000..f90f28949 --- /dev/null +++ b/src/cmd/gofmt/testdata/typeswitch.input @@ -0,0 +1,60 @@ +/* + Parenthesized type switch expressions originally + accepted by gofmt must continue to be rewritten + into the correct unparenthesized form. + + Only type-switches that didn't declare a variable + in the the type switch type assertion and which + contained only "expression-like" (named) types in their + cases were permitted to have their type assertion parenthesized + by go/parser (due to a weak predicate in the parser). All others + were rejected always, either with a syntax error in the + type switch header or in the case. + + See also issue 4470. +*/ +package p + +func f() { + var x interface{} + switch x.(type) { // should remain the same + } + switch (x.(type)) { // should become: switch x.(type) { + } + + switch x.(type) { // should remain the same + case int: + } + switch (x.(type)) { // should become: switch x.(type) { + case int: + } + + switch x.(type) { // should remain the same + case []int: + } + + // Parenthesized (x.(type)) in type switches containing cases + // with unnamed (literal) types were never permitted by gofmt; + // thus there won't be any code in the wild using this style if + // the code was gofmt-ed. + /* + switch (x.(type)) { + case []int: + } + */ + + switch t := x.(type) { // should remain the same + default: + _ = t + } + + // Parenthesized (x.(type)) in type switches declaring a variable + // were never permitted by gofmt; thus there won't be any code in + // the wild using this style if the code was gofmt-ed. + /* + switch t := (x.(type)) { + default: + _ = t + } + */ +} diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 786c10b64..6c6b1be43 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -34,6 +34,7 @@ #include "../ld/lib.h" #include "../ld/elf.h" #include "../ld/pe.h" +#include "../../pkg/runtime/mgc0.h" void dynreloc(void); static vlong addaddrplus4(Sym *s, Sym *t, int32 add); @@ -57,70 +58,73 @@ datcmp(Sym *s1, Sym *s2) } Sym* -datsort(Sym *l) +listsort(Sym *l, int (*cmp)(Sym*, Sym*), int off) { Sym *l1, *l2, *le; + #define NEXT(l) (*(Sym**)((char*)(l)+off)) - if(l == 0 || l->next == 0) + if(l == 0 || NEXT(l) == 0) return l; l1 = l; l2 = l; for(;;) { - l2 = l2->next; + l2 = NEXT(l2); if(l2 == 0) break; - l2 = l2->next; + l2 = NEXT(l2); if(l2 == 0) break; - l1 = l1->next; + l1 = NEXT(l1); } - l2 = l1->next; - l1->next = 0; - l1 = datsort(l); - l2 = datsort(l2); + l2 = NEXT(l1); + NEXT(l1) = 0; + l1 = listsort(l, cmp, off); + l2 = listsort(l2, cmp, off); /* set up lead element */ - if(datcmp(l1, l2) < 0) { + if(cmp(l1, l2) < 0) { l = l1; - l1 = l1->next; + l1 = NEXT(l1); } else { l = l2; - l2 = l2->next; + l2 = NEXT(l2); } le = l; for(;;) { if(l1 == 0) { while(l2) { - le->next = l2; + NEXT(le) = l2; le = l2; - l2 = l2->next; + l2 = NEXT(l2); } - le->next = 0; + NEXT(le) = 0; break; } if(l2 == 0) { while(l1) { - le->next = l1; + NEXT(le) = l1; le = l1; - l1 = l1->next; + l1 = NEXT(l1); } break; } - if(datcmp(l1, l2) < 0) { - le->next = l1; + if(cmp(l1, l2) < 0) { + NEXT(le) = l1; le = l1; - l1 = l1->next; + l1 = NEXT(l1); } else { - le->next = l2; + NEXT(le) = l2; le = l2; - l2 = l2->next; + l2 = NEXT(l2); } } - le->next = 0; + NEXT(le) = 0; return l; + + #undef NEXT } Reloc* @@ -145,21 +149,22 @@ void relocsym(Sym *s) { Reloc *r; + Sym *rs; Prog p; int32 i, off, siz, fl; vlong o; uchar *cast; - + cursym = s; memset(&p, 0, sizeof p); for(r=s->r; rr+s->nr; r++) { off = r->off; siz = r->siz; - if(off < 0 || off+(siz&~Rbig) > s->np) { - diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz&~Rbig, 0, s->np); + if(off < 0 || off+siz > s->np) { + diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np); continue; } - if(r->sym != S && (r->sym->type == 0 || r->sym->type == SXREF)) { + if(r->sym != S && (r->sym->type & SMASK == 0 || r->sym->type & SMASK == SXREF)) { diag("%s: not defined", r->sym->name); continue; } @@ -175,18 +180,35 @@ relocsym(Sym *s) switch(r->type) { default: o = 0; - if(archreloc(r, s, &o) < 0) + if(isobj || archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case D_ADDR: o = symaddr(r->sym) + r->add; + if(isobj && r->sym->type != SCONST) { + if(thechar == '6') + o = 0; + else { + // set up addend for eventual relocation via outer symbol + rs = r->sym; + while(rs->outer != nil) + rs = rs->outer; + o -= symaddr(rs); + } + } break; case D_PCREL: - // r->sym can be null when CALL $(constant) is transformed from absoulte PC to relative PC call. + // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. o = 0; if(r->sym) o += symaddr(r->sym); o += r->add - (s->value + r->off + r->siz); + if(isobj && r->sym->type != SCONST) { + if(thechar == '6') + o = 0; + else + o = r->add - r->siz; + } break; case D_SIZE: o = r->sym->size + r->add; @@ -197,20 +219,6 @@ relocsym(Sym *s) default: cursym = s; diag("bad reloc size %#ux for %s", siz, r->sym->name); - case 4 + Rbig: - fl = o; - s->p[off] = fl>>24; - s->p[off+1] = fl>>16; - s->p[off+2] = fl>>8; - s->p[off+3] = fl; - break; - case 4 + Rlittle: - fl = o; - s->p[off] = fl; - s->p[off+1] = fl>>8; - s->p[off+2] = fl>>16; - s->p[off+3] = fl>>24; - break; case 4: fl = o; cast = (uchar*)&fl; @@ -222,7 +230,7 @@ relocsym(Sym *s) for(i=0; i<8; i++) s->p[off+i] = cast[inuxi8[i]]; break; - } + } } } @@ -230,7 +238,7 @@ void reloc(void) { Sym *s; - + if(debug['v']) Bprint(&bso, "%5.2f reloc\n", cputime()); Bflush(&bso); @@ -245,10 +253,12 @@ void dynrelocsym(Sym *s) { Reloc *r; + Sym *rel; + Sym *got; if(HEADTYPE == Hwindows) { Sym *rel, *targ; - + rel = lookup(".rel", 0); if(s == rel) return; @@ -258,7 +268,7 @@ dynrelocsym(Sym *s) targ->plt = rel->size; r->sym = rel; r->add = targ->plt; - + // jmp *addr if(thechar == '8') { adduint8(rel, 0xff); @@ -281,16 +291,30 @@ dynrelocsym(Sym *s) return; } - for(r=s->r; rr+s->nr; r++) - if(r->sym->type == SDYNIMPORT || r->type >= 256) + got = rel = nil; + if(flag_shared) { + rel = lookuprel(); + got = lookup(".got", 0); + } + s->rel_ro = 0; + for(r=s->r; rr+s->nr; r++) { + if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) adddynrel(s, r); + if(flag_shared && r->sym != S && (r->sym->dynimpname == nil || r->sym->dynexport) && r->type == D_ADDR + && (s == got || s->type == SDATA || s->type == SGOSTRING || s->type == STYPE || s->type == SRODATA)) { + // Create address based RELATIVE relocation + adddynrela(rel, s, r); + if(s->type < SNOPTRDATA) + s->rel_ro = 1; + } + } } void dynreloc(void) { Sym *s; - + // -d supresses dynamic loader format, so we may as well not // compute these sections or mark their symbols as reachable. if(debug['d'] && HEADTYPE != Hwindows) @@ -363,12 +387,12 @@ savedata(Sym *s, Prog *p, char *pn) break; } break; - + case D_SCONST: for(i=0; ip[off+i] = p->to.scon[i]; break; - + case D_CONST: if(p->to.sym) goto Addr; @@ -449,12 +473,12 @@ blk(Sym *start, int32 addr, int32 size) errorexit(); } } - + for(; addr < eaddr; addr++) cput(0); cflush(); } - + void codeblk(int32 addr, int32 size) { @@ -497,7 +521,7 @@ codeblk(int32 addr, int32 size) Bprint(&bso, "%.6llux\t%-20s | foreign text\n", (vlong)addr, sym->name); n = sym->size; q = sym->p; - + while(n >= 16) { Bprint(&bso, "%.6ux\t%-20.16I\n", addr, q); addr += 16; @@ -509,7 +533,7 @@ codeblk(int32 addr, int32 size) addr += n; continue; } - + Bprint(&bso, "%.6llux\t%-20s | %P\n", (vlong)sym->value, sym->name, p); for(p = p->link; p != P; p = p->link) { if(p->link != P) @@ -531,7 +555,7 @@ codeblk(int32 addr, int32 size) } Bflush(&bso); } - + void datblk(int32 addr, int32 size) { @@ -579,7 +603,7 @@ datblk(int32 addr, int32 size) void strnput(char *s, int n) { - for(; *s && n > 0; s++) { + for(; n > 0 && *s; s++) { cput(*s); n--; } @@ -594,18 +618,22 @@ addstrdata(char *name, char *value) { Sym *s, *sp; char *p; - + p = smprint("%s.str", name); sp = lookup(p, 0); free(p); addstring(sp, value); s = lookup(name, 0); + s->size = 0; s->dupok = 1; addaddr(s, sp); adduint32(s, strlen(value)); if(PtrSize == 8) adduint32(s, 0); // round struct to pointer width + + // in case reachability has already been computed + sp->reachable = s->reachable; } vlong @@ -628,41 +656,51 @@ addstring(Sym *s, char *str) } vlong -adduintxx(Sym *s, uint64 v, int wid) +setuintxx(Sym *s, vlong off, uint64 v, int wid) { - int32 i, r, fl; + int32 i, fl; vlong o; uchar *cast; if(s->type == 0) s->type = SDATA; s->reachable = 1; - r = s->size; - s->size += wid; - symgrow(s, s->size); - assert(r+wid <= s->size); + if(s->size < off+wid) { + s->size = off+wid; + symgrow(s, s->size); + } fl = v; cast = (uchar*)&fl; switch(wid) { case 1: - s->p[r] = cast[inuxi1[0]]; + s->p[off] = cast[inuxi1[0]]; break; case 2: for(i=0; i<2; i++) - s->p[r+i] = cast[inuxi2[i]]; + s->p[off+i] = cast[inuxi2[i]]; break; case 4: for(i=0; i<4; i++) - s->p[r+i] = cast[inuxi4[i]]; + s->p[off+i] = cast[inuxi4[i]]; break; case 8: o = v; cast = (uchar*)&o; for(i=0; i<8; i++) - s->p[r+i] = cast[inuxi8[i]]; + s->p[off+i] = cast[inuxi8[i]]; break; } - return r; + return off; +} + +vlong +adduintxx(Sym *s, uint64 v, int wid) +{ + int32 off; + + off = s->size; + setuintxx(s, off, v, wid); + return off; } vlong @@ -689,6 +727,30 @@ adduint64(Sym *s, uint64 v) return adduintxx(s, v, 8); } +void +setuint8(Sym *s, vlong r, uint8 v) +{ + setuintxx(s, r, v, 1); +} + +void +setuint16(Sym *s, vlong r, uint16 v) +{ + setuintxx(s, r, v, 2); +} + +void +setuint32(Sym *s, vlong r, uint32 v) +{ + setuintxx(s, r, v, 4); +} + +void +setuint64(Sym *s, vlong r, uint64 v) +{ + setuintxx(s, r, v, 8); +} + vlong addaddrplus(Sym *s, Sym *t, int32 add) { @@ -736,7 +798,7 @@ addpcrelplus(Sym *s, Sym *t, int32 add) { vlong i; Reloc *r; - + if(s->type == 0) s->type = SDATA; s->reachable = 1; @@ -758,6 +820,33 @@ addaddr(Sym *s, Sym *t) return addaddrplus(s, t, 0); } +vlong +setaddrplus(Sym *s, vlong off, Sym *t, int32 add) +{ + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + if(off+PtrSize > s->size) { + s->size = off + PtrSize; + symgrow(s, s->size); + } + r = addrel(s); + r->sym = t; + r->off = off; + r->siz = PtrSize; + r->type = D_ADDR; + r->add = add; + return off; +} + +vlong +setaddr(Sym *s, vlong off, Sym *t) +{ + return setaddrplus(s, off, t, 0); +} + vlong addsize(Sym *s, Sym *t) { @@ -793,17 +882,91 @@ dosymtype(void) } } +static int32 +alignsymsize(int32 s) +{ + if(s >= 8) + s = rnd(s, 8); + else if(s >= PtrSize) + s = rnd(s, PtrSize); + else if(s > 2) + s = rnd(s, 4); + return s; +} + +static int32 +aligndatsize(int32 datsize, Sym *s) +{ + int32 t; + + if(s->align != 0) { + datsize = rnd(datsize, s->align); + } else { + t = alignsymsize(s->size); + if(t & 1) { + ; + } else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + } + return datsize; +} + +static void +gcaddsym(Sym *gc, Sym *s, int32 off) +{ + int32 a; + Sym *gotype; + + if(s->size < PtrSize) + return; + if(strcmp(s->name, ".string") == 0) + return; + + gotype = s->gotype; + if(gotype != nil) { + //print("gcaddsym: %s %d %s\n", s->name, s->size, gotype->name); + adduintxx(gc, GC_CALL, PtrSize); + adduintxx(gc, off, PtrSize); + addpcrelplus(gc, decodetype_gc(gotype), 3*PtrSize+4); + if(PtrSize == 8) + adduintxx(gc, 0, 4); + } else { + //print("gcaddsym: %s %d \n", s->name, s->size); + for(a = -off&(PtrSize-1); a+PtrSize<=s->size; a+=PtrSize) { + adduintxx(gc, GC_APTR, PtrSize); + adduintxx(gc, off+a, PtrSize); + } + } +} + void dodata(void) { int32 t, datsize; - Section *sect, *noptr; + Section *sect; Sym *s, *last, **l; + Sym *gcdata1, *gcbss1; if(debug['v']) Bprint(&bso, "%5.2f dodata\n", cputime()); Bflush(&bso); + // define garbage collection symbols + gcdata1 = lookup("gcdata1", 0); + gcdata1->type = SGCDATA; + gcdata1->reachable = 1; + gcbss1 = lookup("gcbss1", 0); + gcbss1->type = SGCBSS; + gcbss1->reachable = 1; + + // size of .data and .bss section. the zero value is later replaced by the actual size of the section. + adduintxx(gcdata1, 0, PtrSize); + adduintxx(gcbss1, 0, PtrSize); + last = nil; datap = nil; @@ -834,7 +997,7 @@ dodata(void) * symbol, which is itself data. */ dynreloc(); - + /* some symbols may no longer belong in datap (Mach-O) */ for(l=&datap; (s=*l) != nil; ) { if(s->type <= STEXT || SXREF <= s->type) @@ -844,19 +1007,150 @@ dodata(void) } *l = nil; - datap = datsort(datap); + if(flag_shared) { + for(s=datap; s != nil; s = s->next) { + if(s->rel_ro) + s->type = SDATARELRO; + } + } + datap = listsort(datap, datcmp, offsetof(Sym, next)); /* - * allocate data sections. list is sorted by type, + * allocate sections. list is sorted by type, * so we can just walk it for each piece we want to emit. + * segdata is processed before segtext, because we need + * to see all symbols in the .data and .bss sections in order + * to generate garbage collection information. */ + /* begin segdata */ + + /* skip symbols belonging to segtext */ + s = datap; + for(; s != nil && s->type < SELFSECT; s = s->next) + ; + + /* writable ELF sections */ + datsize = 0; + for(; s != nil && s->type < SNOPTRDATA; s = s->next) { + sect = addsection(&segdata, s->name, 06); + if(s->align != 0) + datsize = rnd(datsize, s->align); + sect->vaddr = datsize; + s->sect = sect; + s->type = SDATA; + s->value = datsize; + datsize += rnd(s->size, PtrSize); + sect->len = datsize - sect->vaddr; + } + + /* pointer-free data */ + sect = addsection(&segdata, ".noptrdata", 06); + sect->vaddr = datsize; + lookup("noptrdata", 0)->sect = sect; + lookup("enoptrdata", 0)->sect = sect; + for(; s != nil && s->type < SDATARELRO; s = s->next) { + s->sect = sect; + s->type = SDATA; + t = alignsymsize(s->size); + datsize = aligndatsize(datsize, s); + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* dynamic relocated rodata */ + if(flag_shared) { + sect = addsection(&segdata, ".data.rel.ro", 06); + sect->vaddr = datsize; + lookup("datarelro", 0)->sect = sect; + lookup("edatarelro", 0)->sect = sect; + for(; s != nil && s->type == SDATARELRO; s = s->next) { + if(s->align != 0) + datsize = rnd(datsize, s->align); + s->sect = sect; + s->type = SDATA; + s->value = datsize; + datsize += rnd(s->size, PtrSize); + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + } + + /* data */ + sect = addsection(&segdata, ".data", 06); + sect->vaddr = datsize; + lookup("data", 0)->sect = sect; + lookup("edata", 0)->sect = sect; + for(; s != nil && s->type < SBSS; s = s->next) { + if(s->type == SDATARELRO) { + cursym = s; + diag("unexpected symbol type %d", s->type); + } + s->sect = sect; + s->type = SDATA; + t = alignsymsize(s->size); + datsize = aligndatsize(datsize, s); + s->value = datsize; + gcaddsym(gcdata1, s, datsize - sect->vaddr); // gc + datsize += t; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + adduintxx(gcdata1, GC_END, PtrSize); + setuintxx(gcdata1, 0, sect->len, PtrSize); + + /* bss */ + sect = addsection(&segdata, ".bss", 06); + sect->vaddr = datsize; + lookup("bss", 0)->sect = sect; + lookup("ebss", 0)->sect = sect; + for(; s != nil && s->type < SNOPTRBSS; s = s->next) { + s->sect = sect; + t = alignsymsize(s->size); + datsize = aligndatsize(datsize, s); + s->value = datsize; + gcaddsym(gcbss1, s, datsize - sect->vaddr); // gc + datsize += t; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + adduintxx(gcbss1, GC_END, PtrSize); + setuintxx(gcbss1, 0, sect->len, PtrSize); + + /* pointer-free bss */ + sect = addsection(&segdata, ".noptrbss", 06); + sect->vaddr = datsize; + lookup("noptrbss", 0)->sect = sect; + lookup("enoptrbss", 0)->sect = sect; + for(; s != nil; s = s->next) { + if(s->type > SNOPTRBSS) { + cursym = s; + diag("unexpected symbol type %d", s->type); + } + s->sect = sect; + t = alignsymsize(s->size); + datsize = aligndatsize(datsize, s); + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + lookup("end", 0)->sect = sect; + + /* we finished segdata, begin segtext */ + /* read-only data */ sect = addsection(&segtext, ".rodata", 04); sect->vaddr = 0; + lookup("rodata", 0)->sect = sect; + lookup("erodata", 0)->sect = sect; datsize = 0; s = datap; - for(; s != nil && s->type < SSYMTAB; s = s->next) { + for(; s != nil && s->type < STYPELINK; s = s->next) { + s->sect = sect; if(s->align != 0) datsize = rnd(datsize, s->align); s->type = SRODATA; @@ -864,11 +1158,57 @@ dodata(void) datsize += rnd(s->size, PtrSize); } sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* type */ + sect = addsection(&segtext, ".typelink", 04); + sect->vaddr = datsize; + lookup("typelink", 0)->sect = sect; + lookup("etypelink", 0)->sect = sect; + for(; s != nil && s->type == STYPELINK; s = s->next) { + s->sect = sect; + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* gcdata */ + sect = addsection(&segtext, ".gcdata", 04); + sect->vaddr = datsize; + lookup("gcdata", 0)->sect = sect; + lookup("egcdata", 0)->sect = sect; + for(; s != nil && s->type == SGCDATA; s = s->next) { + s->sect = sect; + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* gcbss */ + sect = addsection(&segtext, ".gcbss", 04); + sect->vaddr = datsize; + lookup("gcbss", 0)->sect = sect; + lookup("egcbss", 0)->sect = sect; + for(; s != nil && s->type == SGCBSS; s = s->next) { + s->sect = sect; + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); /* gosymtab */ sect = addsection(&segtext, ".gosymtab", 04); sect->vaddr = datsize; + lookup("symtab", 0)->sect = sect; + lookup("esymtab", 0)->sect = sect; for(; s != nil && s->type < SPCLNTAB; s = s->next) { + s->sect = sect; s->type = SRODATA; s->value = datsize; datsize += s->size; @@ -879,7 +1219,10 @@ dodata(void) /* gopclntab */ sect = addsection(&segtext, ".gopclntab", 04); sect->vaddr = datsize; + lookup("pclntab", 0)->sect = sect; + lookup("epclntab", 0)->sect = sect; for(; s != nil && s->type < SELFROSECT; s = s->next) { + s->sect = sect; s->type = SRODATA; s->value = datsize; datsize += s->size; @@ -893,101 +1236,12 @@ dodata(void) if(s->align != 0) datsize = rnd(datsize, s->align); sect->vaddr = datsize; + s->sect = sect; s->type = SRODATA; s->value = datsize; datsize += rnd(s->size, PtrSize); sect->len = datsize - sect->vaddr; } - - /* writable ELF sections */ - datsize = 0; - for(; s != nil && s->type < SNOPTRDATA; s = s->next) { - sect = addsection(&segdata, s->name, 06); - if(s->align != 0) - datsize = rnd(datsize, s->align); - sect->vaddr = datsize; - s->type = SDATA; - s->value = datsize; - datsize += rnd(s->size, PtrSize); - sect->len = datsize - sect->vaddr; - } - - /* pointer-free data, then data */ - sect = addsection(&segdata, ".noptrdata", 06); - sect->vaddr = datsize; - noptr = sect; - for(; ; s = s->next) { - if((s == nil || s->type >= SDATA) && sect == noptr) { - // finish noptrdata, start data - datsize = rnd(datsize, 8); - sect->len = datsize - sect->vaddr; - sect = addsection(&segdata, ".data", 06); - sect->vaddr = datsize; - } - if(s == nil || s->type >= SBSS) { - // finish data - sect->len = datsize - sect->vaddr; - break; - } - s->type = SDATA; - t = s->size; - if(t >= PtrSize) - t = rnd(t, PtrSize); - else if(t > 2) - t = rnd(t, 4); - if(s->align != 0) - datsize = rnd(datsize, s->align); - else if(t & 1) { - ; - } else if(t & 2) - datsize = rnd(datsize, 2); - else if(t & 4) - datsize = rnd(datsize, 4); - else - datsize = rnd(datsize, 8); - s->value = datsize; - datsize += t; - } - - /* bss, then pointer-free bss */ - noptr = nil; - sect = addsection(&segdata, ".bss", 06); - sect->vaddr = datsize; - for(; ; s = s->next) { - if((s == nil || s->type >= SNOPTRBSS) && noptr == nil) { - // finish bss, start noptrbss - datsize = rnd(datsize, 8); - sect->len = datsize - sect->vaddr; - sect = addsection(&segdata, ".noptrbss", 06); - sect->vaddr = datsize; - noptr = sect; - } - if(s == nil) { - sect->len = datsize - sect->vaddr; - break; - } - if(s->type > SNOPTRBSS) { - cursym = s; - diag("unexpected symbol type %d", s->type); - } - t = s->size; - if(t >= PtrSize) - t = rnd(t, PtrSize); - else if(t > 2) - t = rnd(t, 4); - if(s->align != 0) - datsize = rnd(datsize, s->align); - else if(t & 1) { - ; - } else if(t & 2) - datsize = rnd(datsize, 2); - else if(t & 4) - datsize = rnd(datsize, 4); - else - datsize = rnd(datsize, 8); - s->value = datsize; - datsize += t; - } } // assign addresses to text @@ -1002,16 +1256,21 @@ textaddress(void) addsection(&segtext, ".text", 05); // Assign PCs in text segment. - // Could parallelize, by assigning to text + // Could parallelize, by assigning to text // and then letting threads copy down, but probably not worth it. sect = segtext.sect; + lookup("text", 0)->sect = sect; + lookup("etext", 0)->sect = sect; va = INITTEXT; sect->vaddr = va; for(sym = textp; sym != nil; sym = sym->next) { + sym->sect = sect; if(sym->type & SSUB) continue; if(sym->align != 0) va = rnd(va, sym->align); + else if(sym->text != P) + va = rnd(va, FuncAlign); sym->value = 0; for(sub = sym; sub != S; sub = sub->sub) { sub->value += va; @@ -1023,7 +1282,7 @@ textaddress(void) } va += sym->size; } - + // Align end of code so that rodata starts aligned. // 128 bytes is likely overkill but definitely cheap. va = rnd(va, 128); @@ -1035,7 +1294,8 @@ textaddress(void) void address(void) { - Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss; + Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss, *datarelro; + Section *gcdata, *gcbss, *typelink; Sym *sym, *sub; uvlong va; @@ -1058,12 +1318,13 @@ address(void) segdata.filelen = 0; if(HEADTYPE == Hwindows) segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN); - if(HEADTYPE == Hplan9x32) + if(HEADTYPE == Hplan9x64 || HEADTYPE == Hplan9x32) segdata.fileoff = segtext.fileoff + segtext.filelen; data = nil; noptr = nil; bss = nil; noptrbss = nil; + datarelro = nil; for(s=segdata.sect; s != nil; s=s->next) { s->vaddr = va; va += s->len; @@ -1077,12 +1338,17 @@ address(void) bss = s; if(strcmp(s->name, ".noptrbss") == 0) noptrbss = s; + if(strcmp(s->name, ".data.rel.ro") == 0) + datarelro = s; } segdata.filelen -= bss->len + noptrbss->len; // deduct .bss text = segtext.sect; rodata = text->next; - symtab = rodata->next; + typelink = rodata->next; + gcdata = typelink->next; + gcbss = gcdata->next; + symtab = gcbss->next; pclntab = symtab->next; for(sym = datap; sym != nil; sym = sym->next) { @@ -1094,11 +1360,21 @@ address(void) for(sub = sym->sub; sub != nil; sub = sub->sub) sub->value += sym->value; } - + xdefine("text", STEXT, text->vaddr); xdefine("etext", STEXT, text->vaddr + text->len); xdefine("rodata", SRODATA, rodata->vaddr); xdefine("erodata", SRODATA, rodata->vaddr + rodata->len); + xdefine("typelink", SRODATA, typelink->vaddr); + xdefine("etypelink", SRODATA, typelink->vaddr + typelink->len); + if(datarelro != nil) { + xdefine("datarelro", SRODATA, datarelro->vaddr); + xdefine("edatarelro", SRODATA, datarelro->vaddr + datarelro->len); + } + xdefine("gcdata", SGCDATA, gcdata->vaddr); + xdefine("egcdata", SGCDATA, gcdata->vaddr + gcdata->len); + xdefine("gcbss", SGCBSS, gcbss->vaddr); + xdefine("egcbss", SGCBSS, gcbss->vaddr + gcbss->len); xdefine("symtab", SRODATA, symtab->vaddr); xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len); xdefine("pclntab", SRODATA, pclntab->vaddr); diff --git a/src/cmd/ld/decodesym.c b/src/cmd/ld/decodesym.c new file mode 100644 index 000000000..ab3f4fbd5 --- /dev/null +++ b/src/cmd/ld/decodesym.c @@ -0,0 +1,215 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "l.h" +#include "lib.h" +#include "../../pkg/runtime/typekind.h" + +// Decoding the type.* symbols. This has to be in sync with +// ../../pkg/runtime/type.go, or more specificaly, with what +// ../gc/reflect.c stuffs in these. + +static Reloc* +decode_reloc(Sym *s, int32 off) +{ + int i; + + for (i = 0; i < s->nr; i++) + if (s->r[i].off == off) + return s->r + i; + return nil; +} + +static Sym* +decode_reloc_sym(Sym *s, int32 off) +{ + Reloc *r; + + r = decode_reloc(s,off); + if (r == nil) + return nil; + return r->sym; +} + +static uvlong +decode_inuxi(uchar* p, int sz) +{ + uint64 v; + uint32 l; + uchar *cast, *inuxi; + int i; + + v = l = 0; + cast = nil; + inuxi = nil; + switch (sz) { + case 2: + cast = (uchar*)&l; + inuxi = inuxi2; + break; + case 4: + cast = (uchar*)&l; + inuxi = inuxi4; + break; + case 8: + cast = (uchar*)&v; + inuxi = inuxi8; + break; + default: + diag("dwarf: decode inuxi %d", sz); + errorexit(); + } + for (i = 0; i < sz; i++) + cast[inuxi[i]] = p[i]; + if (sz == 8) + return v; + return l; +} + +// Type.commonType.kind +uint8 +decodetype_kind(Sym *s) +{ + return s->p[1*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f +} + +// Type.commonType.size +vlong +decodetype_size(Sym *s) +{ + return decode_inuxi(s->p, PtrSize); // 0x8 / 0x10 +} + +// Type.commonType.gc +Sym* +decodetype_gc(Sym *s) +{ + return decode_reloc_sym(s, 1*PtrSize + 8 + 1*PtrSize); +} + +// Type.ArrayType.elem and Type.SliceType.Elem +Sym* +decodetype_arrayelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +vlong +decodetype_arraylen(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+PtrSize, PtrSize); +} + +// Type.PtrType.elem +Sym* +decodetype_ptrelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +// Type.MapType.key, elem +Sym* +decodetype_mapkey(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} +Sym* +decodetype_mapvalue(Sym *s) +{ + return decode_reloc_sym(s, CommonSize+PtrSize); // 0x20 / 0x38 +} + +// Type.ChanType.elem +Sym* +decodetype_chanelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +int +decodetype_funcdotdotdot(Sym *s) +{ + return s->p[CommonSize]; +} + +// Type.FuncType.in.len +int +decodetype_funcincount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+2*PtrSize, IntSize); +} + +int +decodetype_funcoutcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+3*PtrSize + 2*IntSize, IntSize); +} + +Sym* +decodetype_funcintype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, CommonSize + PtrSize); + if (r == nil) + return nil; + return decode_reloc_sym(r->sym, r->add + i * PtrSize); +} + +Sym* +decodetype_funcouttype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, CommonSize + 2*PtrSize + 2*IntSize); + if (r == nil) + return nil; + return decode_reloc_sym(r->sym, r->add + i * PtrSize); +} + +// Type.StructType.fields.Slice::len +int +decodetype_structfieldcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize + PtrSize, IntSize); +} + +enum { + StructFieldSize = 5*PtrSize +}; +// Type.StructType.fields[]-> name, typ and offset. +char* +decodetype_structfieldname(Sym *s, int i) +{ + Reloc *r; + + // go.string."foo" 0x28 / 0x40 + s = decode_reloc_sym(s, CommonSize + PtrSize + 2*IntSize + i*StructFieldSize); + if (s == nil) // embedded structs have a nil name. + return nil; + r = decode_reloc(s, 0); // s has a pointer to the string data at offset 0 + if (r == nil) // shouldn't happen. + return nil; + return (char*) r->sym->p + r->add; // the c-string +} + +Sym* +decodetype_structfieldtype(Sym *s, int i) +{ + return decode_reloc_sym(s, CommonSize + PtrSize + 2*IntSize + i*StructFieldSize + 2*PtrSize); +} + +vlong +decodetype_structfieldoffs(Sym *s, int i) +{ + return decode_inuxi(s->p + CommonSize + PtrSize + 2*IntSize + i*StructFieldSize + 4*PtrSize, IntSize); +} + +// InterfaceTYpe.methods.len +vlong +decodetype_ifacemethodcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize + PtrSize, IntSize); +} diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go index e99e50466..bad4e540f 100644 --- a/src/cmd/ld/doc.go +++ b/src/cmd/ld/doc.go @@ -2,11 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* Ld is the portable code for a modified version of the Plan 9 linker. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2l + http://plan9.bell-labs.com/magic/man2html/1/8l It reads object files (.5, .6, or .8 files) and writes a binary named for the architecture (5.out, 6.out, 8.out) by default (if $GOOS is windows, a .exe suffix @@ -33,7 +35,7 @@ Options new in this version: Write Apple Mach-O binaries (default when $GOOS is darwin) -Hlinux Write Linux ELF binaries (default when $GOOS is linux) - -Hfreebsd (only in 6l/8l) + -Hfreebsd Write FreeBSD ELF binaries (default when $GOOS is freebsd) -Hnetbsd (only in 6l/8l) Write NetBSD ELF binaries (default when $GOOS is netbsd) @@ -56,5 +58,18 @@ Options new in this version: Set the value of an otherwise uninitialized string variable. The symbol name should be of the form importpath.name, as displayed in the symbol table printed by "go tool nm". + -race + Link with race detection libraries. + -B value + Add a NT_GNU_BUILD_ID note when using ELF. The value + should start with 0x and be an even number of hex digits. + -Z + Zero stack on function entry. This is expensive but it might + be useful in cases where you are suffering from false positives + during garbage collection and are willing to trade the CPU time + for getting rid of the false positives. + NOTE: it only eliminates false positives caused by other function + calls, not false positives caused by dead temporaries stored in + the current function call. */ -package documentation +package main diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index 57e5a4283..d6a357e49 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -19,6 +19,7 @@ #include "../ld/elf.h" #include "../ld/macho.h" #include "../ld/pe.h" +#include "../../pkg/runtime/typekind.h" /* * Offsets and sizes of the debug_* sections in the cout file. @@ -740,241 +741,6 @@ newabslocexprattr(DWDie *die, vlong addr) memmove(die->attr->data, block, i); } -// Decoding the type.* symbols. This has to be in sync with -// ../../pkg/runtime/type.go, or more specificaly, with what -// ../gc/reflect.c stuffs in these. - -enum { - KindBool = 1, - KindInt, - KindInt8, - KindInt16, - KindInt32, - KindInt64, - KindUint, - KindUint8, - KindUint16, - KindUint32, - KindUint64, - KindUintptr, - KindFloat32, - KindFloat64, - KindComplex64, - KindComplex128, - KindArray, - KindChan, - KindFunc, - KindInterface, - KindMap, - KindPtr, - KindSlice, - KindString, - KindStruct, - KindUnsafePointer, - - KindNoPointers = 1<<7, - - // size of Type interface header + CommonType structure. - CommonSize = 2*PtrSize+ 5*PtrSize + 8, -}; - -static Reloc* -decode_reloc(Sym *s, int32 off) -{ - int i; - - for (i = 0; i < s->nr; i++) - if (s->r[i].off == off) - return s->r + i; - return nil; -} - -static Sym* -decode_reloc_sym(Sym *s, int32 off) -{ - Reloc *r; - - r = decode_reloc(s,off); - if (r == nil) - return nil; - return r->sym; -} - -static uvlong -decode_inuxi(uchar* p, int sz) -{ - uint64 v; - uint32 l; - uchar *cast, *inuxi; - int i; - - v = l = 0; - cast = nil; - inuxi = nil; - switch (sz) { - case 2: - cast = (uchar*)&l; - inuxi = inuxi2; - break; - case 4: - cast = (uchar*)&l; - inuxi = inuxi4; - break; - case 8: - cast = (uchar*)&v; - inuxi = inuxi8; - break; - default: - diag("dwarf: decode inuxi %d", sz); - errorexit(); - } - for (i = 0; i < sz; i++) - cast[inuxi[i]] = p[i]; - if (sz == 8) - return v; - return l; -} - -// Type.commonType.kind -static uint8 -decodetype_kind(Sym *s) -{ - return s->p[3*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f -} - -// Type.commonType.size -static vlong -decodetype_size(Sym *s) -{ - return decode_inuxi(s->p + 2*PtrSize, PtrSize); // 0x8 / 0x10 -} - -// Type.ArrayType.elem and Type.SliceType.Elem -static Sym* -decodetype_arrayelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -static vlong -decodetype_arraylen(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+PtrSize, PtrSize); -} - -// Type.PtrType.elem -static Sym* -decodetype_ptrelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -// Type.MapType.key, elem -static Sym* -decodetype_mapkey(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} -static Sym* -decodetype_mapvalue(Sym *s) -{ - return decode_reloc_sym(s, CommonSize+PtrSize); // 0x20 / 0x38 -} - -// Type.ChanType.elem -static Sym* -decodetype_chanelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -// Type.FuncType.dotdotdot -static int -decodetype_funcdotdotdot(Sym *s) -{ - return s->p[CommonSize]; -} - -// Type.FuncType.in.len -static int -decodetype_funcincount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+2*PtrSize, 4); -} - -static int -decodetype_funcoutcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+3*PtrSize + 2*4, 4); -} - -static Sym* -decodetype_funcintype(Sym *s, int i) -{ - Reloc *r; - - r = decode_reloc(s, CommonSize + PtrSize); - if (r == nil) - return nil; - return decode_reloc_sym(r->sym, r->add + i * PtrSize); -} - -static Sym* -decodetype_funcouttype(Sym *s, int i) -{ - Reloc *r; - - r = decode_reloc(s, CommonSize + 2*PtrSize + 2*4); - if (r == nil) - return nil; - return decode_reloc_sym(r->sym, r->add + i * PtrSize); -} - -// Type.StructType.fields.Slice::len -static int -decodetype_structfieldcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize + PtrSize, 4); -} - -enum { - StructFieldSize = 5*PtrSize -}; -// Type.StructType.fields[]-> name, typ and offset. -static char* -decodetype_structfieldname(Sym *s, int i) -{ - Reloc *r; - - // go.string."foo" 0x28 / 0x40 - s = decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize); - if (s == nil) // embedded structs have a nil name. - return nil; - r = decode_reloc(s, 0); // s has a pointer to the string data at offset 0 - if (r == nil) // shouldn't happen. - return nil; - return (char*) r->sym->p + r->add; // the c-string -} - -static Sym* -decodetype_structfieldtype(Sym *s, int i) -{ - return decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize + 2*PtrSize); -} - -static vlong -decodetype_structfieldoffs(Sym *s, int i) -{ - return decode_inuxi(s->p + CommonSize + PtrSize + 2*4 + i*StructFieldSize + 4*PtrSize, 4); -} - -// InterfaceTYpe.methods.len -static vlong -decodetype_ifacemethodcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize + PtrSize, 4); -} - // Fake attributes for slices, maps and channel enum { @@ -1531,6 +1297,10 @@ decodez(char *s) return 0; r = malloc(len + 1); + if(r == nil) { + diag("out of memory"); + errorexit(); + } rb = r; re = rb + len + 1; @@ -1709,6 +1479,10 @@ inithist(Auto *a) continue; if (linehist == 0 || linehist->absline != absline) { Linehist* lh = malloc(sizeof *lh); + if(lh == nil) { + diag("out of memory"); + errorexit(); + } lh->link = linehist; lh->absline = absline; linehist = lh; @@ -2162,7 +1936,8 @@ writeinfo(void) * because we need die->offs and infoo/infosize; */ static int -ispubname(DWDie *die) { +ispubname(DWDie *die) +{ DWAttr *a; switch(die->abbrev) { @@ -2175,7 +1950,8 @@ ispubname(DWDie *die) { } static int -ispubtype(DWDie *die) { +ispubtype(DWDie *die) +{ return die->abbrev >= DW_ABRV_NULLTYPE; } @@ -2316,7 +2092,7 @@ dwarfemitdebugsections(void) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); // Needed by the prettyprinter code for interface inspection. - defgotype(lookup_or_diag("type.runtime.commonType")); + defgotype(lookup_or_diag("type.runtime.rtype")); defgotype(lookup_or_diag("type.runtime.interfaceType")); defgotype(lookup_or_diag("type.runtime.itab")); diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index de9e6b854..630906653 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -11,7 +11,7 @@ * in order to write the code just once. The 64-bit data structure is * written in the 32-bit format on the 32-bit machines. */ -#define NSECT 32 +#define NSECT 48 int iself; @@ -31,6 +31,8 @@ struct Elfstring static Elfstring elfstr[100]; static int nelfstr; +static char buildinfo[32]; + /* Initialize the global variable that describes the ELF header. It will be updated as we write section and prog headers. @@ -52,6 +54,11 @@ elfinit(void) break; // 32-bit architectures + case '5': + // we only use EABI on linux/arm + if(HEADTYPE == Hlinux) + hdr.flags = 0x5000002; // has entry point, Version5 EABI + // fallthrough default: hdr.phoff = ELF32HDRSIZE; /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */ hdr.shoff = ELF32HDRSIZE; /* Will move as we add PHeaders */ @@ -189,13 +196,6 @@ newElfPhdr(void) return e; } -ElfShdr* -newElfShstrtab(vlong name) -{ - hdr.shstrndx = hdr.shnum; - return newElfShdr(name); -} - ElfShdr* newElfShdr(vlong name) { @@ -203,6 +203,7 @@ newElfShdr(vlong name) e = mal(sizeof *e); e->name = name; + e->shnum = hdr.shnum; if (hdr.shnum >= NSECT) { diag("too many shdrs"); } else { @@ -332,26 +333,53 @@ elfinterp(ElfShdr *sh, uint64 startva, uint64 resoff, char *p) } int -elfwriteinterp(vlong stridx) +elfwriteinterp(void) { - ElfShdr *sh = nil; - int i; - - for(i = 0; i < hdr.shnum; i++) - if(shdr[i]->name == stridx) - sh = shdr[i]; - if(sh == nil || interp == nil) - return 0; - + ElfShdr *sh; + + sh = elfshname(".interp"); cseek(sh->off); cwrite(interp, sh->size); return sh->size; } -// Defined in NetBSD's sys/exec_elf.h -#define ELF_NOTE_TYPE_NETBSD_TAG 1 +int +elfnote(ElfShdr *sh, uint64 startva, uint64 resoff, int sz) +{ + uint64 n; + + n = sizeof(Elf_Note) + sz + resoff % 4; + + sh->type = SHT_NOTE; + sh->flags = SHF_ALLOC; + sh->addralign = 4; + sh->addr = startva + resoff - n; + sh->off = resoff - n; + sh->size = n; + + return n; +} + +ElfShdr * +elfwritenotehdr(char *str, uint32 namesz, uint32 descsz, uint32 tag) +{ + ElfShdr *sh; + + sh = elfshname(str); + + // Write Elf_Note header. + cseek(sh->off); + LPUT(namesz); + LPUT(descsz); + LPUT(tag); + + return sh; +} + +// NetBSD Signature (as per sys/exec_elf.h) #define ELF_NOTE_NETBSD_NAMESZ 7 #define ELF_NOTE_NETBSD_DESCSZ 4 +#define ELF_NOTE_NETBSD_TAG 1 #define ELF_NOTE_NETBSD_NAME "NetBSD\0\0" #define ELF_NOTE_NETBSD_VERSION 599000000 /* NetBSD 5.99 */ @@ -360,37 +388,131 @@ elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) { int n; - n = sizeof(Elf_Note) + ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1; - n += resoff % 4; - sh->addr = startva + resoff - n; - sh->off = resoff - n; - sh->size = n; - - return n; + n = ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1; + return elfnote(sh, startva, resoff, n); } int -elfwritenetbsdsig(vlong stridx) { - ElfShdr *sh = nil; - int i; +elfwritenetbsdsig(void) +{ + ElfShdr *sh; - for(i = 0; i < hdr.shnum; i++) - if(shdr[i]->name == stridx) - sh = shdr[i]; + // Write Elf_Note header. + sh = elfwritenotehdr(".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG); if(sh == nil) return 0; - // Write Elf_Note header followed by NetBSD string. - cseek(sh->off); - LPUT(ELF_NOTE_NETBSD_NAMESZ); - LPUT(ELF_NOTE_NETBSD_DESCSZ); - LPUT(ELF_NOTE_TYPE_NETBSD_TAG); - cwrite(ELF_NOTE_NETBSD_NAME, 8); + // Followed by NetBSD string and version. + cwrite(ELF_NOTE_NETBSD_NAME, ELF_NOTE_NETBSD_NAMESZ + 1); LPUT(ELF_NOTE_NETBSD_VERSION); return sh->size; } +// OpenBSD Signature +#define ELF_NOTE_OPENBSD_NAMESZ 8 +#define ELF_NOTE_OPENBSD_DESCSZ 4 +#define ELF_NOTE_OPENBSD_TAG 1 +#define ELF_NOTE_OPENBSD_NAME "OpenBSD\0" +#define ELF_NOTE_OPENBSD_VERSION 0 + +int +elfopenbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) +{ + int n; + + n = ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ; + return elfnote(sh, startva, resoff, n); +} + +int +elfwriteopenbsdsig(void) +{ + ElfShdr *sh; + + // Write Elf_Note header. + sh = elfwritenotehdr(".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG); + if(sh == nil) + return 0; + + // Followed by OpenBSD string and version. + cwrite(ELF_NOTE_OPENBSD_NAME, ELF_NOTE_OPENBSD_NAMESZ); + LPUT(ELF_NOTE_OPENBSD_VERSION); + + return sh->size; +} + +void +addbuildinfo(char *val) +{ + char *ov; + int i, b, j; + + if(val[0] != '0' || val[1] != 'x') { + fprint(2, "%s: -B argument must start with 0x: %s\n", argv0, val); + exits("usage"); + } + ov = val; + val += 2; + i = 0; + while(*val != '\0') { + if(val[1] == '\0') { + fprint(2, "%s: -B argument must have even number of digits: %s\n", argv0, ov); + exits("usage"); + } + b = 0; + for(j = 0; j < 2; j++, val++) { + b *= 16; + if(*val >= '0' && *val <= '9') + b += *val - '0'; + else if(*val >= 'a' && *val <= 'f') + b += *val - 'a' + 10; + else if(*val >= 'A' && *val <= 'F') + b += *val - 'A' + 10; + else { + fprint(2, "%s: -B argument contains invalid hex digit %c: %s\n", argv0, *val, ov); + exits("usage"); + } + } + if(i >= nelem(buildinfo)) { + fprint(2, "%s: -B option too long (max %d digits): %s\n", argv0, (int)nelem(buildinfo), ov); + exits("usage"); + } + buildinfo[i++] = b; + } + buildinfolen = i; +} + +// Build info note +#define ELF_NOTE_BUILDINFO_NAMESZ 4 +#define ELF_NOTE_BUILDINFO_TAG 3 +#define ELF_NOTE_BUILDINFO_NAME "GNU\0" + +int +elfbuildinfo(ElfShdr *sh, uint64 startva, uint64 resoff) +{ + int n; + + n = ELF_NOTE_BUILDINFO_NAMESZ + rnd(buildinfolen, 4); + return elfnote(sh, startva, resoff, n); +} + +int +elfwritebuildinfo(void) +{ + ElfShdr *sh; + + sh = elfwritenotehdr(".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, buildinfolen, ELF_NOTE_BUILDINFO_TAG); + if(sh == nil) + return 0; + + cwrite(ELF_NOTE_BUILDINFO_NAME, ELF_NOTE_BUILDINFO_NAMESZ); + cwrite(buildinfo, buildinfolen); + cwrite("\0\0\0", rnd(buildinfolen, 4) - buildinfolen); + + return sh->size; +} + extern int nelfsym; int elfverneed; @@ -584,18 +706,18 @@ elfphload(Segment *seg) } ElfShdr* -elfshbits(Section *sect) +elfshname(char *name) { int i, off; ElfShdr *sh; for(i=0; iname, elfstr[i].s) == 0) { + if(strcmp(name, elfstr[i].s) == 0) { off = elfstr[i].off; goto found; } } - diag("cannot find elf name %s", sect->name); + diag("cannot find elf name %s", name); errorexit(); return nil; @@ -605,8 +727,30 @@ found: if(sh->name == off) return sh; } - + sh = newElfShdr(off); + return sh; +} + +ElfShdr* +elfshalloc(Section *sect) +{ + ElfShdr *sh; + + sh = elfshname(sect->name); + sect->elfsect = sh; + return sh; +} + +ElfShdr* +elfshbits(Section *sect) +{ + ElfShdr *sh; + + sh = elfshalloc(sect); + if(sh->type > 0) + return sh; + if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen) sh->type = SHT_PROGBITS; else @@ -616,10 +760,732 @@ found: sh->flags |= SHF_EXECINSTR; if(sect->rwx & 2) sh->flags |= SHF_WRITE; - sh->addr = sect->vaddr; + if(!isobj) + sh->addr = sect->vaddr; sh->addralign = PtrSize; sh->size = sect->len; sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; + + return sh; +} + +ElfShdr* +elfshreloc(Section *sect) +{ + int typ; + ElfShdr *sh; + char *prefix; + char buf[100]; + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) + return nil; + if(strcmp(sect->name, ".shstrtab") == 0) + return nil; + + if(thechar == '6') { + prefix = ".rela"; + typ = SHT_RELA; + } else { + prefix = ".rel"; + typ = SHT_REL; + } + + snprint(buf, sizeof buf, "%s%s", prefix, sect->name); + sh = elfshname(buf); + sh->type = typ; + sh->entsize = PtrSize*(2+(typ==SHT_RELA)); + sh->link = elfshname(".symtab")->shnum; + sh->info = sect->elfsect->shnum; + sh->off = sect->reloff; + sh->size = sect->rellen; + sh->addralign = PtrSize; return sh; } + +void +elfrelocsect(Section *sect, Sym *first) +{ + Sym *sym, *rs; + int32 eaddr; + Reloc *r; + int64 add; + + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) + return; + if(strcmp(sect->name, ".shstrtab") == 0) + return; + + sect->reloff = cpos(); + for(sym = first; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= sect->vaddr) + break; + } + + eaddr = sect->vaddr + sect->len; + for(; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= eaddr) + break; + cursym = sym; + + for(r = sym->r; r < sym->r+sym->nr; r++) { + // Ignore relocations handled by reloc already. + switch(r->type) { + case D_SIZE: + continue; + case D_ADDR: + case D_PCREL: + if(r->sym->type == SCONST) + continue; + break; + } + + add = r->add; + rs = r->sym; + while(rs->outer != nil) { + add += rs->value - rs->outer->value; + rs = rs->outer; + } + + if(rs->elfsym == 0) + diag("reloc %d to non-elf symbol %s (rs=%s) %d", r->type, r->sym->name, rs->name, rs->type); + + if(elfreloc1(r, sym->value - sect->vaddr + r->off, rs->elfsym, add) < 0) + diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); + } + } + + sect->rellen = cpos() - sect->reloff; +} + +void +elfemitreloc(void) +{ + Section *sect; + + while(cpos()&7) + cput(0); + + elfrelocsect(segtext.sect, textp); + for(sect=segtext.sect->next; sect!=nil; sect=sect->next) + elfrelocsect(sect, datap); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfrelocsect(sect, datap); +} + +void +doelf(void) +{ + Sym *s, *shstrtab, *dynstr; + + if(!iself) + return; + + /* predefine strings we need for section headers */ + shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFROSECT; + shstrtab->reachable = 1; + + addstring(shstrtab, ""); + addstring(shstrtab, ".text"); + addstring(shstrtab, ".noptrdata"); + addstring(shstrtab, ".data"); + addstring(shstrtab, ".bss"); + addstring(shstrtab, ".noptrbss"); + if(HEADTYPE == Hnetbsd) + addstring(shstrtab, ".note.netbsd.ident"); + if(HEADTYPE == Hopenbsd) + addstring(shstrtab, ".note.openbsd.ident"); + if(buildinfolen > 0) + addstring(shstrtab, ".note.gnu.build-id"); + addstring(shstrtab, ".elfdata"); + addstring(shstrtab, ".rodata"); + addstring(shstrtab, ".typelink"); + if(flag_shared) + addstring(shstrtab, ".data.rel.ro"); + addstring(shstrtab, ".gcdata"); + addstring(shstrtab, ".gcbss"); + addstring(shstrtab, ".gosymtab"); + addstring(shstrtab, ".gopclntab"); + + if(isobj) { + debug['s'] = 0; + debug['d'] = 1; + + if(thechar == '6') { + addstring(shstrtab, ".rela.text"); + addstring(shstrtab, ".rela.rodata"); + addstring(shstrtab, ".rela.typelink"); + addstring(shstrtab, ".rela.gcdata"); + addstring(shstrtab, ".rela.gcbss"); + addstring(shstrtab, ".rela.gosymtab"); + addstring(shstrtab, ".rela.gopclntab"); + addstring(shstrtab, ".rela.noptrdata"); + addstring(shstrtab, ".rela.data"); + } else { + addstring(shstrtab, ".rel.text"); + addstring(shstrtab, ".rel.rodata"); + addstring(shstrtab, ".rel.typelink"); + addstring(shstrtab, ".rel.gcdata"); + addstring(shstrtab, ".rel.gcbss"); + addstring(shstrtab, ".rel.gosymtab"); + addstring(shstrtab, ".rel.gopclntab"); + addstring(shstrtab, ".rel.noptrdata"); + addstring(shstrtab, ".rel.data"); + } + } + + if(!debug['s']) { + addstring(shstrtab, ".symtab"); + addstring(shstrtab, ".strtab"); + dwarfaddshstrings(shstrtab); + } + addstring(shstrtab, ".shstrtab"); + + if(!debug['d']) { /* -d suppresses dynamic loader format */ + addstring(shstrtab, ".interp"); + addstring(shstrtab, ".hash"); + addstring(shstrtab, ".got"); + addstring(shstrtab, ".got.plt"); + addstring(shstrtab, ".dynamic"); + addstring(shstrtab, ".dynsym"); + addstring(shstrtab, ".dynstr"); + if(thechar == '6') { + addstring(shstrtab, ".rela"); + addstring(shstrtab, ".rela.plt"); + } else { + addstring(shstrtab, ".rel"); + addstring(shstrtab, ".rel.plt"); + } + addstring(shstrtab, ".plt"); + addstring(shstrtab, ".gnu.version"); + addstring(shstrtab, ".gnu.version_r"); + + /* dynamic symbol table - first entry all zeros */ + s = lookup(".dynsym", 0); + s->type = SELFROSECT; + s->reachable = 1; + if(thechar == '6') + s->size += ELF64SYMSIZE; + else + s->size += ELF32SYMSIZE; + + /* dynamic string table */ + s = lookup(".dynstr", 0); + s->type = SELFROSECT; + s->reachable = 1; + if(s->size == 0) + addstring(s, ""); + dynstr = s; + + /* relocation table */ + if(thechar == '6') + s = lookup(".rela", 0); + else + s = lookup(".rel", 0); + s->reachable = 1; + s->type = SELFROSECT; + + /* global offset table */ + s = lookup(".got", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".got.plt", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + s = lookup(".plt", 0); + s->reachable = 1; + s->type = SELFROSECT; + + elfsetupplt(); + + if(thechar == '6') + s = lookup(".rela.plt", 0); + else + s = lookup(".rel.plt", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".gnu.version", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".gnu.version_r", 0); + s->reachable = 1; + s->type = SELFROSECT; + + /* define dynamic elf table */ + s = lookup(".dynamic", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + /* + * .dynamic table + */ + elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); + elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); + if(thechar == '6') + elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); + else + elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); + elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); + elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); + if(thechar == '6') { + elfwritedynentsym(s, DT_RELA, lookup(".rela", 0)); + elfwritedynentsymsize(s, DT_RELASZ, lookup(".rela", 0)); + elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); + } else { + elfwritedynentsym(s, DT_REL, lookup(".rel", 0)); + elfwritedynentsymsize(s, DT_RELSZ, lookup(".rel", 0)); + elfwritedynent(s, DT_RELENT, ELF32RELSIZE); + } + if(rpath) + elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + + if(thechar == '6') { + elfwritedynent(s, DT_PLTREL, DT_RELA); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0)); + } else { + elfwritedynent(s, DT_PLTREL, DT_REL); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); + } + + elfwritedynent(s, DT_DEBUG, 0); + + if(flag_shared) { + Sym *init_sym = lookup(LIBINITENTRY, 0); + if(init_sym->type != STEXT) + diag("entry not text: %s", init_sym->name); + elfwritedynentsym(s, DT_INIT, init_sym); + } + + // Do not write DT_NULL. elfdynhash will finish it. + } +} + +void +shsym(ElfShdr *sh, Sym *s) +{ + vlong addr; + addr = symaddr(s); + if(sh->flags&SHF_ALLOC) + sh->addr = addr; + sh->off = datoff(addr); + sh->size = s->size; +} + +void +phsh(ElfPhdr *ph, ElfShdr *sh) +{ + ph->vaddr = sh->addr; + ph->paddr = ph->vaddr; + ph->off = sh->off; + ph->filesz = sh->size; + ph->memsz = sh->size; + ph->align = sh->addralign; +} + +void +asmbelfsetup(void) +{ + Section *sect; + + /* This null SHdr must appear before all others */ + elfshname(""); + + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshalloc(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshalloc(sect); +} + +void +asmbelf(vlong symo) +{ + int a, o; + vlong startva, resoff; + ElfEhdr *eh; + ElfPhdr *ph, *pph, *pnote; + ElfShdr *sh; + Section *sect; + + eh = getElfEhdr(); + switch(thechar) { + default: + diag("unknown architecture in asmbelf"); + errorexit(); + case '5': + eh->machine = EM_ARM; + break; + case '6': + eh->machine = EM_X86_64; + break; + case '8': + eh->machine = EM_386; + break; + } + + startva = INITTEXT - HEADR; + resoff = ELFRESERVE; + + pph = nil; + if(isobj) { + /* skip program headers */ + eh->phoff = 0; + eh->phentsize = 0; + goto elfobj; + } + + /* program header info */ + pph = newElfPhdr(); + pph->type = PT_PHDR; + pph->flags = PF_R + PF_X; + pph->off = eh->ehsize; + pph->vaddr = INITTEXT - HEADR + pph->off; + pph->paddr = INITTEXT - HEADR + pph->off; + pph->align = INITRND; + + /* + * PHDR must be in a loaded segment. Adjust the text + * segment boundaries downwards to include it. + */ + o = segtext.vaddr - pph->vaddr; + segtext.vaddr -= o; + segtext.len += o; + o = segtext.fileoff - pph->off; + segtext.fileoff -= o; + segtext.filelen += o; + + if(!debug['d']) { + /* interpreter */ + sh = elfshname(".interp"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC; + sh->addralign = 1; + if(interpreter == nil) { + switch(HEADTYPE) { + case Hlinux: + interpreter = linuxdynld; + break; + case Hfreebsd: + interpreter = freebsddynld; + break; + case Hnetbsd: + interpreter = netbsddynld; + break; + case Hopenbsd: + interpreter = openbsddynld; + break; + } + } + resoff -= elfinterp(sh, startva, resoff, interpreter); + + ph = newElfPhdr(); + ph->type = PT_INTERP; + ph->flags = PF_R; + phsh(ph, sh); + } + + pnote = nil; + if(HEADTYPE == Hnetbsd || HEADTYPE == Hopenbsd) { + sh = nil; + switch(HEADTYPE) { + case Hnetbsd: + sh = elfshname(".note.netbsd.ident"); + resoff -= elfnetbsdsig(sh, startva, resoff); + break; + case Hopenbsd: + sh = elfshname(".note.openbsd.ident"); + resoff -= elfopenbsdsig(sh, startva, resoff); + break; + } + + pnote = newElfPhdr(); + pnote->type = PT_NOTE; + pnote->flags = PF_R; + phsh(pnote, sh); + } + + if(buildinfolen > 0) { + sh = elfshname(".note.gnu.build-id"); + resoff -= elfbuildinfo(sh, startva, resoff); + + if(pnote == nil) { + pnote = newElfPhdr(); + pnote->type = PT_NOTE; + pnote->flags = PF_R; + } + phsh(pnote, sh); + } + + // Additions to the reserved area must be above this line. + USED(resoff); + + elfphload(&segtext); + elfphload(&segdata); + + /* Dynamic linking sections */ + if(!debug['d']) { /* -d suppresses dynamic loader format */ + sh = elfshname(".dynsym"); + sh->type = SHT_DYNSYM; + sh->flags = SHF_ALLOC; + if(PtrSize == 8) + sh->entsize = ELF64SYMSIZE; + else + sh->entsize = ELF32SYMSIZE; + sh->addralign = PtrSize; + sh->link = elfshname(".dynstr")->shnum; + // sh->info = index of first non-local symbol (number of local symbols) + shsym(sh, lookup(".dynsym", 0)); + + sh = elfshname(".dynstr"); + sh->type = SHT_STRTAB; + sh->flags = SHF_ALLOC; + sh->addralign = 1; + shsym(sh, lookup(".dynstr", 0)); + + if(elfverneed) { + sh = elfshname(".gnu.version"); + sh->type = SHT_GNU_VERSYM; + sh->flags = SHF_ALLOC; + sh->addralign = 2; + sh->link = elfshname(".dynsym")->shnum; + sh->entsize = 2; + shsym(sh, lookup(".gnu.version", 0)); + + sh = elfshname(".gnu.version_r"); + sh->type = SHT_GNU_VERNEED; + sh->flags = SHF_ALLOC; + sh->addralign = PtrSize; + sh->info = elfverneed; + sh->link = elfshname(".dynstr")->shnum; + shsym(sh, lookup(".gnu.version_r", 0)); + } + + switch(eh->machine) { + case EM_X86_64: + sh = elfshname(".rela.plt"); + sh->type = SHT_RELA; + sh->flags = SHF_ALLOC; + sh->entsize = ELF64RELASIZE; + sh->addralign = PtrSize; + sh->link = elfshname(".dynsym")->shnum; + sh->info = elfshname(".plt")->shnum; + shsym(sh, lookup(".rela.plt", 0)); + + sh = elfshname(".rela"); + sh->type = SHT_RELA; + sh->flags = SHF_ALLOC; + sh->entsize = ELF64RELASIZE; + sh->addralign = 8; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rela", 0)); + break; + + default: + sh = elfshname(".rel.plt"); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rel.plt", 0)); + + sh = elfshname(".rel"); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->addralign = 4; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rel", 0)); + break; + } + + sh = elfshname(".plt"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + if(eh->machine == EM_X86_64) + sh->entsize = 16; + else + sh->entsize = 4; + sh->addralign = 4; + shsym(sh, lookup(".plt", 0)); + + sh = elfshname(".got"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = PtrSize; + sh->addralign = PtrSize; + shsym(sh, lookup(".got", 0)); + + sh = elfshname(".got.plt"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = PtrSize; + sh->addralign = PtrSize; + shsym(sh, lookup(".got.plt", 0)); + + sh = elfshname(".hash"); + sh->type = SHT_HASH; + sh->flags = SHF_ALLOC; + sh->entsize = 4; + sh->addralign = PtrSize; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".hash", 0)); + + /* sh and PT_DYNAMIC for .dynamic section */ + sh = elfshname(".dynamic"); + sh->type = SHT_DYNAMIC; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = 2*PtrSize; + sh->addralign = PtrSize; + sh->link = elfshname(".dynstr")->shnum; + shsym(sh, lookup(".dynamic", 0)); + ph = newElfPhdr(); + ph->type = PT_DYNAMIC; + ph->flags = PF_R + PF_W; + phsh(ph, sh); + + /* + * Thread-local storage segment (really just size). + */ + // Do not emit PT_TLS for OpenBSD since ld.so(1) does + // not currently support it. This is handled + // appropriately in runtime/cgo. + if(tlsoffset != 0 && HEADTYPE != Hopenbsd) { + ph = newElfPhdr(); + ph->type = PT_TLS; + ph->flags = PF_R; + ph->memsz = -tlsoffset; + ph->align = PtrSize; + } + } + + if(HEADTYPE == Hlinux) { + ph = newElfPhdr(); + ph->type = PT_GNU_STACK; + ph->flags = PF_W+PF_R; + ph->align = PtrSize; + + ph = newElfPhdr(); + ph->type = PT_PAX_FLAGS; + ph->flags = 0x2a00; // mprotect, randexec, emutramp disabled + ph->align = PtrSize; + } + +elfobj: + sh = elfshname(".shstrtab"); + sh->type = SHT_STRTAB; + sh->addralign = 1; + shsym(sh, lookup(".shstrtab", 0)); + eh->shstrndx = sh->shnum; + + // put these sections early in the list + if(!debug['s']) { + elfshname(".symtab"); + elfshname(".strtab"); + } + + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + + if(isobj) { + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshreloc(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshreloc(sect); + } + + if(!debug['s']) { + sh = elfshname(".symtab"); + sh->type = SHT_SYMTAB; + sh->off = symo; + sh->size = symsize; + sh->addralign = PtrSize; + sh->entsize = 8+2*PtrSize; + sh->link = elfshname(".strtab")->shnum; + sh->info = elfglobalsymndx; + + sh = elfshname(".strtab"); + sh->type = SHT_STRTAB; + sh->off = symo+symsize; + sh->size = elfstrsize; + sh->addralign = 1; + + // TODO(rsc): Enable for isobj too, once we know it works. + if(!isobj) + dwarfaddelfheaders(); + } + + /* Main header */ + eh->ident[EI_MAG0] = '\177'; + eh->ident[EI_MAG1] = 'E'; + eh->ident[EI_MAG2] = 'L'; + eh->ident[EI_MAG3] = 'F'; + if(HEADTYPE == Hfreebsd) + eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; + else if(HEADTYPE == Hnetbsd) + eh->ident[EI_OSABI] = ELFOSABI_NETBSD; + else if(HEADTYPE == Hopenbsd) + eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; + if(PtrSize == 8) + eh->ident[EI_CLASS] = ELFCLASS64; + else + eh->ident[EI_CLASS] = ELFCLASS32; + eh->ident[EI_DATA] = ELFDATA2LSB; + eh->ident[EI_VERSION] = EV_CURRENT; + + if(flag_shared) + eh->type = ET_DYN; + else if(isobj) + eh->type = ET_REL; + else + eh->type = ET_EXEC; + + if(!isobj) + eh->entry = entryvalue(); + + eh->version = EV_CURRENT; + + if(pph != nil) { + pph->filesz = eh->phnum * eh->phentsize; + pph->memsz = pph->filesz; + } + + cseek(0); + a = 0; + a += elfwritehdr(); + a += elfwritephdrs(); + a += elfwriteshdrs(); + if(!debug['d']) + a += elfwriteinterp(); + if(!isobj) { + if(HEADTYPE == Hnetbsd) + a += elfwritenetbsdsig(); + if(HEADTYPE == Hopenbsd) + a += elfwriteopenbsdsig(); + if(buildinfolen > 0) + a += elfwritebuildinfo(); + } + if(a > ELFRESERVE) + diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); +} diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 690ade975..3e22125b2 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -251,6 +251,7 @@ typedef struct { #define PT_LOPROC 0x70000000 /* First processor-specific type. */ #define PT_HIPROC 0x7fffffff /* Last processor-specific type. */ #define PT_GNU_STACK 0x6474e551 +#define PT_PAX_FLAGS 0x65041580 /* Values for p_flags. */ #define PF_X 0x1 /* Executable. */ @@ -562,6 +563,10 @@ typedef struct { #define R_ARM_GOTPC 25 /* Add PC-relative GOT table address. */ #define R_ARM_GOT32 26 /* Add PC-relative GOT offset. */ #define R_ARM_PLT32 27 /* Add PC-relative PLT offset. */ +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_V4BX 40 +#define R_ARM_GOT_PREL 96 #define R_ARM_GNU_VTENTRY 100 #define R_ARM_GNU_VTINHERIT 101 #define R_ARM_RSBREL32 250 @@ -571,7 +576,7 @@ typedef struct { #define R_ARM_RPC24 254 #define R_ARM_RBASE 255 -#define R_ARM_COUNT 33 /* Count of defined relocation types. */ +#define R_ARM_COUNT 37 /* Count of defined relocation types. */ #define R_386_NONE 0 /* No relocation. */ @@ -836,7 +841,8 @@ typedef struct { * Section header. */ -typedef struct { +typedef struct Elf64_Shdr Elf64_Shdr; +struct Elf64_Shdr { Elf64_Word name; /* Section name (index into the section header string table). */ Elf64_Word type; /* Section type. */ @@ -848,7 +854,9 @@ typedef struct { Elf64_Word info; /* Depends on section type. */ Elf64_Xword addralign; /* Alignment in bytes. */ Elf64_Xword entsize; /* Size of each entry in section. */ -} Elf64_Shdr; + + int shnum; /* section number, not stored on disk */ +}; /* * Program header. @@ -952,7 +960,6 @@ typedef Elf64_Phdr ElfPhdr; void elfinit(void); ElfEhdr *getElfEhdr(void); -ElfShdr *newElfShstrtab(vlong); ElfShdr *newElfShdr(vlong); ElfPhdr *newElfPhdr(void); uint32 elfwritehdr(void); @@ -969,18 +976,40 @@ extern int numelfshdr; extern int iself; extern int elfverneed; int elfinterp(ElfShdr*, uint64, uint64, char*); -int elfwriteinterp(vlong); +int elfwriteinterp(void); int elfnetbsdsig(ElfShdr*, uint64, uint64); -int elfwritenetbsdsig(vlong); +int elfwritenetbsdsig(void); +int elfopenbsdsig(ElfShdr*, uint64, uint64); +int elfwriteopenbsdsig(void); +void addbuildinfo(char*); +int elfbuildinfo(ElfShdr*, uint64, uint64); +int elfwritebuildinfo(void); void elfdynhash(void); ElfPhdr* elfphload(Segment*); ElfShdr* elfshbits(Section*); +ElfShdr* elfshalloc(Section*); +ElfShdr* elfshname(char*); +ElfShdr* elfshreloc(Section*); void elfsetstring(char*, int); void elfaddverneed(Sym*); +void elfemitreloc(void); +void shsym(ElfShdr*, Sym*); +void phsh(ElfPhdr*, ElfShdr*); +void doelf(void); +void elfsetupplt(void); +void dwarfaddshstrings(Sym*); +void dwarfaddelfheaders(void); +void asmbelf(vlong symo); +void asmbelfsetup(void); +extern char linuxdynld[]; +extern char freebsddynld[]; +extern char netbsddynld[]; +extern char openbsddynld[]; +int elfreloc1(Reloc*, vlong off, int32 elfsym, vlong add); EXTERN int elfstrsize; EXTERN char* elfstrdat; -EXTERN int elftextsh; +EXTERN int buildinfolen; /* * Total amount of space to reserve at the start of the file diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 3271be1f5..b2527b13e 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -67,8 +67,7 @@ ilookup(char *name) } static void loadpkgdata(char*, char*, char*, int); -static void loaddynimport(char*, char*, char*, int); -static void loaddynexport(char*, char*, char*, int); +static void loadcgo(char*, char*, char*, int); static int parsemethod(char**, char*, char**); static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); @@ -177,12 +176,12 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) loadpkgdata(filename, pkg, p0, p1 - p0); - // look for dynimport section - p0 = strstr(p1, "\n$$ // dynimport"); + // look for cgo section + p0 = strstr(p1, "\n$$ // cgo"); if(p0 != nil) { p0 = strchr(p0+1, '\n'); if(p0 == nil) { - fprint(2, "%s: found $$ // dynimport but no newline in %s\n", argv0, filename); + fprint(2, "%s: found $$ // cgo but no newline in %s\n", argv0, filename); if(debug['u']) errorexit(); return; @@ -191,34 +190,12 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) if(p1 == nil) p1 = strstr(p0, "\n!\n"); if(p1 == nil) { - fprint(2, "%s: cannot find end of // dynimport section in %s\n", argv0, filename); + fprint(2, "%s: cannot find end of // cgo section in %s\n", argv0, filename); if(debug['u']) errorexit(); return; } - loaddynimport(filename, pkg, p0 + 1, p1 - (p0+1)); - } - - // look for dynexp section - p0 = strstr(p1, "\n$$ // dynexport"); - if(p0 != nil) { - p0 = strchr(p0+1, '\n'); - if(p0 == nil) { - fprint(2, "%s: found $$ // dynexporg but no newline in %s\n", argv0, filename); - if(debug['u']) - errorexit(); - return; - } - p1 = strstr(p0, "\n$$"); - if(p1 == nil) - p1 = strstr(p0, "\n!\n"); - if(p1 == nil) { - fprint(2, "%s: cannot find end of // dynexporg section in %s\n", argv0, filename); - if(debug['u']) - errorexit(); - return; - } - loaddynexport(filename, pkg, p0 + 1, p1 - (p0+1)); + loadcgo(filename, pkg, p0 + 1, p1 - (p0+1)); } } @@ -409,10 +386,16 @@ parsemethod(char **pp, char *ep, char **methp) if(p == ep) return 0; + // might be a comment about the method + if(p + 2 < ep && strncmp(p, "//", 2) == 0) + goto useline; + // if it says "func (", it's a method - if(p + 6 >= ep || strncmp(p, "func (", 6) != 0) - return 0; + if(p + 6 < ep && strncmp(p, "func (", 6) == 0) + goto useline; + return 0; +useline: // definition to end of line *methp = p; while(p < ep && *p != '\n') @@ -428,173 +411,182 @@ parsemethod(char **pp, char *ep, char **methp) } static void -loaddynimport(char *file, char *pkg, char *p, int n) +loadcgo(char *file, char *pkg, char *p, int n) { - char *pend, *next, *name, *def, *p0, *lib, *q; + char *pend, *next, *p0, *q; + char *f[10], *local, *remote, *lib; + int nf; Sym *s; USED(file); pend = p + n; + p0 = nil; for(; p 4) + goto err; + + local = f[1]; + remote = local; + if(nf > 2) + remote = f[2]; + lib = ""; + if(nf > 3) + lib = f[3]; + + if(debug['d']) { + fprint(2, "%s: %s: cannot use dynamic imports with -d flag\n", argv0, file); + nerrors++; + return; + } - if(strcmp(name, "_") == 0 && strcmp(def, "_") == 0) { - // allow #pragma dynimport _ _ "foo.so" - // to force a link of foo.so. - havedynamic = 1; - adddynlib(lib); + if(strcmp(local, "_") == 0 && strcmp(remote, "_") == 0) { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + havedynamic = 1; + adddynlib(lib); + continue; + } + + local = expandpkg(local, pkg); + q = strchr(remote, '#'); + if(q) + *q++ = '\0'; + s = lookup(local, 0); + if(local != f[1]) + free(local); + if(s->type == 0 || s->type == SXREF) { + s->dynimplib = lib; + s->dynimpname = remote; + s->dynimpvers = q; + s->type = SDYNIMPORT; + havedynamic = 1; + } continue; } - - name = expandpkg(name, pkg); - q = strchr(def, '@'); - if(q) - *q++ = '\0'; - s = lookup(name, 0); - free(name); - if(s->type == 0 || s->type == SXREF) { - s->dynimplib = lib; - s->dynimpname = def; - s->dynimpvers = q; - s->type = SDYNIMPORT; - havedynamic = 1; + + if(strcmp(f[0], "cgo_import_static") == 0) { + if(nf != 2) + goto err; + if(isobj) { + local = f[1]; + s = lookup(local, 0); + s->type = SHOSTOBJ; + s->size = 0; + } + continue; } - } - return; -err: - fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0); - nerrors++; -} - -static void -loaddynexport(char *file, char *pkg, char *p, int n) -{ - char *pend, *next, *local, *elocal, *remote, *p0; - Sym *s; - - USED(file); - pend = p + n; - for(; p 3) + goto err; + local = f[1]; + if(nf > 2) + remote = f[2]; + else + remote = local; + local = expandpkg(local, pkg); + s = lookup(local, 0); + if(s->dynimplib != nil) { + fprint(2, "%s: symbol is both imported and exported: %s\n", argv0, local); + nerrors++; + } + s->dynimpname = remote; + s->dynexport = 1; - elocal = expandpkg(local, pkg); + if(ndynexp%32 == 0) + dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); + dynexp[ndynexp++] = s; - s = lookup(elocal, 0); - if(s->dynimplib != nil) { - fprint(2, "%s: symbol is both dynimport and dynexport %s\n", argv0, local); - nerrors++; + if(local != f[1]) + free(local); + continue; + } + + if(strcmp(f[0], "cgo_dynamic_linker") == 0) { + if(nf != 2) + goto err; + + if(!debug['I']) { // not overridden by command line + if(interpreter != nil && strcmp(interpreter, f[1]) != 0) { + fprint(2, "%s: conflict dynlinker: %s and %s\n", argv0, interpreter, f[1]); + nerrors++; + return; + } + free(interpreter); + interpreter = strdup(f[1]); + } + continue; } - s->dynimpname = remote; - s->dynexport = 1; - - if(ndynexp%32 == 0) - dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); - dynexp[ndynexp++] = s; - - if (elocal != local) - free(elocal); } + free(p0); return; err: - fprint(2, "%s: invalid dynexport line: %s\n", argv0, p0); + fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0); nerrors++; } -static int markdepth; +static Sym *markq; +static Sym *emarkq; static void -marktext(Sym *s) +mark1(Sym *s, Sym *parent) { - Auto *a; - Prog *p; - - if(s == S) + if(s == S || s->reachable) return; - markdepth++; - if(debug['v'] > 1) - Bprint(&bso, "%d marktext %s\n", markdepth, s->name); - for(a=s->autom; a; a=a->link) - mark(a->gotype); - for(p=s->text; p != P; p=p->link) { - if(p->from.sym) - mark(p->from.sym); - if(p->to.sym) - mark(p->to.sym); - } - markdepth--; + if(strncmp(s->name, "go.weak.", 8) == 0) + return; + s->reachable = 1; + s->reachparent = parent; + if(markq == nil) + markq = s; + else + emarkq->queue = s; + emarkq = s; } void mark(Sym *s) { - int i; + mark1(s, nil); +} - if(s == S || s->reachable) - return; - if(strncmp(s->name, "weak.", 5) == 0) - return; - s->reachable = 1; - if(s->text) - marktext(s); - for(i=0; inr; i++) - mark(s->r[i].sym); - if(s->gotype) - mark(s->gotype); - if(s->sub) - mark(s->sub); - if(s->outer) - mark(s->outer); +static void +markflood(void) +{ + Auto *a; + Prog *p; + Sym *s; + int i; + + for(s=markq; s!=S; s=s->queue) { + if(s->text) { + if(debug['v'] > 1) + Bprint(&bso, "marktext %s\n", s->name); + for(a=s->autom; a; a=a->link) + mark1(a->gotype, s); + for(p=s->text; p != P; p=p->link) { + mark1(p->from.sym, s); + mark1(p->to.sym, s); + } + } + for(i=0; inr; i++) + mark1(s->r[i].sym, s); + mark1(s->gotype, s); + mark1(s->sub, s); + mark1(s->outer, s); + } } static char* @@ -651,19 +643,30 @@ void deadcode(void) { int i; - Sym *s, *last; + Sym *s, *last, *p; Auto *z; + Fmt fmt; if(debug['v']) Bprint(&bso, "%5.2f deadcode\n", cputime()); mark(lookup(INITENTRY, 0)); + if(flag_shared) + mark(lookup(LIBINITENTRY, 0)); for(i=0; iallsym) { + if(strncmp(s->name, "go.typelink.", 12) == 0) + s->reachable = s->nr==1 && s->r[0].sym->reachable; + } + // remove dead text but keep file information (z symbols). last = nil; z = nil; @@ -690,11 +693,34 @@ deadcode(void) last->next = nil; for(s = allsym; s != S; s = s->allsym) - if(strncmp(s->name, "weak.", 5) == 0) { + if(strncmp(s->name, "go.weak.", 8) == 0) { s->special = 1; // do not lay out in data segment s->reachable = 1; s->hide = 1; } + + // record field tracking references + fmtstrinit(&fmt); + for(s = allsym; s != S; s = s->allsym) { + if(strncmp(s->name, "go.track.", 9) == 0) { + s->special = 1; // do not lay out in data segment + s->hide = 1; + if(s->reachable) { + fmtprint(&fmt, "%s", s->name+9); + for(p=s->reachparent; p; p=p->reachparent) + fmtprint(&fmt, "\t%s", p->name); + fmtprint(&fmt, "\n"); + } + s->type = SCONST; + s->value = 0; + } + } + if(tracksym == nil) + return; + s = lookup(tracksym, 0); + if(!s->reachable) + return; + addstrdata(tracksym, fmtstrflush(&fmt)); } void @@ -705,11 +731,12 @@ doweak(void) // resolve weak references only if // target symbol will be in binary anyway. for(s = allsym; s != S; s = s->allsym) { - if(strncmp(s->name, "weak.", 5) == 0) { - t = rlookup(s->name+5, s->version); + if(strncmp(s->name, "go.weak.", 8) == 0) { + t = rlookup(s->name+8, s->version); if(t && t->type != 0 && t->reachable) { s->value = t->value; s->type = t->type; + s->outer = t; } else { s->type = SCONST; s->value = 0; @@ -871,3 +898,28 @@ importcycles(void) for(p=pkgall; p; p=p->all) cycle(p); } + +static int +scmp(const void *p1, const void *p2) +{ + Sym *s1, *s2; + + s1 = *(Sym**)p1; + s2 = *(Sym**)p2; + return strcmp(s1->dynimpname, s2->dynimpname); +} +void +sortdynexp(void) +{ + int i; + + // On Mac OS X Mountain Lion, we must sort exported symbols + // So we sort them here and pre-allocate dynid for them + // See http://golang.org/issue/4029 + if(HEADTYPE != Hdarwin) + return; + qsort(dynexp, ndynexp, sizeof dynexp[0], scmp); + for(i=0; idynid = -i-100; // also known to [68]l/asm.c:^adddynsym + } +} diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index bd4f3e7d8..2bbf4f83e 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -308,9 +308,19 @@ uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' }; static ElfSect* section(ElfObj*, char*); static int map(ElfObj*, ElfSect*); -static int readsym(ElfObj*, int i, ElfSym*); +static int readsym(ElfObj*, int i, ElfSym*, int); static int reltype(char*, int, uchar*); +int +valuecmp(Sym *a, Sym *b) +{ + if(a->value < b->value) + return -1; + if(a->value > b->value) + return +1; + return 0; +} + void ldelf(Biobuf *f, char *pkg, int64 len, char *pn) { @@ -327,8 +337,10 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) Endian *e; Reloc *r, *rp; Sym *s; + Sym **symbols; + + symbols = nil; - USED(pkg); if(debug['v']) Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); @@ -516,7 +528,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) if(sect->type != ElfSectNobits && map(obj, sect) < 0) goto bad; - name = smprint("%s(%s)", pn, sect->name); + name = smprint("%s(%s)", pkg, sect->name); s = lookup(name, version); free(name); switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { @@ -539,15 +551,99 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) } s->size = sect->size; s->align = sect->align; + sect->sym = s; + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + symbols = malloc(obj->nsymtab * sizeof(symbols[0])); + if(symbols == nil) { + diag("out of memory"); + errorexit(); + } + for(i=1; insymtab; i++) { + if(readsym(obj, i, &sym, 1) < 0) + goto bad; + symbols[i] = sym.sym; + if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) + continue; + if(sym.shndx == ElfSymShnCommon) { + s = sym.sym; + if(s->size < sym.size) + s->size = sym.size; + if(s->type == 0 || s->type == SXREF) + s->type = SBSS; + continue; + } + if(sym.shndx >= obj->nsect || sym.shndx == 0) + continue; + // even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols + if(sym.sym == S) + continue; + sect = obj->sect+sym.shndx; + if(sect->sym == nil) { + diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); + continue; + } + s = sym.sym; + if(s->outer != S) { + if(s->dupok) + continue; + diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); + errorexit(); + } + s->sub = sect->sym->sub; + sect->sym->sub = s; + s->type = sect->sym->type | (s->type&~SMASK) | SSUB; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + s->value = sym.value; + s->size = sym.size; + s->outer = sect->sym; + if(sect->sym->type == STEXT) { + Prog *p; + + if(s->text != P) { + if(!s->dupok) + diag("%s: duplicate definition of %s", pn, s->name); + } else { + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + } + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for(i=0; insect; i++) { + s = obj->sect[i].sym; + if(s == S) + continue; + if(s->sub) + s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub)); if(s->type == STEXT) { if(etextp) etextp->next = s; else textp = s; etextp = s; + for(s = s->sub; s != S; s = s->sub) { + etextp->next = s; + etextp = s; + } } - sect->sym = s; - } + } // load relocations for(i=0; insect; i++) { @@ -588,14 +684,24 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) p += 4; } } - if(readsym(obj, info>>32, &sym) < 0) - goto bad; - if(sym.sym == nil) { - werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", - sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); - goto bad; + if((info & 0xffffffff) == 0) { // skip R_*_NONE relocation + j--; + n--; + continue; + } + if((info >> 32) == 0) { // absolute relocation, don't bother reading the null symbol + rp->sym = S; + } else { + if(readsym(obj, info>>32, &sym, 0) < 0) + goto bad; + sym.sym = symbols[info>>32]; + if(sym.sym == nil) { + werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", + sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); + goto bad; + } + rp->sym = sym.sym; } - rp->sym = sym.sym; rp->type = reltype(pn, (uint32)info, &rp->siz); if(rela) rp->add = add; @@ -615,65 +721,13 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) s->r = r; s->nr = n; } + free(symbols); - // enter sub-symbols into symbol table. - // symbol 0 is the null symbol. - for(i=1; insymtab; i++) { - if(readsym(obj, i, &sym) < 0) - goto bad; - if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) - continue; - if(sym.shndx == ElfSymShnCommon) { - s = sym.sym; - if(s->size < sym.size) - s->size = sym.size; - if(s->type == 0 || s->type == SXREF) - s->type = SBSS; - continue; - } - if(sym.shndx >= obj->nsect || sym.shndx == 0) - continue; - sect = obj->sect+sym.shndx; - if(sect->sym == nil) { - diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); - continue; - } - s = sym.sym; - s->sub = sect->sym->sub; - sect->sym->sub = s; - s->type = sect->sym->type | SSUB; - if(!s->dynexport) { - s->dynimplib = nil; // satisfy dynimport - s->dynimpname = nil; // satisfy dynimport - } - s->value = sym.value; - s->size = sym.size; - s->outer = sect->sym; - if(sect->sym->type == STEXT) { - Prog *p; - - if(s->text != P) - diag("%s: duplicate definition of %s", pn, s->name); - // build a TEXT instruction with a unique pc - // just to make the rest of the linker happy. - p = prg(); - p->as = ATEXT; - p->from.type = D_EXTERN; - p->from.sym = s; - p->textflag = 7; - p->to.type = D_CONST; - p->link = nil; - p->pc = pc++; - s->text = p; - - etextp->next = s; - etextp = s; - } - } return; bad: diag("%s: malformed elf file: %r", pn); + free(symbols); } static ElfSect* @@ -707,7 +761,7 @@ map(ElfObj *obj, ElfSect *sect) } static int -readsym(ElfObj *obj, int i, ElfSym *sym) +readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) { Sym *s; @@ -715,6 +769,9 @@ readsym(ElfObj *obj, int i, ElfSym *sym) werrstr("invalid elf symbol index"); return -1; } + if(i == 0) { + diag("readym: read null symbol!"); + } if(obj->is64) { ElfSymBytes64 *b; @@ -743,8 +800,6 @@ readsym(ElfObj *obj, int i, ElfSym *sym) s = nil; if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) sym->name = ".got"; - if(strcmp(sym->name, "__stack_chk_fail_local") == 0) - sym->other = 0; // rewrite hidden -> default visibility switch(sym->type) { case ElfSymTypeSection: s = obj->sect[sym->shndx].sym; @@ -754,13 +809,37 @@ readsym(ElfObj *obj, int i, ElfSym *sym) case ElfSymTypeNone: switch(sym->bind) { case ElfSymBindGlobal: - if(sym->other != 2) { + if(needSym) { s = lookup(sym->name, 0); - break; + // for global scoped hidden symbols we should insert it into + // symbol hash table, but mark them as hidden. + // __i686.get_pc_thunk.bx is allowed to be duplicated, to + // workaround that we set dupok. + // TODO(minux): correctly handle __i686.get_pc_thunk.bx without + // set dupok generally. See http://codereview.appspot.com/5823055/ + // comment #5 for details. + if(s && sym->other == 2) { + s->type |= SHIDDEN; + s->dupok = 1; + } } - // fall through + break; case ElfSymBindLocal: - s = lookup(sym->name, version); + if(!(thechar == '5' && (strcmp(sym->name, "$a") == 0 || strcmp(sym->name, "$d") == 0))) // binutils for arm generate these mapping symbols, ignore these + if(needSym) { + // local names and hidden visiblity global names are unique + // and should only reference by its index, not name, so we + // don't bother to add them into hash table + s = newsym(sym->name, version); + s->type |= SHIDDEN; + } + break; + case ElfSymBindWeak: + if(needSym) { + s = newsym(sym->name, 0); + if(sym->other == 2) + s->type |= SHIDDEN; + } break; default: werrstr("%s: invalid symbol binding %d", sym->name, sym->bind); @@ -797,6 +876,18 @@ reltype(char *pn, int elftype, uchar *siz) switch(R(thechar, elftype)) { default: diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype); + case R('5', R_ARM_ABS32): + case R('5', R_ARM_GOT32): + case R('5', R_ARM_PLT32): + case R('5', R_ARM_GOTOFF): + case R('5', R_ARM_GOTPC): + case R('5', R_ARM_THM_PC22): + case R('5', R_ARM_REL32): + case R('5', R_ARM_CALL): + case R('5', R_ARM_V4BX): + case R('5', R_ARM_GOT_PREL): + case R('5', R_ARM_PC24): + case R('5', R_ARM_JUMP24): case R('6', R_X86_64_PC32): case R('6', R_X86_64_PLT32): case R('6', R_X86_64_GOTPCREL): diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c index 388848767..41852f17c 100644 --- a/src/cmd/ld/ldmacho.c +++ b/src/cmd/ld/ldmacho.c @@ -440,7 +440,6 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) Reloc *r, *rp; char *name; - USED(pkg); version++; base = Boffset(f); if(Bread(f, hdr, sizeof hdr) != sizeof hdr) @@ -566,16 +565,21 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) continue; if(strcmp(sect->name, "__eh_frame") == 0) continue; - name = smprint("%s(%s/%s)", pn, sect->segname, sect->name); + name = smprint("%s(%s/%s)", pkg, sect->segname, sect->name); s = lookup(name, version); if(s->type != 0) { werrstr("duplicate %s/%s", sect->segname, sect->name); goto bad; } free(name); - s->p = dat + sect->addr - c->seg.vmaddr; + s->np = sect->size; s->size = s->np; + if((sect->flags & 0xff) == 1) // S_ZEROFILL + s->p = mal(s->size); + else { + s->p = dat + sect->addr - c->seg.vmaddr; + } if(strcmp(sect->segname, "__TEXT") == 0) { if(strcmp(sect->name, "__text") == 0) @@ -589,13 +593,6 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) } else s->type = SDATA; } - if(s->type == STEXT) { - if(etextp) - etextp->next = s; - else - textp = s; - etextp = s; - } sect->sym = s; } @@ -627,6 +624,12 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) werrstr("reference to invalid section %s/%s", sect->segname, sect->name); continue; } + if(s->outer != S) { + if(s->dupok) + continue; + diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); + errorexit(); + } s->type = outer->type | SSUB; s->sub = outer->sub; outer->sub = s; @@ -657,11 +660,29 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) p->link = nil; p->pc = pc++; s->text = p; + } + sym->sym = s; + } - etextp->next = s; + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for(i=0; iseg.nsect; i++) { + sect = &c->seg.sect[i]; + if((s = sect->sym) == S) + continue; + if(s->sub) + s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub)); + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; etextp = s; + for(s = s->sub; s != S; s = s->sub) { + etextp->next = s; + etextp = s; + } } - sym->sym = s; } // load relocations @@ -680,19 +701,28 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) int k; MachoSect *ks; - if(thechar != '8') + if(thechar != '8') { + // mach-o only uses scattered relocation on 32-bit platforms diag("unexpected scattered relocation"); + continue; + } - // on 386, rewrite scattered 4/1 relocation into - // the pseudo-pc-relative reference that it is. + // on 386, rewrite scattered 4/1 relocation and some + // scattered 2/1 relocation into the pseudo-pc-relative + // reference that it is. // assume that the second in the pair is in this section // and use that as the pc-relative base. - if(thechar != '8' || rel->type != 4 || j+1 >= sect->nreloc || - !(rel+1)->scattered || (rel+1)->type != 1 || - (rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) { + if(j+1 >= sect->nreloc) { + werrstr("unsupported scattered relocation %d", (int)rel->type); + goto bad; + } + if(!(rel+1)->scattered || (rel+1)->type != 1 || + (rel->type != 4 && rel->type != 2) || + (rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) { werrstr("unsupported scattered relocation %d/%d", (int)rel->type, (int)(rel+1)->type); goto bad; } + rp->siz = rel->length; rp->off = rel->addr; diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c index feb8620bd..39c15e6a1 100644 --- a/src/cmd/ld/ldpe.c +++ b/src/cmd/ld/ldpe.c @@ -145,7 +145,6 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) PeSym *sym; USED(len); - USED(pkg); if(debug['v']) Bprint(&bso, "%5.2f ldpe %s\n", cputime(), pn); @@ -213,7 +212,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) if(map(obj, sect) < 0) goto bad; - name = smprint("%s(%s)", pn, sect->name); + name = smprint("%s(%s)", pkg, sect->name); s = lookup(name, version); free(name); switch(sect->sh.Characteristics&(IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_CNT_INITIALIZED_DATA| @@ -237,13 +236,6 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) s->p = sect->base; s->np = sect->size; s->size = sect->size; - if(s->type == STEXT) { - if(etextp) - etextp->next = s; - else - textp = s; - etextp = s; - } sect->sym = s; if(strcmp(sect->name, ".rsrc") == 0) setpersrc(sect->sym); @@ -300,6 +292,11 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) rp->add = le64(rsect->base+rp->off); break; } + // ld -r could generate multiple section symbols for the + // same section but with different values, we have to take + // that into account + if (obj->pesym[symindex].name[0] == '.') + rp->add += obj->pesym[symindex].value; } qsort(r, rsect->sh.NumberOfRelocations, sizeof r[0], rbyoff); @@ -341,6 +338,13 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) if(sect == nil) return; + + if(s->outer != S) { + if(s->dupok) + continue; + diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); + errorexit(); + } s->sub = sect->sym->sub; sect->sym->sub = s; s->type = sect->sym->type | SSUB; @@ -363,9 +367,27 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) p->link = nil; p->pc = pc++; s->text = p; - - etextp->next = s; + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for(i=0; insect; i++) { + s = obj->sect[i].sym; + if(s == S) + continue; + if(s->sub) + s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub)); + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; etextp = s; + for(s = s->sub; s != S; s = s->sub) { + etextp->next = s; + etextp = s; + } } } diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 4a100cac3..26fa4f2ac 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -72,6 +72,8 @@ Lflag(char *arg) void libinit(void) { + char *race; + fmtinstall('i', iconv); fmtinstall('Y', Yconv); fmtinstall('Z', Zconv); @@ -80,7 +82,10 @@ libinit(void) print("goarch is not known: %s\n", goarch); // add goroot to the end of the libdir list. - Lflag(smprint("%s/pkg/%s_%s", goroot, goos, goarch)); + race = ""; + if(flag_race) + race = "_race"; + Lflag(smprint("%s/pkg/%s_%s%s", goroot, goos, goarch, race)); // Unix doesn't like it when we write to a running (or, sometimes, // recently run) binary, so remove the output file before writing it. @@ -99,6 +104,13 @@ libinit(void) sprint(INITENTRY, "_rt0_%s_%s", goarch, goos); } lookup(INITENTRY, 0)->type = SXREF; + if(flag_shared) { + if(LIBINITENTRY == nil) { + LIBINITENTRY = mal(strlen(goarch)+strlen(goos)+20); + sprint(LIBINITENTRY, "_rt0_%s_%s_lib", goarch, goos); + } + lookup(LIBINITENTRY, 0)->type = SXREF; + } } void @@ -281,6 +293,8 @@ loadlib(void) loadinternal("runtime"); if(thechar == '5') loadinternal("math"); + if(flag_race) + loadinternal("runtime/race"); for(i=0; i 1) + Bprint(&bso, "newsym %s\n", symb); + + s->dynid = -1; + s->plt = -1; + s->got = -1; + s->name = mal(l + 1); + memmove(s->name, symb, l); + + s->type = 0; + s->version = v; + s->value = 0; + s->sig = 0; + s->size = 0; + nsymbol++; + + s->allsym = allsym; + allsym = s; + + return s; +} + static Sym* _lookup(char *symb, int v, int creat) { @@ -569,27 +614,10 @@ _lookup(char *symb, int v, int creat) if(!creat) return nil; - s = mal(sizeof(*s)); - if(debug['v'] > 1) - Bprint(&bso, "lookup %s\n", symb); - - s->dynid = -1; - s->plt = -1; - s->got = -1; - s->name = mal(l + 1); - memmove(s->name, symb, l); - + s = newsym(symb, v); s->hash = hash[h]; - s->type = 0; - s->version = v; - s->value = 0; - s->sig = 0; - s->size = 0; hash[h] = s; - nsymbol++; - s->allsym = allsym; - allsym = s; return s; } @@ -1372,6 +1400,19 @@ headtype(char *name) return -1; // not reached } +char* +headstr(int v) +{ + static char buf[20]; + int i; + + for(i=0; headers[i].name; i++) + if(v == headers[i].val) + return headers[i].name; + snprint(buf, sizeof buf, "%d", v); + return buf; +} + void undef(void) { @@ -1419,6 +1460,23 @@ Yconv(Fmt *fp) vlong coutpos; +static void +dowrite(int fd, char *p, int n) +{ + int m; + + while(n > 0) { + m = write(fd, p, n); + if(m <= 0) { + cursym = S; + diag("write error: %r"); + errorexit(); + } + n -= m; + p += m; + } +} + void cflush(void) { @@ -1427,13 +1485,8 @@ cflush(void) if(cbpmax < cbp) cbpmax = cbp; n = cbpmax - buf.cbuf; - if(n) { - if(write(cout, buf.cbuf, n) != n) { - diag("write error: %r"); - errorexit(); - } - coutpos += n; - } + dowrite(cout, buf.cbuf, n); + coutpos += n; cbp = buf.cbuf; cbc = sizeof(buf.cbuf); cbpmax = cbp; @@ -1472,9 +1525,142 @@ void cwrite(void *buf, int n) { cflush(); - if(write(cout, buf, n) != n) { - diag("write error: %r"); - errorexit(); - } + if(n <= 0) + return; + dowrite(cout, buf, n); coutpos += n; } + +void +usage(void) +{ + fprint(2, "usage: %cl [options] main.%c\n", thechar, thechar); + flagprint(2); + exits("usage"); +} + +void +setheadtype(char *s) +{ + HEADTYPE = headtype(s); +} + +void +setinterp(char *s) +{ + debug['I'] = 1; // denote cmdline interpreter override + interpreter = s; +} + +void +doversion(void) +{ + print("%cl version %s\n", thechar, getgoversion()); + errorexit(); +} + +void +genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) +{ + Auto *a; + Sym *s; + int32 off; + + // These symbols won't show up in the first loop below because we + // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp. + s = lookup("text", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + s = lookup("etext", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + + for(s=allsym; s!=S; s=s->allsym) { + if(s->hide) + continue; + switch(s->type&SMASK) { + case SCONST: + case SRODATA: + case SSYMTAB: + case SPCLNTAB: + case SDATA: + case SNOPTRDATA: + case SELFROSECT: + case SMACHOGOT: + case STYPE: + case SSTRING: + case SGOSTRING: + case SWINDOWS: + case SGCDATA: + case SGCBSS: + if(!s->reachable) + continue; + put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); + continue; + + case SBSS: + case SNOPTRBSS: + if(!s->reachable) + continue; + if(s->np > 0) + diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special); + put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); + continue; + + case SFILE: + put(nil, s->name, 'f', s->value, 0, s->version, 0); + continue; + } + } + + for(s = textp; s != nil; s = s->next) { + if(s->text == nil) + continue; + + /* filenames first */ + for(a=s->autom; a; a=a->link) + if(a->type == D_FILE) + put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); + else + if(a->type == D_FILE1) + put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); + + put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + + /* frame, locals, args, auto and param after */ + put(nil, ".frame", 'm', (uint32)s->text->to.offset+PtrSize, 0, 0, 0); + put(nil, ".locals", 'm', s->locals, 0, 0, 0); + put(nil, ".args", 'm', s->args, 0, 0, 0); + + for(a=s->autom; a; a=a->link) { + // Emit a or p according to actual offset, even if label is wrong. + // This avoids negative offsets, which cannot be encoded. + if(a->type != D_AUTO && a->type != D_PARAM) + continue; + + // compute offset relative to FP + if(a->type == D_PARAM) + off = a->aoffset; + else + off = a->aoffset - PtrSize; + + // FP + if(off >= 0) { + put(nil, a->asym->name, 'p', off, 0, 0, a->gotype); + continue; + } + + // SP + if(off <= -PtrSize) { + put(nil, a->asym->name, 'a', -(off+PtrSize), 0, 0, a->gotype); + continue; + } + + // Otherwise, off is addressing the saved program counter. + // Something underhanded is going on. Say nothing. + } + } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %ud\n", symsize); + Bflush(&bso); +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 02dac6e1c..94ad76ecc 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -31,7 +31,7 @@ enum { Sxxx, - + /* order here is order in output file */ STEXT, SMACHOPLT, @@ -39,11 +39,15 @@ enum SSTRING, SGOSTRING, SRODATA, + STYPELINK, + SGCDATA, + SGCBSS, SSYMTAB, SPCLNTAB, SELFROSECT, SELFSECT, SNOPTRDATA, + SDATARELRO, SDATA, SMACHO, /* Mach-O __nl_symbol_ptr */ SMACHOGOT, @@ -59,9 +63,12 @@ enum SFILE, SCONST, SDYNIMPORT, + SHOSTOBJ, SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */ - + SMASK = SSUB - 1, + SHIDDEN = 1<<9, // hidden or local symbol + NHASH = 100003, }; @@ -92,6 +99,8 @@ struct Segment Section* sect; }; +#pragma incomplete struct Elf64_Shdr + struct Section { uchar rwx; @@ -100,6 +109,9 @@ struct Section uvlong len; Section *next; // in segment list Segment *seg; + struct Elf64_Shdr *elfsect; + uvlong reloff; + uvlong rellen; }; extern char symname[]; @@ -128,6 +140,12 @@ EXTERN char* thestring; EXTERN int ndynexp; EXTERN int havedynamic; EXTERN int iscgo; +EXTERN int isobj; +EXTERN int elfglobalsymndx; +EXTERN int flag_race; +EXTERN int flag_shared; +EXTERN char* tracksym; +EXTERN char* interpreter; EXTERN Segment segtext; EXTERN Segment segdata; @@ -142,6 +160,7 @@ void addhist(int32 line, int type); void asmlc(void); void histtoauto(void); void collapsefrog(Sym *s); +Sym* newsym(char *symb, int v); Sym* lookup(char *symb, int v); Sym* rlookup(char *symb, int v); void nuxiinit(void); @@ -163,6 +182,8 @@ void symtab(void); void Lflag(char *arg); void usage(void); void adddynrel(Sym*, Reloc*); +void adddynrela(Sym*, Sym*, Reloc*); +Sym* lookuprel(void); void ldobj1(Biobuf *f, char*, int64 len, char *pn); void ldobj(Biobuf*, char*, int64, char*, int); void ldelf(Biobuf*, char*, int64, char*); @@ -176,21 +197,26 @@ void deadcode(void); Reloc* addrel(Sym*); void codeblk(int32, int32); void datblk(int32, int32); -Sym* datsort(Sym*); void reloc(void); void relocsym(Sym*); void savedata(Sym*, Prog*, char*); void symgrow(Sym*, int32); void addstrdata(char*, char*); vlong addstring(Sym*, char*); +vlong adduint8(Sym*, uint8); +vlong adduint16(Sym*, uint16); vlong adduint32(Sym*, uint32); vlong adduint64(Sym*, uint64); vlong addaddr(Sym*, Sym*); vlong addaddrplus(Sym*, Sym*, int32); vlong addpcrelplus(Sym*, Sym*, int32); vlong addsize(Sym*, Sym*); -vlong adduint8(Sym*, uint8); -vlong adduint16(Sym*, uint16); +vlong setaddrplus(Sym*, vlong, Sym*, int32); +vlong setaddr(Sym*, vlong, Sym*); +void setuint8(Sym*, vlong, uint8); +void setuint16(Sym*, vlong, uint16); +void setuint32(Sym*, vlong, uint32); +void setuint64(Sym*, vlong, uint64); void asmsym(void); void asmelfsym(void); void asmplan9sym(void); @@ -209,6 +235,11 @@ void dostkcheck(void); void undef(void); void doweak(void); void setpersrc(Sym*); +void doversion(void); +void usage(void); +void setinterp(char*); +Sym* listsort(Sym*, int(*cmp)(Sym*, Sym*), int); +int valuecmp(Sym*, Sym*); int pathchar(void); void* mal(uint32); @@ -233,12 +264,6 @@ struct Endian extern Endian be, le; -// relocation size bits -enum { - Rbig = 128, - Rlittle = 64, -}; - /* set by call to mywhatsys() */ extern char* goroot; extern char* goarch; @@ -282,6 +307,8 @@ EXTERN char* headstring; extern Header headers[]; int headtype(char*); +char* headstr(int); +void setheadtype(char*); int Yconv(Fmt*); @@ -312,3 +339,25 @@ void cseek(vlong); void cwrite(void*, int); void importcycles(void); int Zconv(Fmt*); + +uint8 decodetype_kind(Sym*); +vlong decodetype_size(Sym*); +Sym* decodetype_gc(Sym*); +Sym* decodetype_arrayelem(Sym*); +vlong decodetype_arraylen(Sym*); +Sym* decodetype_ptrelem(Sym*); +Sym* decodetype_mapkey(Sym*); +Sym* decodetype_mapvalue(Sym*); +Sym* decodetype_chanelem(Sym*); +int decodetype_funcdotdotdot(Sym*); +int decodetype_funcincount(Sym*); +int decodetype_funcoutcount(Sym*); +Sym* decodetype_funcintype(Sym*, int); +Sym* decodetype_funcouttype(Sym*, int); +int decodetype_structfieldcount(Sym*); +char* decodetype_structfieldname(Sym*, int); +Sym* decodetype_structfieldtype(Sym*, int); +vlong decodetype_structfieldoffs(Sym*, int); +vlong decodetype_ifacemethodcount(Sym*); + +void sortdynexp(void); diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h index f55104150..baea6ff03 100644 --- a/src/cmd/ld/macho.h +++ b/src/cmd/ld/macho.h @@ -50,7 +50,7 @@ struct MachoDebug { uint32 filesize; }; -MachoHdr* getMachoHdr(); +MachoHdr* getMachoHdr(void); MachoSeg* newMachoSeg(char*, int); MachoSect* newMachoSect(MachoSeg*, char*); MachoLoad* newMachoLoad(uint32, uint32); diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index 1d70b4808..f2903ba0f 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -148,6 +148,9 @@ peinit(void) PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN); nextsectoff = PESECTHEADR; nextfileoff = PEFILEHEADR; + + // some mingw libs depend on this symbol, for example, FindPESectionByName + xdefine("__image_base__", SDATA, PEBASE); } static void @@ -533,6 +536,7 @@ addexcept(IMAGE_SECTION_HEADER *text) uvlong n; Sym *sym; + USED(text); if(thechar != '6') return; @@ -656,7 +660,7 @@ asmbpe(void) // for other threads we specify stack size in runtime explicitly // (runtime knows whether cgo is enabled or not). // If you change stack reserve sizes here, - // change them in runtime/cgo/windows_386/amd64.c as well. + // change STACKSIZE in runtime/cgo/gcc_windows_{386,amd64}.c as well. if(!iscgo) { set(SizeOfStackReserve, 0x00010000); set(SizeOfStackCommit, 0x0000ffff); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 129b13ea0..89a594872 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -36,7 +36,7 @@ static int maxelfstr; -int +static int putelfstr(char *s) { int off, n; @@ -57,14 +57,14 @@ putelfstr(char *s) return off; } -void -putelfsyment(int off, vlong addr, vlong size, int info, int shndx) +static void +putelfsyment(int off, vlong addr, vlong size, int info, int shndx, int other) { switch(thechar) { case '6': LPUT(off); cput(info); - cput(0); + cput(other); WPUT(shndx); VPUT(addr); VPUT(size); @@ -75,17 +75,21 @@ putelfsyment(int off, vlong addr, vlong size, int info, int shndx) LPUT(addr); LPUT(size); cput(info); - cput(0); + cput(other); WPUT(shndx); symsize += ELF32SYMSIZE; break; } } -void +static int numelfsym = 1; // 0 is reserved +static int elfbind; + +static void putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) { - int bind, type, shndx, off; + int bind, type, off; + Sym *xo; USED(go); switch(t) { @@ -93,37 +97,68 @@ putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) return; case 'T': type = STT_FUNC; - shndx = elftextsh + 0; break; case 'D': type = STT_OBJECT; - if((x->type&~SSUB) == SRODATA) - shndx = elftextsh + 1; - else - shndx = elftextsh + 2; break; case 'B': type = STT_OBJECT; - shndx = elftextsh + 3; break; } - bind = ver ? STB_LOCAL : STB_GLOBAL; + xo = x; + while(xo->outer != nil) + xo = xo->outer; + if(xo->sect == nil) { + cursym = x; + diag("missing section in putelfsym"); + return; + } + if(xo->sect->elfsect == nil) { + cursym = x; + diag("missing ELF section in putelfsym"); + return; + } + + // One pass for each binding: STB_LOCAL, STB_GLOBAL, + // maybe one day STB_WEAK. + bind = (ver || (x->type & SHIDDEN)) ? STB_LOCAL : STB_GLOBAL; + if(bind != elfbind) + return; + off = putelfstr(s); - putelfsyment(off, addr, size, (bind<<4)|(type&0xf), shndx); + if(isobj) + addr -= xo->sect->vaddr; + putelfsyment(off, addr, size, (bind<<4)|(type&0xf), xo->sect->elfsect->shnum, (x->type & SHIDDEN) ? 2 : 0); + x->elfsym = numelfsym++; } void asmelfsym(void) { + Sym *s; + // the first symbol entry is reserved - putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0); + putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0, 0); + + elfbind = STB_LOCAL; genasmsym(putelfsym); + + elfbind = STB_GLOBAL; + elfglobalsymndx = numelfsym; + genasmsym(putelfsym); + + for(s=allsym; s!=S; s=s->allsym) { + if(s->type != SHOSTOBJ) + continue; + putelfsyment(putelfstr(s->name), 0, 0, (STB_GLOBAL<<4)|STT_NOTYPE, 0, 0); + s->elfsym = numelfsym++; + } } -void +static void putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) { - int i; + int i, l; USED(go); USED(ver); @@ -142,6 +177,11 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) case 'z': case 'Z': case 'm': + l = 4; + if(HEADTYPE == Hplan9x64 && !debug['8']) { + lputb(addr>>32); + l = 8; + } lputb(addr); cput(t+0x80); /* 0x80 is variable length */ @@ -162,7 +202,7 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) cput(s[i]); cput(0); } - symsize += 4 + 1 + i + 1; + symsize += l + 1 + i + 1; break; default: return; @@ -192,7 +232,7 @@ static void slputb(int32 v) { uchar *p; - + symgrow(symt, symt->size+4); p = symt->p + symt->size; *p++ = v>>24; @@ -202,6 +242,22 @@ slputb(int32 v) symt->size += 4; } +static void +slputl(int32 v) +{ + uchar *p; + + symgrow(symt, symt->size+4); + p = symt->p + symt->size; + *p++ = v; + *p++ = v>>8; + *p++ = v>>16; + *p = v>>24; + symt->size += 4; +} + +static void (*slput)(int32); + void wputl(ushort w) { @@ -248,32 +304,76 @@ vputl(uint64 v) lputl(v >> 32); } +// Emit symbol table entry. +// The table format is described at the top of ../../pkg/runtime/symtab.c. void putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) { - int i, f, l; + int i, f, c; + vlong v1; Reloc *rel; USED(size); - if(t == 'f') - name++; - l = 4; -// if(!debug['8']) -// l = 8; + + // type byte + if('A' <= t && t <= 'Z') + c = t - 'A'; + else if('a' <= t && t <= 'z') + c = t - 'a' + 26; + else { + diag("invalid symbol table type %c", t); + errorexit(); + return; + } + + if(s != nil) + c |= 0x40; // wide value + if(typ != nil) + c |= 0x80; // has go type + scput(c); + + // value if(s != nil) { + // full width rel = addrel(symt); - rel->siz = l + Rbig; + rel->siz = PtrSize; rel->sym = s; rel->type = D_ADDR; rel->off = symt->size; - v = 0; - } - if(l == 8) - slputb(v>>32); - slputb(v); - if(ver) - t += 'a' - 'A'; - scput(t+0x80); /* 0x80 is variable length */ + if(PtrSize == 8) + slput(0); + slput(0); + } else { + // varint + if(v < 0) { + diag("negative value in symbol table: %s %lld", name, v); + errorexit(); + } + v1 = v; + while(v1 >= 0x80) { + scput(v1 | 0x80); + v1 >>= 7; + } + scput(v1); + } + + // go type if present + if(typ != nil) { + if(!typ->reachable) + diag("unreachable type %s", typ->name); + rel = addrel(symt); + rel->siz = PtrSize; + rel->sym = typ; + rel->type = D_ADDR; + rel->off = symt->size; + if(PtrSize == 8) + slput(0); + slput(0); + } + + // name + if(t == 'f') + name++; if(t == 'Z' || t == 'z') { scput(name[0]); @@ -283,24 +383,11 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) } scput(0); scput(0); - } - else { + } else { for(i=0; name[i]; i++) scput(name[i]); scput(0); } - if(typ) { - if(!typ->reachable) - diag("unreachable type %s", typ->name); - rel = addrel(symt); - rel->siz = l; - rel->sym = typ; - rel->type = D_ADDR; - rel->off = symt->size; - } - if(l == 8) - slputb(0); - slputb(0); if(debug['n']) { if(t == 'z' || t == 'Z') { @@ -313,25 +400,34 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) return; } if(ver) - Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, s->name, ver, typ ? typ->name : ""); + Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, name, ver, typ ? typ->name : ""); else - Bprint(&bso, "%c %.8llux %s %s\n", t, v, s->name, typ ? typ->name : ""); + Bprint(&bso, "%c %.8llux %s %s\n", t, v, name, typ ? typ->name : ""); } } void symtab(void) { - Sym *s; - + Sym *s, *symtype, *symtypelink, *symgostring; dosymtype(); // Define these so that they'll get put into the symbol table. // data.c:/^address will provide the actual values. xdefine("text", STEXT, 0); xdefine("etext", STEXT, 0); + xdefine("typelink", SRODATA, 0); + xdefine("etypelink", SRODATA, 0); xdefine("rodata", SRODATA, 0); xdefine("erodata", SRODATA, 0); + if(flag_shared) { + xdefine("datarelro", SDATARELRO, 0); + xdefine("edatarelro", SDATARELRO, 0); + } + xdefine("gcdata", SGCDATA, 0); + xdefine("egcdata", SGCDATA, 0); + xdefine("gcbss", SGCBSS, 0); + xdefine("egcbss", SGCBSS, 0); xdefine("noptrdata", SNOPTRDATA, 0); xdefine("enoptrdata", SNOPTRDATA, 0); xdefine("data", SDATA, 0); @@ -343,23 +439,27 @@ symtab(void) xdefine("end", SBSS, 0); xdefine("epclntab", SRODATA, 0); xdefine("esymtab", SRODATA, 0); - + // pseudo-symbols to mark locations of type, string, and go string data. s = lookup("type.*", 0); s->type = STYPE; s->size = 0; s->reachable = 1; + symtype = s; s = lookup("go.string.*", 0); s->type = SGOSTRING; s->size = 0; s->reachable = 1; + symgostring = s; + + symtypelink = lookup("typelink", 0); symt = lookup("symtab", 0); symt->type = SSYMTAB; symt->size = 0; symt->reachable = 1; - + // assign specific types so that they sort together. // within a type they sort by size, so the .* symbols // just defined above will be first. @@ -370,14 +470,44 @@ symtab(void) if(strncmp(s->name, "type.", 5) == 0) { s->type = STYPE; s->hide = 1; + s->outer = symtype; + } + if(strncmp(s->name, "go.typelink.", 12) == 0) { + s->type = STYPELINK; + s->hide = 1; + s->outer = symtypelink; } if(strncmp(s->name, "go.string.", 10) == 0) { s->type = SGOSTRING; s->hide = 1; + s->outer = symgostring; } } if(debug['s']) return; + + switch(thechar) { + default: + diag("unknown architecture %c", thechar); + errorexit(); + case '5': + case '6': + case '8': + // little-endian symbol table + slput = slputl; + break; + case 'v': + // big-endian symbol table + slput = slputb; + break; + } + // new symbol table header. + slput(0xfffffffd); + scput(0); + scput(0); + scput(0); + scput(PtrSize); + genasmsym(putsymb); } diff --git a/src/cmd/nm/doc.go b/src/cmd/nm/doc.go index c84369a5f..480c1c3dd 100644 --- a/src/cmd/nm/doc.go +++ b/src/cmd/nm/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* Nm is a version of the Plan 9 nm command. The original is documented at @@ -11,11 +13,11 @@ Nm is a version of the Plan 9 nm command. The original is documented at It prints the name list (symbol table) for programs compiled by gc as well as the Plan 9 C compiler. -This implementation adds the flag -S, which prints each symbol's size +This implementation adds the flag -S, which prints each symbol's size in decimal after its address. Usage: go tool nm [-aghnsTu] file */ -package documentation +package main diff --git a/src/cmd/nm/nm.c b/src/cmd/nm/nm.c index 845b6c773..668239035 100644 --- a/src/cmd/nm/nm.c +++ b/src/cmd/nm/nm.c @@ -43,7 +43,7 @@ enum{ char *errs; /* exit status */ char *filename; /* current file */ -char symname[]="__.SYMDEF"; /* table of contents file name */ +char symname[]="__.GOSYMDEF"; /* table of contents file name */ int multifile; /* processing multiple files */ int aflag; int gflag; diff --git a/src/cmd/pack/ar.c b/src/cmd/pack/ar.c index 7e07fbc89..7053f841f 100644 --- a/src/cmd/pack/ar.c +++ b/src/cmd/pack/ar.c @@ -77,9 +77,7 @@ typedef struct Armember /* Temp file entry - one per archive member */ typedef struct Arfile /* Temp file control block - one per tempfile */ { - int paged; /* set when some data paged to disk */ char *fname; /* paging file name */ - int fd; /* paging file descriptor */ vlong size; Armember *head; /* head of member chain */ Armember *tail; /* tail of member chain */ @@ -113,7 +111,7 @@ char *opt = "uvnbailogS"; char artemp[] = "/tmp/vXXXXX"; char movtemp[] = "/tmp/v1XXXXX"; char tailtemp[] = "/tmp/v2XXXXX"; -char symdef[] = "__.SYMDEF"; +char symdef[] = "__.GOSYMDEF"; char pkgdef[] = "__.PKGDEF"; int aflag; /* command line flags */ @@ -159,7 +157,6 @@ int bamatch(char*, char*); int duplicate(char*, char**); Armember *getdir(Biobuf*); void getpkgdef(char**, int*); -int getspace(void); void install(char*, Arfile*, Arfile*, Arfile*, int); void loadpkgdata(char*, int); void longt(Armember*); @@ -169,7 +166,6 @@ Arfile *newtempfile(char*); Armember *newmember(void); void objsym(Sym*, void*); int openar(char*, int, int); -int page(Arfile*); void pmode(long); void rl(int); void scanobj(Biobuf*, Arfile*, long); @@ -1534,24 +1530,8 @@ void arstream(int fd, Arfile *ap) { Armember *bp; - int i; - char buf[8192]; - - if (ap->paged) { /* copy from disk */ - seek(ap->fd, 0, 0); - for (;;) { - i = read(ap->fd, buf, sizeof(buf)); - if (i < 0) - rderr(); - if (i == 0) - break; - if (write(fd, buf, i) != i) - wrerr(); - } - close(ap->fd); - ap->paged = 0; - } - /* dump the in-core buffers */ + + /* dump the in-core buffers */ for (bp = ap->head; bp; bp = bp->next) { if (!arwrite(fd, bp)) wrerr(); @@ -1576,35 +1556,6 @@ arwrite(int fd, Armember *bp) return 1; } -/* - * Spill a member to a disk copy of a temp file - */ -int -page(Arfile *ap) -{ - USED(ap); - - sysfatal("page"); - return 1; -} - -/* - * try to reclaim space by paging. we try to spill the start, middle, - * and end files, in that order. there is no particular reason for the - * ordering. - */ -int -getspace(void) -{ - if (astart && astart->head && page(astart)) - return 1; - if (amiddle && amiddle->head && page(amiddle)) - return 1; - if (aend && aend->head && page(aend)) - return 1; - return 0; -} - void arfree(Arfile *ap) /* free a member buffer */ { @@ -1633,13 +1584,11 @@ armalloc(int n) if(n&1) n++; - do { - cp = malloc(n); - if (cp) { - memset(cp, 0, n); - return cp; - } - } while (getspace()); + cp = malloc(n); + if (cp) { + memset(cp, 0, n); + return cp; + } fprint(2, "pack: out of memory\n"); exits("malloc"); return 0; diff --git a/src/cmd/pack/doc.go b/src/cmd/pack/doc.go index 8b17f3ca2..67b789731 100644 --- a/src/cmd/pack/doc.go +++ b/src/cmd/pack/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* Pack is a variant of the Plan 9 ar tool. The original is documented at @@ -24,4 +26,4 @@ The new option 'P' causes pack to remove the given prefix from file names in the line number information in object files that are already stored in or added to the archive. */ -package documentation +package main diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile new file mode 100644 index 000000000..3f528d751 --- /dev/null +++ b/src/cmd/prof/Makefile @@ -0,0 +1,5 @@ +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../Make.dist diff --git a/src/cmd/prof/doc.go b/src/cmd/prof/doc.go new file mode 100644 index 000000000..2640167d3 --- /dev/null +++ b/src/cmd/prof/doc.go @@ -0,0 +1,49 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* + +Prof is a rudimentary real-time profiler. + +Given a command to run or the process id (pid) of a command already +running, it samples the program's state at regular intervals and reports +on its behavior. With no options, it prints a histogram of the locations +in the code that were sampled during execution. + +Since it is a real-time profiler, unlike a traditional profiler it samples +the program's state even when it is not running, such as when it is +asleep or waiting for I/O. Each thread contributes equally to the +statistics. + +Usage: + go tool prof -p pid [-t total_secs] [-d delta_msec] [6.out args ...] + +The output modes (default -h) are: + + -P file.prof: + Write the profile information to file.prof, in the format used by pprof. + At the moment, this only works on Linux amd64 binaries and requires that the + binary be written using 6l -e to produce ELF debug info. + See http://code.google.com/p/google-perftools for details. + -h: histograms + How many times a sample occurred at each location. + -f: dynamic functions + At each sample period, print the name of the executing function. + -l: dynamic file and line numbers + At each sample period, print the file and line number of the executing instruction. + -r: dynamic registers + At each sample period, print the register contents. + -s: dynamic function stack traces + At each sample period, print the symbolic stack trace. + +Flag -t sets the maximum real time to sample, in seconds, and -d +sets the sampling interval in milliseconds. The default is to sample +every 100ms until the program completes. + +It is installed as go tool prof and is architecture-independent. + +*/ +package main diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c new file mode 100644 index 000000000..6c591ba18 --- /dev/null +++ b/src/cmd/prof/main.c @@ -0,0 +1,910 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9 + +#include +#include +#include +#include +#include + +#define Ureg Ureg_amd64 + #include +#undef Ureg +#define Ureg Ureg_x86 + #include +#undef Ureg +#include + +char* file = "6.out"; +static Fhdr fhdr; +int have_syms; +int fd; +struct Ureg_amd64 ureg_amd64; +struct Ureg_x86 ureg_x86; +int total_sec = 0; +int delta_msec = 100; +int nsample; +int nsamplethread; + +// pprof data, stored as sequences of N followed by N PC values. +// See http://code.google.com/p/google-perftools . +uvlong *ppdata; // traces +Biobuf* pproffd; // file descriptor to write trace info +long ppstart; // start position of current trace +long nppdata; // length of data +long ppalloc; // size of allocated data +char ppmapdata[10*1024]; // the map information for the output file + +// output formats +int pprof; // print pprof output to named file +int functions; // print functions +int histograms; // print histograms +int linenums; // print file and line numbers rather than function names +int registers; // print registers +int stacks; // print stack traces + +int pid; // main process pid + +int nthread; // number of threads +int thread[32]; // thread pids +Map *map[32]; // thread maps + +void +Usage(void) +{ + fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec]\n"); + fprint(2, " prof [-t total_secs] [-d delta_msec] 6.out args ...\n"); + fprint(2, "\tformats (default -h):\n"); + fprint(2, "\t\t-P file.prof: write [c]pprof output to file.prof\n"); + fprint(2, "\t\t-h: histograms\n"); + fprint(2, "\t\t-f: dynamic functions\n"); + fprint(2, "\t\t-l: dynamic file and line numbers\n"); + fprint(2, "\t\t-r: dynamic registers\n"); + fprint(2, "\t\t-s: dynamic function stack traces\n"); + fprint(2, "\t\t-hs: include stack info in histograms\n"); + exit(2); +} + +typedef struct PC PC; +struct PC { + uvlong pc; + uvlong callerpc; + unsigned int count; + PC* next; +}; + +enum { + Ncounters = 256 +}; + +PC *counters[Ncounters]; + +// Set up by setarch() to make most of the code architecture-independent. +typedef struct Arch Arch; +struct Arch { + char* name; + void (*regprint)(void); + int (*getregs)(Map*); + int (*getPC)(Map*); + int (*getSP)(Map*); + uvlong (*uregPC)(void); + uvlong (*uregSP)(void); + void (*ppword)(uvlong w); +}; + +void +amd64_regprint(void) +{ + fprint(2, "ax\t0x%llux\n", ureg_amd64.ax); + fprint(2, "bx\t0x%llux\n", ureg_amd64.bx); + fprint(2, "cx\t0x%llux\n", ureg_amd64.cx); + fprint(2, "dx\t0x%llux\n", ureg_amd64.dx); + fprint(2, "si\t0x%llux\n", ureg_amd64.si); + fprint(2, "di\t0x%llux\n", ureg_amd64.di); + fprint(2, "bp\t0x%llux\n", ureg_amd64.bp); + fprint(2, "r8\t0x%llux\n", ureg_amd64.r8); + fprint(2, "r9\t0x%llux\n", ureg_amd64.r9); + fprint(2, "r10\t0x%llux\n", ureg_amd64.r10); + fprint(2, "r11\t0x%llux\n", ureg_amd64.r11); + fprint(2, "r12\t0x%llux\n", ureg_amd64.r12); + fprint(2, "r13\t0x%llux\n", ureg_amd64.r13); + fprint(2, "r14\t0x%llux\n", ureg_amd64.r14); + fprint(2, "r15\t0x%llux\n", ureg_amd64.r15); + fprint(2, "ds\t0x%llux\n", ureg_amd64.ds); + fprint(2, "es\t0x%llux\n", ureg_amd64.es); + fprint(2, "fs\t0x%llux\n", ureg_amd64.fs); + fprint(2, "gs\t0x%llux\n", ureg_amd64.gs); + fprint(2, "type\t0x%llux\n", ureg_amd64.type); + fprint(2, "error\t0x%llux\n", ureg_amd64.error); + fprint(2, "pc\t0x%llux\n", ureg_amd64.ip); + fprint(2, "cs\t0x%llux\n", ureg_amd64.cs); + fprint(2, "flags\t0x%llux\n", ureg_amd64.flags); + fprint(2, "sp\t0x%llux\n", ureg_amd64.sp); + fprint(2, "ss\t0x%llux\n", ureg_amd64.ss); +} + +int +amd64_getregs(Map *map) +{ + int i; + union { + uvlong regs[1]; + struct Ureg_amd64 ureg; + } u; + + for(i = 0; i < sizeof ureg_amd64; i+=8) { + if(get8(map, (uvlong)i, &u.regs[i/8]) < 0) + return -1; + } + ureg_amd64 = u.ureg; + return 0; +} + +int +amd64_getPC(Map *map) +{ + uvlong x; + int r; + + r = get8(map, offsetof(struct Ureg_amd64, ip), &x); + ureg_amd64.ip = x; + return r; +} + +int +amd64_getSP(Map *map) +{ + uvlong x; + int r; + + r = get8(map, offsetof(struct Ureg_amd64, sp), &x); + ureg_amd64.sp = x; + return r; +} + +uvlong +amd64_uregPC(void) +{ + return ureg_amd64.ip; +} + +uvlong +amd64_uregSP(void) +{ + return ureg_amd64.sp; +} + +void +amd64_ppword(uvlong w) +{ + uchar buf[8]; + + buf[0] = w; + buf[1] = w >> 8; + buf[2] = w >> 16; + buf[3] = w >> 24; + buf[4] = w >> 32; + buf[5] = w >> 40; + buf[6] = w >> 48; + buf[7] = w >> 56; + Bwrite(pproffd, buf, 8); +} + +void +x86_regprint(void) +{ + fprint(2, "ax\t0x%ux\n", ureg_x86.ax); + fprint(2, "bx\t0x%ux\n", ureg_x86.bx); + fprint(2, "cx\t0x%ux\n", ureg_x86.cx); + fprint(2, "dx\t0x%ux\n", ureg_x86.dx); + fprint(2, "si\t0x%ux\n", ureg_x86.si); + fprint(2, "di\t0x%ux\n", ureg_x86.di); + fprint(2, "bp\t0x%ux\n", ureg_x86.bp); + fprint(2, "ds\t0x%ux\n", ureg_x86.ds); + fprint(2, "es\t0x%ux\n", ureg_x86.es); + fprint(2, "fs\t0x%ux\n", ureg_x86.fs); + fprint(2, "gs\t0x%ux\n", ureg_x86.gs); + fprint(2, "cs\t0x%ux\n", ureg_x86.cs); + fprint(2, "flags\t0x%ux\n", ureg_x86.flags); + fprint(2, "pc\t0x%ux\n", ureg_x86.pc); + fprint(2, "sp\t0x%ux\n", ureg_x86.sp); + fprint(2, "ss\t0x%ux\n", ureg_x86.ss); +} + +int +x86_getregs(Map *map) +{ + int i; + + for(i = 0; i < sizeof ureg_x86; i+=4) { + if(get4(map, (uvlong)i, &((uint32*)&ureg_x86)[i/4]) < 0) + return -1; + } + return 0; +} + +int +x86_getPC(Map* map) +{ + return get4(map, offsetof(struct Ureg_x86, pc), &ureg_x86.pc); +} + +int +x86_getSP(Map* map) +{ + return get4(map, offsetof(struct Ureg_x86, sp), &ureg_x86.sp); +} + +uvlong +x86_uregPC(void) +{ + return (uvlong)ureg_x86.pc; +} + +uvlong +x86_uregSP(void) +{ + return (uvlong)ureg_x86.sp; +} + +void +x86_ppword(uvlong w) +{ + uchar buf[4]; + + buf[0] = w; + buf[1] = w >> 8; + buf[2] = w >> 16; + buf[3] = w >> 24; + Bwrite(pproffd, buf, 4); +} + +Arch archtab[] = { + { + "amd64", + amd64_regprint, + amd64_getregs, + amd64_getPC, + amd64_getSP, + amd64_uregPC, + amd64_uregSP, + amd64_ppword, + }, + { + "386", + x86_regprint, + x86_getregs, + x86_getPC, + x86_getSP, + x86_uregPC, + x86_uregSP, + x86_ppword, + }, + { + nil + } +}; + +Arch *arch; + +int +setarch(void) +{ + int i; + + if(mach != nil) { + for(i = 0; archtab[i].name != nil; i++) { + if (strcmp(mach->name, archtab[i].name) == 0) { + arch = &archtab[i]; + return 0; + } + } + } + return -1; +} + +int +getthreads(void) +{ + int i, j, curn, found; + Map *curmap[nelem(map)]; + int curthread[nelem(map)]; + static int complained = 0; + + curn = procthreadpids(pid, curthread, nelem(curthread)); + if(curn <= 0) + return curn; + + if(curn > nelem(map)) { + if(complained == 0) { + fprint(2, "prof: too many threads; limiting to %d\n", nthread, nelem(map)); + complained = 1; + } + curn = nelem(map); + } + if(curn == nthread && memcmp(thread, curthread, curn*sizeof(*thread)) == 0) + return curn; // no changes + + // Number of threads has changed (might be the init case). + // A bit expensive but rare enough not to bother being clever. + for(i = 0; i < curn; i++) { + found = 0; + for(j = 0; j < nthread; j++) { + if(curthread[i] == thread[j]) { + found = 1; + curmap[i] = map[j]; + map[j] = nil; + break; + } + } + if(found) + continue; + + // map new thread + curmap[i] = attachproc(curthread[i], &fhdr); + if(curmap[i] == nil) { + fprint(2, "prof: can't attach to %d: %r\n", curthread[i]); + return -1; + } + } + + for(j = 0; j < nthread; j++) + if(map[j] != nil) + detachproc(map[j]); + + nthread = curn; + memmove(thread, curthread, nthread*sizeof thread[0]); + memmove(map, curmap, sizeof map); + return nthread; +} + +int +sample(Map *map) +{ + static int n; + + n++; + if(registers) { + if(arch->getregs(map) < 0) + goto bad; + } else { + // we need only two registers + if(arch->getPC(map) < 0) + goto bad; + if(arch->getSP(map) < 0) + goto bad; + } + return 1; +bad: + if(n == 1) + fprint(2, "prof: can't read registers: %r\n"); + return 0; +} + +void +addtohistogram(uvlong pc, uvlong callerpc, uvlong sp) +{ + int h; + PC *x; + + USED(sp); + + h = (pc + callerpc*101) % Ncounters; + for(x = counters[h]; x != NULL; x = x->next) { + if(x->pc == pc && x->callerpc == callerpc) { + x->count++; + return; + } + } + x = malloc(sizeof(PC)); + if(x == nil) + sysfatal("out of memory"); + x->pc = pc; + x->callerpc = callerpc; + x->count = 1; + x->next = counters[h]; + counters[h] = x; +} + +void +addppword(uvlong pc) +{ + if(pc == 0) { + return; + } + if(nppdata == ppalloc) { + ppalloc = (1000+nppdata)*2; + ppdata = realloc(ppdata, ppalloc * sizeof ppdata[0]); + if(ppdata == nil) { + fprint(2, "prof: realloc failed: %r\n"); + exit(2); + } + } + ppdata[nppdata++] = pc; +} + +void +startpptrace(void) +{ + ppstart = nppdata; + addppword(~0); +} + +void +endpptrace(void) +{ + ppdata[ppstart] = nppdata-ppstart-1; +} + +uvlong nextpc; + +void +xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym) +{ + USED(map); + + char buf[1024]; + if(sym == nil){ + fprint(2, "syms\n"); + return; + } + if(histograms) + addtohistogram(nextpc, pc, sp); + if(!histograms || stacks > 1 || pprof) { + if(nextpc == 0) + nextpc = sym->value; + if(stacks){ + fprint(2, "%s(", sym->name); + fprint(2, ")"); + if(nextpc != sym->value) + fprint(2, "+%#llux ", nextpc - sym->value); + if(have_syms && linenums && fileline(buf, sizeof buf, pc)) { + fprint(2, " %s", buf); + } + fprint(2, "\n"); + } + if (pprof) { + addppword(nextpc); + } + } + nextpc = pc; +} + +void +stacktracepcsp(Map *map, uvlong pc, uvlong sp) +{ + nextpc = pc; + if(pprof){ + startpptrace(); + } + if(machdata->ctrace==nil) + fprint(2, "no machdata->ctrace\n"); + else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0) + fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp); + else { + addtohistogram(nextpc, 0, sp); + if(stacks) + fprint(2, "\n"); + } + if(pprof){ + endpptrace(); + } +} + +void +printpc(Map *map, uvlong pc, uvlong sp) +{ + char buf[1024]; + if(registers) + arch->regprint(); + if(have_syms > 0 && linenums && fileline(buf, sizeof buf, pc)) + fprint(2, "%s\n", buf); + if(have_syms > 0 && functions) { + symoff(buf, sizeof(buf), pc, CANY); + fprint(2, "%s\n", buf); + } + if(stacks || pprof){ + stacktracepcsp(map, pc, sp); + } + else if(histograms){ + addtohistogram(pc, 0, sp); + } +} + +void +ppmaps(void) +{ + int fd, n; + char tmp[100]; + Seg *seg; + + // If it's Linux, the info is in /proc/$pid/maps + snprint(tmp, sizeof tmp, "/proc/%d/maps", pid); + fd = open(tmp, 0); + if(fd >= 0) { + n = read(fd, ppmapdata, sizeof ppmapdata - 1); + close(fd); + if(n < 0) { + fprint(2, "prof: can't read %s: %r\n", tmp); + exit(2); + } + ppmapdata[n] = 0; + return; + } + + // It's probably a mac. Synthesize an entry for the text file. + // The register segment may come first but it has a zero offset, so grab the first non-zero offset segment. + for(n = 0; n < 3; n++){ + seg = &map[0]->seg[n]; + if(seg->b == 0) { + continue; + } + snprint(ppmapdata, sizeof ppmapdata, + "%.16x-%.16x r-xp %d 00:00 34968549 %s\n", + seg->b, seg->e, seg->f, "/home/r/6.out" + ); + return; + } + fprint(2, "prof: no text segment in maps for %s\n", file); + exit(2); +} + +void +samples(void) +{ + int i, pid, msec; + struct timespec req; + int getmaps; + + req.tv_sec = delta_msec/1000; + req.tv_nsec = 1000000*(delta_msec % 1000); + getmaps = 0; + if(pprof) + getmaps= 1; + for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) { + nsample++; + nsamplethread += nthread; + for(i = 0; i < nthread; i++) { + pid = thread[i]; + if(ctlproc(pid, "stop") < 0) + return; + if(!sample(map[i])) { + ctlproc(pid, "start"); + return; + } + printpc(map[i], arch->uregPC(), arch->uregSP()); + ctlproc(pid, "start"); + } + nanosleep(&req, NULL); + getthreads(); + if(nthread == 0) + break; + if(getmaps) { + getmaps = 0; + ppmaps(); + } + } +} + +typedef struct Func Func; +struct Func +{ + Func *next; + Symbol s; + uint onstack; + uint leaf; +}; + +Func *func[257]; +int nfunc; + +Func* +findfunc(uvlong pc) +{ + Func *f; + uint h; + Symbol s; + + if(pc == 0) + return nil; + + if(!findsym(pc, CTEXT, &s)) + return nil; + + h = s.value % nelem(func); + for(f = func[h]; f != NULL; f = f->next) + if(f->s.value == s.value) + return f; + + f = malloc(sizeof *f); + if(f == nil) + sysfatal("out of memory"); + memset(f, 0, sizeof *f); + f->s = s; + f->next = func[h]; + func[h] = f; + nfunc++; + return f; +} + +int +compareleaf(const void *va, const void *vb) +{ + Func *a, *b; + + a = *(Func**)va; + b = *(Func**)vb; + if(a->leaf != b->leaf) + return b->leaf - a->leaf; + if(a->onstack != b->onstack) + return b->onstack - a->onstack; + return strcmp(a->s.name, b->s.name); +} + +void +dumphistogram(void) +{ + int i, h, n; + PC *x; + Func *f, **ff; + + if(!histograms) + return; + + // assign counts to functions. + for(h = 0; h < Ncounters; h++) { + for(x = counters[h]; x != NULL; x = x->next) { + f = findfunc(x->pc); + if(f) { + f->onstack += x->count; + f->leaf += x->count; + } + f = findfunc(x->callerpc); + if(f) + f->leaf -= x->count; + } + } + + // build array + ff = malloc(nfunc*sizeof ff[0]); + if(ff == nil) + sysfatal("out of memory"); + n = 0; + for(h = 0; h < nelem(func); h++) + for(f = func[h]; f != NULL; f = f->next) + ff[n++] = f; + + // sort by leaf counts + qsort(ff, nfunc, sizeof ff[0], compareleaf); + + // print. + fprint(2, "%d samples (avg %.1g threads)\n", nsample, (double)nsamplethread/nsample); + for(i = 0; i < nfunc; i++) { + f = ff[i]; + fprint(2, "%6.2f%%\t", 100.0*(double)f->leaf/nsample); + if(stacks) + fprint(2, "%6.2f%%\t", 100.0*(double)f->onstack/nsample); + fprint(2, "%s\n", f->s.name); + } +} + +typedef struct Trace Trace; +struct Trace { + int count; + int npc; + uvlong *pc; + Trace *next; +}; + +void +dumppprof(void) +{ + uvlong i, n, *p, *e; + int ntrace; + Trace *trace, *tp, *up, *prev; + + if(!pprof) + return; + e = ppdata + nppdata; + // Create list of traces. First, count the traces + ntrace = 0; + for(p = ppdata; p < e;) { + n = *p++; + p += n; + if(n == 0) + continue; + ntrace++; + } + if(ntrace <= 0) + return; + // Allocate and link the traces together. + trace = malloc(ntrace * sizeof(Trace)); + if(trace == nil) + sysfatal("out of memory"); + tp = trace; + for(p = ppdata; p < e;) { + n = *p++; + if(n == 0) + continue; + tp->count = 1; + tp->npc = n; + tp->pc = p; + tp->next = tp+1; + tp++; + p += n; + } + trace[ntrace-1].next = nil; + // Eliminate duplicates. Lousy algorithm, although not as bad as it looks because + // the list collapses fast. + for(tp = trace; tp != nil; tp = tp->next) { + prev = tp; + for(up = tp->next; up != nil; up = up->next) { + if(up->npc == tp->npc && memcmp(up->pc, tp->pc, up->npc*sizeof up->pc[0]) == 0) { + tp->count++; + prev->next = up->next; + } else { + prev = up; + } + } + } + // Write file. + // See http://code.google.com/p/google-perftools/source/browse/trunk/doc/cpuprofile-fileformat.html + // 1) Header + arch->ppword(0); // must be zero + arch->ppword(3); // 3 words follow in header + arch->ppword(0); // must be zero + arch->ppword(delta_msec * 1000); // sampling period in microseconds + arch->ppword(0); // must be zero (padding) + // 2) One record for each trace. + for(tp = trace; tp != nil; tp = tp->next) { + arch->ppword(tp->count); + arch->ppword(tp->npc); + for(i = 0; i < tp->npc; i++) { + arch->ppword(tp->pc[i]); + } + } + // 3) Binary trailer + arch->ppword(0); // must be zero + arch->ppword(1); // must be one + arch->ppword(0); // must be zero + // 4) Mapped objects. + Bwrite(pproffd, ppmapdata, strlen(ppmapdata)); + // 5) That's it. + Bterm(pproffd); +} + +int +startprocess(char **argv) +{ + int pid; + + if((pid = fork()) == 0) { + pid = getpid(); + if(ctlproc(pid, "hang") < 0){ + fprint(2, "prof: child process could not hang\n"); + exits(0); + } + execv(argv[0], argv); + fprint(2, "prof: could not exec %s: %r\n", argv[0]); + exits(0); + } + + if(pid == -1) { + fprint(2, "prof: could not fork\n"); + exit(1); + } + if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) { + fprint(2, "prof: could not attach to child process: %r\n"); + exit(1); + } + return pid; +} + +void +detach(void) +{ + int i; + + for(i = 0; i < nthread; i++) + detachproc(map[i]); +} + +int +main(int argc, char *argv[]) +{ + int i; + char *ppfile; + + ARGBEGIN{ + case 'P': + pprof =1; + ppfile = EARGF(Usage()); + pproffd = Bopen(ppfile, OWRITE); + if(pproffd == nil) { + fprint(2, "prof: cannot open %s: %r\n", ppfile); + exit(2); + } + break; + case 'd': + delta_msec = atoi(EARGF(Usage())); + break; + case 't': + total_sec = atoi(EARGF(Usage())); + break; + case 'p': + pid = atoi(EARGF(Usage())); + break; + case 'f': + functions = 1; + break; + case 'h': + histograms = 1; + break; + case 'l': + linenums = 1; + break; + case 'r': + registers = 1; + break; + case 's': + stacks++; + break; + default: + Usage(); + }ARGEND + if(pid <= 0 && argc == 0) + Usage(); + if(functions+linenums+registers+stacks+pprof == 0) + histograms = 1; + if(!machbyname("amd64")) { + fprint(2, "prof: no amd64 support\n", pid); + exit(1); + } + if(argc > 0) + file = argv[0]; + else if(pid) { + file = proctextfile(pid); + if (file == NULL) { + fprint(2, "prof: can't find file for pid %d: %r\n", pid); + fprint(2, "prof: on Darwin, need to provide file name explicitly\n"); + exit(1); + } + } + fd = open(file, 0); + if(fd < 0) { + fprint(2, "prof: can't open %s: %r\n", file); + exit(1); + } + if(crackhdr(fd, &fhdr)) { + have_syms = syminit(fd, &fhdr); + if(!have_syms) { + fprint(2, "prof: no symbols for %s: %r\n", file); + } + } else { + fprint(2, "prof: crack header for %s: %r\n", file); + exit(1); + } + if(pid <= 0) + pid = startprocess(argv); + attachproc(pid, &fhdr); // initializes thread list + if(setarch() < 0) { + detach(); + fprint(2, "prof: can't identify binary architecture for pid %d\n", pid); + exit(1); + } + if(getthreads() <= 0) { + detach(); + fprint(2, "prof: can't find threads for pid %d\n", pid); + exit(1); + } + for(i = 0; i < nthread; i++) + ctlproc(thread[i], "start"); + samples(); + detach(); + dumphistogram(); + dumppprof(); + exit(0); +} diff --git a/src/cmd/vet/Makefile b/src/cmd/vet/Makefile index 2a35d1ae3..307f4729c 100644 --- a/src/cmd/vet/Makefile +++ b/src/cmd/vet/Makefile @@ -3,5 +3,6 @@ # license that can be found in the LICENSE file. test testshort: - go build - ../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' print.go + go build -tags vet_test + ../../../test/errchk ./vet -compositewhitelist=false -printfuncs='Warn:1,Warnf:1' *.go + diff --git a/src/cmd/vet/atomic.go b/src/cmd/vet/atomic.go new file mode 100644 index 000000000..4ab256f64 --- /dev/null +++ b/src/cmd/vet/atomic.go @@ -0,0 +1,59 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "go/token" +) + +// checkAtomicAssignment walks the assignment statement checking for common +// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1) +func (f *File) checkAtomicAssignment(n *ast.AssignStmt) { + if !vet("atomic") { + return + } + + if len(n.Lhs) != len(n.Rhs) { + return + } + + for i, right := range n.Rhs { + call, ok := right.(*ast.CallExpr) + if !ok { + continue + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + pkg, ok := sel.X.(*ast.Ident) + if !ok || pkg.Name != "atomic" { + continue + } + + switch sel.Sel.Name { + case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": + f.checkAtomicAddAssignment(n.Lhs[i], call) + } + } +} + +// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value +// to the same variable being used in the operation +func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) { + arg := call.Args[0] + broken := false + + if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { + broken = f.gofmt(left) == f.gofmt(uarg.X) + } else if star, ok := left.(*ast.StarExpr); ok { + broken = f.gofmt(star.X) == f.gofmt(arg) + } + + if broken { + f.Warn(left.Pos(), "direct assignment to atomic value") + } +} diff --git a/src/cmd/vet/buildtag.go b/src/cmd/vet/buildtag.go new file mode 100644 index 000000000..4b7580457 --- /dev/null +++ b/src/cmd/vet/buildtag.go @@ -0,0 +1,91 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "fmt" + "os" + "strings" + "unicode" +) + +var ( + nl = []byte("\n") + slashSlash = []byte("//") + plusBuild = []byte("+build") +) + +// checkBuildTag checks that build tags are in the correct location and well-formed. +func checkBuildTag(name string, data []byte) { + if !vet("buildtags") { + return + } + lines := bytes.SplitAfter(data, nl) + + // Determine cutpoint where +build comments are no longer valid. + // They are valid in leading // comments in the file followed by + // a blank line. + var cutoff int + for i, line := range lines { + line = bytes.TrimSpace(line) + if len(line) == 0 { + cutoff = i + continue + } + if bytes.HasPrefix(line, slashSlash) { + continue + } + break + } + + for i, line := range lines { + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, slashSlash) { + continue + } + text := bytes.TrimSpace(line[2:]) + if bytes.HasPrefix(text, plusBuild) { + fields := bytes.Fields(text) + if !bytes.Equal(fields[0], plusBuild) { + // Comment is something like +buildasdf not +build. + fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) + continue + } + if i >= cutoff { + fmt.Fprintf(os.Stderr, "%s:%d: +build comment appears too late in file\n", name, i+1) + setExit(1) + continue + } + // Check arguments. + Args: + for _, arg := range fields[1:] { + for _, elem := range strings.Split(string(arg), ",") { + if strings.HasPrefix(elem, "!!") { + fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg) + setExit(1) + break Args + } + if strings.HasPrefix(elem, "!") { + elem = elem[1:] + } + for _, c := range elem { + if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' { + fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg) + setExit(1) + break Args + } + } + } + } + continue + } + // Comment with +build but not at beginning. + if bytes.Contains(line, plusBuild) && i < cutoff { + fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) + continue + } + } +} diff --git a/src/cmd/vet/buildtag_bad.go b/src/cmd/vet/buildtag_bad.go new file mode 100644 index 000000000..4dca6a443 --- /dev/null +++ b/src/cmd/vet/buildtag_bad.go @@ -0,0 +1,11 @@ +// This file contains misplaced or malformed build constraints. +// The Go tool will skip it, because the constraints are invalid. +// It serves only to test the tag checker during make test. + +// Mention +build // ERROR "possible malformed \+build comment" + +// +build !!bang // ERROR "invalid double negative in build constraint" +// +build @#$ // ERROR "invalid non-alphanumeric build constraint" + +// +build toolate // ERROR "build comment appears too late in file" +package main diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go index 620964aaf..f164eaca2 100644 --- a/src/cmd/vet/doc.go +++ b/src/cmd/vet/doc.go @@ -9,9 +9,12 @@ calls whose arguments do not align with the format string. Vet uses heuristics that do not guarantee all reports are genuine problems, but it can find errors not caught by the compilers. +By default all checks are performed, but if explicit flags are provided, only +those identified by the flags are performed. + Available checks: -1. Printf family +1. Printf family, flag -printf Suspicious calls to functions in the Printf family, including any functions with these names: @@ -28,24 +31,29 @@ complains about arguments that look like format descriptor strings. It also checks for errors such as using a Writer as the first argument of Printf. -2. Methods +2. Methods, flag -methods Non-standard signatures for methods with familiar names, including: Format GobEncode GobDecode MarshalJSON MarshalXML - Peek ReadByte ReadFrom ReadRune Scan Seek + Peek ReadByte ReadFrom ReadRune Scan Seek UnmarshalJSON UnreadByte UnreadRune WriteByte WriteTo -3. Struct tags +3. Struct tags, flag -structtags Struct tags that do not follow the format understood by reflect.StructTag.Get. +4. Untagged composite literals, flag -composites + +Composite struct literals that do not use the type-tagged syntax. + + Usage: go tool vet [flag] [file.go ...] go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively -The flags are: +The other flags are: -v Verbose mode -printfuncs @@ -59,4 +67,4 @@ The flags are: -printfuncs=Warn:1,Warnf:1 */ -package documentation +package main diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go index 625133315..20f6cca1a 100644 --- a/src/cmd/vet/main.go +++ b/src/cmd/vet/main.go @@ -11,9 +11,12 @@ import ( "flag" "fmt" "go/ast" + "go/build" "go/parser" + "go/printer" "go/token" - "io" + "go/types" + "io/ioutil" "os" "path/filepath" "strconv" @@ -23,6 +26,24 @@ import ( var verbose = flag.Bool("v", false, "verbose") var exitCode = 0 +// Flags to control which checks to perform. "all" is set to true here, and disabled later if +// a flag is set explicitly. +var report = map[string]*bool{ + "all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"), + "atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"), + "buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"), + "composites": flag.Bool("composites", false, "check that composite literals used type-tagged elements"), + "methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"), + "printf": flag.Bool("printf", false, "check printf-like invocations"), + "structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"), + "rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"), +} + +// vet tells whether to report errors for the named check, a flag name. +func vet(name string) bool { + return *report["all"] || *report[name] +} + // setExit sets the value for os.Exit when it is called, later. It // remembers the highest value. func setExit(err int) { @@ -34,6 +55,8 @@ func setExit(err int) { // Usage is a replacement usage function for the flags package. func Usage() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n") + fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n") flag.PrintDefaults() os.Exit(2) } @@ -41,7 +64,9 @@ func Usage() { // File is a wrapper for the state of a file used in the parser. // The parse tree walkers are all methods of this type. type File struct { + pkg *Package fset *token.FileSet + name string file *ast.File b bytes.Buffer // for use by methods } @@ -50,6 +75,14 @@ func main() { flag.Usage = Usage flag.Parse() + // If a check is named explicitly, turn off the 'all' flag. + for name, ptr := range report { + if name != "all" && *ptr { + *report["all"] = false + break + } + } + if *printfuncs != "" { for _, name := range strings.Split(*printfuncs, ",") { if len(name) == 0 { @@ -74,41 +107,135 @@ func main() { } if flag.NArg() == 0 { - doFile("stdin", os.Stdin) - } else { + Usage() + } + dirs := false + files := false + for _, name := range flag.Args() { + // Is it a directory? + fi, err := os.Stat(name) + if err != nil { + warnf("error walking tree: %s", err) + continue + } + if fi.IsDir() { + dirs = true + } else { + files = true + } + } + if dirs && files { + Usage() + } + if dirs { for _, name := range flag.Args() { - // Is it a directory? - if fi, err := os.Stat(name); err == nil && fi.IsDir() { - walkDir(name) - } else { - doFile(name, nil) - } + walkDir(name) } + return } + doPackage(flag.Args()) os.Exit(exitCode) } -// doFile analyzes one file. If the reader is nil, the source code is read from the -// named file. -func doFile(name string, reader io.Reader) { - fs := token.NewFileSet() - parsedFile, err := parser.ParseFile(fs, name, reader, 0) +// prefixDirectory places the directory name on the beginning of each name in the list. +func prefixDirectory(directory string, names []string) { + if directory != "." { + for i, name := range names { + names[i] = filepath.Join(directory, name) + } + } +} + +// doPackageDir analyzes the single package found in the directory, if there is one, +// plus a test package, if there is one. +func doPackageDir(directory string) { + pkg, err := build.Default.ImportDir(directory, 0) if err != nil { - errorf("%s: %s", name, err) + // If it's just that there are no go source files, that's fine. + if _, nogo := err.(*build.NoGoError); nogo { + return + } + // Non-fatal: we are doing a recursive walk and there may be other directories. + warnf("cannot process directory %s: %s", directory, err) return } - file := &File{fset: fs, file: parsedFile} - file.walkFile(name, parsedFile) + var names []string + names = append(names, pkg.GoFiles...) + names = append(names, pkg.CgoFiles...) + names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package. + prefixDirectory(directory, names) + doPackage(names) + // Is there also a "foo_test" package? If so, do that one as well. + if len(pkg.XTestGoFiles) > 0 { + names = pkg.XTestGoFiles + prefixDirectory(directory, names) + doPackage(names) + } +} + +type Package struct { + types map[ast.Expr]types.Type + values map[ast.Expr]interface{} +} + +// doPackage analyzes the single package constructed from the named files. +func doPackage(names []string) { + var files []*File + var astFiles []*ast.File + fs := token.NewFileSet() + for _, name := range names { + f, err := os.Open(name) + if err != nil { + errorf("%s: %s", name, err) + } + defer f.Close() + data, err := ioutil.ReadAll(f) + if err != nil { + errorf("%s: %s", name, err) + } + checkBuildTag(name, data) + parsedFile, err := parser.ParseFile(fs, name, bytes.NewReader(data), 0) + if err != nil { + errorf("%s: %s", name, err) + } + files = append(files, &File{fset: fs, name: name, file: parsedFile}) + astFiles = append(astFiles, parsedFile) + } + pkg := new(Package) + pkg.types = make(map[ast.Expr]types.Type) + pkg.values = make(map[ast.Expr]interface{}) + exprFn := func(x ast.Expr, typ types.Type, val interface{}) { + pkg.types[x] = typ + if val != nil { + pkg.values[x] = val + } + } + // By providing the Context with our own error function, it will continue + // past the first error. There is no need for that function to do anything. + context := types.Context{ + Expr: exprFn, + Error: func(error) {}, + } + // Type check the package. + _, err := context.Check(fs, astFiles) + if err != nil && *verbose { + warnf("%s", err) + } + for _, file := range files { + file.pkg = pkg + file.walkFile(file.name, file.file) + } } func visit(path string, f os.FileInfo, err error) error { if err != nil { errorf("walk error: %s", err) - return nil } - if !f.IsDir() && strings.HasSuffix(path, ".go") { - doFile(path, nil) + // One package per directory. Ignore the files themselves. + if !f.IsDir() { + return nil } + doPackageDir(path) return nil } @@ -117,11 +244,18 @@ func walkDir(root string) { filepath.Walk(root, visit) } -// error formats the error to standard error, adding program -// identification and a newline +// errorf formats the error to standard error, adding program +// identification and a newline, and exits. func errorf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) - setExit(2) + os.Exit(2) +} + +// warnf formats the error to standard error, adding program +// identification and a newline, but does not exit. +func warnf(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) + setExit(1) } // Println is fmt.Println guarded by -v. @@ -152,16 +286,22 @@ func (f *File) Badf(pos token.Pos, format string, args ...interface{}) { setExit(1) } +func (f *File) loc(pos token.Pos) string { + // Do not print columns. Because the pos often points to the start of an + // expression instead of the inner part with the actual error, the + // precision can mislead. + posn := f.fset.Position(pos) + return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line) +} + // Warn reports an error but does not set the exit code. func (f *File) Warn(pos token.Pos, args ...interface{}) { - loc := f.fset.Position(pos).String() + ": " - fmt.Fprint(os.Stderr, loc+fmt.Sprintln(args...)) + fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...)) } // Warnf reports a formatted error but does not set the exit code. func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) { - loc := f.fset.Position(pos).String() + ": " - fmt.Fprintf(os.Stderr, loc+format+"\n", args...) + fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...) } // walkFile walks the file's tree. @@ -173,6 +313,8 @@ func (f *File) walkFile(name string, file *ast.File) { // Visit implements the ast.Visitor interface. func (f *File) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { + case *ast.AssignStmt: + f.walkAssignStmt(n) case *ast.CallExpr: f.walkCallExpr(n) case *ast.CompositeLit: @@ -183,15 +325,32 @@ func (f *File) Visit(node ast.Node) ast.Visitor { f.walkMethodDecl(n) case *ast.InterfaceType: f.walkInterfaceType(n) + case *ast.RangeStmt: + f.walkRangeStmt(n) } return f } +// walkAssignStmt walks an assignment statement +func (f *File) walkAssignStmt(stmt *ast.AssignStmt) { + f.checkAtomicAssignment(stmt) +} + // walkCall walks a call expression. func (f *File) walkCall(call *ast.CallExpr, name string) { f.checkFmtPrintfCall(call, name) } +// walkCallExpr walks a call expression. +func (f *File) walkCallExpr(call *ast.CallExpr) { + switch x := call.Fun.(type) { + case *ast.Ident: + f.walkCall(call, x.Name) + case *ast.SelectorExpr: + f.walkCall(call, x.Sel.Name) + } +} + // walkCompositeLit walks a composite literal. func (f *File) walkCompositeLit(c *ast.CompositeLit) { f.checkUntaggedLiteral(c) @@ -228,12 +387,14 @@ func (f *File) walkInterfaceType(t *ast.InterfaceType) { } } -// walkCallExpr walks a call expression. -func (f *File) walkCallExpr(call *ast.CallExpr) { - switch x := call.Fun.(type) { - case *ast.Ident: - f.walkCall(call, x.Name) - case *ast.SelectorExpr: - f.walkCall(call, x.Sel.Name) - } +// walkRangeStmt walks a range statement. +func (f *File) walkRangeStmt(n *ast.RangeStmt) { + checkRangeLoop(f, n) +} + +// gofmt returns a string representation of the expression. +func (f *File) gofmt(x ast.Expr) string { + f.b.Reset() + printer.Fprint(&f.b, f.fset, x) + return f.b.String() } diff --git a/src/cmd/vet/method.go b/src/cmd/vet/method.go index 41cb40ff9..bf982dc7a 100644 --- a/src/cmd/vet/method.go +++ b/src/cmd/vet/method.go @@ -55,6 +55,9 @@ var canonicalMethods = map[string]MethodSig{ } func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) { + if !vet("methods") { + return + } // Expected input/output. expect, ok := canonicalMethods[id.Name] if !ok { @@ -87,9 +90,7 @@ func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) { fmt.Fprintf(&f.b, "<%s>", err) } actual := f.b.String() - if strings.HasPrefix(actual, "func(") { - actual = actual[4:] - } + actual = strings.TrimPrefix(actual, "func") actual = id.Name + actual f.Warnf(id.Pos(), "method %s should have signature %s", actual, expectFmt) diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go index ee9a33c70..fb0fb9f9b 100644 --- a/src/cmd/vet/print.go +++ b/src/cmd/vet/print.go @@ -8,9 +8,10 @@ package main import ( "flag" - "fmt" "go/ast" "go/token" + "go/types" + "strconv" "strings" "unicode/utf8" ) @@ -43,6 +44,9 @@ var printList = map[string]int{ // checkCall triggers the print-specific checks if the call invokes a print function. func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) { + if !vet("printf") { + return + } name := strings.ToLower(Name) if skip, ok := printfList[name]; ok { f.checkPrintf(call, Name, skip) @@ -54,73 +58,134 @@ func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) { } } +// literal returns the literal value represented by the expression, or nil if it is not a literal. +func (f *File) literal(value ast.Expr) *ast.BasicLit { + switch v := value.(type) { + case *ast.BasicLit: + return v + case *ast.ParenExpr: + return f.literal(v.X) + case *ast.BinaryExpr: + if v.Op != token.ADD { + break + } + litX := f.literal(v.X) + litY := f.literal(v.Y) + if litX != nil && litY != nil { + lit := *litX + x, errX := strconv.Unquote(litX.Value) + y, errY := strconv.Unquote(litY.Value) + if errX == nil && errY == nil { + return &ast.BasicLit{ + ValuePos: lit.ValuePos, + Kind: lit.Kind, + Value: strconv.Quote(x + y), + } + } + } + case *ast.Ident: + // See if it's a constant or initial value (we can't tell the difference). + if v.Obj == nil || v.Obj.Decl == nil { + return nil + } + valueSpec, ok := v.Obj.Decl.(*ast.ValueSpec) + if ok && len(valueSpec.Names) == len(valueSpec.Values) { + // Find the index in the list of names + var i int + for i = 0; i < len(valueSpec.Names); i++ { + if valueSpec.Names[i].Name == v.Name { + if lit, ok := valueSpec.Values[i].(*ast.BasicLit); ok { + return lit + } + return nil + } + } + } + } + return nil +} + // checkPrintf checks a call to a formatted print routine such as Printf. -// The skip argument records how many arguments to ignore; that is, -// call.Args[skip] is (well, should be) the format argument. -func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) { - if len(call.Args) <= skip { +// call.Args[formatIndex] is (well, should be) the format argument. +func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) { + if formatIndex >= len(call.Args) { return } - // Common case: literal is first argument. - arg := call.Args[skip] - lit, ok := arg.(*ast.BasicLit) - if !ok { - // Too hard to check. + lit := f.literal(call.Args[formatIndex]) + if lit == nil { if *verbose { f.Warn(call.Pos(), "can't check non-literal format in call to", name) } return } - if lit.Kind == token.STRING { - if !strings.Contains(lit.Value, "%") { - if len(call.Args) > skip+1 { - f.Badf(call.Pos(), "no formatting directive in %s call", name) - } - return + if lit.Kind != token.STRING { + f.Badf(call.Pos(), "literal %v not a string in call to", lit.Value, name) + } + format, err := strconv.Unquote(lit.Value) + if err != nil { + // Shouldn't happen if parser returned no errors, but be safe. + f.Badf(call.Pos(), "invalid quoted string literal") + } + firstArg := formatIndex + 1 // Arguments are immediately after format string. + if !strings.Contains(format, "%") { + if len(call.Args) > firstArg { + f.Badf(call.Pos(), "no formatting directive in %s call", name) } + return } // Hard part: check formats against args. - // Trivial but useful test: count. - numArgs := 0 - for i, w := 0, 0; i < len(lit.Value); i += w { + argNum := firstArg + for i, w := 0, 0; i < len(format); i += w { w = 1 - if lit.Value[i] == '%' { - nbytes, nargs := f.parsePrintfVerb(call, lit.Value[i:]) + if format[i] == '%' { + verb, flags, nbytes, nargs := f.parsePrintfVerb(call, format[i:]) w = nbytes - numArgs += nargs + if verb == '%' { // "%%" does nothing interesting. + continue + } + // If we've run out of args, print after loop will pick that up. + if argNum+nargs <= len(call.Args) { + f.checkPrintfArg(call, verb, flags, argNum, nargs) + } + argNum += nargs } } - expect := len(call.Args) - (skip + 1) - if numArgs != expect { - f.Badf(call.Pos(), "wrong number of args in %s call: %d needed but %d args", name, numArgs, expect) + // TODO: Dotdotdot is hard. + if call.Ellipsis.IsValid() && argNum != len(call.Args) { + return + } + if argNum != len(call.Args) { + expect := argNum - firstArg + numArgs := len(call.Args) - firstArg + f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) } } -// parsePrintfVerb returns the number of bytes and number of arguments -// consumed by the Printf directive that begins s, including its percent sign -// and verb. -func (f *File) parsePrintfVerb(call *ast.CallExpr, s string) (nbytes, nargs int) { +// parsePrintfVerb returns the verb that begins the format string, along with its flags, +// the number of bytes to advance the format to step past the verb, and number of +// arguments it consumes. +func (f *File) parsePrintfVerb(call *ast.CallExpr, format string) (verb rune, flags []byte, nbytes, nargs int) { // There's guaranteed a percent sign. - flags := make([]byte, 0, 5) + flags = make([]byte, 0, 5) nbytes = 1 - end := len(s) + end := len(format) // There may be flags. FlagLoop: for nbytes < end { - switch s[nbytes] { + switch format[nbytes] { case '#', '0', '+', '-', ' ': - flags = append(flags, s[nbytes]) + flags = append(flags, format[nbytes]) nbytes++ default: break FlagLoop } } getNum := func() { - if nbytes < end && s[nbytes] == '*' { + if nbytes < end && format[nbytes] == '*' { nbytes++ nargs++ } else { - for nbytes < end && '0' <= s[nbytes] && s[nbytes] <= '9' { + for nbytes < end && '0' <= format[nbytes] && format[nbytes] <= '9' { nbytes++ } } @@ -128,24 +193,38 @@ FlagLoop: // There may be a width. getNum() // If there's a period, there may be a precision. - if nbytes < end && s[nbytes] == '.' { + if nbytes < end && format[nbytes] == '.' { flags = append(flags, '.') // Treat precision as a flag. nbytes++ getNum() } // Now a verb. - c, w := utf8.DecodeRuneInString(s[nbytes:]) + c, w := utf8.DecodeRuneInString(format[nbytes:]) nbytes += w + verb = c if c != '%' { nargs++ - f.checkPrintfVerb(call, c, flags) } return } +// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. +type printfArgType int + +const ( + argBool printfArgType = 1 << iota + argInt + argRune + argString + argFloat + argPointer + anyType printfArgType = ^0 +) + type printVerb struct { verb rune flags string // known flags are all ASCII + typ printfArgType } // Common flag sets for printf verbs. @@ -164,36 +243,57 @@ var printVerbs = []printVerb{ // '+' is required sign for numbers, Go format for %v. // '#' is alternate format for several verbs. // ' ' is spacer for numbers - {'b', numFlag}, - {'c', "-"}, - {'d', numFlag}, - {'e', numFlag}, - {'E', numFlag}, - {'f', numFlag}, - {'F', numFlag}, - {'g', numFlag}, - {'G', numFlag}, - {'o', sharpNumFlag}, - {'p', "-#"}, - {'q', "-+#."}, - {'s', "-."}, - {'t', "-"}, - {'T', "-"}, - {'U', "-#"}, - {'v', allFlags}, - {'x', sharpNumFlag}, - {'X', sharpNumFlag}, + {'b', numFlag, argInt | argFloat}, + {'c', "-", argRune | argInt}, + {'d', numFlag, argInt}, + {'e', numFlag, argFloat}, + {'E', numFlag, argFloat}, + {'f', numFlag, argFloat}, + {'F', numFlag, argFloat}, + {'g', numFlag, argFloat}, + {'G', numFlag, argFloat}, + {'o', sharpNumFlag, argInt}, + {'p', "-#", argPointer}, + {'q', " -+.0#", argRune | argInt | argString}, + {'s', " -+.0", argString}, + {'t', "-", argBool}, + {'T', "-", anyType}, + {'U', "-#", argRune | argInt}, + {'v', allFlags, anyType}, + {'x', sharpNumFlag, argRune | argInt | argString}, + {'X', sharpNumFlag, argRune | argInt | argString}, } const printfVerbs = "bcdeEfFgGopqstTvxUX" -func (f *File) checkPrintfVerb(call *ast.CallExpr, verb rune, flags []byte) { +func (f *File) checkPrintfArg(call *ast.CallExpr, verb rune, flags []byte, argNum, nargs int) { // Linear scan is fast enough for a small list. for _, v := range printVerbs { if v.verb == verb { for _, flag := range flags { if !strings.ContainsRune(v.flags, rune(flag)) { f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", verb, flag) + return + } + } + // Verb is good. If nargs>1, we have something like %.*s and all but the final + // arg must be integer. + for i := 0; i < nargs-1; i++ { + if !f.matchArgType(argInt, call.Args[argNum+i]) { + f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(call.Args[argNum+i])) + } + } + for _, v := range printVerbs { + if v.verb == verb { + arg := call.Args[argNum+nargs-1] + if !f.matchArgType(v.typ, arg) { + typeString := "" + if typ := f.pkg.types[arg]; typ != nil { + typeString = typ.String() + } + f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), verb, typeString) + } + break } } return @@ -202,15 +302,67 @@ func (f *File) checkPrintfVerb(call *ast.CallExpr, verb rune, flags []byte) { f.Badf(call.Pos(), "unrecognized printf verb %q", verb) } +func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool { + // TODO: for now, we can only test builtin types and untyped constants. + typ := f.pkg.types[arg] + if typ == nil { + return true + } + basic, ok := typ.(*types.Basic) + if !ok { + return true + } + switch basic.Kind { + case types.Bool: + return t&argBool != 0 + case types.Int, types.Int8, types.Int16, types.Int32, types.Int64: + fallthrough + case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: + return t&argInt != 0 + case types.Float32, types.Float64, types.Complex64, types.Complex128: + return t&argFloat != 0 + case types.String: + return t&argString != 0 + case types.UnsafePointer: + return t&(argPointer|argInt) != 0 + case types.UntypedBool: + return t&argBool != 0 + case types.UntypedComplex: + return t&argFloat != 0 + case types.UntypedFloat: + // If it's integral, we can use an int format. + switch f.pkg.values[arg].(type) { + case int, int8, int16, int32, int64: + return t&(argInt|argFloat) != 0 + case uint, uint8, uint16, uint32, uint64: + return t&(argInt|argFloat) != 0 + } + return t&argFloat != 0 + case types.UntypedInt: + return t&argInt != 0 + case types.UntypedRune: + return t&(argInt|argRune) != 0 + case types.UntypedString: + return t&argString != 0 + case types.UntypedNil: + return t&argPointer != 0 // TODO? + case types.Invalid: + if *verbose { + f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg) + } + return true // Probably a type check problem. + } + return false +} + // checkPrint checks a call to an unformatted print routine such as Println. -// The skip argument records how many arguments to ignore; that is, -// call.Args[skip] is the first argument to be printed. -func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { +// call.Args[firstArg] is the first argument to be printed. +func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) { isLn := strings.HasSuffix(name, "ln") isF := strings.HasPrefix(name, "F") args := call.Args // check for Println(os.Stderr, ...) - if skip == 0 && !isF && len(args) > 0 { + if firstArg == 0 && !isF && len(args) > 0 { if sel, ok := args[0].(*ast.SelectorExpr); ok { if x, ok := sel.X.(*ast.Ident); ok { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { @@ -219,13 +371,23 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { } } } - if len(args) <= skip { - if *verbose && !isLn { - f.Badf(call.Pos(), "no args in %s call", name) + if len(args) <= firstArg { + // If we have a call to a method called Error that satisfies the Error interface, + // then it's ok. Otherwise it's something like (*T).Error from the testing package + // and we need to check it. + if name == "Error" && f.isErrorMethodCall(call) { + return + } + // If it's an Error call now, it's probably for printing errors. + if !isLn { + // Check the signature to be sure: there are niladic functions called "error". + if firstArg != 0 || f.numArgsInSignature(call) != firstArg { + f.Badf(call.Pos(), "no args in %s call", name) + } } return } - arg := args[skip] + arg := args[firstArg] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.Contains(lit.Value, "%") { f.Badf(call.Pos(), "possible formatting directive in %s call", name) @@ -242,37 +404,63 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { } } -// This function never executes, but it serves as a simple test for the program. -// Test with make test. -func BadFunctionUsedInTests() { - fmt.Println() // not an error - fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" - fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args in Printf call" - fmt.Printf("%s%%%d", "hi", 3) // correct - fmt.Printf("%.*d", 3, 3) // correct - fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args in Printf call" - printf("now is the time", "buddy") // ERROR "no formatting directive" - Printf("now is the time", "buddy") // ERROR "no formatting directive" - Printf("hi") // ok - f := new(File) - f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" - f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args in Warnf call" - f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb" - f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag" -} - -type BadTypeUsedInTests struct { - X int "hello" // ERROR "struct field tag" -} - -func (t *BadTypeUsedInTests) Scan(x fmt.ScanState, c byte) { // ERROR "method Scan[(]x fmt.ScanState, c byte[)] should have signature Scan[(]fmt.ScanState, rune[)] error" -} - -type BadInterfaceUsedInTests interface { - ReadByte() byte // ERROR "method ReadByte[(][)] byte should have signature ReadByte[(][)] [(]byte, error[)]" +// numArgsInSignature tells how many formal arguments the function type +// being called has. +func (f *File) numArgsInSignature(call *ast.CallExpr) int { + // Check the type of the function or method declaration + typ := f.pkg.types[call.Fun] + if typ == nil { + return 0 + } + // The type must be a signature, but be sure for safety. + sig, ok := typ.(*types.Signature) + if !ok { + return 0 + } + return len(sig.Params) } -// printf is used by the test. -func printf(format string, args ...interface{}) { - panic("don't call - testing only") +// isErrorMethodCall reports whether the call is of a method with signature +// func Error() string +// where "string" is the universe's string type. We know the method is called "Error". +func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { + // Is it a selector expression? Otherwise it's a function call, not a method call. + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return false + } + // The package is type-checked, so if there are no arguments, we're done. + if len(call.Args) > 0 { + return false + } + // Check the type of the method declaration + typ := f.pkg.types[sel] + if typ == nil { + return false + } + // The type must be a signature, but be sure for safety. + sig, ok := typ.(*types.Signature) + if !ok { + return false + } + // There must be a receiver for it to be a method call. Otherwise it is + // a function, not something that satisfies the error interface. + if sig.Recv == nil { + return false + } + // There must be no arguments. Already verified by type checking, but be thorough. + if len(sig.Params) > 0 { + return false + } + // Finally the real questions. + // There must be one result. + if len(sig.Results) != 1 { + return false + } + // It must have return type "string" from the universe. + result := sig.Results[0].Type + if types.IsIdentical(result, types.Typ[types.String]) { + return true + } + return false } diff --git a/src/cmd/vet/rangeloop.go b/src/cmd/vet/rangeloop.go new file mode 100644 index 000000000..ecc595427 --- /dev/null +++ b/src/cmd/vet/rangeloop.go @@ -0,0 +1,65 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +This file contains the code to check range loop variables bound inside function +literals that are deferred or launched in new goroutines. We only check +instances where the defer or go statement is the last statement in the loop +body, as otherwise we would need whole program analysis. + +For example: + + for i, v := range s { + go func() { + println(i, v) // not what you might expect + }() + } + +See: http://golang.org/doc/go_faq.html#closures_and_goroutines +*/ + +package main + +import "go/ast" + +// checkRangeLoop walks the body of the provided range statement, checking if +// its index or value variables are used unsafely inside goroutines or deferred +// function literals. +func checkRangeLoop(f *File, n *ast.RangeStmt) { + if !vet("rangeloops") { + return + } + key, _ := n.Key.(*ast.Ident) + val, _ := n.Value.(*ast.Ident) + if key == nil && val == nil { + return + } + sl := n.Body.List + if len(sl) == 0 { + return + } + var last *ast.CallExpr + switch s := sl[len(sl)-1].(type) { + case *ast.GoStmt: + last = s.Call + case *ast.DeferStmt: + last = s.Call + default: + return + } + lit, ok := last.Fun.(*ast.FuncLit) + if !ok { + return + } + ast.Inspect(lit.Body, func(n ast.Node) bool { + id, ok := n.(*ast.Ident) + if !ok || id.Obj == nil { + return true + } + if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj { + f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function") + } + return true + }) +} diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go index ea2a9d863..545e420c1 100644 --- a/src/cmd/vet/structtag.go +++ b/src/cmd/vet/structtag.go @@ -14,6 +14,9 @@ import ( // checkField checks a struct field tag. func (f *File) checkCanonicalFieldTag(field *ast.Field) { + if !vet("structtags") { + return + } if field.Tag == nil { return } diff --git a/src/cmd/vet/taglit.go b/src/cmd/vet/taglit.go index c3c4f3234..0324e37b0 100644 --- a/src/cmd/vet/taglit.go +++ b/src/cmd/vet/taglit.go @@ -7,13 +7,38 @@ package main import ( + "flag" "go/ast" + "go/types" "strings" ) +var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only") + // checkUntaggedLiteral checks if a composite literal is an struct literal with // untagged fields. func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) { + if !vet("composites") { + return + } + + // Check that the CompositeLit's type is a slice or array (which needs no tag), if possible. + typ := f.pkg.types[c] + if typ != nil { + // If it's a named type, pull out the underlying type. + if namedType, ok := typ.(*types.NamedType); ok { + typ = namedType.Underlying + } + switch typ.(type) { + case *types.Slice: + return + case *types.Array: + return + } + } + + // It's a struct, or we can't tell it's not a struct because we don't have types. + // Check if the CompositeLit contains an untagged field. allKeyValue := true for _, e := range c.Elts { @@ -42,12 +67,12 @@ func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) { f.Warnf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name) return } - typ := path + "." + s.Sel.Name - if untaggedLiteralWhitelist[typ] { + typeName := path + "." + s.Sel.Name + if *compositeWhiteList && untaggedLiteralWhitelist[typeName] { return } - f.Warnf(c.Pos(), "%s struct literal uses untagged fields", typ) + f.Warnf(c.Pos(), "%s composite literal uses untagged fields", typ) } // pkgPath returns the import path "image/png" for the package name "png". diff --git a/src/cmd/vet/test_atomic.go b/src/cmd/vet/test_atomic.go new file mode 100644 index 000000000..9231e9dc0 --- /dev/null +++ b/src/cmd/vet/test_atomic.go @@ -0,0 +1,43 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build vet_test + +// This file contains tests for the atomic checker. + +package main + +import ( + "sync/atomic" +) + +type Counter uint64 + +func AtomicTests() { + x := uint64(1) + x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" + _, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" + x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value" + + y := &x + *y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value" + + var su struct{ Counter uint64 } + su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value" + z1 := atomic.AddUint64(&su.Counter, 1) + _ = z1 // Avoid err "z declared and not used" + + var sp struct{ Counter *uint64 } + *sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value" + z2 := atomic.AddUint64(sp.Counter, 1) + _ = z2 // Avoid err "z declared and not used" + + au := []uint64{10, 20} + au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value" + au[1] = atomic.AddUint64(&au[0], 1) + + ap := []*uint64{&au[0], &au[1]} + *ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value" + *ap[1] = atomic.AddUint64(ap[0], 1) +} diff --git a/src/cmd/vet/test_buildtag.go b/src/cmd/vet/test_buildtag.go new file mode 100644 index 000000000..d7174ade2 --- /dev/null +++ b/src/cmd/vet/test_buildtag.go @@ -0,0 +1,15 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains tests for the buildtag checker. + +// +build vet_test +// +builder // ERROR "possible malformed \+build comment" +// +build !ignore + +package main + +// +build toolate // ERROR "build comment appears too late in file" + +var _ = 3 diff --git a/src/cmd/vet/test_method.go b/src/cmd/vet/test_method.go new file mode 100644 index 000000000..41de62bb1 --- /dev/null +++ b/src/cmd/vet/test_method.go @@ -0,0 +1,24 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains tests for the canonical method checker. + +// +build vet_test + +// This file contains the code to check canonical methods. + +package main + +import ( + "fmt" +) + +type MethodTest int + +func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan" +} + +type MethodTestInterface interface { + ReadByte() byte // ERROR "should have signature ReadByte" +} diff --git a/src/cmd/vet/test_print.go b/src/cmd/vet/test_print.go new file mode 100644 index 000000000..8b41e6c69 --- /dev/null +++ b/src/cmd/vet/test_print.go @@ -0,0 +1,153 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build vet_test + +// This file contains tests for the printf checker. + +package main + +import ( + "fmt" + "unsafe" // just for test case printing unsafe.Pointer +) + +func UnsafePointerPrintfTest() { + var up unsafe.Pointer + fmt.Printf("%p, %x %X", up, up, up) +} + +// Error methods that do not satisfy the Error interface and should be checked. +type errorTest1 int + +func (errorTest1) Error(...interface{}) string { + return "hi" +} + +type errorTest2 int // Analogous to testing's *T type. +func (errorTest2) Error(...interface{}) { +} + +type errorTest3 int + +func (errorTest3) Error() { // No return value. +} + +type errorTest4 int + +func (errorTest4) Error() int { // Different return type. + return 3 +} + +type errorTest5 int + +func (errorTest5) error() { // niladic; don't complain if no args (was bug) +} + +// This function never executes, but it serves as a simple test for the program. +// Test with make test. +func PrintfTests() { + var b bool + var i int + var r rune + var s string + var x float64 + var p *int + // Some good format/argtypes + fmt.Printf("") + fmt.Printf("%b %b", 3, i) + fmt.Printf("%c %c %c %c", 3, i, 'x', r) + fmt.Printf("%d %d", 3, i) + fmt.Printf("%e %e", 3e9, x) + fmt.Printf("%E %E", 3e9, x) + fmt.Printf("%f %f", 3e9, x) + fmt.Printf("%F %F", 3e9, x) + fmt.Printf("%g %g", 3e9, x) + fmt.Printf("%G %G", 3e9, x) + fmt.Printf("%o %o", 3, i) + fmt.Printf("%p %p", p, nil) + fmt.Printf("%q %q %q %q", 3, i, 'x', r) + fmt.Printf("%s %s", "hi", s) + fmt.Printf("%t %t", true, b) + fmt.Printf("%T %T", 3, i) + fmt.Printf("%U %U", 3, i) + fmt.Printf("%v %v", 3, i) + fmt.Printf("%x %x %x %x", 3, i, "hi", s) + fmt.Printf("%X %X %X %X", 3, i, "hi", s) + fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3) + // Some bad format/argTypes + fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type" + fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type" + fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type" + fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type" + fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type" + fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type" + fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type" + fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type" + fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type" + fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type" + fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type" + fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type" + fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type" + fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type" + fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type" + fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type" + fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type" + fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type" + // TODO + fmt.Println() // not an error + fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" + fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call" + fmt.Printf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Printf call" + fmt.Printf("%s%%%d", "hi", 3) // correct + fmt.Printf("%08s", "woo") // correct + fmt.Printf("% 8s", "woo") // correct + fmt.Printf("%.*d", 3, 3) // correct + fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args for format in Printf call" + fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int" + fmt.Printf("%.*d", i, 3) // correct + fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int" + fmt.Printf("%q %q", multi()...) // ok + fmt.Printf("%#q", `blah`) // ok + printf("now is the time", "buddy") // ERROR "no formatting directive" + Printf("now is the time", "buddy") // ERROR "no formatting directive" + Printf("hi") // ok + const format = "%s %s\n" + Printf(format, "hi", "there") + Printf(format, "hi") // ERROR "wrong number of args for format in Printf call" + f := new(File) + f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" + f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call" + f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb" + f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag" + // Something that satisfies the error interface. + var e error + fmt.Println(e.Error()) // ok + // Something that looks like an error interface but isn't, such as the (*T).Error method + // in the testing package. + var et1 errorTest1 + fmt.Println(et1.Error()) // ERROR "no args in Error call" + fmt.Println(et1.Error("hi")) // ok + fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call" + var et2 errorTest2 + et2.Error() // ERROR "no args in Error call" + et2.Error("hi") // ok, not an error method. + et2.Error("%d", 3) // ERROR "possible formatting directive in Error call" + var et3 errorTest3 + et3.Error() // ok, not an error method. + var et4 errorTest4 + et4.Error() // ok, not an error method. + var et5 errorTest5 + et5.error() // ok, not an error method. +} + +// printf is used by the test. +func printf(format string, args ...interface{}) { + panic("don't call - testing only") +} + +// multi is used by the test. +func multi() []interface{} { + panic("don't call - testing only") +} diff --git a/src/cmd/vet/test_rangeloop.go b/src/cmd/vet/test_rangeloop.go new file mode 100644 index 000000000..941fd72aa --- /dev/null +++ b/src/cmd/vet/test_rangeloop.go @@ -0,0 +1,61 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains tests for the rangeloop checker. + +// +build vet_test + +package main + +func RangeLoopTests() { + var s []int + for i, v := range s { + go func() { + println(i) // ERROR "range variable i enclosed by function" + println(v) // ERROR "range variable v enclosed by function" + }() + } + for i, v := range s { + defer func() { + println(i) // ERROR "range variable i enclosed by function" + println(v) // ERROR "range variable v enclosed by function" + }() + } + for i := range s { + go func() { + println(i) // ERROR "range variable i enclosed by function" + }() + } + for _, v := range s { + go func() { + println(v) // ERROR "range variable v enclosed by function" + }() + } + for i, v := range s { + go func() { + println(i, v) + }() + println("unfortunately, we don't catch the error above because of this statement") + } + for i, v := range s { + go func(i, v int) { + println(i, v) + }(i, v) + } + for i, v := range s { + i, v := i, v + go func() { + println(i, v) + }() + } + // If the key of the range statement is not an identifier + // the code should not panic (it used to). + var x [2]int + var f int + for x[0], f = range s { + go func() { + _ = f // ERROR "range variable f enclosed by function" + }() + } +} diff --git a/src/cmd/vet/test_structtag.go b/src/cmd/vet/test_structtag.go new file mode 100644 index 000000000..08cf737fd --- /dev/null +++ b/src/cmd/vet/test_structtag.go @@ -0,0 +1,15 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains tests for the structtag checker. + +// +build vet_test + +// This file contains the test for canonical struct tags. + +package main + +type StructTagTest struct { + X int "hello" // ERROR "not compatible with reflect.StructTag.Get" +} diff --git a/src/cmd/vet/test_taglit.go b/src/cmd/vet/test_taglit.go new file mode 100644 index 000000000..0d83b18fd --- /dev/null +++ b/src/cmd/vet/test_taglit.go @@ -0,0 +1,31 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains tests for the untagged struct literal checker. + +// +build vet_test + +// This file contains the test for untagged struct literals. + +package main + +import ( + "flag" + "go/scanner" +) + +// Testing is awkward because we need to reference things from a separate package +// to trigger the warnings. + +var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "untagged fields" + "Name", + "Usage", + nil, // Value + "DefValue", +} + +// Used to test the check for slices and arrays: If that test is disabled and +// vet is run with --compositewhitelist=false, this line triggers an error. +// Clumsy but sufficient. +var scannerErrorListTest = scanner.ErrorList{nil, nil} diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go index 4a2c2a314..792c104e3 100644 --- a/src/cmd/yacc/doc.go +++ b/src/cmd/yacc/doc.go @@ -48,4 +48,4 @@ referenced by yacc's generated code. Setting it to distinct values allows multiple grammars to be placed in a single package. */ -package documentation +package main diff --git a/src/cmd/yacc/units.txt b/src/cmd/yacc/units.txt index ddb2bc294..df8f567d9 100644 --- a/src/cmd/yacc/units.txt +++ b/src/cmd/yacc/units.txt @@ -215,58 +215,58 @@ lumen cd sr lux cd sr/m² / MONEY DATE -/ Thu Sep 10 2009 - -argentinapeso 1 | 0.2595 $ -australiadollar 1 | 0.8618 $ -boliviaboliviano 1 | 0.1425 $ -brazilreal 1 | 0.5522 $ -britainpound 1 | 1.6651 $ -canadadollar 1 | 0.9277 $ -chilepeso 1 | 0.0018 $ -chinayuan 1 | 0.1464 $ -colombiapeso 1 | 0.0005 $ -czechkoruna 1 | 0.0572 $ -denmarkkrone 1 | 0.1958 $ -dominicanpeso 1 | 0.0278 $ -egyptpound 1 | 0.181 $ -elsalvadorcolon 1 | 0.1143 $ -europeuro 1 | 1.4577 $ -guatemalaquetzal 1 | 0.121 $ -honduraslempira 1 | 0.0529 $ -hongkongdollar 1 | 0.129 $ -hungaryforint 1 | 0.0054 $ -indiarupee 1 | 0.0207 $ -indonesiarupiah 1 | 0.0001 $ -israelshekel 1 | 0.2643 $ -japanyen 1 | 0.0109 $ -kenyashilling 1 | 0.0132 $ -kuwaitdinar 1 | 3.4854 $ -lebanonpound 1 | 0.0007 $ -malaysiaringgit 1 | 0.286 $ -mexicopeso 1 | 0.0748 $ -newzealanddollar 1 | 0.7028 $ -nicaraguacordoba 1 | 0.0487 $ -norwaykrone 1 | 0.1681 $ -pakistanrupee 1 | 0.0121 $ -paraguayguarani 1 | 0.0002 $ -perunewsol 1 | 0.3384 $ -philippinespeso 1 | 0.0207 $ -polandzloty 1 | 0.352 $ -russiaruble 1 | 0.0324 $ -saudiarabiariyal 1 | 0.2666 $ -singaporedollar 1 | 0.7018 $ -slovakkoruna 1 | 0.0484 $ -southafricarand 1 | 0.132 $ -southkoreawon 1 | 0.0008 $ -swedenkrona 1 | 0.1429 $ -switzerlandfranc 1 | 0.9627 $ -taiwandollar 1 | 0.0306 $ -thailandbaht 1 | 0.0294 $ -turkeynewlira 1 | 0.6678 $ -uaedirham 1 | 0.2722 $ -uruguaynewpeso 1 | 0.0451 $ -vietnamdong 1 | 0.0001 $ +/ Wed Aug 29, 2012 + +argentinapeso $ 0.2160 +australiadollar $ 1.0372 +boliviaboliviano $ 0.1427 +brazilreal $ 0.4872 +britainpound $ 1.5843 +canadadollar $ 1.0117 +chilepeso $ 1 | 480.6 +chinayuan $ 0.1574 +colombiapeso $ 1 | 1834 +czechkoruna $ 0.0506 +denmarkkrone $ 0.1681 +dominicanpeso $ 0.0256 +egyptpound $ 0.1640 +elsalvadorcolon $ 1 | 8.75 +europeuro $ 1.2528 +guatemalaquetzal $ 0.1290 +honduraslempira $ 0.0511 +hongkongdollar $ 0.1289 +hungaryforint $ 1 | 226.5 +indiarupee $ 0.0180 +indonesiarupiah $ 1 | 9540 +israelshekel $ 0.2479 +japanyen $ 0.0127 +kenyashilling $ 0.0119 +kuwaitdinar $ 3.5456 +lebanonpound $ 1 | 1505.5 +malaysiaringgit $ 0.3204 +mexicopeso $ 0.0754 +newzealanddollar $ 0.8035 +nicaraguacordoba $ 0.0421 +norwaykrone $ 0.1717 +pakistanrupee $ 0.0106 +paraguayguarani $ 1 | 4415 +perunewsol $ 0.3832 +philippinespeso $ 0.0236 +polandzloty $ 0.3001 +russiaruble $ 0.0311 +saudiarabiariyal $ 1 | 3.75 +singaporedollar $ 0.7976 +slovakkoruna 1 | 30.126 europeuro +southafricarand $ 0.1188 +southkoreawon $ 1 | 1135 +swedenkrona $ 0.1502 +switzerlandfranc $ 1.0431 +taiwandollar $ 0.0334 +thailandbaht $ 0.0319 +turkeynewlira $ 0.5504 +uaedirham $ 0.2723 +uruguaynewpeso $ 0.0465 +vietnamdong $ 1 | 20865 / END MONEY diff --git a/src/cmd/yacc/units.y b/src/cmd/yacc/units.y index 7258e3e59..9c1b0b336 100644 --- a/src/cmd/yacc/units.y +++ b/src/cmd/yacc/units.y @@ -11,6 +11,11 @@ %{ +// This tag will end up in the generated y.go, so that forgetting +// 'make clean' does not fail the next build. + +// +build ignore + // units.y // example of a Go yacc program // usage is @@ -26,11 +31,13 @@ package main import ( + "bufio" "flag" "fmt" - "bufio" - "os" "math" + "runtime" + "os" + "path/filepath" "strconv" "unicode/utf8" ) @@ -74,9 +81,9 @@ var vflag bool %type prog expr expr0 expr1 expr2 expr3 expr4 -%token VAL +%token VÄL // dieresis to test UTF-8 %token VAR -%token SUP +%token _SUP // tests leading underscore in token name %% prog: ':' VAR expr @@ -157,7 +164,7 @@ expr3: expr2: expr1 -| expr2 SUP +| expr2 _SUP { xpn(&$$, &$1, $2) } @@ -197,7 +204,7 @@ expr0: $$ = $1.node } } -| VAL +| VÄL { $$ = one $$.vval = $1 @@ -234,13 +241,13 @@ loop: return '/' case '¹', 'ⁱ': yylval.numb = 1 - return SUP + return _SUP case '²', '⁲': yylval.numb = 2 - return SUP + return _SUP case '³', '⁳': yylval.numb = 3 - return SUP + return _SUP } return int(c) @@ -273,7 +280,7 @@ numb: f = 0 } yylval.vval = f - return VAL + return VÄL } func (UnitsLex) Error(s string) { @@ -287,13 +294,11 @@ func main() { flag.Parse() - if dir := os.Getenv("GOROOT"); dir != "" { - file = dir + "/src/cmd/yacc/units.txt" - } + file = filepath.Join(runtime.GOROOT(), "src/cmd/yacc/units.txt") if flag.NArg() > 0 { file = flag.Arg(0) } else if file == "" { - fmt.Fprintf(os.Stderr, "can not find data file units.txt; provide it as argument or set $GOROOT\n") + fmt.Fprintf(os.Stderr, "cannot find data file units.txt; provide it as argument or set $GOROOT\n") os.Exit(1) } @@ -308,7 +313,7 @@ func main() { /* * read the 'units' file to - * develope a database + * develop a database */ lineno = 0 for { diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go index e94228152..0c18f93b6 100644 --- a/src/cmd/yacc/yacc.go +++ b/src/cmd/yacc/yacc.go @@ -51,6 +51,8 @@ import ( "fmt" "os" "strings" + "unicode" + "unicode/utf8" ) // the following are adjustable @@ -153,6 +155,8 @@ var ftable *bufio.Writer // y.go file var fcode = &bytes.Buffer{} // saved code var foutput *bufio.Writer // y.output file +var fmtImported bool // output file has recorded an import of "fmt" + var oflag string // -o [y.go] - y.go file var vflag string // -v [y.output] - y.output file var lflag bool // -l - disable line directives @@ -323,7 +327,6 @@ var resrv = []Resrv{ var zznewstate = 0 const EOF = -1 -const UTFmax = 0x3f func main() { @@ -716,8 +719,8 @@ func moreprod() { } // -// define s to be a terminal if t=0 -// or a nonterminal if t=1 +// define s to be a terminal if nt==0 +// or a nonterminal if nt==1 // func defin(nt int, s string) int { val := 0 @@ -750,56 +753,66 @@ func defin(nt int, s string) int { // establish value for token // single character literal - if s[0] == ' ' && len(s) == 1+1 { - val = int(s[1]) - } else if s[0] == ' ' && s[1] == '\\' { // escape sequence - if len(s) == 2+1 { - // single character escape sequence - switch s[2] { - case '\'': - val = '\'' - case '"': - val = '"' - case '\\': - val = '\\' - case 'a': - val = '\a' - case 'b': - val = '\b' - case 'n': - val = '\n' - case 'r': - val = '\r' - case 't': - val = '\t' - case 'v': - val = '\v' - default: - errorf("invalid escape %v", s[1:3]) - } - } else if s[2] == 'u' && len(s) == 2+1+4 { // \unnnn sequence - val = 0 - s = s[3:] - for s != "" { - c := int(s[0]) - switch { - case c >= '0' && c <= '9': - c -= '0' - case c >= 'a' && c <= 'f': - c -= 'a' - 10 - case c >= 'A' && c <= 'F': - c -= 'A' - 10 + if s[0] == ' ' { + s = s[1:] + r, size := utf8.DecodeRuneInString(s) + if r == utf8.RuneError && size == 1 { + errorf("invalid UTF-8 sequence %q", s) + } + val = int(r) + if val == '\\' { // escape sequence + switch { + case len(s) == 2: + // single character escape sequence + switch s[1] { + case '\'': + val = '\'' + case '"': + val = '"' + case '\\': + val = '\\' + case 'a': + val = '\a' + case 'b': + val = '\b' + case 'f': + val = '\f' + case 'n': + val = '\n' + case 'r': + val = '\r' + case 't': + val = '\t' + case 'v': + val = '\v' default: - errorf("illegal \\unnnn construction") + errorf("invalid escape %s", s) } - val = val*16 + c - s = s[1:] - } - if val == 0 { - errorf("'\\u0000' is illegal") + case s[1] == 'u' && len(s) == 2+4, // \unnnn sequence + s[1] == 'U' && len(s) == 2+8: // \Unnnnnnnn sequence + val = 0 + s = s[2:] + for s != "" { + c := int(s[0]) + switch { + case c >= '0' && c <= '9': + c -= '0' + case c >= 'a' && c <= 'f': + c -= 'a' - 10 + case c >= 'A' && c <= 'F': + c -= 'A' - 10 + default: + errorf(`illegal \u or \U construction`) + } + val = val*16 + c + s = s[1:] + } + default: + errorf("invalid escape %s", s) } - } else { - errorf("unknown escape") + } + if val == 0 { + errorf("token value 0 is illegal") } } else { val = extval @@ -985,7 +998,7 @@ func gettok() int { func getword(c rune) { tokname = "" - for isword(c) || isdigit(c) || c == '_' || c == '.' || c == '$' { + for isword(c) || isdigit(c) || c == '.' || c == '$' { tokname += string(c) c = getrune(finput) } @@ -1073,6 +1086,7 @@ out: // // saves code between %{ and %} +// adds an import for __fmt__ the first time // func cpycode() { lno := lineno @@ -1085,15 +1099,18 @@ func cpycode() { if !lflag { fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) } + // accumulate until %} + code := make([]rune, 0, 1024) for c != EOF { if c == '%' { c = getrune(finput) if c == '}' { + emitcode(code, lno+1) return } - ftable.WriteRune('%') + code = append(code, '%') } - ftable.WriteRune(c) + code = append(code, c) if c == '\n' { lineno++ } @@ -1103,6 +1120,107 @@ func cpycode() { errorf("eof before %%}") } +// +// emits code saved up from between %{ and %} +// called by cpycode +// adds an import for __yyfmt__ after the package clause +// +func emitcode(code []rune, lineno int) { + for i, line := range lines(code) { + writecode(line) + if !fmtImported && isPackageClause(line) { + fmt.Fprintln(ftable, `import __yyfmt__ "fmt"`) + fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i) + fmtImported = true + } + } +} + +// +// does this line look like a package clause? not perfect: might be confused by early comments. +// +func isPackageClause(line []rune) bool { + line = skipspace(line) + + // must be big enough. + if len(line) < len("package X\n") { + return false + } + + // must start with "package" + for i, r := range []rune("package") { + if line[i] != r { + return false + } + } + line = skipspace(line[len("package"):]) + + // must have another identifier. + if len(line) == 0 || (!unicode.IsLetter(line[0]) && line[0] != '_') { + return false + } + for len(line) > 0 { + if !unicode.IsLetter(line[0]) && !unicode.IsDigit(line[0]) && line[0] != '_' { + break + } + line = line[1:] + } + line = skipspace(line) + + // eol, newline, or comment must follow + if len(line) == 0 { + return true + } + if line[0] == '\r' || line[0] == '\n' { + return true + } + if len(line) >= 2 { + return line[0] == '/' && (line[1] == '/' || line[1] == '*') + } + return false +} + +// +// skip initial spaces +// +func skipspace(line []rune) []rune { + for len(line) > 0 { + if line[0] != ' ' && line[0] != '\t' { + break + } + line = line[1:] + } + return line +} + +// +// break code into lines +// +func lines(code []rune) [][]rune { + l := make([][]rune, 0, 100) + for len(code) > 0 { + // one line per loop + var i int + for i = range code { + if code[i] == '\n' { + break + } + } + l = append(l, code[:i+1]) + code = code[i+1:] + } + return l +} + +// +// writes code to ftable +// +func writecode(code []rune) { + for _, r := range code { + ftable.WriteRune(r) + } +} + // // skip over comments // skipcom is called after reading a '/' @@ -1230,7 +1348,7 @@ loop: if j >= max { errorf("Illegal use of $%v", j) } - } else if isword(c) || c == '_' || c == '.' { + } else if isword(c) || c == '.' { // look for $name ungetrune(finput, c) if gettok() != IDENTIFIER { @@ -2775,7 +2893,7 @@ func others() { j = tokset[i].value if j >= 0 && j < 256 { if temp1[j] != 0 { - fmt.Print("yacc bug -- cant have 2 different Ts with same value\n") + fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n") fmt.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name) nerrors++ } @@ -2799,7 +2917,7 @@ func others() { j = tokset[i].value - PRIVATE if j >= 0 && j < 256 { if temp1[j] != 0 { - fmt.Print("yacc bug -- cant have 2 different Ts with same value\n") + fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n") fmt.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name) nerrors++ } @@ -2982,7 +3100,7 @@ var peekrune rune func isdigit(c rune) bool { return c >= '0' && c <= '9' } func isword(c rune) bool { - return c >= 0xa0 || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + return c >= 0xa0 || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } func mktemp(t string) string { return t } @@ -3110,12 +3228,13 @@ type $$Lexer interface { const $$Flag = -1000 func $$Tokname(c int) string { - if c > 0 && c <= len($$Toknames) { - if $$Toknames[c-1] != "" { - return $$Toknames[c-1] + // 4 is TOKSTART above + if c >= 4 && c-4 < len($$Toknames) { + if $$Toknames[c-4] != "" { + return $$Toknames[c-4] } } - return fmt.Sprintf("tok-%v", c) + return __yyfmt__.Sprintf("tok-%v", c) } func $$Statname(s int) string { @@ -3124,7 +3243,7 @@ func $$Statname(s int) string { return $$Statenames[s] } } - return fmt.Sprintf("state-%v", s) + return __yyfmt__.Sprintf("state-%v", s) } func $$lex1(lex $$Lexer, lval *$$SymType) int { @@ -3157,7 +3276,7 @@ out: c = $$Tok2[1] /* unknown char */ } if $$Debug >= 3 { - fmt.Printf("lex %U %s\n", uint(char), $$Tokname(c)) + __yyfmt__.Printf("lex %U %s\n", uint(char), $$Tokname(c)) } return c } @@ -3184,7 +3303,7 @@ ret1: $$stack: /* put a state and value onto the stack */ if $$Debug >= 4 { - fmt.Printf("char %v in %v\n", $$Tokname($$char), $$Statname($$state)) + __yyfmt__.Printf("char %v in %v\n", $$Tokname($$char), $$Statname($$state)) } $$p++ @@ -3253,8 +3372,8 @@ $$default: $$lex.Error("syntax error") Nerrs++ if $$Debug >= 1 { - fmt.Printf("%s", $$Statname($$state)) - fmt.Printf("saw %s\n", $$Tokname($$char)) + __yyfmt__.Printf("%s", $$Statname($$state)) + __yyfmt__.Printf("saw %s\n", $$Tokname($$char)) } fallthrough @@ -3273,7 +3392,7 @@ $$default: /* the current p has no shift on "error", pop stack */ if $$Debug >= 2 { - fmt.Printf("error recovery pops state %d\n", $$S[$$p].yys) + __yyfmt__.Printf("error recovery pops state %d\n", $$S[$$p].yys) } $$p-- } @@ -3282,7 +3401,7 @@ $$default: case 3: /* no shift yet; clobber input char */ if $$Debug >= 2 { - fmt.Printf("error recovery discards %s\n", $$Tokname($$char)) + __yyfmt__.Printf("error recovery discards %s\n", $$Tokname($$char)) } if $$char == $$EofCode { goto ret1 @@ -3294,7 +3413,7 @@ $$default: /* reduction by production $$n */ if $$Debug >= 2 { - fmt.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state)) + __yyfmt__.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state)) } $$nt := $$n diff --git a/src/lib9/argv0.c b/src/lib9/argv0.c deleted file mode 100644 index 623985122..000000000 --- a/src/lib9/argv0.c +++ /dev/null @@ -1,35 +0,0 @@ -/* -Plan 9 from User Space src/lib9/argv0.c -http://code.swtch.com/plan9port/src/tip/src/lib9/argv0.c - -Copyright 2001-2007 Russ Cox. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include - -char *argv0; - -/* - * Mac OS can't deal with files that only declare data. - * ARGBEGIN mentions this function so that this file gets pulled in. - */ -void __fixargv0(void) { } diff --git a/src/lib9/flag.c b/src/lib9/flag.c new file mode 100644 index 000000000..7c79c1a6d --- /dev/null +++ b/src/lib9/flag.c @@ -0,0 +1,307 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include + +// Flag hash. +typedef struct Flag Flag; + +struct Flag +{ + char *name; + int namelen; + char *desc; + int iscount; + void (*set)(char*, void*); + void (*set2)(char*, char*, void*); + void *arg; + Flag *next; + Flag *allnext; +}; + +static Flag *curflag; + +static Flag *fhash[512]; +static Flag *first, *last; + +char *argv0; + +/* + * Mac OS can't deal with files that only declare data. + * ARGBEGIN mentions this function so that this file gets pulled in. + */ +void __fixargv0(void) { } + +// FNV-1 hash. http://isthe.com/chongo/tech/comp/fnv/ +static uint32 +fnv(char *p, int n) +{ + uint32 h; + + h = 2166136261U; + while(n-- > 0) + h = (h*16777619) ^ (uchar)*p++; + return h; +} + +static Flag* +lookflag(char *name, int namelen, int creat) +{ + uint32 h; + Flag *f; + + h = fnv(name, namelen) & (nelem(fhash)-1); + for(f=fhash[h]; f; f=f->next) { + if(f->namelen == namelen && memcmp(f->name, name, namelen) == 0) { + if(creat) + sysfatal("multiple definitions of flag -%s", name); + return f; + } + } + + if(!creat) + return nil; + + f = malloc(sizeof *f); + if(f == nil) + sysfatal("out of memory"); + memset(f, 0, sizeof *f); + f->name = name; + f->namelen = namelen; + f->next = fhash[h]; + if(first == nil) + first = f; + else + last->allnext = f; + last = f; + fhash[h] = f; + return f; +} + +static void +count(char *arg, void *p) +{ + int *ip; + + ip = p; + if(arg != nil) + *ip = atoi(arg); + else + (*ip)++; +} + +void +flagcount(char *name, char *desc, int *p) +{ + Flag *f; + + f = lookflag(name, strlen(name), 1); + f->desc = desc; + f->iscount = 1; + f->set = count; + f->arg = p; +} + +static void +atollwhex(char *s, void *p) +{ + char *t; + + *(int64*)p = strtoll(s, &t, 0); + if(*s == '\0' || *t != '\0') + sysfatal("invalid numeric argument -%s=%s", curflag->name, s); +} + +void +flagint64(char *name, char *desc, int64 *p) +{ + Flag *f; + + f = lookflag(name, strlen(name), 1); + f->desc = desc; + f->set = atollwhex; + f->arg = p; +} + +static void +atolwhex(char *s, void *p) +{ + char *t; + + *(int32*)p = strtol(s, &t, 0); + if(*s == '\0' || *t != '\0') + sysfatal("invalid numeric argument -%s=%s", curflag->name, s); +} + +void +flagint32(char *name, char *desc, int32 *p) +{ + Flag *f; + + f = lookflag(name, strlen(name), 1); + f->desc = desc; + f->set = atolwhex; + f->arg = p; +} + +static void +string(char *s, void *p) +{ + *(char**)p = s; +} + +void +flagstr(char *name, char *desc, char **p) +{ + + Flag *f; + + f = lookflag(name, strlen(name), 1); + f->desc = desc; + f->set = string; + f->arg = p; +} + +static void +fn0(char *s, void *p) +{ + USED(s); + ((void(*)(void))p)(); +} + +void +flagfn0(char *name, char *desc, void (*fn)(void)) +{ + Flag *f; + + f = lookflag(name, strlen(name), 1); + f->desc = desc; + f->set = fn0; + f->arg = fn; + f->iscount = 1; +} + +static void +fn1(char *s, void *p) +{ + ((void(*)(char*))p)(s); +} + +void +flagfn1(char *name, char *desc, void (*fn)(char*)) +{ + Flag *f; + + f = lookflag(name, strlen(name), 1); + f->desc = desc; + f->set = fn1; + f->arg = fn; +} + +static void +fn2(char *s, char *t, void *p) +{ + ((void(*)(char*, char*))p)(s, t); +} + +void +flagfn2(char *name, char *desc, void (*fn)(char*, char*)) +{ + Flag *f; + + f = lookflag(name, strlen(name), 1); + f->desc = desc; + f->set2 = fn2; + f->arg = fn; +} + +void +flagparse(int *argcp, char ***argvp, void (*usage)(void)) +{ + int argc; + char **argv, *p, *q; + char *name; + int namelen; + Flag *f; + + argc = *argcp; + argv = *argvp; + + argv0 = argv[0]; + argc--; + argv++; + + while(argc > 0) { + p = *argv; + // stop before non-flag or - + if(*p != '-' || p[1] == '\0') + break; + argc--; + argv++; + // stop after -- + if(p[1] == '-' && p[2] == '\0') { + break; + } + + // turn --foo into -foo + if(p[1] == '-' && p[2] != '-') + p++; + + // allow -flag=arg if present + name = p+1; + q = strchr(name, '='); + if(q != nil) + namelen = q++ - name; + else + namelen = strlen(name); + f = lookflag(name, namelen, 0); + if(f == nil) { + if(strcmp(p, "-h") == 0 || strcmp(p, "-help") == 0 || strcmp(p, "-?") == 0) + usage(); + sysfatal("unknown flag %s", p); + } + curflag = f; + + // otherwise consume next argument if non-boolean + if(!f->iscount && q == nil) { + if(argc-- == 0) + sysfatal("missing argument to flag %s", p); + q = *argv++; + } + + // and another if we need two + if(f->set2 != nil) { + if(argc-- == 0) + sysfatal("missing second argument to flag %s", p); + f->set2(q, *argv++, f->arg); + continue; + } + + f->set(q, f->arg); + } + + *argcp = argc; + *argvp = argv; +} + +void +flagprint(int fd) +{ + Flag *f; + char *p, *q; + + for(f=first; f; f=f->allnext) { + p = f->desc; + if(p == nil || *p == '\0') // undocumented flag + continue; + q = strstr(p, ": "); + if(q) + fprint(fd, " -%s %.*s\n \t%s\n", f->name, utfnlen(p, q-p), p, q+2); + else if(f->namelen > 1) + fprint(fd, " -%s\n \t%s\n", f->name, p); + else + fprint(fd, " -%s\t%s\n", f->name, p); + } +} diff --git a/src/lib9/fmt/test.c b/src/lib9/fmt/test.c index 1710c5e48..d82ff78ba 100644 --- a/src/lib9/fmt/test.c +++ b/src/lib9/fmt/test.c @@ -1,3 +1,5 @@ +// +build ignore + /* * The authors of this software are Rob Pike and Ken Thompson, * with contributions from Mike Burrows and Sean Dorward. diff --git a/src/lib9/getuser.c b/src/lib9/getuser.c deleted file mode 100644 index d611f4467..000000000 --- a/src/lib9/getuser.c +++ /dev/null @@ -1,43 +0,0 @@ -// +build !windows - -/* -Plan 9 from User Space src/lib9/getuser.c -http://code.swtch.com/plan9port/src/tip/src/lib9/getuser.c - -Copyright 2001-2007 Russ Cox. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include - -char* -getuser(void) -{ - static char user[64]; - struct passwd *pw; - - pw = getpwuid(getuid()); - if(pw == nil) - return "none"; - strecpy(user, user+sizeof user, pw->pw_name); - return user; -} diff --git a/src/lib9/getwd.c b/src/lib9/getwd.c index 3c8cafb3a..566d3f647 100644 --- a/src/lib9/getwd.c +++ b/src/lib9/getwd.c @@ -26,10 +26,9 @@ THE SOFTWARE. #include #include #include +#define NOPLAN9DEFINES #include -#undef getwd - char* p9getwd(char *s, int ns) { diff --git a/src/lib9/goos.c b/src/lib9/goos.c index f3ee1110a..3b0027111 100644 --- a/src/lib9/goos.c +++ b/src/lib9/goos.c @@ -39,3 +39,15 @@ getgoversion(void) { return GOVERSION; } + +char* +getgoarm(void) +{ + return defgetenv("GOARM", GOARM); +} + +char* +getgo386(void) +{ + return defgetenv("GO386", GO386); +} diff --git a/src/lib9/main.c b/src/lib9/main.c index 45f86c7ec..816494af0 100644 --- a/src/lib9/main.c +++ b/src/lib9/main.c @@ -27,11 +27,31 @@ THE SOFTWARE. #define NOPLAN9DEFINES #include +#ifdef WIN32 +#include + +static void +crashhandler(int sig) +{ + USED(sig); + fprint(2, "%s: internal fatal error.\n", argv0); + exit(1); +} +#endif + extern void p9main(int, char**); int main(int argc, char **argv) { +#ifdef WIN32 + signal(SIGSEGV, crashhandler); + signal(SIGBUS, crashhandler); + // don't display the crash dialog + DWORD mode = SetErrorMode(SEM_NOGPFAULTERRORBOX); + SetErrorMode(mode | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + argv0 = argv[0]; +#endif p9main(argc, argv); exits("main"); return 99; diff --git a/src/lib9/utf/Makefile b/src/lib9/utf/Makefile index bbb2da6a9..5c9cdf051 100644 --- a/src/lib9/utf/Makefile +++ b/src/lib9/utf/Makefile @@ -15,13 +15,13 @@ UnicodeData-%.txt: mkrunetype: mkrunetype.c cc -I../../../include -o mkrunetype -L$(GOROOT)/pkg/obj/$(GOOS)_$(GOARCH)/ mkrunetype.c -l9 -runetypebody-%.c: mkrunetype UnicodeData-%.txt +runetypebody-%.h: mkrunetype UnicodeData-%.txt mkrunetype -p UnicodeData-$*.txt >_$@ mv _$@ $@ CLEANFILES+=UnicodeData.txt -UNICODE_VERSION=6.0.0 +UNICODE_VERSION=6.2.0 test: mkrunetype UnicodeData-$(UNICODE_VERSION).txt mkrunetype -c UnicodeData-$(UNICODE_VERSION).txt diff --git a/src/lib9/utf/rune.c b/src/lib9/utf/rune.c index cf98bab15..818771cfd 100644 --- a/src/lib9/utf/rune.c +++ b/src/lib9/utf/rune.c @@ -36,12 +36,14 @@ enum Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ - Rune4 = (1<<(Bit4+3*Bitx))-1, - /* 0001 1111 1111 1111 1111 1111 */ + Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0001 1111 1111 1111 1111 1111 */ Maskx = (1< Runemax) goto bad; *rune = l; return 4; @@ -208,6 +212,8 @@ chartorune(Rune *rune, const char *str) l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; if(l <= Rune2) goto bad; + if (SurrogateMin <= l && l <= SurrogateMax) + goto bad; *rune = l; return 3; } @@ -221,7 +227,7 @@ chartorune(Rune *rune, const char *str) goto bad; if (c < T5) { l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; - if (l <= Rune3) + if (l <= Rune3 || l > Runemax) goto bad; *rune = l; return 4; @@ -241,7 +247,8 @@ bad: } int -isvalidcharntorune(const char* str, int length, Rune* rune, int* consumed) { +isvalidcharntorune(const char* str, int length, Rune* rune, int* consumed) +{ *consumed = charntorune(rune, str, length); return *rune != Runeerror || *consumed == 3; } @@ -273,13 +280,15 @@ runetochar(char *str, const Rune *rune) } /* - * If the Rune is out of range, convert it to the error rune. + * If the Rune is out of range or a surrogate half, convert it to the error rune. * Do this test here because the error rune encodes to three bytes. * Doing it earlier would duplicate work, since an out of range * Rune wouldn't have fit in one or two bytes. */ if (c > Runemax) c = Runeerror; + if (SurrogateMin <= c && c <= SurrogateMax) + c = Runeerror; /* * three character sequence diff --git a/src/lib9/utf/runetype.c b/src/lib9/utf/runetype.c index 51729fb01..b3634965f 100644 --- a/src/lib9/utf/runetype.c +++ b/src/lib9/utf/runetype.c @@ -35,4 +35,4 @@ rbsearch(Rune c, Rune *t, int n, int ne) return 0; } -#include "runetypebody-6.0.0.h" +#include "runetypebody-6.2.0.h" diff --git a/src/lib9/utf/runetypebody-6.0.0.h b/src/lib9/utf/runetypebody-6.0.0.h deleted file mode 100644 index 47c0faf73..000000000 --- a/src/lib9/utf/runetypebody-6.0.0.h +++ /dev/null @@ -1,1565 +0,0 @@ -/* generated automatically by mkrunetype.c from UnicodeData-6.0.0.txt */ - -static Rune __isspacer[] = { - 0x0009, 0x000d, - 0x0020, 0x0020, - 0x0085, 0x0085, - 0x00a0, 0x00a0, - 0x1680, 0x1680, - 0x180e, 0x180e, - 0x2000, 0x200a, - 0x2028, 0x2029, - 0x202f, 0x202f, - 0x205f, 0x205f, - 0x3000, 0x3000, - 0xfeff, 0xfeff, -}; - -int -isspacerune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __isspacer, nelem(__isspacer)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - return 0; -} - -static Rune __isdigitr[] = { - 0x0030, 0x0039, - 0x0660, 0x0669, - 0x06f0, 0x06f9, - 0x07c0, 0x07c9, - 0x0966, 0x096f, - 0x09e6, 0x09ef, - 0x0a66, 0x0a6f, - 0x0ae6, 0x0aef, - 0x0b66, 0x0b6f, - 0x0be6, 0x0bef, - 0x0c66, 0x0c6f, - 0x0ce6, 0x0cef, - 0x0d66, 0x0d6f, - 0x0e50, 0x0e59, - 0x0ed0, 0x0ed9, - 0x0f20, 0x0f29, - 0x1040, 0x1049, - 0x1090, 0x1099, - 0x17e0, 0x17e9, - 0x1810, 0x1819, - 0x1946, 0x194f, - 0x19d0, 0x19d9, - 0x1a80, 0x1a89, - 0x1a90, 0x1a99, - 0x1b50, 0x1b59, - 0x1bb0, 0x1bb9, - 0x1c40, 0x1c49, - 0x1c50, 0x1c59, - 0xa620, 0xa629, - 0xa8d0, 0xa8d9, - 0xa900, 0xa909, - 0xa9d0, 0xa9d9, - 0xaa50, 0xaa59, - 0xabf0, 0xabf9, - 0xff10, 0xff19, - 0x104a0, 0x104a9, - 0x11066, 0x1106f, - 0x1d7ce, 0x1d7ff, -}; - -int -isdigitrune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __isdigitr, nelem(__isdigitr)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - return 0; -} - -static Rune __isalphar[] = { - 0x0041, 0x005a, - 0x0061, 0x007a, - 0x00c0, 0x00d6, - 0x00d8, 0x00f6, - 0x00f8, 0x02c1, - 0x02c6, 0x02d1, - 0x02e0, 0x02e4, - 0x0370, 0x0374, - 0x0376, 0x0377, - 0x037a, 0x037d, - 0x0388, 0x038a, - 0x038e, 0x03a1, - 0x03a3, 0x03f5, - 0x03f7, 0x0481, - 0x048a, 0x0527, - 0x0531, 0x0556, - 0x0561, 0x0587, - 0x05d0, 0x05ea, - 0x05f0, 0x05f2, - 0x0620, 0x064a, - 0x066e, 0x066f, - 0x0671, 0x06d3, - 0x06e5, 0x06e6, - 0x06ee, 0x06ef, - 0x06fa, 0x06fc, - 0x0712, 0x072f, - 0x074d, 0x07a5, - 0x07ca, 0x07ea, - 0x07f4, 0x07f5, - 0x0800, 0x0815, - 0x0840, 0x0858, - 0x0904, 0x0939, - 0x0958, 0x0961, - 0x0971, 0x0977, - 0x0979, 0x097f, - 0x0985, 0x098c, - 0x098f, 0x0990, - 0x0993, 0x09a8, - 0x09aa, 0x09b0, - 0x09b6, 0x09b9, - 0x09dc, 0x09dd, - 0x09df, 0x09e1, - 0x09f0, 0x09f1, - 0x0a05, 0x0a0a, - 0x0a0f, 0x0a10, - 0x0a13, 0x0a28, - 0x0a2a, 0x0a30, - 0x0a32, 0x0a33, - 0x0a35, 0x0a36, - 0x0a38, 0x0a39, - 0x0a59, 0x0a5c, - 0x0a72, 0x0a74, - 0x0a85, 0x0a8d, - 0x0a8f, 0x0a91, - 0x0a93, 0x0aa8, - 0x0aaa, 0x0ab0, - 0x0ab2, 0x0ab3, - 0x0ab5, 0x0ab9, - 0x0ae0, 0x0ae1, - 0x0b05, 0x0b0c, - 0x0b0f, 0x0b10, - 0x0b13, 0x0b28, - 0x0b2a, 0x0b30, - 0x0b32, 0x0b33, - 0x0b35, 0x0b39, - 0x0b5c, 0x0b5d, - 0x0b5f, 0x0b61, - 0x0b85, 0x0b8a, - 0x0b8e, 0x0b90, - 0x0b92, 0x0b95, - 0x0b99, 0x0b9a, - 0x0b9e, 0x0b9f, - 0x0ba3, 0x0ba4, - 0x0ba8, 0x0baa, - 0x0bae, 0x0bb9, - 0x0c05, 0x0c0c, - 0x0c0e, 0x0c10, - 0x0c12, 0x0c28, - 0x0c2a, 0x0c33, - 0x0c35, 0x0c39, - 0x0c58, 0x0c59, - 0x0c60, 0x0c61, - 0x0c85, 0x0c8c, - 0x0c8e, 0x0c90, - 0x0c92, 0x0ca8, - 0x0caa, 0x0cb3, - 0x0cb5, 0x0cb9, - 0x0ce0, 0x0ce1, - 0x0cf1, 0x0cf2, - 0x0d05, 0x0d0c, - 0x0d0e, 0x0d10, - 0x0d12, 0x0d3a, - 0x0d60, 0x0d61, - 0x0d7a, 0x0d7f, - 0x0d85, 0x0d96, - 0x0d9a, 0x0db1, - 0x0db3, 0x0dbb, - 0x0dc0, 0x0dc6, - 0x0e01, 0x0e30, - 0x0e32, 0x0e33, - 0x0e40, 0x0e46, - 0x0e81, 0x0e82, - 0x0e87, 0x0e88, - 0x0e94, 0x0e97, - 0x0e99, 0x0e9f, - 0x0ea1, 0x0ea3, - 0x0eaa, 0x0eab, - 0x0ead, 0x0eb0, - 0x0eb2, 0x0eb3, - 0x0ec0, 0x0ec4, - 0x0edc, 0x0edd, - 0x0f40, 0x0f47, - 0x0f49, 0x0f6c, - 0x0f88, 0x0f8c, - 0x1000, 0x102a, - 0x1050, 0x1055, - 0x105a, 0x105d, - 0x1065, 0x1066, - 0x106e, 0x1070, - 0x1075, 0x1081, - 0x10a0, 0x10c5, - 0x10d0, 0x10fa, - 0x1100, 0x1248, - 0x124a, 0x124d, - 0x1250, 0x1256, - 0x125a, 0x125d, - 0x1260, 0x1288, - 0x128a, 0x128d, - 0x1290, 0x12b0, - 0x12b2, 0x12b5, - 0x12b8, 0x12be, - 0x12c2, 0x12c5, - 0x12c8, 0x12d6, - 0x12d8, 0x1310, - 0x1312, 0x1315, - 0x1318, 0x135a, - 0x1380, 0x138f, - 0x13a0, 0x13f4, - 0x1401, 0x166c, - 0x166f, 0x167f, - 0x1681, 0x169a, - 0x16a0, 0x16ea, - 0x1700, 0x170c, - 0x170e, 0x1711, - 0x1720, 0x1731, - 0x1740, 0x1751, - 0x1760, 0x176c, - 0x176e, 0x1770, - 0x1780, 0x17b3, - 0x1820, 0x1877, - 0x1880, 0x18a8, - 0x18b0, 0x18f5, - 0x1900, 0x191c, - 0x1950, 0x196d, - 0x1970, 0x1974, - 0x1980, 0x19ab, - 0x19c1, 0x19c7, - 0x1a00, 0x1a16, - 0x1a20, 0x1a54, - 0x1b05, 0x1b33, - 0x1b45, 0x1b4b, - 0x1b83, 0x1ba0, - 0x1bae, 0x1baf, - 0x1bc0, 0x1be5, - 0x1c00, 0x1c23, - 0x1c4d, 0x1c4f, - 0x1c5a, 0x1c7d, - 0x1ce9, 0x1cec, - 0x1cee, 0x1cf1, - 0x1d00, 0x1dbf, - 0x1e00, 0x1f15, - 0x1f18, 0x1f1d, - 0x1f20, 0x1f45, - 0x1f48, 0x1f4d, - 0x1f50, 0x1f57, - 0x1f5f, 0x1f7d, - 0x1f80, 0x1fb4, - 0x1fb6, 0x1fbc, - 0x1fc2, 0x1fc4, - 0x1fc6, 0x1fcc, - 0x1fd0, 0x1fd3, - 0x1fd6, 0x1fdb, - 0x1fe0, 0x1fec, - 0x1ff2, 0x1ff4, - 0x1ff6, 0x1ffc, - 0x2090, 0x209c, - 0x210a, 0x2113, - 0x2119, 0x211d, - 0x212a, 0x212d, - 0x212f, 0x2139, - 0x213c, 0x213f, - 0x2145, 0x2149, - 0x2183, 0x2184, - 0x2c00, 0x2c2e, - 0x2c30, 0x2c5e, - 0x2c60, 0x2ce4, - 0x2ceb, 0x2cee, - 0x2d00, 0x2d25, - 0x2d30, 0x2d65, - 0x2d80, 0x2d96, - 0x2da0, 0x2da6, - 0x2da8, 0x2dae, - 0x2db0, 0x2db6, - 0x2db8, 0x2dbe, - 0x2dc0, 0x2dc6, - 0x2dc8, 0x2dce, - 0x2dd0, 0x2dd6, - 0x2dd8, 0x2dde, - 0x3005, 0x3006, - 0x3031, 0x3035, - 0x303b, 0x303c, - 0x3041, 0x3096, - 0x309d, 0x309f, - 0x30a1, 0x30fa, - 0x30fc, 0x30ff, - 0x3105, 0x312d, - 0x3131, 0x318e, - 0x31a0, 0x31ba, - 0x31f0, 0x31ff, - 0x3400, 0x4db5, - 0x4e00, 0x9fcb, - 0xa000, 0xa48c, - 0xa4d0, 0xa4fd, - 0xa500, 0xa60c, - 0xa610, 0xa61f, - 0xa62a, 0xa62b, - 0xa640, 0xa66e, - 0xa67f, 0xa697, - 0xa6a0, 0xa6e5, - 0xa717, 0xa71f, - 0xa722, 0xa788, - 0xa78b, 0xa78e, - 0xa790, 0xa791, - 0xa7a0, 0xa7a9, - 0xa7fa, 0xa801, - 0xa803, 0xa805, - 0xa807, 0xa80a, - 0xa80c, 0xa822, - 0xa840, 0xa873, - 0xa882, 0xa8b3, - 0xa8f2, 0xa8f7, - 0xa90a, 0xa925, - 0xa930, 0xa946, - 0xa960, 0xa97c, - 0xa984, 0xa9b2, - 0xaa00, 0xaa28, - 0xaa40, 0xaa42, - 0xaa44, 0xaa4b, - 0xaa60, 0xaa76, - 0xaa80, 0xaaaf, - 0xaab5, 0xaab6, - 0xaab9, 0xaabd, - 0xaadb, 0xaadd, - 0xab01, 0xab06, - 0xab09, 0xab0e, - 0xab11, 0xab16, - 0xab20, 0xab26, - 0xab28, 0xab2e, - 0xabc0, 0xabe2, - 0xac00, 0xd7a3, - 0xd7b0, 0xd7c6, - 0xd7cb, 0xd7fb, - 0xf900, 0xfa2d, - 0xfa30, 0xfa6d, - 0xfa70, 0xfad9, - 0xfb00, 0xfb06, - 0xfb13, 0xfb17, - 0xfb1f, 0xfb28, - 0xfb2a, 0xfb36, - 0xfb38, 0xfb3c, - 0xfb40, 0xfb41, - 0xfb43, 0xfb44, - 0xfb46, 0xfbb1, - 0xfbd3, 0xfd3d, - 0xfd50, 0xfd8f, - 0xfd92, 0xfdc7, - 0xfdf0, 0xfdfb, - 0xfe70, 0xfe74, - 0xfe76, 0xfefc, - 0xff21, 0xff3a, - 0xff41, 0xff5a, - 0xff66, 0xffbe, - 0xffc2, 0xffc7, - 0xffca, 0xffcf, - 0xffd2, 0xffd7, - 0xffda, 0xffdc, - 0x10000, 0x1000b, - 0x1000d, 0x10026, - 0x10028, 0x1003a, - 0x1003c, 0x1003d, - 0x1003f, 0x1004d, - 0x10050, 0x1005d, - 0x10080, 0x100fa, - 0x10280, 0x1029c, - 0x102a0, 0x102d0, - 0x10300, 0x1031e, - 0x10330, 0x10340, - 0x10342, 0x10349, - 0x10380, 0x1039d, - 0x103a0, 0x103c3, - 0x103c8, 0x103cf, - 0x10400, 0x1049d, - 0x10800, 0x10805, - 0x1080a, 0x10835, - 0x10837, 0x10838, - 0x1083f, 0x10855, - 0x10900, 0x10915, - 0x10920, 0x10939, - 0x10a10, 0x10a13, - 0x10a15, 0x10a17, - 0x10a19, 0x10a33, - 0x10a60, 0x10a7c, - 0x10b00, 0x10b35, - 0x10b40, 0x10b55, - 0x10b60, 0x10b72, - 0x10c00, 0x10c48, - 0x11003, 0x11037, - 0x11083, 0x110af, - 0x12000, 0x1236e, - 0x13000, 0x1342e, - 0x16800, 0x16a38, - 0x1b000, 0x1b001, - 0x1d400, 0x1d454, - 0x1d456, 0x1d49c, - 0x1d49e, 0x1d49f, - 0x1d4a5, 0x1d4a6, - 0x1d4a9, 0x1d4ac, - 0x1d4ae, 0x1d4b9, - 0x1d4bd, 0x1d4c3, - 0x1d4c5, 0x1d505, - 0x1d507, 0x1d50a, - 0x1d50d, 0x1d514, - 0x1d516, 0x1d51c, - 0x1d51e, 0x1d539, - 0x1d53b, 0x1d53e, - 0x1d540, 0x1d544, - 0x1d54a, 0x1d550, - 0x1d552, 0x1d6a5, - 0x1d6a8, 0x1d6c0, - 0x1d6c2, 0x1d6da, - 0x1d6dc, 0x1d6fa, - 0x1d6fc, 0x1d714, - 0x1d716, 0x1d734, - 0x1d736, 0x1d74e, - 0x1d750, 0x1d76e, - 0x1d770, 0x1d788, - 0x1d78a, 0x1d7a8, - 0x1d7aa, 0x1d7c2, - 0x1d7c4, 0x1d7cb, - 0x20000, 0x2a6d6, - 0x2a700, 0x2b734, - 0x2b740, 0x2b81d, - 0x2f800, 0x2fa1d, -}; - -static Rune __isalphas[] = { - 0x00aa, - 0x00b5, - 0x00ba, - 0x02ec, - 0x02ee, - 0x0386, - 0x038c, - 0x0559, - 0x06d5, - 0x06ff, - 0x0710, - 0x07b1, - 0x07fa, - 0x081a, - 0x0824, - 0x0828, - 0x093d, - 0x0950, - 0x09b2, - 0x09bd, - 0x09ce, - 0x0a5e, - 0x0abd, - 0x0ad0, - 0x0b3d, - 0x0b71, - 0x0b83, - 0x0b9c, - 0x0bd0, - 0x0c3d, - 0x0cbd, - 0x0cde, - 0x0d3d, - 0x0d4e, - 0x0dbd, - 0x0e84, - 0x0e8a, - 0x0e8d, - 0x0ea5, - 0x0ea7, - 0x0ebd, - 0x0ec6, - 0x0f00, - 0x103f, - 0x1061, - 0x108e, - 0x10fc, - 0x1258, - 0x12c0, - 0x17d7, - 0x17dc, - 0x18aa, - 0x1aa7, - 0x1f59, - 0x1f5b, - 0x1f5d, - 0x1fbe, - 0x2071, - 0x207f, - 0x2102, - 0x2107, - 0x2115, - 0x2124, - 0x2126, - 0x2128, - 0x214e, - 0x2d6f, - 0x2e2f, - 0xa8fb, - 0xa9cf, - 0xaa7a, - 0xaab1, - 0xaac0, - 0xaac2, - 0xfb1d, - 0xfb3e, - 0x10808, - 0x1083c, - 0x10a00, - 0x1d4a2, - 0x1d4bb, - 0x1d546, -}; - -int -isalpharune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __isalphar, nelem(__isalphar)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - p = rbsearch(c, __isalphas, nelem(__isalphas), 1); - if(p && c == p[0]) - return 1; - return 0; -} - -static Rune __isupperr[] = { - 0x0041, 0x005a, - 0x00c0, 0x00d6, - 0x00d8, 0x00de, - 0x0178, 0x0179, - 0x0181, 0x0182, - 0x0186, 0x0187, - 0x0189, 0x018b, - 0x018e, 0x0191, - 0x0193, 0x0194, - 0x0196, 0x0198, - 0x019c, 0x019d, - 0x019f, 0x01a0, - 0x01a6, 0x01a7, - 0x01ae, 0x01af, - 0x01b1, 0x01b3, - 0x01b7, 0x01b8, - 0x01f6, 0x01f8, - 0x023a, 0x023b, - 0x023d, 0x023e, - 0x0243, 0x0246, - 0x0388, 0x038a, - 0x038e, 0x038f, - 0x0391, 0x03a1, - 0x03a3, 0x03ab, - 0x03d2, 0x03d4, - 0x03f9, 0x03fa, - 0x03fd, 0x042f, - 0x04c0, 0x04c1, - 0x0531, 0x0556, - 0x10a0, 0x10c5, - 0x1f08, 0x1f0f, - 0x1f18, 0x1f1d, - 0x1f28, 0x1f2f, - 0x1f38, 0x1f3f, - 0x1f48, 0x1f4d, - 0x1f68, 0x1f6f, - 0x1f88, 0x1f8f, - 0x1f98, 0x1f9f, - 0x1fa8, 0x1faf, - 0x1fb8, 0x1fbc, - 0x1fc8, 0x1fcc, - 0x1fd8, 0x1fdb, - 0x1fe8, 0x1fec, - 0x1ff8, 0x1ffc, - 0x210b, 0x210d, - 0x2110, 0x2112, - 0x2119, 0x211d, - 0x212a, 0x212d, - 0x2130, 0x2133, - 0x213e, 0x213f, - 0x2160, 0x216f, - 0x24b6, 0x24cf, - 0x2c00, 0x2c2e, - 0x2c62, 0x2c64, - 0x2c6d, 0x2c70, - 0x2c7e, 0x2c80, - 0xa77d, 0xa77e, - 0xff21, 0xff3a, - 0x10400, 0x10427, - 0x1d400, 0x1d419, - 0x1d434, 0x1d44d, - 0x1d468, 0x1d481, - 0x1d49e, 0x1d49f, - 0x1d4a5, 0x1d4a6, - 0x1d4a9, 0x1d4ac, - 0x1d4ae, 0x1d4b5, - 0x1d4d0, 0x1d4e9, - 0x1d504, 0x1d505, - 0x1d507, 0x1d50a, - 0x1d50d, 0x1d514, - 0x1d516, 0x1d51c, - 0x1d538, 0x1d539, - 0x1d53b, 0x1d53e, - 0x1d540, 0x1d544, - 0x1d54a, 0x1d550, - 0x1d56c, 0x1d585, - 0x1d5a0, 0x1d5b9, - 0x1d5d4, 0x1d5ed, - 0x1d608, 0x1d621, - 0x1d63c, 0x1d655, - 0x1d670, 0x1d689, - 0x1d6a8, 0x1d6c0, - 0x1d6e2, 0x1d6fa, - 0x1d71c, 0x1d734, - 0x1d756, 0x1d76e, - 0x1d790, 0x1d7a8, -}; - -static Rune __isupperp[] = { - 0x0100, 0x0136, - 0x0139, 0x0147, - 0x014a, 0x0176, - 0x017b, 0x017d, - 0x01a2, 0x01a4, - 0x01cd, 0x01db, - 0x01de, 0x01ee, - 0x01fa, 0x0232, - 0x0248, 0x024e, - 0x0370, 0x0372, - 0x03d8, 0x03ee, - 0x0460, 0x0480, - 0x048a, 0x04be, - 0x04c3, 0x04cd, - 0x04d0, 0x0526, - 0x1e00, 0x1e94, - 0x1e9e, 0x1efe, - 0x1f59, 0x1f5f, - 0x2124, 0x2128, - 0x2c67, 0x2c6b, - 0x2c82, 0x2ce2, - 0x2ceb, 0x2ced, - 0xa640, 0xa66c, - 0xa680, 0xa696, - 0xa722, 0xa72e, - 0xa732, 0xa76e, - 0xa779, 0xa77b, - 0xa780, 0xa786, - 0xa78b, 0xa78d, - 0xa7a0, 0xa7a8, -}; - -static Rune __isuppers[] = { - 0x0184, - 0x01a9, - 0x01ac, - 0x01b5, - 0x01bc, - 0x01c4, - 0x01c7, - 0x01ca, - 0x01f1, - 0x01f4, - 0x0241, - 0x0376, - 0x0386, - 0x038c, - 0x03cf, - 0x03f4, - 0x03f7, - 0x2102, - 0x2107, - 0x2115, - 0x2145, - 0x2183, - 0x2c60, - 0x2c72, - 0x2c75, - 0xa790, - 0x1d49c, - 0x1d4a2, - 0x1d546, - 0x1d7ca, -}; - -int -isupperrune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __isupperr, nelem(__isupperr)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - p = rbsearch(c, __isupperp, nelem(__isupperp)/2, 2); - if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) - return 1; - p = rbsearch(c, __isuppers, nelem(__isuppers), 1); - if(p && c == p[0]) - return 1; - return 0; -} - -static Rune __islowerr[] = { - 0x0061, 0x007a, - 0x00df, 0x00f6, - 0x00f8, 0x00ff, - 0x0137, 0x0138, - 0x0148, 0x0149, - 0x017e, 0x0180, - 0x018c, 0x018d, - 0x0199, 0x019b, - 0x01aa, 0x01ab, - 0x01b9, 0x01ba, - 0x01bd, 0x01bf, - 0x01dc, 0x01dd, - 0x01ef, 0x01f0, - 0x0233, 0x0239, - 0x023f, 0x0240, - 0x024f, 0x0293, - 0x0295, 0x02af, - 0x037b, 0x037d, - 0x03ac, 0x03ce, - 0x03d0, 0x03d1, - 0x03d5, 0x03d7, - 0x03ef, 0x03f3, - 0x03fb, 0x03fc, - 0x0430, 0x045f, - 0x04ce, 0x04cf, - 0x0561, 0x0587, - 0x1d00, 0x1d2b, - 0x1d62, 0x1d77, - 0x1d79, 0x1d9a, - 0x1e95, 0x1e9d, - 0x1eff, 0x1f07, - 0x1f10, 0x1f15, - 0x1f20, 0x1f27, - 0x1f30, 0x1f37, - 0x1f40, 0x1f45, - 0x1f50, 0x1f57, - 0x1f60, 0x1f67, - 0x1f70, 0x1f7d, - 0x1f80, 0x1f87, - 0x1f90, 0x1f97, - 0x1fa0, 0x1fa7, - 0x1fb0, 0x1fb4, - 0x1fb6, 0x1fb7, - 0x1fc2, 0x1fc4, - 0x1fc6, 0x1fc7, - 0x1fd0, 0x1fd3, - 0x1fd6, 0x1fd7, - 0x1fe0, 0x1fe7, - 0x1ff2, 0x1ff4, - 0x1ff6, 0x1ff7, - 0x210e, 0x210f, - 0x213c, 0x213d, - 0x2146, 0x2149, - 0x2170, 0x217f, - 0x24d0, 0x24e9, - 0x2c30, 0x2c5e, - 0x2c65, 0x2c66, - 0x2c73, 0x2c74, - 0x2c76, 0x2c7c, - 0x2ce3, 0x2ce4, - 0x2d00, 0x2d25, - 0xa72f, 0xa731, - 0xa771, 0xa778, - 0xfb00, 0xfb06, - 0xfb13, 0xfb17, - 0xff41, 0xff5a, - 0x10428, 0x1044f, - 0x1d41a, 0x1d433, - 0x1d44e, 0x1d454, - 0x1d456, 0x1d467, - 0x1d482, 0x1d49b, - 0x1d4b6, 0x1d4b9, - 0x1d4bd, 0x1d4c3, - 0x1d4c5, 0x1d4cf, - 0x1d4ea, 0x1d503, - 0x1d51e, 0x1d537, - 0x1d552, 0x1d56b, - 0x1d586, 0x1d59f, - 0x1d5ba, 0x1d5d3, - 0x1d5ee, 0x1d607, - 0x1d622, 0x1d63b, - 0x1d656, 0x1d66f, - 0x1d68a, 0x1d6a5, - 0x1d6c2, 0x1d6da, - 0x1d6dc, 0x1d6e1, - 0x1d6fc, 0x1d714, - 0x1d716, 0x1d71b, - 0x1d736, 0x1d74e, - 0x1d750, 0x1d755, - 0x1d770, 0x1d788, - 0x1d78a, 0x1d78f, - 0x1d7aa, 0x1d7c2, - 0x1d7c4, 0x1d7c9, -}; - -static Rune __islowerp[] = { - 0x0101, 0x0135, - 0x013a, 0x0146, - 0x014b, 0x0177, - 0x017a, 0x017c, - 0x0183, 0x0185, - 0x01a1, 0x01a5, - 0x01b4, 0x01b6, - 0x01cc, 0x01da, - 0x01df, 0x01ed, - 0x01f3, 0x01f5, - 0x01f9, 0x0231, - 0x0247, 0x024d, - 0x0371, 0x0373, - 0x03d9, 0x03ed, - 0x0461, 0x0481, - 0x048b, 0x04bf, - 0x04c2, 0x04cc, - 0x04d1, 0x0527, - 0x1e01, 0x1e93, - 0x1e9f, 0x1efd, - 0x2c68, 0x2c6c, - 0x2c81, 0x2ce1, - 0x2cec, 0x2cee, - 0xa641, 0xa66d, - 0xa681, 0xa697, - 0xa723, 0xa72d, - 0xa733, 0xa76f, - 0xa77a, 0xa77c, - 0xa77f, 0xa787, - 0xa78c, 0xa78e, - 0xa7a1, 0xa7a9, -}; - -static Rune __islowers[] = { - 0x00aa, - 0x00b5, - 0x00ba, - 0x0188, - 0x0192, - 0x0195, - 0x019e, - 0x01a8, - 0x01ad, - 0x01b0, - 0x01c6, - 0x01c9, - 0x023c, - 0x0242, - 0x0377, - 0x0390, - 0x03f5, - 0x03f8, - 0x1fbe, - 0x210a, - 0x2113, - 0x212f, - 0x2134, - 0x2139, - 0x214e, - 0x2184, - 0x2c61, - 0x2c71, - 0xa791, - 0xa7fa, - 0x1d4bb, - 0x1d7cb, -}; - -int -islowerrune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __islowerr, nelem(__islowerr)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - p = rbsearch(c, __islowerp, nelem(__islowerp)/2, 2); - if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) - return 1; - p = rbsearch(c, __islowers, nelem(__islowers), 1); - if(p && c == p[0]) - return 1; - return 0; -} - -static Rune __istitler[] = { - 0x0041, 0x005a, - 0x00c0, 0x00d6, - 0x00d8, 0x00de, - 0x0178, 0x0179, - 0x0181, 0x0182, - 0x0186, 0x0187, - 0x0189, 0x018b, - 0x018e, 0x0191, - 0x0193, 0x0194, - 0x0196, 0x0198, - 0x019c, 0x019d, - 0x019f, 0x01a0, - 0x01a6, 0x01a7, - 0x01ae, 0x01af, - 0x01b1, 0x01b3, - 0x01b7, 0x01b8, - 0x01f6, 0x01f8, - 0x023a, 0x023b, - 0x023d, 0x023e, - 0x0243, 0x0246, - 0x0388, 0x038a, - 0x038e, 0x038f, - 0x0391, 0x03a1, - 0x03a3, 0x03ab, - 0x03f9, 0x03fa, - 0x03fd, 0x042f, - 0x04c0, 0x04c1, - 0x0531, 0x0556, - 0x10a0, 0x10c5, - 0x1f08, 0x1f0f, - 0x1f18, 0x1f1d, - 0x1f28, 0x1f2f, - 0x1f38, 0x1f3f, - 0x1f48, 0x1f4d, - 0x1f68, 0x1f6f, - 0x1f88, 0x1f8f, - 0x1f98, 0x1f9f, - 0x1fa8, 0x1faf, - 0x1fb8, 0x1fbc, - 0x1fc8, 0x1fcc, - 0x1fd8, 0x1fdb, - 0x1fe8, 0x1fec, - 0x1ff8, 0x1ffc, - 0x2160, 0x216f, - 0x24b6, 0x24cf, - 0x2c00, 0x2c2e, - 0x2c62, 0x2c64, - 0x2c6d, 0x2c70, - 0x2c7e, 0x2c80, - 0xa77d, 0xa77e, - 0xff21, 0xff3a, - 0x10400, 0x10427, -}; - -static Rune __istitlep[] = { - 0x0100, 0x012e, - 0x0132, 0x0136, - 0x0139, 0x0147, - 0x014a, 0x0176, - 0x017b, 0x017d, - 0x01a2, 0x01a4, - 0x01cb, 0x01db, - 0x01de, 0x01ee, - 0x01f2, 0x01f4, - 0x01fa, 0x0232, - 0x0248, 0x024e, - 0x0370, 0x0372, - 0x03d8, 0x03ee, - 0x0460, 0x0480, - 0x048a, 0x04be, - 0x04c3, 0x04cd, - 0x04d0, 0x0526, - 0x1e00, 0x1e94, - 0x1ea0, 0x1efe, - 0x1f59, 0x1f5f, - 0x2c67, 0x2c6b, - 0x2c82, 0x2ce2, - 0x2ceb, 0x2ced, - 0xa640, 0xa66c, - 0xa680, 0xa696, - 0xa722, 0xa72e, - 0xa732, 0xa76e, - 0xa779, 0xa77b, - 0xa780, 0xa786, - 0xa78b, 0xa78d, - 0xa7a0, 0xa7a8, -}; - -static Rune __istitles[] = { - 0x0184, - 0x01a9, - 0x01ac, - 0x01b5, - 0x01bc, - 0x01c5, - 0x01c8, - 0x0241, - 0x0376, - 0x0386, - 0x038c, - 0x03cf, - 0x03f7, - 0x2132, - 0x2183, - 0x2c60, - 0x2c72, - 0x2c75, - 0xa790, -}; - -int -istitlerune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __istitler, nelem(__istitler)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - p = rbsearch(c, __istitlep, nelem(__istitlep)/2, 2); - if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) - return 1; - p = rbsearch(c, __istitles, nelem(__istitles), 1); - if(p && c == p[0]) - return 1; - return 0; -} - -static Rune __toupperr[] = { - 0x0061, 0x007a, 1048544, - 0x00e0, 0x00f6, 1048544, - 0x00f8, 0x00fe, 1048544, - 0x023f, 0x0240, 1059391, - 0x0256, 0x0257, 1048371, - 0x028a, 0x028b, 1048359, - 0x037b, 0x037d, 1048706, - 0x03ad, 0x03af, 1048539, - 0x03b1, 0x03c1, 1048544, - 0x03c3, 0x03cb, 1048544, - 0x03cd, 0x03ce, 1048513, - 0x0430, 0x044f, 1048544, - 0x0450, 0x045f, 1048496, - 0x0561, 0x0586, 1048528, - 0x1f00, 0x1f07, 1048584, - 0x1f10, 0x1f15, 1048584, - 0x1f20, 0x1f27, 1048584, - 0x1f30, 0x1f37, 1048584, - 0x1f40, 0x1f45, 1048584, - 0x1f60, 0x1f67, 1048584, - 0x1f70, 0x1f71, 1048650, - 0x1f72, 0x1f75, 1048662, - 0x1f76, 0x1f77, 1048676, - 0x1f78, 0x1f79, 1048704, - 0x1f7a, 0x1f7b, 1048688, - 0x1f7c, 0x1f7d, 1048702, - 0x1f80, 0x1f87, 1048584, - 0x1f90, 0x1f97, 1048584, - 0x1fa0, 0x1fa7, 1048584, - 0x1fb0, 0x1fb1, 1048584, - 0x1fd0, 0x1fd1, 1048584, - 0x1fe0, 0x1fe1, 1048584, - 0x2170, 0x217f, 1048560, - 0x24d0, 0x24e9, 1048550, - 0x2c30, 0x2c5e, 1048528, - 0x2d00, 0x2d25, 1041312, - 0xff41, 0xff5a, 1048544, - 0x10428, 0x1044f, 1048536, -}; - -static Rune __toupperp[] = { - 0x0101, 0x012f, 1048575, - 0x0133, 0x0137, 1048575, - 0x013a, 0x0148, 1048575, - 0x014b, 0x0177, 1048575, - 0x017a, 0x017e, 1048575, - 0x0183, 0x0185, 1048575, - 0x01a1, 0x01a5, 1048575, - 0x01b4, 0x01b6, 1048575, - 0x01ce, 0x01dc, 1048575, - 0x01df, 0x01ef, 1048575, - 0x01f9, 0x021f, 1048575, - 0x0223, 0x0233, 1048575, - 0x0247, 0x024f, 1048575, - 0x0371, 0x0373, 1048575, - 0x03d9, 0x03ef, 1048575, - 0x0461, 0x0481, 1048575, - 0x048b, 0x04bf, 1048575, - 0x04c2, 0x04ce, 1048575, - 0x04d1, 0x0527, 1048575, - 0x1e01, 0x1e95, 1048575, - 0x1ea1, 0x1eff, 1048575, - 0x1f51, 0x1f57, 1048584, - 0x2c68, 0x2c6c, 1048575, - 0x2c81, 0x2ce3, 1048575, - 0x2cec, 0x2cee, 1048575, - 0xa641, 0xa66d, 1048575, - 0xa681, 0xa697, 1048575, - 0xa723, 0xa72f, 1048575, - 0xa733, 0xa76f, 1048575, - 0xa77a, 0xa77c, 1048575, - 0xa77f, 0xa787, 1048575, - 0xa7a1, 0xa7a9, 1048575, -}; - -static Rune __touppers[] = { - 0x00b5, 1049319, - 0x00ff, 1048697, - 0x0131, 1048344, - 0x017f, 1048276, - 0x0180, 1048771, - 0x0188, 1048575, - 0x018c, 1048575, - 0x0192, 1048575, - 0x0195, 1048673, - 0x0199, 1048575, - 0x019a, 1048739, - 0x019e, 1048706, - 0x01a8, 1048575, - 0x01ad, 1048575, - 0x01b0, 1048575, - 0x01b9, 1048575, - 0x01bd, 1048575, - 0x01bf, 1048632, - 0x01c5, 1048575, - 0x01c6, 1048574, - 0x01c8, 1048575, - 0x01c9, 1048574, - 0x01cb, 1048575, - 0x01cc, 1048574, - 0x01dd, 1048497, - 0x01f2, 1048575, - 0x01f3, 1048574, - 0x01f5, 1048575, - 0x023c, 1048575, - 0x0242, 1048575, - 0x0250, 1059359, - 0x0251, 1059356, - 0x0252, 1059358, - 0x0253, 1048366, - 0x0254, 1048370, - 0x0259, 1048374, - 0x025b, 1048373, - 0x0260, 1048371, - 0x0263, 1048369, - 0x0265, 1090856, - 0x0268, 1048367, - 0x0269, 1048365, - 0x026b, 1059319, - 0x026f, 1048365, - 0x0271, 1059325, - 0x0272, 1048363, - 0x0275, 1048362, - 0x027d, 1059303, - 0x0280, 1048358, - 0x0283, 1048358, - 0x0288, 1048358, - 0x0289, 1048507, - 0x028c, 1048505, - 0x0292, 1048357, - 0x0345, 1048660, - 0x0377, 1048575, - 0x03ac, 1048538, - 0x03c2, 1048545, - 0x03cc, 1048512, - 0x03d0, 1048514, - 0x03d1, 1048519, - 0x03d5, 1048529, - 0x03d6, 1048522, - 0x03d7, 1048568, - 0x03f0, 1048490, - 0x03f1, 1048496, - 0x03f2, 1048583, - 0x03f5, 1048480, - 0x03f8, 1048575, - 0x03fb, 1048575, - 0x04cf, 1048561, - 0x1d79, 1083908, - 0x1d7d, 1052390, - 0x1e9b, 1048517, - 0x1fb3, 1048585, - 0x1fbe, 1041371, - 0x1fc3, 1048585, - 0x1fe5, 1048583, - 0x1ff3, 1048585, - 0x214e, 1048548, - 0x2184, 1048575, - 0x2c61, 1048575, - 0x2c65, 1037781, - 0x2c66, 1037784, - 0x2c73, 1048575, - 0x2c76, 1048575, - 0xa78c, 1048575, - 0xa791, 1048575, -}; - -Rune -toupperrune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __toupperr, nelem(__toupperr)/3, 3); - if(p && c >= p[0] && c <= p[1]) - return c + p[2] - 1048576; - p = rbsearch(c, __toupperp, nelem(__toupperp)/3, 3); - if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) - return c + p[2] - 1048576; - p = rbsearch(c, __touppers, nelem(__touppers)/2, 2); - if(p && c == p[0]) - return c + p[1] - 1048576; - return c; -} - -static Rune __tolowerr[] = { - 0x0041, 0x005a, 1048608, - 0x00c0, 0x00d6, 1048608, - 0x00d8, 0x00de, 1048608, - 0x0189, 0x018a, 1048781, - 0x01b1, 0x01b2, 1048793, - 0x0388, 0x038a, 1048613, - 0x038e, 0x038f, 1048639, - 0x0391, 0x03a1, 1048608, - 0x03a3, 0x03ab, 1048608, - 0x03fd, 0x03ff, 1048446, - 0x0400, 0x040f, 1048656, - 0x0410, 0x042f, 1048608, - 0x0531, 0x0556, 1048624, - 0x10a0, 0x10c5, 1055840, - 0x1f08, 0x1f0f, 1048568, - 0x1f18, 0x1f1d, 1048568, - 0x1f28, 0x1f2f, 1048568, - 0x1f38, 0x1f3f, 1048568, - 0x1f48, 0x1f4d, 1048568, - 0x1f68, 0x1f6f, 1048568, - 0x1f88, 0x1f8f, 1048568, - 0x1f98, 0x1f9f, 1048568, - 0x1fa8, 0x1faf, 1048568, - 0x1fb8, 0x1fb9, 1048568, - 0x1fba, 0x1fbb, 1048502, - 0x1fc8, 0x1fcb, 1048490, - 0x1fd8, 0x1fd9, 1048568, - 0x1fda, 0x1fdb, 1048476, - 0x1fe8, 0x1fe9, 1048568, - 0x1fea, 0x1feb, 1048464, - 0x1ff8, 0x1ff9, 1048448, - 0x1ffa, 0x1ffb, 1048450, - 0x2160, 0x216f, 1048592, - 0x24b6, 0x24cf, 1048602, - 0x2c00, 0x2c2e, 1048624, - 0x2c7e, 0x2c7f, 1037761, - 0xff21, 0xff3a, 1048608, - 0x10400, 0x10427, 1048616, -}; - -static Rune __tolowerp[] = { - 0x0100, 0x012e, 1048577, - 0x0132, 0x0136, 1048577, - 0x0139, 0x0147, 1048577, - 0x014a, 0x0176, 1048577, - 0x017b, 0x017d, 1048577, - 0x01a2, 0x01a4, 1048577, - 0x01b3, 0x01b5, 1048577, - 0x01cd, 0x01db, 1048577, - 0x01de, 0x01ee, 1048577, - 0x01f8, 0x021e, 1048577, - 0x0222, 0x0232, 1048577, - 0x0248, 0x024e, 1048577, - 0x0370, 0x0372, 1048577, - 0x03d8, 0x03ee, 1048577, - 0x0460, 0x0480, 1048577, - 0x048a, 0x04be, 1048577, - 0x04c3, 0x04cd, 1048577, - 0x04d0, 0x0526, 1048577, - 0x1e00, 0x1e94, 1048577, - 0x1ea0, 0x1efe, 1048577, - 0x1f59, 0x1f5f, 1048568, - 0x2c67, 0x2c6b, 1048577, - 0x2c80, 0x2ce2, 1048577, - 0x2ceb, 0x2ced, 1048577, - 0xa640, 0xa66c, 1048577, - 0xa680, 0xa696, 1048577, - 0xa722, 0xa72e, 1048577, - 0xa732, 0xa76e, 1048577, - 0xa779, 0xa77b, 1048577, - 0xa780, 0xa786, 1048577, - 0xa7a0, 0xa7a8, 1048577, -}; - -static Rune __tolowers[] = { - 0x0130, 1048377, - 0x0178, 1048455, - 0x0179, 1048577, - 0x0181, 1048786, - 0x0182, 1048577, - 0x0184, 1048577, - 0x0186, 1048782, - 0x0187, 1048577, - 0x018b, 1048577, - 0x018e, 1048655, - 0x018f, 1048778, - 0x0190, 1048779, - 0x0191, 1048577, - 0x0193, 1048781, - 0x0194, 1048783, - 0x0196, 1048787, - 0x0197, 1048785, - 0x0198, 1048577, - 0x019c, 1048787, - 0x019d, 1048789, - 0x019f, 1048790, - 0x01a0, 1048577, - 0x01a6, 1048794, - 0x01a7, 1048577, - 0x01a9, 1048794, - 0x01ac, 1048577, - 0x01ae, 1048794, - 0x01af, 1048577, - 0x01b7, 1048795, - 0x01b8, 1048577, - 0x01bc, 1048577, - 0x01c4, 1048578, - 0x01c5, 1048577, - 0x01c7, 1048578, - 0x01c8, 1048577, - 0x01ca, 1048578, - 0x01cb, 1048577, - 0x01f1, 1048578, - 0x01f2, 1048577, - 0x01f4, 1048577, - 0x01f6, 1048479, - 0x01f7, 1048520, - 0x0220, 1048446, - 0x023a, 1059371, - 0x023b, 1048577, - 0x023d, 1048413, - 0x023e, 1059368, - 0x0241, 1048577, - 0x0243, 1048381, - 0x0244, 1048645, - 0x0245, 1048647, - 0x0246, 1048577, - 0x0376, 1048577, - 0x0386, 1048614, - 0x038c, 1048640, - 0x03cf, 1048584, - 0x03f4, 1048516, - 0x03f7, 1048577, - 0x03f9, 1048569, - 0x03fa, 1048577, - 0x04c0, 1048591, - 0x04c1, 1048577, - 0x1e9e, 1040961, - 0x1fbc, 1048567, - 0x1fcc, 1048567, - 0x1fec, 1048569, - 0x1ffc, 1048567, - 0x2126, 1041059, - 0x212a, 1040193, - 0x212b, 1040314, - 0x2132, 1048604, - 0x2183, 1048577, - 0x2c60, 1048577, - 0x2c62, 1037833, - 0x2c63, 1044762, - 0x2c64, 1037849, - 0x2c6d, 1037796, - 0x2c6e, 1037827, - 0x2c6f, 1037793, - 0x2c70, 1037794, - 0x2c72, 1048577, - 0x2c75, 1048577, - 0xa77d, 1013244, - 0xa77e, 1048577, - 0xa78b, 1048577, - 0xa78d, 1006296, - 0xa790, 1048577, -}; - -Rune -tolowerrune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __tolowerr, nelem(__tolowerr)/3, 3); - if(p && c >= p[0] && c <= p[1]) - return c + p[2] - 1048576; - p = rbsearch(c, __tolowerp, nelem(__tolowerp)/3, 3); - if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) - return c + p[2] - 1048576; - p = rbsearch(c, __tolowers, nelem(__tolowers)/2, 2); - if(p && c == p[0]) - return c + p[1] - 1048576; - return c; -} - -static Rune __totitler[] = { - 0x0061, 0x007a, 1048544, - 0x00e0, 0x00f6, 1048544, - 0x00f8, 0x00fe, 1048544, - 0x023f, 0x0240, 1059391, - 0x0256, 0x0257, 1048371, - 0x028a, 0x028b, 1048359, - 0x037b, 0x037d, 1048706, - 0x03ad, 0x03af, 1048539, - 0x03b1, 0x03c1, 1048544, - 0x03c3, 0x03cb, 1048544, - 0x03cd, 0x03ce, 1048513, - 0x0430, 0x044f, 1048544, - 0x0450, 0x045f, 1048496, - 0x0561, 0x0586, 1048528, - 0x1f00, 0x1f07, 1048584, - 0x1f10, 0x1f15, 1048584, - 0x1f20, 0x1f27, 1048584, - 0x1f30, 0x1f37, 1048584, - 0x1f40, 0x1f45, 1048584, - 0x1f60, 0x1f67, 1048584, - 0x1f70, 0x1f71, 1048650, - 0x1f72, 0x1f75, 1048662, - 0x1f76, 0x1f77, 1048676, - 0x1f78, 0x1f79, 1048704, - 0x1f7a, 0x1f7b, 1048688, - 0x1f7c, 0x1f7d, 1048702, - 0x1f80, 0x1f87, 1048584, - 0x1f90, 0x1f97, 1048584, - 0x1fa0, 0x1fa7, 1048584, - 0x1fb0, 0x1fb1, 1048584, - 0x1fd0, 0x1fd1, 1048584, - 0x1fe0, 0x1fe1, 1048584, - 0x2170, 0x217f, 1048560, - 0x24d0, 0x24e9, 1048550, - 0x2c30, 0x2c5e, 1048528, - 0x2d00, 0x2d25, 1041312, - 0xff41, 0xff5a, 1048544, - 0x10428, 0x1044f, 1048536, -}; - -static Rune __totitlep[] = { - 0x0101, 0x012f, 1048575, - 0x0133, 0x0137, 1048575, - 0x013a, 0x0148, 1048575, - 0x014b, 0x0177, 1048575, - 0x017a, 0x017e, 1048575, - 0x0183, 0x0185, 1048575, - 0x01a1, 0x01a5, 1048575, - 0x01b4, 0x01b6, 1048575, - 0x01cc, 0x01dc, 1048575, - 0x01df, 0x01ef, 1048575, - 0x01f3, 0x01f5, 1048575, - 0x01f9, 0x021f, 1048575, - 0x0223, 0x0233, 1048575, - 0x0247, 0x024f, 1048575, - 0x0371, 0x0373, 1048575, - 0x03d9, 0x03ef, 1048575, - 0x0461, 0x0481, 1048575, - 0x048b, 0x04bf, 1048575, - 0x04c2, 0x04ce, 1048575, - 0x04d1, 0x0527, 1048575, - 0x1e01, 0x1e95, 1048575, - 0x1ea1, 0x1eff, 1048575, - 0x1f51, 0x1f57, 1048584, - 0x2c68, 0x2c6c, 1048575, - 0x2c81, 0x2ce3, 1048575, - 0x2cec, 0x2cee, 1048575, - 0xa641, 0xa66d, 1048575, - 0xa681, 0xa697, 1048575, - 0xa723, 0xa72f, 1048575, - 0xa733, 0xa76f, 1048575, - 0xa77a, 0xa77c, 1048575, - 0xa77f, 0xa787, 1048575, - 0xa7a1, 0xa7a9, 1048575, -}; - -static Rune __totitles[] = { - 0x00b5, 1049319, - 0x00ff, 1048697, - 0x0131, 1048344, - 0x017f, 1048276, - 0x0180, 1048771, - 0x0188, 1048575, - 0x018c, 1048575, - 0x0192, 1048575, - 0x0195, 1048673, - 0x0199, 1048575, - 0x019a, 1048739, - 0x019e, 1048706, - 0x01a8, 1048575, - 0x01ad, 1048575, - 0x01b0, 1048575, - 0x01b9, 1048575, - 0x01bd, 1048575, - 0x01bf, 1048632, - 0x01c4, 1048577, - 0x01c6, 1048575, - 0x01c7, 1048577, - 0x01c9, 1048575, - 0x01ca, 1048577, - 0x01dd, 1048497, - 0x01f1, 1048577, - 0x023c, 1048575, - 0x0242, 1048575, - 0x0250, 1059359, - 0x0251, 1059356, - 0x0252, 1059358, - 0x0253, 1048366, - 0x0254, 1048370, - 0x0259, 1048374, - 0x025b, 1048373, - 0x0260, 1048371, - 0x0263, 1048369, - 0x0265, 1090856, - 0x0268, 1048367, - 0x0269, 1048365, - 0x026b, 1059319, - 0x026f, 1048365, - 0x0271, 1059325, - 0x0272, 1048363, - 0x0275, 1048362, - 0x027d, 1059303, - 0x0280, 1048358, - 0x0283, 1048358, - 0x0288, 1048358, - 0x0289, 1048507, - 0x028c, 1048505, - 0x0292, 1048357, - 0x0345, 1048660, - 0x0377, 1048575, - 0x03ac, 1048538, - 0x03c2, 1048545, - 0x03cc, 1048512, - 0x03d0, 1048514, - 0x03d1, 1048519, - 0x03d5, 1048529, - 0x03d6, 1048522, - 0x03d7, 1048568, - 0x03f0, 1048490, - 0x03f1, 1048496, - 0x03f2, 1048583, - 0x03f5, 1048480, - 0x03f8, 1048575, - 0x03fb, 1048575, - 0x04cf, 1048561, - 0x1d79, 1083908, - 0x1d7d, 1052390, - 0x1e9b, 1048517, - 0x1fb3, 1048585, - 0x1fbe, 1041371, - 0x1fc3, 1048585, - 0x1fe5, 1048583, - 0x1ff3, 1048585, - 0x214e, 1048548, - 0x2184, 1048575, - 0x2c61, 1048575, - 0x2c65, 1037781, - 0x2c66, 1037784, - 0x2c73, 1048575, - 0x2c76, 1048575, - 0xa78c, 1048575, - 0xa791, 1048575, -}; - -Rune -totitlerune(Rune c) -{ - Rune *p; - - p = rbsearch(c, __totitler, nelem(__totitler)/3, 3); - if(p && c >= p[0] && c <= p[1]) - return c + p[2] - 1048576; - p = rbsearch(c, __totitlep, nelem(__totitlep)/3, 3); - if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) - return c + p[2] - 1048576; - p = rbsearch(c, __totitles, nelem(__totitles)/2, 2); - if(p && c == p[0]) - return c + p[1] - 1048576; - return c; -} - diff --git a/src/lib9/utf/runetypebody-6.2.0.h b/src/lib9/utf/runetypebody-6.2.0.h new file mode 100644 index 000000000..a603af0df --- /dev/null +++ b/src/lib9/utf/runetypebody-6.2.0.h @@ -0,0 +1,1639 @@ +/* generated automatically by mkrunetype.c from UnicodeData-6.2.0.txt */ + +static Rune __isspacer[] = { + 0x0009, 0x000d, + 0x0020, 0x0020, + 0x0085, 0x0085, + 0x00a0, 0x00a0, + 0x1680, 0x1680, + 0x180e, 0x180e, + 0x2000, 0x200a, + 0x2028, 0x2029, + 0x202f, 0x202f, + 0x205f, 0x205f, + 0x3000, 0x3000, + 0xfeff, 0xfeff, +}; + +int +isspacerune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __isspacer, nelem(__isspacer)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + return 0; +} + +static Rune __isdigitr[] = { + 0x0030, 0x0039, + 0x0660, 0x0669, + 0x06f0, 0x06f9, + 0x07c0, 0x07c9, + 0x0966, 0x096f, + 0x09e6, 0x09ef, + 0x0a66, 0x0a6f, + 0x0ae6, 0x0aef, + 0x0b66, 0x0b6f, + 0x0be6, 0x0bef, + 0x0c66, 0x0c6f, + 0x0ce6, 0x0cef, + 0x0d66, 0x0d6f, + 0x0e50, 0x0e59, + 0x0ed0, 0x0ed9, + 0x0f20, 0x0f29, + 0x1040, 0x1049, + 0x1090, 0x1099, + 0x17e0, 0x17e9, + 0x1810, 0x1819, + 0x1946, 0x194f, + 0x19d0, 0x19d9, + 0x1a80, 0x1a89, + 0x1a90, 0x1a99, + 0x1b50, 0x1b59, + 0x1bb0, 0x1bb9, + 0x1c40, 0x1c49, + 0x1c50, 0x1c59, + 0xa620, 0xa629, + 0xa8d0, 0xa8d9, + 0xa900, 0xa909, + 0xa9d0, 0xa9d9, + 0xaa50, 0xaa59, + 0xabf0, 0xabf9, + 0xff10, 0xff19, + 0x104a0, 0x104a9, + 0x11066, 0x1106f, + 0x110f0, 0x110f9, + 0x11136, 0x1113f, + 0x111d0, 0x111d9, + 0x116c0, 0x116c9, + 0x1d7ce, 0x1d7ff, +}; + +int +isdigitrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __isdigitr, nelem(__isdigitr)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + return 0; +} + +static Rune __isalphar[] = { + 0x0041, 0x005a, + 0x0061, 0x007a, + 0x00c0, 0x00d6, + 0x00d8, 0x00f6, + 0x00f8, 0x02c1, + 0x02c6, 0x02d1, + 0x02e0, 0x02e4, + 0x0370, 0x0374, + 0x0376, 0x0377, + 0x037a, 0x037d, + 0x0388, 0x038a, + 0x038e, 0x03a1, + 0x03a3, 0x03f5, + 0x03f7, 0x0481, + 0x048a, 0x0527, + 0x0531, 0x0556, + 0x0561, 0x0587, + 0x05d0, 0x05ea, + 0x05f0, 0x05f2, + 0x0620, 0x064a, + 0x066e, 0x066f, + 0x0671, 0x06d3, + 0x06e5, 0x06e6, + 0x06ee, 0x06ef, + 0x06fa, 0x06fc, + 0x0712, 0x072f, + 0x074d, 0x07a5, + 0x07ca, 0x07ea, + 0x07f4, 0x07f5, + 0x0800, 0x0815, + 0x0840, 0x0858, + 0x08a2, 0x08ac, + 0x0904, 0x0939, + 0x0958, 0x0961, + 0x0971, 0x0977, + 0x0979, 0x097f, + 0x0985, 0x098c, + 0x098f, 0x0990, + 0x0993, 0x09a8, + 0x09aa, 0x09b0, + 0x09b6, 0x09b9, + 0x09dc, 0x09dd, + 0x09df, 0x09e1, + 0x09f0, 0x09f1, + 0x0a05, 0x0a0a, + 0x0a0f, 0x0a10, + 0x0a13, 0x0a28, + 0x0a2a, 0x0a30, + 0x0a32, 0x0a33, + 0x0a35, 0x0a36, + 0x0a38, 0x0a39, + 0x0a59, 0x0a5c, + 0x0a72, 0x0a74, + 0x0a85, 0x0a8d, + 0x0a8f, 0x0a91, + 0x0a93, 0x0aa8, + 0x0aaa, 0x0ab0, + 0x0ab2, 0x0ab3, + 0x0ab5, 0x0ab9, + 0x0ae0, 0x0ae1, + 0x0b05, 0x0b0c, + 0x0b0f, 0x0b10, + 0x0b13, 0x0b28, + 0x0b2a, 0x0b30, + 0x0b32, 0x0b33, + 0x0b35, 0x0b39, + 0x0b5c, 0x0b5d, + 0x0b5f, 0x0b61, + 0x0b85, 0x0b8a, + 0x0b8e, 0x0b90, + 0x0b92, 0x0b95, + 0x0b99, 0x0b9a, + 0x0b9e, 0x0b9f, + 0x0ba3, 0x0ba4, + 0x0ba8, 0x0baa, + 0x0bae, 0x0bb9, + 0x0c05, 0x0c0c, + 0x0c0e, 0x0c10, + 0x0c12, 0x0c28, + 0x0c2a, 0x0c33, + 0x0c35, 0x0c39, + 0x0c58, 0x0c59, + 0x0c60, 0x0c61, + 0x0c85, 0x0c8c, + 0x0c8e, 0x0c90, + 0x0c92, 0x0ca8, + 0x0caa, 0x0cb3, + 0x0cb5, 0x0cb9, + 0x0ce0, 0x0ce1, + 0x0cf1, 0x0cf2, + 0x0d05, 0x0d0c, + 0x0d0e, 0x0d10, + 0x0d12, 0x0d3a, + 0x0d60, 0x0d61, + 0x0d7a, 0x0d7f, + 0x0d85, 0x0d96, + 0x0d9a, 0x0db1, + 0x0db3, 0x0dbb, + 0x0dc0, 0x0dc6, + 0x0e01, 0x0e30, + 0x0e32, 0x0e33, + 0x0e40, 0x0e46, + 0x0e81, 0x0e82, + 0x0e87, 0x0e88, + 0x0e94, 0x0e97, + 0x0e99, 0x0e9f, + 0x0ea1, 0x0ea3, + 0x0eaa, 0x0eab, + 0x0ead, 0x0eb0, + 0x0eb2, 0x0eb3, + 0x0ec0, 0x0ec4, + 0x0edc, 0x0edf, + 0x0f40, 0x0f47, + 0x0f49, 0x0f6c, + 0x0f88, 0x0f8c, + 0x1000, 0x102a, + 0x1050, 0x1055, + 0x105a, 0x105d, + 0x1065, 0x1066, + 0x106e, 0x1070, + 0x1075, 0x1081, + 0x10a0, 0x10c5, + 0x10d0, 0x10fa, + 0x10fc, 0x1248, + 0x124a, 0x124d, + 0x1250, 0x1256, + 0x125a, 0x125d, + 0x1260, 0x1288, + 0x128a, 0x128d, + 0x1290, 0x12b0, + 0x12b2, 0x12b5, + 0x12b8, 0x12be, + 0x12c2, 0x12c5, + 0x12c8, 0x12d6, + 0x12d8, 0x1310, + 0x1312, 0x1315, + 0x1318, 0x135a, + 0x1380, 0x138f, + 0x13a0, 0x13f4, + 0x1401, 0x166c, + 0x166f, 0x167f, + 0x1681, 0x169a, + 0x16a0, 0x16ea, + 0x1700, 0x170c, + 0x170e, 0x1711, + 0x1720, 0x1731, + 0x1740, 0x1751, + 0x1760, 0x176c, + 0x176e, 0x1770, + 0x1780, 0x17b3, + 0x1820, 0x1877, + 0x1880, 0x18a8, + 0x18b0, 0x18f5, + 0x1900, 0x191c, + 0x1950, 0x196d, + 0x1970, 0x1974, + 0x1980, 0x19ab, + 0x19c1, 0x19c7, + 0x1a00, 0x1a16, + 0x1a20, 0x1a54, + 0x1b05, 0x1b33, + 0x1b45, 0x1b4b, + 0x1b83, 0x1ba0, + 0x1bae, 0x1baf, + 0x1bba, 0x1be5, + 0x1c00, 0x1c23, + 0x1c4d, 0x1c4f, + 0x1c5a, 0x1c7d, + 0x1ce9, 0x1cec, + 0x1cee, 0x1cf1, + 0x1cf5, 0x1cf6, + 0x1d00, 0x1dbf, + 0x1e00, 0x1f15, + 0x1f18, 0x1f1d, + 0x1f20, 0x1f45, + 0x1f48, 0x1f4d, + 0x1f50, 0x1f57, + 0x1f5f, 0x1f7d, + 0x1f80, 0x1fb4, + 0x1fb6, 0x1fbc, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fcc, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fdb, + 0x1fe0, 0x1fec, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ffc, + 0x2090, 0x209c, + 0x210a, 0x2113, + 0x2119, 0x211d, + 0x212a, 0x212d, + 0x212f, 0x2139, + 0x213c, 0x213f, + 0x2145, 0x2149, + 0x2183, 0x2184, + 0x2c00, 0x2c2e, + 0x2c30, 0x2c5e, + 0x2c60, 0x2ce4, + 0x2ceb, 0x2cee, + 0x2cf2, 0x2cf3, + 0x2d00, 0x2d25, + 0x2d30, 0x2d67, + 0x2d80, 0x2d96, + 0x2da0, 0x2da6, + 0x2da8, 0x2dae, + 0x2db0, 0x2db6, + 0x2db8, 0x2dbe, + 0x2dc0, 0x2dc6, + 0x2dc8, 0x2dce, + 0x2dd0, 0x2dd6, + 0x2dd8, 0x2dde, + 0x3005, 0x3006, + 0x3031, 0x3035, + 0x303b, 0x303c, + 0x3041, 0x3096, + 0x309d, 0x309f, + 0x30a1, 0x30fa, + 0x30fc, 0x30ff, + 0x3105, 0x312d, + 0x3131, 0x318e, + 0x31a0, 0x31ba, + 0x31f0, 0x31ff, + 0x3400, 0x4db5, + 0x4e00, 0x9fcc, + 0xa000, 0xa48c, + 0xa4d0, 0xa4fd, + 0xa500, 0xa60c, + 0xa610, 0xa61f, + 0xa62a, 0xa62b, + 0xa640, 0xa66e, + 0xa67f, 0xa697, + 0xa6a0, 0xa6e5, + 0xa717, 0xa71f, + 0xa722, 0xa788, + 0xa78b, 0xa78e, + 0xa790, 0xa793, + 0xa7a0, 0xa7aa, + 0xa7f8, 0xa801, + 0xa803, 0xa805, + 0xa807, 0xa80a, + 0xa80c, 0xa822, + 0xa840, 0xa873, + 0xa882, 0xa8b3, + 0xa8f2, 0xa8f7, + 0xa90a, 0xa925, + 0xa930, 0xa946, + 0xa960, 0xa97c, + 0xa984, 0xa9b2, + 0xaa00, 0xaa28, + 0xaa40, 0xaa42, + 0xaa44, 0xaa4b, + 0xaa60, 0xaa76, + 0xaa80, 0xaaaf, + 0xaab5, 0xaab6, + 0xaab9, 0xaabd, + 0xaadb, 0xaadd, + 0xaae0, 0xaaea, + 0xaaf2, 0xaaf4, + 0xab01, 0xab06, + 0xab09, 0xab0e, + 0xab11, 0xab16, + 0xab20, 0xab26, + 0xab28, 0xab2e, + 0xabc0, 0xabe2, + 0xac00, 0xd7a3, + 0xd7b0, 0xd7c6, + 0xd7cb, 0xd7fb, + 0xf900, 0xfa6d, + 0xfa70, 0xfad9, + 0xfb00, 0xfb06, + 0xfb13, 0xfb17, + 0xfb1f, 0xfb28, + 0xfb2a, 0xfb36, + 0xfb38, 0xfb3c, + 0xfb40, 0xfb41, + 0xfb43, 0xfb44, + 0xfb46, 0xfbb1, + 0xfbd3, 0xfd3d, + 0xfd50, 0xfd8f, + 0xfd92, 0xfdc7, + 0xfdf0, 0xfdfb, + 0xfe70, 0xfe74, + 0xfe76, 0xfefc, + 0xff21, 0xff3a, + 0xff41, 0xff5a, + 0xff66, 0xffbe, + 0xffc2, 0xffc7, + 0xffca, 0xffcf, + 0xffd2, 0xffd7, + 0xffda, 0xffdc, + 0x10000, 0x1000b, + 0x1000d, 0x10026, + 0x10028, 0x1003a, + 0x1003c, 0x1003d, + 0x1003f, 0x1004d, + 0x10050, 0x1005d, + 0x10080, 0x100fa, + 0x10280, 0x1029c, + 0x102a0, 0x102d0, + 0x10300, 0x1031e, + 0x10330, 0x10340, + 0x10342, 0x10349, + 0x10380, 0x1039d, + 0x103a0, 0x103c3, + 0x103c8, 0x103cf, + 0x10400, 0x1049d, + 0x10800, 0x10805, + 0x1080a, 0x10835, + 0x10837, 0x10838, + 0x1083f, 0x10855, + 0x10900, 0x10915, + 0x10920, 0x10939, + 0x10980, 0x109b7, + 0x109be, 0x109bf, + 0x10a10, 0x10a13, + 0x10a15, 0x10a17, + 0x10a19, 0x10a33, + 0x10a60, 0x10a7c, + 0x10b00, 0x10b35, + 0x10b40, 0x10b55, + 0x10b60, 0x10b72, + 0x10c00, 0x10c48, + 0x11003, 0x11037, + 0x11083, 0x110af, + 0x110d0, 0x110e8, + 0x11103, 0x11126, + 0x11183, 0x111b2, + 0x111c1, 0x111c4, + 0x11680, 0x116aa, + 0x12000, 0x1236e, + 0x13000, 0x1342e, + 0x16800, 0x16a38, + 0x16f00, 0x16f44, + 0x16f93, 0x16f9f, + 0x1b000, 0x1b001, + 0x1d400, 0x1d454, + 0x1d456, 0x1d49c, + 0x1d49e, 0x1d49f, + 0x1d4a5, 0x1d4a6, + 0x1d4a9, 0x1d4ac, + 0x1d4ae, 0x1d4b9, + 0x1d4bd, 0x1d4c3, + 0x1d4c5, 0x1d505, + 0x1d507, 0x1d50a, + 0x1d50d, 0x1d514, + 0x1d516, 0x1d51c, + 0x1d51e, 0x1d539, + 0x1d53b, 0x1d53e, + 0x1d540, 0x1d544, + 0x1d54a, 0x1d550, + 0x1d552, 0x1d6a5, + 0x1d6a8, 0x1d6c0, + 0x1d6c2, 0x1d6da, + 0x1d6dc, 0x1d6fa, + 0x1d6fc, 0x1d714, + 0x1d716, 0x1d734, + 0x1d736, 0x1d74e, + 0x1d750, 0x1d76e, + 0x1d770, 0x1d788, + 0x1d78a, 0x1d7a8, + 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7cb, + 0x1ee00, 0x1ee03, + 0x1ee05, 0x1ee1f, + 0x1ee21, 0x1ee22, + 0x1ee29, 0x1ee32, + 0x1ee34, 0x1ee37, + 0x1ee4d, 0x1ee4f, + 0x1ee51, 0x1ee52, + 0x1ee61, 0x1ee62, + 0x1ee67, 0x1ee6a, + 0x1ee6c, 0x1ee72, + 0x1ee74, 0x1ee77, + 0x1ee79, 0x1ee7c, + 0x1ee80, 0x1ee89, + 0x1ee8b, 0x1ee9b, + 0x1eea1, 0x1eea3, + 0x1eea5, 0x1eea9, + 0x1eeab, 0x1eebb, + 0x20000, 0x2a6d6, + 0x2a700, 0x2b734, + 0x2b740, 0x2b81d, + 0x2f800, 0x2fa1d, +}; + +static Rune __isalphas[] = { + 0x00aa, + 0x00b5, + 0x00ba, + 0x02ec, + 0x02ee, + 0x0386, + 0x038c, + 0x0559, + 0x06d5, + 0x06ff, + 0x0710, + 0x07b1, + 0x07fa, + 0x081a, + 0x0824, + 0x0828, + 0x08a0, + 0x093d, + 0x0950, + 0x09b2, + 0x09bd, + 0x09ce, + 0x0a5e, + 0x0abd, + 0x0ad0, + 0x0b3d, + 0x0b71, + 0x0b83, + 0x0b9c, + 0x0bd0, + 0x0c3d, + 0x0cbd, + 0x0cde, + 0x0d3d, + 0x0d4e, + 0x0dbd, + 0x0e84, + 0x0e8a, + 0x0e8d, + 0x0ea5, + 0x0ea7, + 0x0ebd, + 0x0ec6, + 0x0f00, + 0x103f, + 0x1061, + 0x108e, + 0x10c7, + 0x10cd, + 0x1258, + 0x12c0, + 0x17d7, + 0x17dc, + 0x18aa, + 0x1aa7, + 0x1f59, + 0x1f5b, + 0x1f5d, + 0x1fbe, + 0x2071, + 0x207f, + 0x2102, + 0x2107, + 0x2115, + 0x2124, + 0x2126, + 0x2128, + 0x214e, + 0x2d27, + 0x2d2d, + 0x2d6f, + 0x2e2f, + 0xa8fb, + 0xa9cf, + 0xaa7a, + 0xaab1, + 0xaac0, + 0xaac2, + 0xfb1d, + 0xfb3e, + 0x10808, + 0x1083c, + 0x10a00, + 0x16f50, + 0x1d4a2, + 0x1d4bb, + 0x1d546, + 0x1ee24, + 0x1ee27, + 0x1ee39, + 0x1ee3b, + 0x1ee42, + 0x1ee47, + 0x1ee49, + 0x1ee4b, + 0x1ee54, + 0x1ee57, + 0x1ee59, + 0x1ee5b, + 0x1ee5d, + 0x1ee5f, + 0x1ee64, + 0x1ee7e, +}; + +int +isalpharune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __isalphar, nelem(__isalphar)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = rbsearch(c, __isalphas, nelem(__isalphas), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +static Rune __isupperr[] = { + 0x0041, 0x005a, + 0x00c0, 0x00d6, + 0x00d8, 0x00de, + 0x0178, 0x0179, + 0x0181, 0x0182, + 0x0186, 0x0187, + 0x0189, 0x018b, + 0x018e, 0x0191, + 0x0193, 0x0194, + 0x0196, 0x0198, + 0x019c, 0x019d, + 0x019f, 0x01a0, + 0x01a6, 0x01a7, + 0x01ae, 0x01af, + 0x01b1, 0x01b3, + 0x01b7, 0x01b8, + 0x01f6, 0x01f8, + 0x023a, 0x023b, + 0x023d, 0x023e, + 0x0243, 0x0246, + 0x0388, 0x038a, + 0x038e, 0x038f, + 0x0391, 0x03a1, + 0x03a3, 0x03ab, + 0x03d2, 0x03d4, + 0x03f9, 0x03fa, + 0x03fd, 0x042f, + 0x04c0, 0x04c1, + 0x0531, 0x0556, + 0x10a0, 0x10c5, + 0x1f08, 0x1f0f, + 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, + 0x1f38, 0x1f3f, + 0x1f48, 0x1f4d, + 0x1f68, 0x1f6f, + 0x1f88, 0x1f8f, + 0x1f98, 0x1f9f, + 0x1fa8, 0x1faf, + 0x1fb8, 0x1fbc, + 0x1fc8, 0x1fcc, + 0x1fd8, 0x1fdb, + 0x1fe8, 0x1fec, + 0x1ff8, 0x1ffc, + 0x210b, 0x210d, + 0x2110, 0x2112, + 0x2119, 0x211d, + 0x212a, 0x212d, + 0x2130, 0x2133, + 0x213e, 0x213f, + 0x2160, 0x216f, + 0x24b6, 0x24cf, + 0x2c00, 0x2c2e, + 0x2c62, 0x2c64, + 0x2c6d, 0x2c70, + 0x2c7e, 0x2c80, + 0xa77d, 0xa77e, + 0xff21, 0xff3a, + 0x10400, 0x10427, + 0x1d400, 0x1d419, + 0x1d434, 0x1d44d, + 0x1d468, 0x1d481, + 0x1d49e, 0x1d49f, + 0x1d4a5, 0x1d4a6, + 0x1d4a9, 0x1d4ac, + 0x1d4ae, 0x1d4b5, + 0x1d4d0, 0x1d4e9, + 0x1d504, 0x1d505, + 0x1d507, 0x1d50a, + 0x1d50d, 0x1d514, + 0x1d516, 0x1d51c, + 0x1d538, 0x1d539, + 0x1d53b, 0x1d53e, + 0x1d540, 0x1d544, + 0x1d54a, 0x1d550, + 0x1d56c, 0x1d585, + 0x1d5a0, 0x1d5b9, + 0x1d5d4, 0x1d5ed, + 0x1d608, 0x1d621, + 0x1d63c, 0x1d655, + 0x1d670, 0x1d689, + 0x1d6a8, 0x1d6c0, + 0x1d6e2, 0x1d6fa, + 0x1d71c, 0x1d734, + 0x1d756, 0x1d76e, + 0x1d790, 0x1d7a8, +}; + +static Rune __isupperp[] = { + 0x0100, 0x0136, + 0x0139, 0x0147, + 0x014a, 0x0176, + 0x017b, 0x017d, + 0x01a2, 0x01a4, + 0x01cd, 0x01db, + 0x01de, 0x01ee, + 0x01fa, 0x0232, + 0x0248, 0x024e, + 0x0370, 0x0372, + 0x03d8, 0x03ee, + 0x0460, 0x0480, + 0x048a, 0x04be, + 0x04c3, 0x04cd, + 0x04d0, 0x0526, + 0x1e00, 0x1e94, + 0x1e9e, 0x1efe, + 0x1f59, 0x1f5f, + 0x2124, 0x2128, + 0x2c67, 0x2c6b, + 0x2c82, 0x2ce2, + 0x2ceb, 0x2ced, + 0xa640, 0xa66c, + 0xa680, 0xa696, + 0xa722, 0xa72e, + 0xa732, 0xa76e, + 0xa779, 0xa77b, + 0xa780, 0xa786, + 0xa78b, 0xa78d, + 0xa790, 0xa792, + 0xa7a0, 0xa7aa, +}; + +static Rune __isuppers[] = { + 0x0184, + 0x01a9, + 0x01ac, + 0x01b5, + 0x01bc, + 0x01c4, + 0x01c7, + 0x01ca, + 0x01f1, + 0x01f4, + 0x0241, + 0x0376, + 0x0386, + 0x038c, + 0x03cf, + 0x03f4, + 0x03f7, + 0x10c7, + 0x10cd, + 0x2102, + 0x2107, + 0x2115, + 0x2145, + 0x2183, + 0x2c60, + 0x2c72, + 0x2c75, + 0x2cf2, + 0x1d49c, + 0x1d4a2, + 0x1d546, + 0x1d7ca, +}; + +int +isupperrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __isupperr, nelem(__isupperr)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = rbsearch(c, __isupperp, nelem(__isupperp)/2, 2); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return 1; + p = rbsearch(c, __isuppers, nelem(__isuppers), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +static Rune __islowerr[] = { + 0x0061, 0x007a, + 0x00df, 0x00f6, + 0x00f8, 0x00ff, + 0x0137, 0x0138, + 0x0148, 0x0149, + 0x017e, 0x0180, + 0x018c, 0x018d, + 0x0199, 0x019b, + 0x01aa, 0x01ab, + 0x01b9, 0x01ba, + 0x01bd, 0x01bf, + 0x01dc, 0x01dd, + 0x01ef, 0x01f0, + 0x0233, 0x0239, + 0x023f, 0x0240, + 0x024f, 0x0293, + 0x0295, 0x02af, + 0x037b, 0x037d, + 0x03ac, 0x03ce, + 0x03d0, 0x03d1, + 0x03d5, 0x03d7, + 0x03ef, 0x03f3, + 0x03fb, 0x03fc, + 0x0430, 0x045f, + 0x04ce, 0x04cf, + 0x0561, 0x0587, + 0x1d00, 0x1d2b, + 0x1d6b, 0x1d77, + 0x1d79, 0x1d9a, + 0x1e95, 0x1e9d, + 0x1eff, 0x1f07, + 0x1f10, 0x1f15, + 0x1f20, 0x1f27, + 0x1f30, 0x1f37, + 0x1f40, 0x1f45, + 0x1f50, 0x1f57, + 0x1f60, 0x1f67, + 0x1f70, 0x1f7d, + 0x1f80, 0x1f87, + 0x1f90, 0x1f97, + 0x1fa0, 0x1fa7, + 0x1fb0, 0x1fb4, + 0x1fb6, 0x1fb7, + 0x1fc2, 0x1fc4, + 0x1fc6, 0x1fc7, + 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fd7, + 0x1fe0, 0x1fe7, + 0x1ff2, 0x1ff4, + 0x1ff6, 0x1ff7, + 0x210e, 0x210f, + 0x213c, 0x213d, + 0x2146, 0x2149, + 0x2170, 0x217f, + 0x24d0, 0x24e9, + 0x2c30, 0x2c5e, + 0x2c65, 0x2c66, + 0x2c73, 0x2c74, + 0x2c76, 0x2c7b, + 0x2ce3, 0x2ce4, + 0x2d00, 0x2d25, + 0xa72f, 0xa731, + 0xa771, 0xa778, + 0xfb00, 0xfb06, + 0xfb13, 0xfb17, + 0xff41, 0xff5a, + 0x10428, 0x1044f, + 0x1d41a, 0x1d433, + 0x1d44e, 0x1d454, + 0x1d456, 0x1d467, + 0x1d482, 0x1d49b, + 0x1d4b6, 0x1d4b9, + 0x1d4bd, 0x1d4c3, + 0x1d4c5, 0x1d4cf, + 0x1d4ea, 0x1d503, + 0x1d51e, 0x1d537, + 0x1d552, 0x1d56b, + 0x1d586, 0x1d59f, + 0x1d5ba, 0x1d5d3, + 0x1d5ee, 0x1d607, + 0x1d622, 0x1d63b, + 0x1d656, 0x1d66f, + 0x1d68a, 0x1d6a5, + 0x1d6c2, 0x1d6da, + 0x1d6dc, 0x1d6e1, + 0x1d6fc, 0x1d714, + 0x1d716, 0x1d71b, + 0x1d736, 0x1d74e, + 0x1d750, 0x1d755, + 0x1d770, 0x1d788, + 0x1d78a, 0x1d78f, + 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7c9, +}; + +static Rune __islowerp[] = { + 0x0101, 0x0135, + 0x013a, 0x0146, + 0x014b, 0x0177, + 0x017a, 0x017c, + 0x0183, 0x0185, + 0x01a1, 0x01a5, + 0x01b4, 0x01b6, + 0x01cc, 0x01da, + 0x01df, 0x01ed, + 0x01f3, 0x01f5, + 0x01f9, 0x0231, + 0x0247, 0x024d, + 0x0371, 0x0373, + 0x03d9, 0x03ed, + 0x0461, 0x0481, + 0x048b, 0x04bf, + 0x04c2, 0x04cc, + 0x04d1, 0x0527, + 0x1e01, 0x1e93, + 0x1e9f, 0x1efd, + 0x2c68, 0x2c6c, + 0x2c81, 0x2ce1, + 0x2cec, 0x2cee, + 0xa641, 0xa66d, + 0xa681, 0xa697, + 0xa723, 0xa72d, + 0xa733, 0xa76f, + 0xa77a, 0xa77c, + 0xa77f, 0xa787, + 0xa78c, 0xa78e, + 0xa791, 0xa793, + 0xa7a1, 0xa7a9, +}; + +static Rune __islowers[] = { + 0x00b5, + 0x0188, + 0x0192, + 0x0195, + 0x019e, + 0x01a8, + 0x01ad, + 0x01b0, + 0x01c6, + 0x01c9, + 0x023c, + 0x0242, + 0x0377, + 0x0390, + 0x03f5, + 0x03f8, + 0x1fbe, + 0x210a, + 0x2113, + 0x212f, + 0x2134, + 0x2139, + 0x214e, + 0x2184, + 0x2c61, + 0x2c71, + 0x2cf3, + 0x2d27, + 0x2d2d, + 0xa7fa, + 0x1d4bb, + 0x1d7cb, +}; + +int +islowerrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __islowerr, nelem(__islowerr)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = rbsearch(c, __islowerp, nelem(__islowerp)/2, 2); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return 1; + p = rbsearch(c, __islowers, nelem(__islowers), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +static Rune __istitler[] = { + 0x0041, 0x005a, + 0x00c0, 0x00d6, + 0x00d8, 0x00de, + 0x0178, 0x0179, + 0x0181, 0x0182, + 0x0186, 0x0187, + 0x0189, 0x018b, + 0x018e, 0x0191, + 0x0193, 0x0194, + 0x0196, 0x0198, + 0x019c, 0x019d, + 0x019f, 0x01a0, + 0x01a6, 0x01a7, + 0x01ae, 0x01af, + 0x01b1, 0x01b3, + 0x01b7, 0x01b8, + 0x01f6, 0x01f8, + 0x023a, 0x023b, + 0x023d, 0x023e, + 0x0243, 0x0246, + 0x0388, 0x038a, + 0x038e, 0x038f, + 0x0391, 0x03a1, + 0x03a3, 0x03ab, + 0x03f9, 0x03fa, + 0x03fd, 0x042f, + 0x04c0, 0x04c1, + 0x0531, 0x0556, + 0x10a0, 0x10c5, + 0x1f08, 0x1f0f, + 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, + 0x1f38, 0x1f3f, + 0x1f48, 0x1f4d, + 0x1f68, 0x1f6f, + 0x1f88, 0x1f8f, + 0x1f98, 0x1f9f, + 0x1fa8, 0x1faf, + 0x1fb8, 0x1fbc, + 0x1fc8, 0x1fcc, + 0x1fd8, 0x1fdb, + 0x1fe8, 0x1fec, + 0x1ff8, 0x1ffc, + 0x2160, 0x216f, + 0x24b6, 0x24cf, + 0x2c00, 0x2c2e, + 0x2c62, 0x2c64, + 0x2c6d, 0x2c70, + 0x2c7e, 0x2c80, + 0xa77d, 0xa77e, + 0xff21, 0xff3a, + 0x10400, 0x10427, +}; + +static Rune __istitlep[] = { + 0x0100, 0x012e, + 0x0132, 0x0136, + 0x0139, 0x0147, + 0x014a, 0x0176, + 0x017b, 0x017d, + 0x01a2, 0x01a4, + 0x01cb, 0x01db, + 0x01de, 0x01ee, + 0x01f2, 0x01f4, + 0x01fa, 0x0232, + 0x0248, 0x024e, + 0x0370, 0x0372, + 0x03d8, 0x03ee, + 0x0460, 0x0480, + 0x048a, 0x04be, + 0x04c3, 0x04cd, + 0x04d0, 0x0526, + 0x1e00, 0x1e94, + 0x1ea0, 0x1efe, + 0x1f59, 0x1f5f, + 0x2c67, 0x2c6b, + 0x2c82, 0x2ce2, + 0x2ceb, 0x2ced, + 0xa640, 0xa66c, + 0xa680, 0xa696, + 0xa722, 0xa72e, + 0xa732, 0xa76e, + 0xa779, 0xa77b, + 0xa780, 0xa786, + 0xa78b, 0xa78d, + 0xa790, 0xa792, + 0xa7a0, 0xa7aa, +}; + +static Rune __istitles[] = { + 0x0184, + 0x01a9, + 0x01ac, + 0x01b5, + 0x01bc, + 0x01c5, + 0x01c8, + 0x0241, + 0x0376, + 0x0386, + 0x038c, + 0x03cf, + 0x03f7, + 0x10c7, + 0x10cd, + 0x2132, + 0x2183, + 0x2c60, + 0x2c72, + 0x2c75, + 0x2cf2, +}; + +int +istitlerune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __istitler, nelem(__istitler)/2, 2); + if(p && c >= p[0] && c <= p[1]) + return 1; + p = rbsearch(c, __istitlep, nelem(__istitlep)/2, 2); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return 1; + p = rbsearch(c, __istitles, nelem(__istitles), 1); + if(p && c == p[0]) + return 1; + return 0; +} + +static Rune __toupperr[] = { + 0x0061, 0x007a, 1048544, + 0x00e0, 0x00f6, 1048544, + 0x00f8, 0x00fe, 1048544, + 0x023f, 0x0240, 1059391, + 0x0256, 0x0257, 1048371, + 0x028a, 0x028b, 1048359, + 0x037b, 0x037d, 1048706, + 0x03ad, 0x03af, 1048539, + 0x03b1, 0x03c1, 1048544, + 0x03c3, 0x03cb, 1048544, + 0x03cd, 0x03ce, 1048513, + 0x0430, 0x044f, 1048544, + 0x0450, 0x045f, 1048496, + 0x0561, 0x0586, 1048528, + 0x1f00, 0x1f07, 1048584, + 0x1f10, 0x1f15, 1048584, + 0x1f20, 0x1f27, 1048584, + 0x1f30, 0x1f37, 1048584, + 0x1f40, 0x1f45, 1048584, + 0x1f60, 0x1f67, 1048584, + 0x1f70, 0x1f71, 1048650, + 0x1f72, 0x1f75, 1048662, + 0x1f76, 0x1f77, 1048676, + 0x1f78, 0x1f79, 1048704, + 0x1f7a, 0x1f7b, 1048688, + 0x1f7c, 0x1f7d, 1048702, + 0x1f80, 0x1f87, 1048584, + 0x1f90, 0x1f97, 1048584, + 0x1fa0, 0x1fa7, 1048584, + 0x1fb0, 0x1fb1, 1048584, + 0x1fd0, 0x1fd1, 1048584, + 0x1fe0, 0x1fe1, 1048584, + 0x2170, 0x217f, 1048560, + 0x24d0, 0x24e9, 1048550, + 0x2c30, 0x2c5e, 1048528, + 0x2d00, 0x2d25, 1041312, + 0xff41, 0xff5a, 1048544, + 0x10428, 0x1044f, 1048536, +}; + +static Rune __toupperp[] = { + 0x0101, 0x012f, 1048575, + 0x0133, 0x0137, 1048575, + 0x013a, 0x0148, 1048575, + 0x014b, 0x0177, 1048575, + 0x017a, 0x017e, 1048575, + 0x0183, 0x0185, 1048575, + 0x01a1, 0x01a5, 1048575, + 0x01b4, 0x01b6, 1048575, + 0x01ce, 0x01dc, 1048575, + 0x01df, 0x01ef, 1048575, + 0x01f9, 0x021f, 1048575, + 0x0223, 0x0233, 1048575, + 0x0247, 0x024f, 1048575, + 0x0371, 0x0373, 1048575, + 0x03d9, 0x03ef, 1048575, + 0x0461, 0x0481, 1048575, + 0x048b, 0x04bf, 1048575, + 0x04c2, 0x04ce, 1048575, + 0x04d1, 0x0527, 1048575, + 0x1e01, 0x1e95, 1048575, + 0x1ea1, 0x1eff, 1048575, + 0x1f51, 0x1f57, 1048584, + 0x2c68, 0x2c6c, 1048575, + 0x2c81, 0x2ce3, 1048575, + 0x2cec, 0x2cee, 1048575, + 0xa641, 0xa66d, 1048575, + 0xa681, 0xa697, 1048575, + 0xa723, 0xa72f, 1048575, + 0xa733, 0xa76f, 1048575, + 0xa77a, 0xa77c, 1048575, + 0xa77f, 0xa787, 1048575, + 0xa791, 0xa793, 1048575, + 0xa7a1, 0xa7a9, 1048575, +}; + +static Rune __touppers[] = { + 0x00b5, 1049319, + 0x00ff, 1048697, + 0x0131, 1048344, + 0x017f, 1048276, + 0x0180, 1048771, + 0x0188, 1048575, + 0x018c, 1048575, + 0x0192, 1048575, + 0x0195, 1048673, + 0x0199, 1048575, + 0x019a, 1048739, + 0x019e, 1048706, + 0x01a8, 1048575, + 0x01ad, 1048575, + 0x01b0, 1048575, + 0x01b9, 1048575, + 0x01bd, 1048575, + 0x01bf, 1048632, + 0x01c5, 1048575, + 0x01c6, 1048574, + 0x01c8, 1048575, + 0x01c9, 1048574, + 0x01cb, 1048575, + 0x01cc, 1048574, + 0x01dd, 1048497, + 0x01f2, 1048575, + 0x01f3, 1048574, + 0x01f5, 1048575, + 0x023c, 1048575, + 0x0242, 1048575, + 0x0250, 1059359, + 0x0251, 1059356, + 0x0252, 1059358, + 0x0253, 1048366, + 0x0254, 1048370, + 0x0259, 1048374, + 0x025b, 1048373, + 0x0260, 1048371, + 0x0263, 1048369, + 0x0265, 1090856, + 0x0266, 1090884, + 0x0268, 1048367, + 0x0269, 1048365, + 0x026b, 1059319, + 0x026f, 1048365, + 0x0271, 1059325, + 0x0272, 1048363, + 0x0275, 1048362, + 0x027d, 1059303, + 0x0280, 1048358, + 0x0283, 1048358, + 0x0288, 1048358, + 0x0289, 1048507, + 0x028c, 1048505, + 0x0292, 1048357, + 0x0345, 1048660, + 0x0377, 1048575, + 0x03ac, 1048538, + 0x03c2, 1048545, + 0x03cc, 1048512, + 0x03d0, 1048514, + 0x03d1, 1048519, + 0x03d5, 1048529, + 0x03d6, 1048522, + 0x03d7, 1048568, + 0x03f0, 1048490, + 0x03f1, 1048496, + 0x03f2, 1048583, + 0x03f5, 1048480, + 0x03f8, 1048575, + 0x03fb, 1048575, + 0x04cf, 1048561, + 0x1d79, 1083908, + 0x1d7d, 1052390, + 0x1e9b, 1048517, + 0x1fb3, 1048585, + 0x1fbe, 1041371, + 0x1fc3, 1048585, + 0x1fe5, 1048583, + 0x1ff3, 1048585, + 0x214e, 1048548, + 0x2184, 1048575, + 0x2c61, 1048575, + 0x2c65, 1037781, + 0x2c66, 1037784, + 0x2c73, 1048575, + 0x2c76, 1048575, + 0x2cf3, 1048575, + 0x2d27, 1041312, + 0x2d2d, 1041312, + 0xa78c, 1048575, +}; + +Rune +toupperrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __toupperr, nelem(__toupperr)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + p = rbsearch(c, __toupperp, nelem(__toupperp)/3, 3); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return c + p[2] - 1048576; + p = rbsearch(c, __touppers, nelem(__touppers)/2, 2); + if(p && c == p[0]) + return c + p[1] - 1048576; + return c; +} + +static Rune __tolowerr[] = { + 0x0041, 0x005a, 1048608, + 0x00c0, 0x00d6, 1048608, + 0x00d8, 0x00de, 1048608, + 0x0189, 0x018a, 1048781, + 0x01b1, 0x01b2, 1048793, + 0x0388, 0x038a, 1048613, + 0x038e, 0x038f, 1048639, + 0x0391, 0x03a1, 1048608, + 0x03a3, 0x03ab, 1048608, + 0x03fd, 0x03ff, 1048446, + 0x0400, 0x040f, 1048656, + 0x0410, 0x042f, 1048608, + 0x0531, 0x0556, 1048624, + 0x10a0, 0x10c5, 1055840, + 0x1f08, 0x1f0f, 1048568, + 0x1f18, 0x1f1d, 1048568, + 0x1f28, 0x1f2f, 1048568, + 0x1f38, 0x1f3f, 1048568, + 0x1f48, 0x1f4d, 1048568, + 0x1f68, 0x1f6f, 1048568, + 0x1f88, 0x1f8f, 1048568, + 0x1f98, 0x1f9f, 1048568, + 0x1fa8, 0x1faf, 1048568, + 0x1fb8, 0x1fb9, 1048568, + 0x1fba, 0x1fbb, 1048502, + 0x1fc8, 0x1fcb, 1048490, + 0x1fd8, 0x1fd9, 1048568, + 0x1fda, 0x1fdb, 1048476, + 0x1fe8, 0x1fe9, 1048568, + 0x1fea, 0x1feb, 1048464, + 0x1ff8, 0x1ff9, 1048448, + 0x1ffa, 0x1ffb, 1048450, + 0x2160, 0x216f, 1048592, + 0x24b6, 0x24cf, 1048602, + 0x2c00, 0x2c2e, 1048624, + 0x2c7e, 0x2c7f, 1037761, + 0xff21, 0xff3a, 1048608, + 0x10400, 0x10427, 1048616, +}; + +static Rune __tolowerp[] = { + 0x0100, 0x012e, 1048577, + 0x0132, 0x0136, 1048577, + 0x0139, 0x0147, 1048577, + 0x014a, 0x0176, 1048577, + 0x017b, 0x017d, 1048577, + 0x01a2, 0x01a4, 1048577, + 0x01b3, 0x01b5, 1048577, + 0x01cd, 0x01db, 1048577, + 0x01de, 0x01ee, 1048577, + 0x01f8, 0x021e, 1048577, + 0x0222, 0x0232, 1048577, + 0x0248, 0x024e, 1048577, + 0x0370, 0x0372, 1048577, + 0x03d8, 0x03ee, 1048577, + 0x0460, 0x0480, 1048577, + 0x048a, 0x04be, 1048577, + 0x04c3, 0x04cd, 1048577, + 0x04d0, 0x0526, 1048577, + 0x1e00, 0x1e94, 1048577, + 0x1ea0, 0x1efe, 1048577, + 0x1f59, 0x1f5f, 1048568, + 0x2c67, 0x2c6b, 1048577, + 0x2c80, 0x2ce2, 1048577, + 0x2ceb, 0x2ced, 1048577, + 0xa640, 0xa66c, 1048577, + 0xa680, 0xa696, 1048577, + 0xa722, 0xa72e, 1048577, + 0xa732, 0xa76e, 1048577, + 0xa779, 0xa77b, 1048577, + 0xa780, 0xa786, 1048577, + 0xa790, 0xa792, 1048577, + 0xa7a0, 0xa7a8, 1048577, +}; + +static Rune __tolowers[] = { + 0x0130, 1048377, + 0x0178, 1048455, + 0x0179, 1048577, + 0x0181, 1048786, + 0x0182, 1048577, + 0x0184, 1048577, + 0x0186, 1048782, + 0x0187, 1048577, + 0x018b, 1048577, + 0x018e, 1048655, + 0x018f, 1048778, + 0x0190, 1048779, + 0x0191, 1048577, + 0x0193, 1048781, + 0x0194, 1048783, + 0x0196, 1048787, + 0x0197, 1048785, + 0x0198, 1048577, + 0x019c, 1048787, + 0x019d, 1048789, + 0x019f, 1048790, + 0x01a0, 1048577, + 0x01a6, 1048794, + 0x01a7, 1048577, + 0x01a9, 1048794, + 0x01ac, 1048577, + 0x01ae, 1048794, + 0x01af, 1048577, + 0x01b7, 1048795, + 0x01b8, 1048577, + 0x01bc, 1048577, + 0x01c4, 1048578, + 0x01c5, 1048577, + 0x01c7, 1048578, + 0x01c8, 1048577, + 0x01ca, 1048578, + 0x01cb, 1048577, + 0x01f1, 1048578, + 0x01f2, 1048577, + 0x01f4, 1048577, + 0x01f6, 1048479, + 0x01f7, 1048520, + 0x0220, 1048446, + 0x023a, 1059371, + 0x023b, 1048577, + 0x023d, 1048413, + 0x023e, 1059368, + 0x0241, 1048577, + 0x0243, 1048381, + 0x0244, 1048645, + 0x0245, 1048647, + 0x0246, 1048577, + 0x0376, 1048577, + 0x0386, 1048614, + 0x038c, 1048640, + 0x03cf, 1048584, + 0x03f4, 1048516, + 0x03f7, 1048577, + 0x03f9, 1048569, + 0x03fa, 1048577, + 0x04c0, 1048591, + 0x04c1, 1048577, + 0x10c7, 1055840, + 0x10cd, 1055840, + 0x1e9e, 1040961, + 0x1fbc, 1048567, + 0x1fcc, 1048567, + 0x1fec, 1048569, + 0x1ffc, 1048567, + 0x2126, 1041059, + 0x212a, 1040193, + 0x212b, 1040314, + 0x2132, 1048604, + 0x2183, 1048577, + 0x2c60, 1048577, + 0x2c62, 1037833, + 0x2c63, 1044762, + 0x2c64, 1037849, + 0x2c6d, 1037796, + 0x2c6e, 1037827, + 0x2c6f, 1037793, + 0x2c70, 1037794, + 0x2c72, 1048577, + 0x2c75, 1048577, + 0x2cf2, 1048577, + 0xa77d, 1013244, + 0xa77e, 1048577, + 0xa78b, 1048577, + 0xa78d, 1006296, + 0xa7aa, 1006268, +}; + +Rune +tolowerrune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __tolowerr, nelem(__tolowerr)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + p = rbsearch(c, __tolowerp, nelem(__tolowerp)/3, 3); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return c + p[2] - 1048576; + p = rbsearch(c, __tolowers, nelem(__tolowers)/2, 2); + if(p && c == p[0]) + return c + p[1] - 1048576; + return c; +} + +static Rune __totitler[] = { + 0x0061, 0x007a, 1048544, + 0x00e0, 0x00f6, 1048544, + 0x00f8, 0x00fe, 1048544, + 0x023f, 0x0240, 1059391, + 0x0256, 0x0257, 1048371, + 0x028a, 0x028b, 1048359, + 0x037b, 0x037d, 1048706, + 0x03ad, 0x03af, 1048539, + 0x03b1, 0x03c1, 1048544, + 0x03c3, 0x03cb, 1048544, + 0x03cd, 0x03ce, 1048513, + 0x0430, 0x044f, 1048544, + 0x0450, 0x045f, 1048496, + 0x0561, 0x0586, 1048528, + 0x1f00, 0x1f07, 1048584, + 0x1f10, 0x1f15, 1048584, + 0x1f20, 0x1f27, 1048584, + 0x1f30, 0x1f37, 1048584, + 0x1f40, 0x1f45, 1048584, + 0x1f60, 0x1f67, 1048584, + 0x1f70, 0x1f71, 1048650, + 0x1f72, 0x1f75, 1048662, + 0x1f76, 0x1f77, 1048676, + 0x1f78, 0x1f79, 1048704, + 0x1f7a, 0x1f7b, 1048688, + 0x1f7c, 0x1f7d, 1048702, + 0x1f80, 0x1f87, 1048584, + 0x1f90, 0x1f97, 1048584, + 0x1fa0, 0x1fa7, 1048584, + 0x1fb0, 0x1fb1, 1048584, + 0x1fd0, 0x1fd1, 1048584, + 0x1fe0, 0x1fe1, 1048584, + 0x2170, 0x217f, 1048560, + 0x24d0, 0x24e9, 1048550, + 0x2c30, 0x2c5e, 1048528, + 0x2d00, 0x2d25, 1041312, + 0xff41, 0xff5a, 1048544, + 0x10428, 0x1044f, 1048536, +}; + +static Rune __totitlep[] = { + 0x0101, 0x012f, 1048575, + 0x0133, 0x0137, 1048575, + 0x013a, 0x0148, 1048575, + 0x014b, 0x0177, 1048575, + 0x017a, 0x017e, 1048575, + 0x0183, 0x0185, 1048575, + 0x01a1, 0x01a5, 1048575, + 0x01b4, 0x01b6, 1048575, + 0x01cc, 0x01dc, 1048575, + 0x01df, 0x01ef, 1048575, + 0x01f3, 0x01f5, 1048575, + 0x01f9, 0x021f, 1048575, + 0x0223, 0x0233, 1048575, + 0x0247, 0x024f, 1048575, + 0x0371, 0x0373, 1048575, + 0x03d9, 0x03ef, 1048575, + 0x0461, 0x0481, 1048575, + 0x048b, 0x04bf, 1048575, + 0x04c2, 0x04ce, 1048575, + 0x04d1, 0x0527, 1048575, + 0x1e01, 0x1e95, 1048575, + 0x1ea1, 0x1eff, 1048575, + 0x1f51, 0x1f57, 1048584, + 0x2c68, 0x2c6c, 1048575, + 0x2c81, 0x2ce3, 1048575, + 0x2cec, 0x2cee, 1048575, + 0xa641, 0xa66d, 1048575, + 0xa681, 0xa697, 1048575, + 0xa723, 0xa72f, 1048575, + 0xa733, 0xa76f, 1048575, + 0xa77a, 0xa77c, 1048575, + 0xa77f, 0xa787, 1048575, + 0xa791, 0xa793, 1048575, + 0xa7a1, 0xa7a9, 1048575, +}; + +static Rune __totitles[] = { + 0x00b5, 1049319, + 0x00ff, 1048697, + 0x0131, 1048344, + 0x017f, 1048276, + 0x0180, 1048771, + 0x0188, 1048575, + 0x018c, 1048575, + 0x0192, 1048575, + 0x0195, 1048673, + 0x0199, 1048575, + 0x019a, 1048739, + 0x019e, 1048706, + 0x01a8, 1048575, + 0x01ad, 1048575, + 0x01b0, 1048575, + 0x01b9, 1048575, + 0x01bd, 1048575, + 0x01bf, 1048632, + 0x01c4, 1048577, + 0x01c6, 1048575, + 0x01c7, 1048577, + 0x01c9, 1048575, + 0x01ca, 1048577, + 0x01dd, 1048497, + 0x01f1, 1048577, + 0x023c, 1048575, + 0x0242, 1048575, + 0x0250, 1059359, + 0x0251, 1059356, + 0x0252, 1059358, + 0x0253, 1048366, + 0x0254, 1048370, + 0x0259, 1048374, + 0x025b, 1048373, + 0x0260, 1048371, + 0x0263, 1048369, + 0x0265, 1090856, + 0x0266, 1090884, + 0x0268, 1048367, + 0x0269, 1048365, + 0x026b, 1059319, + 0x026f, 1048365, + 0x0271, 1059325, + 0x0272, 1048363, + 0x0275, 1048362, + 0x027d, 1059303, + 0x0280, 1048358, + 0x0283, 1048358, + 0x0288, 1048358, + 0x0289, 1048507, + 0x028c, 1048505, + 0x0292, 1048357, + 0x0345, 1048660, + 0x0377, 1048575, + 0x03ac, 1048538, + 0x03c2, 1048545, + 0x03cc, 1048512, + 0x03d0, 1048514, + 0x03d1, 1048519, + 0x03d5, 1048529, + 0x03d6, 1048522, + 0x03d7, 1048568, + 0x03f0, 1048490, + 0x03f1, 1048496, + 0x03f2, 1048583, + 0x03f5, 1048480, + 0x03f8, 1048575, + 0x03fb, 1048575, + 0x04cf, 1048561, + 0x1d79, 1083908, + 0x1d7d, 1052390, + 0x1e9b, 1048517, + 0x1fb3, 1048585, + 0x1fbe, 1041371, + 0x1fc3, 1048585, + 0x1fe5, 1048583, + 0x1ff3, 1048585, + 0x214e, 1048548, + 0x2184, 1048575, + 0x2c61, 1048575, + 0x2c65, 1037781, + 0x2c66, 1037784, + 0x2c73, 1048575, + 0x2c76, 1048575, + 0x2cf3, 1048575, + 0x2d27, 1041312, + 0x2d2d, 1041312, + 0xa78c, 1048575, +}; + +Rune +totitlerune(Rune c) +{ + Rune *p; + + p = rbsearch(c, __totitler, nelem(__totitler)/3, 3); + if(p && c >= p[0] && c <= p[1]) + return c + p[2] - 1048576; + p = rbsearch(c, __totitlep, nelem(__totitlep)/3, 3); + if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1)) + return c + p[2] - 1048576; + p = rbsearch(c, __totitles, nelem(__totitles)/2, 2); + if(p && c == p[0]) + return c + p[1] - 1048576; + return c; +} + diff --git a/src/lib9/windows.c b/src/lib9/windows.c index 90753bb8d..d8ee402a2 100644 --- a/src/lib9/windows.c +++ b/src/lib9/windows.c @@ -5,22 +5,26 @@ #include #include -int fork() +int +fork(void) { return -1; } -int p9rfork(int flags) +int +p9rfork(int flags) { return -1; } -Waitmsg *p9wait() +Waitmsg* +p9wait(void) { return 0; } -int p9waitpid() +int +p9waitpid(void) { return -1; } diff --git a/src/libmach/5obj.c b/src/libmach/5obj.c index e539362b0..57573b866 100644 --- a/src/libmach/5obj.c +++ b/src/libmach/5obj.c @@ -42,6 +42,7 @@ struct Addr char type; char sym; char name; + char gotype; }; static Addr addr(Biobuf*); static char type2char(int); @@ -115,6 +116,7 @@ addr(Biobuf *bp) skip(bp,1); /* reg */ a.sym = Bgetc(bp); /* sym index */ a.name = Bgetc(bp); /* sym type */ + a.gotype = Bgetc(bp); /* go type */ switch(a.type){ default: case D_NONE: @@ -124,6 +126,7 @@ addr(Biobuf *bp) case D_FPCR: break; case D_REGREG: + case D_REGREG2: Bgetc(bp); break; case D_CONST2: diff --git a/src/libmach/8db.c b/src/libmach/8db.c index ce1b4ddd7..9ef02c428 100644 --- a/src/libmach/8db.c +++ b/src/libmach/8db.c @@ -622,6 +622,7 @@ static Optable optab0F[256]= [0x15] = { RM,0, "UNPCKH%s %x,%X" }, [0x16] = { RM,0, "MOV[L]H%s %x,%X" }, /* TO DO: L if source is XMM */ [0x17] = { RM,0, "MOVH%s %X,%x" }, +[0x1F] = { RM,0, "NOP%S %e" }, [0x20] = { RMR,0, "MOVL %C,%e" }, [0x21] = { RMR,0, "MOVL %D,%e" }, [0x22] = { RMR,0, "MOVL %e,%C" }, diff --git a/src/libmach/darwin.c b/src/libmach/darwin.c index 63abde313..753d940fd 100644 --- a/src/libmach/darwin.c +++ b/src/libmach/darwin.c @@ -751,7 +751,7 @@ havet: static void* excthread(void *v) { - extern boolean_t exc_server(); + extern boolean_t exc_server(mach_msg_header_t *, mach_msg_header_t *); mach_msg_server(exc_server, 2048, excport, 0); return 0; } diff --git a/src/libmach/executable.c b/src/libmach/executable.c index 3db3e7da4..a93a8c268 100644 --- a/src/libmach/executable.c +++ b/src/libmach/executable.c @@ -1091,12 +1091,21 @@ machdotout(int fd, Fhdr *fp, ExecHdr *hp) } cmdbuf = malloc(mp->sizeofcmds); + if(!cmdbuf) { + werrstr("out of memory"); + return 0; + } seek(fd, hdrsize, 0); if(read(fd, cmdbuf, mp->sizeofcmds) != mp->sizeofcmds) { free(cmdbuf); return 0; } cmd = malloc(mp->ncmds * sizeof(MachCmd*)); + if(!cmd) { + free(cmdbuf); + werrstr("out of memory"); + return 0; + } cmdp = cmdbuf; textva = 0; textoff = 0; @@ -1340,12 +1349,12 @@ static int pedotout(int fd, Fhdr *fp, ExecHdr *hp) { uint32 start, magic; - uint32 symtab, esymtab; + uint32 symtab, esymtab, pclntab, epclntab; IMAGE_FILE_HEADER fh; IMAGE_SECTION_HEADER sh; IMAGE_OPTIONAL_HEADER oh; uint8 sym[18]; - uint32 *valp; + uint32 *valp, ib; int i; USED(hp); @@ -1380,6 +1389,19 @@ pedotout(int fd, Fhdr *fp, ExecHdr *hp) return 0; } + switch(oh.Magic) { + case 0x10b: // PE32 + fp->type = FI386; + break; + case 0x20b: // PE32+ + fp->type = FAMD64; + break; + default: + werrstr("invalid PE Optional magic number"); + return 0; + } + + ib=leswal(oh.ImageBase); seek(fd, start+sizeof(magic)+sizeof(fh)+leswab(fh.SizeOfOptionalHeader), 0); fp->txtaddr = 0; fp->dataddr = 0; @@ -1389,9 +1411,9 @@ pedotout(int fd, Fhdr *fp, ExecHdr *hp) return 0; } if (match8(sh.Name, ".text")) - settext(fp, leswal(sh.VirtualAddress), leswal(oh.AddressOfEntryPoint), leswal(sh.VirtualSize), leswal(sh.PointerToRawData)); + settext(fp, ib+leswal(oh.AddressOfEntryPoint), ib+leswal(sh.VirtualAddress), leswal(sh.VirtualSize), leswal(sh.PointerToRawData)); if (match8(sh.Name, ".data")) - setdata(fp, leswal(sh.VirtualAddress), leswal(sh.SizeOfRawData), leswal(sh.PointerToRawData), leswal(sh.VirtualSize)-leswal(sh.SizeOfRawData)); + setdata(fp, ib+leswal(sh.VirtualAddress), leswal(sh.SizeOfRawData), leswal(sh.PointerToRawData), leswal(sh.VirtualSize)-leswal(sh.SizeOfRawData)); } if (fp->txtaddr==0 || fp->dataddr==0) { werrstr("no .text or .data"); @@ -1399,7 +1421,7 @@ pedotout(int fd, Fhdr *fp, ExecHdr *hp) } seek(fd, leswal(fh.PointerToSymbolTable), 0); - symtab = esymtab = 0; + symtab = esymtab = pclntab = epclntab = 0; for (i=0; is.name = name; sp->s.type = type; sp->s.sig = sig; diff --git a/src/libmach/sym.c b/src/libmach/sym.c index 1512d7a4f..120328d09 100644 --- a/src/libmach/sym.c +++ b/src/libmach/sym.c @@ -1,11 +1,11 @@ // Inferno libmach/sym.c // http://code.google.com/p/inferno-os/source/browse/utils/libmach/sym.c // -// Copyright © 1994-1999 Lucent Technologies Inc. -// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net). -// Portions Copyright © 1997-1999 Vita Nuova Limited. -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). -// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others. +// Copyright © 1994-1999 Lucent Technologies Inc. +// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net). +// Portions Copyright © 1997-1999 Vita Nuova Limited. +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). +// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others. // Portions Copyright © 2009 The Go Authors. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -112,13 +112,19 @@ syminit(int fd, Fhdr *fp) int32 i, l, size; vlong vl; Biobuf b; - int svalsz; + int svalsz, newformat, shift; + uvlong (*swav)(uvlong); + uint32 (*swal)(uint32); + uchar buf[8], c; if(fp->symsz == 0) return 0; if(fp->type == FNONE) return 0; + swav = beswav; + swal = beswal; + cleansyms(); textseg(fp->txtaddr, fp); /* minimum symbol record size = 4+1+2 bytes */ @@ -129,40 +135,128 @@ syminit(int fd, Fhdr *fp) } Binit(&b, fd, OREAD); Bseek(&b, fp->symoff, 0); + memset(buf, 0, sizeof buf); + Bread(&b, buf, sizeof buf); + newformat = 0; + if(memcmp(buf, "\xfd\xff\xff\xff\x00\x00\x00", 7) == 0) { + swav = leswav; + swal = leswal; + newformat = 1; + } else if(memcmp(buf, "\xff\xff\xff\xfd\x00\x00\x00", 7) == 0) { + newformat = 1; + } else if(memcmp(buf, "\xfe\xff\xff\xff\x00\x00", 6) == 0) { + // Table format used between Go 1.0 and Go 1.1: + // little-endian but otherwise same as the old Go 1.0 table. + // Not likely to be seen much in practice, but easy to handle. + swav = leswav; + swal = leswal; + Bseek(&b, fp->symoff+6, 0); + } else { + Bseek(&b, fp->symoff, 0); + } + svalsz = 0; + if(newformat) { + svalsz = buf[7]; + if(svalsz != 4 && svalsz != 8) { + werrstr("invalid word size %d bytes", svalsz); + return -1; + } + } + nsym = 0; size = 0; for(p = symbols; size < fp->symsz; p++, nsym++) { - if(fp->_magic && (fp->magic & HDR_MAGIC)){ - svalsz = 8; - if(Bread(&b, &vl, 8) != 8) - return symerrmsg(8, "symbol"); - p->value = beswav(vl); - } - else{ - svalsz = 4; - if(Bread(&b, &l, 4) != 4) - return symerrmsg(4, "symbol"); - p->value = (u32int)beswal(l); - } - if(Bread(&b, &p->type, sizeof(p->type)) != sizeof(p->type)) - return symerrmsg(sizeof(p->value), "symbol"); - - i = decodename(&b, p); - if(i < 0) - return -1; - size += i+svalsz+sizeof(p->type); - - if(svalsz == 8){ - if(Bread(&b, &vl, 8) != 8) - return symerrmsg(8, "symbol"); - p->gotype = beswav(vl); - } - else{ - if(Bread(&b, &l, 4) != 4) - return symerrmsg(4, "symbol"); - p->gotype = (u32int)beswal(l); + if(newformat) { + // Go 1.1 format. See comment at top of ../pkg/runtime/symtab.c. + if(Bread(&b, &c, 1) != 1) + return symerrmsg(1, "symbol"); + if((c&0x3F) < 26) + p->type = (c&0x3F)+ 'A'; + else + p->type = (c&0x3F) - 26 + 'a'; + size++; + + if(c&0x40) { + // Fixed-width address. + if(svalsz == 8) { + if(Bread(&b, &vl, 8) != 8) + return symerrmsg(8, "symbol"); + p->value = swav(vl); + } else { + if(Bread(&b, &l, 4) != 4) + return symerrmsg(4, "symbol"); + p->value = (u32int)swal(l); + } + size += svalsz; + } else { + // Varint address. + shift = 0; + p->value = 0; + for(;;) { + if(Bread(&b, buf, 1) != 1) + return symerrmsg(1, "symbol"); + p->value |= (uint64)(buf[0]&0x7F)<gotype = 0; + if(c&0x80) { + // Has Go type. Fixed-width address. + if(svalsz == 8) { + if(Bread(&b, &vl, 8) != 8) + return symerrmsg(8, "symbol"); + p->gotype = swav(vl); + } else { + if(Bread(&b, &l, 4) != 4) + return symerrmsg(4, "symbol"); + p->gotype = (u32int)swal(l); + } + size += svalsz; + } + + // Name. + p->type |= 0x80; // for decodename + i = decodename(&b, p); + if(i < 0) + return -1; + size += i; + } else { + // Go 1.0 format: Plan 9 format + go type symbol. + if(fp->_magic && (fp->magic & HDR_MAGIC)){ + svalsz = 8; + if(Bread(&b, &vl, 8) != 8) + return symerrmsg(8, "symbol"); + p->value = swav(vl); + } + else{ + svalsz = 4; + if(Bread(&b, &l, 4) != 4) + return symerrmsg(4, "symbol"); + p->value = (u32int)swal(l); + } + if(Bread(&b, &p->type, sizeof(p->type)) != sizeof(p->type)) + return symerrmsg(sizeof(p->value), "symbol"); + + i = decodename(&b, p); + if(i < 0) + return -1; + size += i+svalsz+sizeof(p->type); + + if(svalsz == 8){ + if(Bread(&b, &vl, 8) != 8) + return symerrmsg(8, "symbol"); + p->gotype = swav(vl); + } + else{ + if(Bread(&b, &l, 4) != 4) + return symerrmsg(4, "symbol"); + p->gotype = (u32int)swal(l); + } + size += svalsz; } - size += svalsz; /* count global & auto vars, text symbols, and file names */ switch (p->type) { @@ -576,7 +670,8 @@ lookup(char *fn, char *var, Symbol *s) * strcmp, but allow '_' to match center dot (rune 00b7 == bytes c2 b7) */ int -cdotstrcmp(char *sym, char *user) { +cdotstrcmp(char *sym, char *user) +{ for (;;) { while (*sym == *user) { if (*sym++ == '\0') diff --git a/src/libmach/windows.c b/src/libmach/windows.c index 81fa6b6d2..aea7bace0 100644 --- a/src/libmach/windows.c +++ b/src/libmach/windows.c @@ -48,8 +48,12 @@ procthreadpids(int pid, int *p, int np) int pread(int fd, void *buf, int count, int offset) { - sysfatal("pread unimplemented in Windows"); - return -1; + int oldoffset, n; + + oldoffset = seek(fd, offset, 0); + n = read(fd, buf, count); + seek(fd, oldoffset, 0); + return n; } int diff --git a/src/make.bash b/src/make.bash index b2de37b72..2d83b6f4d 100755 --- a/src/make.bash +++ b/src/make.bash @@ -23,9 +23,15 @@ # GO_LDFLAGS: Additional 5l/6l/8l arguments to use when # building the commands. # +# GO_CCFLAGS: Additional 5c/6c/8c arguments to use when +# building. +# # CGO_ENABLED: Controls cgo usage during the build. Set it to 1 # to include all cgo related files, .c and .go file with "cgo" # build directive, in the build. Set it to 0 to ignore them. +# +# CC: Command line to run to get at host C compiler. +# Default is "gcc". Also supported: "clang". set -e if [ ! -f run.bash ]; then @@ -77,6 +83,13 @@ do fi done +# Test for debian/kFreeBSD. +# cmd/dist will detect kFreeBSD as freebsd/$GOARCH, but we need to +# disable cgo manually. +if [ "$(uname -s)" == "GNU/kFreeBSD" ]; then + export CGO_ENABLED=0 +fi + # Clean old generated file that will cause problems in the build. rm -f ./pkg/runtime/runtime_defs.go @@ -93,7 +106,7 @@ case "$GOHOSTARCH" in 386) mflag=-m32;; amd64) mflag=-m64;; esac -gcc $mflag -O2 -Wall -Werror -ggdb -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c +${CC:-gcc} $mflag -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c eval $(./cmd/dist/dist env -p) echo @@ -122,12 +135,12 @@ echo if [ "$GOHOSTARCH" != "$GOARCH" -o "$GOHOSTOS" != "$GOOS" ]; then echo "# Building packages and commands for host, $GOHOSTOS/$GOHOSTARCH." GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH \ - "$GOTOOLDIR"/go_bootstrap install -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std + "$GOTOOLDIR"/go_bootstrap install -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std echo fi echo "# Building packages and commands for $GOOS/$GOARCH." -"$GOTOOLDIR"/go_bootstrap install -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std +"$GOTOOLDIR"/go_bootstrap install $GO_FLAGS -ccflags "$GO_CCFLAGS" -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std echo rm -f "$GOTOOLDIR"/go_bootstrap diff --git a/src/make.bat b/src/make.bat index ec39392dd..be1c8f52d 100644 --- a/src/make.bat +++ b/src/make.bat @@ -60,7 +60,7 @@ echo # Building C bootstrap tool. echo cmd/dist if not exist ..\bin\tool mkdir ..\bin\tool :: Windows has no glob expansion, so spell out cmd/dist/*.c. -gcc -O2 -Wall -Werror -o cmd/dist/dist.exe -Icmd/dist %DEFGOROOT% cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildruntime.c cmd/dist/goc2c.c cmd/dist/main.c cmd/dist/windows.c +gcc -O2 -Wall -Werror -o cmd/dist/dist.exe -Icmd/dist %DEFGOROOT% cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildruntime.c cmd/dist/goc2c.c cmd/dist/main.c cmd/dist/windows.c cmd/dist/arm.c if errorlevel 1 goto fail .\cmd\dist\dist env -wp >env.bat if errorlevel 1 goto fail @@ -68,6 +68,9 @@ call env.bat del env.bat echo. +if x%1==x--dist-tool goto copydist +if x%2==x--dist-tool goto copydist + echo # Building compilers and Go bootstrap tool. set buildall=-a if x%1==x--no-clean set buildall= @@ -105,6 +108,11 @@ if x%1==x--no-banner goto nobanner goto end +:copydist +mkdir %GOTOOLDIR% 2>NUL +copy cmd\dist\dist.exe %GOTOOLDIR%\ +goto end + :fail set GOBUILDFAIL=1 diff --git a/src/make.rc b/src/make.rc new file mode 100755 index 000000000..83d16c045 --- /dev/null +++ b/src/make.rc @@ -0,0 +1,97 @@ +#!/bin/rc -e +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# Environment variables that control make.rc: +# +# GOROOT_FINAL: The expected final Go root, baked into binaries. +# The default is the location of the Go tree during the build. +# +# GOHOSTARCH: The architecture for host tools (compilers and +# binaries). Binaries of this type must be executable on the current +# system, so the only common reason to set this is to set +# GOHOSTARCH=386 on an amd64 machine. +# +# GOARCH: The target architecture for installed packages and tools. +# +# GOOS: The target operating system for installed packages and tools. +# +# GO_GCFLAGS: Additional 5g/6g/8g arguments to use when +# building the packages and commands. +# +# GO_LDFLAGS: Additional 5l/6l/8l arguments to use when +# building the commands. +# +# GO_CCFLAGS: Additional 5c/6c/8c arguments to use when +# building. +# +# CGO_ENABLED: Controls cgo usage during the build. Set it to 1 +# to include all cgo related files, .c and .go file with "cgo" +# build directive, in the build. Set it to 0 to ignore them. + +rfork e +if(! test -f run.rc){ + echo 'make.rc must be run from $GOROOT/src' >[1=2] + exit wrongdir +} + +# Clean old generated file that will cause problems in the build. +rm -f ./pkg/runtime/runtime_defs.go + +# Determine the host compiler toolchain. +eval `{grep '^(CC|LD|O)=' /$objtype/mkfile} + +echo '# Building C bootstrap tool.' +echo cmd/dist +GOROOT = `{cd .. && pwd} +if(! ~ $#GOROOT_FINAL 1) + GOROOT_FINAL = $GOROOT +DEFGOROOT='-DGOROOT_FINAL="'$GOROOT_FINAL'"' + +for(i in cmd/dist/*.c) + $CC -FTVwp+ -DPLAN9 $DEFGOROOT $i +$LD -o cmd/dist/dist *.$O +rm *.$O + +eval `{./cmd/dist/dist env -9} +echo + +if(~ $1 --dist-tool){ + # Stop after building dist tool. + mkdir -p $GOTOOLDIR + if(! ~ $2 '') + cp cmd/dist/dist $2 + mv cmd/dist/dist $GOTOOLDIR/dist + exit +} + +echo '# Building compilers and Go bootstrap tool for host,' $GOHOSTOS/$GOHOSTARCH^. +buildall = -a +if(~ $1 --no-clean) + buildall = () +./cmd/dist/dist bootstrap $buildall -v # builds go_bootstrap +# Delay move of dist tool to now, because bootstrap may clear tool directory. +mv cmd/dist/dist $GOTOOLDIR/dist +$GOTOOLDIR/go_bootstrap clean -i std +echo + +# Run only one process at a time on 9vx. +if(~ $sysname vx32) + pflag = (-p 1) + +if(! ~ $GOHOSTARCH $GOARCH || ! ~ $GOHOSTOS $GOOS){ + echo '# Building packages and commands for host,' $GOHOSTOS/$GOHOSTARCH^. + GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH \ + $GOTOOLDIR/go_bootstrap install -ccflags $"GO_CCFLAGS -gcflags $"GO_GCFLAGS -ldflags $"GO_LDFLAGS -v $pflag std + echo +} + +echo '# Building packages and commands for' $GOOS/$GOARCH^. +$GOTOOLDIR/go_bootstrap install -ccflags $"GO_CCFLAGS -gcflags $"GO_GCFLAGS -ldflags $"GO_LDFLAGS -v $pflag std +echo + +rm -f $GOTOOLDIR/go_bootstrap + +if(! ~ $1 --no-banner) + $GOTOOLDIR/dist banner diff --git a/src/pkg/archive/tar/common.go b/src/pkg/archive/tar/common.go index fc7a40923..60d207c48 100644 --- a/src/pkg/archive/tar/common.go +++ b/src/pkg/archive/tar/common.go @@ -9,9 +9,16 @@ // References: // http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5 // http://www.gnu.org/software/tar/manual/html_node/Standard.html +// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html package tar -import "time" +import ( + "errors" + "fmt" + "os" + "path" + "time" +) const ( blockSize = 512 @@ -28,6 +35,8 @@ const ( TypeCont = '7' // reserved TypeXHeader = 'x' // extended header TypeXGlobalHeader = 'g' // global extended header + TypeGNULongName = 'L' // Next file has a long name + TypeGNULongLink = 'K' // Next file symlinks to a file w/ a long name ) // A Header represents a single header in a tar archive. @@ -49,6 +58,179 @@ type Header struct { ChangeTime time.Time // status change time } +// File name constants from the tar spec. +const ( + fileNameSize = 100 // Maximum number of bytes in a standard tar name. + fileNamePrefixSize = 155 // Maximum number of ustar extension bytes. +) + +// FileInfo returns an os.FileInfo for the Header. +func (h *Header) FileInfo() os.FileInfo { + return headerFileInfo{h} +} + +// headerFileInfo implements os.FileInfo. +type headerFileInfo struct { + h *Header +} + +func (fi headerFileInfo) Size() int64 { return fi.h.Size } +func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } +func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } +func (fi headerFileInfo) Sys() interface{} { return fi.h } + +// Name returns the base name of the file. +func (fi headerFileInfo) Name() string { + if fi.IsDir() { + return path.Clean(fi.h.Name) + } + return fi.h.Name +} + +// Mode returns the permission and mode bits for the headerFileInfo. +func (fi headerFileInfo) Mode() (mode os.FileMode) { + // Set file permission bits. + mode = os.FileMode(fi.h.Mode).Perm() + + // Set setuid, setgid and sticky bits. + if fi.h.Mode&c_ISUID != 0 { + // setuid + mode |= os.ModeSetuid + } + if fi.h.Mode&c_ISGID != 0 { + // setgid + mode |= os.ModeSetgid + } + if fi.h.Mode&c_ISVTX != 0 { + // sticky + mode |= os.ModeSticky + } + + // Set file mode bits. + // clear perm, setuid, setgid and sticky bits. + m := os.FileMode(fi.h.Mode) &^ 07777 + if m == c_ISDIR { + // directory + mode |= os.ModeDir + } + if m == c_ISFIFO { + // named pipe (FIFO) + mode |= os.ModeNamedPipe + } + if m == c_ISLNK { + // symbolic link + mode |= os.ModeSymlink + } + if m == c_ISBLK { + // device file + mode |= os.ModeDevice + } + if m == c_ISCHR { + // Unix character device + mode |= os.ModeDevice + mode |= os.ModeCharDevice + } + if m == c_ISSOCK { + // Unix domain socket + mode |= os.ModeSocket + } + + switch fi.h.Typeflag { + case TypeLink, TypeSymlink: + // hard link, symbolic link + mode |= os.ModeSymlink + case TypeChar: + // character device node + mode |= os.ModeDevice + mode |= os.ModeCharDevice + case TypeBlock: + // block device node + mode |= os.ModeDevice + case TypeDir: + // directory + mode |= os.ModeDir + case TypeFifo: + // fifo node + mode |= os.ModeNamedPipe + } + + return mode +} + +// sysStat, if non-nil, populates h from system-dependent fields of fi. +var sysStat func(fi os.FileInfo, h *Header) error + +// Mode constants from the tar spec. +const ( + c_ISUID = 04000 // Set uid + c_ISGID = 02000 // Set gid + c_ISVTX = 01000 // Save text (sticky bit) + c_ISDIR = 040000 // Directory + c_ISFIFO = 010000 // FIFO + c_ISREG = 0100000 // Regular file + c_ISLNK = 0120000 // Symbolic link + c_ISBLK = 060000 // Block special file + c_ISCHR = 020000 // Character special file + c_ISSOCK = 0140000 // Socket +) + +// FileInfoHeader creates a partially-populated Header from fi. +// If fi describes a symlink, FileInfoHeader records link as the link target. +// If fi describes a directory, a slash is appended to the name. +func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { + if fi == nil { + return nil, errors.New("tar: FileInfo is nil") + } + fm := fi.Mode() + h := &Header{ + Name: fi.Name(), + ModTime: fi.ModTime(), + Mode: int64(fm.Perm()), // or'd with c_IS* constants later + } + switch { + case fm.IsRegular(): + h.Mode |= c_ISREG + h.Typeflag = TypeReg + h.Size = fi.Size() + case fi.IsDir(): + h.Typeflag = TypeDir + h.Mode |= c_ISDIR + h.Name += "/" + case fm&os.ModeSymlink != 0: + h.Typeflag = TypeSymlink + h.Mode |= c_ISLNK + h.Linkname = link + case fm&os.ModeDevice != 0: + if fm&os.ModeCharDevice != 0 { + h.Mode |= c_ISCHR + h.Typeflag = TypeChar + } else { + h.Mode |= c_ISBLK + h.Typeflag = TypeBlock + } + case fm&os.ModeNamedPipe != 0: + h.Typeflag = TypeFifo + h.Mode |= c_ISFIFO + case fm&os.ModeSocket != 0: + h.Mode |= c_ISSOCK + default: + return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) + } + if fm&os.ModeSetuid != 0 { + h.Mode |= c_ISUID + } + if fm&os.ModeSetgid != 0 { + h.Mode |= c_ISGID + } + if fm&os.ModeSticky != 0 { + h.Mode |= c_ISVTX + } + if sysStat != nil { + return h, sysStat(fi, h) + } + return h, nil +} + var zeroBlock = make([]byte, blockSize) // POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values. diff --git a/src/pkg/archive/tar/example_test.go b/src/pkg/archive/tar/example_test.go new file mode 100644 index 000000000..351eaa0e6 --- /dev/null +++ b/src/pkg/archive/tar/example_test.go @@ -0,0 +1,79 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar_test + +import ( + "archive/tar" + "bytes" + "fmt" + "io" + "log" + "os" +) + +func Example() { + // Create a buffer to write our archive to. + buf := new(bytes.Buffer) + + // Create a new tar archive. + tw := tar.NewWriter(buf) + + // Add some files to the archive. + var files = []struct { + Name, Body string + }{ + {"readme.txt", "This archive contains some text files."}, + {"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"}, + {"todo.txt", "Get animal handling licence."}, + } + for _, file := range files { + hdr := &tar.Header{ + Name: file.Name, + Size: int64(len(file.Body)), + } + if err := tw.WriteHeader(hdr); err != nil { + log.Fatalln(err) + } + if _, err := tw.Write([]byte(file.Body)); err != nil { + log.Fatalln(err) + } + } + // Make sure to check the error on Close. + if err := tw.Close(); err != nil { + log.Fatalln(err) + } + + // Open the tar archive for reading. + r := bytes.NewReader(buf.Bytes()) + tr := tar.NewReader(r) + + // Iterate through the files in the archive. + for { + hdr, err := tr.Next() + if err == io.EOF { + // end of tar archive + break + } + if err != nil { + log.Fatalln(err) + } + fmt.Printf("Contents of %s:\n", hdr.Name) + if _, err := io.Copy(os.Stdout, tr); err != nil { + log.Fatalln(err) + } + fmt.Println() + } + + // Output: + // Contents of readme.txt: + // This archive contains some text files. + // Contents of gopher.txt: + // Gopher names: + // George + // Geoffrey + // Gonzo + // Contents of todo.txt: + // Get animal handling licence. +} diff --git a/src/pkg/archive/tar/reader.go b/src/pkg/archive/tar/reader.go index 1b40af812..05f82a40d 100644 --- a/src/pkg/archive/tar/reader.go +++ b/src/pkg/archive/tar/reader.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "os" "strconv" + "strings" "time" ) @@ -21,24 +22,12 @@ var ( ErrHeader = errors.New("archive/tar: invalid tar header") ) +const maxNanoSecondIntSize = 9 + // A Reader provides sequential access to the contents of a tar archive. // A tar archive consists of a sequence of files. // The Next method advances to the next file in the archive (including the first), // and then it can be treated as an io.Reader to access the file's data. -// -// Example: -// tr := tar.NewReader(r) -// for { -// hdr, err := tr.Next() -// if err == io.EOF { -// // end of tar archive -// break -// } -// if err != nil { -// // handle error -// } -// io.Copy(data, tr) -// } type Reader struct { r io.Reader err error @@ -55,13 +44,183 @@ func (tr *Reader) Next() (*Header, error) { if tr.err == nil { tr.skipUnread() } - if tr.err == nil { + if tr.err != nil { + return hdr, tr.err + } + hdr = tr.readHeader() + if hdr == nil { + return hdr, tr.err + } + // Check for PAX/GNU header. + switch hdr.Typeflag { + case TypeXHeader: + // PAX extended header + headers, err := parsePAX(tr) + if err != nil { + return nil, err + } + // We actually read the whole file, + // but this skips alignment padding + tr.skipUnread() hdr = tr.readHeader() + mergePAX(hdr, headers) + return hdr, nil + case TypeGNULongName: + // We have a GNU long name header. Its contents are the real file name. + realname, err := ioutil.ReadAll(tr) + if err != nil { + return nil, err + } + hdr, err := tr.Next() + hdr.Name = cString(realname) + return hdr, err + case TypeGNULongLink: + // We have a GNU long link header. + realname, err := ioutil.ReadAll(tr) + if err != nil { + return nil, err + } + hdr, err := tr.Next() + hdr.Linkname = cString(realname) + return hdr, err } return hdr, tr.err } -// Parse bytes as a NUL-terminated C-style string. +// mergePAX merges well known headers according to PAX standard. +// In general headers with the same name as those found +// in the header struct overwrite those found in the header +// struct with higher precision or longer values. Esp. useful +// for name and linkname fields. +func mergePAX(hdr *Header, headers map[string]string) error { + for k, v := range headers { + switch k { + case "path": + hdr.Name = v + case "linkpath": + hdr.Linkname = v + case "gname": + hdr.Gname = v + case "uname": + hdr.Uname = v + case "uid": + uid, err := strconv.ParseInt(v, 10, 0) + if err != nil { + return err + } + hdr.Uid = int(uid) + case "gid": + gid, err := strconv.ParseInt(v, 10, 0) + if err != nil { + return err + } + hdr.Gid = int(gid) + case "atime": + t, err := parsePAXTime(v) + if err != nil { + return err + } + hdr.AccessTime = t + case "mtime": + t, err := parsePAXTime(v) + if err != nil { + return err + } + hdr.ModTime = t + case "ctime": + t, err := parsePAXTime(v) + if err != nil { + return err + } + hdr.ChangeTime = t + case "size": + size, err := strconv.ParseInt(v, 10, 0) + if err != nil { + return err + } + hdr.Size = int64(size) + } + + } + return nil +} + +// parsePAXTime takes a string of the form %d.%d as described in +// the PAX specification. +func parsePAXTime(t string) (time.Time, error) { + buf := []byte(t) + pos := bytes.IndexByte(buf, '.') + var seconds, nanoseconds int64 + var err error + if pos == -1 { + seconds, err = strconv.ParseInt(t, 10, 0) + if err != nil { + return time.Time{}, err + } + } else { + seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0) + if err != nil { + return time.Time{}, err + } + nano_buf := string(buf[pos+1:]) + // Pad as needed before converting to a decimal. + // For example .030 -> .030000000 -> 30000000 nanoseconds + if len(nano_buf) < maxNanoSecondIntSize { + // Right pad + nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf)) + } else if len(nano_buf) > maxNanoSecondIntSize { + // Right truncate + nano_buf = nano_buf[:maxNanoSecondIntSize] + } + nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0) + if err != nil { + return time.Time{}, err + } + } + ts := time.Unix(seconds, nanoseconds) + return ts, nil +} + +// parsePAX parses PAX headers. +// If an extended header (type 'x') is invalid, ErrHeader is returned +func parsePAX(r io.Reader) (map[string]string, error) { + buf, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + headers := make(map[string]string) + // Each record is constructed as + // "%d %s=%s\n", length, keyword, value + for len(buf) > 0 { + // or the header was empty to start with. + var sp int + // The size field ends at the first space. + sp = bytes.IndexByte(buf, ' ') + if sp == -1 { + return nil, ErrHeader + } + // Parse the first token as a decimal integer. + n, err := strconv.ParseInt(string(buf[:sp]), 10, 0) + if err != nil { + return nil, ErrHeader + } + // Extract everything between the decimal and the n -1 on the + // beginning to to eat the ' ', -1 on the end to skip the newline. + var record []byte + record, buf = buf[sp+1:n-1], buf[n:] + // The first equals is guaranteed to mark the end of the key. + // Everything else is value. + eq := bytes.IndexByte(record, '=') + if eq == -1 { + return nil, ErrHeader + } + key, value := record[:eq], record[eq+1:] + headers[string(key)] = string(value) + } + return headers, nil +} + +// cString parses bytes as a NUL-terminated C-style string. // If a NUL byte is not found then the whole slice is returned as a string. func cString(b []byte) string { n := 0 @@ -72,6 +231,18 @@ func cString(b []byte) string { } func (tr *Reader) octal(b []byte) int64 { + // Check for binary format first. + if len(b) > 0 && b[0]&0x80 != 0 { + var x int64 + for i, c := range b { + if i == 0 { + c &= 0x7f // ignore signal bit in first byte + } + x = x<<8 | int64(c) + } + return x + } + // Removing leading spaces. for len(b) > 0 && b[0] == ' ' { b = b[1:] @@ -87,7 +258,7 @@ func (tr *Reader) octal(b []byte) int64 { return int64(x) } -// Skip any unread bytes in the existing file entry, as well as any alignment padding. +// skipUnread skips any unread bytes in the existing file entry, as well as any alignment padding. func (tr *Reader) skipUnread() { nr := tr.nb + tr.pad // number of bytes to skip tr.nb, tr.pad = 0, 0 diff --git a/src/pkg/archive/tar/reader_test.go b/src/pkg/archive/tar/reader_test.go index 0a8646c39..9a1968237 100644 --- a/src/pkg/archive/tar/reader_test.go +++ b/src/pkg/archive/tar/reader_test.go @@ -10,6 +10,8 @@ import ( "fmt" "io" "os" + "reflect" + "strings" "testing" "time" ) @@ -108,6 +110,38 @@ var untarTests = []*untarTest{ }, }, }, + { + file: "testdata/pax.tar", + headers: []*Header{ + { + Name: "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", + Mode: 0664, + Uid: 1000, + Gid: 1000, + Uname: "shane", + Gname: "shane", + Size: 7, + ModTime: time.Unix(1350244992, 23960108), + ChangeTime: time.Unix(1350244992, 23960108), + AccessTime: time.Unix(1350244992, 23960108), + Typeflag: TypeReg, + }, + { + Name: "a/b", + Mode: 0777, + Uid: 1000, + Gid: 1000, + Uname: "shane", + Gname: "shane", + Size: 0, + ModTime: time.Unix(1350266320, 910238425), + ChangeTime: time.Unix(1350266320, 910238425), + AccessTime: time.Unix(1350266320, 910238425), + Typeflag: TypeSymlink, + Linkname: "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", + }, + }, + }, } func TestReader(t *testing.T) { @@ -133,7 +167,7 @@ testLoop: } hdr, err := tr.Next() if err == io.EOF { - break + continue testLoop } if hdr != nil || err != nil { t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) @@ -260,3 +294,73 @@ func TestNonSeekable(t *testing.T) { t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread) } } + +func TestParsePAXHeader(t *testing.T) { + paxTests := [][3]string{ + {"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths + {"a", "a=name", "9 a=name\n"}, // Test case involving multiple acceptable length + {"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}} + for _, test := range paxTests { + key, expected, raw := test[0], test[1], test[2] + reader := bytes.NewBuffer([]byte(raw)) + headers, err := parsePAX(reader) + if err != nil { + t.Errorf("Couldn't parse correctly formatted headers: %v", err) + continue + } + if strings.EqualFold(headers[key], expected) { + t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected) + continue + } + trailer := make([]byte, 100) + n, err := reader.Read(trailer) + if err != io.EOF || n != 0 { + t.Error("Buffer wasn't consumed") + } + } + badHeader := bytes.NewBuffer([]byte("3 somelongkey=")) + if _, err := parsePAX(badHeader); err != ErrHeader { + t.Fatal("Unexpected success when parsing bad header") + } +} + +func TestParsePAXTime(t *testing.T) { + // Some valid PAX time values + timestamps := map[string]time.Time{ + "1350244992.023960108": time.Unix(1350244992, 23960108), // The commoon case + "1350244992.02396010": time.Unix(1350244992, 23960100), // Lower precision value + "1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value + "1350244992": time.Unix(1350244992, 0), // Low precision value + } + for input, expected := range timestamps { + ts, err := parsePAXTime(input) + if err != nil { + t.Fatal(err) + } + if !ts.Equal(expected) { + t.Fatalf("Time parsing failure %s %s", ts, expected) + } + } +} + +func TestMergePAX(t *testing.T) { + hdr := new(Header) + // Test a string, integer, and time based value. + headers := map[string]string{ + "path": "a/b/c", + "uid": "1000", + "mtime": "1350244992.023960108", + } + err := mergePAX(hdr, headers) + if err != nil { + t.Fatal(err) + } + want := &Header{ + Name: "a/b/c", + Uid: 1000, + ModTime: time.Unix(1350244992, 23960108), + } + if !reflect.DeepEqual(hdr, want) { + t.Errorf("incorrect merge: got %+v, want %+v", hdr, want) + } +} diff --git a/src/pkg/archive/tar/stat_atim.go b/src/pkg/archive/tar/stat_atim.go new file mode 100644 index 000000000..6029b0871 --- /dev/null +++ b/src/pkg/archive/tar/stat_atim.go @@ -0,0 +1,20 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux openbsd + +package tar + +import ( + "syscall" + "time" +) + +func statAtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Atim.Unix()) +} + +func statCtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Ctim.Unix()) +} diff --git a/src/pkg/archive/tar/stat_atimespec.go b/src/pkg/archive/tar/stat_atimespec.go new file mode 100644 index 000000000..6f17dbe30 --- /dev/null +++ b/src/pkg/archive/tar/stat_atimespec.go @@ -0,0 +1,20 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd netbsd + +package tar + +import ( + "syscall" + "time" +) + +func statAtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Atimespec.Unix()) +} + +func statCtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Ctimespec.Unix()) +} diff --git a/src/pkg/archive/tar/stat_unix.go b/src/pkg/archive/tar/stat_unix.go new file mode 100644 index 000000000..92bc92424 --- /dev/null +++ b/src/pkg/archive/tar/stat_unix.go @@ -0,0 +1,32 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin freebsd openbsd netbsd + +package tar + +import ( + "os" + "syscall" +) + +func init() { + sysStat = statUnix +} + +func statUnix(fi os.FileInfo, h *Header) error { + sys, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return nil + } + h.Uid = int(sys.Uid) + h.Gid = int(sys.Gid) + // TODO(bradfitz): populate username & group. os/user + // doesn't cache LookupId lookups, and lacks group + // lookup functions. + h.AccessTime = statAtime(sys) + h.ChangeTime = statCtime(sys) + // TODO(bradfitz): major/minor device numbers? + return nil +} diff --git a/src/pkg/archive/tar/tar_test.go b/src/pkg/archive/tar/tar_test.go new file mode 100644 index 000000000..dd6310313 --- /dev/null +++ b/src/pkg/archive/tar/tar_test.go @@ -0,0 +1,271 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "io/ioutil" + "os" + "reflect" + "testing" + "time" +) + +func TestFileInfoHeader(t *testing.T) { + fi, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + h, err := FileInfoHeader(fi, "") + if err != nil { + t.Fatalf("FileInfoHeader: %v", err) + } + if g, e := h.Name, "small.txt"; g != e { + t.Errorf("Name = %q; want %q", g, e) + } + if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e { + t.Errorf("Mode = %#o; want %#o", g, e) + } + if g, e := h.Size, int64(5); g != e { + t.Errorf("Size = %v; want %v", g, e) + } + if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) { + t.Errorf("ModTime = %v; want %v", g, e) + } +} + +func TestFileInfoHeaderDir(t *testing.T) { + fi, err := os.Stat("testdata") + if err != nil { + t.Fatal(err) + } + h, err := FileInfoHeader(fi, "") + if err != nil { + t.Fatalf("FileInfoHeader: %v", err) + } + if g, e := h.Name, "testdata/"; g != e { + t.Errorf("Name = %q; want %q", g, e) + } + // Ignoring c_ISGID for golang.org/issue/4867 + if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm())|c_ISDIR; g != e { + t.Errorf("Mode = %#o; want %#o", g, e) + } + if g, e := h.Size, int64(0); g != e { + t.Errorf("Size = %v; want %v", g, e) + } + if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) { + t.Errorf("ModTime = %v; want %v", g, e) + } +} + +func TestFileInfoHeaderSymlink(t *testing.T) { + h, err := FileInfoHeader(symlink{}, "some-target") + if err != nil { + t.Fatal(err) + } + if g, e := h.Name, "some-symlink"; g != e { + t.Errorf("Name = %q; want %q", g, e) + } + if g, e := h.Linkname, "some-target"; g != e { + t.Errorf("Linkname = %q; want %q", g, e) + } +} + +type symlink struct{} + +func (symlink) Name() string { return "some-symlink" } +func (symlink) Size() int64 { return 0 } +func (symlink) Mode() os.FileMode { return os.ModeSymlink } +func (symlink) ModTime() time.Time { return time.Time{} } +func (symlink) IsDir() bool { return false } +func (symlink) Sys() interface{} { return nil } + +func TestRoundTrip(t *testing.T) { + data := []byte("some file contents") + + var b bytes.Buffer + tw := NewWriter(&b) + hdr := &Header{ + Name: "file.txt", + Uid: 1 << 21, // too big for 8 octal digits + Size: int64(len(data)), + ModTime: time.Now(), + } + // tar only supports second precision. + hdr.ModTime = hdr.ModTime.Add(-time.Duration(hdr.ModTime.Nanosecond()) * time.Nanosecond) + if err := tw.WriteHeader(hdr); err != nil { + t.Fatalf("tw.WriteHeader: %v", err) + } + if _, err := tw.Write(data); err != nil { + t.Fatalf("tw.Write: %v", err) + } + if err := tw.Close(); err != nil { + t.Fatalf("tw.Close: %v", err) + } + + // Read it back. + tr := NewReader(&b) + rHdr, err := tr.Next() + if err != nil { + t.Fatalf("tr.Next: %v", err) + } + if !reflect.DeepEqual(rHdr, hdr) { + t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr) + } + rData, err := ioutil.ReadAll(tr) + if err != nil { + t.Fatalf("Read: %v", err) + } + if !bytes.Equal(rData, data) { + t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data) + } +} + +type headerRoundTripTest struct { + h *Header + fm os.FileMode +} + +func TestHeaderRoundTrip(t *testing.T) { + golden := []headerRoundTripTest{ + // regular file. + { + h: &Header{ + Name: "test.txt", + Mode: 0644 | c_ISREG, + Size: 12, + ModTime: time.Unix(1360600916, 0), + Typeflag: TypeReg, + }, + fm: 0644, + }, + // hard link. + { + h: &Header{ + Name: "hard.txt", + Mode: 0644 | c_ISLNK, + Size: 0, + ModTime: time.Unix(1360600916, 0), + Typeflag: TypeLink, + }, + fm: 0644 | os.ModeSymlink, + }, + // symbolic link. + { + h: &Header{ + Name: "link.txt", + Mode: 0777 | c_ISLNK, + Size: 0, + ModTime: time.Unix(1360600852, 0), + Typeflag: TypeSymlink, + }, + fm: 0777 | os.ModeSymlink, + }, + // character device node. + { + h: &Header{ + Name: "dev/null", + Mode: 0666 | c_ISCHR, + Size: 0, + ModTime: time.Unix(1360578951, 0), + Typeflag: TypeChar, + }, + fm: 0666 | os.ModeDevice | os.ModeCharDevice, + }, + // block device node. + { + h: &Header{ + Name: "dev/sda", + Mode: 0660 | c_ISBLK, + Size: 0, + ModTime: time.Unix(1360578954, 0), + Typeflag: TypeBlock, + }, + fm: 0660 | os.ModeDevice, + }, + // directory. + { + h: &Header{ + Name: "dir/", + Mode: 0755 | c_ISDIR, + Size: 0, + ModTime: time.Unix(1360601116, 0), + Typeflag: TypeDir, + }, + fm: 0755 | os.ModeDir, + }, + // fifo node. + { + h: &Header{ + Name: "dev/initctl", + Mode: 0600 | c_ISFIFO, + Size: 0, + ModTime: time.Unix(1360578949, 0), + Typeflag: TypeFifo, + }, + fm: 0600 | os.ModeNamedPipe, + }, + // setuid. + { + h: &Header{ + Name: "bin/su", + Mode: 0755 | c_ISREG | c_ISUID, + Size: 23232, + ModTime: time.Unix(1355405093, 0), + Typeflag: TypeReg, + }, + fm: 0755 | os.ModeSetuid, + }, + // setguid. + { + h: &Header{ + Name: "group.txt", + Mode: 0750 | c_ISREG | c_ISGID, + Size: 0, + ModTime: time.Unix(1360602346, 0), + Typeflag: TypeReg, + }, + fm: 0750 | os.ModeSetgid, + }, + // sticky. + { + h: &Header{ + Name: "sticky.txt", + Mode: 0600 | c_ISREG | c_ISVTX, + Size: 7, + ModTime: time.Unix(1360602540, 0), + Typeflag: TypeReg, + }, + fm: 0600 | os.ModeSticky, + }, + } + + for i, g := range golden { + fi := g.h.FileInfo() + h2, err := FileInfoHeader(fi, "") + if err != nil { + t.Error(err) + continue + } + if got, want := h2.Name, g.h.Name; got != want { + t.Errorf("i=%d: Name: got %v, want %v", i, got, want) + } + if got, want := h2.Size, g.h.Size; got != want { + t.Errorf("i=%d: Size: got %v, want %v", i, got, want) + } + if got, want := h2.Mode, g.h.Mode; got != want { + t.Errorf("i=%d: Mode: got %o, want %o", i, got, want) + } + if got, want := fi.Mode(), g.fm; got != want { + t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want) + } + if got, want := h2.ModTime, g.h.ModTime; got != want { + t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want) + } + if sysh, ok := fi.Sys().(*Header); !ok || sysh != g.h { + t.Errorf("i=%d: Sys didn't return original *Header", i) + } + } +} diff --git a/src/pkg/archive/tar/testdata/pax.tar b/src/pkg/archive/tar/testdata/pax.tar new file mode 100644 index 000000000..9bc24b658 Binary files /dev/null and b/src/pkg/archive/tar/testdata/pax.tar differ diff --git a/src/pkg/archive/tar/testdata/ustar.tar b/src/pkg/archive/tar/testdata/ustar.tar new file mode 100644 index 000000000..29679d9a3 Binary files /dev/null and b/src/pkg/archive/tar/testdata/ustar.tar differ diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go index b2b7a58a1..d92dd06ea 100644 --- a/src/pkg/archive/tar/writer.go +++ b/src/pkg/archive/tar/writer.go @@ -8,33 +8,28 @@ package tar // - catch more errors (no first header, etc.) import ( + "bytes" "errors" "fmt" "io" + "os" + "path" "strconv" + "strings" + "time" ) var ( ErrWriteTooLong = errors.New("archive/tar: write too long") ErrFieldTooLong = errors.New("archive/tar: header field too long") ErrWriteAfterClose = errors.New("archive/tar: write after close") + errNameTooLong = errors.New("archive/tar: name too long") ) // A Writer provides sequential writing of a tar archive in POSIX.1 format. // A tar archive consists of a sequence of files. // Call WriteHeader to begin a new file, and then call Write to supply that file's data, // writing at most hdr.Size bytes in total. -// -// Example: -// tw := tar.NewWriter(w) -// hdr := new(Header) -// hdr.Size = length of data in bytes -// // populate other hdr fields as desired -// if err := tw.WriteHeader(hdr); err != nil { -// // handle error -// } -// io.Copy(tw, data) -// tw.Close() type Writer struct { w io.Writer err error @@ -110,6 +105,12 @@ func (tw *Writer) numeric(b []byte, x int64) { b[0] |= 0x80 // highest bit indicates binary format } +var ( + minTime = time.Unix(0, 0) + // There is room for 11 octal digits (33 bits) of mtime. + maxTime = minTime.Add((1<<33 - 1) * time.Second) +) + // WriteHeader writes hdr and prepares to accept the file's contents. // WriteHeader calls Flush if it is not the first header. // Calling after a Close will return ErrWriteAfterClose. @@ -123,34 +124,60 @@ func (tw *Writer) WriteHeader(hdr *Header) error { if tw.err != nil { return tw.err } - + // Decide whether or not to use PAX extensions + // TODO(shanemhansen): we might want to use PAX headers for + // subsecond time resolution, but for now let's just capture + // the long name/long symlink use case. + suffix := hdr.Name + prefix := "" + if len(hdr.Name) > fileNameSize || len(hdr.Linkname) > fileNameSize { + var err error + prefix, suffix, err = tw.splitUSTARLongName(hdr.Name) + // Either we were unable to pack the long name into ustar format + // or the link name is too long; use PAX headers. + if err == errNameTooLong || len(hdr.Linkname) > fileNameSize { + if err := tw.writePAXHeader(hdr); err != nil { + return err + } + } else if err != nil { + return err + } + } tw.nb = int64(hdr.Size) tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two header := make([]byte, blockSize) s := slicer(header) + tw.cString(s.next(fileNameSize), suffix) - // TODO(dsymonds): handle names longer than 100 chars - copy(s.next(100), []byte(hdr.Name)) - - tw.octal(s.next(8), hdr.Mode) // 100:108 - tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 - tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 - tw.numeric(s.next(12), hdr.Size) // 124:136 - tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148 - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 - tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) - copy(s.next(8), []byte("ustar\x0000")) // 257:265 - tw.cString(s.next(32), hdr.Uname) // 265:297 - tw.cString(s.next(32), hdr.Gname) // 297:329 - tw.numeric(s.next(8), hdr.Devmajor) // 329:337 - tw.numeric(s.next(8), hdr.Devminor) // 337:345 + // Handle out of range ModTime carefully. + var modTime int64 + if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) { + modTime = hdr.ModTime.Unix() + } + tw.octal(s.next(8), hdr.Mode) // 100:108 + tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 + tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 + tw.numeric(s.next(12), hdr.Size) // 124:136 + tw.numeric(s.next(12), modTime) // 136:148 + s.next(8) // chksum (148:156) + s.next(1)[0] = hdr.Typeflag // 156:157 + tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) + copy(s.next(8), []byte("ustar\x0000")) // 257:265 + tw.cString(s.next(32), hdr.Uname) // 265:297 + tw.cString(s.next(32), hdr.Gname) // 297:329 + tw.numeric(s.next(8), hdr.Devmajor) // 329:337 + tw.numeric(s.next(8), hdr.Devminor) // 337:345 + tw.cString(s.next(155), prefix) // 345:500 // Use the GNU magic instead of POSIX magic if we used any GNU extensions. if tw.usedBinary { copy(header[257:265], []byte("ustar \x00")) } + // Use the ustar magic if we used ustar long names. + if len(prefix) > 0 { + copy(header[257:265], []byte("ustar\000")) + } // The chksum field is terminated by a NUL and a space. // This is different from the other octal fields. @@ -168,6 +195,78 @@ func (tw *Writer) WriteHeader(hdr *Header) error { return tw.err } +// writeUSTARLongName splits a USTAR long name hdr.Name. +// name must be < 256 characters. errNameTooLong is returned +// if hdr.Name can't be split. The splitting heuristic +// is compatible with gnu tar. +func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err error) { + length := len(name) + if length > fileNamePrefixSize+1 { + length = fileNamePrefixSize + 1 + } else if name[length-1] == '/' { + length-- + } + i := strings.LastIndex(name[:length], "/") + nlen := length - i - 1 + if i <= 0 || nlen > fileNameSize || nlen == 0 { + err = errNameTooLong + return + } + prefix, suffix = name[:i], name[i+1:] + return +} + +// writePaxHeader writes an extended pax header to the +// archive. +func (tw *Writer) writePAXHeader(hdr *Header) error { + // Prepare extended header + ext := new(Header) + ext.Typeflag = TypeXHeader + // Setting ModTime is required for reader parsing to + // succeed, and seems harmless enough. + ext.ModTime = hdr.ModTime + // The spec asks that we namespace our pseudo files + // with the current pid. + pid := os.Getpid() + dir, file := path.Split(hdr.Name) + ext.Name = path.Join(dir, + fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100] + // Construct the body + var buf bytes.Buffer + if len(hdr.Name) > fileNameSize { + fmt.Fprint(&buf, paxHeader("path="+hdr.Name)) + } + if len(hdr.Linkname) > fileNameSize { + fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname)) + } + ext.Size = int64(len(buf.Bytes())) + if err := tw.WriteHeader(ext); err != nil { + return err + } + if _, err := tw.Write(buf.Bytes()); err != nil { + return err + } + if err := tw.Flush(); err != nil { + return err + } + return nil +} + +// paxHeader formats a single pax record, prefixing it with the appropriate length +func paxHeader(msg string) string { + const padding = 2 // Extra padding for space and newline + size := len(msg) + padding + size += len(strconv.Itoa(size)) + record := fmt.Sprintf("%d %s\n", size, msg) + if len(record) != size { + // Final adjustment if adding size increased + // the number of digits in size + size = len(record) + record = fmt.Sprintf("%d %s\n", size, msg) + } + return record +} + // Write writes to the current entry in the tar archive. // Write returns the error ErrWriteTooLong if more than // hdr.Size bytes are written after WriteHeader. diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go index a214e57b9..4cf7c72af 100644 --- a/src/pkg/archive/tar/writer_test.go +++ b/src/pkg/archive/tar/writer_test.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "io/ioutil" + "os" "strings" "testing" "testing/iotest" @@ -101,6 +102,27 @@ var writerTests = []*writerTest{ }, }, }, + // This file was produced using gnu tar 1.17 + // gnutar -b 4 --format=ustar (longname/)*15 + file.txt + { + file: "testdata/ustar.tar", + entries: []*writerTestEntry{ + { + header: &Header{ + Name: strings.Repeat("longname/", 15) + "file.txt", + Mode: 0644, + Uid: 0765, + Gid: 024, + Size: 06, + ModTime: time.Unix(1360135598, 0), + Typeflag: '0', + Uname: "shane", + Gname: "staff", + }, + contents: "hello\n", + }, + }, + }, } // Render byte array in a two-character hexadecimal string, spaced for easy visual inspection. @@ -180,3 +202,61 @@ testLoop: } } } + +func TestPax(t *testing.T) { + // Create an archive with a large name + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + if err != nil { + t.Fatalf("os.Stat: %v", err) + } + // Force a PAX long name to be written + longName := strings.Repeat("ab", 100) + contents := strings.Repeat(" ", int(hdr.Size)) + hdr.Name = longName + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if _, err = writer.Write([]byte(contents)); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Simple test to make sure PAX extensions are in effect + if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { + t.Fatal("Expected at least one PAX header to be written.") + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Name != longName { + t.Fatal("Couldn't recover long file name") + } +} + +func TestPAXHeader(t *testing.T) { + medName := strings.Repeat("CD", 50) + longName := strings.Repeat("AB", 100) + paxTests := [][2]string{ + {"name=/etc/hosts", "19 name=/etc/hosts\n"}, + {"a=b", "6 a=b\n"}, // Single digit length + {"a=names", "11 a=names\n"}, // Test case involving carries + {"name=" + longName, fmt.Sprintf("210 name=%s\n", longName)}, + {"name=" + medName, fmt.Sprintf("110 name=%s\n", medName)}} + + for _, test := range paxTests { + key, expected := test[0], test[1] + if result := paxHeader(key); result != expected { + t.Fatalf("paxHeader: got %s, expected %s", result, expected) + } + } +} diff --git a/src/pkg/archive/zip/reader.go b/src/pkg/archive/zip/reader.go index ddd507538..c10f29a83 100644 --- a/src/pkg/archive/zip/reader.go +++ b/src/pkg/archive/zip/reader.go @@ -103,7 +103,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error { } z.File = append(z.File, f) } - if uint16(len(z.File)) != end.directoryRecords { + if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here // Return the readDirectoryHeader error if we read // the wrong number of directory entries. return err @@ -123,7 +123,7 @@ func (f *File) Open() (rc io.ReadCloser, err error) { if err != nil { return } - size := int64(f.CompressedSize) + size := int64(f.CompressedSize64) r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) switch f.Method { case Store: // (no compression) @@ -220,6 +220,8 @@ func readDirectoryHeader(f *File, r io.Reader) error { f.CRC32 = b.uint32() f.CompressedSize = b.uint32() f.UncompressedSize = b.uint32() + f.CompressedSize64 = uint64(f.CompressedSize) + f.UncompressedSize64 = uint64(f.UncompressedSize) filenameLen := int(b.uint16()) extraLen := int(b.uint16()) commentLen := int(b.uint16()) @@ -233,6 +235,35 @@ func readDirectoryHeader(f *File, r io.Reader) error { f.Name = string(d[:filenameLen]) f.Extra = d[filenameLen : filenameLen+extraLen] f.Comment = string(d[filenameLen+extraLen:]) + + if len(f.Extra) > 0 { + b := readBuf(f.Extra) + for len(b) >= 4 { // need at least tag and size + tag := b.uint16() + size := b.uint16() + if int(size) > len(b) { + return ErrFormat + } + if tag == zip64ExtraId { + // update directory values from the zip64 extra block + eb := readBuf(b) + if len(eb) >= 8 { + f.UncompressedSize64 = eb.uint64() + } + if len(eb) >= 8 { + f.CompressedSize64 = eb.uint64() + } + if len(eb) >= 8 { + f.headerOffset = int64(eb.uint64()) + } + } + b = b[size:] + } + // Should have consumed the whole header. + if len(b) != 0 { + return ErrFormat + } + } return nil } @@ -263,15 +294,23 @@ func readDataDescriptor(r io.Reader, f *File) error { return err } b := readBuf(buf[:12]) - f.CRC32 = b.uint32() - f.CompressedSize = b.uint32() - f.UncompressedSize = b.uint32() + if b.uint32() != f.CRC32 { + return ErrChecksum + } + + // The two sizes that follow here can be either 32 bits or 64 bits + // but the spec is not very clear on this and different + // interpretations has been made causing incompatibilities. We + // already have the sizes from the central directory so we can + // just ignore these. + return nil } func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { // look for directoryEndSignature in the last 1k, then in the last 65k var buf []byte + var directoryEndOffset int64 for i, bLen := range []int64{1024, 65 * 1024} { if bLen > size { bLen = size @@ -282,6 +321,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) } if p := findSignatureInBlock(buf); p >= 0 { buf = buf[p:] + directoryEndOffset = size - bLen + int64(p) break } if i == 1 || bLen == size { @@ -292,12 +332,12 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) // read header into struct b := readBuf(buf[4:]) // skip signature d := &directoryEnd{ - diskNbr: b.uint16(), - dirDiskNbr: b.uint16(), - dirRecordsThisDisk: b.uint16(), - directoryRecords: b.uint16(), - directorySize: b.uint32(), - directoryOffset: b.uint32(), + diskNbr: uint32(b.uint16()), + dirDiskNbr: uint32(b.uint16()), + dirRecordsThisDisk: uint64(b.uint16()), + directoryRecords: uint64(b.uint16()), + directorySize: uint64(b.uint32()), + directoryOffset: uint64(b.uint32()), commentLen: b.uint16(), } l := int(d.commentLen) @@ -305,9 +345,62 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) return nil, errors.New("zip: invalid comment length") } d.comment = string(b[:l]) + + p, err := findDirectory64End(r, directoryEndOffset) + if err == nil && p >= 0 { + err = readDirectory64End(r, p, d) + } + if err != nil { + return nil, err + } return d, nil } +// findDirectory64End tries to read the zip64 locator just before the +// directory end and returns the offset of the zip64 directory end if +// found. +func findDirectory64End(r io.ReaderAt, directoryEndOffset int64) (int64, error) { + locOffset := directoryEndOffset - directory64LocLen + if locOffset < 0 { + return -1, nil // no need to look for a header outside the file + } + buf := make([]byte, directory64LocLen) + if _, err := r.ReadAt(buf, locOffset); err != nil { + return -1, err + } + b := readBuf(buf) + if sig := b.uint32(); sig != directory64LocSignature { + return -1, nil + } + b = b[4:] // skip number of the disk with the start of the zip64 end of central directory + p := b.uint64() // relative offset of the zip64 end of central directory record + return int64(p), nil +} + +// readDirectory64End reads the zip64 directory end and updates the +// directory end with the zip64 directory end values. +func readDirectory64End(r io.ReaderAt, offset int64, d *directoryEnd) (err error) { + buf := make([]byte, directory64EndLen) + if _, err := r.ReadAt(buf, offset); err != nil { + return err + } + + b := readBuf(buf) + if sig := b.uint32(); sig != directory64EndSignature { + return ErrFormat + } + + b = b[12:] // skip dir size, version and version needed (uint64 + 2x uint16) + d.diskNbr = b.uint32() // number of this disk + d.dirDiskNbr = b.uint32() // number of the disk with the start of the central directory + d.dirRecordsThisDisk = b.uint64() // total number of entries in the central directory on this disk + d.directoryRecords = b.uint64() // total number of entries in the central directory + d.directorySize = b.uint64() // size of the central directory + d.directoryOffset = b.uint64() // offset of start of central directory with respect to the starting disk number + + return nil +} + func findSignatureInBlock(b []byte) int { for i := len(b) - directoryEndLen; i >= 0; i-- { // defined from directoryEndSignature in struct.go @@ -335,3 +428,9 @@ func (b *readBuf) uint32() uint32 { *b = (*b)[4:] return v } + +func (b *readBuf) uint64() uint64 { + v := binary.LittleEndian.Uint64(*b) + *b = (*b)[8:] + return v +} diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go index 5f1d1b28a..cf9c59c4b 100644 --- a/src/pkg/archive/zip/reader_test.go +++ b/src/pkg/archive/zip/reader_test.go @@ -206,6 +206,17 @@ var tests = []ZipTest{ }, }, }, + { + Name: "zip64.zip", + File: []ZipTestFile{ + { + Name: "README", + Content: []byte("This small file is in ZIP64 format.\n"), + Mtime: "08-10-12 14:33:32", + Mode: 0644, + }, + }, + }, } var crossPlatform = []ZipTestFile{ diff --git a/src/pkg/archive/zip/struct.go b/src/pkg/archive/zip/struct.go index 55f3dcfb8..ea067f355 100644 --- a/src/pkg/archive/zip/struct.go +++ b/src/pkg/archive/zip/struct.go @@ -7,12 +7,19 @@ Package zip provides support for reading and writing ZIP archives. See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT -This package does not support ZIP64 or disk spanning. +This package does not support disk spanning. + +A note about ZIP64: + +To be backwards compatible the FileHeader has both 32 and 64 bit Size +fields. The 64 bit fields will always contain the correct value and +for normal archives both fields will be the same. For files requiring +the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit +fields must be used instead. */ package zip import ( - "errors" "os" "time" ) @@ -27,11 +34,16 @@ const ( fileHeaderSignature = 0x04034b50 directoryHeaderSignature = 0x02014b50 directoryEndSignature = 0x06054b50 + directory64LocSignature = 0x07064b50 + directory64EndSignature = 0x06064b50 dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder fileHeaderLen = 30 // + filename + extra directoryHeaderLen = 46 // + filename + extra + comment directoryEndLen = 22 // + comment dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size + dataDescriptor64Len = 24 // descriptor with 8 byte sizes + directory64LocLen = 20 // + directory64EndLen = 56 // + extra // Constants for the first byte in CreatorVersion creatorFAT = 0 @@ -39,22 +51,35 @@ const ( creatorNTFS = 11 creatorVFAT = 14 creatorMacOSX = 19 + + // version numbers + zipVersion20 = 20 // 2.0 + zipVersion45 = 45 // 4.5 (reads and writes zip64 archives) + + // limits for non zip64 files + uint16max = (1 << 16) - 1 + uint32max = (1 << 32) - 1 + + // extra header id's + zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field ) type FileHeader struct { - Name string - CreatorVersion uint16 - ReaderVersion uint16 - Flags uint16 - Method uint16 - ModifiedTime uint16 // MS-DOS time - ModifiedDate uint16 // MS-DOS date - CRC32 uint32 - CompressedSize uint32 - UncompressedSize uint32 - Extra []byte - ExternalAttrs uint32 // Meaning depends on CreatorVersion - Comment string + Name string + CreatorVersion uint16 + ReaderVersion uint16 + Flags uint16 + Method uint16 + ModifiedTime uint16 // MS-DOS time + ModifiedDate uint16 // MS-DOS date + CRC32 uint32 + CompressedSize uint32 // deprecated; use CompressedSize64 + UncompressedSize uint32 // deprecated; use UncompressedSize64 + CompressedSize64 uint64 + UncompressedSize64 uint64 + Extra []byte + ExternalAttrs uint32 // Meaning depends on CreatorVersion + Comment string } // FileInfo returns an os.FileInfo for the FileHeader. @@ -67,8 +92,13 @@ type headerFileInfo struct { fh *FileHeader } -func (fi headerFileInfo) Name() string { return fi.fh.Name } -func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) } +func (fi headerFileInfo) Name() string { return fi.fh.Name } +func (fi headerFileInfo) Size() int64 { + if fi.fh.UncompressedSize64 > 0 { + return int64(fi.fh.UncompressedSize64) + } + return int64(fi.fh.UncompressedSize) +} func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } @@ -78,25 +108,27 @@ func (fi headerFileInfo) Sys() interface{} { return fi.fh } // os.FileInfo. func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { size := fi.Size() - if size > (1<<32 - 1) { - return nil, errors.New("zip: file over 4GB") - } fh := &FileHeader{ - Name: fi.Name(), - UncompressedSize: uint32(size), + Name: fi.Name(), + UncompressedSize64: uint64(size), } fh.SetModTime(fi.ModTime()) fh.SetMode(fi.Mode()) + if fh.UncompressedSize64 > uint32max { + fh.UncompressedSize = uint32max + } else { + fh.UncompressedSize = uint32(fh.UncompressedSize64) + } return fh, nil } type directoryEnd struct { - diskNbr uint16 // unused - dirDiskNbr uint16 // unused - dirRecordsThisDisk uint16 // unused - directoryRecords uint16 - directorySize uint32 - directoryOffset uint32 // relative to file + diskNbr uint32 // unused + dirDiskNbr uint32 // unused + dirRecordsThisDisk uint64 // unused + directoryRecords uint64 + directorySize uint64 + directoryOffset uint64 // relative to file commentLen uint16 comment string } @@ -190,6 +222,11 @@ func (h *FileHeader) SetMode(mode os.FileMode) { } } +// isZip64 returns true if the file size exceeds the 32 bit limit +func (fh *FileHeader) isZip64() bool { + return fh.CompressedSize64 > uint32max || fh.UncompressedSize64 > uint32max +} + func msdosModeToFileMode(m uint32) (mode os.FileMode) { if m&msdosDir != 0 { mode = os.ModeDir | 0777 diff --git a/src/pkg/archive/zip/testdata/zip64.zip b/src/pkg/archive/zip/testdata/zip64.zip new file mode 100644 index 000000000..a2ee1fa33 Binary files /dev/null and b/src/pkg/archive/zip/testdata/zip64.zip differ diff --git a/src/pkg/archive/zip/writer.go b/src/pkg/archive/zip/writer.go index 45eb6bd73..4c696e152 100644 --- a/src/pkg/archive/zip/writer.go +++ b/src/pkg/archive/zip/writer.go @@ -27,7 +27,7 @@ type Writer struct { type header struct { *FileHeader - offset uint32 + offset uint64 } // NewWriter returns a new Writer writing a zip file to w. @@ -62,14 +62,36 @@ func (w *Writer) Close() error { b.uint16(h.ModifiedTime) b.uint16(h.ModifiedDate) b.uint32(h.CRC32) - b.uint32(h.CompressedSize) - b.uint32(h.UncompressedSize) + if h.isZip64() || h.offset > uint32max { + // the file needs a zip64 header. store maxint in both + // 32 bit size fields (and offset later) to signal that the + // zip64 extra header should be used. + b.uint32(uint32max) // compressed size + b.uint32(uint32max) // uncompressed size + + // append a zip64 extra block to Extra + var buf [28]byte // 2x uint16 + 3x uint64 + eb := writeBuf(buf[:]) + eb.uint16(zip64ExtraId) + eb.uint16(24) // size = 3x uint64 + eb.uint64(h.UncompressedSize64) + eb.uint64(h.CompressedSize64) + eb.uint64(h.offset) + h.Extra = append(h.Extra, buf[:]...) + } else { + b.uint32(h.CompressedSize) + b.uint32(h.UncompressedSize) + } b.uint16(uint16(len(h.Name))) b.uint16(uint16(len(h.Extra))) b.uint16(uint16(len(h.Comment))) b = b[4:] // skip disk number start and internal file attr (2x uint16) b.uint32(h.ExternalAttrs) - b.uint32(h.offset) + if h.offset > uint32max { + b.uint32(uint32max) + } else { + b.uint32(uint32(h.offset)) + } if _, err := w.cw.Write(buf[:]); err != nil { return err } @@ -85,15 +107,52 @@ func (w *Writer) Close() error { } end := w.cw.count + records := uint64(len(w.dir)) + size := uint64(end - start) + offset := uint64(start) + + if records > uint16max || size > uint32max || offset > uint32max { + var buf [directory64EndLen + directory64LocLen]byte + b := writeBuf(buf[:]) + + // zip64 end of central directory record + b.uint32(directory64EndSignature) + b.uint64(directory64EndLen) + b.uint16(zipVersion45) // version made by + b.uint16(zipVersion45) // version needed to extract + b.uint32(0) // number of this disk + b.uint32(0) // number of the disk with the start of the central directory + b.uint64(records) // total number of entries in the central directory on this disk + b.uint64(records) // total number of entries in the central directory + b.uint64(size) // size of the central directory + b.uint64(offset) // offset of start of central directory with respect to the starting disk number + + // zip64 end of central directory locator + b.uint32(directory64LocSignature) + b.uint32(0) // number of the disk with the start of the zip64 end of central directory + b.uint64(uint64(end)) // relative offset of the zip64 end of central directory record + b.uint32(1) // total number of disks + + if _, err := w.cw.Write(buf[:]); err != nil { + return err + } + + // store max values in the regular end record to signal that + // that the zip64 values should be used instead + records = uint16max + size = uint32max + offset = uint32max + } + // write end record var buf [directoryEndLen]byte b := writeBuf(buf[:]) b.uint32(uint32(directoryEndSignature)) - b = b[4:] // skip over disk number and first disk number (2x uint16) - b.uint16(uint16(len(w.dir))) // number of entries this disk - b.uint16(uint16(len(w.dir))) // number of entries total - b.uint32(uint32(end - start)) // size of directory - b.uint32(uint32(start)) // start of directory + b = b[4:] // skip over disk number and first disk number (2x uint16) + b.uint16(uint16(records)) // number of entries this disk + b.uint16(uint16(records)) // number of entries total + b.uint32(uint32(size)) // size of directory + b.uint32(uint32(offset)) // start of directory // skipped size of comment (always zero) if _, err := w.cw.Write(buf[:]); err != nil { return err @@ -115,7 +174,7 @@ func (w *Writer) Create(name string) (io.Writer, error) { } // CreateHeader adds a file to the zip file using the provided FileHeader -// for the file metadata. +// for the file metadata. // It returns a Writer to which the file contents should be written. // The file's contents must be written to the io.Writer before the next // call to Create, CreateHeader, or Close. @@ -127,8 +186,9 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { } fh.Flags |= 0x8 // we will write a data descriptor - fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14 - fh.ReaderVersion = 0x14 + + fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte + fh.ReaderVersion = zipVersion20 fw := &fileWriter{ zipw: w.cw, @@ -151,7 +211,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { h := &header{ FileHeader: fh, - offset: uint32(w.cw.count), + offset: uint64(w.cw.count), } w.dir = append(w.dir, h) fw.header = h @@ -173,9 +233,9 @@ func writeHeader(w io.Writer, h *FileHeader) error { b.uint16(h.Method) b.uint16(h.ModifiedTime) b.uint16(h.ModifiedDate) - b.uint32(h.CRC32) - b.uint32(h.CompressedSize) - b.uint32(h.UncompressedSize) + b.uint32(0) // since we are writing a data descriptor crc32, + b.uint32(0) // compressed size, + b.uint32(0) // and uncompressed size should be zero b.uint16(uint16(len(h.Name))) b.uint16(uint16(len(h.Extra))) if _, err := w.Write(buf[:]); err != nil { @@ -218,17 +278,40 @@ func (w *fileWriter) close() error { // update FileHeader fh := w.header.FileHeader fh.CRC32 = w.crc32.Sum32() - fh.CompressedSize = uint32(w.compCount.count) - fh.UncompressedSize = uint32(w.rawCount.count) + fh.CompressedSize64 = uint64(w.compCount.count) + fh.UncompressedSize64 = uint64(w.rawCount.count) - // write data descriptor - var buf [dataDescriptorLen]byte - b := writeBuf(buf[:]) + if fh.isZip64() { + fh.CompressedSize = uint32max + fh.UncompressedSize = uint32max + fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions + } else { + fh.CompressedSize = uint32(fh.CompressedSize64) + fh.UncompressedSize = uint32(fh.UncompressedSize64) + } + + // Write data descriptor. This is more complicated than one would + // think, see e.g. comments in zipfile.c:putextended() and + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588. + // The approach here is to write 8 byte sizes if needed without + // adding a zip64 extra in the local header (too late anyway). + var buf []byte + if fh.isZip64() { + buf = make([]byte, dataDescriptor64Len) + } else { + buf = make([]byte, dataDescriptorLen) + } + b := writeBuf(buf) b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X b.uint32(fh.CRC32) - b.uint32(fh.CompressedSize) - b.uint32(fh.UncompressedSize) - _, err := w.zipw.Write(buf[:]) + if fh.isZip64() { + b.uint64(fh.CompressedSize64) + b.uint64(fh.UncompressedSize64) + } else { + b.uint32(fh.CompressedSize) + b.uint32(fh.UncompressedSize) + } + _, err := w.zipw.Write(buf) return err } @@ -262,3 +345,8 @@ func (b *writeBuf) uint32(v uint32) { binary.LittleEndian.PutUint32(*b, v) *b = (*b)[4:] } + +func (b *writeBuf) uint64(v uint64) { + binary.LittleEndian.PutUint64(*b, v) + *b = (*b)[8:] +} diff --git a/src/pkg/archive/zip/zip_test.go b/src/pkg/archive/zip/zip_test.go index d6490c4cb..a8af206a8 100644 --- a/src/pkg/archive/zip/zip_test.go +++ b/src/pkg/archive/zip/zip_test.go @@ -9,7 +9,8 @@ package zip import ( "bytes" "fmt" - "reflect" + "io" + "io/ioutil" "strings" "testing" "time" @@ -17,8 +18,7 @@ import ( func TestOver65kFiles(t *testing.T) { if testing.Short() { - t.Logf("slow test; skipping") - return + t.Skip("slow test; skipping") } buf := new(bytes.Buffer) w := NewWriter(buf) @@ -58,6 +58,33 @@ func TestModTime(t *testing.T) { } } +func testHeaderRoundTrip(fh *FileHeader, wantUncompressedSize uint32, wantUncompressedSize64 uint64, t *testing.T) { + fi := fh.FileInfo() + fh2, err := FileInfoHeader(fi) + if err != nil { + t.Fatal(err) + } + if got, want := fh2.Name, fh.Name; got != want { + t.Errorf("Name: got %s, want %s\n", got, want) + } + if got, want := fh2.UncompressedSize, wantUncompressedSize; got != want { + t.Errorf("UncompressedSize: got %d, want %d\n", got, want) + } + if got, want := fh2.UncompressedSize64, wantUncompressedSize64; got != want { + t.Errorf("UncompressedSize64: got %d, want %d\n", got, want) + } + if got, want := fh2.ModifiedTime, fh.ModifiedTime; got != want { + t.Errorf("ModifiedTime: got %d, want %d\n", got, want) + } + if got, want := fh2.ModifiedDate, fh.ModifiedDate; got != want { + t.Errorf("ModifiedDate: got %d, want %d\n", got, want) + } + + if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh { + t.Errorf("Sys didn't return original *FileHeader") + } +} + func TestFileHeaderRoundTrip(t *testing.T) { fh := &FileHeader{ Name: "foo.txt", @@ -65,17 +92,164 @@ func TestFileHeaderRoundTrip(t *testing.T) { ModifiedTime: 1234, ModifiedDate: 5678, } - fi := fh.FileInfo() - fh2, err := FileInfoHeader(fi) + testHeaderRoundTrip(fh, fh.UncompressedSize, uint64(fh.UncompressedSize), t) +} - // Ignore these fields: - fh2.CreatorVersion = 0 - fh2.ExternalAttrs = 0 +func TestFileHeaderRoundTrip64(t *testing.T) { + fh := &FileHeader{ + Name: "foo.txt", + UncompressedSize64: 9876543210, + ModifiedTime: 1234, + ModifiedDate: 5678, + } + testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t) +} - if !reflect.DeepEqual(fh, fh2) { - t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err) +func TestZip64(t *testing.T) { + if testing.Short() { + t.Skip("slow test; skipping") } - if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh { - t.Errorf("Sys didn't return original *FileHeader") + // write 2^32 bytes plus "END\n" to a zip file + buf := new(bytes.Buffer) + w := NewWriter(buf) + f, err := w.Create("huge.txt") + if err != nil { + t.Fatal(err) + } + chunk := make([]byte, 1024) + for i := range chunk { + chunk[i] = '.' + } + chunk[len(chunk)-1] = '\n' + end := []byte("END\n") + for i := 0; i < (1<<32)/1024; i++ { + _, err := f.Write(chunk) + if err != nil { + t.Fatal("write chunk:", err) + } + } + _, err = f.Write(end) + if err != nil { + t.Fatal("write end:", err) + } + if err := w.Close(); err != nil { + t.Fatal(err) + } + + // read back zip file and check that we get to the end of it + r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) + if err != nil { + t.Fatal("reader:", err) + } + f0 := r.File[0] + rc, err := f0.Open() + if err != nil { + t.Fatal("opening:", err) + } + for i := 0; i < (1<<32)/1024; i++ { + _, err := io.ReadFull(rc, chunk) + if err != nil { + t.Fatal("read:", err) + } + } + gotEnd, err := ioutil.ReadAll(rc) + if err != nil { + t.Fatal("read end:", err) + } + if !bytes.Equal(gotEnd, end) { + t.Errorf("End of zip64 archive %q, want %q", gotEnd, end) + } + err = rc.Close() + if err != nil { + t.Fatal("closing:", err) + } + if got, want := f0.UncompressedSize, uint32(uint32max); got != want { + t.Errorf("UncompressedSize %d, want %d", got, want) + } + + if got, want := f0.UncompressedSize64, (1<<32)+uint64(len(end)); got != want { + t.Errorf("UncompressedSize64 %d, want %d", got, want) + } +} + +func testInvalidHeader(h *FileHeader, t *testing.T) { + var buf bytes.Buffer + z := NewWriter(&buf) + + f, err := z.CreateHeader(h) + if err != nil { + t.Fatalf("error creating header: %v", err) + } + if _, err := f.Write([]byte("hi")); err != nil { + t.Fatalf("error writing content: %v", err) + } + if err := z.Close(); err != nil { + t.Fatalf("error closing zip writer: %v", err) + } + + b := buf.Bytes() + if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != ErrFormat { + t.Fatalf("got %v, expected ErrFormat", err) + } +} + +func testValidHeader(h *FileHeader, t *testing.T) { + var buf bytes.Buffer + z := NewWriter(&buf) + + f, err := z.CreateHeader(h) + if err != nil { + t.Fatalf("error creating header: %v", err) + } + if _, err := f.Write([]byte("hi")); err != nil { + t.Fatalf("error writing content: %v", err) + } + if err := z.Close(); err != nil { + t.Fatalf("error closing zip writer: %v", err) + } + + b := buf.Bytes() + if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != nil { + t.Fatalf("got %v, expected nil", err) + } +} + +// Issue 4302. +func TestHeaderInvalidTagAndSize(t *testing.T) { + const timeFormat = "20060102T150405.000.txt" + + ts := time.Now() + filename := ts.Format(timeFormat) + + h := FileHeader{ + Name: filename, + Method: Deflate, + Extra: []byte(ts.Format(time.RFC3339Nano)), // missing tag and len + } + h.SetModTime(ts) + + testInvalidHeader(&h, t) +} + +func TestHeaderTooShort(t *testing.T) { + h := FileHeader{ + Name: "foo.txt", + Method: Deflate, + Extra: []byte{zip64ExtraId}, // missing size + } + testInvalidHeader(&h, t) +} + +// Issue 4393. It is valid to have an extra data header +// which contains no body. +func TestZeroLengthHeader(t *testing.T) { + h := FileHeader{ + Name: "extadata.txt", + Method: Deflate, + Extra: []byte{ + 85, 84, 5, 0, 3, 154, 144, 195, 77, // tag 21589 size 5 + 85, 120, 0, 0, // tag 30805 size 0 + }, } + testValidHeader(&h, t) } diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go index b44d0e7d1..ee69c2d31 100644 --- a/src/pkg/bufio/bufio.go +++ b/src/pkg/bufio/bufio.go @@ -64,6 +64,8 @@ func NewReader(rd io.Reader) *Reader { return NewReaderSize(rd, defaultBufSize) } +var errNegativeRead = errors.New("bufio: reader returned negative count from Read") + // fill reads a new chunk into the buffer. func (b *Reader) fill() { // Slide existing data to beginning. @@ -74,10 +76,13 @@ func (b *Reader) fill() { } // Read new data. - n, e := b.rd.Read(b.buf[b.w:]) + n, err := b.rd.Read(b.buf[b.w:]) + if n < 0 { + panic(errNegativeRead) + } b.w += n - if e != nil { - b.err = e + if err != nil { + b.err = err } } @@ -272,6 +277,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { panic("not reached") } +// ReadLine is a low-level line-reading primitive. Most callers should use +// ReadBytes('\n') or ReadString('\n') instead or use a Scanner. +// // ReadLine tries to return a single line, not including the end-of-line bytes. // If the line was too long for the buffer then isPrefix is set and the // beginning of the line is returned. The rest of the line will be returned @@ -279,6 +287,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { // of the line. The returned buffer is only valid until the next call to // ReadLine. ReadLine either returns a non-nil line or it returns an error, // never both. +// +// The text returned from ReadLine does not include the line end ("\r\n" or "\n"). +// No indication or error is given if the input ends without a final line end. func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) { line, err = b.ReadSlice('\n') if err == ErrBufferFull { @@ -320,6 +331,7 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) { // it returns the data read before the error and the error itself (often io.EOF). // ReadBytes returns err != nil if and only if the returned data does not end in // delim. +// For simple uses, a Scanner may be more convenient. func (b *Reader) ReadBytes(delim byte) (line []byte, err error) { // Use ReadSlice to look for array, // accumulating full buffers. @@ -367,9 +379,45 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err error) { // it returns the data read before the error and the error itself (often io.EOF). // ReadString returns err != nil if and only if the returned data does not end in // delim. +// For simple uses, a Scanner may be more convenient. func (b *Reader) ReadString(delim byte) (line string, err error) { - bytes, e := b.ReadBytes(delim) - return string(bytes), e + bytes, err := b.ReadBytes(delim) + return string(bytes), err +} + +// WriteTo implements io.WriterTo. +func (b *Reader) WriteTo(w io.Writer) (n int64, err error) { + n, err = b.writeBuf(w) + if err != nil { + return + } + + if r, ok := b.rd.(io.WriterTo); ok { + m, err := r.WriteTo(w) + n += m + return n, err + } + + for b.fill(); b.r < b.w; b.fill() { + m, err := b.writeBuf(w) + n += m + if err != nil { + return n, err + } + } + + if b.err == io.EOF { + b.err = nil + } + + return n, b.readErr() +} + +// writeBuf writes the Reader's buffer to the writer. +func (b *Reader) writeBuf(w io.Writer) (int64, error) { + n, err := w.Write(b.buf[b.r:b.w]) + b.r += n + return int64(n), err } // buffered output @@ -415,17 +463,17 @@ func (b *Writer) Flush() error { if b.n == 0 { return nil } - n, e := b.wr.Write(b.buf[0:b.n]) - if n < b.n && e == nil { - e = io.ErrShortWrite + n, err := b.wr.Write(b.buf[0:b.n]) + if n < b.n && err == nil { + err = io.ErrShortWrite } - if e != nil { + if err != nil { if n > 0 && n < b.n { copy(b.buf[0:b.n-n], b.buf[n:b.n]) } b.n -= n - b.err = e - return e + b.err = err + return err } b.n = 0 return nil @@ -529,6 +577,36 @@ func (b *Writer) WriteString(s string) (int, error) { return nn, nil } +// ReadFrom implements io.ReaderFrom. +func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { + if b.Buffered() == 0 { + if w, ok := b.wr.(io.ReaderFrom); ok { + return w.ReadFrom(r) + } + } + var m int + for { + m, err = r.Read(b.buf[b.n:]) + if m == 0 { + break + } + b.n += m + n += int64(m) + if b.Available() == 0 { + if err1 := b.Flush(); err1 != nil { + return n, err1 + } + } + if err != nil { + break + } + } + if err == io.EOF { + err = nil + } + return n, err +} + // buffered input and output // ReadWriter stores pointers to a Reader and a Writer. diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go index a43cbd23a..b0e811443 100644 --- a/src/pkg/bufio/bufio_test.go +++ b/src/pkg/bufio/bufio_test.go @@ -28,9 +28,9 @@ func newRot13Reader(r io.Reader) *rot13Reader { } func (r13 *rot13Reader) Read(p []byte) (int, error) { - n, e := r13.r.Read(p) - if e != nil { - return n, e + n, err := r13.r.Read(p) + if err != nil { + return n, err } for i := 0; i < n; i++ { c := p[i] | 0x20 // lowercase byte @@ -48,15 +48,15 @@ func readBytes(buf *Reader) string { var b [1000]byte nb := 0 for { - c, e := buf.ReadByte() - if e == io.EOF { + c, err := buf.ReadByte() + if err == io.EOF { break } - if e == nil { + if err == nil { b[nb] = c nb++ - } else if e != iotest.ErrTimeout { - panic("Data: " + e.Error()) + } else if err != iotest.ErrTimeout { + panic("Data: " + err.Error()) } } return string(b[0:nb]) @@ -93,12 +93,12 @@ var readMakers = []readMaker{ func readLines(b *Reader) string { s := "" for { - s1, e := b.ReadString('\n') - if e == io.EOF { + s1, err := b.ReadString('\n') + if err == io.EOF { break } - if e != nil && e != iotest.ErrTimeout { - panic("GetLines: " + e.Error()) + if err != nil && err != iotest.ErrTimeout { + panic("GetLines: " + err.Error()) } s += s1 } @@ -110,9 +110,9 @@ func reads(buf *Reader, m int) string { var b [1000]byte nb := 0 for { - n, e := buf.Read(b[nb : nb+m]) + n, err := buf.Read(b[nb : nb+m]) nb += n - if e == io.EOF { + if err == io.EOF { break } } @@ -748,7 +748,7 @@ func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) { b := NewReaderSize(strings.NewReader(input), minReadBufferSize) for i, e := range expect { line, isPrefix, err := b.ReadLine() - if bytes.Compare(line, e.line) != 0 { + if !bytes.Equal(line, e.line) { t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line) return } @@ -762,3 +762,283 @@ func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) { } } } + +func createTestInput(n int) []byte { + input := make([]byte, n) + for i := range input { + // 101 and 251 are arbitrary prime numbers. + // The idea is to create an input sequence + // which doesn't repeat too frequently. + input[i] = byte(i % 251) + if i%101 == 0 { + input[i] ^= byte(i / 101) + } + } + return input +} + +func TestReaderWriteTo(t *testing.T) { + input := createTestInput(8192) + r := NewReader(onlyReader{bytes.NewBuffer(input)}) + w := new(bytes.Buffer) + if n, err := r.WriteTo(w); err != nil || n != int64(len(input)) { + t.Fatalf("r.WriteTo(w) = %d, %v, want %d, nil", n, err, len(input)) + } + + for i, val := range w.Bytes() { + if val != input[i] { + t.Errorf("after write: out[%d] = %#x, want %#x", i, val, input[i]) + } + } +} + +type errorWriterToTest struct { + rn, wn int + rerr, werr error + expected error +} + +func (r errorWriterToTest) Read(p []byte) (int, error) { + return len(p) * r.rn, r.rerr +} + +func (w errorWriterToTest) Write(p []byte) (int, error) { + return len(p) * w.wn, w.werr +} + +var errorWriterToTests = []errorWriterToTest{ + {1, 0, nil, io.ErrClosedPipe, io.ErrClosedPipe}, + {0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe}, + {0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrClosedPipe}, + {0, 1, io.EOF, nil, nil}, +} + +func TestReaderWriteToErrors(t *testing.T) { + for i, rw := range errorWriterToTests { + r := NewReader(rw) + if _, err := r.WriteTo(rw); err != rw.expected { + t.Errorf("r.WriteTo(errorWriterToTests[%d]) = _, %v, want _,%v", i, err, rw.expected) + } + } +} + +func TestWriterReadFrom(t *testing.T) { + ws := []func(io.Writer) io.Writer{ + func(w io.Writer) io.Writer { return onlyWriter{w} }, + func(w io.Writer) io.Writer { return w }, + } + + rs := []func(io.Reader) io.Reader{ + iotest.DataErrReader, + func(r io.Reader) io.Reader { return r }, + } + + for ri, rfunc := range rs { + for wi, wfunc := range ws { + input := createTestInput(8192) + b := new(bytes.Buffer) + w := NewWriter(wfunc(b)) + r := rfunc(bytes.NewBuffer(input)) + if n, err := w.ReadFrom(r); err != nil || n != int64(len(input)) { + t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input)) + continue + } + if got, want := b.String(), string(input); got != want { + t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want) + } + } + } +} + +type errorReaderFromTest struct { + rn, wn int + rerr, werr error + expected error +} + +func (r errorReaderFromTest) Read(p []byte) (int, error) { + return len(p) * r.rn, r.rerr +} + +func (w errorReaderFromTest) Write(p []byte) (int, error) { + return len(p) * w.wn, w.werr +} + +var errorReaderFromTests = []errorReaderFromTest{ + {0, 1, io.EOF, nil, nil}, + {1, 1, io.EOF, nil, nil}, + {0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe}, + {0, 0, io.ErrClosedPipe, io.ErrShortWrite, io.ErrClosedPipe}, + {1, 0, nil, io.ErrShortWrite, io.ErrShortWrite}, +} + +func TestWriterReadFromErrors(t *testing.T) { + for i, rw := range errorReaderFromTests { + w := NewWriter(rw) + if _, err := w.ReadFrom(rw); err != rw.expected { + t.Errorf("w.ReadFrom(errorReaderFromTests[%d]) = _, %v, want _,%v", i, err, rw.expected) + } + } +} + +// TestWriterReadFromCounts tests that using io.Copy to copy into a +// bufio.Writer does not prematurely flush the buffer. For example, when +// buffering writes to a network socket, excessive network writes should be +// avoided. +func TestWriterReadFromCounts(t *testing.T) { + var w0 writeCountingDiscard + b0 := NewWriterSize(&w0, 1234) + b0.WriteString(strings.Repeat("x", 1000)) + if w0 != 0 { + t.Fatalf("write 1000 'x's: got %d writes, want 0", w0) + } + b0.WriteString(strings.Repeat("x", 200)) + if w0 != 0 { + t.Fatalf("write 1200 'x's: got %d writes, want 0", w0) + } + io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 30))}) + if w0 != 0 { + t.Fatalf("write 1230 'x's: got %d writes, want 0", w0) + } + io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 9))}) + if w0 != 1 { + t.Fatalf("write 1239 'x's: got %d writes, want 1", w0) + } + + var w1 writeCountingDiscard + b1 := NewWriterSize(&w1, 1234) + b1.WriteString(strings.Repeat("x", 1200)) + b1.Flush() + if w1 != 1 { + t.Fatalf("flush 1200 'x's: got %d writes, want 1", w1) + } + b1.WriteString(strings.Repeat("x", 89)) + if w1 != 1 { + t.Fatalf("write 1200 + 89 'x's: got %d writes, want 1", w1) + } + io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 700))}) + if w1 != 1 { + t.Fatalf("write 1200 + 789 'x's: got %d writes, want 1", w1) + } + io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 600))}) + if w1 != 2 { + t.Fatalf("write 1200 + 1389 'x's: got %d writes, want 2", w1) + } + b1.Flush() + if w1 != 3 { + t.Fatalf("flush 1200 + 1389 'x's: got %d writes, want 3", w1) + } +} + +// A writeCountingDiscard is like ioutil.Discard and counts the number of times +// Write is called on it. +type writeCountingDiscard int + +func (w *writeCountingDiscard) Write(p []byte) (int, error) { + *w++ + return len(p), nil +} + +type negativeReader int + +func (r *negativeReader) Read([]byte) (int, error) { return -1, nil } + +func TestNegativeRead(t *testing.T) { + // should panic with a description pointing at the reader, not at itself. + // (should NOT panic with slice index error, for example.) + b := NewReader(new(negativeReader)) + defer func() { + switch err := recover().(type) { + case nil: + t.Fatal("read did not panic") + case error: + if !strings.Contains(err.Error(), "reader returned negative count from Read") { + t.Fatalf("wrong panic: %v", err) + } + default: + t.Fatalf("unexpected panic value: %T(%v)", err, err) + } + }() + b.Read(make([]byte, 100)) +} + +// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have. +type onlyReader struct { + r io.Reader +} + +func (r onlyReader) Read(b []byte) (int, error) { + return r.r.Read(b) +} + +// An onlyWriter only implements io.Writer, no matter what other methods the underlying implementation may have. +type onlyWriter struct { + w io.Writer +} + +func (w onlyWriter) Write(b []byte) (int, error) { + return w.w.Write(b) +} + +func BenchmarkReaderCopyOptimal(b *testing.B) { + // Optimal case is where the underlying reader implements io.WriterTo + for i := 0; i < b.N; i++ { + b.StopTimer() + src := NewReader(bytes.NewBuffer(make([]byte, 8192))) + dst := onlyWriter{new(bytes.Buffer)} + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkReaderCopyUnoptimal(b *testing.B) { + // Unoptimal case is where the underlying reader doesn't implement io.WriterTo + for i := 0; i < b.N; i++ { + b.StopTimer() + src := NewReader(onlyReader{bytes.NewBuffer(make([]byte, 8192))}) + dst := onlyWriter{new(bytes.Buffer)} + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkReaderCopyNoWriteTo(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + src := onlyReader{NewReader(bytes.NewBuffer(make([]byte, 8192)))} + dst := onlyWriter{new(bytes.Buffer)} + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkWriterCopyOptimal(b *testing.B) { + // Optimal case is where the underlying writer implements io.ReaderFrom + for i := 0; i < b.N; i++ { + b.StopTimer() + src := onlyReader{bytes.NewBuffer(make([]byte, 8192))} + dst := NewWriter(new(bytes.Buffer)) + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkWriterCopyUnoptimal(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + src := onlyReader{bytes.NewBuffer(make([]byte, 8192))} + dst := NewWriter(onlyWriter{new(bytes.Buffer)}) + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkWriterCopyNoReadFrom(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + src := onlyReader{bytes.NewBuffer(make([]byte, 8192))} + dst := onlyWriter{NewWriter(new(bytes.Buffer))} + b.StartTimer() + io.Copy(dst, src) + } +} diff --git a/src/pkg/bufio/example_test.go b/src/pkg/bufio/example_test.go new file mode 100644 index 000000000..b545ce39a --- /dev/null +++ b/src/pkg/bufio/example_test.go @@ -0,0 +1,74 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufio_test + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +// The simplest use of a Scanner, to read standard input as a set of lines. +func ExampleScanner_lines() { + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + fmt.Println(scanner.Text()) // Println will add back the final '\n' + } + if err := scanner.Err(); err != nil { + fmt.Fprintln(os.Stdout, "reading standard input:", err) + } +} + +// Use a Scanner to implement a simple word-count utility by scanning the +// input as a sequence of space-delimited tokens. +func ExampleScanner_words() { + // An artificial input source. + const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n" + scanner := bufio.NewScanner(strings.NewReader(input)) + // Set the split function for the scanning operation. + scanner.Split(bufio.ScanWords) + // Count the words. + count := 0 + for scanner.Scan() { + count++ + } + if err := scanner.Err(); err != nil { + fmt.Fprintln(os.Stdout, "reading input:", err) + } + fmt.Printf("%d\n", count) + // Output: 15 +} + +// Use a Scanner with a custom split function (built by wrapping ScanWords) to validate +// 32-bit decimal input. +func ExampleScanner_custom() { + // An artificial input source. + const input = "1234 5678 1234567901234567890" + scanner := bufio.NewScanner(strings.NewReader(input)) + // Create a custom split function by wrapping the existing ScanWords function. + split := func(data []byte, atEOF bool) (advance int, token []byte, err error) { + advance, token, err = bufio.ScanWords(data, atEOF) + if err == nil && token != nil { + _, err = strconv.ParseInt(string(token), 10, 32) + } + return + } + // Set the split function for the scanning operation. + scanner.Split(split) + // Validate the input + for scanner.Scan() { + fmt.Printf("%s\n", scanner.Text()) + } + + if err := scanner.Err(); err != nil { + fmt.Printf("Invalid input: %s", err) + } + // Output: + // 1234 + // 5678 + // Invalid input: strconv.ParseInt: parsing "1234567901234567890": value out of range +} diff --git a/src/pkg/bufio/export_test.go b/src/pkg/bufio/export_test.go new file mode 100644 index 000000000..3d3bb27d8 --- /dev/null +++ b/src/pkg/bufio/export_test.go @@ -0,0 +1,27 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufio + +// Exported for testing only. +import ( + "unicode/utf8" +) + +var IsSpace = isSpace + +func (s *Scanner) MaxTokenSize(n int) { + if n < utf8.UTFMax || n > 1e9 { + panic("bad max token size") + } + if n < len(s.buf) { + s.buf = make([]byte, n) + } + s.maxTokenSize = n +} + +// ErrOrEOF is like Err, but returns EOF. Used to test a corner case. +func (s *Scanner) ErrOrEOF() error { + return s.err +} diff --git a/src/pkg/bufio/scan.go b/src/pkg/bufio/scan.go new file mode 100644 index 000000000..268ce6d1d --- /dev/null +++ b/src/pkg/bufio/scan.go @@ -0,0 +1,338 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufio + +import ( + "bytes" + "errors" + "io" + "unicode/utf8" +) + +// Scanner provides a convenient interface for reading data such as +// a file of newline-delimited lines of text. Successive calls to +// the Scan method will step through the 'tokens' of a file, skipping +// the bytes between the tokens. The specification of a token is +// defined by a split function of type SplitFunc; the default split +// function breaks the input into lines with newlines stripped. Split +// functions are defined in this package for scanning a file into +// lines, bytes, UTF-8-encoded runes, and space-delimited words. The +// client may instead provide a custom split function. +// +// Scanning stops unrecoverably at EOF, the first I/O error, or a token too +// large to fit in the buffer. When a scan stops, the reader may have +// advanced arbitrarily far past the last token. Programs that need more +// control over error handling or large tokens, or must run sequential scans +// on a reader, should use bufio.Reader instead. +// +// TODO(r): Provide executable examples. +// +type Scanner struct { + r io.Reader // The reader provided by the client. + split SplitFunc // The function to split the tokens. + maxTokenSize int // Maximum size of a token; modified by tests. + token []byte // Last token returned by split. + buf []byte // Buffer used as argument to split. + start int // First non-processed byte in buf. + end int // End of data in buf. + err error // Sticky error. +} + +// SplitFunc is the signature of the split function used to tokenize the +// input. The arguments are an initial substring of the remaining unprocessed +// data and a flag, atEOF, that reports whether the Reader has no more data +// to give. The return values are the number of bytes to advance the input +// and the next token to return to the user, plus an error, if any. If the +// data does not yet hold a complete token, for instance if it has no newline +// while scanning lines, SplitFunc can return (0, nil) to signal the Scanner +// to read more data into the slice and try again with a longer slice +// starting at the same point in the input. +// +// If the returned error is non-nil, scanning stops and the error +// is returned to the client. +// +// The function is never called with an empty data slice unless atEOF +// is true. If atEOF is true, however, data may be non-empty and, +// as always, holds unprocessed text. +type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error) + +// Errors returned by Scanner. +var ( + ErrTooLong = errors.New("bufio.Scanner: token too long") + ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count") + ErrAdvanceTooFar = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input") +) + +const ( + // Maximum size used to buffer a token. The actual maximum token size + // may be smaller as the buffer may need to include, for instance, a newline. + MaxScanTokenSize = 64 * 1024 +) + +// NewScanner returns a new Scanner to read from r. +func NewScanner(r io.Reader) *Scanner { + return &Scanner{ + r: r, + split: ScanLines, + maxTokenSize: MaxScanTokenSize, + buf: make([]byte, 4096), // Plausible starting size; needn't be large. + } +} + +// Err returns the first non-EOF error that was encountered by the Scanner. +func (s *Scanner) Err() error { + if s.err == io.EOF { + return nil + } + return s.err +} + +// Bytes returns the most recent token generated by a call to Scan. +// The underlying array may point to data that will be overwritten +// by a subsequent call to Scan. It does no allocation. +func (s *Scanner) Bytes() []byte { + return s.token +} + +// Text returns the most recent token generated by a call to Scan +// as a newly allocated string holding its bytes. +func (s *Scanner) Text() string { + return string(s.token) +} + +// Scan advances the Scanner to the next token, which will then be +// available through the Bytes or Text method. It returns false when the +// scan stops, either by reaching the end of the input or an error. +// After Scan returns false, the Err method will return any error that +// occurred during scanning, except that if it was io.EOF, Err +// will return nil. +func (s *Scanner) Scan() bool { + // Loop until we have a token. + for { + // See if we can get a token with what we already have. + if s.end > s.start { + advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil) + if err != nil { + s.setErr(err) + return false + } + if !s.advance(advance) { + return false + } + s.token = token + if token != nil { + return true + } + } + // We cannot generate a token with what we are holding. + // If we've already hit EOF or an I/O error, we are done. + if s.err != nil { + // Shut it down. + s.start = 0 + s.end = 0 + return false + } + // Must read more data. + // First, shift data to beginning of buffer if there's lots of empty space + // or space is neded. + if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) { + copy(s.buf, s.buf[s.start:s.end]) + s.end -= s.start + s.start = 0 + } + // Is the buffer full? If so, resize. + if s.end == len(s.buf) { + if len(s.buf) >= s.maxTokenSize { + s.setErr(ErrTooLong) + return false + } + newSize := len(s.buf) * 2 + if newSize > s.maxTokenSize { + newSize = s.maxTokenSize + } + newBuf := make([]byte, newSize) + copy(newBuf, s.buf[s.start:s.end]) + s.buf = newBuf + s.end -= s.start + s.start = 0 + continue + } + // Finally we can read some input. + n, err := s.r.Read(s.buf[s.end:len(s.buf)]) + if err != nil { + s.setErr(err) + } + if n == 0 { // Don't loop forever if Reader doesn't deliver EOF. + s.err = io.EOF + } + s.end += n + } + panic("not reached") +} + +// advance consumes n bytes of the buffer. It reports whether the advance was legal. +func (s *Scanner) advance(n int) bool { + if n < 0 { + s.setErr(ErrNegativeAdvance) + return false + } + if n > s.end-s.start { + s.setErr(ErrAdvanceTooFar) + return false + } + s.start += n + return true +} + +// setErr records the first error encountered. +func (s *Scanner) setErr(err error) { + if s.err == nil || s.err == io.EOF { + s.err = err + } +} + +// Split sets the split function for the Scanner. If called, it must be +// called before Scan. The default split function is ScanLines. +func (s *Scanner) Split(split SplitFunc) { + s.split = split +} + +// Split functions + +// ScanBytes is a split function for a Scanner that returns each byte as a token. +func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + return 1, data[0:1], nil +} + +var errorRune = []byte(string(utf8.RuneError)) + +// ScanRunes is a split function for a Scanner that returns each +// UTF-8-encoded rune as a token. The sequence of runes returned is +// equivalent to that from a range loop over the input as a string, which +// means that erroneous UTF-8 encodings translate to U+FFFD = "\xef\xbf\xbd". +// Because of the Scan interface, this makes it impossible for the client to +// distinguish correctly encoded replacement runes from encoding errors. +func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + + // Fast path 1: ASCII. + if data[0] < utf8.RuneSelf { + return 1, data[0:1], nil + } + + // Fast path 2: Correct UTF-8 decode without error. + _, width := utf8.DecodeRune(data) + if width > 1 { + // It's a valid encoding. Width cannot be one for a correctly encoded + // non-ASCII rune. + return width, data[0:width], nil + } + + // We know it's an error: we have width==1 and implicitly r==utf8.RuneError. + // Is the error because there wasn't a full rune to be decoded? + // FullRune distinguishes correctly between erroneous and incomplete encodings. + if !atEOF && !utf8.FullRune(data) { + // Incomplete; get more bytes. + return 0, nil, nil + } + + // We have a real UTF-8 encoding error. Return a properly encoded error rune + // but advance only one byte. This matches the behavior of a range loop over + // an incorrectly encoded string. + return 1, errorRune, nil +} + +// dropCR drops a terminal \r from the data. +func dropCR(data []byte) []byte { + if len(data) > 0 && data[len(data)-1] == '\r' { + return data[0 : len(data)-1] + } + return data +} + +// ScanLines is a split function for a Scanner that returns each line of +// text, stripped of any trailing end-of-line marker. The returned line may +// be empty. The end-of-line marker is one optional carriage return followed +// by one mandatory newline. In regular expression notation, it is `\r?\n'. +// The last non-empty line of input will be returned even if it has no +// newline. +func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + if i := bytes.IndexByte(data, '\n'); i >= 0 { + // We have a full newline-terminated line. + return i + 1, dropCR(data[0:i]), nil + } + // If we're at EOF, we have a final, non-terminated line. Return it. + if atEOF { + return len(data), dropCR(data), nil + } + // Request more data. + return 0, nil, nil +} + +// isSpace returns whether the character is a Unicode white space character. +// We avoid dependency on the unicode package, but check validity of the implementation +// in the tests. +func isSpace(r rune) bool { + if r <= '\u00FF' { + // Obvious ASCII ones: \t through \r plus space. Plus two Latin-1 oddballs. + switch r { + case ' ', '\t', '\n', '\v', '\f', '\r': + return true + case '\u0085', '\u00A0': + return true + } + return false + } + // High-valued ones. + if '\u2000' <= r && r <= '\u200a' { + return true + } + switch r { + case '\u1680', '\u180e', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000': + return true + } + return false +} + +// ScanWords is a split function for a Scanner that returns each +// space-separated word of text, with surrounding spaces deleted. It will +// never return an empty string. The definition of space is set by +// unicode.IsSpace. +func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) { + // Skip leading spaces. + start := 0 + for width := 0; start < len(data); start += width { + var r rune + r, width = utf8.DecodeRune(data[start:]) + if !isSpace(r) { + break + } + } + if atEOF && len(data) == 0 { + return 0, nil, nil + } + // Scan until space, marking end of word. + for width, i := 0, start; i < len(data); i += width { + var r rune + r, width = utf8.DecodeRune(data[i:]) + if isSpace(r) { + return i + width, data[start:i], nil + } + } + // If we're at EOF, we have a final, non-empty, non-terminated word. Return it. + if atEOF && len(data) > start { + return len(data), data[start:], nil + } + // Request more data. + return 0, nil, nil +} diff --git a/src/pkg/bufio/scan_test.go b/src/pkg/bufio/scan_test.go new file mode 100644 index 000000000..48729aabb --- /dev/null +++ b/src/pkg/bufio/scan_test.go @@ -0,0 +1,370 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufio_test + +import ( + . "bufio" + "bytes" + "errors" + "io" + "strings" + "testing" + "unicode" + "unicode/utf8" +) + +// Test white space table matches the Unicode definition. +func TestSpace(t *testing.T) { + for r := rune(0); r <= utf8.MaxRune; r++ { + if IsSpace(r) != unicode.IsSpace(r) { + t.Fatalf("white space property disagrees: %#U should be %t", r, unicode.IsSpace(r)) + } + } +} + +var scanTests = []string{ + "", + "a", + "¼", + "☹", + "\x81", // UTF-8 error + "\uFFFD", // correctly encoded RuneError + "abcdefgh", + "abc def\n\t\tgh ", + "abc¼☹\x81\uFFFD日本語\x82abc", +} + +func TestScanByte(t *testing.T) { + for n, test := range scanTests { + buf := bytes.NewBufferString(test) + s := NewScanner(buf) + s.Split(ScanBytes) + var i int + for i = 0; s.Scan(); i++ { + if b := s.Bytes(); len(b) != 1 || b[0] != test[i] { + t.Errorf("#%d: %d: expected %q got %q", n, i, test, b) + } + } + if i != len(test) { + t.Errorf("#%d: termination expected at %d; got %d", n, len(test), i) + } + err := s.Err() + if err != nil { + t.Errorf("#%d: %v", n, err) + } + } +} + +// Test that the rune splitter returns same sequence of runes (not bytes) as for range string. +func TestScanRune(t *testing.T) { + for n, test := range scanTests { + buf := bytes.NewBufferString(test) + s := NewScanner(buf) + s.Split(ScanRunes) + var i, runeCount int + var expect rune + // Use a string range loop to validate the sequence of runes. + for i, expect = range string(test) { + if !s.Scan() { + break + } + runeCount++ + got, _ := utf8.DecodeRune(s.Bytes()) + if got != expect { + t.Errorf("#%d: %d: expected %q got %q", n, i, expect, got) + } + } + if s.Scan() { + t.Errorf("#%d: scan ran too long, got %q", n, s.Text()) + } + testRuneCount := utf8.RuneCountInString(test) + if runeCount != testRuneCount { + t.Errorf("#%d: termination expected at %d; got %d", n, testRuneCount, runeCount) + } + err := s.Err() + if err != nil { + t.Errorf("#%d: %v", n, err) + } + } +} + +var wordScanTests = []string{ + "", + " ", + "\n", + "a", + " a ", + "abc def", + " abc def ", + " abc\tdef\nghi\rjkl\fmno\vpqr\u0085stu\u00a0\n", +} + +// Test that the word splitter returns the same data as strings.Fields. +func TestScanWords(t *testing.T) { + for n, test := range wordScanTests { + buf := bytes.NewBufferString(test) + s := NewScanner(buf) + s.Split(ScanWords) + words := strings.Fields(test) + var wordCount int + for wordCount = 0; wordCount < len(words); wordCount++ { + if !s.Scan() { + break + } + got := s.Text() + if got != words[wordCount] { + t.Errorf("#%d: %d: expected %q got %q", n, wordCount, words[wordCount], got) + } + } + if s.Scan() { + t.Errorf("#%d: scan ran too long, got %q", n, s.Text()) + } + if wordCount != len(words) { + t.Errorf("#%d: termination expected at %d; got %d", n, len(words), wordCount) + } + err := s.Err() + if err != nil { + t.Errorf("#%d: %v", n, err) + } + } +} + +// slowReader is a reader that returns only a few bytes at a time, to test the incremental +// reads in Scanner.Scan. +type slowReader struct { + max int + buf *bytes.Buffer +} + +func (sr *slowReader) Read(p []byte) (n int, err error) { + if len(p) > sr.max { + p = p[0:sr.max] + } + return sr.buf.Read(p) +} + +// genLine writes to buf a predictable but non-trivial line of text of length +// n, including the terminal newline and an occasional carriage return. +// If addNewline is false, the \r and \n are not emitted. +func genLine(buf *bytes.Buffer, lineNum, n int, addNewline bool) { + buf.Reset() + doCR := lineNum%5 == 0 + if doCR { + n-- + } + for i := 0; i < n-1; i++ { // Stop early for \n. + c := 'a' + byte(lineNum+i) + if c == '\n' || c == '\r' { // Don't confuse us. + c = 'N' + } + buf.WriteByte(c) + } + if addNewline { + if doCR { + buf.WriteByte('\r') + } + buf.WriteByte('\n') + } + return +} + +// Test the line splitter, including some carriage returns but no long lines. +func TestScanLongLines(t *testing.T) { + const smallMaxTokenSize = 256 // Much smaller for more efficient testing. + // Build a buffer of lots of line lengths up to but not exceeding smallMaxTokenSize. + tmp := new(bytes.Buffer) + buf := new(bytes.Buffer) + lineNum := 0 + j := 0 + for i := 0; i < 2*smallMaxTokenSize; i++ { + genLine(tmp, lineNum, j, true) + if j < smallMaxTokenSize { + j++ + } else { + j-- + } + buf.Write(tmp.Bytes()) + lineNum++ + } + s := NewScanner(&slowReader{1, buf}) + s.Split(ScanLines) + s.MaxTokenSize(smallMaxTokenSize) + j = 0 + for lineNum := 0; s.Scan(); lineNum++ { + genLine(tmp, lineNum, j, false) + if j < smallMaxTokenSize { + j++ + } else { + j-- + } + line := tmp.String() // We use the string-valued token here, for variety. + if s.Text() != line { + t.Errorf("%d: bad line: %d %d\n%.100q\n%.100q\n", lineNum, len(s.Bytes()), len(line), s.Text(), line) + } + } + err := s.Err() + if err != nil { + t.Fatal(err) + } +} + +// Test that the line splitter errors out on a long line. +func TestScanLineTooLong(t *testing.T) { + const smallMaxTokenSize = 256 // Much smaller for more efficient testing. + // Build a buffer of lots of line lengths up to but not exceeding smallMaxTokenSize. + tmp := new(bytes.Buffer) + buf := new(bytes.Buffer) + lineNum := 0 + j := 0 + for i := 0; i < 2*smallMaxTokenSize; i++ { + genLine(tmp, lineNum, j, true) + j++ + buf.Write(tmp.Bytes()) + lineNum++ + } + s := NewScanner(&slowReader{3, buf}) + s.Split(ScanLines) + s.MaxTokenSize(smallMaxTokenSize) + j = 0 + for lineNum := 0; s.Scan(); lineNum++ { + genLine(tmp, lineNum, j, false) + if j < smallMaxTokenSize { + j++ + } else { + j-- + } + line := tmp.Bytes() + if !bytes.Equal(s.Bytes(), line) { + t.Errorf("%d: bad line: %d %d\n%.100q\n%.100q\n", lineNum, len(s.Bytes()), len(line), s.Bytes(), line) + } + } + err := s.Err() + if err != ErrTooLong { + t.Fatalf("expected ErrTooLong; got %s", err) + } +} + +// Test that the line splitter handles a final line without a newline. +func testNoNewline(text string, lines []string, t *testing.T) { + buf := bytes.NewBufferString(text) + s := NewScanner(&slowReader{7, buf}) + s.Split(ScanLines) + for lineNum := 0; s.Scan(); lineNum++ { + line := lines[lineNum] + if s.Text() != line { + t.Errorf("%d: bad line: %d %d\n%.100q\n%.100q\n", lineNum, len(s.Bytes()), len(line), s.Bytes(), line) + } + } + err := s.Err() + if err != nil { + t.Fatal(err) + } +} + +var noNewlineLines = []string{ + "abcdefghijklmn\nopqrstuvwxyz", +} + +// Test that the line splitter handles a final line without a newline. +func TestScanLineNoNewline(t *testing.T) { + const text = "abcdefghijklmn\nopqrstuvwxyz" + lines := []string{ + "abcdefghijklmn", + "opqrstuvwxyz", + } + testNoNewline(text, lines, t) +} + +// Test that the line splitter handles a final line with a carriage return but nonewline. +func TestScanLineReturnButNoNewline(t *testing.T) { + const text = "abcdefghijklmn\nopqrstuvwxyz\r" + lines := []string{ + "abcdefghijklmn", + "opqrstuvwxyz", + } + testNoNewline(text, lines, t) +} + +// Test that the line splitter handles a final empty line. +func TestScanLineEmptyFinalLine(t *testing.T) { + const text = "abcdefghijklmn\nopqrstuvwxyz\n\n" + lines := []string{ + "abcdefghijklmn", + "opqrstuvwxyz", + "", + } + testNoNewline(text, lines, t) +} + +// Test that the line splitter handles a final empty line with a carriage return but no newline. +func TestScanLineEmptyFinalLineWithCR(t *testing.T) { + const text = "abcdefghijklmn\nopqrstuvwxyz\n\r" + lines := []string{ + "abcdefghijklmn", + "opqrstuvwxyz", + "", + } + testNoNewline(text, lines, t) +} + +var testError = errors.New("testError") + +// Test the correct error is returned when the split function errors out. +func TestSplitError(t *testing.T) { + // Create a split function that delivers a little data, then a predictable error. + numSplits := 0 + const okCount = 7 + errorSplit := func(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF { + panic("didn't get enough data") + } + if numSplits >= okCount { + return 0, nil, testError + } + numSplits++ + return 1, data[0:1], nil + } + // Read the data. + const text = "abcdefghijklmnopqrstuvwxyz" + buf := bytes.NewBufferString(text) + s := NewScanner(&slowReader{1, buf}) + s.Split(errorSplit) + var i int + for i = 0; s.Scan(); i++ { + if len(s.Bytes()) != 1 || text[i] != s.Bytes()[0] { + t.Errorf("#%d: expected %q got %q", i, text[i], s.Bytes()[0]) + } + } + // Check correct termination location and error. + if i != okCount { + t.Errorf("unexpected termination; expected %d tokens got %d", okCount, i) + } + err := s.Err() + if err != testError { + t.Fatalf("expected %q got %v", testError, err) + } +} + +// Test that an EOF is overridden by a user-generated scan error. +func TestErrAtEOF(t *testing.T) { + s := NewScanner(strings.NewReader("1 2 33")) + // This spitter will fail on last entry, after s.err==EOF. + split := func(data []byte, atEOF bool) (advance int, token []byte, err error) { + advance, token, err = ScanWords(data, atEOF) + if len(token) > 1 { + if s.ErrOrEOF() != io.EOF { + t.Fatal("not testing EOF") + } + err = testError + } + return + } + s.Split(split) + for s.Scan() { + } + if s.Err() != testError { + t.Fatal("wrong error:", s.Err()) + } +} diff --git a/src/pkg/builtin/builtin.go b/src/pkg/builtin/builtin.go index e81616ca4..7b5e9ab1d 100644 --- a/src/pkg/builtin/builtin.go +++ b/src/pkg/builtin/builtin.go @@ -81,9 +81,8 @@ type uintptr uintptr // integer values. type byte byte -// rune is an alias for int and is equivalent to int in all ways. It is +// rune is an alias for int32 and is equivalent to int32 in all ways. It is // used, by convention, to distinguish character values from integer values. -// In a future version of Go, it will change to an alias of int32. type rune rune // Type is here for the purposes of documentation only. It is a stand-in @@ -115,6 +114,8 @@ type ComplexType complex64 // result of append, often in the variable holding the slice itself: // slice = append(slice, elem1, elem2) // slice = append(slice, anotherSlice...) +// As a special case, it is legal to append a string to a byte slice, like this: +// slice = append([]byte("hello "), "world"...) func append(slice []Type, elems ...Type) []Type // The copy built-in function copies elements from a source slice into a @@ -125,8 +126,8 @@ func append(slice []Type, elems ...Type) []Type func copy(dst, src []Type) int // The delete built-in function deletes the element with the specified key -// (m[key]) from the map. If there is no such element, delete is a no-op. -// If m is nil, delete panics. +// (m[key]) from the map. If m is nil or there is no such element, delete +// is a no-op. func delete(m map[Type]Type1, key Type) // The len built-in function returns the length of v, according to its type: diff --git a/src/pkg/bytes/asm_386.s b/src/pkg/bytes/asm_386.s index e7833de0c..c444b55e1 100644 --- a/src/pkg/bytes/asm_386.s +++ b/src/pkg/bytes/asm_386.s @@ -3,31 +3,31 @@ // license that can be found in the LICENSE file. TEXT ·IndexByte(SB),7,$0 - MOVL p+0(FP), SI - MOVL len+4(FP), CX - MOVB b+12(FP), AL + MOVL s+0(FP), SI + MOVL s+4(FP), CX + MOVB c+12(FP), AL MOVL SI, DI CLD; REPN; SCASB JZ 3(PC) - MOVL $-1, ret+16(FP) + MOVL $-1, r+16(FP) RET SUBL SI, DI SUBL $1, DI - MOVL DI, ret+16(FP) + MOVL DI, r+16(FP) RET TEXT ·Equal(SB),7,$0 - MOVL len+4(FP), BX - MOVL len1+16(FP), CX + MOVL a+4(FP), BX + MOVL b+16(FP), CX MOVL $0, AX CMPL BX, CX JNE eqret - MOVL p+0(FP), SI - MOVL q+12(FP), DI + MOVL a+0(FP), SI + MOVL b+12(FP), DI CLD REP; CMPSB JNE eqret MOVL $1, AX eqret: - MOVB AX, ret+24(FP) + MOVB AX, r+24(FP) RET diff --git a/src/pkg/bytes/asm_amd64.s b/src/pkg/bytes/asm_amd64.s index 5caea5c52..482422642 100644 --- a/src/pkg/bytes/asm_amd64.s +++ b/src/pkg/bytes/asm_amd64.s @@ -3,12 +3,12 @@ // license that can be found in the LICENSE file. TEXT ·IndexByte(SB),7,$0 - MOVQ p+0(FP), SI - MOVL len+8(FP), BX - MOVB b+16(FP), AL + MOVQ s+0(FP), SI + MOVQ s+8(FP), BX + MOVB c+24(FP), AL MOVQ SI, DI - CMPL BX, $16 + CMPQ BX, $16 JLT small // round up to first 16-byte boundary @@ -63,15 +63,15 @@ condition: JZ success failure: - MOVL $-1, ret+24(FP) + MOVQ $-1, r+32(FP) RET // handle for lengths < 16 small: - MOVL BX, CX + MOVQ BX, CX REPN; SCASB JZ success - MOVL $-1, ret+24(FP) + MOVQ $-1, r+32(FP) RET // we've found the chunk containing the byte @@ -81,28 +81,28 @@ ssesuccess: BSFW DX, DX SUBQ SI, DI ADDQ DI, DX - MOVL DX, ret+24(FP) + MOVQ DX, r+32(FP) RET success: SUBQ SI, DI SUBL $1, DI - MOVL DI, ret+24(FP) + MOVQ DI, r+32(FP) RET TEXT ·Equal(SB),7,$0 - MOVL len+8(FP), BX - MOVL len1+24(FP), CX + MOVQ a+8(FP), BX + MOVQ b+32(FP), CX MOVL $0, AX - CMPL BX, CX + CMPQ BX, CX JNE eqret - MOVQ p+0(FP), SI - MOVQ q+16(FP), DI + MOVQ a+0(FP), SI + MOVQ b+24(FP), DI CLD REP; CMPSB MOVL $1, DX CMOVLEQ DX, AX eqret: - MOVB AX, ret+32(FP) + MOVB AX, r+48(FP) RET diff --git a/src/pkg/bytes/asm_arm.s b/src/pkg/bytes/asm_arm.s index 4ed0c1580..c7685f041 100644 --- a/src/pkg/bytes/asm_arm.s +++ b/src/pkg/bytes/asm_arm.s @@ -2,10 +2,55 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// no memchr implementation on arm yet TEXT ·IndexByte(SB),7,$0 - B ·indexBytePortable(SB) + MOVW s+0(FP), R0 + MOVW s+4(FP), R1 + MOVBU c+12(FP), R2 // byte to find + MOVW R0, R4 // store base for later + ADD R0, R1 // end + +_loop: + CMP R0, R1 + B.EQ _notfound + MOVBU.P 1(R0), R3 + CMP R2, R3 + B.NE _loop + + SUB $1, R0 // R0 will be one beyond the position we want + SUB R4, R0 // remove base + MOVW R0, r+16(FP) + RET + +_notfound: + MOVW $-1, R0 + MOVW R0, r+16(FP) + RET -// no memcmp implementation on arm yet TEXT ·Equal(SB),7,$0 - B ·equalPortable(SB) + MOVW a+4(FP), R1 + MOVW b+16(FP), R3 + + CMP R1, R3 // unequal lengths are not equal + B.NE _notequal + + MOVW a+0(FP), R0 + MOVW b+12(FP), R2 + ADD R0, R1 // end + +_next: + CMP R0, R1 + B.EQ _equal // reached the end + MOVBU.P 1(R0), R4 + MOVBU.P 1(R2), R5 + CMP R4, R5 + B.EQ _next + +_notequal: + MOVW $0, R0 + MOVBU R0, r+24(FP) + RET + +_equal: + MOVW $1, R0 + MOVBU R0, r+24(FP) + RET diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go index afdf22055..85c157798 100644 --- a/src/pkg/bytes/buffer.go +++ b/src/pkg/bytes/buffer.go @@ -99,6 +99,19 @@ func (b *Buffer) grow(n int) int { return b.off + m } +// Grow grows the buffer's capacity, if necessary, to guarantee space for +// another n bytes. After Grow(n), at least n bytes can be written to the +// buffer without another allocation. +// If n is negative, Grow will panic. +// If the buffer can't grow it will panic with ErrTooLarge. +func (b *Buffer) Grow(n int) { + if n < 0 { + panic("bytes.Buffer.Grow: negative count") + } + m := b.grow(n) + b.buf = b.buf[0:m] +} + // Write appends the contents of p to the buffer. The return // value n is the length of p; err is always nil. // If the buffer becomes too large, Write will panic with @@ -347,16 +360,25 @@ func (b *Buffer) UnreadByte() error { // ReadBytes returns err != nil if and only if the returned data does not end in // delim. func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { + slice, err := b.readSlice(delim) + // return a copy of slice. The buffer's backing array may + // be overwritten by later calls. + line = append(line, slice...) + return +} + +// readSlice is like ReadBytes but returns a reference to internal buffer data. +func (b *Buffer) readSlice(delim byte) (line []byte, err error) { i := IndexByte(b.buf[b.off:], delim) - size := i + 1 + end := b.off + i + 1 if i < 0 { - size = len(b.buf) - b.off + end = len(b.buf) err = io.EOF } - line = make([]byte, size) - copy(line, b.buf[b.off:]) - b.off += size - return + line = b.buf[b.off:end] + b.off = end + b.lastRead = opRead + return line, err } // ReadString reads until the first occurrence of delim in the input, @@ -366,8 +388,8 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { // ReadString returns err != nil if and only if the returned data does not end // in delim. func (b *Buffer) ReadString(delim byte) (line string, err error) { - bytes, err := b.ReadBytes(delim) - return string(bytes), err + slice, err := b.readSlice(delim) + return string(slice), err } // NewBuffer creates and initializes a new Buffer using buf as its initial diff --git a/src/pkg/bytes/buffer_test.go b/src/pkg/bytes/buffer_test.go index d0af11f10..f9fb2625a 100644 --- a/src/pkg/bytes/buffer_test.go +++ b/src/pkg/bytes/buffer_test.go @@ -8,20 +8,21 @@ import ( . "bytes" "io" "math/rand" + "runtime" "testing" "unicode/utf8" ) -const N = 10000 // make this bigger for a larger (and slower) test -var data string // test data for write tests -var bytes []byte // test data; same as data but as a slice. +const N = 10000 // make this bigger for a larger (and slower) test +var data string // test data for write tests +var testBytes []byte // test data; same as data but as a slice. func init() { - bytes = make([]byte, N) + testBytes = make([]byte, N) for i := 0; i < N; i++ { - bytes[i] = 'a' + byte(i%26) + testBytes[i] = 'a' + byte(i%26) } - data = string(bytes) + data = string(testBytes) } // Verify that contents of buf match the string s. @@ -84,7 +85,7 @@ func fillBytes(t *testing.T, testname string, buf *Buffer, s string, n int, fub } func TestNewBuffer(t *testing.T) { - buf := NewBuffer(bytes) + buf := NewBuffer(testBytes) check(t, "NewBuffer", buf, data) } @@ -187,7 +188,7 @@ func TestLargeByteWrites(t *testing.T) { limit = 9 } for i := 3; i < limit; i += 3 { - s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, bytes) + s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, testBytes) empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i)) } check(t, "TestLargeByteWrites (3)", &buf, "") @@ -205,7 +206,7 @@ func TestLargeStringReads(t *testing.T) { func TestLargeByteReads(t *testing.T) { var buf Buffer for i := 3; i < 30; i += 3 { - s := fillBytes(t, "TestLargeReads (1)", &buf, "", 5, bytes[0:len(bytes)/i]) + s := fillBytes(t, "TestLargeReads (1)", &buf, "", 5, testBytes[0:len(testBytes)/i]) empty(t, "TestLargeReads (2)", &buf, s, make([]byte, len(data))) } check(t, "TestLargeByteReads (3)", &buf, "") @@ -219,7 +220,7 @@ func TestMixedReadsAndWrites(t *testing.T) { if i%2 == 0 { s = fillString(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, data[0:wlen]) } else { - s = fillBytes(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, bytes[0:wlen]) + s = fillBytes(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, testBytes[0:wlen]) } rlen := rand.Intn(len(data)) @@ -240,7 +241,7 @@ func TestNil(t *testing.T) { func TestReadFrom(t *testing.T) { var buf Buffer for i := 3; i < 30; i += 3 { - s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, bytes[0:len(bytes)/i]) + s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, testBytes[0:len(testBytes)/i]) var b Buffer b.ReadFrom(&buf) empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data))) @@ -250,16 +251,16 @@ func TestReadFrom(t *testing.T) { func TestWriteTo(t *testing.T) { var buf Buffer for i := 3; i < 30; i += 3 { - s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, bytes[0:len(bytes)/i]) + s := fillBytes(t, "TestWriteTo (1)", &buf, "", 5, testBytes[0:len(testBytes)/i]) var b Buffer buf.WriteTo(&b) - empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data))) + empty(t, "TestWriteTo (2)", &b, s, make([]byte, len(data))) } } func TestRuneIO(t *testing.T) { const NRune = 1000 - // Built a test array while we write the data + // Built a test slice while we write the data b := make([]byte, utf8.UTFMax*NRune) var buf Buffer n := 0 @@ -374,6 +375,72 @@ func TestReadBytes(t *testing.T) { } } +func TestReadString(t *testing.T) { + for _, test := range readBytesTests { + buf := NewBufferString(test.buffer) + var err error + for _, expected := range test.expected { + var s string + s, err = buf.ReadString(test.delim) + if s != expected { + t.Errorf("expected %q, got %q", expected, s) + } + if err != nil { + break + } + } + if err != test.err { + t.Errorf("expected error %v, got %v", test.err, err) + } + } +} + +func BenchmarkReadString(b *testing.B) { + const n = 32 << 10 + + data := make([]byte, n) + data[n-1] = 'x' + b.SetBytes(int64(n)) + for i := 0; i < b.N; i++ { + buf := NewBuffer(data) + _, err := buf.ReadString('x') + if err != nil { + b.Fatal(err) + } + } +} + +func TestGrow(t *testing.T) { + x := []byte{'x'} + y := []byte{'y'} + tmp := make([]byte, 72) + for _, startLen := range []int{0, 100, 1000, 10000, 100000} { + xBytes := Repeat(x, startLen) + for _, growLen := range []int{0, 100, 1000, 10000, 100000} { + buf := NewBuffer(xBytes) + // If we read, this affects buf.off, which is good to test. + readBytes, _ := buf.Read(tmp) + buf.Grow(growLen) + yBytes := Repeat(y, growLen) + // Check no allocation occurs in write, as long as we're single-threaded. + var m1, m2 runtime.MemStats + runtime.ReadMemStats(&m1) + buf.Write(yBytes) + runtime.ReadMemStats(&m2) + if runtime.GOMAXPROCS(-1) == 1 && m1.Mallocs != m2.Mallocs { + t.Errorf("allocation occurred during write") + } + // Check that buffer has correct data. + if !Equal(buf.Bytes()[0:startLen-readBytes], xBytes[readBytes:]) { + t.Errorf("bad initial data at %d %d", startLen, growLen) + } + if !Equal(buf.Bytes()[startLen-readBytes:startLen-readBytes+growLen], yBytes) { + t.Errorf("bad written data at %d %d", startLen, growLen) + } + } + } +} + // Was a bug: used to give EOF reading empty slice at EOF. func TestReadEmptyAtEOF(t *testing.T) { b := new(Buffer) @@ -386,3 +453,25 @@ func TestReadEmptyAtEOF(t *testing.T) { t.Errorf("wrong count; got %d want 0", n) } } + +func TestUnreadByte(t *testing.T) { + b := new(Buffer) + b.WriteString("abcdefghijklmnopqrstuvwxyz") + + _, err := b.ReadBytes('m') + if err != nil { + t.Fatalf("ReadBytes: %v", err) + } + + err = b.UnreadByte() + if err != nil { + t.Fatalf("UnreadByte: %v", err) + } + c, err := b.ReadByte() + if err != nil { + t.Fatalf("ReadByte: %v", err) + } + if c != 'm' { + t.Errorf("ReadByte = %q; want %q", c, 'm') + } +} diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go index 7d1426fb4..3bab65ef9 100644 --- a/src/pkg/bytes/bytes.go +++ b/src/pkg/bytes/bytes.go @@ -11,8 +11,8 @@ import ( "unicode/utf8" ) -// Compare returns an integer comparing the two byte arrays lexicographically. -// The result will be 0 if a==b, -1 if a < b, and +1 if a > b +// Compare returns an integer comparing two byte slices lexicographically. +// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. // A nil argument is equivalent to an empty slice. func Compare(a, b []byte) int { m := len(a) @@ -37,10 +37,6 @@ func Compare(a, b []byte) int { return 0 } -// Equal returns a boolean reporting whether a == b. -// A nil argument is equivalent to an empty slice. -func Equal(a, b []byte) bool - func equalPortable(a, b []byte) bool { if len(a) != len(b) { return false @@ -53,8 +49,8 @@ func equalPortable(a, b []byte) bool { return true } -// explode splits s into an array of UTF-8 sequences, one per Unicode character (still arrays of bytes), -// up to a maximum of n byte arrays. Invalid UTF-8 sequences are chopped into individual bytes. +// explode splits s into a slice of UTF-8 sequences, one per Unicode character (still slices of bytes), +// up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes. func explode(s []byte, n int) [][]byte { if n <= 0 { n = len(s) @@ -226,7 +222,7 @@ func LastIndexAny(s []byte, chars string) int { } // Generic split: splits after each instance of sep, -// including sepSave bytes of sep in the subarrays. +// including sepSave bytes of sep in the subslices. func genSplit(s, sep []byte, sepSave, n int) [][]byte { if n == 0 { return nil @@ -287,15 +283,15 @@ func SplitAfter(s, sep []byte) [][]byte { return genSplit(s, sep, len(sep), -1) } -// Fields splits the array s around each instance of one or more consecutive white space -// characters, returning a slice of subarrays of s or an empty list if s contains only white space. +// Fields splits the slice s around each instance of one or more consecutive white space +// characters, returning a slice of subslices of s or an empty list if s contains only white space. func Fields(s []byte) [][]byte { return FieldsFunc(s, unicode.IsSpace) } // FieldsFunc interprets s as a sequence of UTF-8-encoded Unicode code points. -// It splits the array s at each run of code points c satisfying f(c) and -// returns a slice of subarrays of s. If no code points in s satisfy f(c), an +// It splits the slice s at each run of code points c satisfying f(c) and +// returns a slice of subslices of s. If no code points in s satisfy f(c), an // empty slice is returned. func FieldsFunc(s []byte, f func(rune) bool) [][]byte { n := 0 @@ -333,45 +329,46 @@ func FieldsFunc(s []byte, f func(rune) bool) [][]byte { return a[0:na] } -// Join concatenates the elements of a to create a single byte array. The separator -// sep is placed between elements in the resulting array. -func Join(a [][]byte, sep []byte) []byte { - if len(a) == 0 { +// Join concatenates the elements of s to create a new byte slice. The separator +// sep is placed between elements in the resulting slice. +func Join(s [][]byte, sep []byte) []byte { + if len(s) == 0 { return []byte{} } - if len(a) == 1 { - return a[0] + if len(s) == 1 { + // Just return a copy. + return append([]byte(nil), s[0]...) } - n := len(sep) * (len(a) - 1) - for i := 0; i < len(a); i++ { - n += len(a[i]) + n := len(sep) * (len(s) - 1) + for _, v := range s { + n += len(v) } b := make([]byte, n) - bp := copy(b, a[0]) - for _, s := range a[1:] { + bp := copy(b, s[0]) + for _, v := range s[1:] { bp += copy(b[bp:], sep) - bp += copy(b[bp:], s) + bp += copy(b[bp:], v) } return b } -// HasPrefix tests whether the byte array s begins with prefix. +// HasPrefix tests whether the byte slice s begins with prefix. func HasPrefix(s, prefix []byte) bool { return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix) } -// HasSuffix tests whether the byte array s ends with suffix. +// HasSuffix tests whether the byte slice s ends with suffix. func HasSuffix(s, suffix []byte) bool { return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix) } -// Map returns a copy of the byte array s with all its characters modified +// Map returns a copy of the byte slice s with all its characters modified // according to the mapping function. If mapping returns a negative value, the character is // dropped from the string with no replacement. The characters in s and the // output are interpreted as UTF-8-encoded Unicode code points. func Map(mapping func(r rune) rune, s []byte) []byte { - // In the worst case, the array can grow when mapped, making + // In the worst case, the slice can grow when mapped, making // things unpleasant. But it's so rare we barge in assuming it's // fine. It could also shrink but that falls out naturally. maxbytes := len(s) // length of b @@ -412,28 +409,28 @@ func Repeat(b []byte, count int) []byte { return nb } -// ToUpper returns a copy of the byte array s with all Unicode letters mapped to their upper case. +// ToUpper returns a copy of the byte slice s with all Unicode letters mapped to their upper case. func ToUpper(s []byte) []byte { return Map(unicode.ToUpper, s) } -// ToUpper returns a copy of the byte array s with all Unicode letters mapped to their lower case. +// ToLower returns a copy of the byte slice s with all Unicode letters mapped to their lower case. func ToLower(s []byte) []byte { return Map(unicode.ToLower, s) } -// ToTitle returns a copy of the byte array s with all Unicode letters mapped to their title case. +// ToTitle returns a copy of the byte slice s with all Unicode letters mapped to their title case. func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) } -// ToUpperSpecial returns a copy of the byte array s with all Unicode letters mapped to their +// ToUpperSpecial returns a copy of the byte slice s with all Unicode letters mapped to their // upper case, giving priority to the special casing rules. func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte { return Map(func(r rune) rune { return _case.ToUpper(r) }, s) } -// ToLowerSpecial returns a copy of the byte array s with all Unicode letters mapped to their +// ToLowerSpecial returns a copy of the byte slice s with all Unicode letters mapped to their // lower case, giving priority to the special casing rules. func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte { return Map(func(r rune) rune { return _case.ToLower(r) }, s) } -// ToTitleSpecial returns a copy of the byte array s with all Unicode letters mapped to their +// ToTitleSpecial returns a copy of the byte slice s with all Unicode letters mapped to their // title case, giving priority to the special casing rules. func ToTitleSpecial(_case unicode.SpecialCase, s []byte) []byte { return Map(func(r rune) rune { return _case.ToTitle(r) }, s) @@ -514,6 +511,24 @@ func TrimFunc(s []byte, f func(r rune) bool) []byte { return TrimRightFunc(TrimLeftFunc(s, f), f) } +// TrimPrefix returns s without the provided leading prefix string. +// If s doesn't start with prefix, s is returned unchanged. +func TrimPrefix(s, prefix []byte) []byte { + if HasPrefix(s, prefix) { + return s[len(prefix):] + } + return s +} + +// TrimSuffix returns s without the provided trailing suffix string. +// If s doesn't end with suffix, s is returned unchanged. +func TrimSuffix(s, suffix []byte) []byte { + if HasSuffix(s, suffix) { + return s[:len(s)-len(suffix)] + } + return s +} + // IndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points. // It returns the byte index in s of the first Unicode // code point satisfying f(c), or -1 if none do. @@ -552,7 +567,10 @@ func indexFunc(s []byte, f func(r rune) bool, truth bool) int { // inverted. func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int { for i := len(s); i > 0; { - r, size := utf8.DecodeLastRune(s[0:i]) + r, size := rune(s[i-1]), 1 + if r >= utf8.RuneSelf { + r, size = utf8.DecodeLastRune(s[0:i]) + } i -= size if f(r) == truth { return i @@ -619,10 +637,8 @@ func Replace(s, old, new []byte, n int) []byte { m = Count(s, old) } if m == 0 { - // Nothing to do. Just copy. - t := make([]byte, len(s)) - copy(t, s) - return t + // Just return a copy. + return append([]byte(nil), s...) } if n < 0 || m < n { n = m diff --git a/src/pkg/bytes/bytes_decl.go b/src/pkg/bytes/bytes_decl.go index 5d2b9e639..ce78be416 100644 --- a/src/pkg/bytes/bytes_decl.go +++ b/src/pkg/bytes/bytes_decl.go @@ -4,5 +4,13 @@ package bytes +//go:noescape + // IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s. func IndexByte(s []byte, c byte) int // asm_$GOARCH.s + +//go:noescape + +// Equal returns a boolean reporting whether a == b. +// A nil argument is equivalent to an empty slice. +func Equal(a, b []byte) bool // asm_$GOARCH.s diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go index 000f23517..1d6274c33 100644 --- a/src/pkg/bytes/bytes_test.go +++ b/src/pkg/bytes/bytes_test.go @@ -6,6 +6,7 @@ package bytes_test import ( . "bytes" + "math/rand" "reflect" "testing" "unicode" @@ -24,16 +25,16 @@ func eq(a, b []string) bool { return true } -func arrayOfString(a [][]byte) []string { - result := make([]string, len(a)) - for j := 0; j < len(a); j++ { - result[j] = string(a[j]) +func sliceOfString(s [][]byte) []string { + result := make([]string, len(s)) + for i, v := range s { + result[i] = string(v) } return result } // For ease of reading, the test cases use strings that are converted to byte -// arrays before invoking the functions. +// slices before invoking the functions. var abcd = "abcd" var faces = "☺☻☹" @@ -434,7 +435,7 @@ var explodetests = []ExplodeTest{ func TestExplode(t *testing.T) { for _, tt := range explodetests { a := SplitN([]byte(tt.s), nil, tt.n) - result := arrayOfString(a) + result := sliceOfString(a) if !eq(result, tt.a) { t.Errorf(`Explode("%s", %d) = %v; want %v`, tt.s, tt.n, result, tt.a) continue @@ -472,7 +473,7 @@ var splittests = []SplitTest{ func TestSplit(t *testing.T) { for _, tt := range splittests { a := SplitN([]byte(tt.s), []byte(tt.sep), tt.n) - result := arrayOfString(a) + result := sliceOfString(a) if !eq(result, tt.a) { t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a) continue @@ -490,6 +491,12 @@ func TestSplit(t *testing.T) { t.Errorf("Split disagrees withSplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a) } } + if len(a) > 0 { + in, out := a[0], s + if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] { + t.Errorf("Join(%#v, %q) didn't copy", a, tt.sep) + } + } } } @@ -512,7 +519,7 @@ var splitaftertests = []SplitTest{ func TestSplitAfter(t *testing.T) { for _, tt := range splitaftertests { a := SplitAfterN([]byte(tt.s), []byte(tt.sep), tt.n) - result := arrayOfString(a) + result := sliceOfString(a) if !eq(result, tt.a) { t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a) continue @@ -552,7 +559,7 @@ var fieldstests = []FieldsTest{ func TestFields(t *testing.T) { for _, tt := range fieldstests { a := Fields([]byte(tt.s)) - result := arrayOfString(a) + result := sliceOfString(a) if !eq(result, tt.a) { t.Errorf("Fields(%q) = %v; want %v", tt.s, a, tt.a) continue @@ -561,6 +568,14 @@ func TestFields(t *testing.T) { } func TestFieldsFunc(t *testing.T) { + for _, tt := range fieldstests { + a := FieldsFunc([]byte(tt.s), unicode.IsSpace) + result := sliceOfString(a) + if !eq(result, tt.a) { + t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a) + continue + } + } pred := func(c rune) bool { return c == 'X' } var fieldsFuncTests = []FieldsTest{ {"", []string{}}, @@ -570,15 +585,15 @@ func TestFieldsFunc(t *testing.T) { } for _, tt := range fieldsFuncTests { a := FieldsFunc([]byte(tt.s), pred) - result := arrayOfString(a) + result := sliceOfString(a) if !eq(result, tt.a) { t.Errorf("FieldsFunc(%q) = %v, want %v", tt.s, a, tt.a) } } } -// Test case for any function which accepts and returns a byte array. -// For ease of creation, we write the byte arrays as strings. +// Test case for any function which accepts and returns a byte slice. +// For ease of creation, we write the byte slices as strings. type StringTest struct { in, out string } @@ -779,8 +794,8 @@ func TestRunes(t *testing.T) { } type TrimTest struct { - f string - in, cutset, out string + f string + in, arg, out string } var trimTests = []TrimTest{ @@ -805,12 +820,17 @@ var trimTests = []TrimTest{ {"TrimRight", "", "123", ""}, {"TrimRight", "", "", ""}, {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, + {"TrimPrefix", "aabb", "a", "abb"}, + {"TrimPrefix", "aabb", "b", "aabb"}, + {"TrimSuffix", "aabb", "a", "aabb"}, + {"TrimSuffix", "aabb", "b", "aab"}, } func TestTrim(t *testing.T) { for _, tc := range trimTests { name := tc.f var f func([]byte, string) []byte + var fb func([]byte, []byte) []byte switch name { case "Trim": f = Trim @@ -818,12 +838,21 @@ func TestTrim(t *testing.T) { f = TrimLeft case "TrimRight": f = TrimRight + case "TrimPrefix": + fb = TrimPrefix + case "TrimSuffix": + fb = TrimSuffix default: t.Errorf("Undefined trim function %s", name) } - actual := string(f([]byte(tc.in), tc.cutset)) + var actual string + if f != nil { + actual = string(f([]byte(tc.in), tc.arg)) + } else { + actual = string(fb([]byte(tc.in), []byte(tc.arg))) + } if actual != tc.out { - t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out) + t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out) } } } @@ -1008,3 +1037,46 @@ func TestEqualFold(t *testing.T) { } } } + +var makeFieldsInput = func() []byte { + x := make([]byte, 1<<20) + // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. + for i := range x { + switch rand.Intn(10) { + case 0: + x[i] = ' ' + case 1: + if i > 0 && x[i-1] == 'x' { + copy(x[i-1:], "χ") + break + } + fallthrough + default: + x[i] = 'x' + } + } + return x +} + +var fieldsInput = makeFieldsInput() + +func BenchmarkFields(b *testing.B) { + b.SetBytes(int64(len(fieldsInput))) + for i := 0; i < b.N; i++ { + Fields(fieldsInput) + } +} + +func BenchmarkFieldsFunc(b *testing.B) { + b.SetBytes(int64(len(fieldsInput))) + for i := 0; i < b.N; i++ { + FieldsFunc(fieldsInput, unicode.IsSpace) + } +} + +func BenchmarkTrimSpace(b *testing.B) { + s := []byte(" Some text. \n") + for i := 0; i < b.N; i++ { + TrimSpace(s) + } +} diff --git a/src/pkg/bytes/example_test.go b/src/pkg/bytes/example_test.go index 6fe8cd5a9..ad2dbc69b 100644 --- a/src/pkg/bytes/example_test.go +++ b/src/pkg/bytes/example_test.go @@ -5,24 +5,81 @@ package bytes_test import ( - . "bytes" + "bytes" "encoding/base64" + "fmt" "io" "os" + "sort" ) func ExampleBuffer() { - var b Buffer // A Buffer needs no initialization. + var b bytes.Buffer // A Buffer needs no initialization. b.Write([]byte("Hello ")) - b.Write([]byte("world!")) + fmt.Fprintf(&b, "world!") b.WriteTo(os.Stdout) // Output: Hello world! } func ExampleBuffer_reader() { // A Buffer can turn a string or a []byte into an io.Reader. - buf := NewBufferString("R29waGVycyBydWxlIQ==") + buf := bytes.NewBufferString("R29waGVycyBydWxlIQ==") dec := base64.NewDecoder(base64.StdEncoding, buf) io.Copy(os.Stdout, dec) // Output: Gophers rule! } + +func ExampleCompare() { + // Interpret Compare's result by comparing it to zero. + var a, b []byte + if bytes.Compare(a, b) < 0 { + // a less b + } + if bytes.Compare(a, b) <= 0 { + // a less or equal b + } + if bytes.Compare(a, b) > 0 { + // a greater b + } + if bytes.Compare(a, b) >= 0 { + // a greater or equal b + } + + // Prefer Equal to Compare for equality comparisons. + if bytes.Equal(a, b) { + // a equal b + } + if !bytes.Equal(a, b) { + // a not equal b + } +} + +func ExampleCompare_search() { + // Binary search to find a matching byte slice. + var needle []byte + var haystack [][]byte // Assume sorted + i := sort.Search(len(haystack), func(i int) bool { + // Return haystack[i] >= needle. + return bytes.Compare(haystack[i], needle) >= 0 + }) + if i < len(haystack) && bytes.Equal(haystack[i], needle) { + // Found it! + } +} + +func ExampleTrimSuffix() { + var b = []byte("Hello, goodbye, etc!") + b = bytes.TrimSuffix(b, []byte("goodbye, etc!")) + b = bytes.TrimSuffix(b, []byte("gopher")) + b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...) + os.Stdout.Write(b) + // Output: Hello, world! +} + +func ExampleTrimPrefix() { + var b = []byte("Goodbye,, world!") + b = bytes.TrimPrefix(b, []byte("Goodbye,")) + b = bytes.TrimPrefix(b, []byte("See ya,")) + fmt.Printf("Hello%s", b) + // Output: Hello, world! +} diff --git a/src/pkg/bytes/reader.go b/src/pkg/bytes/reader.go index a062e54ba..77511b945 100644 --- a/src/pkg/bytes/reader.go +++ b/src/pkg/bytes/reader.go @@ -10,7 +10,7 @@ import ( "unicode/utf8" ) -// A Reader implements the io.Reader, io.ReaderAt, io.Seeker, +// A Reader implements the io.Reader, io.ReaderAt, io.WriterTo, io.Seeker, // io.ByteScanner, and io.RuneScanner interfaces by reading from // a byte slice. // Unlike a Buffer, a Reader is read-only and supports seeking. @@ -121,5 +121,24 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) { return abs, nil } +// WriteTo implements the io.WriterTo interface. +func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { + r.prevRune = -1 + if r.i >= len(r.s) { + return 0, nil + } + b := r.s[r.i:] + m, err := w.Write(b) + if m > len(b) { + panic("bytes.Reader.WriteTo: invalid Write count") + } + r.i += m + n = int64(m) + if m != len(b) && err == nil { + err = io.ErrShortWrite + } + return +} + // NewReader returns a new Reader reading from b. func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} } diff --git a/src/pkg/bytes/reader_test.go b/src/pkg/bytes/reader_test.go index 2e4b1f26e..f0a3e26c4 100644 --- a/src/pkg/bytes/reader_test.go +++ b/src/pkg/bytes/reader_test.go @@ -8,6 +8,7 @@ import ( . "bytes" "fmt" "io" + "io/ioutil" "os" "testing" ) @@ -86,3 +87,51 @@ func TestReaderAt(t *testing.T) { } } } + +func TestReaderWriteTo(t *testing.T) { + for i := 0; i < 30; i += 3 { + var l int + if i > 0 { + l = len(data) / i + } + s := data[:l] + r := NewReader(testBytes[:l]) + var b Buffer + n, err := r.WriteTo(&b) + if expect := int64(len(s)); n != expect { + t.Errorf("got %v; want %v", n, expect) + } + if err != nil { + t.Errorf("for length %d: got error = %v; want nil", l, err) + } + if b.String() != s { + t.Errorf("got string %q; want %q", b.String(), s) + } + if r.Len() != 0 { + t.Errorf("reader contains %v bytes; want 0", r.Len()) + } + } +} + +// verify that copying from an empty reader always has the same results, +// regardless of the presence of a WriteTo method. +func TestReaderCopyNothing(t *testing.T) { + type nErr struct { + n int64 + err error + } + type justReader struct { + io.Reader + } + type justWriter struct { + io.Writer + } + discard := justWriter{ioutil.Discard} // hide ReadFrom + + var with, withOut nErr + with.n, with.err = io.Copy(discard, NewReader(nil)) + withOut.n, withOut.err = io.Copy(discard, justReader{NewReader(nil)}) + if with != withOut { + t.Errorf("behavior differs: with = %#v; without: %#v", with, withOut) + } +} diff --git a/src/pkg/compress/bzip2/bit_reader.go b/src/pkg/compress/bzip2/bit_reader.go index b35c69a1c..ab1d60651 100644 --- a/src/pkg/compress/bzip2/bit_reader.go +++ b/src/pkg/compress/bzip2/bit_reader.go @@ -14,21 +14,16 @@ import ( // because the error handling was verbose. Instead, any error is kept and can // be checked afterwards. type bitReader struct { - r byteReader + r io.ByteReader n uint64 bits uint err error } -// bitReader needs to read bytes from an io.Reader. We attempt to convert the -// given io.Reader to this interface and, if it doesn't already fit, we wrap in -// a bufio.Reader. -type byteReader interface { - ReadByte() (byte, error) -} - +// newBitReader returns a new bitReader reading from r. If r is not +// already an io.ByteReader, it will be converted via a bufio.Reader. func newBitReader(r io.Reader) bitReader { - byter, ok := r.(byteReader) + byter, ok := r.(io.ByteReader) if !ok { byter = bufio.NewReader(r) } diff --git a/src/pkg/compress/flate/copy.go b/src/pkg/compress/flate/copy.go new file mode 100644 index 000000000..06e5d2e66 --- /dev/null +++ b/src/pkg/compress/flate/copy.go @@ -0,0 +1,17 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +// forwardCopy is like the built-in copy function except that it always goes +// forward from the start, even if the dst and src overlap. +func forwardCopy(dst, src []byte) int { + if len(src) > len(dst) { + src = src[:len(dst)] + } + for i, x := range src { + dst[i] = x + } + return len(src) +} diff --git a/src/pkg/compress/flate/copy_test.go b/src/pkg/compress/flate/copy_test.go new file mode 100644 index 000000000..a9281d446 --- /dev/null +++ b/src/pkg/compress/flate/copy_test.go @@ -0,0 +1,52 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "testing" +) + +func TestForwardCopy(t *testing.T) { + testCases := []struct { + dst0, dst1 int + src0, src1 int + want string + }{ + {0, 9, 0, 9, "012345678"}, + {0, 5, 4, 9, "45678"}, + {4, 9, 0, 5, "01230"}, + {1, 6, 3, 8, "34567"}, + {3, 8, 1, 6, "12121"}, + {0, 9, 3, 6, "345"}, + {3, 6, 0, 9, "012"}, + {1, 6, 0, 9, "00000"}, + {0, 4, 7, 8, "7"}, + {0, 1, 6, 8, "6"}, + {4, 4, 6, 9, ""}, + {2, 8, 6, 6, ""}, + {0, 0, 0, 0, ""}, + } + for _, tc := range testCases { + b := []byte("0123456789") + dst := b[tc.dst0:tc.dst1] + src := b[tc.src0:tc.src1] + n := forwardCopy(dst, src) + got := string(dst[:n]) + if got != tc.want { + t.Errorf("dst=b[%d:%d], src=b[%d:%d]: got %q, want %q", + tc.dst0, tc.dst1, tc.src0, tc.src1, got, tc.want) + } + // Check that the bytes outside of dst[:n] were not modified. + for i, x := range b { + if i >= tc.dst0 && i < tc.dst0+n { + continue + } + if int(x) != '0'+i { + t.Errorf("dst=b[%d:%d], src=b[%d:%d]: copy overrun at b[%d]: got '%c', want '%c'", + tc.dst0, tc.dst1, tc.src0, tc.src1, i, x, '0'+i) + } + } + } +} diff --git a/src/pkg/compress/flate/deflate.go b/src/pkg/compress/flate/deflate.go index e511b50fd..d357fe361 100644 --- a/src/pkg/compress/flate/deflate.go +++ b/src/pkg/compress/flate/deflate.go @@ -22,7 +22,7 @@ const ( logMaxOffsetSize = 15 // Standard DEFLATE minMatchLength = 3 // The smallest match that the compressor looks for maxMatchLength = 258 // The longest match for the compressor - minOffsetSize = 1 // The shortest offset that makes any sence + minOffsetSize = 1 // The shortest offset that makes any sense // The maximum number of tokens we put into a single flat block, just too // stop things from getting too large. diff --git a/src/pkg/compress/flate/deflate_test.go b/src/pkg/compress/flate/deflate_test.go index f1e6db2ac..8f4e196b4 100644 --- a/src/pkg/compress/flate/deflate_test.go +++ b/src/pkg/compress/flate/deflate_test.go @@ -124,8 +124,7 @@ func (r *sparseReader) Read(b []byte) (n int, err error) { func TestVeryLongSparseChunk(t *testing.T) { if testing.Short() { - t.Logf("skipping sparse chunk during short test") - return + t.Skip("skipping sparse chunk during short test") } w, err := NewWriter(ioutil.Discard, 1) if err != nil { @@ -334,7 +333,7 @@ var deflateInflateStringTests = []deflateInflateStringTest{ { "../testdata/e.txt", "2.718281828...", - [...]int{10013, 5065, 5096, 5115, 5093, 5079, 5079, 5079, 5079, 5079}, + [...]int{100018, 50650, 50960, 51150, 50930, 50790, 50790, 50790, 50790, 50790}, }, { "../testdata/Mark.Twain-Tom.Sawyer.txt", diff --git a/src/pkg/compress/flate/fixedhuff.go b/src/pkg/compress/flate/fixedhuff.go new file mode 100644 index 000000000..41a6b25df --- /dev/null +++ b/src/pkg/compress/flate/fixedhuff.go @@ -0,0 +1,74 @@ +package flate + +// autogenerated by gen.go, DO NOT EDIT + +var fixedHuffmanDecoder = huffmanDecoder{ + 7, + [huffmanNumChunks]uint32{ + 0x1007, 0x0508, 0x0108, 0x1188, 0x1107, 0x0708, 0x0308, 0x0c09, + 0x1087, 0x0608, 0x0208, 0x0a09, 0x0008, 0x0808, 0x0408, 0x0e09, + 0x1047, 0x0588, 0x0188, 0x0909, 0x1147, 0x0788, 0x0388, 0x0d09, + 0x10c7, 0x0688, 0x0288, 0x0b09, 0x0088, 0x0888, 0x0488, 0x0f09, + 0x1027, 0x0548, 0x0148, 0x11c8, 0x1127, 0x0748, 0x0348, 0x0c89, + 0x10a7, 0x0648, 0x0248, 0x0a89, 0x0048, 0x0848, 0x0448, 0x0e89, + 0x1067, 0x05c8, 0x01c8, 0x0989, 0x1167, 0x07c8, 0x03c8, 0x0d89, + 0x10e7, 0x06c8, 0x02c8, 0x0b89, 0x00c8, 0x08c8, 0x04c8, 0x0f89, + 0x1017, 0x0528, 0x0128, 0x11a8, 0x1117, 0x0728, 0x0328, 0x0c49, + 0x1097, 0x0628, 0x0228, 0x0a49, 0x0028, 0x0828, 0x0428, 0x0e49, + 0x1057, 0x05a8, 0x01a8, 0x0949, 0x1157, 0x07a8, 0x03a8, 0x0d49, + 0x10d7, 0x06a8, 0x02a8, 0x0b49, 0x00a8, 0x08a8, 0x04a8, 0x0f49, + 0x1037, 0x0568, 0x0168, 0x11e8, 0x1137, 0x0768, 0x0368, 0x0cc9, + 0x10b7, 0x0668, 0x0268, 0x0ac9, 0x0068, 0x0868, 0x0468, 0x0ec9, + 0x1077, 0x05e8, 0x01e8, 0x09c9, 0x1177, 0x07e8, 0x03e8, 0x0dc9, + 0x10f7, 0x06e8, 0x02e8, 0x0bc9, 0x00e8, 0x08e8, 0x04e8, 0x0fc9, + 0x1007, 0x0518, 0x0118, 0x1198, 0x1107, 0x0718, 0x0318, 0x0c29, + 0x1087, 0x0618, 0x0218, 0x0a29, 0x0018, 0x0818, 0x0418, 0x0e29, + 0x1047, 0x0598, 0x0198, 0x0929, 0x1147, 0x0798, 0x0398, 0x0d29, + 0x10c7, 0x0698, 0x0298, 0x0b29, 0x0098, 0x0898, 0x0498, 0x0f29, + 0x1027, 0x0558, 0x0158, 0x11d8, 0x1127, 0x0758, 0x0358, 0x0ca9, + 0x10a7, 0x0658, 0x0258, 0x0aa9, 0x0058, 0x0858, 0x0458, 0x0ea9, + 0x1067, 0x05d8, 0x01d8, 0x09a9, 0x1167, 0x07d8, 0x03d8, 0x0da9, + 0x10e7, 0x06d8, 0x02d8, 0x0ba9, 0x00d8, 0x08d8, 0x04d8, 0x0fa9, + 0x1017, 0x0538, 0x0138, 0x11b8, 0x1117, 0x0738, 0x0338, 0x0c69, + 0x1097, 0x0638, 0x0238, 0x0a69, 0x0038, 0x0838, 0x0438, 0x0e69, + 0x1057, 0x05b8, 0x01b8, 0x0969, 0x1157, 0x07b8, 0x03b8, 0x0d69, + 0x10d7, 0x06b8, 0x02b8, 0x0b69, 0x00b8, 0x08b8, 0x04b8, 0x0f69, + 0x1037, 0x0578, 0x0178, 0x11f8, 0x1137, 0x0778, 0x0378, 0x0ce9, + 0x10b7, 0x0678, 0x0278, 0x0ae9, 0x0078, 0x0878, 0x0478, 0x0ee9, + 0x1077, 0x05f8, 0x01f8, 0x09e9, 0x1177, 0x07f8, 0x03f8, 0x0de9, + 0x10f7, 0x06f8, 0x02f8, 0x0be9, 0x00f8, 0x08f8, 0x04f8, 0x0fe9, + 0x1007, 0x0508, 0x0108, 0x1188, 0x1107, 0x0708, 0x0308, 0x0c19, + 0x1087, 0x0608, 0x0208, 0x0a19, 0x0008, 0x0808, 0x0408, 0x0e19, + 0x1047, 0x0588, 0x0188, 0x0919, 0x1147, 0x0788, 0x0388, 0x0d19, + 0x10c7, 0x0688, 0x0288, 0x0b19, 0x0088, 0x0888, 0x0488, 0x0f19, + 0x1027, 0x0548, 0x0148, 0x11c8, 0x1127, 0x0748, 0x0348, 0x0c99, + 0x10a7, 0x0648, 0x0248, 0x0a99, 0x0048, 0x0848, 0x0448, 0x0e99, + 0x1067, 0x05c8, 0x01c8, 0x0999, 0x1167, 0x07c8, 0x03c8, 0x0d99, + 0x10e7, 0x06c8, 0x02c8, 0x0b99, 0x00c8, 0x08c8, 0x04c8, 0x0f99, + 0x1017, 0x0528, 0x0128, 0x11a8, 0x1117, 0x0728, 0x0328, 0x0c59, + 0x1097, 0x0628, 0x0228, 0x0a59, 0x0028, 0x0828, 0x0428, 0x0e59, + 0x1057, 0x05a8, 0x01a8, 0x0959, 0x1157, 0x07a8, 0x03a8, 0x0d59, + 0x10d7, 0x06a8, 0x02a8, 0x0b59, 0x00a8, 0x08a8, 0x04a8, 0x0f59, + 0x1037, 0x0568, 0x0168, 0x11e8, 0x1137, 0x0768, 0x0368, 0x0cd9, + 0x10b7, 0x0668, 0x0268, 0x0ad9, 0x0068, 0x0868, 0x0468, 0x0ed9, + 0x1077, 0x05e8, 0x01e8, 0x09d9, 0x1177, 0x07e8, 0x03e8, 0x0dd9, + 0x10f7, 0x06e8, 0x02e8, 0x0bd9, 0x00e8, 0x08e8, 0x04e8, 0x0fd9, + 0x1007, 0x0518, 0x0118, 0x1198, 0x1107, 0x0718, 0x0318, 0x0c39, + 0x1087, 0x0618, 0x0218, 0x0a39, 0x0018, 0x0818, 0x0418, 0x0e39, + 0x1047, 0x0598, 0x0198, 0x0939, 0x1147, 0x0798, 0x0398, 0x0d39, + 0x10c7, 0x0698, 0x0298, 0x0b39, 0x0098, 0x0898, 0x0498, 0x0f39, + 0x1027, 0x0558, 0x0158, 0x11d8, 0x1127, 0x0758, 0x0358, 0x0cb9, + 0x10a7, 0x0658, 0x0258, 0x0ab9, 0x0058, 0x0858, 0x0458, 0x0eb9, + 0x1067, 0x05d8, 0x01d8, 0x09b9, 0x1167, 0x07d8, 0x03d8, 0x0db9, + 0x10e7, 0x06d8, 0x02d8, 0x0bb9, 0x00d8, 0x08d8, 0x04d8, 0x0fb9, + 0x1017, 0x0538, 0x0138, 0x11b8, 0x1117, 0x0738, 0x0338, 0x0c79, + 0x1097, 0x0638, 0x0238, 0x0a79, 0x0038, 0x0838, 0x0438, 0x0e79, + 0x1057, 0x05b8, 0x01b8, 0x0979, 0x1157, 0x07b8, 0x03b8, 0x0d79, + 0x10d7, 0x06b8, 0x02b8, 0x0b79, 0x00b8, 0x08b8, 0x04b8, 0x0f79, + 0x1037, 0x0578, 0x0178, 0x11f8, 0x1137, 0x0778, 0x0378, 0x0cf9, + 0x10b7, 0x0678, 0x0278, 0x0af9, 0x0078, 0x0878, 0x0478, 0x0ef9, + 0x1077, 0x05f8, 0x01f8, 0x09f9, 0x1177, 0x07f8, 0x03f8, 0x0df9, + 0x10f7, 0x06f8, 0x02f8, 0x0bf9, 0x00f8, 0x08f8, 0x04f8, 0x0ff9, + }, + nil, 0, +} diff --git a/src/pkg/compress/flate/flate_test.go b/src/pkg/compress/flate/flate_test.go index 94efc90ac..aba820a1f 100644 --- a/src/pkg/compress/flate/flate_test.go +++ b/src/pkg/compress/flate/flate_test.go @@ -10,122 +10,9 @@ package flate import ( "bytes" - "reflect" "testing" ) -// The Huffman code lengths used by the fixed-format Huffman blocks. -var fixedHuffmanBits = [...]int{ - // 0-143 length 8 - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - - // 144-255 length 9 - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - - // 256-279 length 7 - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - - // 280-287 length 8 - 8, 8, 8, 8, 8, 8, 8, 8, -} - -type InitDecoderTest struct { - in []int - out huffmanDecoder - ok bool -} - -var initDecoderTests = []*InitDecoderTest{ - // Example from Connell 1973, - { - []int{3, 5, 2, 4, 3, 5, 5, 4, 4, 3, 4, 5}, - huffmanDecoder{ - 2, 5, - [maxCodeLen + 1]int{2: 0, 4, 13, 31}, - [maxCodeLen + 1]int{2: 0, 1, 6, 20}, - // Paper used different code assignment: - // 2, 9, 4, 0, 10, 8, 3, 7, 1, 5, 11, 6 - // Reordered here so that codes of same length - // are assigned to increasing numbers. - []int{2, 0, 4, 9, 3, 7, 8, 10, 1, 5, 6, 11}, - }, - true, - }, - - // Example from RFC 1951 section 3.2.2 - { - []int{2, 1, 3, 3}, - huffmanDecoder{ - 1, 3, - [maxCodeLen + 1]int{1: 0, 2, 7}, - [maxCodeLen + 1]int{1: 0, 1, 4}, - []int{1, 0, 2, 3}, - }, - true, - }, - - // Second example from RFC 1951 section 3.2.2 - { - []int{3, 3, 3, 3, 3, 2, 4, 4}, - huffmanDecoder{ - 2, 4, - [maxCodeLen + 1]int{2: 0, 6, 15}, - [maxCodeLen + 1]int{2: 0, 1, 8}, - []int{5, 0, 1, 2, 3, 4, 6, 7}, - }, - true, - }, - - // Static Huffman codes (RFC 1951 section 3.2.6) - { - fixedHuffmanBits[0:], - fixedHuffmanDecoder, - true, - }, - - // Illegal input. - { - []int{}, - huffmanDecoder{}, - false, - }, - - // Illegal input. - { - []int{0, 0, 0, 0, 0, 0, 0}, - huffmanDecoder{}, - false, - }, -} - -func TestInitDecoder(t *testing.T) { - for i, tt := range initDecoderTests { - var h huffmanDecoder - if h.init(tt.in) != tt.ok { - t.Errorf("test %d: init = %v", i, !tt.ok) - continue - } - if !reflect.DeepEqual(&h, &tt.out) { - t.Errorf("test %d:\nhave %v\nwant %v", i, h, tt.out) - } - } -} - func TestUncompressedSource(t *testing.T) { decoder := NewReader(bytes.NewBuffer([]byte{0x01, 0x01, 0x00, 0xfe, 0xff, 0x11})) output := make([]byte, 1) diff --git a/src/pkg/compress/flate/gen.go b/src/pkg/compress/flate/gen.go new file mode 100644 index 000000000..1427557f8 --- /dev/null +++ b/src/pkg/compress/flate/gen.go @@ -0,0 +1,165 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// This program generates fixedhuff.go +// Invoke as +// +// go run gen.go |gofmt >fixedhuff.go + +package main + +import ( + "fmt" +) + +const maxCodeLen = 16 + +// Note: the definition of the huffmanDecoder struct is copied from +// inflate.go, as it is private to the implementation. + +// chunk & 15 is number of bits +// chunk >> 4 is value, including table link + +const ( + huffmanChunkBits = 9 + huffmanNumChunks = 1 << huffmanChunkBits + huffmanCountMask = 15 + huffmanValueShift = 4 +) + +type huffmanDecoder struct { + min int // the minimum code length + chunks [huffmanNumChunks]uint32 // chunks as described above + links [][]uint32 // overflow links + linkMask uint32 // mask the width of the link table +} + +// Initialize Huffman decoding tables from array of code lengths. +func (h *huffmanDecoder) init(bits []int) bool { + // Count number of codes of each length, + // compute min and max length. + var count [maxCodeLen]int + var min, max int + for _, n := range bits { + if n == 0 { + continue + } + if min == 0 || n < min { + min = n + } + if n > max { + max = n + } + count[n]++ + } + if max == 0 { + return false + } + + h.min = min + var linkBits uint + var numLinks int + if max > huffmanChunkBits { + linkBits = uint(max) - huffmanChunkBits + numLinks = 1 << linkBits + h.linkMask = uint32(numLinks - 1) + } + code := 0 + var nextcode [maxCodeLen]int + for i := min; i <= max; i++ { + if i == huffmanChunkBits+1 { + // create link tables + link := code >> 1 + h.links = make([][]uint32, huffmanNumChunks-link) + for j := uint(link); j < huffmanNumChunks; j++ { + reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8 + reverse >>= uint(16 - huffmanChunkBits) + off := j - uint(link) + h.chunks[reverse] = uint32(off<>8]) | int(reverseByte[code&0xff])<<8 + reverse >>= uint(16 - n) + if n <= huffmanChunkBits { + for off := reverse; off < huffmanNumChunks; off += 1 << uint(n) { + h.chunks[off] = chunk + } + } else { + linktab := h.links[h.chunks[reverse&(huffmanNumChunks-1)]>>huffmanValueShift] + reverse >>= huffmanChunkBits + for off := reverse; off < numLinks; off += 1 << uint(n-huffmanChunkBits) { + linktab[off] = chunk + } + } + } + return true +} + +func main() { + var h huffmanDecoder + var bits [288]int + initReverseByte() + for i := 0; i < 144; i++ { + bits[i] = 8 + } + for i := 144; i < 256; i++ { + bits[i] = 9 + } + for i := 256; i < 280; i++ { + bits[i] = 7 + } + for i := 280; i < 288; i++ { + bits[i] = 8 + } + h.init(bits[:]) + fmt.Println("package flate") + fmt.Println() + fmt.Println("// autogenerated by gen.go, DO NOT EDIT") + fmt.Println() + fmt.Println("var fixedHuffmanDecoder = huffmanDecoder{") + fmt.Printf("\t%d,\n", h.min) + fmt.Println("\t[huffmanNumChunks]uint32{") + for i := 0; i < huffmanNumChunks; i++ { + if i&7 == 0 { + fmt.Printf("\t\t") + } else { + fmt.Printf(" ") + } + fmt.Printf("0x%04x,", h.chunks[i]) + if i&7 == 7 { + fmt.Println() + } + } + fmt.Println("\t},") + fmt.Println("\tnil, 0,") + fmt.Println("}") +} + +var reverseByte [256]byte + +func initReverseByte() { + for x := 0; x < 256; x++ { + var result byte + for i := uint(0); i < 8; i++ { + result |= byte(((x >> i) & 1) << (7 - i)) + } + reverseByte[x] = result + } +} diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go index 3f2042bfe..a8d646019 100644 --- a/src/pkg/compress/flate/inflate.go +++ b/src/pkg/compress/flate/inflate.go @@ -16,9 +16,10 @@ import ( const ( maxCodeLen = 16 // max length of Huffman code maxHist = 32768 // max history required - maxLit = 286 - maxDist = 32 - numCodes = 19 // number of codes in Huffman meta-code + // The next three numbers come from the RFC, section 3.2.7. + maxLit = 286 + maxDist = 32 + numCodes = 19 // number of codes in Huffman meta-code ) // A CorruptInputError reports the presence of corrupt input at a given offset. @@ -53,32 +54,46 @@ func (e *WriteError) Error() string { return "flate: write error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error() } -// Huffman decoder is based on -// J. Brian Connell, ``A Huffman-Shannon-Fano Code,'' -// Proceedings of the IEEE, 61(7) (July 1973), pp 1046-1047. -type huffmanDecoder struct { - // min, max code length - min, max int +// Note that much of the implemenation of huffmanDecoder is also copied +// into gen.go (in package main) for the purpose of precomputing the +// fixed huffman tables so they can be included statically. + +// The data structure for decoding Huffman tables is based on that of +// zlib. There is a lookup table of a fixed bit width (huffmanChunkBits), +// For codes smaller than the table width, there are multiple entries +// (each combination of trailing bits has the same value). For codes +// larger than the table width, the table contains a link to an overflow +// table. The width of each entry in the link table is the maximum code +// size minus the chunk width. + +// Note that you can do a lookup in the table even without all bits +// filled. Since the extra bits are zero, and the DEFLATE Huffman codes +// have the property that shorter codes come before longer ones, the +// bit length estimate in the result is a lower bound on the actual +// number of bits. - // limit[i] = largest code word of length i - // Given code v of length n, - // need more bits if v > limit[n]. - limit [maxCodeLen + 1]int +// chunk & 15 is number of bits +// chunk >> 4 is value, including table link - // base[i] = smallest code word of length i - seq number - base [maxCodeLen + 1]int +const ( + huffmanChunkBits = 9 + huffmanNumChunks = 1 << huffmanChunkBits + huffmanCountMask = 15 + huffmanValueShift = 4 +) - // codes[seq number] = output code. - // Given code v of length n, value is - // codes[v - base[n]]. - codes []int +type huffmanDecoder struct { + min int // the minimum code length + chunks [huffmanNumChunks]uint32 // chunks as described above + links [][]uint32 // overflow links + linkMask uint32 // mask the width of the link table } // Initialize Huffman decoding tables from array of code lengths. func (h *huffmanDecoder) init(bits []int) bool { // Count number of codes of each length, // compute min and max length. - var count [maxCodeLen + 1]int + var count [maxCodeLen]int var min, max int for _, n := range bits { if n == 0 { @@ -97,93 +112,58 @@ func (h *huffmanDecoder) init(bits []int) bool { } h.min = min - h.max = max - - // For each code range, compute - // nextcode (first code of that length), - // limit (last code of that length), and - // base (offset from first code to sequence number). + var linkBits uint + var numLinks int + if max > huffmanChunkBits { + linkBits = uint(max) - huffmanChunkBits + numLinks = 1 << linkBits + h.linkMask = uint32(numLinks - 1) + } code := 0 - seq := 0 var nextcode [maxCodeLen]int for i := min; i <= max; i++ { + if i == huffmanChunkBits+1 { + // create link tables + link := code >> 1 + h.links = make([][]uint32, huffmanNumChunks-link) + for j := uint(link); j < huffmanNumChunks; j++ { + reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8 + reverse >>= uint(16 - huffmanChunkBits) + off := j - uint(link) + h.chunks[reverse] = uint32(off<>8]) | int(reverseByte[code&0xff])<<8 + reverse >>= uint(16 - n) + if n <= huffmanChunkBits { + for off := reverse; off < huffmanNumChunks; off += 1 << uint(n) { + h.chunks[off] = chunk + } + } else { + linktab := h.links[h.chunks[reverse&(huffmanNumChunks-1)]>>huffmanValueShift] + reverse >>= huffmanChunkBits + for off := reverse; off < numLinks; off += 1 << uint(n-huffmanChunkBits) { + linktab[off] = chunk + } + } } return true } -// Hard-coded Huffman tables for DEFLATE algorithm. -// See RFC 1951, section 3.2.6. -var fixedHuffmanDecoder = huffmanDecoder{ - 7, 9, - [maxCodeLen + 1]int{7: 23, 199, 511}, - [maxCodeLen + 1]int{7: 0, 24, 224}, - []int{ - // length 7: 256-279 - 256, 257, 258, 259, 260, 261, 262, - 263, 264, 265, 266, 267, 268, 269, - 270, 271, 272, 273, 274, 275, 276, - 277, 278, 279, - - // length 8: 0-143 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, - 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, - 92, 93, 94, 95, 96, 97, 98, 99, 100, - 101, 102, 103, 104, 105, 106, 107, 108, - 109, 110, 111, 112, 113, 114, 115, 116, - 117, 118, 119, 120, 121, 122, 123, 124, - 125, 126, 127, 128, 129, 130, 131, 132, - 133, 134, 135, 136, 137, 138, 139, 140, - 141, 142, 143, - - // length 8: 280-287 - 280, 281, 282, 283, 284, 285, 286, 287, - - // length 9: 144-255 - 144, 145, 146, 147, 148, 149, 150, 151, - 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, - 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, - 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 215, - 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, - 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 252, 253, 254, 255, - }, -} - // The actual read interface needed by NewReader. // If the passed in io.Reader does not also have ReadByte, // the NewReader will introduce its own buffering. @@ -207,11 +187,11 @@ type decompressor struct { h1, h2 huffmanDecoder // Length arrays used to define Huffman codes. - bits [maxLit + maxDist]int - codebits [numCodes]int + bits *[maxLit + maxDist]int + codebits *[numCodes]int // Output history, buffer. - hist [maxHist]byte + hist *[maxHist]byte hp int // current output position in buffer hw int // have written hist[0:hw] already hfull bool // buffer has filled at least once @@ -306,10 +286,15 @@ func (f *decompressor) readHuffman() error { } } nlit := int(f.b&0x1F) + 257 + if nlit > maxLit { + return CorruptInputError(f.roffset) + } f.b >>= 5 ndist := int(f.b&0x1F) + 1 + // maxDist is 32, so ndist is always valid. f.b >>= 5 nclen := int(f.b&0xF) + 4 + // numCodes is 19, so nclen is always valid. f.b >>= 4 f.nb -= 5 + 5 + 4 @@ -505,51 +490,49 @@ func (f *decompressor) huffmanBlock() { return } - p := f.hp - dist - if p < 0 { - p += len(f.hist) - } - for i := 0; i < length; i++ { - f.hist[f.hp] = f.hist[p] - f.hp++ - p++ - if f.hp == len(f.hist) { - // After flush continue copying out of history. - f.copyLen = length - (i + 1) - f.copyDist = dist - f.flush((*decompressor).copyHuff) - return - } - if p == len(f.hist) { - p = 0 - } + f.copyLen, f.copyDist = length, dist + if f.copyHist() { + return } } panic("unreached") } -func (f *decompressor) copyHuff() { - length := f.copyLen - dist := f.copyDist - p := f.hp - dist +// copyHist copies f.copyLen bytes from f.hist (f.copyDist bytes ago) to itself. +// It reports whether the f.hist buffer is full. +func (f *decompressor) copyHist() bool { + p := f.hp - f.copyDist if p < 0 { p += len(f.hist) } - for i := 0; i < length; i++ { - f.hist[f.hp] = f.hist[p] - f.hp++ - p++ + for f.copyLen > 0 { + n := f.copyLen + if x := len(f.hist) - f.hp; n > x { + n = x + } + if x := len(f.hist) - p; n > x { + n = x + } + forwardCopy(f.hist[f.hp:f.hp+n], f.hist[p:p+n]) + p += n + f.hp += n + f.copyLen -= n if f.hp == len(f.hist) { - f.copyLen = length - (i + 1) + // After flush continue copying out of history. f.flush((*decompressor).copyHuff) - return + return true } if p == len(f.hist) { p = 0 } } + return false +} - // Continue processing Huffman block. +func (f *decompressor) copyHuff() { + if f.copyHist() { + return + } f.huffmanBlock() } @@ -584,9 +567,9 @@ func (f *decompressor) dataBlock() { f.copyData() } +// copyData copies f.copyLen bytes from the underlying reader into f.hist. +// It pauses for reads when f.hist is full. func (f *decompressor) copyData() { - // Read f.dataLen bytes into history, - // pausing for reads as history fills. n := f.copyLen for n > 0 { m := len(f.hist) - f.hp @@ -640,23 +623,23 @@ func (f *decompressor) moreBits() error { // Read the next Huffman-encoded symbol from f according to h. func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) { - for n := uint(h.min); n <= uint(h.max); n++ { - lim := h.limit[n] - if lim == -1 { - continue - } + n := uint(h.min) + for { for f.nb < n { if err := f.moreBits(); err != nil { return 0, err } } - v := int(f.b & uint32(1<>8]) | int(reverseByte[v&0xFF])<<8 // reverse bits - if v <= lim { + chunk := h.chunks[f.b&(huffmanNumChunks-1)] + n = uint(chunk & huffmanCountMask) + if n > huffmanChunkBits { + chunk = h.links[chunk>>huffmanValueShift][(f.b>>huffmanChunkBits)&h.linkMask] + n = uint(chunk & huffmanCountMask) + } + if n <= f.nb { f.b >>= n f.nb -= n - return h.codes[v-h.base[n]], nil + return int(chunk >> huffmanValueShift), nil } } return 0, CorruptInputError(f.roffset) @@ -688,7 +671,10 @@ func makeReader(r io.Reader) Reader { // finished reading. func NewReader(r io.Reader) io.ReadCloser { var f decompressor + f.bits = new([maxLit + maxDist]int) + f.codebits = new([numCodes]int) f.r = makeReader(r) + f.hist = new([maxHist]byte) f.step = (*decompressor).nextBlock return &f } @@ -700,8 +686,11 @@ func NewReader(r io.Reader) io.ReadCloser { // to read data compressed by NewWriterDict. func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser { var f decompressor - f.setDict(dict) f.r = makeReader(r) + f.hist = new([maxHist]byte) + f.bits = new([maxLit + maxDist]int) + f.codebits = new([numCodes]int) f.step = (*decompressor).nextBlock + f.setDict(dict) return &f } diff --git a/src/pkg/compress/flate/reader_test.go b/src/pkg/compress/flate/reader_test.go new file mode 100644 index 000000000..54ed788db --- /dev/null +++ b/src/pkg/compress/flate/reader_test.go @@ -0,0 +1,95 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "bytes" + "io" + "io/ioutil" + "runtime" + "strings" + "testing" +) + +func TestNlitOutOfRange(t *testing.T) { + // Trying to decode this bogus flate data, which has a Huffman table + // with nlit=288, should not panic. + io.Copy(ioutil.Discard, NewReader(strings.NewReader( + "\xfc\xfe\x36\xe7\x5e\x1c\xef\xb3\x55\x58\x77\xb6\x56\xb5\x43\xf4"+ + "\x6f\xf2\xd2\xe6\x3d\x99\xa0\x85\x8c\x48\xeb\xf8\xda\x83\x04\x2a"+ + "\x75\xc4\xf8\x0f\x12\x11\xb9\xb4\x4b\x09\xa0\xbe\x8b\x91\x4c"))) +} + +const ( + digits = iota + twain +) + +var testfiles = []string{ + // Digits is the digits of the irrational number e. Its decimal representation + // does not repeat, but there are only 10 posible digits, so it should be + // reasonably compressible. + digits: "../testdata/e.txt", + // Twain is Project Gutenberg's edition of Mark Twain's classic English novel. + twain: "../testdata/Mark.Twain-Tom.Sawyer.txt", +} + +func benchmarkDecode(b *testing.B, testfile, level, n int) { + b.StopTimer() + b.SetBytes(int64(n)) + buf0, err := ioutil.ReadFile(testfiles[testfile]) + if err != nil { + b.Fatal(err) + } + if len(buf0) == 0 { + b.Fatalf("test file %q has no data", testfiles[testfile]) + } + compressed := new(bytes.Buffer) + w, err := NewWriter(compressed, level) + if err != nil { + b.Fatal(err) + } + for i := 0; i < n; i += len(buf0) { + if len(buf0) > n-i { + buf0 = buf0[:n-i] + } + io.Copy(w, bytes.NewBuffer(buf0)) + } + w.Close() + buf1 := compressed.Bytes() + buf0, compressed, w = nil, nil, nil + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1))) + } +} + +// These short names are so that gofmt doesn't break the BenchmarkXxx function +// bodies below over multiple lines. +const ( + speed = BestSpeed + default_ = DefaultCompression + compress = BestCompression +) + +func BenchmarkDecodeDigitsSpeed1e4(b *testing.B) { benchmarkDecode(b, digits, speed, 1e4) } +func BenchmarkDecodeDigitsSpeed1e5(b *testing.B) { benchmarkDecode(b, digits, speed, 1e5) } +func BenchmarkDecodeDigitsSpeed1e6(b *testing.B) { benchmarkDecode(b, digits, speed, 1e6) } +func BenchmarkDecodeDigitsDefault1e4(b *testing.B) { benchmarkDecode(b, digits, default_, 1e4) } +func BenchmarkDecodeDigitsDefault1e5(b *testing.B) { benchmarkDecode(b, digits, default_, 1e5) } +func BenchmarkDecodeDigitsDefault1e6(b *testing.B) { benchmarkDecode(b, digits, default_, 1e6) } +func BenchmarkDecodeDigitsCompress1e4(b *testing.B) { benchmarkDecode(b, digits, compress, 1e4) } +func BenchmarkDecodeDigitsCompress1e5(b *testing.B) { benchmarkDecode(b, digits, compress, 1e5) } +func BenchmarkDecodeDigitsCompress1e6(b *testing.B) { benchmarkDecode(b, digits, compress, 1e6) } +func BenchmarkDecodeTwainSpeed1e4(b *testing.B) { benchmarkDecode(b, twain, speed, 1e4) } +func BenchmarkDecodeTwainSpeed1e5(b *testing.B) { benchmarkDecode(b, twain, speed, 1e5) } +func BenchmarkDecodeTwainSpeed1e6(b *testing.B) { benchmarkDecode(b, twain, speed, 1e6) } +func BenchmarkDecodeTwainDefault1e4(b *testing.B) { benchmarkDecode(b, twain, default_, 1e4) } +func BenchmarkDecodeTwainDefault1e5(b *testing.B) { benchmarkDecode(b, twain, default_, 1e5) } +func BenchmarkDecodeTwainDefault1e6(b *testing.B) { benchmarkDecode(b, twain, default_, 1e6) } +func BenchmarkDecodeTwainCompress1e4(b *testing.B) { benchmarkDecode(b, twain, compress, 1e4) } +func BenchmarkDecodeTwainCompress1e5(b *testing.B) { benchmarkDecode(b, twain, compress, 1e5) } +func BenchmarkDecodeTwainCompress1e6(b *testing.B) { benchmarkDecode(b, twain, compress, 1e6) } diff --git a/src/pkg/compress/flate/writer_test.go b/src/pkg/compress/flate/writer_test.go new file mode 100644 index 000000000..58431774e --- /dev/null +++ b/src/pkg/compress/flate/writer_test.go @@ -0,0 +1,60 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flate + +import ( + "io/ioutil" + "runtime" + "testing" +) + +func benchmarkEncoder(b *testing.B, testfile, level, n int) { + b.StopTimer() + b.SetBytes(int64(n)) + buf0, err := ioutil.ReadFile(testfiles[testfile]) + if err != nil { + b.Fatal(err) + } + if len(buf0) == 0 { + b.Fatalf("test file %q has no data", testfiles[testfile]) + } + buf1 := make([]byte, n) + for i := 0; i < n; i += len(buf0) { + if len(buf0) > n-i { + buf0 = buf0[:n-i] + } + copy(buf1[i:], buf0) + } + buf0 = nil + runtime.GC() + b.StartTimer() + for i := 0; i < b.N; i++ { + w, err := NewWriter(ioutil.Discard, level) + if err != nil { + b.Fatal(err) + } + w.Write(buf1) + w.Close() + } +} + +func BenchmarkEncodeDigitsSpeed1e4(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e4) } +func BenchmarkEncodeDigitsSpeed1e5(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e5) } +func BenchmarkEncodeDigitsSpeed1e6(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e6) } +func BenchmarkEncodeDigitsDefault1e4(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e4) } +func BenchmarkEncodeDigitsDefault1e5(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e5) } +func BenchmarkEncodeDigitsDefault1e6(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e6) } +func BenchmarkEncodeDigitsCompress1e4(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e4) } +func BenchmarkEncodeDigitsCompress1e5(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e5) } +func BenchmarkEncodeDigitsCompress1e6(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e6) } +func BenchmarkEncodeTwainSpeed1e4(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e4) } +func BenchmarkEncodeTwainSpeed1e5(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e5) } +func BenchmarkEncodeTwainSpeed1e6(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e6) } +func BenchmarkEncodeTwainDefault1e4(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e4) } +func BenchmarkEncodeTwainDefault1e5(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e5) } +func BenchmarkEncodeTwainDefault1e6(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e6) } +func BenchmarkEncodeTwainCompress1e4(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e4) } +func BenchmarkEncodeTwainCompress1e5(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e5) } +func BenchmarkEncodeTwainCompress1e6(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e6) } diff --git a/src/pkg/compress/lzw/reader_test.go b/src/pkg/compress/lzw/reader_test.go index e5be12f54..6f155b1bd 100644 --- a/src/pkg/compress/lzw/reader_test.go +++ b/src/pkg/compress/lzw/reader_test.go @@ -114,11 +114,19 @@ func TestReader(t *testing.T) { func benchmarkDecoder(b *testing.B, n int) { b.StopTimer() b.SetBytes(int64(n)) - buf0, _ := ioutil.ReadFile("../testdata/e.txt") - buf0 = buf0[:10000] + buf0, err := ioutil.ReadFile("../testdata/e.txt") + if err != nil { + b.Fatal(err) + } + if len(buf0) == 0 { + b.Fatalf("test file has no data") + } compressed := new(bytes.Buffer) w := NewWriter(compressed, LSB, 8) for i := 0; i < n; i += len(buf0) { + if len(buf0) > n-i { + buf0 = buf0[:n-i] + } io.Copy(w, bytes.NewBuffer(buf0)) } w.Close() diff --git a/src/pkg/compress/lzw/writer.go b/src/pkg/compress/lzw/writer.go index 488ba6428..b20691864 100644 --- a/src/pkg/compress/lzw/writer.go +++ b/src/pkg/compress/lzw/writer.go @@ -13,7 +13,7 @@ import ( // A writer is a buffered, flushable writer. type writer interface { - WriteByte(byte) error + io.ByteWriter Flush() error } @@ -131,13 +131,14 @@ func (e *encoder) incHi() error { } // Write writes a compressed representation of p to e's underlying writer. -func (e *encoder) Write(p []byte) (int, error) { +func (e *encoder) Write(p []byte) (n int, err error) { if e.err != nil { return 0, e.err } if len(p) == 0 { return 0, nil } + n = len(p) litMask := uint32(1< e.hi into the map that e.table represents. @@ -184,7 +185,7 @@ loop: } } e.savedCode = code - return len(p), nil + return n, nil } // Close closes the encoder, flushing any pending output. It does not close or diff --git a/src/pkg/compress/lzw/writer_test.go b/src/pkg/compress/lzw/writer_test.go index d249a09b2..3e4e6de21 100644 --- a/src/pkg/compress/lzw/writer_test.go +++ b/src/pkg/compress/lzw/writer_test.go @@ -96,13 +96,29 @@ func TestWriter(t *testing.T) { } } +func TestWriterReturnValues(t *testing.T) { + w := NewWriter(ioutil.Discard, LSB, 8) + n, err := w.Write([]byte("asdf")) + if n != 4 || err != nil { + t.Errorf("got %d, %v, want 4, nil", n, err) + } +} + func benchmarkEncoder(b *testing.B, n int) { b.StopTimer() b.SetBytes(int64(n)) - buf0, _ := ioutil.ReadFile("../testdata/e.txt") - buf0 = buf0[:10000] + buf0, err := ioutil.ReadFile("../testdata/e.txt") + if err != nil { + b.Fatal(err) + } + if len(buf0) == 0 { + b.Fatalf("test file has no data") + } buf1 := make([]byte, n) for i := 0; i < n; i += len(buf0) { + if len(buf0) > n-i { + buf0 = buf0[:n-i] + } copy(buf1[i:], buf0) } buf0 = nil diff --git a/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt b/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt index 8d0ff4e65..c97da7ecc 100644 --- a/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt +++ b/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt @@ -1,4 +1,4 @@ -The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete +The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete by Mark Twain (Samuel Clemens) This eBook is for the use of anyone anywhere at no cost and with diff --git a/src/pkg/compress/testdata/e.txt b/src/pkg/compress/testdata/e.txt index 76cf2a7b6..5ca186f14 100644 --- a/src/pkg/compress/testdata/e.txt +++ b/src/pkg/compress/testdata/e.txt @@ -1 +1 @@ -2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354021234078498193343210681701210056278802351930332247450158539047304199577770935036604169973297250886876966403555707162268447162560798826517871341951246652010305921236677194325278675398558944896970964097545918569563802363701621120477427228364896134225164450781824423529486363721417402388934412479635743702637552944483379980161254922785092577825620926226483262779333865664816277251640191059004916449982893150566047258027786318641551956532442586982946959308019152987211725563475463964479101459040905862984967912874068705048958586717479854667757573205681288459205413340539220001137863009455606881667400169842055804033637953764520304024322566135278369511778838638744396625322498506549958862342818997077332761717839280349465014345588970719425863987727547109629537415211151368350627526023264847287039207643100595841166120545297030236472549296669381151373227536450988890313602057248176585118063036442812314965507047510254465011727211555194866850800368532281831521960037356252794495158284188294787610852639813955990067376482922443752871846245780361929819713991475644882626039033814418232625150974827987779964373089970388867782271383605772978824125611907176639465070633045279546618550966661856647097113444740160704626215680717481877844371436988218559670959102596862002353718588748569652200050311734392073211390803293634479727355955277349071783793421637012050054513263835440001863239914907054797780566978533580489669062951194324730995876552368128590413832411607226029983305353708761389396391779574540161372236187893652605381558415871869255386061647798340254351284396129460352913325942794904337299085731580290958631382683291477116396337092400316894586360606458459251269946557248391865642097526850823075442545993769170419777800853627309417101634349076964237222943523661255725088147792231519747780605696725380171807763603462459278778465850656050780844211529697521890874019660906651803516501792504619501366585436632712549639908549144200014574760819302212066024330096412704894390397177195180699086998606636583232278709376502260149291011517177635944602023249300280401867723910288097866605651183260043688508817157238669842242201024950551881694803221002515426494639812873677658927688163598312477886520141174110913601164995076629077943646005851941998560162647907615321038727557126992518275687989302761761146162549356495903798045838182323368612016243736569846703785853305275833337939907521660692380533698879565137285593883499894707416181550125397064648171946708348197214488898790676503795903669672494992545279033729636162658976039498576741397359441023744329709355477982629614591442936451428617158587339746791897571211956187385783644758448423555581050025611492391518893099463428413936080383091662818811503715284967059741625628236092168075150177725387402564253470879089137291722828611515915683725241630772254406337875931059826760944203261924285317018781772960235413060672136046000389661093647095141417185777014180606443636815464440053316087783143174440811949422975599314011888683314832802706553833004693290115744147563139997221703804617092894579096271662260740718749975359212756084414737823303270330168237193648002173285734935947564334129943024850235732214597843282641421684878721673367010615094243456984401873312810107945127223737886126058165668053714396127888732527373890392890506865324138062796025930387727697783792868409325365880733988457218746021005311483351323850047827169376218004904795597959290591655470505777514308175112698985188408718564026035305583737832422924185625644255022672155980274012617971928047139600689163828665277009752767069777036439260224372841840883251848770472638440379530166905465937461619323840363893131364327137688841026811219891275223056256756254701725086349765367288605966752740868627407912856576996313789753034660616669804218267724560530660773899624218340859882071864682623215080288286359746839654358856685503773131296587975810501214916207656769950659715344763470320853215603674828608378656803073062657633469774295634643716709397193060876963495328846833613038829431040800296873869117066666146800015121143442256023874474325250769387077775193299942137277211258843608715834835626961661980572526612206797540621062080649882918454395301529982092503005498257043390553570168653120526495614857249257386206917403695213533732531666345466588597286659451136441370331393672118569553952108458407244323835586063106806964924851232632699514603596037297253198368423363904632136710116192821711150282801604488058802382031981493096369596735832742024988245684941273860566491352526706046234450549227581151709314921879592718001940968866986837037302200475314338181092708030017205935530520700706072233999463990571311587099635777359027196285061146514837526209565346713290025994397663114545902685898979115837093419370441155121920117164880566945938131183843765620627846310490346293950029458341164824114969758326011800731699437393506966295712410273239138741754923071862454543222039552735295240245903805744502892246886285336542213815722131163288112052146489805180092024719391710555390113943316681515828843687606961102505171007392762385553386272553538830960671644662370922646809671254061869502143176211668140097595281493907222601112681153108387317617323235263605838173151034595736538223534992935822836851007810884634349983518404451704270189381994243410090575376257767571118090088164183319201962623416288166521374717325477727783488774366518828752156685719506371936565390389449366421764003121527870222366463635755503565576948886549500270853923617105502131147413744106134445544192101336172996285694899193369184729478580729156088510396781959429833186480756083679551496636448965592948187851784038773326247051945050419847742014183947731202815886845707290544057510601285258056594703046836344592652552137008068752009593453607316226118728173928074623094685367823106097921599360019946237993434210687813497346959246469752506246958616909178573976595199392993995567542714654910456860702099012606818704984178079173924071945996323060254707901774527513186809982284730860766536866855516467702911336827563107223346726113705490795365834538637196235856312618387156774118738527722922594743373785695538456246801013905727871016512966636764451872465653730402443684140814488732957847348490003019477888020460324660842875351848364959195082888323206522128104190448047247949291342284951970022601310430062410717971502793433263407995960531446053230488528972917659876016667811937932372453857209607582277178483361613582612896226118129455927462767137794487586753657544861407611931125958512655759734573015333642630767985443385761715333462325270572005303988289499034259566232975782488735029259166825894456894655992658454762694528780516501720674785417887982276806536650641910973434528878338621726156269582654478205672987756426325321594294418039943217000090542650763095588465895171709147607437136893319469090981904501290307099566226620303182649365733698419555776963787624918852865686607600566025605445711337286840205574416030837052312242587223438854123179481388550075689381124935386318635287083799845692619981794523364087429591180747453419551420351726184200845509170845682368200897739455842679214273477560879644279202708312150156406341341617166448069815483764491573900121217041547872591998943825364950514771379399147205219529079396137621107238494290616357604596231253506068537651423115349665683715116604220796394466621163255157729070978473156278277598788136491951257483328793771571459091064841642678309949723674420175862269402159407924480541255360431317992696739157542419296607312393763542139230617876753958711436104089409966089471418340698362993675362621545247298464213752891079884381306095552622720837518629837066787224430195793793786072107254277289071732854874374355781966511716618330881129120245204048682200072344035025448202834254187884653602591506445271657700044521097735585897622655484941621714989532383421600114062950718490427789258552743035221396835679018076406042138307308774460170842688272261177180842664333651780002171903449234264266292261456004337383868335555343453004264818473989215627086095650629340405264943244261445665921291225648893569655009154306426134252668472594914314239398845432486327461842846655985332312210466259890141712103446084271616619001257195870793217569698544013397622096749454185407118446433946990162698351607848924514058940946395267807354579700307051163682519487701189764002827648414160587206184185297189154019688253289309149665345753571427318482016384644832499037886069008072709327673127581966563941148961716832980455139729506687604740915420428429993541025829113502241690769431668574242522509026939034814856451303069925199590436384028429267412573422447765584177886171737265462085498294498946787350929581652632072258992368768457017823038096567883112289305809140572610865884845873101658151167533327674887014829167419701512559782572707406431808601428149024146780472327597684269633935773542930186739439716388611764209004068663398856841681003872389214483176070116684503887212364367043314091155733280182977988736590916659612402021778558854876176161989370794380056663364884365089144805571039765214696027662583599051987042300179465536788 +2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354021234078498193343210681701210056278802351930332247450158539047304199577770935036604169973297250886876966403555707162268447162560798826517871341951246652010305921236677194325278675398558944896970964097545918569563802363701621120477427228364896134225164450781824423529486363721417402388934412479635743702637552944483379980161254922785092577825620926226483262779333865664816277251640191059004916449982893150566047258027786318641551956532442586982946959308019152987211725563475463964479101459040905862984967912874068705048958586717479854667757573205681288459205413340539220001137863009455606881667400169842055804033637953764520304024322566135278369511778838638744396625322498506549958862342818997077332761717839280349465014345588970719425863987727547109629537415211151368350627526023264847287039207643100595841166120545297030236472549296669381151373227536450988890313602057248176585118063036442812314965507047510254465011727211555194866850800368532281831521960037356252794495158284188294787610852639813955990067376482922443752871846245780361929819713991475644882626039033814418232625150974827987779964373089970388867782271383605772978824125611907176639465070633045279546618550966661856647097113444740160704626215680717481877844371436988218559670959102596862002353718588748569652200050311734392073211390803293634479727355955277349071783793421637012050054513263835440001863239914907054797780566978533580489669062951194324730995876552368128590413832411607226029983305353708761389396391779574540161372236187893652605381558415871869255386061647798340254351284396129460352913325942794904337299085731580290958631382683291477116396337092400316894586360606458459251269946557248391865642097526850823075442545993769170419777800853627309417101634349076964237222943523661255725088147792231519747780605696725380171807763603462459278778465850656050780844211529697521890874019660906651803516501792504619501366585436632712549639908549144200014574760819302212066024330096412704894390397177195180699086998606636583232278709376502260149291011517177635944602023249300280401867723910288097866605651183260043688508817157238669842242201024950551881694803221002515426494639812873677658927688163598312477886520141174110913601164995076629077943646005851941998560162647907615321038727557126992518275687989302761761146162549356495903798045838182323368612016243736569846703785853305275833337939907521660692380533698879565137285593883499894707416181550125397064648171946708348197214488898790676503795903669672494992545279033729636162658976039498576741397359441023744329709355477982629614591442936451428617158587339746791897571211956187385783644758448423555581050025611492391518893099463428413936080383091662818811503715284967059741625628236092168075150177725387402564253470879089137291722828611515915683725241630772254406337875931059826760944203261924285317018781772960235413060672136046000389661093647095141417185777014180606443636815464440053316087783143174440811949422975599314011888683314832802706553833004693290115744147563139997221703804617092894579096271662260740718749975359212756084414737823303270330168237193648002173285734935947564334129943024850235732214597843282641421684878721673367010615094243456984401873312810107945127223737886126058165668053714396127888732527373890392890506865324138062796025930387727697783792868409325365880733988457218746021005311483351323850047827169376218004904795597959290591655470505777514308175112698985188408718564026035305583737832422924185625644255022672155980274012617971928047139600689163828665277009752767069777036439260224372841840883251848770472638440379530166905465937461619323840363893131364327137688841026811219891275223056256756254701725086349765367288605966752740868627407912856576996313789753034660616669804218267724560530660773899624218340859882071864682623215080288286359746839654358856685503773131296587975810501214916207656769950659715344763470320853215603674828608378656803073062657633469774295634643716709397193060876963495328846833613038829431040800296873869117066666146800015121143442256023874474325250769387077775193299942137277211258843608715834835626961661980572526612206797540621062080649882918454395301529982092503005498257043390553570168653120526495614857249257386206917403695213533732531666345466588597286659451136441370331393672118569553952108458407244323835586063106806964924851232632699514603596037297253198368423363904632136710116192821711150282801604488058802382031981493096369596735832742024988245684941273860566491352526706046234450549227581151709314921879592718001940968866986837037302200475314338181092708030017205935530520700706072233999463990571311587099635777359027196285061146514837526209565346713290025994397663114545902685898979115837093419370441155121920117164880566945938131183843765620627846310490346293950029458341164824114969758326011800731699437393506966295712410273239138741754923071862454543222039552735295240245903805744502892246886285336542213815722131163288112052146489805180092024719391710555390113943316681515828843687606961102505171007392762385553386272553538830960671644662370922646809671254061869502143176211668140097595281493907222601112681153108387317617323235263605838173151034595736538223534992935822836851007810884634349983518404451704270189381994243410090575376257767571118090088164183319201962623416288166521374717325477727783488774366518828752156685719506371936565390389449366421764003121527870222366463635755503565576948886549500270853923617105502131147413744106134445544192101336172996285694899193369184729478580729156088510396781959429833186480756083679551496636448965592948187851784038773326247051945050419847742014183947731202815886845707290544057510601285258056594703046836344592652552137008068752009593453607316226118728173928074623094685367823106097921599360019946237993434210687813497346959246469752506246958616909178573976595199392993995567542714654910456860702099012606818704984178079173924071945996323060254707901774527513186809982284730860766536866855516467702911336827563107223346726113705490795365834538637196235856312618387156774118738527722922594743373785695538456246801013905727871016512966636764451872465653730402443684140814488732957847348490003019477888020460324660842875351848364959195082888323206522128104190448047247949291342284951970022601310430062410717971502793433263407995960531446053230488528972917659876016667811937932372453857209607582277178483361613582612896226118129455927462767137794487586753657544861407611931125958512655759734573015333642630767985443385761715333462325270572005303988289499034259566232975782488735029259166825894456894655992658454762694528780516501720674785417887982276806536650641910973434528878338621726156269582654478205672987756426325321594294418039943217000090542650763095588465895171709147607437136893319469090981904501290307099566226620303182649365733698419555776963787624918852865686607600566025605445711337286840205574416030837052312242587223438854123179481388550075689381124935386318635287083799845692619981794523364087429591180747453419551420351726184200845509170845682368200897739455842679214273477560879644279202708312150156406341341617166448069815483764491573900121217041547872591998943825364950514771379399147205219529079396137621107238494290616357604596231253506068537651423115349665683715116604220796394466621163255157729070978473156278277598788136491951257483328793771571459091064841642678309949723674420175862269402159407924480541255360431317992696739157542419296607312393763542139230617876753958711436104089409966089471418340698362993675362621545247298464213752891079884381306095552622720837518629837066787224430195793793786072107254277289071732854874374355781966511716618330881129120245204048682200072344035025448202834254187884653602591506445271657700044521097735585897622655484941621714989532383421600114062950718490427789258552743035221396835679018076406042138307308774460170842688272261177180842664333651780002171903449234264266292261456004337383868335555343453004264818473989215627086095650629340405264943244261445665921291225648893569655009154306426134252668472594914314239398845432486327461842846655985332312210466259890141712103446084271616619001257195870793217569698544013397622096749454185407118446433946990162698351607848924514058940946395267807354579700307051163682519487701189764002827648414160587206184185297189154019688253289309149665345753571427318482016384644832499037886069008072709327673127581966563941148961716832980455139729506687604740915420428429993541025829113502241690769431668574242522509026939034814856451303069925199590436384028429267412573422447765584177886171737265462085498294498946787350929581652632072258992368768457017823038096567883112289305809140572610865884845873101658151167533327674887014829167419701512559782572707406431808601428149024146780472327597684269633935773542930186739439716388611764209004068663398856841681003872389214483176070116684503887212364367043314091155733280182977988736590916659612402021778558854876176161989370794380056663364884365089144805571039765214696027662583599051987042300179465536788567430285974600143785483237068701190078499404930918919181649327259774030074879681484882342932023012128032327460392219687528340516906974194257614673978110715464186273369091584973185011183960482533518748438923177292613543024932562896371361977285456622924461644497284597867711574125670307871885109336344480149675240618536569532074170533486782754827815415561966911055101472799040386897220465550833170782394808785990501947563108984124144672821865459971596639015641941751820935932616316888380132758752601460507676098392625726411120135288591317848299475682472564885533357279772205543568126302535748216585414000805314820697137262149755576051890481622376790414926742600071045922695314835188137463887104273544767623577933993970632396604969145303273887874557905934937772320142954803345000695256980935282887783710670585567749481373858630385762823040694005665340584887527005308832459182183494318049834199639981458773435863115940570443683515285383609442955964360676090221741896883548131643997437764158365242234642619597390455450680695232850751868719449064767791886720306418630751053512149851051207313846648717547518382979990189317751550639981016466414592102406838294603208535554058147159273220677567669213664081505900806952540610628536408293276621931939933861623836069111767785448236129326858199965239275488427435414402884536455595124735546139403154952097397051896240157976832639450633230452192645049651735466775699295718989690470902730288544945416699791992948038254980285946029052763145580316514066229171223429375806143993484914362107993576737317948964252488813720435579287511385856973381976083524423240466778020948399639946684833774706725483618848273000648319163826022110555221246733323184463005504481849916996622087746140216157021029603318588727333298779352570182393861244026868339555870607758169954398469568540671174444932479519572159419645863736126915526457574786985964242176592896862383506370433939811671397544736228625506803682664135541448048997721373174119199970017293907303350869020922519124447393278376156321810842898207706974138707053266117683698647741787180202729412982310888796831880854367327806879771659111654224453806625861711729498038248879986504061563975629936962809358189761491017145343556659542757064194408833816841111166200759787244137082333917886114708228657531078536674695018462140736493917366254937783014074302668422150335117736471853872324040421037907750266020114814935482228916663640782450166815341213505278578539332606110249802273093636740213515386431693015267460536064351732154701091440650878823636764236831187390937464232609021646365627553976834019482932795750624399645272578624400375983422050808935129023122475970644105678361870877172333555465482598906861201410107222465904008553798235253885171623518256518482203125214950700378300411216212126052726059944320443056274522916128891766814160639131235975350390320077529587392412476451850809163911459296071156344204347133544720981178461451077872399140606290228276664309264900592249810291068759434533858330391178747575977065953570979640012224092199031158229259667913153991561438070129260780197022589662923368154312499412259460023399472228171056603931877226800493833148980338548909468685130789292064242819174795866199944411196208730498064385006852620258432842085582338566936649849720817046135376163584015342840674118587581546514598270228676671855309311923340191286170613364873183197560812569460089402953094429119590295968563923037689976327462283900735457144596414108229285922239332836210192822937243590283003884445701383771632056518351970100115722010956997890484964453434612129224964732356126321951155701565824427661599326463155806672053127596948538057364208384918887095176052287817339462747644656858900936266123311152910816041524100214195937349786431661556732702792109593543055579732660554677963552005378304619540636971842916168582734122217145885870814274090248185446421774876925093328785670674677381226752831653559245204578070541352576903253522738963847495646255940378924925007624386893776475310102323746733771474581625530698032499033676455430305274561512961214585944432150749051491453950981001388737926379964873728396416897555132275962011838248650746985492038097691932606437608743209385602815642849756549307909733854185583515789409814007691892389063090542534883896831762904120212949167195811935791203162514344096503132835216728021372415947344095498316138322505486708172221475138425166790445416617303200820330902895488808516797258495813407132180533988828139346049850532340472595097214331492586604248511405819579711564191458842833000525684776874305916390494306871343118796189637475503362820939949343690321031976898112055595369465424704173323895394046035325396758354395350516720261647961347790912327995264929045151148307923369382166010702872651938143844844532639517394110131152502750465749343063766541866128915264446926222884366299462732467958736383501937142786471398054038215513463223702071533134887083174146591492406359493020921122052610312390682941345696785958518393491382340884274312419099152870804332809132993078936867127413922890033069995875921815297612482409116951587789964090352577345938248232053055567238095022266790439614231852991989181065554412477204508510210071522352342792531266930108270633942321762570076323139159349709946933241013908779161651226804414809765618979735043151396066913258379033748620836695475083280318786707751177525663963479259219733577949555498655214193398170268639987388347010255262052312317215254062571636771270010760912281528326508984359568975961038372157726831170734552250194121701541318793651818502020877326906133592182000762327269503283827391243828198170871168108951187896746707073377869592565542713340052326706040004348843432902760360498027862160749469654989210474443927871934536701798673920803845633723311983855862638008516345597194441994344624761123844617615736242015935078520825600604101556889899501732554337298073561699861101908472096600708320280569917042590103876928658336557728758684250492690370934262028022399861803400211320742198642917383679176232826444645756330336556777374808644109969141827774253417010988435853189339175934511574023847292909015468559163792696196841000676598399744972047287881831200233383298030567865480871476464512824264478216644266616732096012564794514827125671326697067367144617795643752391742928503987022583734069852309190464967260243411270345611114149835783901793499713790913696706497637127248466613279908254305449295528594932793818341607827091326680865655921102733746700132583428715240835661522165574998431236278287106649401564670141943713823863454729606978693335973109537126499416282656463708490580151538205338326511289504938566468752921135932220265681856418260827538790002407915892646028490894922299966167437731347776134150965262448332709343898412056926145108857812249139616912534202918139898683901335795857624435194008943955180554746554000051766240202825944828833811886381749594284892013520090951007864941868256009273977667585642598378587497776669563350170748579027248701370264203283965756348010818356182372177082236423186591595883669487322411726504487268392328453010991677518376831599821263237123854357312681202445175401852132663740538802901249728180895021553100673598184430429105288459323064725590442355960551978839325930339572934663055160430923785677229293537208416693134575284011873746854691620648991164726909428982971065606801805807843600461866223562874591385185904416250663222249561448724413813849763797102676020845531824111963927941069619465426480006761727618115630063644321116224837379105623611358836334550102286170517890440570419577859833348463317921904494652923021469259756566389965893747728751393377105569802455757436190501772466214587592374418657530064998056688376964229825501195065837843125232135309371235243969149662310110328243570065781487677299160941153954063362752423712935549926713485031578238899567545287915578420483105749330060197958207739558522807307048950936235550769837881926357141779338750216344391014187576711938914416277109602859415809719913429313295145924373636456473035037374538503489286113141638094752301745088784885645741275003353303416138096560043105860548355773946625033230034341587814634602169235079216111013148948281895391028916816328709309713184139815427678818067628650978085718262117003140003377301581536334149093237034703637513354537634521050370995452942055232078817449370937677056009306353645510913481627378204985657055608784211964039972344556458607689515569686899384896439195225232309703301037277227710870564912966121061494072782442033414057441446459968236966118878411656290355117839944070961772567164919790168195234523807446299877664824873753313018142763910519234685081979001796519907050490865237442841652776611425351538665162781316090964802801234493372427866930894827913465443931965254154829494577875758599482099181824522449312077768250830768282335001597040419199560509705364696473142448453825888112602753909548852639708652339052941829691802357120545328231809270356491743371932080628731303589640570873779967845174740515317401384878082881006046388936711640477755985481263907504747295012609419990373721246201677030517790352952793168766305099837441859803498821239340919805055103821539827677291373138006715339240126954586376422065097810852907639079727841301764553247527073788764069366420012194745702358295481365781809867944020220280822637957006755393575808086318932075864444206644691649334467698180811716568665213389686173592450920801465312529777966137198695916451869432324246404401672381978020728394418264502183131483366019384891972317817154372192103946638473715630226701801343515930442853848941825678870721238520597263859224934763623122188113706307506918260109689069251417142514218153491532129077723748506635489170892850760234351768218355008829647410655814882049239533702270536705630750317499788187009989251020178015601042277836283644323729779929935160925884515772055232896978333126427671291093993103773425910592303277652667641874842441076564447767097790392324958416348527735171981064673837142742974468992320406932506062834468937543016787815320616009057693404906146176607094380110915443261929000745209895959201159412324102274845482605404361871836330268992858623582145643879695210235266673372434423091577183277565800211928270391042391966426911155333594569685782817020325495552528875464466074620294766116004435551604735044292127916358748473501590215522120388281168021413865865168464569964810015633741255098479730138656275460161279246359783661480163871602794405482710196290774543628092612567507181773641749763254436773503632580004042919906963117397787875081560227368824967077635559869284901628768699628053790181848148810833946900016380791075960745504688912686792812391148880036720729730801354431325347713094186717178607522981373539126772812593958220524289991371690685650421575056729991274177149279608831502358697816190894908487717722503860872618384947939757440664912760518878124233683125467278331513186758915668300679210215947336858591201395360301678110413444411030903388761520488296909104689167671555373346622545575975202624771242796225983278405833585897671474205724047439720232895903726148688388003174146490203843590358527993123871042845981608996101945691646983837718267264685264869172948414153004604004299585035164101899027529366867431834955447458124140190754681607770977920579383895378192128847409929537040546962226547278807248685508046571043123854873351653070570784584243335550958221912862797205455466267099131902370311779690892786623112661337671178512943059323281605826535623848164192144732543731002062738466812351691016359252588256806438946389880872735284406462208149513862275239938938734905082625472417781702582044129853760499827899020083498387362992498125742354568439023012261733665820546785671147973065077035475620567428300187473019197310881157516777005071432012726354601912460800451608108641835539669946936947322271670748972850464195392966434725254724357659192969949061670189061433616907056148280980363243454128229968275980226694045642181328624517549652147221620839824594576613342710564957193564431561774500828376935700995419541839029151033187933907614207467028867968594985439789457300768939890070073924697461812855764662265412913204052279071212820653775058280040897163467163709024906774736309136904002615646432159560910851092445162454420141442641660181385990017417408244245378610158433361777292580611159192008414091888191208858207627011483671760749046980914443057262211104583300789331698191603917150622792986282709446275915009683226345073725451366858172483498470080840163868209726371345205439802277866337293290829914010645589761697455978409211409167684020269370229231743334499986901841510888993165125090001163719114994852024821586396216294981753094623047604832399379391002142532996476235163569009445086058091202459904612118623318278614464727795523218635916551883057930657703331498510068357135624341881884405780028844018129031378653794869614630467726914552953690154167025838032477842272417994513653582260971652588356712133519546838335349801503269359798167463231847628306340588324731228951257944267639877946713121042763380872695738609314631539148548792514028885025189788076023838995615684850391995855029256054176767663145354058496296796781349420116003325874431438746248313850214980401681940795687219268462617287403480967931949965604299190281810597603263251746405016454606266765529010639868703668263299050577706266397868453584384057673298268163448646707439990917504018892319267557518354054956017732907127219134577524905771512773358423314008356080926962298894163047287780054743798498545562870729968407382937218623831766524716090967192007237658894226186550487552614557855898773008703234726418384831040394818743616224455286163287628541175946460497027724490799275146445792982549802258601001772437840167723166802004162547244179415547810554178036773553354467030326469619447560812831933095679685582771932031205941616693902049665352189672822671972640029493307384717544753761937017882976382487233361813499414541694736549254840633793674361541081593464960431603544354737728802361047743115330785159902977771499610274627769759612488879448609863349422852847651310277926279743981957617505591300993377368240510902583759345170015340522266144077237050890044496613295859536020556034009492820943862994618834790932894161098856594954213114335608810239423706087108026465913203560121875933791639666437282836752328391688865373751335794859860107569374889645657187292540448508624449947816273842517229343960137212406286783636675845331904743954740664015260871940915743955282773904303868772728262065663129387459875317749973799293043294371763801856280061141619563942414312254397099163565102848315765427037906837175764870230052388197498746636856292655058222887713221781440489538099681072143012394693530931524054081215705402274414521876541901428386744260011889041724570537470755550581632831687247110220353727166112304857340460879272501694701067831178927095527253222125224361673343366384756590949728221809418684074238351567868893421148203905824224324264643630201441787982022116248471657468291146315407563770222740135841109076078464780070182766336227978104546331131294044833570134869585165267459515187680033395522410548181767867772152798270250117195816577603549732923724732067853690257536233971216884390878879262188202305529937132397194333083536231248870386416194361506529551267334207198502259771408638122015980894363561808597010080081622557455039101321981979045520049618583777721048046635533806616517023595097133203631578945644487800945620369784973459902004606886572701865867757842758530645706617127194967371083950603267501532435909029491516973738110897934782297684100117657987098185725131372267749706609250481876835516003714638685918913011736805218743265426063700710595364425062760458252336880552521181566417553430681181548267844169315284408461087588214317641649835663127518728182948655658524206852221830755306118393326934164459415342651778653397980580828158806300749952897558204686612590853678738603318442905510689778698417735603118111677563872589911516803236547002987989628986181014596471307916144369564690909518788574398821730583884980809523077569358851616027719521488998358632323127308909861560777386006984035267826785387215920936255817889813416247486456433211043194821421299793188104636399541496539441501383868748384870224681829391860319598667962363489309283087840712400431022706137591368056518861313458307990705003607588327248867879324093380071864152853317943535073401891193638546730000660453783784472469288830546979000131248952100446949032058838294923613919284305249167833012980192255157050378521810552961623637523647962685751660066539364142273063001648652613891842243501797455993616794063303522111829071597538821839777552812981538570168702202620274678647916644030729018445497956399844836807851997088201407769199261674991148329821854382718946282165387064858588646221611410343570342878862979083418871606214430014533275029715104673156021000043869510583773779766003460887624861640938645252177935289947578496255243925598620521409052346250847830487046492688313289470553891357290706967599556298586669559721686506052072801342104355762779184021797626656484580261591407173477009039475168017709900129391137881248534255949312866653465033728846390649968460644741907524313323903404908195233044389559060547854954620263256676813262435925020249516275607080900436460421497025691488555265022810327762115842282433269528629137662675481993546118143913367579700141255870143319434764035725376914388899683088262844616425575034001428982557620386364384137906519612917777354183694676232982904981261717676191554292570438432239918482261744350470199171258214687683172646078959690569981353264435973965173473319484798758064137926885413552523275720457329477215706850016950046959758389373527538622664943456437071610511521617176237598050900553232154896062817794302268640579555845730600598376482703339859420098582351400179507104569019191359062304102336798080907240196312675268916362136351032648077232914950859151265812143823371072949148088472355286394195993455684156344577951727033374238129903260198160571971183950662758220321837136059718025940870615534713104482272716848395524105913605919812444978458110854511231668173534838253724825347636777581712867205865148285317273569069839935110763432091319780314031658897379628301178409806410175016511072932907832177487566289310650383806093372841399226733384778203302020700517188941706465146238366720632742644336612174011766914919235570905644803016342294301837655263108450172510307540942604409687066288066265900569082451407632599158164499361455172452057020443093722305550217222299706209749268609762787409626448772056043078634808885709143464793241536214303199965695610753570417207285334250171325558818113295504095217830139465216436594262960768570585698507157151317262928960072587601564840556088613165411835958628710665496282599535127193244635791046554389165150954187306071015034430609582302257455974944275067630926322529966338219395202927917973247094559691016402983683080426309910481567503623509654924302589575273521412445149542462972258510120707802110188106722347972579330653187713438466713807546383471635428854957610942841898601794658721444495198801550804042506452191484989920400007310672369944655246020908767882300064337725657385010969899058191290957079866699453765080407917852438222041070599278889267745752084287526377986730360561230710723922581504781379172731261234878334034473833573601973235946604273704635201327182592410906040097638585857716958419563109577748529579836844756803121874818202833941887076311731615289811756429711334181497218078040465077657204457082859417475114926179367379999220181789399433337731146911970737861041963986422166045588965683206701337505745038872111332436739840284188639147633491695114032583475841514170325690161784931455706904169858050217798497637014758914810543205854914100662201721719726878930012101267481270235940855162601689425111458499658315589660460091525797881670384625905383256920520425791378948827579603278877535466861441826827797651258953563761485994485049706638406266121957141911063246061774180577212381659872472432252969098533628440799030007594546281549235506086481557928961969617060715201589825299772803520002610888814176506636216905928021516429198484077446143617891415191517976537848282687018750030264867608433204658525470555882410254654806040437372771834769014720664234434374255514129178503032471263418076525187802925534774001104853996960549926508093910691337614841834884596365621526610332239417467064368340504749943339802285610313083038484571294767389856293937641914407036507544622061186499127249643799875806537850203753189972618014404667793050140301580709266213229273649718653952866567538572115133606114457222800851183757899219543063413692302293139751143702404830227357629039911794499248480915071002444078482866598579406525539141041497342780203520135419925977628178182825372022920108186449448349255421793982723279357095828748597126780783134286180750497175747373730296280477376908932558914598141724852658299510882230055223242218586191394795184220131553319634363922684259164168669438122537135960710031743651959027712571604588486044820674410935215327906816032054215967959066411120187618531256710150212239401285668608469435937408158536481912528004920724042172170913983123118054043277015835629513656274610248827706488865037765175678806872498861657094846665770674577000207144332525555736557083150320019082992096545498737419756608619533492312940263904930982014700371161829485939931199955070455381196711289367735249958182011774799788636393286405807810818657337668157893827656450642917396685579555053188715314552353070355994740186225988149854660737787698781542360397080977412361518245964026869979609564523828584235953564615185448165799966460648261396618720304839119560250381111550938420209894591555760083897989949964566262540514195610780090298667014635238532066032574466820259430618801773091109212741138269148784355679352572808875543164693077235363768226036080174040660997151176880434927489197133087822951123746632635635328517394189466510943745768270782209928468034684157443127739811044186762032954475468077511126663685479944460934809992951875666499902261686019672053749149951226823637895865245462813439289338365156536992413109638102559114643923805213907862893561660998836479175633176725856523591069520326895990054884753424160586689820067483163174286329119633399132709086065074595260357157323069712106423424081597068328707624437165532750228797802598690981111226558888151520837482450034463046505984569690276166958278982913613535306291331427881888249342136442417833519319786543940201465328083410341785272489879050919932369270996567133507711905899945951923990615156165480300145359212550696405345263823452155999210578191371030188979206408883974767667144727314254467923500524618849237455307575734902707342496298879996942094595961008702501329453325358045689285707241207965919809225550560061971283541270202072583994171175520920820151096509526685113897577150810849443508285458749912943857563115668324566827992991861539009255871716840495663991959154034218364537212023678608655364745175654879318925644085274489190918193411667583563439758886046349413111875241038425467937999203546910411935443113219136068129657568583611774564654674861061988591414805799318725367531243470335482637527081353105570818049642498584646147973467599315946514787025065271083508782350656532331797738656666181652390017664988485456054961300215776115255813396184027067814900350252876823607822107397102339146870159735868589015297010347780503292154014359595298683404657471756232196640515401477953167461726208727304820634652469109953327375561090578378455945469160223687689641425960164689647106348074109928546482353083540132332924864037318003195202317476206537726163717445360549726690601711176761047774971666890152163838974311714180622222345718567941507299526201086205084783127474791909996889937275229053674785020500038630036526218800670926674104806027341997756660029427941090400064654281074454007616429525362460261476180471744322889953285828397762184600967669267581270302806519535452053173536808954589902180783145775891280203970053633193821100095443241244197949192916205234421346395653840781209416214835001155883618421164283992454027590719621537570187067083731012246141362048926555668109467076386536083015847614512581588569610030337081197058344452874666198891534664244887911940711423940115986970795745946337170243268484864632018986352827092313047089215684758207753034387689978702323438584381125011714013265769320554911860153519551654627941175593967947958810333935413289702528893533748106257875620364294270257512121137330213811951395756419122685155962476203282038726342066227347868223036522019655729325905068134849292299647248229359787842720945578267329975853818536442370617353517653060396801087899490506654491544577952166038552398013798104340564182403396162494910454712104839439200945914647542424785991096900046541371091630096785951563947332190934511838669964622788855817353221326876634958059123761251203010983867841195725887799206041260049865895027247133146763722204388398558347770112599424691208308595666787531942465131444389971195968105937957532155524204659410081418351120174196853432672343271868099625045432475688702055341969199545300952644398446384346598830418262932239295612610045884644244285011551557765935780379565026806130721758672048541797157896401554276881090475899564605488362989140226580026134158039480357971019004151547655018391755772677897148793477372747525743898158705040701968215101218826088040084551332795162841280679678965570163917067779841529149397403158167896865448841319046368332179115059107813898261026271979696826411179918656038993895418928488851750122504754778999508544083983800725431468842988412616042682248823097788556495765424017114510393927980290997604904428832198976751320535115230545666467143795931915272680278210241540629795828828466355623580986725638200565215519951793551069127710538552661926903526081367717666435071213453983711357500975854405939558661737828297120544693182260401670308530911657973113259516101749193468250063285777004686987177255226525708428745733039859744230639751837209975339055095883623642814493247460522424051972825153787541962759327436278819283740253185668545040893929401040561666867664402868211607294830305236465560955351079987185041352121321534713770667681396211443891632403235741573773787908838267618458756361026435182951815392455211729022985278518025598478407179607904114472041476091765804302984501746867981277584971731733287305281134969591668387877072315968334322509070204019030503595891994666652037530271923764252552910347950343816357721698115464329245608951158732012675424975710520894362639501382962152214033621065422821876739580121286442788547491928976959315766891987305176388698461503354594898541849550251690616888419122873385522699976822609645007504500096116866129171093180282355042553653997166054753907348915189650027442328981181709248273610863801576007240601649547082331349361582435128299050405405333992577071321011503713898695076713447940748097845416328110406350804863393555238405735580863718763530261867971725608155328716436111474875107033512913923595452951407437943144900950809932872153235195999616750297532475931909938012968640379783553559071355708369947311923538531051736669154087312467233440702525006918026747725078958903448856673081487299464807786497709361969389290891718228134002845552513917355978456150353144603409441211512001738697261466786933733154341007587514908295822756919350542184106448264951943804240543255345965248373785310657979037977505031436474651422484768831323479762673689855474944277949916560108528257618964374464656819789319422077536824661110427671936481836360534108748971066866318805026555929568123959680449295166615409802610781691689418764353363449482900125929366840591370059526914934421861891742142561071896846626335874414976973921566392767687720145153302241853125308442727245771161505550519076276250016522166274796257424425420546785767478190959486500575711016264847833741198041625940813327229905891486422127968042984725356237202887830051788539737909455265135144073130049869453403245984236934627060242579432563660640597549471239092372458126154582526667304702319359866523378856244229188278436440434628094888288712101968642736370461639297485616780079779959696843367730352483047478240669928277140069031660709951473154191919911453182543906294573298686613524886500574780251977607442660798300291573030523199052185718628543687577860915726925232573171665625274275808460620177046433101212443409281314659760221360416223031167750085960128475289259463348312408766740128170543067985261868949895004918275008304998926472034986965363326210919830621495095877228260815566702155693484634079776879525038204442326697479264829899016938511552124688935873289878336267819361764023681714606495185508780596635354698788205094762016350757090024201498400967867845405354130050482404996646978558002628931826518708714613909521454987992300431779500489569529280112698632533646737179519363094399609176354568799002814515169743717518330632232942199132137614506411391269837128970829395360832883050256072727563548374205497856659895469089938558918441085605111510354367477810778500572718180809661542709143010161515013086522842238721618109043183163796046431523184434669799904865336375319295967726080853457652274714047941973192220960296582500937408249714373040087376988068797038047223488825819819025644086847749767508999164153502160223967816357097637814023962825054332801828798160046910336602415904504637333597488119998663995617171089911809851197616486499233594328274275983382931099806461605360243604040848379619072542165869409486682092396143083817303621520642297839982533698027039931804024928814430649614747600087654305571672697259114631990688823893005380061568007730984416061355843701277573463708822073792921409548717956947854414951731561828176343929570234710460088230637509877521391223419548471196982303169544468045517922669260631327498272520906329003279972932906827204647650366969765227673645419031639887433042226322021325368176044169612053532174352764937901877252263626883107879345194133825996368795020985033021472307603375442346871647223795507794130304865403488955400210765171630884759704098331306109510294140865574071074640401937347718815339902047036749084359309086354777210564861918603858715882024476138160390378532660185842568914109194464566162667753712365992832481865739251429498555141512136758288423285957759412684479036912662015308418041737698963759002546999454131659341985624780714434977201991702665380714107259910648709897259362243300706760476097690456341576573395549588448948093604077155688747288451838106069038026528318275560395905381507241627615047252487759578650784894547389096573312763852962664517004459626327934637721151028545472312880039058405918498833810711366073657536918428084655898982349219315205257478363855266205400703561310260405145079325925798227406012199249391735122145336707913500607486561657301854049217477162051678486507913573336334257685988361252720250944019430674728667983441293018131344299088234006652915385763779110955708000600143579956351811596764725075668367726052352939773016348235753572874236648294604770429166438403558846422370760111774821079625901180265548868995181239470625954254584491340203400196442965370643088660925268811549596291166168612036195319253262662271108142149856132646467211954801142455133946382385908540917878668826947602781853283155445565265933912487885639504644196022475186011405239187543742526581685003052301877096152411653980646785444273124462179491306502631062903402737260479940181929954454297256377507172705659271779285537195547433852182309492703218343678206382655341157162788603990157495208065443409462446634653253581574814022471260618973060860559065082163068709634119751925774318683671722139063093061019303182326666420628155129647685313861018672921889347039342072245556791239578260248978371473556820782675452142687314252252601795889759116238720807580527221031327444754083319215135934526961397220564699247718289310588394769170851420631557192703636345039529604362885088555160008371973526383838996789184600327073682083234847108471706160879195227388252347506380811606090840124222431476103563328940609282430125462013806032608121942876847907192546246309055749298781661271916548229644317263587524548607563020667656942355342774617635549231817456159185668061686428714964129290560130053913469569829490891003991259088290348791943368696942620662946948514931472688923571615032405542263391673583102728579723061998175868700492227418629077079508809336215346303842967525604369606110193842723883107587771653594778681499030978765900869583480043137176832954871752604714113064847270887246697164585218774442100900090916189819413456305028950484575822161887397443918833085509908566008543102796375247476265353031558684515120283396640547496946343986288291957510384781539068343717740714095628337554413567955424664601335663617305811711646062717854078898495334329100315985673932305693426085376230981047171826940937686754301837015557540822371538037838383342702379535934403549452173960327095407712107332936507766465603712364707109272580867897181182493799540477008369348889220963814281561595610931815183701135104790176383595168144627670903450457460997444500166918675661035889313483800512736411157304599205955471122443903196476642761038164285918037488354360663299436899730090925177601162043761411616688128178292382311221745850238080733727204908880095181889576314103157447684338100457385008523652069340710078955916549813037292944462306371284357984809871964143085146878525033128989319500645722582281175483887671061073178169281242483613796475692482076321356427357261609825142445262515952514875273805633150964052552659776922077806644338105562443538136258941809788015677378951310313157361136026047890761945591820289365770116416881703644242694283057457471567494391573593353763114830246668754727566653059819746822346578699972291792416156043557665183382167059157867799311835820189855730344883681934418305987021880502259192818047775223884407167894780414701414651073580452021499197980812095692195622632313741870979731320870864552236740416185590793816745658234353037283309503729022429802768451559528656923189798000383061378732434546500582722712325031420712488100290697226311129067629080951145758060270806092801504406139446350643069742785469477459876821004441453438033759717384777232052065301037861326418823586036569054773343070911759152582503029410738914441818378779490613137536794654893375260322906277631983337976816641721083140551864133302224787118511817036598365960493964571491686005656771360533192423185262166760222073368844844409234470948568027905894191829969467724456269443308241243846160408284006424867072583661011433404214473683453638496544701067827313169538435919120440283949541956874453676459875488726170687163109591315801609722382049772577307454562979127906177531663252857205858766376754282917933549923678212008601904369428956102301731743150352204665675088491593025926618816581008701658499456495586855628208747248318351516339189292646558880593601275151838235485893426165223086697314511412035659916934103076974774451947043836739600076578628245472064617380804602903639144493859012422380173377038154675297645596518492676039300171943042511794045679862114630138402371099347243455794730048929825402680821621522346560274258486595687074510352794291633405915025075992398611224340312056999780516223878772230396359709132856830486160362127579561601328561866388146004722200580017580282279272167842720649966956840905752590774886105493806116954293569077377792821084159737469613143291808510446953973485067590503662391722108732333169909603363771705474725026941732982890400239372879549386540463828596742216318201530139629734398479588628632934746650690284066719018081265539973675916799759010867483920062877888531102781695087545740384607594616919584610655963327283485609570305572502494416337066573150237126843581984154103154401008430380631442183776750349813408169325201240813452285974626715177152223063741359255747513535160669108359443999692315898156732033027129284241219651936303734407981204656795322986357374589031654007016472204989445629050395873788912680565516464274460174738175296313458739390484560414203426465560422112239134631023161290836446988901247285192778589195228773637440432659264672239982186452797664826673070168802722052338600372842903155828454593854349099449420750911108532138744823216151007808922516285123275724355101999038195993350032641446053470357293073912578481757987468353429629749652545426864234949270336399427519354240001973125098882419600095766257217621860474573769577649582201796258392376391717855799468922496750179251915218219624653575570564228220399546682648329822996167217080156801080799777126517156274295763666959661983507435667132218383358509536665806605597148376773866922551603463644386269977295750658468929599809168949981898588529537874489519527097766262684177088590284321676352132630838812766335363319004134332844347630067982023716933653652880580156390360562722752187272454764258840995216482554453662083811789117725225682611478014242896970967121967502094421226279437073328703410646312100557376727450271638975234111426287828736758358819056742163061523416789476056879277154789714326222041069587947186435439940738639948986836168919377836648327137363654676901173760246643082285362494712605173293777247276797635865806019396287718060679122426813922872134061694882029506831654589707623668302556167559477498715183426989208952182644710514911419441192277010977616645850068963849426165593473112961064282379048216056210094265076173838082479030510998790719611852832556787472942907151041468948104916751035295897242381802288151276582257190705537652455285511598636421244284176256230139538669970308943645907600684938040875210854159851278070333207779865635907968462191534944587677170063778573171211036517486371634098385626541555573292664616402279791195975248525300376741774056125700303625811704838385391207273191845064713669122576415213769896260940351804147432053600369234179035440735703058314741623452840188940808983125191307741823338981880316339159565954543405777784331681162551898060409183018907512170192983622897099598983405484962284289398469847938668614293324543983592637036699355184231661615244505980576745765335552338715678211466689996845227042954589710922163652573965950289645637766038988037941517917867910675199009966139206238732318786758420544279396366759104126821843375015743069045967947046685602358283919759975285865384338189120042853787549302768972168199113340697282255535300044743958830079799736518459131437946494086272149669719100359399974735262764126125995350902609540048669398955899487421379590802893196914845826873123710180229775301190684280440780938156598081694611679374425663244656799606363751546304833112722231812338371779800439731087402647536582575657351059978314264831879619843765495877803685261751835391844920488198629786329743136948511780579298636452193232481339393090754566368038513630619718033957979522539508697432546502659123585049283028832934489284591373621624852528877442891851104093746333590660233239711922814450735588373324057814862662207486215513375036775585494138678352928273109003823116855374520901095101174796663003330352534143230024288248051396631446632656081582045216883922312025671065388459503224002320453633895521539919011035217362720909565500846486605368975498478995875596103167696587161281951919668893326641203784750417081752273735270989343717167642329956935697166213782736138899530515711822960896394055380431939398453970864418654291655853168697537052760701061488025700785387150835779480952313152747735711713643356413242974208137266896149109564214803567792270566625834289773407718710649866150447478726164249976671481383053947984958938064202886667951943482750168192023591633247099185942520392818083953020434979919361853380201407072481627304313418985942503858404365993281651941497377286729589582881907490040331593436076189609669494800067194371424058105327517721952474344983414191979918179909864631583246021516575531754156198940698289315745851842783390581029411600498699307751428513021286202539508732388779357409781288187000829944831476678183644656510024467827445695591845768068704978044824105799710771577579093525803824227377612436908709875189149049904225568041463131309240101049368241449253427992201346380538342369643767428862595140146178201810734100565466708236854312816339049676558789901487477972479202502227218169405159042170892104287552188658308608452708423928652597536146290037780167001654671681605343292907573031466562485809639550080023347676187068086526878722783177420214068980703410506200235273632267291964034093571225623659496432076928058165514428643204955256838543079254299909353199329432966018220787933122323225928276556048763399988478426451731890365879756498207607478270258861409976050788036706732268192473513646356758611212953074644777149423343867876705824452296605797007134458987594126654609414211447540007211790607458330686866231309155780005966522736183536340439991445294960728379007338249976020630448806064574892740547730693971337007962746135534442514745423654662752252624869916077111131569725392943756732215758704952417232428206555322808868670153681482911738542735797154157943689491063759749151524510096986573825654899585216747260540468342338610760823605782941948009334370046866568258579827323875158302566720152604684361412652956519894291184887986819088277339147282063794512260294515707367105637720023427811802621502691790400488001808901847311751199425460594416773315777951735444490965752131026306836047140331442314298077895617051256930051804287472368435536402764392777908638966566390166776625678575354239947427919442544664643315554138265543388487778859972063679660692327601733858843763144148113561693030468420017434061395220072403658812798249143261731617813894970955038369479594617979829257740992171922783223006387384996138434398468502234780438733784470928703890536420557474836284616809363650973790900204118525835525201575239280826462555785658190226958376345342663420946214426672453987171047721482128157607275305173330963455909323664528978019175132987747952929099598069790148515839540444283988381797511245355548426126784217797728268989735007954505834273726937288386902125284843370917479603207479554080911491866208687184899550445210616155437083299502854903659617362726552868081324793106686855857401668022408227992433394360936223390321499357262507480617409173636062365464458476384647869520547719533384203403990244761056010612777546471464177412625548519830144627405538601855708359981544891286863480720710061787059669365218674805943569985859699554089329219507269337550235821561424994538234781138316591662683103065194730233419384164076823699357668723462219641322516076261161976034708844046473083172682611277723613381938490606534404043904909864126903479263503943531836741051762565704797064478004684323069430241749029731181951132935746854550484711078742905499870600373983113761544808189067620753424526993443755719446665453524088287267537759197074526286322840219629557247932987132852479994638938924943286917770190128914220188747760484939855471168524810559991574441551507431214406120333762869533792439547155394213121021954430556748370425907553004950664994802614794524739012802842646689229455664958621308118913500279654910344806150170407268010067948926855360944990373928383520627992820181576427054962997401900837493444950600754365525758905546552402103412862124809003162941975876195941956592556732874237856112669741771367104424821916671499611728903944393665340294226514575682907490402153401026923964977275904729573320027982816062130523130658731513076913832317193626664465502290735017347656293033318520949298475227462534564256702254695786484819977513326393221579478212493307051107367474918016345667888810782101151826314878755138027101379868751299375133303843885631415175908928986956197561123025310875057188962535763225834275763348421016668109884514141469311719314272028007223449941999003964948245457520704922091620614222912795322688239046498239081592961111003756999529251250673688233852648213896986384052437049402152187547825163347082430303521036927849762517317825860862215614519165573478940019558704784741658847364803865995119651409542615026615147651220820245816010801218275982577477652393859159165067449846149161165153821266726927461290533753163055654440793427876550267301214578324885948736899073512166118397877342715872870912311383472485146035661382188014840560716074652441118841800734067898587159273982452147328317214621907330492060817440914125388918087968538960627860118193099489240811702350413554126823863744341209267781729790694714759018264824761112414556423937732224538665992861551475342773370683344173073150805440138894084087253197595538897613986400165639906934600670780501058567196636796167140097031535132386972899001749862948883362389858632127176571330142071330179992326381982094042993377790345261665892577931395405145369730429462079488033141099249907113241694504241391265397274078984953073730364134893688060340009640631540701820289244667315059736321311926231179142794944897281477264038321021720718017561601025111179022163703476297572233435788863537030535008357679180120653016668316780269873860755423748298548246360981608957670421903145684942967286646362305101773132268579232832164818921732941553151386988781837232271364011755881332524294135348699384658137175857614330952147617551708342432434174779579226338663454959438736807839569911987059388085500837507984051126658973018149321061950769007587519836861526164087252594820126991923916722273718430385263107266000047367872474915828601694439920041571102706081507270147619679971490141639274282889578424398001497985658130305740620028554097382687819891158955487586486645709231721825870342960508203415938806006561845735081804032347750084214100574577342802985404049555529215986404933246481040773076611691605586804857302606467764258503301836174306413323887707999698641372275526317649662882467901094531117120243890323410259937511584651917675138077575448307953064925086002835629697045016137935696266759775923436166369375035368699454550392874449940328328128905560530091416446608691247256021455381248285307613556149618444364923014290938289373215312818797541139219415606631622784836152140668972661027123715779503062132916001988806369127647416567067485490795342762338253943990022498972883660263920518704790601584084302914787302246651371144395418253441269003331181914268070735159284180415100555199146564934872796969351992963117195821262627236458009708099166752820365818699111948365866102758375863322993225541477479210421324166848264953111826527351008031659958888814809945737293785681411438021523876706455063233067233939551964260397443829874822322662036352861302543796600943104500158604854027036789711934695579989189112302233381602302236277726084846296189550730850698061500281436425336666311433321645213882557346329366870956708432252564333895997812402164189946978348320376011613913855499933990786652305860332060641949298931012423081105800169745975038516887112037747631577311831360002742502722451570906304496369230938382329175076469684003556425503797106891999812319602533733677437970687713814747552190142928586781724044248049323750330957002929126630316970587409214456472022710796484778657310660832173093768033821742156446602190335203981531618935787083561603302255162155107179460621892674335641960083663483835896703409115513087820138723494714321400450513941428998350576038799343355677628023346565854351219361896876831439866735726040869511136649881229957801618882834124004126142251475184552502502640896823664946401177803776799157180146386554733265278569418005501363433953502870836220605121839418516239153709790768084909674194289061134979961034672077354959593868862427986411437928435620575955500144308051267664432183688321434583708549082240014585748228606859593502657405750939203135881722442164955416889785558265198046245527898343289578416968890756237467281044803018524217706136533236073856228166664597654076844715963930782091017090763377917711485205493367936868430832404126789220929930411890501756484917499452393770674524578019171841679541825554377930299249277892416277257788147974770446005423669346157135208417428211847353652367573702352791459837645712257646122605628127852169580892808988394594406165340521932514843306105322700231133680378433377389724881307874325614952744243584753011150345103737688223837573804282007358586938044331529253129961025096113761670187568525921208929131354473196308440066835155160913925692912175784379179004808848023029304392630921342768601226558630456913133560978156776098711809238440656353136182676923761613389237802972720736243967239854144480757286813436768000573823963610796223140429490728058551444771338682314499547929338131259971996894072233847404542592316639781608209399269744676323921370773991899853301483814622364299493902073285072098040905300059160091641710175605409814301906444379905831277826625762288108104414704097708248077905168225857235732665234414956169007985520848841886027352780861218049418060017941147110410688703738674378147161236141950474056521041002268987858525470689031657094677131822113205505046579701869337769278257145248837213394613987859786320048011792814546859096532616616068403160077901584946840224344163938313618742275417712170336151163782359059685168880561304838542087505126933144171705880517278127917564053282929427357971823360842784676292324980318169828654166132873909074116734612367109059236155113860447246378721244612580406931724769152219217409096880209008801535633471775664392125733993165330324425899852598966724744126503608416484160724482125980550754851232313331300621490042708542735985913041306918279258584509440150719217604794274047740253314305451367710311947544521321732225875550489799267468541529538871443696399406391099267018219539890685186755868574434469213792094590683677929528246795437302263472495359466300235998990248299853826140395410812427393530207575128774273992824866921285637240069184859771126480352376025469714309316636539718514623865421671429236191647402172547787238964043145364190541101514371773797752463632741619269990461595895793940622986041489302535678633503526382069821487003578061101552210224486633247184367035502326672749787730470216165019711937442505629639916559369593557640005236360445141148916155147776301876302136068825296274460238077523189646894043033182148655637014692476427395401909403584437251915352134557610698046469739424511797999048754951422010043090235713636892619493763602673645872492900162675597083797995647487354531686531900176427222751039446099641439322672532108666047912598938351926694497553568096931962642014042788365702610390456105151611792018698900673027082384103280213487456720062839744828713298223957579105420819286308176631987048287388639069922461848323992902685392499812367091421613488781501234093387999776097433615750910992585468475923085725368613605356762146929424264323906626708602846163376051573599050869800314239735368928435294958099434465414316189806451480849292695749412903363373410480943579407321266012450796613789442208485840536446021616517885568969302685188950832476793300404851688934411125834396590422211152736276278672366665845757559585409486248261694480201791748223085835007862255216359325125768382924978090431102048708975715033330963651576804501966025215527080352103848176167004443740572131294252820989545456276344353575741673638980108310579931697917916718271145837435222026387771805250290791645414791173616253155840768495583288190293564201219633684854080865928095131505012602919562576032932512847250469881908146475324342363863860247943921015193235101390117789997483527186469346024554247028375300033725403910085997650987642832802908445662021678362267272292737780213652404028817217012490974899454430826861772239385250883760749742195942655217301733355851389407457348144161511380845358039740277795072051893487170722955427683655826706766313911972211811528466502223383490906676554168336907959409404576472940901354356409277969379842065738891481990225399022315913388145851487225126560927576795873759207013915029216513720851137197522734365458411622066281660256333632074449918511469174455062297146086578736313585389023662557285424516018080487167823688885575325066254262367702604215835160174851981885460860036597606743233346410471991027562358645341748631726556391320606407754779439671383653877377610828300019937359760370467245737880967939894493795829602910746901609451288456550071458091887879542641820145369659962842686882363495879277007025298960996798975941955735253914237782443302746708282008722602053415292735847582937522487377937899136764642153727843553986244015856488692101644781661602962113570056638347990334049623875941092886778920270077504951511405782565295015024484968204744379710872943108541684540513016310902267112951959140520827546866418137305837933236150599142045255880213558474751516267815309465541240524091663857551298894834797423322854504140527354235070335984964593699534959698554244978249586929179182415068053002553370412778703476446244329205906832901886692400222391918714603175399666877477960121790688623311002908668305431787009355066944389131913333586368037447530664502418437136030852288582121720231274167009740351431532131803978033680228154223490183737494117973254478594157962104378787072154814091725163615415163381388912588517924237727229603497305533840942889918919161186249580560073570527227874940321250645426206304469470804277945973817146810395192821550688079136701210109944220737024613687196031491162370967939354636396448139025711768057799751751298979667073292674886430097398814873780767363792886767781170520534367705731566895899181530825761606591843760505051704242093231358724816618683821026679970982966436224723644898648976857100173643547336955619347638598187756855912376232580849341570570863450733443976604780386678461711520325115528237161469200634713570383377229877321365028868868859434051205798386937002783312365427450532283462669786446920780944052138528653384627970748017872477988461146015077617116261800781557915472305214759943058006652042710117125674185860274188801377931279938153727692612114066810156521441903567333926116697140453812010040811760123270513163743154487571768761575554916236601762880220601068655524141619314312671535587154866747899398685510873576261006923021359580838145290642217792987748784161516349497309700794368305080955621264592795333690631936594413261117944256602433064619312002953123619348034504503004315096798588111896950537335671086336886944665564112662287921812114121425167348136472449021275252555647623248505638391391630760976364990288930588053406631352470996993362568102360392264043588787550723319888417590521211390376609272658409023873553418516426444865247805763826160023858280693148922231457758783791564902227590699346481624734399733206013058796068136378152964615963260698744961105368384203105364183675373594176373955988088591188920114871545460924735613515979992999722298041707112256996310945945097765566409972722824015293663094891067963296735505830412258608050740410916678539569261234499102819759563955711753011823480304181029089719655278245770283085321733741593938595853203645590564229716679900322284081259569032886928291260139267587858284765599075828016611120063145411315144108875767081854894287737618991537664505164279985451077400771946398046265077776614053524831090497899859510873112620613018757108643735744708366215377470972660188656210681516328000908086198554303597948479869789466434027029290899143432223920333487108261968698934611177160561910681226015874410833093070377506876977485840324132474643763087889666151972556180371472590029550718424245405129246729039791532535999005557334600111693557020225722442772950263840538309433999383388018839553821540371447394465152512354603526742382254148328248990134023054550811390236768038649723899924257800315803725555410178461863478690646045865826036072306952576113184134225274786464852363324759102670562466350802553058142201552282050989197818420425028259521880098846231828512448393059455162005455907776121981297954040150653985341579053629101777939776957892084510979265382905626736402636703151957650493344879513766262192237185642999150828898080904189181015450813145034385734032579549707819385285699926238835221520814478940626889936085239827537174490903769904145555260249190126341431327373827075950390882531223536876389814182564965563294518709637484074360669912550026080424160562533591856230955376566866124027875883101021495284600804805028045254063691285010599912421270508133194975917146762267305044225075915290251742774636494555052325186322411388406191257012917881384181566918237215400893603475101448554254698937834239606460813666829750019379115061709452680984785152862123171377897417492087541064556959508967969794980679770961683057941674310519254486327358885118436597143583348756027405400165571178309126113117314169066606067613797690123141099672013123730329707678988740099317309687380126740538923612230370779727025191340850390101739924877352408881040807749924412635346413181858792480760553268122881584307471326768283097203149049868884456187976015468233715478415429742230166504759393312132256510189175368566338139736836336126010908419590215582111816677413843969205870515074254852744810154541079359513596653630049188769523677579147319184225806802539818418929888943038224766186405856591859943091324575886587044653095332668532261321209825839180538360814144791320319699276037194760191286674308615217243049852806380129834255379486287824758850820609389214668693729881191560115633701248675404205911464930888219050248857645752083363921499441937170268576222251074166230901665867067714568862793343153513505688216165112807318529333124070912343832502302341169501745502360505475824093175657701604884577017762183184615567978427541088499501610912720817913532406784267161792013428902861583277304794830971705537485109380418091491750245433432217445924133037928381694330975012918544596923388733288616144238100112755828623259628572648121538348900698511503485369544461542161283241700533583180520082915722904696365553178152398468725451306350506984981006205514844020769539324155096762680887603572463913955278222246439122592651921288446961107463586148252820017348957533954255019475442643148903233373926763409115527189768429887783617346613535388507656327107814312435018965109238453660236940276060642119384227665755210663671879603217527184404651560427289869560206997012906367847161654793068868305846508082886614111979138822898112498261434559408961813509226857611474609406147937240008842153535862052780125014270055274468359151840373309373580494342483940467505708347927948338133276237937844629209323999417593374917899786484958148818865149169302451512835579818112344900827168644548306546633975256079615935830821400021951611342337058359111545217293721664061708131602078213341260356852013161345136871600980378712556766143923146458085652084039744217352744813741215277475202259244561520365608268890193913957991844109971588312780020898275935898106482117936157951837937026741451400902833064466209280549839169261068975151083963132117128513257434964510681479694782619701483204392206140109523453209269311762298139422044308117317394338867965739135764377642819353621467837436136161591167926578700137748127848510041447845416464568496606699139509524527949914769441031612575776863713634644477006787131066832417871556281779122339077841275184193161188155887229676749605752053192594847679397486414128879475647133049543555044790277128690095643357913405127375570391806822344718167939329121448449553897728696601037841520390662890781218240141299368590465146519209198605347788576842696538459445700169758422531241268031418456268722581132040056433413524302102739213788415250475704533878002467378571470021087314693254557923134757243640544448132093266582986850659125571745568328831440322798049274104403921761438405750750288608423536966715191668510428001748971774811216784160854454400190449242294333666338347684438072624307319019363571067447363413698467328522605570126450123348367412135721830146848071241856625742852208909104583727386227300781566668914250733456373259567253354316171586533339843321723688126003809020585719930855573100508771533737446465211874481748868710652311198691114058503492239156755462142467550498676710264926176510110766876596258810039163948397811986615585196216487695936398904500383258041054420595482859955239065758108017936807080830518996468540836412752905182813744878769639548306385089756146421874889271294890398025623046812175145502330254086076115859321603465240763923593699949180470780496764486889980902123735780457040380820770357387588525976042434608851075199334470112741787878845674656640471901619633546770714090590826954225196409446319547658653032104723804625249971910690110456227579220926904132753699634145768795242244563973018311291451151322757841320376225862458224784696669785947914981610522628786944136373683125108310682898766123782697506343047263278453719024447970975017396831214493357290791648779915089163278018852504558488782722376705263811803792477835540018117452957747339714012352011459901984753358434861297092928529424139865507522507808919352104173963493428604871342370429572757862549365917805401652536330410692033704691093097588782938291296447890613200063096560747882082122140978472301680600835812336957051454650181292694364578357815608503303392466039553797630836137289498678842851139853615593352782103740733076818433040893624460576706096188294529171362940967592507631348636606011346115980434147450705511490716640635688739020690279453438236930531133440901381392849163507484449076828386687476663619303412376248380175840467851210698290605196112357188811150723607303158506622574566366740720668999061320627793994112805759798332878792144188725498543014546662945079670707688135022230580562225942983096887732856788971494623888272184647618153045844390967248232348259587963698908456664795754200195991919240707615823002328977439748112690476546256873684352229063217889227643289360535947903046811114130586348244566489159211382258867880972564351646404364328416076247766114349880319792230537889671148058968061594279189647401954989466232962162567264739015818692956765601444248501821713300527995551312539849919933907083138030214072556753022600033565715934283182650908979350869698950542635843046765145668997627989606295925119763672907762567862769469947280606094290314917493590511523235698715397127866718077578671910380368991445381484562682604003456798248689847811138328054940490519768008320299631757043011485087384048591850157264392187414592464617404735275250506783992273121600117160338604710710015235631159734711153198198710616109850375758965576728904060387168114313084172893710817412764581206119054145955378853200366615264923610030157044627231777788649806700723598889528747481372190175074700005571108178930354895017924552067329003818814068686247959272205591627902292600592107710510448103392878991286820705448979977319695574374529708195463942431669050083984398993036790655541596099324867822475424361758944371791403787168166189093900243862038610001362193667280872414291108080291896093127526202667881902085595708111853836166128848729527875143202956393295910508349687029060692838441522579419764824996318479414814660898281725690484184326061946254276693688953540732363428302189694947766126078346328490315128061501009539164530614554234923393806214007779256337619373052025699319099789404390847443596972052065999017828537676265683558625452697455260991024576619614037537859594506363227095122489241931813728141668427013096050734578659047904243852086508154491350136491698639048125666610843702294730266721499164849610746803261583352580352858275799038584091667618877199539888680431991650866887781701439663176815592262016991396613153738021294160006906947533431677802632207226265881842757216055461439677336258462997385077307751473833315101468395296411397329672457933540390136107395245686243008096720460995545708974893048753897955544443791303790422346037768729236001386569593952300768091377768847789746299699489949016141866131552200856673695770822720338936659590666350594330040363762591189195691561626122704788696510356062748423100605472091437069471661080277379848576543481249822444235828329813543645124092220896643987201997945619030397327254617823136363375927622656301565813545578319730419339269008282952718252138855126583037630477490625995514925943105307478901043009876580816508144862607975129633326675259272351611791836777128931053144471668835182920514343609292493191180249366051791485330421043899773019267686085347768149502299280938065840007311767895491286098112311307002535600347898600653805084532572431553654422067661352337408211307834360326940015926958459588297845649462271300855594293344520727007718206398887404742186697709349647758173683580193168322111365547392288184271373843690526638607662451284299368435082612881367358536293873792369928837047900484722240370919885912556341130849457067599032002751632513926694249485692320904596897775676762684224768120033279577059394613185252356456291805905295974791266162882381429824622654141067246487216174351317397697122228010100668178786776119825961537643641828573481088089988571570279722274734750248439022607880448075724807701621064670166965100202654371260046641935546165838945950143502160890185703558173661823437491622669077311800121188299737319891006060966841193266075165452741829459541189277264192546108246351931647783837078295218389645376236304858042774417907169146356546201215125418664885396161542055152375000426794253417764590821513675258479774465114750438460596325820468809667795709044645884673847481638045635188183210386594798204376334738389017759714236223057776395541011294523488098341476645559342209402059733452337956309441446698222457026367119493286653989491344225517746402732596722993581333110831711807234044326813737231209669052411856734897392234152750707954137453460386506786693396236535556479102508529284294227710593056660625152290924148057080971159783458351173168204129645967070633303569271821496292272073250126955216172649821895790908865085382490848904421755530946832055636316431893917626269931034289485184392539670922412565933079102365485294162132200251193795272480340133135247014182195618419055761030190199521647459734401211601239235679307823190770288415814605647291481745105388060109787505925537152356112290181284710137917215124667428500061818271276125025241876177485994084521492727902567005925854431027704636911098800554312457229683836980470864041706010966962231877065395275783874454229129966623016408054769705821417128636329650130416501278156397799631957412627634011130135082721772287129164002237230234809031485343677016544959380750634285293053131127965945266651960426350406454862543383772209428482543536823186182982713182489884498260285705690699045790998144649193654563259496570044689011049923939218088155626191834404362264965506449848521612498442375928443642612004256628602157801140467879662339228190804577624109076487087406157070486658398144845855803277997327929143195789110373530019873110486895656281917362036703039179710646309906285483702836118486672219457621775034511770110458001291255925462680537427727378863726783016568351092332280649908459179620305691566806180826586923920561895421631986004793961133953226395999749526798801074576466538377400437463695133685671362553184054638475191646737948743270916620098057717103475575333102702706317395612448413745782734376330101853438497450236265733191742446567787499665000938706441886733491099877926005340862442833450486907338279348425305698737469497333364267191968992849534561045719338665222471536681145666596959735075972188416698767321649331898967182978657974612216573922404856900225324160367805329990925438960169901664189038843548375648056012628830409421321300206164540821986138099462721214327234457806819925823202851398237118926541234460723597174777907172041523181575194793527456442984630888846385381068621715274531612303165705848974316209831401326306699896632888532682145204083110738032052784669279984003137878996525635126885368435559620598057278951754498694219326972133205286374577983487319388899574634252048213337552584571056619586932031563299451502519194559691231437579991138301656117185508816658756751184338145761060365142858427872190232598107834593970738225147111878311540875777560020664124562293239116606733386480367086953749244898068000217666674827426925968686433731916548717750106343608307376281613984107392410037196754833838054369880310983922140260514297591221159148505938770679068701351029862207502287721123345624421024715163941251258954337788492834236361124473822814504596821452253550035968325337489186278678359443979041598043992124889848660795045011701169092519383155609441705397900600291315024253848282782826223304151370929502192196508374714697845805550615914539506437316401173317807741497557116733034632008408954066541694665746735785483133770133628948904397670025863002540635264006601631712883920305576358989492412827022489373848906764385339931878608019223108328847459816417701264089078551777830131616162049792779670521847212730327970738223860581986744668610994383049960437407323195784473254857416239738852016202384784256163512597161783106850156299135559874758848151014815490937380933394074455700842090155903853444962128368313687375166780513082594599771257467939781491953642874321122421579851584491669362551569370916855252644720786527971466476760328471332985501945689772758983450586004316822658631176606237201721007922216410188299330808409384014213759697185976897042759041500946595252763487628135867117352364964121058854934496645898651826545634382851159137631569519895230262881794959971545221250667461174394884433312659432286710965281109501693028351496524082850120190831078678067061851145740970787563117610746428835593915985421673115153096948758378955979586132649569817205284291038172721213138681565524428109871168862743968021885581515367531218374119972919471325465199144188500672036481975944167950887487934416759598361960010994838744709079104099785974656112459851972157558134628546189728615020774374529539536929655449012953097288963767713353842429715394179547179095580120134210175150931491664699052366350233024087218654727629639065723341455005903913890253699317155917179823065162679744711857951506573868504088229934804445549850597823297898617029498418376255258757455303112991914341109413088238114443068843062655305601658801408561023324210300218460588586954418502977463085858496130037238190325162225570729975710727306066072916922978033647048840958711228045188511908718588299514331534128549297173849768523136276076868494780364948299904475715771141080958058141208956059471668626290036145602625334863284986816039463372436667112964460292915746181117789169695839947080954788863503281129626899231110099889317815313946681882028368363373822281414974006917942192888817139116283910295684918233358930813360131488748366464224381776081007739183393749346933644748150564933649323157235306109385796839902153381449126925350768211098738352197507736653475499431740580563099143218212547336281359488317681489194306530426029773885492974570569448783077945878865062970895499843760181694031056909587141386804846359853684034105948341788438963179956468815791937174656705047441528027712541569401365862097760735632832966564135817028088013546326104892768731829917950379944446328158595181380144716817284996793061814177131912099236282922612543236071226270324572637946863533391758737446552006008819975294017572421299723542069630427857950608911113416534893431149175314953530067419744979017235181671568754163484949491289001739377451431928382431183263265079530371177806185851153508809998200482761808307209649636476943066172549186143700971387567940218696710148540307471561091358933165600167252126542502898612259306484105898847129649230941215144563947889999327145875969555737090855150648002321476443037232466147111552578583071024936898814562568786834745518893385181791667579054210421036349316257870476543126790661216644142285017446278477132740595579600648343288827864837043456066966456899746910373987712891593313271266247505582258634928427718355831641593667712218537642376222104779338956378722902509543014182257180331300148113377736941508488867501893156994849838936052666818012783912005801431596441910546663236810148207799356523056490420711364192200177189107935243234322761787712568251126481332974354926568682748715986654943041648468220593921673359485057849622807932422649812705271398407720995707236227009245067665680069149966555737866411877079767754867028786431817941521796178310655030287157272282250812017060713380339641841211253856248920130010782462165136989511064611133562443838185366273563783436921279354709230119655914915800561707258518503167289370411936374780625824298250726464801821523430268081486978164824349353456855843696378384153838051184406043696871666416514036129729992912630842812149152469877429332305214999981829046119471676727503742221367186614654042534463141660649871499001000660041544868437352208483059495953182872280520828676300361091734508632133033647289584176588755345227938480297724485711815574893561311524926772006362198369980664159549388683836411891430443767715498026544959061738265591178545999378510861446014967645550103653971251138583505085112442517772923814396233043724036032603181442991365750246012787514117944901305803452199992701148071712847770301254994886841867572975189214295652512486943983729047410363121899124217339550688778643130750024823361832738729697376598820053895902935486054979802320400472236873557411858132734337978931582039412878989728973298812553514507641535360519462112217000676321611195841029252568536561813138784086477147099724553013170761712163186600291464501378587854802096244703771373587720086738054108140042311418525803293267396324596914044834665722042880679280616029884043400536534009706581694636096660911110968789751801325224478246957913251892122653056085866541115373584912790254654369020869419871125588453729063224423222287139122012248769976837147645598526739225904997885514250047585260297929306159913444898341973583316070107516452301310796620382579278533125161760789984630103493496981494261055367836366022561213767081421091373531780682420175737470287189310207606953355721704357535177461573524838432101571399813798596607129664438314791296359275429627129436142685922138993054980645399144588692472767598544271527788443836760149912897358259961869729756588978741082189422337344547375227693199222635973520722998387368484349176841191020246627479579564349615012657433845758638834735832242535328142047826934473129971189346354502994681747128179298167439644524956655532311649920677163664580318205849626132234652606175413532444702007661807418914040158148560001030119994109595492321434406067634769713089513389171050503856336503545166431774489640061738861761193622676890576955693918707703942304940038440622614449572516631017080642923345170422426679607075404028551182398361531383751432493056398381877995594942545196756559181968690885283434886050828529642437578712929439366177362830136595872723080969468398938676366226456791132977469812675226595621009318322081754694778878755356188335083870248295346078597023609865656376722755704495258739871812593441903785275571333409842450127258596692434317689018966145404453679047136294238156127656824247864736176671770647002431119711090007474065945650315375044177982192306323700872039212085499569681061379189029961178936752146022386905665481382858280449537530160921422195940638787074787991194920898374091788534417523064715030278397979864517336625329511775105559014160459873338186887977858817291976604516353353556047648420520888811722831990044504284486852338334530105533929637308039738230604714104525470094899407601215247602819963846343554852932377161410869591950786873276075400085220065031871239272857835807010762542769655355964789450166013816295177908531139811092831583216931563867459747449584385282701658246192092219529134323496779345585613140207765996142546463288677356891785576835169608392864188830094883324700447958316931533832382377876344426323456301679513671047510469669001217777128065522453689371871451567394733440447280450959433090683667110655953338602938000999949010642769859623260401863733572846679531229683156358145420890540651226419162015504500430562136991850941034609601030543816694795964585804425194905110733387679946734471718615647723811737035654917628707589456035519195603962301157866323750234725054461073979402475184415558178087962822231972692984516683306919505079993357259165675557294585962182052650473353712351623662770479333289322136141858785972771685682725303734836891911847197133753088446777943274857148827821608844765700041403499921376794209627560883081509438030705666022764678117533361028187800710219794428777313146387857817205661409023041499923248268982477222109852189758140879763486146763606368674611966620347304608917277240045953051376938375381543486981101990651706961774052218247422657652138152740612699012706880875386408669901461740890540981877671880076124151967064152117653084325544261017536348281196837493395825742541244634247233586360777980960199745187758845459645895956779558869098404768259253477849930457883128541747079059795909431627722327844578918694214929451540174214623240300841907975296782445969183509474202123617940309048634960534054931299919496087957952586977170236680033862505764938088740994009589948109397983231108838769236490221499111120870639202892490698435333152727991330986335454324971441378059132240814960156485679843966464780280409057580889190254236606774500413415794312112501275232250148067232979652230488493751166084976116412777395311302041566848265531411348993243747890268935173904043294851610659785832253168204202834993641595980197343889883020994152152288611175126686173051956249367180053845637855129171848417841594797435580617856680758491080185805695567990185198397660693358224779136504562705766735170961550493338390452612404395517449136885115987454340932040102218982707539212403241042424451570052968378815749468441508011138612561164102477190903050040240662278945607061512108266146098662040425010583978098192019726759010749924884966139441184159734610382401178556739080566483321039073867083298691078093495828888707110651559651222542929154212923108071159723275797510859911398076844732639426419452063138217862260999160086752446265457028969067192282283045169111363652774517975842147102219099906257373383472726498678244401048998507631630668050267115944636293525120269424810854530602810627264236538250773340575475701704367039596467715959261029438313074897245505729085688496091346323165819468660587092144653716755655531962091865952628448253731353698162517351930115341581171353292035873164168839107994000677266031617527582917398395852606454113318985505747847121053505795649095931672167565624818782002769963734155880000867852567422461511406015760115910256449002264980039498403358091309140197877843650167960167465370287466062584346329708303725980494653589318912163976013193079476972058034710553111117215859219066231028099212084069283091906017370764654655683413207556315315006453462321007133584907633048328153458698497332599801187479664273140279381289961720524540674695271948079930396730194274036466594154400092799908634806622334906695224044652158992864203435098858422692019340575496840904812955522654754650713532842543496616084954788090727649930252702815067862810825243222979985391759845188868387004477101866772159439708514664612871148749531862180941719676843144666435175837688436786081446319641912566574047718699160915550910878919431253671945651261878486910876729910565595155159739659034383628124629118117760949411880105946336671039049777312004243578115790429823045072038322781246413671297959415082918378213212876890545963586369344879749784841123274921331663162812456388238288715648447883142417650147980187858215768793063001153788998014623690135803753306246148576074932567807682651045738059018831237617271889933790487113395588485234240255002352200613574914318259142479829367775490496399350755839668967578364316618369307625603528602940662803255416535431518013714821941772672244005268401996533334184004345525296592918502940131600651124395297874364222806977720437363717873457948420238745151249157913139411148608416429347958793681868609689684640858334131017858142710955416293375915178392341303110543328703526599993904966822112768158316511246866451167351378214345336650598328347443536290312393672084593164394941881138607974670134709640378534907149089842317891739783650654751982883367395714360000003439863363212091718954899055748693397700245632475954504411422582410783866837655467400137324322809113692670682805397549111166171102397437749479335174036135005397581475520834285772800986189401984375446435081498218360112577632447389452051636938585136484259964518361856989088721789764694721246807900330925083496645841656554261294195108847197209106605105540933731954888406444080280579549008076040034154662137669606444293774985897353625591959618552448187940317374508256072895120945456562159540405425814886929842786582357673195799285293120866275922366115137445767916063621675267440451221051052090834707443986137829082352772895849625656881972792768694795806100573787084121444815034797422312103295359297822377134077549545477791813823542607184617108389097825964406170543546968567030745411634244134486308676327949177682923093183221341455482591367202823284396549001805653203960795517074496039006696990334199278212696767771835209083959545341866777944872740383733381985235884202840150981579594685874537989503257362809837592216229258598599123843993575573285028613155970362934249814178056461615863415338635077223269996508860870999964899373049307170967888740149746147542880387421250689212155876692242387434701120990859082164073576380817386959755176083877600277517253037133445654852635661720197563001580049790223419586738061442401502436288957503206533690825756785507020555105572381878574650371086308158185862815883054564662297694803970618265491385181326737485227188267917919091354407852685476254126683398240534022469989966652573155637645862251862823092085424412805997628505488913098331761884983352975136073772030571342739638126588567405013841074788943393996603591853934198416322617654857376671943132840050626295140357877264680649549355746326408186979718630218760025813995719923601345374229758918285167511358171472625828596940798518571870075823122317068134867930884899275181661399609753105295773584618525865211893339375771859916335112163441037910451845019023066893064178977808158101360449495409665363660370075881004450265734935127707426742578608784898185628869980851665713320835842613381142623855420315774246613108873106318111989880289722849790551075148403702290580483052731884959994156606537314021296702220821915862905952604040620011815269664910068587592655660567562963361434230232810747488395040380984981860056164646099819257616235478710913832967563761506732550860683433720438748186791668975746563456020002562889601191100980453350423842063824039434163502977688802779835087481178298349417211674919425601608685332435385951152061809031241698182079314615062073826097180458265687043623935757495737332781578904386011378078508110273049446611821957450170106059384336519458628360682108585130499820420578458577175933849015564447305834515291412561679970569657426139901681932056241927977282026714297258700193234337873153939403115411184101414292741703537542003698760608765500109345299007034032401334806388514095769557147190364152027721127070187421548123931953220997506553022646844227700020589045922742423904937051507367764629844971682121994198274794049092601715727439368569721862936007387077810797440975556627807371228030350048829843919546433753355787895064018998685060281902452191177018634505171087023903398550540704454189088472042376499749035038518949505897971286631644699407490959473411581934618336692169573605081585080837952036335619947691937965065016808710250735070825260046821242820434367245824478859256555487861614478717581068572356895150707602217433511627331709472765932413249132702425519391509083601346239612335001086614623850633127072987745618984384288764099836164964775714638573247333226653894523588365972955159905187411779288608760239306160016168434070611663449248395156319152882728822831375458678269830696691220130954815935450754923554167766876455212545681242936427474153815692219503331560151614492247512488957534835926226263545406704767033866410025277276800886383266629488582740369655329362236090572479794734434077704284318507901973469071141230364111729224929307731939309795452877412451183953480382210373644697046967493042810911797232448615413264031578430955396671061468083815548947146733652483679138566431084747848676243012018489329109615281108087617422779131629345494425395422727309645057976122885347393189600810965202090151104579377602529543130188938184010247010134929317443562883578609861545691161669857388024973756940558138630581099823372565164920155443216861690537054630176154809626620800633059320775897175589925862195462096455464624399535391743228225433267174308492508396461328929584567927365409119947616225155964704061297047759818551878441419948614013153859322060745185909608884280218943358691959604936409651570327527570641500776261323783648149005245481413195989296398441371781402764122087644989688629798910870164270169014007825748311598976330612951195680427485317886333041169767175063822135213839779138443325644288490872919067009802496281560626258636942322658490628628035057282983101266919109637258378149363774960594515216932644945188292639525772348420077356021656909077097264985642831778694777804964343991762549216500608626285329471055602670413384500507827390640287529864161287496473708235188892189612641279553536442286955430551308700009878557534223100547153412810957024870812654319123261956462149376527526356402127388765103883255007364899937167183280028398832319373301564123277185395654932422977953016534830128490677845037490891749347389015649588574802194996722621185874361039774946338633057887487405540005440439344888192044102134790034598411927024921557026873700970995205391930979319495883265922171508324621942300185974396706491149559411733728199869021311629886680267446443489233020607003821262841723679627307191405008084085703978151998148822390059948911946474438682533745889962375133378280532928272016815977970066488394482446332210928320504045983008943565954267256879714918703447338237767914829203283196838105907715727191903042365315650957464549643425328069510396558733549803850995143463506175361480050195045201350200180281506933241918267855737764414097080945745624854867704904368368717590918057269794010465019484853146726642978667687697789291431128505043098192949736165944259471754765135205245072597538577958372797702972231435199958499522344049394502115428867244188717409524554771867484911475031801773304689909317974472957035192387686405544278134169807249382219749124257510162187439772902147704638010731470653154201300583810458905006764557332998149945854655105526374914354195867992595981412218735238407957416123372264063860431988936249867649693592569592128495906254446474331759999685163660305216426770428154681777589339252115538590526823311608302751194384823861552852465010329467297198112105314125898165100120742688143577590825227466863206188376830450921784582526239594189673003640808624233657620979111641766331328852352062487922978959456450333733139422384778582717195412347860434376165241568717943562570215636666680088531006728947033079540804583324192188488870712275670333173939262509073556164513677064199539111948881240659821685787131385056850623094155206877987539740658484250135205615103489821873770245063583314243624807432542464195984647411575625441010389671576677263196442524931941806472423789334668561083789808830313571333157729435664956078125304917594015895146954965223118559669048559467607968190167266634650186182955669893965019614544401768162810604465068448139561667220729261210164692339016793399632833013163850830967942792934551268435760356901970523138364640961311774904600772840862214747547653221505518116489887879087780918009050706040061220010051271575991225725282523378026809030528461581739558198122397010092017202251606352922464781615533532275453264543087093320924631855976580561717446840450048285353396546862678852330044967795580761661801833668792312510460809773895565488962815089519622093675058841609752282328250433712970186608193748968699961301486924694482420723632912367052542145464162968910442981633373266871675946715392611950649224725627254543274193495995569590243279097174392258098103601486364409101491734183079646345064833303404765711827040276868271418084574998493392039317445402616663674646668754385093967129918067471909885312710726724428584870694307099756567949198418996425748884764622030325637751112534060087936904565779272035205921345924272965206683338510673615276261016026647772485083344719891986802656197236420847504962661607797092906844757798251795569758235084371746103310387911789239441630112634077535773520558040066982523191225570519133631407211349723226549151062961739050617857127509403623146700931176133132018631158730886798239298009805089491510788371194099750375473674305745187265414016446924576792185753680363289139664155342066705623272936001177781498886100830877849571709880858667023104043242526785955562077310543072298032125941107957349146684680220501816192150766649106862033378713826058987655210423668198670177861672671972374156917880001690656659046965316154923604061891820982414006103779407166342002735828911994182647812782659666207030384795881442790246669264032799404016800137293477301530941805070587421153284642203006550763966756168318897005152026656649929417382840327305940740147117478464839241225676523593418554066440983706083636457657081801664285044258224551650808864421212113914352453935225522162483791737330329812349528984098613273709957407786789349311975204237925022851375880436791854547836416773151821457226504640800104202100410766027807729152555503218182387221708112766208665317651926458452495269685376314437998340336947124447247796973890514941120010934140073794061859447165516612674930799374705772930521750426383798367668159183589049652163726492960837147204067428996276720315410211504333742057182854090136325721437592054640471894328548696883599785122262130812989581571391597464534806099601555877223193450760315411663112963843719400333736013305526352571490454327925190794007111504785378036370897340146753465517470747096935814912797188187854376797751675927822300312945518595042883902735494672667647506072643698761394806879080593531793001711000214417701504495496412454361656210150919997862972495905809191825255486358703529320142005857057855419217730505342687533799076038746689684283402648733290888881745453047194740939258407362058242849349024756883352446212456101562729065130618520732925434179252299417447855189995098959999877410951464170076989305620163502192692653166599093238118295411937545448509428621839424186218067457128099385258842631930670182098008050900019819621758458932516877698594110522845465835679362969619219080897536813210484518784516230623911878024604050824909336069998094776253792973597037759066145994638578378211017122446355845171941670344732162722443265914858595797823752976323442911242311368603724514438765801271594060878788638511089680883165505046309006148832545452819908256238805872042843941834687865142541377686054291079721004271658 diff --git a/src/pkg/compress/testdata/pi.txt b/src/pkg/compress/testdata/pi.txt index 58d8f3b6d..ca99bbc2a 100644 --- a/src/pkg/compress/testdata/pi.txt +++ b/src/pkg/compress/testdata/pi.txt @@ -1 +1 @@ -3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275900994657640789512694683983525957098258226205224894077267194782684826014769909026401363944374553050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382686838689427741559918559252459539594310499725246808459872736446958486538367362226260991246080512438843904512441365497627807977156914359977001296160894416948685558484063534220722258284886481584560285060168427394522674676788952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288797108931456691368672287489405601015033086179286809208747609178249385890097149096759852613655497818931297848216829989487226588048575640142704775551323796414515237462343645428584447952658678210511413547357395231134271661021359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435064302184531910484810053706146806749192781911979399520614196634287544406437451237181921799983910159195618146751426912397489409071864942319615679452080951465502252316038819301420937621378559566389377870830390697920773467221825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539057962685610055081066587969981635747363840525714591028970641401109712062804390397595156771577004203378699360072305587631763594218731251471205329281918261861258673215791984148488291644706095752706957220917567116722910981690915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398315019701651511685171437657618351556508849099898599823873455283316355076479185358932261854896321329330898570642046752590709154814165498594616371802709819943099244889575712828905923233260972997120844335732654893823911932597463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100449293215160842444859637669838952286847831235526582131449576857262433441893039686426243410773226978028073189154411010446823252716201052652272111660396665573092547110557853763466820653109896526918620564769312570586356620185581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318586769751456614068007002378776591344017127494704205622305389945613140711270004078547332699390814546646458807972708266830634328587856983052358089330657574067954571637752542021149557615814002501262285941302164715509792592309907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111790429782856475032031986915140287080859904801094121472213179476477726224142548545403321571853061422881375850430633217518297986622371721591607716692547487389866549494501146540628433663937900397692656721463853067360965712091807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862947265473642523081770367515906735023507283540567040386743513622224771589150495309844489333096340878076932599397805419341447377441842631298608099888687413260472156951623965864573021631598193195167353812974167729478672422924654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001593776471651228935786015881617557829735233446042815126272037343146531977774160319906655418763979293344195215413418994854447345673831624993419131814809277771038638773431772075456545322077709212019051660962804909263601975988281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267945612753181340783303362542327839449753824372058353114771199260638133467768796959703098339130771098704085913374641442822772634659470474587847787201927715280731767907707157213444730605700733492436931138350493163128404251219256517980694113528013147013047816437885185290928545201165839341965621349143415956258658655705526904965209858033850722426482939728584783163057777560688876446248246857926039535277348030480290058760758251047470916439613626760449256274204208320856611906254543372131535958450687724602901618766795240616342522577195429162991930645537799140373404328752628889639958794757291746426357455254079091451357111369410911939325191076020825202618798531887705842972591677813149699009019211697173727847684726860849003377024242916513005005168323364350389517029893922334517220138128069650117844087451960121228599371623130171144484640903890644954440061986907548516026327505298349187407866808818338510228334508504860825039302133219715518430635455007668282949304137765527939751754613953984683393638304746119966538581538420568533862186725233402830871123282789212507712629463229563989898935821167456270102183564622013496715188190973038119800497340723961036854066431939509790190699639552453005450580685501956730229219139339185680344903982059551002263535361920419947455385938102343955449597783779023742161727111723643435439478221818528624085140066604433258885698670543154706965747458550332323342107301545940516553790686627333799585115625784322988273723198987571415957811196358330059408730681216028764962867446047746491599505497374256269010490377819868359381465741268049256487985561453723478673303904688383436346553794986419270563872931748723320837601123029911367938627089438799362016295154133714248928307220126901475466847653576164773794675200490757155527819653621323926406160136358155907422020203187277605277219005561484255518792530343513984425322341576233610642506390497500865627109535919465897514131034822769306247435363256916078154781811528436679570611086153315044521274739245449454236828860613408414863776700961207151249140430272538607648236341433462351897576645216413767969031495019108575984423919862916421939949072362346468441173940326591840443780513338945257423995082965912285085558215725031071257012668302402929525220118726767562204154205161841634847565169998116141010029960783869092916030288400269104140792886215078424516709087000699282120660418371806535567252532567532861291042487761825829765157959847035622262934860034158722980534989650226291748788202734209222245339856264766914905562842503912757710284027998066365825488926488025456610172967026640765590429099456815065265305371829412703369313785178609040708667114965583434347693385781711386455873678123014587687126603489139095620099393610310291616152881384379099042317473363948045759314931405297634757481193567091101377517210080315590248530906692037671922033229094334676851422144773793937517034436619910403375111735471918550464490263655128162288244625759163330391072253837421821408835086573917715096828874782656995995744906617583441375223970968340800535598491754173818839994469748676265516582765848358845314277568790029095170283529716344562129640435231176006651012412006597558512761785838292041974844236080071930457618932349229279650198751872127267507981255470958904556357921221033346697499235630254947802490114195212382815309114079073860251522742995818072471625916685451333123948049470791191532673430282441860414263639548000448002670496248201792896476697583183271314251702969234889627668440323260927524960357996469256504936818360900323809293459588970695365349406034021665443755890045632882250545255640564482465151875471196218443965825337543885690941130315095261793780029741207665147939425902989695946995565761218656196733786236256125216320862869222103274889218654364802296780705765615144632046927906821207388377814233562823608963208068222468012248261177185896381409183903673672220888321513755600372798394004152970028783076670944474560134556417254370906979396122571429894671543578468788614445812314593571984922528471605049221242470141214780573455105008019086996033027634787081081754501193071412233908663938339529425786905076431006383519834389341596131854347546495569781038293097164651438407007073604112373599843452251610507027056235266012764848308407611830130527932054274628654036036745328651057065874882256981579367897669742205750596834408697350201410206723585020072452256326513410559240190274216248439140359989535394590944070469120914093870012645600162374288021092764579310657922955249887275846101264836999892256959688159205600101655256375678 +3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275900994657640789512694683983525957098258226205224894077267194782684826014769909026401363944374553050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382686838689427741559918559252459539594310499725246808459872736446958486538367362226260991246080512438843904512441365497627807977156914359977001296160894416948685558484063534220722258284886481584560285060168427394522674676788952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288797108931456691368672287489405601015033086179286809208747609178249385890097149096759852613655497818931297848216829989487226588048575640142704775551323796414515237462343645428584447952658678210511413547357395231134271661021359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435064302184531910484810053706146806749192781911979399520614196634287544406437451237181921799983910159195618146751426912397489409071864942319615679452080951465502252316038819301420937621378559566389377870830390697920773467221825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539057962685610055081066587969981635747363840525714591028970641401109712062804390397595156771577004203378699360072305587631763594218731251471205329281918261861258673215791984148488291644706095752706957220917567116722910981690915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398315019701651511685171437657618351556508849099898599823873455283316355076479185358932261854896321329330898570642046752590709154814165498594616371802709819943099244889575712828905923233260972997120844335732654893823911932597463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100449293215160842444859637669838952286847831235526582131449576857262433441893039686426243410773226978028073189154411010446823252716201052652272111660396665573092547110557853763466820653109896526918620564769312570586356620185581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318586769751456614068007002378776591344017127494704205622305389945613140711270004078547332699390814546646458807972708266830634328587856983052358089330657574067954571637752542021149557615814002501262285941302164715509792592309907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111790429782856475032031986915140287080859904801094121472213179476477726224142548545403321571853061422881375850430633217518297986622371721591607716692547487389866549494501146540628433663937900397692656721463853067360965712091807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862947265473642523081770367515906735023507283540567040386743513622224771589150495309844489333096340878076932599397805419341447377441842631298608099888687413260472156951623965864573021631598193195167353812974167729478672422924654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001593776471651228935786015881617557829735233446042815126272037343146531977774160319906655418763979293344195215413418994854447345673831624993419131814809277771038638773431772075456545322077709212019051660962804909263601975988281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267945612753181340783303362542327839449753824372058353114771199260638133467768796959703098339130771098704085913374641442822772634659470474587847787201927715280731767907707157213444730605700733492436931138350493163128404251219256517980694113528013147013047816437885185290928545201165839341965621349143415956258658655705526904965209858033850722426482939728584783163057777560688876446248246857926039535277348030480290058760758251047470916439613626760449256274204208320856611906254543372131535958450687724602901618766795240616342522577195429162991930645537799140373404328752628889639958794757291746426357455254079091451357111369410911939325191076020825202618798531887705842972591677813149699009019211697173727847684726860849003377024242916513005005168323364350389517029893922334517220138128069650117844087451960121228599371623130171144484640903890644954440061986907548516026327505298349187407866808818338510228334508504860825039302133219715518430635455007668282949304137765527939751754613953984683393638304746119966538581538420568533862186725233402830871123282789212507712629463229563989898935821167456270102183564622013496715188190973038119800497340723961036854066431939509790190699639552453005450580685501956730229219139339185680344903982059551002263535361920419947455385938102343955449597783779023742161727111723643435439478221818528624085140066604433258885698670543154706965747458550332323342107301545940516553790686627333799585115625784322988273723198987571415957811196358330059408730681216028764962867446047746491599505497374256269010490377819868359381465741268049256487985561453723478673303904688383436346553794986419270563872931748723320837601123029911367938627089438799362016295154133714248928307220126901475466847653576164773794675200490757155527819653621323926406160136358155907422020203187277605277219005561484255518792530343513984425322341576233610642506390497500865627109535919465897514131034822769306247435363256916078154781811528436679570611086153315044521274739245449454236828860613408414863776700961207151249140430272538607648236341433462351897576645216413767969031495019108575984423919862916421939949072362346468441173940326591840443780513338945257423995082965912285085558215725031071257012668302402929525220118726767562204154205161841634847565169998116141010029960783869092916030288400269104140792886215078424516709087000699282120660418371806535567252532567532861291042487761825829765157959847035622262934860034158722980534989650226291748788202734209222245339856264766914905562842503912757710284027998066365825488926488025456610172967026640765590429099456815065265305371829412703369313785178609040708667114965583434347693385781711386455873678123014587687126603489139095620099393610310291616152881384379099042317473363948045759314931405297634757481193567091101377517210080315590248530906692037671922033229094334676851422144773793937517034436619910403375111735471918550464490263655128162288244625759163330391072253837421821408835086573917715096828874782656995995744906617583441375223970968340800535598491754173818839994469748676265516582765848358845314277568790029095170283529716344562129640435231176006651012412006597558512761785838292041974844236080071930457618932349229279650198751872127267507981255470958904556357921221033346697499235630254947802490114195212382815309114079073860251522742995818072471625916685451333123948049470791191532673430282441860414263639548000448002670496248201792896476697583183271314251702969234889627668440323260927524960357996469256504936818360900323809293459588970695365349406034021665443755890045632882250545255640564482465151875471196218443965825337543885690941130315095261793780029741207665147939425902989695946995565761218656196733786236256125216320862869222103274889218654364802296780705765615144632046927906821207388377814233562823608963208068222468012248261177185896381409183903673672220888321513755600372798394004152970028783076670944474560134556417254370906979396122571429894671543578468788614445812314593571984922528471605049221242470141214780573455105008019086996033027634787081081754501193071412233908663938339529425786905076431006383519834389341596131854347546495569781038293097164651438407007073604112373599843452251610507027056235266012764848308407611830130527932054274628654036036745328651057065874882256981579367897669742205750596834408697350201410206723585020072452256326513410559240190274216248439140359989535394590944070469120914093870012645600162374288021092764579310657922955249887275846101264836999892256959688159205600101655256375678566722796619885782794848855834397518744545512965634434803966420557982936804352202770984294232533022576341807039476994159791594530069752148293366555661567873640053666564165473217043903521329543529169414599041608753201868379370234888689479151071637852902345292440773659495630510074210871426134974595615138498713757047101787957310422969066670214498637464595280824369445789772330048764765241339075920434019634039114732023380715095222010682563427471646024335440051521266932493419673977041595683753555166730273900749729736354964533288869844061196496162773449518273695588220757355176651589855190986665393549481068873206859907540792342402300925900701731960362254756478940647548346647760411463233905651343306844953979070903023460461470961696886885014083470405460742958699138296682468185710318879065287036650832431974404771855678934823089431068287027228097362480939962706074726455399253994428081137369433887294063079261595995462624629707062594845569034711972996409089418059534393251236235508134949004364278527138315912568989295196427287573946914272534366941532361004537304881985517065941217352462589548730167600298865925786628561249665523533829428785425340483083307016537228563559152534784459818313411290019992059813522051173365856407826484942764411376393866924803118364453698589175442647399882284621844900877769776312795722672655562596282542765318300134070922334365779160128093179401718598599933849235495640057099558561134980252499066984233017350358044081168552653117099570899427328709258487894436460050410892266917835258707859512983441729535195378855345737426085902908176515578039059464087350612322611200937310804854852635722825768203416050484662775045003126200800799804925485346941469775164932709504934639382432227188515974054702148289711177792376122578873477188196825462981268685817050740272550263329044976277894423621674119186269439650671515779586756482399391760426017633870454990176143641204692182370764887834196896861181558158736062938603810171215855272668300823834046564758804051380801633638874216371406435495561868964112282140753302655100424104896783528588290243670904887118190909494533144218287661810310073547705498159680772009474696134360928614849417850171807793068108546900094458995279424398139213505586422196483491512639012803832001097738680662877923971801461343244572640097374257007359210031541508936793008169980536520276007277496745840028362405346037263416554259027601834840306811381855105979705664007509426087885735796037324514146786703688098806097164258497595138069309449401515422221943291302173912538355915031003330325111749156969174502714943315155885403922164097229101129035521815762823283182342548326111912800928252561902052630163911477247331485739107775874425387611746578671169414776421441111263583553871361011023267987756410246824032264834641766369806637857681349204530224081972785647198396308781543221166912246415911776732253264335686146186545222681268872684459684424161078540167681420808850280054143613146230821025941737562389942075713627516745731891894562835257044133543758575342698699472547031656613991999682628247270641336222178923903176085428943733935618891651250424404008952719837873864805847268954624388234375178852014395600571048119498842390606136957342315590796703461491434478863604103182350736502778590897578272731305048893989009923913503373250855982655867089242612429473670193907727130706869170926462548423240748550366080136046689511840093668609546325002145852930950000907151058236267293264537382104938724996699339424685516483261134146110680267446637334375340764294026682973865220935701626384648528514903629320199199688285171839536691345222444708045923966028171565515656661113598231122506289058549145097157553900243931535190902107119457300243880176615035270862602537881797519478061013715004489917210022201335013106016391541589578037117792775225978742891917915522417189585361680594741234193398420218745649256443462392531953135103311476394911995072858430658361935369329699289837914941939406085724863968836903265564364216644257607914710869984315733749648835292769328220762947282381537409961545598798259891093717126218283025848112389011968221429457667580718653806506487026133892822994972574530332838963818439447707794022843598834100358385423897354243956475556840952248445541392394100016207693636846776413017819659379971557468541946334893748439129742391433659360410035234377706588867781139498616478747140793263858738624732889645643598774667638479466504074111825658378878454858148962961273998413442726086061872455452360643153710112746809778704464094758280348769758948328241239292960582948619196670918958089833201210318430340128495116203534280144127617285830243559830032042024512072872535581195840149180969253395075778400067465526031446167050827682772223534191102634163157147406123850425845988419907611287258059113935689601431668283176323567325417073420817332230462987992804908514094790368878687894930546955703072619009502076433493359106024545086453628935456862958531315337183868265617862273637169757741830239860065914816164049449650117321313895747062088474802365371031150898427992754426853277974311395143574172219759799359685252285745263796289612691572357986620573408375766873884266405990993505000813375432454635967504844235284874701443545419576258473564216198134073468541117668831186544893776979566517279662326714810338643913751865946730024434500544995399742372328712494834706044063471606325830649829795510109541836235030309453097335834462839476304775645015008507578949548931393944899216125525597701436858943585877526379625597081677643800125436502371412783467926101995585224717220177723700417808419423948725406801556035998390548985723546745642390585850216719031395262944554391316631345308939062046784387785054239390524731362012947691874975191011472315289326772533918146607300089027768963114810902209724520759167297007850580717186381054967973100167870850694207092232908070383263453452038027860990556900134137182368370991949516489600755049341267876436746384902063964019766685592335654639138363185745698147196210841080961884605456039038455343729141446513474940784884423772175154334260306698831768331001133108690421939031080143784334151370924353013677631084913516156422698475074303297167469640666531527035325467112667522460551199581831963763707617991919203579582007595605302346267757943936307463056901080114942714100939136913810725813781357894005599500183542511841721360557275221035268037357265279224173736057511278872181908449006178013889710770822931002797665935838758909395688148560263224393726562472776037890814458837855019702843779362407825052704875816470324581290878395232453237896029841669225489649715606981192186584926770403956481278102179913217416305810554598801300484562997651121241536374515005635070127815926714241342103301566165356024733807843028655257222753049998837015348793008062601809623815161366903341111386538510919367393835229345888322550887064507539473952043968079067086806445096986548801682874343786126453815834280753061845485903798217994599681154419742536344399602902510015888272164745006820704193761584547123183460072629339550548239557137256840232268213012476794522644820910235647752723082081063518899152692889108455571126603965034397896278250016110153235160519655904211844949907789992007329476905868577878720982901352956613978884860509786085957017731298155314951681467176959760994210036183559138777817698458758104466283998806006162298486169353373865787735983361613384133853684211978938900185295691967804554482858483701170967212535338758621582310133103877668272115726949518179589754693992642197915523385766231676275475703546994148929041301863861194391962838870543677743224276809132365449485366768000001065262485473055861598999140170769838548318875014293890899506854530765116803337322265175662207526951791442252808165171667766727930354851542040238174608923283917032754257508676551178593950027933895920576682789677644531840404185540104351348389531201326378369283580827193783126549617459970567450718332065034556644034490453627560011250184335607361222765949278393706478426456763388188075656121689605041611390390639601620221536849410926053876887148379895599991120991646464411918568277004574243434021672276445589330127781586869525069499364610175685060167145354315814801054588605645501332037586454858403240298717093480910556211671546848477803944756979804263180991756422809873998766973237695737015808068229045992123661689025962730430679316531149401764737693873514093361833216142802149763399189835484875625298752423873077559555955465196394401821840998412489826236737714672260616336432964063357281070788758164043814850188411431885988276944901193212968271588841338694346828590066640806314077757725705630729400492940302420498416565479736705485580445865720227637840466823379852827105784319753541795011347273625774080213476826045022851579795797647467022840999561601569108903845824502679265942055503958792298185264800706837650418365620945554346135134152570065974881916341359556719649654032187271602648593049039787489589066127250794828276938953521753621850796297785146188432719223223810158744450528665238022532843891375273845892384422535472653098171578447834215822327020690287232330053862163479885094695472004795231120150432932266282727632177908840087861480221475376578105819702226309717495072127248479478169572961423658595782090830733233560348465318730293026659645013718375428897557971449924654038681799213893469244741985097334626793321072686870768062639919361965044099542167627840914669856925715074315740793805323925239477557441591845821562518192155233709607483329234921034514626437449805596103307994145347784574699992128599999399612281615219314888769388022281083001986016549416542616968586788372609587745676182507275992950893180521872924610867639958916145855058397274209809097817293239301067663868240401113040247007350857828724627134946368531815469690466968693925472519413992914652423857762550047485295476814795467007050347999588867695016124972282040303995463278830695976249361510102436555352230690612949388599015734661023712235478911292547696176005047974928060721268039226911027772261025441492215765045081206771735712027180242968106203776578837166909109418074487814049075517820385653909910477594141321543284406250301802757169650820964273484146957263978842560084531214065935809041271135920041975985136254796160632288736181367373244506079244117639975974619383584574915988097667447093006546342423460634237474666080431701260052055928493695941434081468529815053947178900451835755154125223590590687264878635752541911288877371766374860276606349603536794702692322971868327717393236192007774522126247518698334951510198642698878471719396649769070825217423365662725928440620430214113719922785269984698847702323823840055655517889087661360130477098438611687052310553149162517283732728676007248172987637569816335415074608838663640693470437206688651275688266149730788657015685016918647488541679154596507234287730699853713904300266530783987763850323818215535597323530686043010675760838908627049841888595138091030423595782495143988590113185835840667472370297149785084145853085781339156270760356390763947311455495832266945702494139831634332378975955680856836297253867913275055542524491943589128405045226953812179131914513500993846311774017971512283785460116035955402864405902496466930707769055481028850208085800878115773817191741776017330738554758006056014337743299012728677253043182519757916792969965041460706645712588834697979642931622965520168797300035646304579308840327480771811555330909887025505207680463034608658165394876951960044084820659673794731680864156456505300498816164905788311543454850526600698230931577765003780704661264706021457505793270962047825615247145918965223608396645624105195510522357239739512881816405978591427914816542632892004281609136937773722299983327082082969955737727375667615527113922588055201898876201141680054687365580633471603734291703907986396522961312801782679717289822936070288069087768660593252746378405397691848082041021944719713869256084162451123980620113184541244782050110798760717155683154078865439041210873032402010685341947230476666721749869868547076781205124736792479193150856444775379853799732234456122785843296846647513336573692387201464723679427870042503255589926884349592876124007558756946413705625140011797133166207153715436006876477318675587148783989081074295309410605969443158477539700943988394914432353668539209946879645066533985738887866147629443414010498889931600512076781035886116602029611936396821349607501116498327856353161451684576956871090029997698412632665023477167286573785790857466460772283415403114415294188047825438761770790430001566986776795760909966936075594965152736349811896413043311662774712338817406037317439705406703109676765748695358789670031925866259410510533584384656023391796749267844763708474978333655579007384191473198862713525954625181604342253729962863267496824058060296421146386436864224724887283434170441573482481833301640566959668866769563491416328426414974533349999480002669987588815935073578151958899005395120853510357261373640343675347141048360175464883004078464167452167371904831096767113443494819262681110739948250607394950735031690197318521195526356325843390998224986240670310768318446607291248747540316179699411397387765899868554170318847788675929026070043212666179192235209382278788809886335991160819235355570464634911320859189796132791319756490976000139962344455350143464268604644958624769094347048293294140411146540923988344435159133201077394411184074107684981066347241048239358274019449356651610884631256785297769734684303061462418035852933159734583038455410337010916767763742762102137013548544509263071901147318485749233181672072137279355679528443925481560913728128406333039373562420016045664557414588166052166608738748047243391212955877763906969037078828527753894052460758496231574369171131761347838827194168606625721036851321566478001476752310393578606896111259960281839309548709059073861351914591819510297327875571049729011487171897180046961697770017913919613791417162707018958469214343696762927459109940060084983568425201915593703701011049747339493877885989417433031785348707603221982970579751191440510994235883034546353492349826883624043327267415540301619505680654180939409982020609994140216890900708213307230896621197755306659188141191577836272927461561857103721724710095214236964830864102592887457999322374955191221951903424452307535133806856807354464995127203174487195403976107308060269906258076020292731455252078079914184290638844373499681458273372072663917670201183004648190002413083508846584152148991276106513741539435657211390328574918769094413702090517031487773461652879848235338297260136110984514841823808120540996125274580881099486972216128524897425555516076371675054896173016809613803811914361143992106380050832140987604599309324851025168294467260666138151745712559754953580239983146982203613380828499356705575524712902745397762140493182014658008021566536067765508783804304134310591804606800834591136640834887408005741272586704792258319127415739080914383138456424150940849133918096840251163991936853225557338966953749026620923261318855891580832455571948453875628786128859004106006073746501402627824027346962528217174941582331749239683530136178653673760642166778137739951006589528877427662636841830680190804609849809469763667335662282915132352788806157768278159588669180238940333076441912403412022316368577860357276941541778826435238131905028087018575047046312933353757285386605888904583111450773942935201994321971171642235005644042979892081594307167019857469273848653833436145794634175922573898588001698014757420542995801242958105456510831046297282937584161162532562516572498078492099897990620035936509934721582965174135798491047111660791587436986541222348341887722929446335178653856731962559852026072947674072616767145573649812105677716893484917660771705277187601199908144113058645577910525684304811440261938402322470939249802933550731845890355397133088446174107959162511714864874468611247605428673436709046678468670274091881014249711149657817724279347070216688295610877794405048437528443375108828264771978540006509704033021862556147332117771174413350281608840351781452541964320309576018694649088681545285621346988355444560249556668436602922195124830910605377201980218310103270417838665447181260397190688462370857518080035327047185659499476124248110999288679158969049563947624608424065930948621507690314987020673533848349550836366017848771060809804269247132410009464014373603265645184566792456669551001502298330798496079949882497061723674493612262229617908143114146609412341593593095854079139087208322733549572080757165171876599449856937956238755516175754380917805280294642004472153962807463602113294255916002570735628126387331060058910652457080244749375431841494014821199962764531068006631183823761639663180931444671298615527598201451410275600689297502463040173514891945763607893528555053173314164570504996443890936308438744847839616840518452732884032345202470568516465716477139323775517294795126132398229602394548579754586517458787713318138752959809412174227300352296508089177705068259248822322154938048371454781647213976820963320508305647920482085920475499857320388876391601995240918938945576768749730856955958010659526503036266159750662225084067428898265907510637563569968211510949669744580547288693631020367823250182323708459790111548472087618212477813266330412076216587312970811230758159821248639807212407868878114501655825136178903070860870198975889807456643955157415363193191981070575336633738038272152798849350397480015890519420879711308051233933221903466249917169150948541401871060354603794643379005890957721180804465743962806186717861017156740967662080295766577051291209907944304632892947306159510430902221439371849560634056189342513057268291465783293340524635028929175470872564842600349629611654138230077313327298305001602567240141851520418907011542885799208121984493156999059182011819733500126187728036812481995877070207532406361259313438595542547781961142935163561223496661522614735399674051584998603552953329245752388810136202347624669055816438967863097627365504724348643071218494373485300606387644566272186661701238127715621379746149861328744117714552444708997144522885662942440230184791205478498574521634696448973892062401943518310088283480249249085403077863875165911302873958787098100772718271874529013972836614842142871705531796543076504534324600536361472618180969976933486264077435199928686323835088756683595097265574815431940195576850437248001020413749831872259677387154958399718444907279141965845930083942637020875635398216962055324803212267498911402678528599673405242031091797899905718821949391320753431707980023736590985375520238911643467185582906853711897952626234492483392496342449714656846591248918556629589329909035239233333647435203707701010843880032907598342170185542283861617210417603011645918780539367447472059985023582891833692922337323999480437108419659473162654825748099482509991833006976569367159689364493348864744213500840700660883597235039532340179582557036016936990988671132109798897070517280755855191269930673099250704070245568507786790694766126298082251633136399521170984528092630375922426742575599892892783704744452189363203489415521044597261883800300677617931381399162058062701651024458869247649246891924612125310275731390840470007143561362316992371694848132554200914530410371354532966206392105479824392125172540132314902740585892063217589494345489068463993137570910346332714153162232805522972979538018801628590735729554162788676498274186164218789885741071649069191851162815285486794173638906653885764229158342500673612453849160674137340173572779956341043326883569507814931378007362354180070619180267328551191942676091221035987469241172837493126163395001239599240508454375698507957046222664619000103500490183034153545842833764378111988556318777792537201166718539541835984438305203762819440761594106820716970302285152250573126093046898423433152732131361216582808075212631547730604423774753505952287174402666389148817173086436111389069420279088143119448799417154042103412190847094080254023932942945493878640230512927119097513536000921971105412096683111516328705423028470073120658032626417116165957613272351566662536672718998534199895236884830999302757419916463841427077988708874229277053891227172486322028898425125287217826030500994510824783572905691988555467886079462805371227042466543192145281760741482403827835829719301017888345674167811398954750448339314689630763396657226727043393216745421824557062524797219978668542798977992339579057581890622525473582205236424850783407110144980478726691990186438822932305382318559732869780922253529591017341407334884761005564018242392192695062083183814546983923664613639891012102177095976704908305081854704194664371312299692358895384930136356576186106062228705599423371631021278457446463989738188566746260879482018647487672727222062676465338099801966883680994159075776852639865146253336312450536402610569605513183813174261184420189088853196356986962795036738424313011331753305329802016688817481342988681585577810343231753064784983210629718425184385534427620128234570716988530518326179641178579608888150329602290705614476220915094739035946646916235396809201394578175891088931992112260073928149169481615273842736264298098234063200244024495894456129167049508235812487391799648641133480324757775219708932772262349486015046652681439877051615317026696929704928316285504212898146706195331970269507214378230476875280287354126166391708245925170010714180854800636923259462019002278087409859771921805158532147392653251559035410209284665925299914353791825314545290598415817637058927906909896911164381187809435371521332261443625314490127454772695739393481546916311624928873574718824071503995009446731954316193855485207665738825139639163576723151005556037263394867208207808653734942440115799667507360711159351331959197120948964717553024531364770942094635696982226673775209945168450643623824211853534887989395673187806606107885440005508276570305587448541805778891719207881423351138662929667179643468760077047999537883387870348718021842437342112273940255717690819603092018240188427057046092622564178375265263358324240661253311529423457965569502506810018310900411245379015332966156970522379210325706937051090830789479999004999395322153622748476603613677697978567386584670936679588583788795625946464891376652199588286933801836011932368578558558195556042156250883650203322024513762158204618106705195330653060606501054887167245377942831338871631395596905832083416898476065607118347136218123246227258841990286142087284956879639325464285343075301105285713829643709990356948885285190402956047346131138263878897551788560424998748316382804046848618938189590542039889872650697620201995548412650005394428203930127481638158530396439925470201672759328574366661644110962566337305409219519675148328734808957477775278344221091073111351828046036347198185655572957144747682552857863349342858423118749440003229690697758315903858039353521358860079600342097547392296733310649395601812237812854584317605561733861126734780745850676063048229409653041118306671081893031108871728167519579675347188537229309616143204006381322465841111157758358581135018569047815368938137718472814751998350504781297718599084707621974605887423256995828892535041937958260616211842368768511418316068315867994601652057740529423053601780313357263267054790338401257305912339601880137825421927094767337191987287385248057421248921183470876629667207272325650565129333126059505777727542471241648312832982072361750574673870128209575544305968395555686861188397135522084452852640081252027665557677495969626612604565245684086139238265768583384698499778726706555191854468698469478495734622606294219624557085371272776523098955450193037732166649182578154677292005212667143463209637891852323215018976126034373684067194193037746880999296877582441047878123266253181845960453853543839114496775312864260925211537673258866722604042523491087026958099647595805794663973419064010036361904042033113579336542426303561457009011244800890020801478056603710154122328891465722393145076071670643556827437743965789067972687438473076346451677562103098604092717090951280863090297385044527182892749689212106670081648583395537735919136950153162018908887484210798706899114804669270650940762046502772528650728905328548561433160812693005693785417861096969202538865034577183176686885923681488475276498468821949739729707737187188400414323127636504814531122850990020742409255859252926103021067368154347015252348786351643976235860419194129697690405264832347009911154242601273438022089331096686367898694977994001260164227609260823493041180643829138347354679725399262338791582998486459271734059225620749105308531537182911681637219395188700957788181586850464507699343940987433514431626330317247747486897918209239480833143970840673084079589358108966564775859905563769525232653614424780230826811831037735887089240613031336477371011628214614661679404090518615260360092521947218890918107335871964142144478654899528582343947050079830388538860831035719306002771194558021911942899922722353458707566246926177663178855144350218287026685610665003531050216318206017609217984684936863161293727951873078972637353717150256378733579771808184878458866504335824377004147710414934927438457587107159731559439426412570270965125108115548247939403597681188117282472158250109496096625393395380922195591918188552678062149923172763163218339896938075616855911752998450132067129392404144593862398809381240452191484831646210147389182510109096773869066404158973610476436500068077105656718486281496371118832192445663945814491486165500495676982690308911185687986929470513524816091743243015383684707292898982846022237301452655679898627767968091469798378268764311598832109043715611299766521539635464420869197567370005738764978437686287681792497469438427465256316323005551304174227341646455127812784577772457520386543754282825671412885834544435132562054464241011037955464190581168623059644769587054072141985212106734332410756767575818456990693046047522770167005684543969234041711089888993416350585157887353430815520811772071880379104046983069578685473937656433631979786803671873079693924236321448450354776315670255390065423117920153464977929066241508328858395290542637687668968805033317227800185885069736232403894700471897619347344308437443759925034178807972235859134245813144049847701732361694719765715353197754997162785663119046912609182591249890367654176979903623755286526375733763526969344354400473067198868901968147428767790866979688522501636949856730217523132529265375896415171479559538784278499866456302878831962099830494519874396369070682762657485810439112232618794059941554063270131989895703761105323606298674803779153767511583043208498720920280929752649812569163425000522908872646925284666104665392171482080130502298052637836426959733707053922789153510568883938113249757071331029504430346715989448786847116438328050692507766274500122003526203709466023414648998390252588830148678162196775194583167718762757200505439794412459900771152051546199305098386982542846407255540927403132571632640792934183342147090412542533523248021932277075355546795871638358750181593387174236061551171013123525633485820365146141870049205704372018261733194715700867578539336078622739558185797587258744102542077105475361294047460100094095444959662881486915903899071865980563617137692227290764197755177720104276496949611056220592502420217704269622154958726453989227697660310524980855759471631075870133208861463266412591148633881220284440694169488261529577625325019870359870674380469821942056381255833436421949232275937221289056420943082352544084110864545369404969271494003319782861318186188811118408257865928757426384450059944229568586460481033015388911499486935436030221810943466764000022362550573631294626296096198760564259963946138692330837196265954739234624134597795748524647837980795693198650815977675350553918991151335252298736112779182748542008689539658359421963331502869561192012298889887006079992795411188269023078913107603617634779489432032102773359416908650071932804017163840644987871753756781185321328408216571107549528294974936214608215583205687232185574065161096274874375098092230211609982633033915469494644491004515280925089745074896760324090768983652940657920198315265410658136823791984090645712468948470209357761193139980246813405200394781949866202624008902150166163813538381515037735022966074627952910384068685569070157516624192987244482719429331004854824454580718897633003232525821581280327467962002814762431828622171054352898348208273451680186131719593324711074662228508710666117703465352839577625997744672185715816126411143271794347885990892808486694914139097716736900277758502686646540565950394867841110790116104008572744562938425494167594605487117235946429105850909950214958793112196135908315882620682332156153086833730838173279328196983875087083483880463884784418840031847126974543709373298362402875197920802321878744882872843727378017827008058782410749357514889978911739746129320351081432703251409030487462262942344327571260086642508333187688650756429271605525289544921537651751492196367181049435317858383453865255656640657251363575064353236508936790431702597878177190314867963840828810209461490079715137717099061954969640070867667102330048672631475510537231757114322317411411680622864206388906210192355223546711662137499693269321737043105987225039456574924616978260970253359475020913836673772894438696400028110344026084712899000746807764844088711341352503367877316797709372778682166117865344231732264637847697875144332095340001650692130546476890985050203015044880834261845208730530973189492916425322933612431514306578264070283898409841602950309241897120971601649265613413433422298827909921786042679812457285345801338260995877178113102167340256562744007296834066198480676615805021691833723680399027931606420436812079900316264449146190219458229690992122788553948783538305646864881655562294315673128274390826450611628942803501661336697824051770155219626522725455850738640585299830379180350432876703809252167907571204061237596327685674845079151147313440001832570344920909712435809447900462494313455028900680648704293534037436032625820535790118395649089354345101342969617545249573960621490288728932792520696535386396443225388327522499605986974759882329916263545973324445163755334377492928990581175786355555626937426910947117002165411718219750519831787137106051063795558588905568852887989084750915764639074693619881507814685262133252473837651192990156109189777922008705793396463827490680698769168197492365624226087154176100430608904377976678519661891404144925270480881971498801542057787006521594009289777601330756847966992955433656139847738060394368895887646054983871478968482805384701730871117761159663505039979343869339119789887109156541709133082607647406305711411098839388095481437828474528838368079418884342666222070438722887413947801017721392281911992365405516395893474263953824829609036900288359327745855060801317988407162446563997948275783650195514221551339281978226984278638391679715091262410548725700924070045488485692950448110738087996547481568913935380943474556972128919827177020766613602489581468119133614121258783895577357194986317210844398901423948496659251731388171602663261931065366535041473070804414939169363262373767777095850313255990095762731957308648042467701212327020533742667053142448208168130306397378736642483672539837487690980602182785786216512738563513290148903509883270617258932575363993979055729175160097615459044771692265806315111028038436017374742152476085152099016158582312571590733421736576267142390478279587281505095633092802668458937649649770232973641319060982740633531089792464242134583740901169391964250459128813403498810635400887596820054408364386516617880557608956896727531538081942077332597917278437625661184319891025007491829086475149794003160703845549465385946027452447466812314687943441610993338908992638411847425257044572517459325738989565185716575961481266020310797628254165590506042479114016957900338356574869252800743025623419498286467914476322774005529460903940177536335655471931000175430047504719144899841040015867946179241610016454716551337074073950260442769538553834397550548871099785205401175169747581344926079433689543783221172450687344231989878844128542064742809735625807066983106979935260693392135685881391214807354728463227784908087002467776303605551232386656295178853719673034634701222939581606792509153217489030840886516061119011498443412350124646928028805996134283511884715449771278473361766285062169778717743824362565711779450064477718370221999106695021656757644044997940765037999954845002710665987813603802314126836905783190460792765297277694043613023051787080546511542469395265127101052927070306673024447125973939950514628404767431363739978259184541176413327906460636584152927019030276017339474866960348694976541752429306040727005059039503148522921392575594845078867977925253931765156416197168443524369794447355964260633391055126826061595726217036698506473281266724521989060549880280782881429796336696744124805982192146339565745722102298677599746738126069367069134081559412016115960190237753525556300606247983261249881288192937343476862689219239777833910733106588256813777172328315329082525092733047850724977139448333892552081175608452966590553940965568541706001179857293813998258319293679100391844099286575605993598910002969864460974714718470101531283762631146774209145574041815908800064943237855839308530828305476076799524357391631221886057549673832243195650655460852881201902363644712703748634421727257879503428486312944916318475347531435041392096108796057730987201352484075057637199253650470908582513936863463863368042891767107602111159828875539940120076013947033661793715396306139863655492213741597905119083588290097656647300733879314678913181465109316761575821351424860442292445304113160652700974330088499034675405518640677342603583409608605533747362760935658853109760994238347382222087292464497684560579562516765574088410321731345627735856052358236389532038534024842273371639123973215995440828421666636023296545694703577184873442034227706653837387506169212768015766181095420097708363604361110592409117889540338021426523948929686439808926114635414571535194342850721353453018315875628275733898268898523557799295727645229391567477566676051087887648453493636068278050564622813598885879259940946446041705204470046315137975431737187756039815962647501410906658866162180038266989961965580587208639721176995219466789857011798332440601811575658074284182910615193917630059194314434605154047710570054339000182453117733718955857603607182860506356479979004139761808955363669603162193113250223851791672055180659263518036251214575926238369348222665895576994660491938112486609099798128571823494006615552196112207203092277646200999315244273589488710576623894693889446495093960330454340842102462401048723328750081749179875543879387381439894238011762700837196053094383940063756116458560943129517597713935396074322792489221267045808183313764165818269562105872892447740035947009268662659651422050630078592002488291860839743732353849083964326147000532423540647042089499210250404726781059083644007466380020870126664209457181702946752278540074508552377720890581683918446592829417018288233014971554235235911774818628592967605048203864343108779562892925405638946621948268711042828163893975711757786915430165058602965217459581988878680408110328432739867198621306205559855266036405046282152306154594474489908839081999738747452969810776201487134000122535522246695409315213115337915798026979555710508507473874750758068765376445782524432638046143042889235934852961058269382103498000405248407084403561167817170512813378805705643450616119330424440798260377951198548694559152051960093041271007277849301555038895360338261929343797081874320949914159593396368110627557295278004254863060054523839151068998913578820019411786535682149118528207852130125518518493711503422159542244511900207393539627400208110465530207932867254740543652717595893500716336076321614725815407642053020045340183572338292661915308354095120226329165054426123619197051613839357326693760156914429944943744856809775696303129588719161129294681884936338647392747601226964158848900965717086160598147204467428664208765334799858222090619802173211614230419477754990738738567941189824660913091691772274207233367635032678340586301930193242996397204445179288122854478211953530898910125342975524727635730226281382091807439748671453590778633530160821559911314144205091447293535022230817193663509346865858656314855575862447818620108711889760652969899269328178705576435143382060141077329261063431525337182243385263520217735440715281898137698755157574546939727150488469793619500477720970561793913828989845327426227288647108883270173723258818244658436249580592560338105215606206155713299156084892064340303395262263451454283678698288074251422567451806184149564686111635404971897682154227722479474033571527436819409892050113653400123846714296551867344153741615042563256713430247655125219218035780169240326699541746087592409207004669340396510178134857835694440760470232540755557764728450751826890418293966113310160131119077398632462778219023650660374041606724962490137433217246454097412995570529142438208076098364823465973886691349919784013108015581343979194852830436739012482082444814128095443773898320059864909159505322857914576884962578665885999179867520554558099004556461178755249370124553217170194282884617402736649978475508294228020232901221630102309772151569446427909802190826689868834263071609207914085197695235553488657743425277531197247430873043619511396119080030255878387644206085044730631299277888942729189727169890575925244679660189707482960949190648764693702750773866432391919042254290235318923377293166736086996228032557185308919284403805071030064776847863243191000223929785255372375566213644740096760539439838235764606992465260089090624105904215453927904411529580345334500256244101006359530039598864466169595626351878060688513723462707997327233134693971456285542615467650632465676620279245208581347717608521691340946520307673391841147504140168924121319826881568664561485380287539331160232292555618941042995335640095786495340935115266454024418775949316930560448686420862757201172319526405023099774567647838488973464317215980626787671838005247696884084989185086149003432403476742686245952395890358582135006450998178244636087317754378859677672919526111213859194725451400301180503437875277664402762618941017576872680428176623860680477885242887430259145247073950546525135339459598789619778911041890292943818567205070964606263541732944649576612651953495701860015412623962286413897796733329070567376962156498184506842263690367849555970026079867996261019039331263768556968767029295371162528005543100786408728939225714512481135778627664902425161990277471090335933309304948380597856628844787441469841499067123764789582263294904679812089984857163571087831191848630254501620929805829208334813638405421720056121989353669371336733392464416125223196943471206417375491216357008573694397305979709719726666642267431117762176403068681310351899112271339724036887000996862922546465006385288620393800504778276912835603372548255793912985251506829969107754257647488325341412132800626717094009098223529657957997803018282428490221470748111124018607613415150387569830918652780658896682362523937845272634530420418802508442363190383318384550522367992357752929106925043261446950109861088899914658551881873582528164302520939285258077969737620845637482114433988162710031703151334402309526351929588680690821355853680161000213740851154484912685841268695899174149133820578492800698255195740201818105641297250836070356851055331787840829000041552511865779453963317538532092149720526607831260281961164858098684587525129997404092797683176639914655386108937587952214971731728131517932904431121815871023518740757222100123768721944747209349312324107065080618562372526732540733324875754482967573450019321902199119960797989373383673242576103938985349278777473980508080015544764061053522202325409443567718794565430406735896491017610775948364540823486130254718476485189575836674399791508512858020607820554462991723202028222914886959399729974297471155371858924238493855858595407438104882624648788053304271463011941589896328792678327322456103852197011130466587100500083285177311776489735230926661234588873102883515626446023671996644554727608310118788389151149340939344750073025855814756190881398752357812331342279866503522725367171230756861045004548970360079569827626392344107146584895780241408158405229536937499710665594894459246286619963556350652623405339439142111271810691052290024657423604130093691889255865784668461215679554256605416005071276641766056874274200329577160643448606201239821698271723197826816628249938714995449137302051843669076723577400053932662622760323659751718925901801104290384274185507894887438832703063283279963007200698012244365116394086922220745320244624121155804354542064215121585056896157356414313068883443185280853975927734433655384188340303517822946253702015782157373265523185763554098954033236382319219892171177449469403678296185920803403867575834111518824177439145077366384071880489358256868542011645031357633355509440319236720348651010561049872726472131986543435450409131859513145181276437310438972507004981987052176272494065214619959232142314439776546708351714749367986186552791715824080651063799500184295938799158350171580759883784962257398512129810326379376218322456594236685376799113140108043139732335449090824910499143325843298821033984698141715756010829706583065211347076803680695322971990599904451209087275776225351040902392888779424630483280319132710495478599180196967835321464441189260631526618167443193550817081875477050802654025294109218264858213857526688155584113198560022135158887210365696087515063187533002942118682221893775546027227291290504292259787710667873840000616772154638441292371193521828499824350920891801685572798156421858191197490985730570332667646460728757430565372602768982373259745084479649545648030771598153955827779139373601717422996027353102768719449444917939785144631597314435351850491413941557329382048542123508173912549749819308714396615132942045919380106231421774199184060180347949887691051557905554806953878540066453375981862846419905220452803306263695626490910827627115903856995051246529996062855443838330327638599800792922846659503551211245284087516229060262011857775313747949362055496401073001348853150735487353905602908933526400713274732621960311773433943673385759124508149335736911664541281788171454023054750667136518258284898099512139193995633241336556777098003081910272040997148687418134667006094051021462690280449159646545330107754695413088714165312544813061192407821188690056027781824235022696189344352547633573536485619363254417756613981703930632872166905722259745209192917262199844409646158269456380239502837121686446561785235565164127712826918688615572716201474934052276946595712198314943381622114006936307430444173284786101777743837977037231795255434107223445512555589998646183876764903972461167959018100035098928641204195163551108763204267612979826529425882951141275841262732790798807559751851576841264742209479721843309352972665210015662514552994745127631550917636730259462132930190402837954246323258550301096706922720227074863419005438302650681214142135057154175057508639907673946335146209082888934938376439399256900604067311422093312195936202982972351163259386772241477911629572780752395056251581603133359382311500518626890530658368129988108663263271980611271548858798093487912913707498230575929091862939195014721197586067270092547718025750337730799397134539532646195269996596385654917590458333585799102012713204583903200853878881633637685182083727885131175227769609787962142372162545214591281831798216044111311671406914827170981015457781939202311563871950805024679725792497605772625913328559726371211201905720771409148645074094926718035815157571514050397610963846755569298970383547314100223802583468767350129775413279532060971154506484212185936490997917766874774481882870632315515865032898164228288232746866106592732197907162384642153489852476216789050260998045266483929542357287343977680495774091449538391575565485459058976495198513801007958010783759945775299196700547602252552034453988712538780171960718164078124847847257912407824544361682345239570689514272269750431873633263011103053423335821609333191218806608268341428910415173247216053355849993224548730778822905252324234861531520976938461042582849714963475341837562003014915703279685301868631572488401526639835689563634657435321783493199825542117308467745297085839507616458229630324424328237737450517028560698067889521768198156710781633405266759539424926280756968326107495323390536223090807081455919837355377748742029039018142937311529334644468151212945097596534306284215319445727118614900017650558177095302468875263250119705209476159416768727784472000192789137251841622857783792284439084301181121496366424659033634194540657183544771912446621259392656620306888520055599121235363718226922531781458792593750441448933981608657900876165024635197045828895481793756681046474614105142498870252139936870509372305447734112641354892806841059107716677821238332810262185587751312721179344448201440425745083063944738363793906283008973306241380614589414227694747931665717623182472168350678076487573420491557628217583972975134478990696589532548940335615613167403276472469212505759116251529654568544633498114317670257295661844775487469378464233737238981920662048511894378868224807279352022501796545343757274163910791972952950812942922205347717304184477915673991738418311710362524395716152714669005814700002633010452643547865903290733205468338872078735444762647925297690170912007874183736735087713376977683496344252419949951388315074877537433849458259765560996555954318040920178497184685497370696212088524377013853757681416632722412634423982152941645378000492507262765150789085071265997036708726692764308377229685985169122305037462744310852934305273078865283977335246017463527703205938179125396915621063637625882937571373840754406468964783100704580613446731271591194608435935825987782835266531151065041623295329047772174083559349723758552138048305090009646676088301540612824308740645594431853413755220166305812111033453120745086824339432159043594430312431227471385842030390106070940315235556172767994160020393975099897629335325855575624808996691829864222677502360193257974726742578211119734709402357457222271212526852384295874273501563660093188045493338989741571490544182559738080871565281430102670460284316819230392535297795765862414392701549740879273131051636119137577008929564823323648298263024607975875767745377160102490804624301856524161756655600160859121534556267602192689982855377872583145144082654583484409478463178777374794653580169960779405568701192328608041130904629350871827125934668712766694873899824598527786499569165464029458935064964335809824765965165142090986755203808309203230487342703468288751604071546653834619611223013759451579252696743642531927390036038608236450762698827497618723575476762889950752114804852527950845033958570838130476937881321123674281319487950228066320170022460331989671970649163741175854851878484012054844672588851401562725019821719066960812627785485964818369621410721714214986361918774754509650308957099470934337856981674465828267911940611956037845397855839240761276344105766751024307559814552786167815949657062559755074306521085301597908073343736079432866757890533483669555486803913433720156498834220893399971641479746938696905480089193067138057171505857307148815649920714086758259602876056459782423770242469805328056632787041926768467116266879463486950464507420219373945259262668613552940624781361206202636498199999498405143868285258956342264328707663299304891723400725471764188685351372332667877921738347541480022803392997357936152412755829569276837231234798989446274330454566790062032420516396282588443085438307201495672106460533238537203143242112607424485845094580494081820927639140008540422023556260218564348994145439950410980591817948882628052066441086319001688568155169229486203010738897181007709290590480749092427141018933542818429995988169660993836961644381528877214085268088757488293258735809905670755817017949161906114001908553744882726200936685604475596557476485674008177381703307380305476973609786543859382187220583902344443508867499866506040645874346005331827436296177862518081893144363251205107094690813586440519229512932450078833398788429339342435126343365204385812912834345297308652909783300671261798130316794385535726296998740359570458452230856390098913179475948752126397078375944861139451960286751210561638976008880092746115860800207803341591451797073036835196977766076373785333012024120112046988609209339085365773222392412449051532780950955866459477634482269986074813297302630975028812103517723124465095349653693090018637764094094349837313251321862080214809922685502948454661814715557444709669530177690434272031892770604717784527939160472281534379803539679861424370956683221491465438014593829277393396032754048009552231816667380357183932757077142046723838624617803976292377131209580789363841447929802588065522129262093623930637313496640186619510811583471173312025805866727639992763579078063818813069156366274125431259589936119647626101405563503399523140323113819656236327198961837254845333702062563464223952766943568376761368711962921818754576081617053031590728828700712313666308722754918661395773730546065997437810987649802414011242142773668082751390959313404155826266789510846776118665957660165998178089414985754976284387856100263796543178313634025135814161151902096499133548733131115022700681930135929595971640197196053625033558479980963488718039111612813595968565478868325856437896173159762002419621552896297904819822199462269487137462444729093456470028537694958859591606789282491054412515996300781368367490209374915732896270028656829344431342347351239298259166739503425995868970697267332582735903121288746660451461487850346142827765991608090398652575717263081833494441820193533385071292345774375579344062178711330063106003324053991693682603746176638565758877580201229366353270267100681261825172914608202541892885935244491070138206211553827793565296914576502048643282865557934707209634807372692141186895467322767751335690190153723669036865389161291688887876407525493494249733427181178892759931596719354758988097924525262363659036320070854440784544797348291802082044926670634420437555325050527522833778887040804033531923407685630109347772125639088640413101073817853338316038135280828119040832564401842053746792992622037698718018061122624490909242641985820861751177113789051609140381575003366424156095216328197122335023167422600567941281406217219641842705784328959802882335059828208196666249035857789940333152274817776952843681630088531769694783690580671064828083598046698841098135158654906933319522394363287923990534810987830274500172065433699066117784554364687723631844464768069142828004551074686645392805399409108754939166095731619715033166968309929466349142798780842257220697148875580637480308862995118473187124777291910070227588893486939456289515802965372150409603107761289831263589964893410247036036645058687287589051406841238124247386385427908282733827973326885504935874303160274749063129572349742611221517417153133618622410913869500688835898962349276317316478340077460886655598733382113829928776911495492184192087771606068472874673681886167507221017261103830671787856694812948785048943063086169948798703160515884108282351274153538513365895332948629494495061868514779105804696039069372662670386512905201137810858616188886947957607413585534585151768051973334433495230120395770739623771316030242887200537320998253008977618973129817881944671731160647231476248457551928732782825127182446807824215216469567819294098238926284943760248852279003620219386696482215628093605373178040863727268426696421929946819214908701707533361094791381804063287387593848269535583077395761447997270003472880182785281389503217986345216111066608839314053226944905455527867894417579202440021450780192099804461382547805858048442416404775031536054906591430078158372430123137511562284015838644270890718284816757527123846782459534334449622010096071051370608461801187543120725491334994247617115633321408934609156561550600317384218701570226103101916603887064661438897736318780940711527528174689576401581047016965247557740891644568677717158500583269943401677202156767724068128366565264122982439465133197359199709403275938502669557470231813203243716420586141033606524536939160050644953060161267822648942437397166717661231048975031885732165554988342121802846912529086101485527815277625623750456375769497734336846015607727035509629049392487088406281067943622418704747008368842671022558302403599841645951122485272633632645114017395248086194635840783753556885622317115520947223065437092606797351000565549381224575483728545711797393615756167641692895805257297522338558611388322171107362265816218842443178857488798109026653793426664216990914056536432249301334867988154886628665052346997235574738424830590423677143278792316422403877764330192600192284778313837632536121025336935812624086866699738275977365682227907215832478888642369346396164363308730139814211430306008730666164803678984091335926293402304324974926887831643602681011309570716141912830686577323532639653677390317661361315965553584999398600565155921936759977717933019744688148371103206503693192894521402650915465184309936553493337183425298433679915939417466223900389527673813330617747629574943868716978453767219493506590875711917720875477107189937960894774512654757501871194870738736785890200617373321075693302216320628432065671192096950585761173961632326217708945426214609858410237813215817727602222738133495410481003073275107799948991977963883530734443457532975914263768405442264784216063122769646967156473999043715903323906560726644116438605404838847161912109008701019130726071044114143241976796828547885524779476481802959736049439700479596040292746299203572099761950140348315380947714601056333446998820822120587281510729182971211917876424880354672316916541852256729234429187128163232596965413548589577133208339911288775917226115273379010341362085614577992398778325083550730199818459025958355989260553299673770491722454935329683300002230181517226575787524058832249085821280089747909326100762578770428656006996176212176845478996440705066241710213327486796237430229155358200780141165348065647488230615003392068983794766255036549822805329662862117930628430170492402301985719978948836897183043805182174419147660429752437251683435411217038631379411422095295885798060152938752753799030938871683572095760715221900279379292786303637268765822681241993384808166021603722154710143007377537792699069587121289288019052031601285861825494413353820784883465311632650407642428390870121015194231961652268422003711230464300673442064747718021353070124098860353399152667923871101706221865883573781210935179775604425634694999787251125440854522274810914874307259869602040275941178942581281882159952359658979181144077653354321757595255536158128001163846720319346507296807990793963714961774312119402021297573125165253768017359101557338153772001952444543620071848475663415407442328621060997613243487548847434539665981338717466093020535070271952983943271425371155766600025784423031073429551533945060486222764966687624079324353192992639253731076892135352572321080889819339168668278948281170472624501948409700975760920983724090074717973340788141825195842598096241747610138252643955135259311885045636264188300338539652435997416931322894719878308427600401368074703904097238473945834896186539790594118599310356168436869219485382055780395773881360679549900085123259442529724486666766834641402189915944565309423440650667851948417766779470472041958822043295380326310537494883122180391279678446100139726753892195119117836587662528083690053249004597410947068772912328214304635337283519953648274325833119144459017809607782883583730111857543659958982724531925310588115026307542571493943024453931870179923608166611305426253995833897942971602070338767815033010280120095997252222280801423571094760351925544434929986767817891045559063015953809761875920358937341978962358931125983902598310267193304189215109689156225069659119828323455503059081730735195503721665870288053992138576037035377105178021280129566841984140362872725623214428754302210909472721073474134975514190737043318276626177275996888826027225247133683353452816692779591328861381766349857728936900965749562287103024362590772412219094300871755692625758065709912016659622436080242870024547362036394841255954881727272473653467783647201918303998717627037515724649922289467932322693619177641614618795613956699567783068290316589699430767333508234990790624100202506134057344300695745474682175690441651540636584680463692621274211075399042188716127617787014258864825775223889184599523376292377915585744549477361295525952226578636462118377598473700347971408206994145580719080213590732269233100831759510659019121294795408603640757358750205890208704579670007055262505811420663907459215273309406823649441590891009220296680523325266198911311842016291631076894084723564366808182168657219688268358402785500782804043453710183651096951782335743030504852653738073531074185917705610397395062640355442275156101107261779370634723804990666922161971194259120445084641746383589938239946517395509000859479990136026674261494290066467115067175422177038774507673563742154782905911012619157555870238957001405117822646989944917908301795475876760168094100135837613578591356924455647764464178667115391951357696104864922490083446715486383054477914330097680486878348184672733758436892724310447406807685278625585165092088263813233623148733336714764520450876627614950389949504809560460989604329123358348859990294526400284994280878624039811814884767301216754161106629995553668193123287425702063738352020086863691311733469731741219153633246745325630871347302792174956227014687325867891734558379964351358800959350877556356248810493852999007675135513527792412429277488565888566513247302514710210575352516511814850902750476845518252096331899068527614435138213662152368890578786699432288816028377482035506016029894009119713850179871683633744139275973644017007014763706655703504338121113576415018451821413619823495159601064752712575935185304332875537783057509567425442684712219618709178560783936144511383335649103256405733898667178123972237519316430617013859539474367843392670986712452211189690840236327411496601243483098929941738030588417166613073040067588380432111555379440605497721705942821514886165672771240903387727745629097110134885184374118695655449745736845218066982911045058004299887953899027804383596282409421860556287788428802127553884803728640019441614257499904272009595204654170598104989967504511936471172772220436102614079750809686975176600237187748348016120310234680567112644766123747627852190241202569943534716226660893675219833111813511146503854895025120655772636145473604426859498074396932331297127377157347099713952291182653485155587137336629120242714302503763269501350911612952993785864681307226486008270881333538193703682598867893321238327053297625857382790097826460545598555131836688844628265133798491667839409761353766251798258249663458771950124384040359140849209733754642474488176184070023569580177410177696925077814893386672557898564589851056891960924398841569280696983352240225634570497312245269354193837004843183357196516626721575524193401933099018319309196582920969656247667683659647019595754739345514337413708761517323677204227385674279170698204549953095918872434939524094441678998846319845504852393662972079777452814399418256789457795712552426826089940863317371538896262889629402112108884427376568624527612130371017300785135715404533041507959447776143597437803742436646973247138410492124314138903579092416036406314038149831481905251720937103964026808994832572297954564042701757722904173234796073618787889913318305843069394825961318713816423467218730845133877219086975104942843769325024981656673816260615941768252509993741672883951744066932549653403101452225316189009235376486378482881344209870048096227171226407489571939002918573307460104360729190945767994614929290427981687729426487729952858434647775386906950148984133924540394144680263625402118614317031251117577642829914644533408920976961699098372652361768745605894704968170136974909523072082682887890730190018253425805343421705928713931737993142410852647390948284596418093614138475831136130576108462366837237695913492615824516221552134879244145041756848064120636520170386330129532777699023118648020067556905682295016354931992305914246396217025329747573114094220180199368035026495636955866425906762685687372110339156793839895765565193177883000241613539562437777840801748819373095020699900890899328088397430367736595524891300156633294077907139615464534088791510300651321934486673248275907946807879819425019582622320395131252014109960531260696555404248670549986786923021746989009547850725672978794769888831093487464426400718183160331655511534276155622405474473378049246214952133258527698847336269182649174338987824789278468918828054669982303689939783413747587025805716349413568433929396068192061773331791738208562436433635359863494496890781064019674074436583667071586924521182997893804077137501290858646578905771426833582768978554717687184427726120509266486102051535642840632368481807287940717127966820060727559555904040233178749447346454760628189541512139162918444297651066947969354016866010055196077687335396511614930937570968554559381513789569039251014953265628147011998326992200066392875374713135236421589265126204072887716578358405219646054105435443642166562244565042999010256586927279142752931172082793937751326106052881235373451068372939893580871243869385934389175713376300720319760816604464683937725806909237297523486702916910426369262090199605204121024077648190316014085863558427609537086558164273995349346546314504040199528537252004957805254656251154109252437991326262713609099402902262062836752132305065183934057450112099341464918433323646569371725914489324159006242020612885732926133596808726500045628284557574596592120530341310111827501306961509835515632004310784601906565493806542525229161991819959602752327702249855738824899882707465936355768582560518068964285376850772012220347920993936179268206590142165615925306737944568949070853263568196831861772268249911472615732035807646298116244013316737892788689229032593349861797021994981925739617673075834417098559222170171825712777534491508205278430904619460835217402005838672849709411023266953921445461066215006410674740207009189911951376466904481267253691537162290791385403937560077835153374167747942100384002308951850994548779039346122220865060160500351776264831611153325587705073541279249909859373473787081194253055121436979749914951860535920403830235716352727630874693219622190064260886183676103346002255477477813641012691906569686495012688376296907233961276287223041141813610060264044030035996988919945827397624114613744804059697062576764723766065541618574690527229238228275186799156983390747671146103022776606020061246876477728819096791613354019881402757992174167678799231603963569492851513633647219540611171767387372555728522940054361785176502307544693869307873499110352182532929726044553210797887711449898870911511237250604238753734841257086064069052058452122754533848008205302450456517669518576913200042816758054924811780519832646032445792829730129105318385636821206215531288668564956512613892261367064093953334570526986959692350353094224543865278677673027540402702246384483553239914751363441044050092330361271496081355490531539021002299595756583705381261965683144286057956696622154721695620870013727768536960840704833325132793112232507148630206951245395003735723346807094656483089209801534878705633491092366057554050864111521441481434630437273271045027768661953107858323334857840297160925215326092558932655600672124359464255065996771770388445396181632879614460817789272171836908880126778207430106422524634807454300476492885553409062185153654355474125476152769772667769772777058315801412185688011705028365275543214803488004442979998062157904564161957212784508928489806426497427090579129069217807298769477975112447305991406050629946894280931034216416629935614828130998870745292716048433630818404126469637925843094185442216359084576146078558562473814931427078266215185541603870206876980461747400808324343665382354555109449498431093494759944672673665352517662706772194183191977196378015702169933675083760057163454643671776723387588643405644871566964321041282595645349841388412890420682047007615596916843038999348366793542549210328113363184722592305554383058206941675629992013373175489122037230349072681068534454035993561823576312837767640631013125335212141994611869350833176587852047112364331226765129964171325217513553261867681942338790365468908001827135283584888444111761234101179918709236507184857856221021104009776994453121795022479578069506532965940383987369907240797679040826794007618729547835963492793904576973661643405359792219285870574957481696694062334272619733518136626063735982575552496509807260123668283605928341855848026958413772558970883789942910549800331113884603401939166122186696058491571485733568286149500019097591125218800396419762163559375743718011480559442298730418196808085647265713547612831629200449880315402105530597076666362749328308916880932359290081787411985738317192616728834918402429721290434965526942726402559641463525914348400675867690350382320572934132981593533044446496829441367323442158380761694831219333119819061096142952201536170298575105594326461468505452684975764807808009221335811378197749271768545075538328768874474591593731162470601091244609829424841287520224462594477638749491997840446829257360968534549843266536862844489365704111817793806441616531223600214918768769467398407517176307516849856359201486892943105940202457969622924566644881967576294349535326382171613395757790766370764569570259738800438415805894336137106551859987600754924187211714889295221737721146081154344982665479872580056674724051122007383459271575727715218589946948117940644466399432370044291140747218180224825837736017346685300744985564715420036123593397312914458591522887408719508708632218837288262822884631843717261903305777147651564143822306791847386039147683108141358275755853643597721650028277803713422869688787349795096031108899196143386664068450697420787700280509367203387232629637856038653216432348815557557018469089074647879122436375556668678067610544955017260791142930831285761254481944449473244819093795369008206384631678225064809531810406570254327604385703505922818919878065865412184299217273720955103242251079718077833042609086794273428955735559252723805511440438001239041687716445180226491681641927401106451622431101700056691121733189423400547959684669804298017362570406733282129962153684881404102194463424646220745575643960452985313071409084608499653767803793201899140865814662175319337665970114330608625009829566917638846056762972931464911493704624469351984039534449135141193667933301936617663652555149174982307987072280860859626112660504289296966535652516688885572112276802772743708917389639772257564890533401038855931125679991516589025016486961427207005916056166159702451989051832969278935550303934681219761582183980483960562523091462638447386296039848924386187298507775928792722068554807210497817653286210187476766897248841139560349480376727036316921007350834073865261684507482496448597428134936480372426116704266870831925040997615319076855770327421785010006441984124207396400139603601583810565928413684574119102736420274163723488214524101347716529603128408658419787951116511529827814620379139855006399960326591248525308493690313130100799977191362230866011099929142871249388541612038020411340188887219693477904497527454288072803509305828754420755134816660927879353566521255620139988249628478726214432362853676502591450468377635282587652139156480972141929675549384375582600253168536356731379262475878049445944183429172756988376226261846365452743497662411138451305481449836311789784489732076719508784158618879692955819733250699951402601511675529750575437810242238957925786562128432731202200716730574069286869363930186765958251326499145950260917069347519408975357464016830811798846452473618956056479426358070562563281189269663026479535951097127659136233180866921535788607812759910537171402204506186075374866306350591483916467656723205714516886170790984695932236724946737583099607042589220481550799132752088583781117685214269334786921895240622657921043620348852926267984013953216458791151579050460579710838983371864038024417511347226472547010794793996953554669619726763255229914654933499663234185951450360980344092212206712567698723427940708857070474293173329188523896721971353924492426178641188637790962814486917869468177591717150669111480020759432012061969637795103227089029566085562225452602610460736131368869009281721068198618553780982018471154163630326265699283424155023600978046417108525537612728905335045506135684143775854429677977014660294387687225115363801191758154028120818255606485410787933598921064427244898618961629413418001295130683638609294100083136673372153008352696235737175330738653338204842190308186449184093723944033405244909554558016406460761581010301767488475017661908692946098769201691202181688291040870709560951470416921147027413390052253340834812870353031023919699978597413908593605433599697075604460134242453682496098772581311024732798562072126572499003468293886872304895562253204463602639854225258416464324271611419817802482595563544907219226583863662663750835944314877635156145710745528016159677048442714194435183275698407552677926411261765250615965235457187956673170913319358761628255920783080185206890151504713340386100310055914817852110384754542933389188444120517943969970194112695119526564919594189975418393234647424290702718875223534393673633663200307232747037407123982562024662651974090199762452056198557625760008708173083288344381831070054514493545885422678578551915372292379555494333410174420169600090696415612732297770221217951868376359082255128816470021992348864043959153018464004714321186360622527011541122283802778538911098490201342741014121559769965438877197485376431158229838533123071751132961904559007938064276695819014842627991221792947987348901868471676503827328552059082984529806259250352128451925927986593506132961946796252373972565584157853744567558998032405492186962888490332560851455344391660226257775512916200772796852629387937530454181080729285891989715381797343496187232927614747850192611450413274873242970583408471112333746274617274626582415324271059322506255302314738759251724787322881491455915605036334575424233779160374952502493022351481961381162563911415610326844958072508273431765944054098269765269344579863479709743124498271933113863873159636361218623497261409556079920628316999420072054811525353393946076850019909886553861433495781650089961649079678142901148387645682174914075623767618453775144031475411206760160726460556859257799322070337333398916369504346690694828436629980037414527627716547623825546170883189810868806847853705536480469350958818025360529740793538676511195079373282083146268960071075175520614433784114549950136432446328193346389050936545714506900864483440180428363390513578157273973334537284263372174065775771079830517555721036795976901889958494130195999573017901240193908681356585539661941371794487632079868800371607303220547423572266896801882123424391885984168972277652194032493227314793669234004848976059037958094696041754279613782553781223947646147832926976545162290281701100437846038756544151739433960048915318817576650500951697402415644771293656614253949368884230517400129920556854289853897942669956777027089146513736892206104415481662156804219838476730871787590279209175900695273456682026513373111518000181434120962601658629821076663523361774007837783423709152644063054071807843358061072961105550020415131696373046849213356837265400307509829089364612047891114753037049893952833457824082817386441322710002968311940203323456420826473276233830294639378998375836554559919340866235090967961134004867027123176526663710778725111860354037554487418693519733656621772359229396776463251562023487570113795712096237723431370212031004965152111976013176419408203437348512852602913334915125083119802850177855710725373149139215709105130965059885999931560863655477403551898166733535880048214665099741433761182777723351910741217572841592580872591315074606025634903777263373914461377038021318347447301113032670296917335047701632106616227830027269283365584011791419447808748253360714403296252285775009808599609040936312635621328162071453406104224112083010008587264252112262480142647519426184325853386753874054743491072710049754281159466017136122590440158991600229827801796035194080046513534752698777609527839984368086908989197839693532179980139135442552717910225397010810632143048511378291498511381969143043497500189980681644412123273328307192824362406733196554692677851193152775113446468905504248113361434984604849051258345683266441528489713972376040328212660253516693914082049947320486021627759791771234751097502403078935759937715095021751693555827072533911892334070223832077585802137174778378778391015234132098489423459613692340497998279304144463162707214796117456975719681239291913740982925805561955207434243295982898980529233366415419256367380689494201471241340525072204061794355252555225008748790086568314542835167750542294803274783044056438581591952666758282929705226127628711040134801787224801789684052407924360582742467443076721645270313451354167649668901274786801010295133862698649748212118629040337691568576240699296372493097201628707200189835423690364149270236961938547372480329855045112089192879829874467864129159417531675602533435310626745254507114181483239880607297140234725520713490798398982355268723950909365667878992383712578976248755990443228895388377317348941122757071410959790047919301046740750411435381782464630795989555638991884773781341347070246747362112048986226991888517456251732519341352038115863350123913054441910073628447567514161050410973505852762044489190978901984315485280533985777844313933883994310444465669244550885946314081751220331390681596592510546858013133838152176418210433429788826119630443111388796258746090226130900849975430395771243230616906262919403921439740270894777663702488155499322458825979020631257436910946393252806241642476868495455324938017639371615636847859823715902385421265840615367228607131702674740131145261063765383390315921943469817605358380310612887852051546933639241088467632009567089718367490578163085158138161966882222047570437590614338040725853862083565176998426774523195824182683698270160237414938363496629351576854061397342746470899685618170160551104880971554859118617189668025973541705423985135560018720335079060946421271143993196046527424050882225359773481519135438571253258540493946010865793798058620143366078825219717809025817370870916460452727977153509910340736425020386386718220522879694458387652947951048660717390229327455426785669776865939923416834122274663015062155320502655341460995249356050854921756549134830958906536175693817637473644183378974229700703545206663170929607591989627732423090252397443861014263098687733913882518684316501027964911497737582888913450341148865948670215492101084328080783428089417298008983297536940644969903125399863919581601468995220880662285408414864274786281975546629278814621607171381880180840572084715868906836919393381864278454537956719272397972364651667592011057995663962598535512763558768140213409829016296873429850792471846056874828331381259161962476156902875901072733103299140623864608333378638257926302391590003557609032477281338887339178096966601469615031754226751125993315529674213336300222964906480934582008181061802100227664580400278213336758573019011371754672763059044353131319036092489097246427928455549913490005180295707082919052556781889913899625138662319380053611346224294610248954072404857123256628888931722116432947816190554868054943441034090680716088028227959686950133643814268252170472870863010137301155236861416908375675747637239763185757038109443390564564468524183028148107998376918512127201935044041804604721626939445788377090105974693219720558114078775989772072009689382249303236830515862657281114637996983137517937623215111252349734305240622105244234353732905655163406669506165892878218707756794176080712973781335187117931650033155523822487730653444179453415395202424449703410120874072188109388268167512042299404948179449472732894770111574139441228455521828424922240658752689172272780607116754046973008037039618787796694882555614674384392570115829546661358678671897661297311267200072971553613027503556167817765442287442114729881614802705243806817653573275578602505847084013208837932816008769081300492491473682517035382219619039014999523495387105997351143478292339499187936608692301375596368532373806703591144243268561512109404259582639301678017128669239283231057658851714020211196957064799814031505633045141564414623163763809904402816256917576489142569714163598439317433270237812336938043012892626375382667795034169334323607500248175741808750388475094939454896209740485442635637164995949920980884294790363666297526003243856352945844728944547166209297495496616877414120882130477022816116456044007236351581149729739218966737382647204722642221242016560150284971306332795814302516013694825567014780935790889657134926158161346901806965089556310121218491805847922720691871696316330044858020102860657858591269974637661741463934159569539554203314628026518951167938074573315759846086173702687867602943677780500244673391332431669880354073232388281847501051641331189537036488422690270478052742490603492082954755054003457160184072574536938145531175354210726557835615499874447480427323457880061873149341566046352979779455075359304795687209316724536547208381685855606043801977030764246083489876101345709394877002946175792061952549255757109038525171488525265671045349813419803390641529876343695420256080277614421914318921393908834543131769685101840103844472348948869520981943531906506555354617335814045544837884752526253949665869992058417652780125341033896469818642430034146791380619028059607854888010789705516946215228773090104467462497979992627120951684779568482583341402266477210843362437593741610536734041954738964197895425335036301861400951534766961476255651873823292468547356935802896011536791787303553159378363082248615177770541577576561759358512016692943111138863582159667618830326104164651714846979385422621687161400122378213779774131268977266712992025922017408770076956283473932201088159356286281928563571893384958850603853158179760679479840878360975960149733420572704603521790605647603285569276273495182203236144112584182426247712012035776388895974318232827871314608053533574494297621796789034568169889553518504478325616380709476951699086247100019748809205009521943632378719764870339223811540363475488626845956159755193765410115014067001226927474393888589943859730245414801061235908036274585288493563251585384383242493252666087588908318700709100237377106576985056433928854337658342596750653715005333514489908293887737352051459333049626531415141386124437935885070944688045486975358170212908490787347806814366323322819415827345671356443171537967818058195852464840084032909981943781718177302317003989733050495387356116261023999433259780126893432605584710278764901070923443884634011735556865903585244919370181041626208504299258697435817098133894045934471937493877624232409852832762266604942385129709453245586252103600829286649724174919141988966129558076770979594795306013119159011773943104209049079424448868513086844493705909026006120649425744710353547657859242708130410618546219881830090634588187038755856274911587375421064667951346487586771543838018521348281915812462599335160198935595167968932852205824799421034512715877163345222995418839680448835529753361286837225935390079201666941339091168758803988828869216002373257361588207163516271332810518187602104852180675526648673908900907195138058626735124312215691637902277328705410842037841525683288718046987952513073266340278519059417338920358540395677035611329354482585628287610610698229721420961993509331312171187891078766872044548876089410174798647137882462153955933333275562009439580434537919782280590395959927436913793778664940964048777841748336432684026282932406260081908081804390914556351936856063045089142289645219987798849347477729132797266027658401667890136490508741142126861969862044126965282981087045479861559545338021201155646979976785738920186243599326777689454060508218838227909833627167124490026761178498264377033002081844590009717235204331994708242098771514449751017055643029542821819670009202515615844174205933658148134902693111517093872260026458630561325605792560927332265579346280805683443921373688405650434307396574061017779370141424615493070741360805442100295600095663588977899267630517718781943706761498217564186590116160865408635391513039201316805769034172596453692350806417446562351523929050409479953184074862151210561833854566176652606393713658802521666223576132201941701372664966073252010771947931265282763302413805164907174565964853748354669194523580315301969160480994606814904037819829732360930087135760798621425422096419004367905479049930078372421581954535418371129368658430553842717628035279128821129308351575656599944741788438381565148434229858704245592434693295232821803508333726283791830216591836181554217157448465778420134329982594566884558266171979012180849480332448787258183774805522268151011371745368417870280274452442905474518234674919564188551244421337783521423865979925988203287085109338386829906571994614906290257427686038850511032638544540419184958866538545040571323629681069146814847869659166861842756798460041868762298055562963045953227923051616721591968675849523635298935788507746081537321454642984792310511676357749494622952569497660359473962430995343310404994209677883827002714478494069037073249106444151696053256560586778757417472110827435774315194060757983563629143326397812218946287447798119807225646714664054850131009656786314880090303749338875364183165134982546694673316118123364854397649325026179549357204305402182974871251107404011611405899911093062492312813116340549262571356721818628932786138833718028535056503591952741400869510926167541476792668032109237467087213606278332922386413619594121339278036118276324106004740971111048140003623342714514483334641675466354699731494756643423659493496845884551524150756376605086632827424794136062876041290644913828519456402643153225858624043141838669590633245063000392213192647625962691510904457695301444054618037857503036686212462278639752746667870121003392984873375014475600322100622358029343774955032037012738468163061026570300872275462966796880890587127676361066225722352229739206443093524327228100859973095132528630601105497915644791845004618046762408928925680912930592960642357021061524646205023248966593987324933967376952023991760898474571843531936646529125848064480196520162838795189499336759241485626136995945307287254532463291529110128763770605570609531377527751867923292134955245133089867969165129073841302167573238637575820080363575728002754490327953079900799442541108725693188014667935595834676432868876966610097395749967836593397846346959948950610490383647409504695226063858046758073069912290474089879166872117147527644711604401952718169508289733537148530928937046384420893299771125856840846608339934045689026787516008775461267988015465856522061210953490796707365539702576199431376639960606061106406959330828171876426043573425361756943784848495250108266488395159700490598380812105221111091943323951136051446459834210799058082093716464523127704023160072138543723461267260997870385657091998507595634613248460188409850194287687902268734556500519121546544063829253851276317663922050938345204300773017029940362615434001322763910912988327863920412300445551684054889809080779174636092439334912641164240093880746356607262336695842764583698268734815881961058571835767462009650526065929263548291499045768307210893245857073701660717398194485028842603963660746031184786225831056580870870305567595861341700745402965687634774176431051751036732869245558582082372038601781739405175130437994868822320044378043103170921034261674998000073016094814586374488778522273076330495383944345382770608760763542098445008306247630253572781032783461766970544287155315340016497076657195985041748199087201490875686037783591994719343352772947285537925787684832301101859365800717291186967617655053775030293033830706448912811412025506150896411007623824574488655182581058140345320124754723269087547507078577659732542844459353044992070014538748948226556442223696365544194225441338212225477497535494624827680533336983284156138692363443358553868471111430498248398991803165458638289353799130535222833430137953372954016257623228081138499491876144141322933767106563492528814528239506209022357876684650116660097382753660405446941653422239052108314585847035529352219928272760574821266065291385530345549744551470344939486863429459658431024190785923680224560763936784166270518555178702904073557304620639692453307795782245949710420188043000183881429008173039450507342787013124466860092778581811040911511729374873627887874907465285565434748886831064110051023020875107768918781525622735251550379532444857787277617001964853703555167655209119339343762866284619844026295252183678522367475108809781507098978413086245881522660963551401874495836926917799047120726494905737264286005211403581231076006699518536124862746756375896225299116496066876508261734178484789337295056739007878617925351440621045366250640463728815698232317500596261080921955211150859302955654967538862612972339914628358476048627627027309739202001432248707582337354915246085608210328882974183906478869923273691360048837436615223517058437705545210815513361262142911815615301758882573594892507108879262128641392443309383797333867806131795237315266773820858024701433527009243803266951742119507670884326346442749127558907746863582162166042741315170212458586056233631493164646913946562497471741958354218607748711057338458433689939645913740603382159352243594751626239188685307822821763983237306180204246560477527943104796189724299533029792497481684052893791044947004590864991872727345413508101983881864673609392571930511968645601855782450218231065889437986522432050677379966196955472440585922417953006820451795370043472451762893566770508490213107736625751697335527462302943031203596260953423574397249659211010657817826108745318874803187430823573699195156340957162700992444929749105489851519658664740148225106335367949737142510229341882585117371994499115097583746130105505064197721531929354875371191630262030328588658528480193509225875775597425276584011721342323648084027143356367542046375182552524944329657043861387865901965738802868401894087672816714137033661732650120578653915780703088714261519075001492576112927675193096728453971160213606303090542243966320674323582797889332324405779199278484633339777737655901870574806828678347965624146102899508487399692970750432753029972872297327934442988646412725348160603779707298299173029296308695801996312413304939350493325412355071054461182591141116454534710329881047844067780138077131465400099386306481266614330858206811395838319169545558259426895769841428893743467084107946318932539106963955780706021245974898293564613560788983472419979478564362042094613412387613198865352358312996862268948608408456655606876954501274486631405054735351746873009806322780468912246821460806727627708402402266155485024008952891657117617439020337584877842911289623247059191874691042005848326140677333751027195653994697162517248312230633919328707983800748485726516123434933273356664473358556430235280883924348278760886164943289399166399210488307847777048045728491456303353265070029588906265915498509407972767567129795010098229476228961891591441520032283878773485130979081019129267227103778898053964156362364169154985768408398468861684375407065121039062506128107663799047908879674778069738473170475253442156390387201238806323688037017949308954900776331523063548374256816653361606641980030188287123767481898330246836371488309259283375902278942588060087286038859168849730693948020511221766359138251524278670094406942355120201568377778851824670025651708509249623747726813694284350062938814429987905301056217375459182679973217735029368928065210025396268807498092643458011655715886700443503976505323478287327368840863540002740676783821963522226539290939807367391364082898722017776747168118195856133721583119054682936083236976113450281757830202934845982925000895682630271263295866292147653142233351793093387951357095346377183684092444422096319331295620305575517340067973740614162107923633423805646850092037167152642556371853889571416419772387422610596667396997173168169415435095283193556417705668622215217991151355639707143312893657553844648326201206424338016955862698561022460646069330793847858814367407000599769703649019273328826135329363112403650698652160638987250267238087403396744397830258296894256896741864336134979475245526291426522842419243083388103580053787023999542172113686550275341362211693140694669513186928102574795985605145005021715913317751609957865551981886193211282110709442287240442481153406055895958355815232012184605820563592699303478851132068626627588771446035996656108430725696500563064489187599466596772847171539573612108180841547273142661748933134174632662354222072600146012701206934639520564445543291662986660783089068118790090815295063626782075614388815781351134695366303878412092346942868730839320432333872775496805210302821544324723388845215343727250128589747691460808314404125868181540049187772287869801853454537006526655649170915429522756709222217474112062720656622989806032891672068743654948246108697367225547404812889242471854323605753411672850757552057131156697954584887398742228135887985840783135060548290551482785294891121905383195624228719484759407859398047901094194070671764439032730712135887385049993638838205501683402777496070276844880281912220636888636811043569529300652195528261526991271637277388418993287130563464688227398288763198645709836308917786487086676185485680047672552675414742851028145807403152992197814557756843681110185317498167016426647884090262682824448258027532094549915104518517716546311804904567985713257528117913656278158111288816562285876030875974963849435275676612168959261485030785362045274507752950631012480341804584059432926079854435620093708091821523920371790678121992280496069738238743312626730306795943960954957189577217915597300588693646845576676092450906088202212235719254536715191834872587423919410890444115959932760044506556206461164655665487594247369252336955993030355095817626176231849561906494839673002037763874369343999829430209147073618947932692762445186560239559053705128978163455423320114975994896278424327483788032701418676952621180975006405149755889650293004867605208010491537885413909424531691719987628941277221129464568294860281493181560249677887949813777216229359437811004448060797672429276249510784153446429150842764520002042769470698041775832209097020291657347251582904630910359037842977572651720877244740952267166306005469716387943171196873484688738186656751279298575016363411314627530499019135646823804329970695770150789337728658035712790913767420805655493624646 diff --git a/src/pkg/compress/zlib/example_test.go b/src/pkg/compress/zlib/example_test.go new file mode 100644 index 000000000..b934ffa61 --- /dev/null +++ b/src/pkg/compress/zlib/example_test.go @@ -0,0 +1,37 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zlib_test + +import ( + "bytes" + "compress/zlib" + "fmt" + "io" + "os" +) + +func ExampleNewWriter() { + var b bytes.Buffer + + w := zlib.NewWriter(&b) + w.Write([]byte("hello, world\n")) + w.Close() + fmt.Println(b.Bytes()) + // Output: [120 156 202 72 205 201 201 215 81 40 207 47 202 73 225 2 4 0 0 255 255 33 231 4 147] +} + +func ExampleNewReader() { + buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207, + 47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147} + b := bytes.NewBuffer(buff) + + r, err := zlib.NewReader(b) + if err != nil { + panic(err) + } + io.Copy(os.Stdout, r) + // Output: hello, world + r.Close() +} diff --git a/src/pkg/compress/zlib/reader.go b/src/pkg/compress/zlib/reader.go index f38ef5a88..d54746f4c 100644 --- a/src/pkg/compress/zlib/reader.go +++ b/src/pkg/compress/zlib/reader.go @@ -11,7 +11,7 @@ and compress during writing. For example, to write compressed data to a buffer: var b bytes.Buffer - w, err := zlib.NewWriter(&b) + w := zlib.NewWriter(&b) w.Write([]byte("hello, world\n")) w.Close() diff --git a/src/pkg/container/heap/example_intheap_test.go b/src/pkg/container/heap/example_intheap_test.go new file mode 100644 index 000000000..e718cbc58 --- /dev/null +++ b/src/pkg/container/heap/example_intheap_test.go @@ -0,0 +1,43 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This example demonstrates an integer heap built using the heap interface. +package heap_test + +import ( + "container/heap" + "fmt" +) + +// An IntHeap is a min-heap of ints. +type IntHeap []int + +func (h IntHeap) Len() int { return len(h) } +func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } +func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *IntHeap) Push(x interface{}) { + // Push and Pop use pointer receivers because they modify the slice's length, + // not just its contents. + *h = append(*h, x.(int)) +} + +func (h *IntHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +// This example inserts several ints into an IntHeap and removes them in order of priority. +func Example_intHeap() { + h := &IntHeap{2, 1, 5} + heap.Init(h) + heap.Push(h, 3) + for h.Len() > 0 { + fmt.Printf("%d ", heap.Pop(h)) + } + // Output: 1 2 3 5 +} diff --git a/src/pkg/container/heap/example_pq_test.go b/src/pkg/container/heap/example_pq_test.go new file mode 100644 index 000000000..8cbeb8d70 --- /dev/null +++ b/src/pkg/container/heap/example_pq_test.go @@ -0,0 +1,95 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This example demonstrates a priority queue built using the heap interface. +package heap_test + +import ( + "container/heap" + "fmt" +) + +// An Item is something we manage in a priority queue. +type Item struct { + value string // The value of the item; arbitrary. + priority int // The priority of the item in the queue. + // The index is needed by update and is maintained by the heap.Interface methods. + index int // The index of the item in the heap. +} + +// A PriorityQueue implements heap.Interface and holds Items. +type PriorityQueue []*Item + +func (pq PriorityQueue) Len() int { return len(pq) } + +func (pq PriorityQueue) Less(i, j int) bool { + // We want Pop to give us the highest, not lowest, priority so we use greater than here. + return pq[i].priority > pq[j].priority +} + +func (pq PriorityQueue) Swap(i, j int) { + pq[i], pq[j] = pq[j], pq[i] + pq[i].index = i + pq[j].index = j +} + +func (pq *PriorityQueue) Push(x interface{}) { + n := len(*pq) + item := x.(*Item) + item.index = n + *pq = append(*pq, item) +} + +func (pq *PriorityQueue) Pop() interface{} { + old := *pq + n := len(old) + item := old[n-1] + item.index = -1 // for safety + *pq = old[0 : n-1] + return item +} + +// update modifies the priority and value of an Item in the queue. +func (pq *PriorityQueue) update(item *Item, value string, priority int) { + heap.Remove(pq, item.index) + item.value = value + item.priority = priority + heap.Push(pq, item) +} + +// This example inserts some items into a PriorityQueue, manipulates an item, +// and then removes the items in priority order. +func Example_priorityQueue() { + // Some items and their priorities. + items := map[string]int{ + "banana": 3, "apple": 2, "pear": 4, + } + + // Create a priority queue and put the items in it. + pq := &PriorityQueue{} + heap.Init(pq) + for value, priority := range items { + item := &Item{ + value: value, + priority: priority, + } + heap.Push(pq, item) + } + + // Insert a new item and then modify its priority. + item := &Item{ + value: "orange", + priority: 1, + } + heap.Push(pq, item) + pq.update(item, item.value, 5) + + // Take the items out; they arrive in decreasing priority order. + for pq.Len() > 0 { + item := heap.Pop(pq).(*Item) + fmt.Printf("%.2d:%s ", item.priority, item.value) + } + // Output: + // 05:orange 04:pear 03:banana 02:apple +} diff --git a/src/pkg/container/heap/example_test.go b/src/pkg/container/heap/example_test.go deleted file mode 100644 index 2050bc835..000000000 --- a/src/pkg/container/heap/example_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This example demonstrates a priority queue built using the heap interface. -package heap_test - -import ( - "container/heap" - "fmt" -) - -// An Item is something we manage in a priority queue. -type Item struct { - value string // The value of the item; arbitrary. - priority int // The priority of the item in the queue. - // The index is needed by changePriority and is maintained by the heap.Interface methods. - index int // The index of the item in the heap. -} - -// A PriorityQueue implements heap.Interface and holds Items. -type PriorityQueue []*Item - -func (pq PriorityQueue) Len() int { return len(pq) } - -func (pq PriorityQueue) Less(i, j int) bool { - // We want Pop to give us the highest, not lowest, priority so we use greater than here. - return pq[i].priority > pq[j].priority -} - -func (pq PriorityQueue) Swap(i, j int) { - pq[i], pq[j] = pq[j], pq[i] - pq[i].index = i - pq[j].index = j -} - -func (pq *PriorityQueue) Push(x interface{}) { - // Push and Pop use pointer receivers because they modify the slice's length, - // not just its contents. - // To simplify indexing expressions in these methods, we save a copy of the - // slice object. We could instead write (*pq)[i]. - a := *pq - n := len(a) - a = a[0 : n+1] - item := x.(*Item) - item.index = n - a[n] = item - *pq = a -} - -func (pq *PriorityQueue) Pop() interface{} { - a := *pq - n := len(a) - item := a[n-1] - item.index = -1 // for safety - *pq = a[0 : n-1] - return item -} - -// update is not used by the example but shows how to take the top item from -// the queue, update its priority and value, and put it back. -func (pq *PriorityQueue) update(value string, priority int) { - item := heap.Pop(pq).(*Item) - item.value = value - item.priority = priority - heap.Push(pq, item) -} - -// changePriority is not used by the example but shows how to change the -// priority of an arbitrary item. -func (pq *PriorityQueue) changePriority(item *Item, priority int) { - heap.Remove(pq, item.index) - item.priority = priority - heap.Push(pq, item) -} - -// This example pushes 10 items into a PriorityQueue and takes them out in -// order of priority. -func Example() { - const nItem = 10 - // Random priorities for the items (a permutation of 0..9, times 11)). - priorities := [nItem]int{ - 77, 22, 44, 55, 11, 88, 33, 99, 00, 66, - } - values := [nItem]string{ - "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", - } - // Create a priority queue and put some items in it. - pq := make(PriorityQueue, 0, nItem) - for i := 0; i < cap(pq); i++ { - item := &Item{ - value: values[i], - priority: priorities[i], - } - heap.Push(&pq, item) - } - // Take the items out; should arrive in decreasing priority order. - // For example, the highest priority (99) is the seventh item, so output starts with 99:"seven". - for i := 0; i < nItem; i++ { - item := heap.Pop(&pq).(*Item) - fmt.Printf("%.2d:%s ", item.priority, item.value) - } - // Output: - // 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 00:eight -} diff --git a/src/pkg/container/heap/heap.go b/src/pkg/container/heap/heap.go index 67018e6ba..c37e50e3c 100644 --- a/src/pkg/container/heap/heap.go +++ b/src/pkg/container/heap/heap.go @@ -4,13 +4,13 @@ // Package heap provides heap operations for any type that implements // heap.Interface. A heap is a tree with the property that each node is the -// highest-valued node in its subtree. +// minimum-valued node in its subtree. // // A heap is a common way to implement a priority queue. To build a priority // queue, implement the Heap interface with the (negative) priority as the // ordering for the Less method, so Push adds items while Pop removes the // highest-priority item from the queue. The Examples include such an -// implementation; the file example_test.go has the complete source. +// implementation; the file example_pq_test.go has the complete source. // package heap @@ -79,7 +79,7 @@ func Remove(h Interface, i int) interface{} { func up(h Interface, j int) { for { i := (j - 1) / 2 // parent - if i == j || h.Less(i, j) { + if i == j || !h.Less(j, i) { break } h.Swap(i, j) @@ -90,14 +90,14 @@ func up(h Interface, j int) { func down(h Interface, i, n int) { for { j1 := 2*i + 1 - if j1 >= n { + if j1 >= n || j1 < 0 { // j1 < 0 after int overflow break } j := j1 // left child if j2 := j1 + 1; j2 < n && !h.Less(j1, j2) { j = j2 // = 2*i + 2 // right child } - if h.Less(i, j) { + if !h.Less(j, i) { break } h.Swap(i, j) diff --git a/src/pkg/container/heap/heap_test.go b/src/pkg/container/heap/heap_test.go index cb31ef6d3..274d587d8 100644 --- a/src/pkg/container/heap/heap_test.go +++ b/src/pkg/container/heap/heap_test.go @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package heap_test +package heap import ( - . "container/heap" "testing" ) @@ -170,3 +169,16 @@ func TestRemove2(t *testing.T) { } } } + +func BenchmarkDup(b *testing.B) { + const n = 10000 + h := make(myHeap, n) + for i := 0; i < b.N; i++ { + for j := 0; j < n; j++ { + Push(&h, 0) // all elements are the same + } + for h.Len() > 0 { + Pop(&h) + } + } +} diff --git a/src/pkg/container/list/example_test.go b/src/pkg/container/list/example_test.go new file mode 100644 index 000000000..7361212d7 --- /dev/null +++ b/src/pkg/container/list/example_test.go @@ -0,0 +1,30 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package list_test + +import ( + "container/list" + "fmt" +) + +func Example() { + // Create a new list and put some numbers in it. + l := list.New() + e4 := l.PushBack(4) + e1 := l.PushFront(1) + l.InsertBefore(3, e4) + l.InsertAfter(2, e1) + + // Iterate through list and and print its contents. + for e := l.Front(); e != nil; e = e.Next() { + fmt.Println(e.Value) + } + + // Output: + // 1 + // 2 + // 3 + // 4 +} diff --git a/src/pkg/container/list/list.go b/src/pkg/container/list/list.go index a3fd4b39f..562a5badb 100644 --- a/src/pkg/container/list/list.go +++ b/src/pkg/container/list/list.go @@ -11,201 +11,187 @@ // package list -// Element is an element in the linked list. +// Element is an element of a linked list. type Element struct { // Next and previous pointers in the doubly-linked list of elements. - // The front of the list has prev = nil, and the back has next = nil. + // To simplify the implementation, internally a list l is implemented + // as a ring, such that &l.root is both the next element of the last + // list element (l.Back()) and the previous element of the first list + // element (l.Front()). next, prev *Element // The list to which this element belongs. list *List - // The contents of this list element. + // The value stored with this element. Value interface{} } // Next returns the next list element or nil. -func (e *Element) Next() *Element { return e.next } +func (e *Element) Next() *Element { + if p := e.next; p != &e.list.root { + return p + } + return nil +} // Prev returns the previous list element or nil. -func (e *Element) Prev() *Element { return e.prev } +func (e *Element) Prev() *Element { + if p := e.prev; p != &e.list.root { + return p + } + return nil +} // List represents a doubly linked list. // The zero value for List is an empty list ready to use. type List struct { - front, back *Element - len int + root Element // sentinel list element, only &root, root.prev, and root.next are used + len int // current list length excluding (this) sentinel element } -// Init initializes or clears a List. +// Init initializes or clears list l. func (l *List) Init() *List { - l.front = nil - l.back = nil + l.root.next = &l.root + l.root.prev = &l.root l.len = 0 return l } // New returns an initialized list. -func New() *List { return new(List) } - -// Front returns the first element in the list. -func (l *List) Front() *Element { return l.front } +func New() *List { return new(List).Init() } -// Back returns the last element in the list. -func (l *List) Back() *Element { return l.back } +// Len returns the number of elements of list l. +func (l *List) Len() int { return l.len } -// Remove removes the element from the list -// and returns its Value. -func (l *List) Remove(e *Element) interface{} { - l.remove(e) - e.list = nil // do what remove does not - return e.Value +// Front returns the first element of list l or nil +func (l *List) Front() *Element { + if l.len == 0 { + return nil + } + return l.root.next } -// remove the element from the list, but do not clear the Element's list field. -// This is so that other List methods may use remove when relocating Elements -// without needing to restore the list field. -func (l *List) remove(e *Element) { - if e.list != l { - return - } - if e.prev == nil { - l.front = e.next - } else { - e.prev.next = e.next - } - if e.next == nil { - l.back = e.prev - } else { - e.next.prev = e.prev +// Back returns the last element of list l or nil. +func (l *List) Back() *Element { + if l.len == 0 { + return nil } - - e.prev = nil - e.next = nil - l.len-- + return l.root.prev } -func (l *List) insertBefore(e *Element, mark *Element) { - if mark.prev == nil { - // new front of the list - l.front = e - } else { - mark.prev.next = e +// lazyInit lazily initializes a zero List value. +func (l *List) lazyInit() { + if l.root.next == nil { + l.Init() } - e.prev = mark.prev - mark.prev = e - e.next = mark - l.len++ } -func (l *List) insertAfter(e *Element, mark *Element) { - if mark.next == nil { - // new back of the list - l.back = e - } else { - mark.next.prev = e - } - e.next = mark.next - mark.next = e - e.prev = mark +// insert inserts e after at, increments l.len, and returns e. +func (l *List) insert(e, at *Element) *Element { + n := at.next + at.next = e + e.prev = at + e.next = n + n.prev = e + e.list = l l.len++ + return e } -func (l *List) insertFront(e *Element) { - if l.front == nil { - // empty list - l.front, l.back = e, e - e.prev, e.next = nil, nil - l.len = 1 - return - } - l.insertBefore(e, l.front) +// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). +func (l *List) insertValue(v interface{}, at *Element) *Element { + return l.insert(&Element{Value: v}, at) } -func (l *List) insertBack(e *Element) { - if l.back == nil { - // empty list - l.front, l.back = e, e - e.prev, e.next = nil, nil - l.len = 1 - return +// remove removes e from its list, decrements l.len, and returns e. +func (l *List) remove(e *Element) *Element { + e.prev.next = e.next + e.next.prev = e.prev + e.next = nil // avoid memory leaks + e.prev = nil // avoid memory leaks + e.list = nil + l.len-- + return e +} + +// Remove removes e from l if e is an element of list l. +// It returns the element value e.Value. +func (l *List) Remove(e *Element) interface{} { + if e.list == l { + // if e.list == l, l must have been initialized when e was inserted + // in l or l == nil (e is a zero Element) and l.remove will crash + l.remove(e) } - l.insertAfter(e, l.back) + return e.Value } -// PushFront inserts the value at the front of the list and returns a new Element containing the value. -func (l *List) PushFront(value interface{}) *Element { - e := &Element{nil, nil, l, value} - l.insertFront(e) - return e +// Pushfront inserts a new element e with value v at the front of list l and returns e. +func (l *List) PushFront(v interface{}) *Element { + l.lazyInit() + return l.insertValue(v, &l.root) } -// PushBack inserts the value at the back of the list and returns a new Element containing the value. -func (l *List) PushBack(value interface{}) *Element { - e := &Element{nil, nil, l, value} - l.insertBack(e) - return e +// PushBack inserts a new element e with value v at the back of list l and returns e. +func (l *List) PushBack(v interface{}) *Element { + l.lazyInit() + return l.insertValue(v, l.root.prev) } -// InsertBefore inserts the value immediately before mark and returns a new Element containing the value. -func (l *List) InsertBefore(value interface{}, mark *Element) *Element { +// InsertBefore inserts a new element e with value v immediately before mark and returns e. +// If mark is not an element of l, the list is not modified. +func (l *List) InsertBefore(v interface{}, mark *Element) *Element { if mark.list != l { return nil } - e := &Element{nil, nil, l, value} - l.insertBefore(e, mark) - return e + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark.prev) } -// InsertAfter inserts the value immediately after mark and returns a new Element containing the value. -func (l *List) InsertAfter(value interface{}, mark *Element) *Element { +// InsertAfter inserts a new element e with value v immediately after mark and returns e. +// If mark is not an element of l, the list is not modified. +func (l *List) InsertAfter(v interface{}, mark *Element) *Element { if mark.list != l { return nil } - e := &Element{nil, nil, l, value} - l.insertAfter(e, mark) - return e + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark) } -// MoveToFront moves the element to the front of the list. +// MoveToFront moves element e to the front of list l. +// If e is not an element of l, the list is not modified. func (l *List) MoveToFront(e *Element) { - if e.list != l || l.front == e { + if e.list != l || l.root.next == e { return } - l.remove(e) - l.insertFront(e) + // see comment in List.Remove about initialization of l + l.insert(l.remove(e), &l.root) } -// MoveToBack moves the element to the back of the list. +// MoveToBack moves element e to the back of list l. +// If e is not an element of l, the list is not modified. func (l *List) MoveToBack(e *Element) { - if e.list != l || l.back == e { + if e.list != l || l.root.prev == e { return } - l.remove(e) - l.insertBack(e) + // see comment in List.Remove about initialization of l + l.insert(l.remove(e), l.root.prev) } -// Len returns the number of elements in the list. -func (l *List) Len() int { return l.len } - -// PushBackList inserts each element of ol at the back of the list. -func (l *List) PushBackList(ol *List) { - last := ol.Back() - for e := ol.Front(); e != nil; e = e.Next() { - l.PushBack(e.Value) - if e == last { - break - } +// PushBackList inserts a copy of an other list at the back of list l. +// The lists l and other may be the same. +func (l *List) PushBackList(other *List) { + l.lazyInit() + for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { + l.insertValue(e.Value, l.root.prev) } } -// PushFrontList inserts each element of ol at the front of the list. The ordering of the passed list is preserved. -func (l *List) PushFrontList(ol *List) { - first := ol.Front() - for e := ol.Back(); e != nil; e = e.Prev() { - l.PushFront(e.Value) - if e == first { - break - } +// PushFrontList inserts a copy of an other list at the front of list l. +// The lists l and other may be the same. +func (l *List) PushFrontList(other *List) { + l.lazyInit() + for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { + l.insertValue(e.Value, &l.root) } } diff --git a/src/pkg/container/list/list_test.go b/src/pkg/container/list/list_test.go index 1d44ff84e..b4fc77d14 100644 --- a/src/pkg/container/list/list_test.go +++ b/src/pkg/container/list/list_test.go @@ -4,65 +4,75 @@ package list -import ( - "testing" -) +import "testing" + +func checkListLen(t *testing.T, l *List, len int) bool { + if n := l.Len(); n != len { + t.Errorf("l.Len() = %d, want %d", n, len) + return false + } + return true +} func checkListPointers(t *testing.T, l *List, es []*Element) { - if len(es) == 0 { - if l.front != nil || l.back != nil { - t.Errorf("l.front/l.back = %v/%v should be nil/nil", l.front, l.back) - } + root := &l.root + + if !checkListLen(t, l, len(es)) { return } - if l.front != es[0] { - t.Errorf("l.front = %v, want %v", l.front, es[0]) - } - if last := es[len(es)-1]; l.back != last { - t.Errorf("l.back = %v, want %v", l.back, last) + // zero length lists must be the zero value or properly initialized (sentinel circle) + if len(es) == 0 { + if l.root.next != nil && l.root.next != root || l.root.prev != nil && l.root.prev != root { + t.Errorf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.root.next, l.root.prev, root) + } + return } + // len(es) > 0 + // check internal and external prev/next connections for i, e := range es { - var e_prev, e_next *Element = nil, nil + prev := root + Prev := (*Element)(nil) if i > 0 { - e_prev = es[i-1] + prev = es[i-1] + Prev = prev + } + if p := e.prev; p != prev { + t.Errorf("elt[%d](%p).prev = %p, want %p", i, e, p, prev) + } + if p := e.Prev(); p != Prev { + t.Errorf("elt[%d](%p).Prev() = %p, want %p", i, e, p, Prev) } + + next := root + Next := (*Element)(nil) if i < len(es)-1 { - e_next = es[i+1] + next = es[i+1] + Next = next } - if e.prev != e_prev { - t.Errorf("elt #%d (%v) has prev=%v, want %v", i, e, e.prev, e_prev) + if n := e.next; n != next { + t.Errorf("elt[%d](%p).next = %p, want %p", i, e, n, next) } - if e.next != e_next { - t.Errorf("elt #%d (%v) has next=%v, want %v", i, e, e.next, e_next) + if n := e.Next(); n != Next { + t.Errorf("elt[%d](%p).Next() = %p, want %p", i, e, n, Next) } } } -func checkListLen(t *testing.T, l *List, n int) { - if an := l.Len(); an != n { - t.Errorf("l.Len() = %d, want %d", an, n) - } -} - func TestList(t *testing.T) { l := New() checkListPointers(t, l, []*Element{}) - checkListLen(t, l, 0) // Single element list e := l.PushFront("a") - checkListLen(t, l, 1) checkListPointers(t, l, []*Element{e}) l.MoveToFront(e) checkListPointers(t, l, []*Element{e}) l.MoveToBack(e) checkListPointers(t, l, []*Element{e}) - checkListLen(t, l, 1) l.Remove(e) checkListPointers(t, l, []*Element{}) - checkListLen(t, l, 0) // Bigger list e2 := l.PushFront(2) @@ -70,11 +80,9 @@ func TestList(t *testing.T) { e3 := l.PushBack(3) e4 := l.PushBack("banana") checkListPointers(t, l, []*Element{e1, e2, e3, e4}) - checkListLen(t, l, 4) l.Remove(e2) checkListPointers(t, l, []*Element{e1, e3, e4}) - checkListLen(t, l, 3) l.MoveToFront(e3) // move from middle checkListPointers(t, l, []*Element{e3, e1, e4}) @@ -121,7 +129,7 @@ func TestList(t *testing.T) { } } if sum != 4 { - t.Errorf("sum over l.Iter() = %d, want 4", sum) + t.Errorf("sum over l = %d, want 4", sum) } // Clear all elements by iterating @@ -131,19 +139,18 @@ func TestList(t *testing.T) { l.Remove(e) } checkListPointers(t, l, []*Element{}) - checkListLen(t, l, 0) } func checkList(t *testing.T, l *List, es []interface{}) { - if l.Len() != len(es) { - t.Errorf("list has len=%v, want %v", l.Len(), len(es)) + if !checkListLen(t, l, len(es)) { return } + i := 0 for e := l.Front(); e != nil; e = e.Next() { le := e.Value.(int) if le != es[i] { - t.Errorf("elt #%d has value=%v, want %v", i, le, es[i]) + t.Errorf("elt[%d].Value = %v, want %v", i, le, es[i]) } i++ } @@ -202,8 +209,27 @@ func TestRemove(t *testing.T) { e := l.Front() l.Remove(e) checkListPointers(t, l, []*Element{e2}) - checkListLen(t, l, 1) l.Remove(e) checkListPointers(t, l, []*Element{e2}) - checkListLen(t, l, 1) +} + +func TestIssue4103(t *testing.T) { + l1 := New() + l1.PushBack(1) + l1.PushBack(2) + + l2 := New() + l2.PushBack(3) + l2.PushBack(4) + + e := l1.Front() + l2.Remove(e) // l2 should not change because e is not an element of l2 + if n := l2.Len(); n != 2 { + t.Errorf("l2.Len() = %d, want 2", n) + } + + l1.InsertBefore(8, e) + if n := l1.Len(); n != 3 { + t.Errorf("l1.Len() = %d, want 3", n) + } } diff --git a/src/pkg/container/ring/ring.go b/src/pkg/container/ring/ring.go index 1d96918d3..6d3b3e5b3 100644 --- a/src/pkg/container/ring/ring.go +++ b/src/pkg/container/ring/ring.go @@ -74,7 +74,7 @@ func New(n int) *Ring { return r } -// Link connects ring r with with ring s such that r.Next() +// Link connects ring r with ring s such that r.Next() // becomes s and returns the original value for r.Next(). // r must not be empty. // diff --git a/src/pkg/crypto/aes/aes_test.go b/src/pkg/crypto/aes/aes_test.go index e500c666d..6261dd09f 100644 --- a/src/pkg/crypto/aes/aes_test.go +++ b/src/pkg/crypto/aes/aes_test.go @@ -221,7 +221,10 @@ L: if tt.dec != nil { dec = make([]uint32, len(tt.dec)) } - expandKey(tt.key, enc, dec) + // This test could only test Go version of expandKey because asm + // version might use different memory layout for expanded keys + // This is OK because we don't expose expanded keys to the outside + expandKeyGo(tt.key, enc, dec) for j, v := range enc { if v != tt.enc[j] { t.Errorf("key %d: enc[%d] = %#x, want %#x", i, j, v, tt.enc[j]) @@ -352,15 +355,39 @@ func TestCipherDecrypt(t *testing.T) { } func BenchmarkEncrypt(b *testing.B) { - b.StopTimer() tt := encryptTests[0] c, err := NewCipher(tt.key) if err != nil { b.Fatal("NewCipher:", err) } out := make([]byte, len(tt.in)) - b.StartTimer() + b.SetBytes(int64(len(out))) + b.ResetTimer() for i := 0; i < b.N; i++ { c.Encrypt(out, tt.in) } } + +func BenchmarkDecrypt(b *testing.B) { + tt := encryptTests[0] + c, err := NewCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.out)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Decrypt(out, tt.out) + } +} + +func BenchmarkExpand(b *testing.B) { + tt := encryptTests[0] + n := len(tt.key) + 28 + c := &aesCipher{make([]uint32, n), make([]uint32, n)} + b.ResetTimer() + for i := 0; i < b.N; i++ { + expandKey(tt.key, c.enc, c.dec) + } +} diff --git a/src/pkg/crypto/aes/asm_amd64.s b/src/pkg/crypto/aes/asm_amd64.s new file mode 100644 index 000000000..25decf978 --- /dev/null +++ b/src/pkg/crypto/aes/asm_amd64.s @@ -0,0 +1,287 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// func hasAsm() bool +// returns whether AES-NI is supported +TEXT ·hasAsm(SB),7,$0 + XORQ AX, AX + INCL AX + CPUID + SHRQ $25, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET + +// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) +TEXT ·encryptBlockAsm(SB),7,$0 + MOVQ nr+0(FP), CX + MOVQ xk+8(FP), AX + MOVQ dst+16(FP), DX + MOVQ src+24(FP), BX + MOVUPS 0(AX), X1 + MOVUPS 0(BX), X0 + ADDQ $16, AX + PXOR X1, X0 + SUBQ $12, CX + JE Lenc196 + JB Lenc128 +Lenc256: + MOVUPS 0(AX), X1 + AESENC X1, X0 + MOVUPS 16(AX), X1 + AESENC X1, X0 + ADDQ $32, AX +Lenc196: + MOVUPS 0(AX), X1 + AESENC X1, X0 + MOVUPS 16(AX), X1 + AESENC X1, X0 + ADDQ $32, AX +Lenc128: + MOVUPS 0(AX), X1 + AESENC X1, X0 + MOVUPS 16(AX), X1 + AESENC X1, X0 + MOVUPS 32(AX), X1 + AESENC X1, X0 + MOVUPS 48(AX), X1 + AESENC X1, X0 + MOVUPS 64(AX), X1 + AESENC X1, X0 + MOVUPS 80(AX), X1 + AESENC X1, X0 + MOVUPS 96(AX), X1 + AESENC X1, X0 + MOVUPS 112(AX), X1 + AESENC X1, X0 + MOVUPS 128(AX), X1 + AESENC X1, X0 + MOVUPS 144(AX), X1 + AESENCLAST X1, X0 + MOVUPS X0, 0(DX) + RET + +// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) +TEXT ·decryptBlockAsm(SB),7,$0 + MOVQ nr+0(FP), CX + MOVQ xk+8(FP), AX + MOVQ dst+16(FP), DX + MOVQ src+24(FP), BX + MOVUPS 0(AX), X1 + MOVUPS 0(BX), X0 + ADDQ $16, AX + PXOR X1, X0 + SUBQ $12, CX + JE Ldec196 + JB Ldec128 +Ldec256: + MOVUPS 0(AX), X1 + AESDEC X1, X0 + MOVUPS 16(AX), X1 + AESDEC X1, X0 + ADDQ $32, AX +Ldec196: + MOVUPS 0(AX), X1 + AESDEC X1, X0 + MOVUPS 16(AX), X1 + AESDEC X1, X0 + ADDQ $32, AX +Ldec128: + MOVUPS 0(AX), X1 + AESDEC X1, X0 + MOVUPS 16(AX), X1 + AESDEC X1, X0 + MOVUPS 32(AX), X1 + AESDEC X1, X0 + MOVUPS 48(AX), X1 + AESDEC X1, X0 + MOVUPS 64(AX), X1 + AESDEC X1, X0 + MOVUPS 80(AX), X1 + AESDEC X1, X0 + MOVUPS 96(AX), X1 + AESDEC X1, X0 + MOVUPS 112(AX), X1 + AESDEC X1, X0 + MOVUPS 128(AX), X1 + AESDEC X1, X0 + MOVUPS 144(AX), X1 + AESDECLAST X1, X0 + MOVUPS X0, 0(DX) + RET + +// func expandKeyAsm(nr int, key *byte, enc, dec *uint32) { +// Note that round keys are stored in uint128 format, not uint32 +TEXT ·expandKeyAsm(SB),7,$0 + MOVQ nr+0(FP), CX + MOVQ key+8(FP), AX + MOVQ enc+16(FP), BX + MOVQ dec+24(FP), DX + MOVUPS (AX), X0 + // enc + MOVUPS X0, (BX) + ADDQ $16, BX + PXOR X4, X4 // _expand_key_* expect X4 to be zero + CMPL CX, $12 + JE Lexp_enc196 + JB Lexp_enc128 +Lexp_enc256: + MOVUPS 16(AX), X2 + MOVUPS X2, (BX) + ADDQ $16, BX + AESKEYGENASSIST $0x01, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x01, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x02, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x02, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x04, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x04, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x08, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x08, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x10, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x10, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x20, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x20, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x40, X2, X1 + CALL _expand_key_256a<>(SB) + JMP Lexp_dec +Lexp_enc196: + MOVQ 16(AX), X2 + AESKEYGENASSIST $0x01, X2, X1 + CALL _expand_key_192a<>(SB) + AESKEYGENASSIST $0x02, X2, X1 + CALL _expand_key_192b<>(SB) + AESKEYGENASSIST $0x04, X2, X1 + CALL _expand_key_192a<>(SB) + AESKEYGENASSIST $0x08, X2, X1 + CALL _expand_key_192b<>(SB) + AESKEYGENASSIST $0x10, X2, X1 + CALL _expand_key_192a<>(SB) + AESKEYGENASSIST $0x20, X2, X1 + CALL _expand_key_192b<>(SB) + AESKEYGENASSIST $0x40, X2, X1 + CALL _expand_key_192a<>(SB) + AESKEYGENASSIST $0x80, X2, X1 + CALL _expand_key_192b<>(SB) + JMP Lexp_dec +Lexp_enc128: + AESKEYGENASSIST $0x01, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x02, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x04, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x08, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x10, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x20, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x40, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x80, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x1b, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x36, X0, X1 + CALL _expand_key_128<>(SB) +Lexp_dec: + // dec + SUBQ $16, BX + MOVUPS (BX), X1 + MOVUPS X1, (DX) + DECQ CX +Lexp_dec_loop: + MOVUPS -16(BX), X1 + AESIMC X1, X0 + MOVUPS X0, 16(DX) + SUBQ $16, BX + ADDQ $16, DX + DECQ CX + JNZ Lexp_dec_loop + MOVUPS -16(BX), X0 + MOVUPS X0, 16(DX) + RET + +#define PSHUFD_X0_X0_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xc0 +#define PSHUFD_X1_X1_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xc9 +TEXT _expand_key_128<>(SB),7,$0 + PSHUFD $0xff, X1, X1 + SHUFPS $0x10, X0, X4 + PXOR X4, X0 + SHUFPS $0x8c, X0, X4 + PXOR X4, X0 + PXOR X1, X0 + MOVUPS X0, (BX) + ADDQ $16, BX + RET + +#define PSLLDQ_X5_ BYTE $0x66; BYTE $0x0f; BYTE $0x73; BYTE $0xfd +#define PSHUFD_X0_X3_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xd8 +TEXT _expand_key_192a<>(SB),7,$0 + PSHUFD $0x55, X1, X1 + SHUFPS $0x10, X0, X4 + PXOR X4, X0 + SHUFPS $0x8c, X0, X4 + PXOR X4, X0 + PXOR X1, X0 + + MOVAPS X2, X5 + MOVAPS X2, X6 + PSLLDQ_X5_; BYTE $0x4 + PSHUFD $0xff, X0, X3 + PXOR X3, X2 + PXOR X5, X2 + + MOVAPS X0, X1 + SHUFPS $0x44, X0, X6 + MOVUPS X6, (BX) + SHUFPS $0x4e, X2, X1 + MOVUPS X1, 16(BX) + ADDQ $32, BX + RET + +TEXT _expand_key_192b<>(SB),7,$0 + PSHUFD $0x55, X1, X1 + SHUFPS $0x10, X0, X4 + PXOR X4, X0 + SHUFPS $0x8c, X0, X4 + PXOR X4, X0 + PXOR X1, X0 + + MOVAPS X2, X5 + PSLLDQ_X5_; BYTE $0x4 + PSHUFD $0xff, X0, X3 + PXOR X3, X2 + PXOR X5, X2 + + MOVUPS X0, (BX) + ADDQ $16, BX + RET + +TEXT _expand_key_256a<>(SB),7,$0 + JMP _expand_key_128<>(SB) + +TEXT _expand_key_256b<>(SB),7,$0 + PSHUFD $0xaa, X1, X1 + SHUFPS $0x10, X2, X4 + PXOR X4, X2 + SHUFPS $0x8c, X2, X4 + PXOR X4, X2 + PXOR X1, X2 + + MOVUPS X2, (BX) + ADDQ $16, BX + RET diff --git a/src/pkg/crypto/aes/block.go b/src/pkg/crypto/aes/block.go index b930787ce..57a7e9e25 100644 --- a/src/pkg/crypto/aes/block.go +++ b/src/pkg/crypto/aes/block.go @@ -37,7 +37,7 @@ package aes // Encrypt one block from src into dst, using the expanded key xk. -func encryptBlock(xk []uint32, dst, src []byte) { +func encryptBlockGo(xk []uint32, dst, src []byte) { var s0, s1, s2, s3, t0, t1, t2, t3 uint32 s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) @@ -82,7 +82,7 @@ func encryptBlock(xk []uint32, dst, src []byte) { } // Decrypt one block from src into dst, using the expanded key xk. -func decryptBlock(xk []uint32, dst, src []byte) { +func decryptBlockGo(xk []uint32, dst, src []byte) { var s0, s1, s2, s3, t0, t1, t2, t3 uint32 s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) @@ -139,7 +139,7 @@ func rotw(w uint32) uint32 { return w<<8 | w>>24 } // Key expansion algorithm. See FIPS-197, Figure 11. // Their rcon[i] is our powx[i-1] << 24. -func expandKey(key []byte, enc, dec []uint32) { +func expandKeyGo(key []byte, enc, dec []uint32) { // Encryption key setup. var i int nk := len(key) / 4 diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go index 7d307c93a..d931134a7 100644 --- a/src/pkg/crypto/aes/cipher.go +++ b/src/pkg/crypto/aes/cipher.go @@ -45,6 +45,10 @@ func NewCipher(key []byte) (cipher.Block, error) { func (c *aesCipher) BlockSize() int { return BlockSize } -func (c *aesCipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } +func (c *aesCipher) Encrypt(dst, src []byte) { + encryptBlock(c.enc, dst, src) +} -func (c *aesCipher) Decrypt(dst, src []byte) { decryptBlock(c.dec, dst, src) } +func (c *aesCipher) Decrypt(dst, src []byte) { + decryptBlock(c.dec, dst, src) +} diff --git a/src/pkg/crypto/aes/cipher_asm.go b/src/pkg/crypto/aes/cipher_asm.go new file mode 100644 index 000000000..21369fc38 --- /dev/null +++ b/src/pkg/crypto/aes/cipher_asm.go @@ -0,0 +1,46 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64 + +package aes + +// defined in asm_$GOARCH.s +func hasAsm() bool +func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) +func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) +func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32) + +var useAsm = hasAsm() + +func encryptBlock(xk []uint32, dst, src []byte) { + if useAsm { + encryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0]) + } else { + encryptBlockGo(xk, dst, src) + } +} +func decryptBlock(xk []uint32, dst, src []byte) { + if useAsm { + decryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0]) + } else { + decryptBlockGo(xk, dst, src) + } +} +func expandKey(key []byte, enc, dec []uint32) { + if useAsm { + rounds := 10 + switch len(key) { + case 128 / 8: + rounds = 10 + case 192 / 8: + rounds = 12 + case 256 / 8: + rounds = 14 + } + expandKeyAsm(rounds, &key[0], &enc[0], &dec[0]) + } else { + expandKeyGo(key, enc, dec) + } +} diff --git a/src/pkg/crypto/aes/cipher_generic.go b/src/pkg/crypto/aes/cipher_generic.go new file mode 100644 index 000000000..1714e0f1e --- /dev/null +++ b/src/pkg/crypto/aes/cipher_generic.go @@ -0,0 +1,19 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64 + +package aes + +func encryptBlock(xk []uint32, dst, src []byte) { + encryptBlockGo(xk, dst, src) +} + +func decryptBlock(xk []uint32, dst, src []byte) { + decryptBlockGo(xk, dst, src) +} + +func expandKey(key []byte, enc, dec []uint32) { + expandKeyGo(key, enc, dec) +} diff --git a/src/pkg/crypto/cipher/cbc.go b/src/pkg/crypto/cipher/cbc.go index a48929cf5..913a5643f 100644 --- a/src/pkg/crypto/cipher/cbc.go +++ b/src/pkg/crypto/cipher/cbc.go @@ -33,12 +33,21 @@ type cbcEncrypter cbc // mode, using the given Block. The length of iv must be the same as the // Block's block size. func NewCBCEncrypter(b Block, iv []byte) BlockMode { + if len(iv) != b.BlockSize() { + panic("cipher.NewCBCEncrypter: IV length must equal block size") + } return (*cbcEncrypter)(newCBC(b, iv)) } func (x *cbcEncrypter) BlockSize() int { return x.blockSize } func (x *cbcEncrypter) CryptBlocks(dst, src []byte) { + if len(src)%x.blockSize != 0 { + panic("crypto/cipher: input not full blocks") + } + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } for len(src) > 0 { for i := 0; i < x.blockSize; i++ { x.iv[i] ^= src[i] @@ -58,12 +67,21 @@ type cbcDecrypter cbc // mode, using the given Block. The length of iv must be the same as the // Block's block size and must match the iv used to encrypt the data. func NewCBCDecrypter(b Block, iv []byte) BlockMode { + if len(iv) != b.BlockSize() { + panic("cipher.NewCBCDecrypter: IV length must equal block size") + } return (*cbcDecrypter)(newCBC(b, iv)) } func (x *cbcDecrypter) BlockSize() int { return x.blockSize } func (x *cbcDecrypter) CryptBlocks(dst, src []byte) { + if len(src)%x.blockSize != 0 { + panic("crypto/cipher: input not full blocks") + } + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } for len(src) > 0 { x.b.Decrypt(x.tmp, src[:x.blockSize]) for i := 0; i < x.blockSize; i++ { diff --git a/src/pkg/crypto/cipher/cfb.go b/src/pkg/crypto/cipher/cfb.go index d14165a86..99006b546 100644 --- a/src/pkg/crypto/cipher/cfb.go +++ b/src/pkg/crypto/cipher/cfb.go @@ -17,6 +17,9 @@ type cfb struct { // using the given Block. The iv must be the same length as the Block's block // size. func NewCFBEncrypter(block Block, iv []byte) Stream { + if len(iv) != block.BlockSize() { + panic("cipher.NewCBFEncrypter: IV length must equal block size") + } return newCFB(block, iv, false) } @@ -24,6 +27,9 @@ func NewCFBEncrypter(block Block, iv []byte) Stream { // using the given Block. The iv must be the same length as the Block's block // size. func NewCFBDecrypter(block Block, iv []byte) Stream { + if len(iv) != block.BlockSize() { + panic("cipher.NewCBFEncrypter: IV length must equal block size") + } return newCFB(block, iv, true) } diff --git a/src/pkg/crypto/cipher/cipher_test.go b/src/pkg/crypto/cipher/cipher_test.go new file mode 100644 index 000000000..8da5bce93 --- /dev/null +++ b/src/pkg/crypto/cipher/cipher_test.go @@ -0,0 +1,36 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cipher_test + +import ( + "crypto/aes" + "crypto/cipher" + "testing" +) + +func TestCryptBlocks(t *testing.T) { + buf := make([]byte, 16) + block, _ := aes.NewCipher(buf) + + mode := cipher.NewCBCDecrypter(block, buf) + mustPanic(t, "crypto/cipher: input not full blocks", func() { mode.CryptBlocks(buf, buf[:3]) }) + mustPanic(t, "crypto/cipher: output smaller than input", func() { mode.CryptBlocks(buf[:3], buf) }) + + mode = cipher.NewCBCEncrypter(block, buf) + mustPanic(t, "crypto/cipher: input not full blocks", func() { mode.CryptBlocks(buf, buf[:3]) }) + mustPanic(t, "crypto/cipher: output smaller than input", func() { mode.CryptBlocks(buf[:3], buf) }) +} + +func mustPanic(t *testing.T, msg string, f func()) { + defer func() { + err := recover() + if err == nil { + t.Errorf("function did not panic, wanted %q", msg) + } else if err != msg { + t.Errorf("got panic %v, wanted %q", err, msg) + } + }() + f() +} diff --git a/src/pkg/crypto/cipher/ctr.go b/src/pkg/crypto/cipher/ctr.go index 147b74fc2..d9ee9d827 100644 --- a/src/pkg/crypto/cipher/ctr.go +++ b/src/pkg/crypto/cipher/ctr.go @@ -23,7 +23,7 @@ type ctr struct { // counter mode. The length of iv must be the same as the Block's block size. func NewCTR(block Block, iv []byte) Stream { if len(iv) != block.BlockSize() { - panic("cipher.NewCTR: iv length must equal block size") + panic("cipher.NewCTR: IV length must equal block size") } return &ctr{ diff --git a/src/pkg/crypto/cipher/example_test.go b/src/pkg/crypto/cipher/example_test.go new file mode 100644 index 000000000..e0027cac2 --- /dev/null +++ b/src/pkg/crypto/cipher/example_test.go @@ -0,0 +1,283 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cipher_test + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "os" +) + +func ExampleNewCBCDecrypter() { + key := []byte("example key 1234") + ciphertext, _ := hex.DecodeString("f363f3ccdcb12bb883abf484ba77d9cd7d32b5baecb3d4b1b3e0e4beffdb3ded") + + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + if len(ciphertext) < aes.BlockSize { + panic("ciphertext too short") + } + iv := ciphertext[:aes.BlockSize] + ciphertext = ciphertext[aes.BlockSize:] + + // CBC mode always works in whole blocks. + if len(ciphertext)%aes.BlockSize != 0 { + panic("ciphertext is not a multiple of the block size") + } + + mode := cipher.NewCBCDecrypter(block, iv) + + // CryptBlocks can work in-place if the two arguments are the same. + mode.CryptBlocks(ciphertext, ciphertext) + + // If the original plaintext lengths are not a multiple of the block + // size, padding would have to be added when encrypting, which would be + // removed at this point. For an example, see + // https://tools.ietf.org/html/rfc5246#section-6.2.3.2. However, it's + // critical to note that ciphertexts must be authenticated (i.e. by + // using crypto/hmac) before being decrypted in order to avoid creating + // a padding oracle. + + fmt.Printf("%s\n", ciphertext) + // Output: exampleplaintext +} + +func ExampleNewCBCEncrypter() { + key := []byte("example key 1234") + plaintext := []byte("exampleplaintext") + + // CBC mode works on blocks so plaintexts may need to be padded to the + // next whole block. For an example of such padding, see + // https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll + // assume that the plaintext is already of the correct length. + if len(plaintext)%aes.BlockSize != 0 { + panic("plaintext is not a multiple of the block size") + } + + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + ciphertext := make([]byte, aes.BlockSize+len(plaintext)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic(err) + } + + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext) + + // It's important to remember that ciphertexts must be authenticated + // (i.e. by using crypto/hmac) as well as being encrypted in order to + // be secure. + + fmt.Printf("%x\n", ciphertext) +} + +func ExampleNewCFBDecrypter() { + key := []byte("example key 1234") + ciphertext, _ := hex.DecodeString("22277966616d9bc47177bd02603d08c9a67d5380d0fe8cf3b44438dff7b9") + + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + if len(ciphertext) < aes.BlockSize { + panic("ciphertext too short") + } + iv := ciphertext[:aes.BlockSize] + ciphertext = ciphertext[aes.BlockSize:] + + stream := cipher.NewCFBDecrypter(block, iv) + + // XORKeyStream can work in-place if the two arguments are the same. + stream.XORKeyStream(ciphertext, ciphertext) + fmt.Printf("%s", ciphertext) + // Output: some plaintext +} + +func ExampleNewCFBEncrypter() { + key := []byte("example key 1234") + plaintext := []byte("some plaintext") + + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + ciphertext := make([]byte, aes.BlockSize+len(plaintext)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic(err) + } + + stream := cipher.NewCFBEncrypter(block, iv) + stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext) + + // It's important to remember that ciphertexts must be authenticated + // (i.e. by using crypto/hmac) as well as being encrypted in order to + // be secure. +} + +func ExampleNewCTR() { + key := []byte("example key 1234") + plaintext := []byte("some plaintext") + + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + ciphertext := make([]byte, aes.BlockSize+len(plaintext)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic(err) + } + + stream := cipher.NewCTR(block, iv) + stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext) + + // It's important to remember that ciphertexts must be authenticated + // (i.e. by using crypto/hmac) as well as being encrypted in order to + // be secure. + + // CTR mode is the same for both encryption and decryption, so we can + // also decrypt that ciphertext with NewCTR. + + plaintext2 := make([]byte, len(plaintext)) + stream = cipher.NewCTR(block, iv) + stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:]) + + fmt.Printf("%s\n", plaintext2) + // Output: some plaintext +} + +func ExampleNewOFB() { + key := []byte("example key 1234") + plaintext := []byte("some plaintext") + + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + ciphertext := make([]byte, aes.BlockSize+len(plaintext)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic(err) + } + + stream := cipher.NewOFB(block, iv) + stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext) + + // It's important to remember that ciphertexts must be authenticated + // (i.e. by using crypto/hmac) as well as being encrypted in order to + // be secure. + + // OFB mode is the same for both encryption and decryption, so we can + // also decrypt that ciphertext with NewOFB. + + plaintext2 := make([]byte, len(plaintext)) + stream = cipher.NewOFB(block, iv) + stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:]) + + fmt.Printf("%s\n", plaintext2) + // Output: some plaintext +} + +func ExampleStreamReader() { + key := []byte("example key 1234") + + inFile, err := os.Open("encrypted-file") + if err != nil { + panic(err) + } + defer inFile.Close() + + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // If the key is unique for each ciphertext, then it's ok to use a zero + // IV. + var iv [aes.BlockSize]byte + stream := cipher.NewOFB(block, iv[:]) + + outFile, err := os.OpenFile("decrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + panic(err) + } + defer outFile.Close() + + reader := &cipher.StreamReader{stream, inFile} + // Copy the input file to the output file, decrypting as we go. + if _, err := io.Copy(outFile, reader); err != nil { + panic(err) + } + + // Note that this example is simplistic in that it omits any + // authentication of the encrypted data. It you were actually to use + // StreamReader in this manner, an attacker could flip arbitrary bits in + // the output. +} + +func ExampleStreamWriter() { + key := []byte("example key 1234") + + inFile, err := os.Open("plaintext-file") + if err != nil { + panic(err) + } + defer inFile.Close() + + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + // If the key is unique for each ciphertext, then it's ok to use a zero + // IV. + var iv [aes.BlockSize]byte + stream := cipher.NewOFB(block, iv[:]) + + outFile, err := os.OpenFile("encrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + panic(err) + } + defer outFile.Close() + + writer := &cipher.StreamWriter{stream, outFile, nil} + // Copy the input file to the output file, encrypting as we go. + if _, err := io.Copy(writer, inFile); err != nil { + panic(err) + } + + // Note that this example is simplistic in that it omits any + // authentication of the encrypted data. It you were actually to use + // StreamReader in this manner, an attacker could flip arbitrary bits in + // the decrypted result. +} diff --git a/src/pkg/crypto/cipher/io.go b/src/pkg/crypto/cipher/io.go index 76048fbf3..807e8daea 100644 --- a/src/pkg/crypto/cipher/io.go +++ b/src/pkg/crypto/cipher/io.go @@ -28,13 +28,10 @@ func (r StreamReader) Read(dst []byte) (n int, err error) { type StreamWriter struct { S Stream W io.Writer - Err error + Err error // unused } func (w StreamWriter) Write(src []byte) (n int, err error) { - if w.Err != nil { - return 0, w.Err - } c := make([]byte, len(src)) w.S.XORKeyStream(c, src) n, err = w.W.Write(c) @@ -42,7 +39,6 @@ func (w StreamWriter) Write(src []byte) (n int, err error) { if err == nil { // should never happen err = io.ErrShortWrite } - w.Err = err } return } diff --git a/src/pkg/crypto/des/des_test.go b/src/pkg/crypto/des/des_test.go index e9fc23629..2e87e99b6 100644 --- a/src/pkg/crypto/des/des_test.go +++ b/src/pkg/crypto/des/des_test.go @@ -1503,3 +1503,21 @@ func TestSubstitutionTableKnownAnswerDecrypt(t *testing.T) { } } } + +func ExampleNewTripleDESCipher() { + // NewTripleDESCipher can also be used when EDE2 is required by + // duplicating the first 8 bytes of the 16-byte key. + ede2Key := []byte("example key 1234") + + var tripleDESKey []byte + tripleDESKey = append(tripleDESKey, ede2Key[:16]...) + tripleDESKey = append(tripleDESKey, ede2Key[:8]...) + + _, err := NewTripleDESCipher(tripleDESKey) + if err != nil { + panic(err) + } + + // See crypto/cipher for how to use a cipher.Block for encryption and + // decryption. +} diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go index 8508e3b4f..512d20c63 100644 --- a/src/pkg/crypto/ecdsa/ecdsa.go +++ b/src/pkg/crypto/ecdsa/ecdsa.go @@ -140,14 +140,16 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { w := new(big.Int).ModInverse(s, N) u1 := e.Mul(e, w) + u1.Mod(u1, N) u2 := w.Mul(r, w) + u2.Mod(u2, N) x1, y1 := c.ScalarBaseMult(u1.Bytes()) x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes()) - if x1.Cmp(x2) == 0 { + x, y := c.Add(x1, y1, x2, y2) + if x.Sign() == 0 && y.Sign() == 0 { return false } - x, _ := c.Add(x1, y1, x2, y2) x.Mod(x, N) return x.Cmp(r) == 0 } diff --git a/src/pkg/crypto/ecdsa/ecdsa_test.go b/src/pkg/crypto/ecdsa/ecdsa_test.go index 3a2b3efba..0c0643193 100644 --- a/src/pkg/crypto/ecdsa/ecdsa_test.go +++ b/src/pkg/crypto/ecdsa/ecdsa_test.go @@ -5,11 +5,19 @@ package ecdsa import ( + "bufio" + "compress/bzip2" "crypto/elliptic" "crypto/rand" "crypto/sha1" + "crypto/sha256" + "crypto/sha512" "encoding/hex" + "hash" + "io" "math/big" + "os" + "strings" "testing" ) @@ -72,156 +80,112 @@ func fromHex(s string) *big.Int { return r } -// These test vectors were taken from -// http://csrc.nist.gov/groups/STM/cavp/documents/dss/ecdsatestvectors.zip -var testVectors = []struct { - msg string - Qx, Qy string - r, s string - ok bool -}{ - { - "09626b45493672e48f3d1226a3aff3201960e577d33a7f72c7eb055302db8fe8ed61685dd036b554942a5737cd1512cdf811ee0c00e6dd2f08c69f08643be396e85dafda664801e772cdb7396868ac47b172245b41986aa2648cb77fbbfa562581be06651355a0c4b090f9d17d8f0ab6cced4e0c9d386cf465a516630f0231bd", - "9504b5b82d97a264d8b3735e0568decabc4b6ca275bc53cbadfc1c40", - "03426f80e477603b10dee670939623e3da91a94267fc4e51726009ed", - "81d3ac609f9575d742028dd496450a58a60eea2dcf8b9842994916e1", - "96a8c5f382c992e8f30ccce9af120b067ec1d74678fa8445232f75a5", - false, - }, - { - "96b2b6536f6df29be8567a72528aceeaccbaa66c66c534f3868ca9778b02faadb182e4ed34662e73b9d52ecbe9dc8e875fc05033c493108b380689ebf47e5b062e6a0cdb3dd34ce5fe347d92768d72f7b9b377c20aea927043b509c078ed2467d7113405d2ddd458811e6faf41c403a2a239240180f1430a6f4330df5d77de37", - "851e3100368a22478a0029353045ae40d1d8202ef4d6533cfdddafd8", - "205302ac69457dd345e86465afa72ee8c74ca97e2b0b999aec1f10c2", - "4450c2d38b697e990721aa2dbb56578d32b4f5aeb3b9072baa955ee0", - "e26d4b589166f7b4ba4b1c8fce823fa47aad22f8c9c396b8c6526e12", - false, - }, - { - "86778dbb4a068a01047a8d245d632f636c11d2ad350740b36fad90428b454ad0f120cb558d12ea5c8a23db595d87543d06d1ef489263d01ee529871eb68737efdb8ff85bc7787b61514bed85b7e01d6be209e0a4eb0db5c8df58a5c5bf706d76cb2bdf7800208639e05b89517155d11688236e6a47ed37d8e5a2b1e0adea338e", - "ad5bda09d319a717c1721acd6688d17020b31b47eef1edea57ceeffc", - "c8ce98e181770a7c9418c73c63d01494b8b80a41098c5ea50692c984", - "de5558c257ab4134e52c19d8db3b224a1899cbd08cc508ce8721d5e9", - "745db7af5a477e5046705c0a5eff1f52cb94a79d481f0c5a5e108ecd", - true, - }, - { - "4bc6ef1958556686dab1e39c3700054a304cbd8f5928603dcd97fafd1f29e69394679b638f71c9344ce6a535d104803d22119f57b5f9477e253817a52afa9bfbc9811d6cc8c8be6b6566c6ef48b439bbb532abe30627548c598867f3861ba0b154dc1c3deca06eb28df8efd28258554b5179883a36fbb1eecf4f93ee19d41e3d", - "cc5eea2edf964018bdc0504a3793e4d2145142caa09a72ac5fb8d3e8", - "a48d78ae5d08aa725342773975a00d4219cf7a8029bb8cf3c17c374a", - "67b861344b4e416d4094472faf4272f6d54a497177fbc5f9ef292836", - "1d54f3fcdad795bf3b23408ecbac3e1321d1d66f2e4e3d05f41f7020", - false, - }, - { - "bb658732acbf3147729959eb7318a2058308b2739ec58907dd5b11cfa3ecf69a1752b7b7d806fe00ec402d18f96039f0b78dbb90a59c4414fb33f1f4e02e4089de4122cd93df5263a95be4d7084e2126493892816e6a5b4ed123cb705bf930c8f67af0fb4514d5769232a9b008a803af225160ce63f675bd4872c4c97b146e5e", - "6234c936e27bf141fc7534bfc0a7eedc657f91308203f1dcbd642855", - "27983d87ca785ef4892c3591ef4a944b1deb125dd58bd351034a6f84", - "e94e05b42d01d0b965ffdd6c3a97a36a771e8ea71003de76c4ecb13f", - "1dc6464ffeefbd7872a081a5926e9fc3e66d123f1784340ba17737e9", - false, - }, - { - "7c00be9123bfa2c4290be1d8bc2942c7f897d9a5b7917e3aabd97ef1aab890f148400a89abd554d19bec9d8ed911ce57b22fbcf6d30ca2115f13ce0a3f569a23bad39ee645f624c49c60dcfc11e7d2be24de9c905596d8f23624d63dc46591d1f740e46f982bfae453f107e80db23545782be23ce43708245896fc54e1ee5c43", - "9f3f037282aaf14d4772edffff331bbdda845c3f65780498cde334f1", - "8308ee5a16e3bcb721b6bc30000a0419bc1aaedd761be7f658334066", - "6381d7804a8808e3c17901e4d283b89449096a8fba993388fa11dc54", - "8e858f6b5b253686a86b757bad23658cda53115ac565abca4e3d9f57", - false, - }, - { - "cffc122a44840dc705bb37130069921be313d8bde0b66201aebc48add028ca131914ef2e705d6bedd19dc6cf9459bbb0f27cdfe3c50483808ffcdaffbeaa5f062e097180f07a40ef4ab6ed03fe07ed6bcfb8afeb42c97eafa2e8a8df469de07317c5e1494c41547478eff4d8c7d9f0f484ad90fedf6e1c35ee68fa73f1691601", - "a03b88a10d930002c7b17ca6af2fd3e88fa000edf787dc594f8d4fd4", - "e0cf7acd6ddc758e64847fe4df9915ebda2f67cdd5ec979aa57421f5", - "387b84dcf37dc343c7d2c5beb82f0bf8bd894b395a7b894565d296c1", - "4adc12ce7d20a89ce3925e10491c731b15ddb3f339610857a21b53b4", - false, - }, - { - "26e0e0cafd85b43d16255908ccfd1f061c680df75aba3081246b337495783052ba06c60f4a486c1591a4048bae11b4d7fec4f161d80bdc9a7b79d23e44433ed625eab280521a37f23dd3e1bdc5c6a6cfaa026f3c45cf703e76dab57add93fe844dd4cda67dc3bddd01f9152579e49df60969b10f09ce9372fdd806b0c7301866", - "9a8983c42f2b5a87c37a00458b5970320d247f0c8a88536440173f7d", - "15e489ec6355351361900299088cfe8359f04fe0cab78dde952be80c", - "929a21baa173d438ec9f28d6a585a2f9abcfc0a4300898668e476dc0", - "59a853f046da8318de77ff43f26fe95a92ee296fa3f7e56ce086c872", - true, - }, - { - "1078eac124f48ae4f807e946971d0de3db3748dd349b14cca5c942560fb25401b2252744f18ad5e455d2d97ed5ae745f55ff509c6c8e64606afe17809affa855c4c4cdcaf6b69ab4846aa5624ed0687541aee6f2224d929685736c6a23906d974d3c257abce1a3fb8db5951b89ecb0cda92b5207d93f6618fd0f893c32cf6a6e", - "d6e55820bb62c2be97650302d59d667a411956138306bd566e5c3c2b", - "631ab0d64eaf28a71b9cbd27a7a88682a2167cee6251c44e3810894f", - "65af72bc7721eb71c2298a0eb4eed3cec96a737cc49125706308b129", - "bd5a987c78e2d51598dbd9c34a9035b0069c580edefdacee17ad892a", - false, - }, - { - "919deb1fdd831c23481dfdb2475dcbe325b04c34f82561ced3d2df0b3d749b36e255c4928973769d46de8b95f162b53cd666cad9ae145e7fcfba97919f703d864efc11eac5f260a5d920d780c52899e5d76f8fe66936ff82130761231f536e6a3d59792f784902c469aa897aabf9a0678f93446610d56d5e0981e4c8a563556b", - "269b455b1024eb92d860a420f143ac1286b8cce43031562ae7664574", - "baeb6ca274a77c44a0247e5eb12ca72bdd9a698b3f3ae69c9f1aaa57", - "cb4ec2160f04613eb0dfe4608486091a25eb12aa4dec1afe91cfb008", - "40b01d8cd06589481574f958b98ca08ade9d2a8fe31024375c01bb40", - false, - }, - { - "6e012361250dacf6166d2dd1aa7be544c3206a9d43464b3fcd90f3f8cf48d08ec099b59ba6fe7d9bdcfaf244120aed1695d8be32d1b1cd6f143982ab945d635fb48a7c76831c0460851a3d62b7209c30cd9c2abdbe3d2a5282a9fcde1a6f418dd23c409bc351896b9b34d7d3a1a63bbaf3d677e612d4a80fa14829386a64b33f", - "6d2d695efc6b43b13c14111f2109608f1020e3e03b5e21cfdbc82fcd", - "26a4859296b7e360b69cf40be7bd97ceaffa3d07743c8489fc47ca1b", - "9a8cb5f2fdc288b7183c5b32d8e546fc2ed1ca4285eeae00c8b572ad", - "8c623f357b5d0057b10cdb1a1593dab57cda7bdec9cf868157a79b97", - true, - }, - { - "bf6bd7356a52b234fe24d25557200971fc803836f6fec3cade9642b13a8e7af10ab48b749de76aada9d8927f9b12f75a2c383ca7358e2566c4bb4f156fce1fd4e87ef8c8d2b6b1bdd351460feb22cdca0437ac10ca5e0abbbce9834483af20e4835386f8b1c96daaa41554ceee56730aac04f23a5c765812efa746051f396566", - "14250131b2599939cf2d6bc491be80ddfe7ad9de644387ee67de2d40", - "b5dc473b5d014cd504022043c475d3f93c319a8bdcb7262d9e741803", - "4f21642f2201278a95339a80f75cc91f8321fcb3c9462562f6cbf145", - "452a5f816ea1f75dee4fd514fa91a0d6a43622981966c59a1b371ff8", - false, - }, - { - "0eb7f4032f90f0bd3cf9473d6d9525d264d14c031a10acd31a053443ed5fe919d5ac35e0be77813071b4062f0b5fdf58ad5f637b76b0b305aec18f82441b6e607b44cdf6e0e3c7c57f24e6fd565e39430af4a6b1d979821ed0175fa03e3125506847654d7e1ae904ce1190ae38dc5919e257bdac2db142a6e7cd4da6c2e83770", - "d1f342b7790a1667370a1840255ac5bbbdc66f0bc00ae977d99260ac", - "76416cabae2de9a1000b4646338b774baabfa3db4673790771220cdb", - "bc85e3fc143d19a7271b2f9e1c04b86146073f3fab4dda1c3b1f35ca", - "9a5c70ede3c48d5f43307a0c2a4871934424a3303b815df4bb0f128e", - false, - }, - { - "5cc25348a05d85e56d4b03cec450128727bc537c66ec3a9fb613c151033b5e86878632249cba83adcefc6c1e35dcd31702929c3b57871cda5c18d1cf8f9650a25b917efaed56032e43b6fc398509f0d2997306d8f26675f3a8683b79ce17128e006aa0903b39eeb2f1001be65de0520115e6f919de902b32c38d691a69c58c92", - "7e49a7abf16a792e4c7bbc4d251820a2abd22d9f2fc252a7bf59c9a6", - "44236a8fb4791c228c26637c28ae59503a2f450d4cfb0dc42aa843b9", - "084461b4050285a1a85b2113be76a17878d849e6bc489f4d84f15cd8", - "079b5bddcc4d45de8dbdfd39f69817c7e5afa454a894d03ee1eaaac3", - false, - }, - { - "1951533ce33afb58935e39e363d8497a8dd0442018fd96dff167b3b23d7206a3ee182a3194765df4768a3284e23b8696c199b4686e670d60c9d782f08794a4bccc05cffffbd1a12acd9eb1cfa01f7ebe124da66ecff4599ea7720c3be4bb7285daa1a86ebf53b042bd23208d468c1b3aa87381f8e1ad63e2b4c2ba5efcf05845", - "31945d12ebaf4d81f02be2b1768ed80784bf35cf5e2ff53438c11493", - "a62bebffac987e3b9d3ec451eb64c462cdf7b4aa0b1bbb131ceaa0a4", - "bc3c32b19e42b710bca5c6aaa128564da3ddb2726b25f33603d2af3c", - "ed1a719cc0c507edc5239d76fe50e2306c145ad252bd481da04180c0", - false, - }, -} - func TestVectors(t *testing.T) { - sha := sha1.New() + // This test runs the full set of NIST test vectors from + // http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip + // + // The SigVer.rsp file has been edited to remove test vectors for + // unsupported algorithms and has been compressed. + + if testing.Short() { + return + } + + f, err := os.Open("testdata/SigVer.rsp.bz2") + if err != nil { + t.Fatal(err) + } + + buf := bufio.NewReader(bzip2.NewReader(f)) + + lineNo := 1 + var h hash.Hash + var msg []byte + var hashed []byte + var r, s *big.Int + pub := new(PublicKey) - for i, test := range testVectors { - pub := PublicKey{ - Curve: elliptic.P224(), - X: fromHex(test.Qx), - Y: fromHex(test.Qy), + for { + line, err := buf.ReadString('\n') + if len(line) == 0 { + if err == io.EOF { + break + } + t.Fatalf("error reading from input: %s", err) } - msg, _ := hex.DecodeString(test.msg) - sha.Reset() - sha.Write(msg) - hashed := sha.Sum(nil) - r := fromHex(test.r) - s := fromHex(test.s) - if Verify(&pub, hashed, r, s) != test.ok { - t.Errorf("%d: bad result", i) + lineNo++ + // Need to remove \r\n from the end of the line. + if !strings.HasSuffix(line, "\r\n") { + t.Fatalf("bad line ending (expected \\r\\n) on line %d", lineNo) } - if testing.Short() { - break + line = line[:len(line)-2] + + if len(line) == 0 || line[0] == '#' { + continue + } + + if line[0] == '[' { + line = line[1 : len(line)-1] + parts := strings.SplitN(line, ",", 2) + + switch parts[0] { + case "P-224": + pub.Curve = elliptic.P224() + case "P-256": + pub.Curve = elliptic.P256() + case "P-384": + pub.Curve = elliptic.P384() + case "P-521": + pub.Curve = elliptic.P521() + default: + pub.Curve = nil + } + + switch parts[1] { + case "SHA-1": + h = sha1.New() + case "SHA-224": + h = sha256.New224() + case "SHA-256": + h = sha256.New() + case "SHA-384": + h = sha512.New384() + case "SHA-512": + h = sha512.New() + default: + h = nil + } + + continue + } + + if h == nil || pub.Curve == nil { + continue + } + + switch { + case strings.HasPrefix(line, "Msg = "): + if msg, err = hex.DecodeString(line[6:]); err != nil { + t.Fatalf("failed to decode message on line %d: %s", lineNo, err) + } + case strings.HasPrefix(line, "Qx = "): + pub.X = fromHex(line[5:]) + case strings.HasPrefix(line, "Qy = "): + pub.Y = fromHex(line[5:]) + case strings.HasPrefix(line, "R = "): + r = fromHex(line[4:]) + case strings.HasPrefix(line, "S = "): + s = fromHex(line[4:]) + case strings.HasPrefix(line, "Result = "): + expected := line[9] == 'P' + h.Reset() + h.Write(msg) + hashed := h.Sum(hashed[:0]) + if Verify(pub, hashed, r, s) != expected { + t.Fatalf("incorrect result on line %d", lineNo) + } + default: + t.Fatalf("unknown variable on line %d: %s", lineNo, line) } } } diff --git a/src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2 b/src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2 new file mode 100644 index 000000000..09fe2b427 Binary files /dev/null and b/src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2 differ diff --git a/src/pkg/crypto/elliptic/elliptic.go b/src/pkg/crypto/elliptic/elliptic.go index 30835a90b..7a4ff6614 100644 --- a/src/pkg/crypto/elliptic/elliptic.go +++ b/src/pkg/crypto/elliptic/elliptic.go @@ -31,10 +31,10 @@ type Curve interface { // Double returns 2*(x,y) Double(x1, y1 *big.Int) (x, y *big.Int) // ScalarMult returns k*(Bx,By) where k is a number in big-endian form. - ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) - // ScalarBaseMult returns k*G, where G is the base point of the group and k - // is an integer in big-endian form. - ScalarBaseMult(scalar []byte) (x, y *big.Int) + ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) + // ScalarBaseMult returns k*G, where G is the base point of the group + // and k is an integer in big-endian form. + ScalarBaseMult(k []byte) (x, y *big.Int) } // CurveParams contains the parameters of an elliptic curve and also provides @@ -69,9 +69,24 @@ func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool { return x3.Cmp(y2) == 0 } +// zForAffine returns a Jacobian Z value for the affine point (x, y). If x and +// y are zero, it assumes that they represent the point at infinity because (0, +// 0) is not on the any of the curves handled here. +func zForAffine(x, y *big.Int) *big.Int { + z := new(big.Int) + if x.Sign() != 0 || y.Sign() != 0 { + z.SetInt64(1) + } + return z +} + // affineFromJacobian reverses the Jacobian transform. See the comment at the -// top of the file. +// top of the file. If the point is ∞ it returns 0, 0. func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + if z.Sign() == 0 { + return new(big.Int), new(big.Int) + } + zinv := new(big.Int).ModInverse(z, curve.P) zinvsq := new(big.Int).Mul(zinv, zinv) @@ -84,14 +99,29 @@ func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big. } func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { - z := new(big.Int).SetInt64(1) - return curve.affineFromJacobian(curve.addJacobian(x1, y1, z, x2, y2, z)) + z1 := zForAffine(x1, y1) + z2 := zForAffine(x2, y2) + return curve.affineFromJacobian(curve.addJacobian(x1, y1, z1, x2, y2, z2)) } // addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and // (x2, y2, z2) and returns their sum, also in Jacobian form. func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl + x3, y3, z3 := new(big.Int), new(big.Int), new(big.Int) + if z1.Sign() == 0 { + x3.Set(x2) + y3.Set(y2) + z3.Set(z2) + return x3, y3, z3 + } + if z2.Sign() == 0 { + x3.Set(x1) + y3.Set(y1) + z3.Set(z1) + return x3, y3, z3 + } + z1z1 := new(big.Int).Mul(z1, z1) z1z1.Mod(z1z1, curve.P) z2z2 := new(big.Int).Mul(z2, z2) @@ -102,6 +132,7 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int u2 := new(big.Int).Mul(x2, z1z1) u2.Mod(u2, curve.P) h := new(big.Int).Sub(u2, u1) + xEqual := h.Sign() == 0 if h.Sign() == -1 { h.Add(h, curve.P) } @@ -119,17 +150,21 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int if r.Sign() == -1 { r.Add(r, curve.P) } + yEqual := r.Sign() == 0 + if xEqual && yEqual { + return curve.doubleJacobian(x1, y1, z1) + } r.Lsh(r, 1) v := new(big.Int).Mul(u1, i) - x3 := new(big.Int).Set(r) + x3.Set(r) x3.Mul(x3, x3) x3.Sub(x3, j) x3.Sub(x3, v) x3.Sub(x3, v) x3.Mod(x3, curve.P) - y3 := new(big.Int).Set(r) + y3.Set(r) v.Sub(v, x3) y3.Mul(y3, v) s1.Mul(s1, j) @@ -137,16 +172,10 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int y3.Sub(y3, s1) y3.Mod(y3, curve.P) - z3 := new(big.Int).Add(z1, z2) + z3.Add(z1, z2) z3.Mul(z3, z3) z3.Sub(z3, z1z1) - if z3.Sign() == -1 { - z3.Add(z3, curve.P) - } z3.Sub(z3, z2z2) - if z3.Sign() == -1 { - z3.Add(z3, curve.P) - } z3.Mul(z3, h) z3.Mod(z3, curve.P) @@ -154,7 +183,7 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int } func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { - z1 := new(big.Int).SetInt64(1) + z1 := zForAffine(x1, y1) return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1)) } @@ -219,40 +248,19 @@ func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, } func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { - // We have a slight problem in that the identity of the group (the - // point at infinity) cannot be represented in (x, y) form on a finite - // machine. Thus the standard add/double algorithm has to be tweaked - // slightly: our initial state is not the identity, but x, and we - // ignore the first true bit in |k|. If we don't find any true bits in - // |k|, then we return nil, nil, because we cannot return the identity - // element. - Bz := new(big.Int).SetInt64(1) - x := Bx - y := By - z := Bz + x, y, z := new(big.Int), new(big.Int), new(big.Int) - seenFirstTrue := false for _, byte := range k { for bitNum := 0; bitNum < 8; bitNum++ { - if seenFirstTrue { - x, y, z = curve.doubleJacobian(x, y, z) - } + x, y, z = curve.doubleJacobian(x, y, z) if byte&0x80 == 0x80 { - if !seenFirstTrue { - seenFirstTrue = true - } else { - x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z) - } + x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z) } byte <<= 1 } } - if !seenFirstTrue { - return nil, nil - } - return curve.affineFromJacobian(x, y, z) } @@ -370,7 +378,7 @@ func P384() Curve { return p384 } -// P256 returns a Curve which implements P-521 (see FIPS 186-3, section D.2.5) +// P521 returns a Curve which implements P-521 (see FIPS 186-3, section D.2.5) func P521() Curve { initonce.Do(initAll) return p521 diff --git a/src/pkg/crypto/elliptic/elliptic_test.go b/src/pkg/crypto/elliptic/elliptic_test.go index 1e3407ee0..58f903966 100644 --- a/src/pkg/crypto/elliptic/elliptic_test.go +++ b/src/pkg/crypto/elliptic/elliptic_test.go @@ -322,6 +322,44 @@ func TestGenericBaseMult(t *testing.T) { } } +func TestInfinity(t *testing.T) { + tests := []struct { + name string + curve Curve + }{ + {"p224", P224()}, + {"p256", P256()}, + } + + for _, test := range tests { + curve := test.curve + x, y := curve.ScalarBaseMult(nil) + if x.Sign() != 0 || y.Sign() != 0 { + t.Errorf("%s: x^0 != ∞", test.name) + } + x.SetInt64(0) + y.SetInt64(0) + + x2, y2 := curve.Double(x, y) + if x2.Sign() != 0 || y2.Sign() != 0 { + t.Errorf("%s: 2∞ != ∞", test.name) + } + + baseX := curve.Params().Gx + baseY := curve.Params().Gy + + x3, y3 := curve.Add(baseX, baseY, x, y) + if x3.Cmp(baseX) != 0 || y3.Cmp(baseY) != 0 { + t.Errorf("%s: x+∞ != x", test.name) + } + + x4, y4 := curve.Add(x, y, baseX, baseY) + if x4.Cmp(baseX) != 0 || y4.Cmp(baseY) != 0 { + t.Errorf("%s: ∞+x != x", test.name) + } + } +} + func BenchmarkBaseMult(b *testing.B) { b.ResetTimer() p224 := P224() diff --git a/src/pkg/crypto/elliptic/p224.go b/src/pkg/crypto/elliptic/p224.go index 17571c252..1f7ff3f9d 100644 --- a/src/pkg/crypto/elliptic/p224.go +++ b/src/pkg/crypto/elliptic/p224.go @@ -80,10 +80,14 @@ func (p224Curve) Add(bigX1, bigY1, bigX2, bigY2 *big.Int) (x, y *big.Int) { p224FromBig(&x1, bigX1) p224FromBig(&y1, bigY1) - z1[0] = 1 + if bigX1.Sign() != 0 || bigY1.Sign() != 0 { + z1[0] = 1 + } p224FromBig(&x2, bigX2) p224FromBig(&y2, bigY2) - z2[0] = 1 + if bigX2.Sign() != 0 || bigY2.Sign() != 0 { + z2[0] = 1 + } p224AddJacobian(&x3, &y3, &z3, &x1, &y1, &z1, &x2, &y2, &z2) return p224ToAffine(&x3, &y3, &z3) @@ -132,6 +136,44 @@ func (curve p224Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) { // exactly, making the reflections during a reduce much nicer. type p224FieldElement [8]uint32 +// p224P is the order of the field, represented as a p224FieldElement. +var p224P = [8]uint32{1, 0, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff} + +// p224IsZero returns 1 if a == 0 mod p and 0 otherwise. +// +// a[i] < 2**29 +func p224IsZero(a *p224FieldElement) uint32 { + // Since a p224FieldElement contains 224 bits there are two possible + // representations of 0: 0 and p. + var minimal p224FieldElement + p224Contract(&minimal, a) + + var isZero, isP uint32 + for i, v := range minimal { + isZero |= v + isP |= v - p224P[i] + } + + // If either isZero or isP is 0, then we should return 1. + isZero |= isZero >> 16 + isZero |= isZero >> 8 + isZero |= isZero >> 4 + isZero |= isZero >> 2 + isZero |= isZero >> 1 + + isP |= isP >> 16 + isP |= isP >> 8 + isP |= isP >> 4 + isP |= isP >> 2 + isP |= isP >> 1 + + // For isZero and isP, the LSB is 0 iff all the bits are zero. + result := isZero & isP + result = (^result) & 1 + + return result +} + // p224Add computes *out = a+b // // a[i] + b[i] < 2**32 @@ -406,7 +448,7 @@ func p224Contract(out, in *p224FieldElement) { // true. top4AllOnes := uint32(0xffffffff) for i := 4; i < 8; i++ { - top4AllOnes &= (out[i] & bottom28Bits) - 1 + top4AllOnes &= out[i] } top4AllOnes |= 0xf0000000 // Now we replicate any zero bits to all the bits in top4AllOnes. @@ -441,7 +483,7 @@ func p224Contract(out, in *p224FieldElement) { out3Equal = ^uint32(int32(out3Equal<<31) >> 31) // If out[3] > 0xffff000 then n's MSB will be zero. - out3GT := ^uint32(int32(n<<31) >> 31) + out3GT := ^uint32(int32(n) >> 31) mask := top4AllOnes & ((out3Equal & bottom3NonZero) | out3GT) out[0] -= 1 & mask @@ -463,6 +505,9 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) { var z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v p224FieldElement var c p224LargeFieldElement + z1IsZero := p224IsZero(z1) + z2IsZero := p224IsZero(z2) + // Z1Z1 = Z1² p224Square(&z1z1, z1, &c) // Z2Z2 = Z2² @@ -480,6 +525,7 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) { // H = U2-U1 p224Sub(&h, &u2, &u1) p224Reduce(&h) + xEqual := p224IsZero(&h) // I = (2*H)² for j := 0; j < 8; j++ { i[j] = h[j] << 1 @@ -491,6 +537,11 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) { // r = 2*(S2-S1) p224Sub(&r, &s2, &s1) p224Reduce(&r) + yEqual := p224IsZero(&r) + if xEqual == 1 && yEqual == 1 && z1IsZero == 0 && z2IsZero == 0 { + p224DoubleJacobian(x3, y3, z3, x1, y1, z1) + return + } for i := 0; i < 8; i++ { r[i] <<= 1 } @@ -524,6 +575,13 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) { p224Mul(&z1z1, &z1z1, &r, &c) p224Sub(y3, &z1z1, &s1) p224Reduce(y3) + + p224CopyConditional(x3, x2, z1IsZero) + p224CopyConditional(x3, x1, z2IsZero) + p224CopyConditional(y3, y2, z1IsZero) + p224CopyConditional(y3, y1, z2IsZero) + p224CopyConditional(z3, z2, z1IsZero) + p224CopyConditional(z3, z1, z2IsZero) } // p224DoubleJacobian computes *out = a+a. @@ -593,22 +651,19 @@ func p224CopyConditional(out, in *p224FieldElement, control uint32) { func p224ScalarMult(outX, outY, outZ, inX, inY, inZ *p224FieldElement, scalar []byte) { var xx, yy, zz p224FieldElement for i := 0; i < 8; i++ { + outX[i] = 0 + outY[i] = 0 outZ[i] = 0 } - firstBit := uint32(1) for _, byte := range scalar { for bitNum := uint(0); bitNum < 8; bitNum++ { p224DoubleJacobian(outX, outY, outZ, outX, outY, outZ) bit := uint32((byte >> (7 - bitNum)) & 1) p224AddJacobian(&xx, &yy, &zz, inX, inY, inZ, outX, outY, outZ) - p224CopyConditional(outX, inX, firstBit&bit) - p224CopyConditional(outY, inY, firstBit&bit) - p224CopyConditional(outZ, inZ, firstBit&bit) - p224CopyConditional(outX, &xx, ^firstBit&bit) - p224CopyConditional(outY, &yy, ^firstBit&bit) - p224CopyConditional(outZ, &zz, ^firstBit&bit) - firstBit = firstBit & ^bit + p224CopyConditional(outX, &xx, bit) + p224CopyConditional(outY, &yy, bit) + p224CopyConditional(outZ, &zz, bit) } } } @@ -618,16 +673,8 @@ func p224ToAffine(x, y, z *p224FieldElement) (*big.Int, *big.Int) { var zinv, zinvsq, outx, outy p224FieldElement var tmp p224LargeFieldElement - isPointAtInfinity := true - for i := 0; i < 8; i++ { - if z[i] != 0 { - isPointAtInfinity = false - break - } - } - - if isPointAtInfinity { - return nil, nil + if isPointAtInfinity := p224IsZero(z); isPointAtInfinity == 1 { + return new(big.Int), new(big.Int) } p224Invert(&zinv, z) diff --git a/src/pkg/crypto/hmac/hmac.go b/src/pkg/crypto/hmac/hmac.go index a97ce0972..b6f4919a7 100644 --- a/src/pkg/crypto/hmac/hmac.go +++ b/src/pkg/crypto/hmac/hmac.go @@ -2,13 +2,27 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as -// defined in U.S. Federal Information Processing Standards Publication 198. -// An HMAC is a cryptographic hash that uses a key to sign a message. -// The receiver verifies the hash by recomputing it using the same key. +/* +Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as +defined in U.S. Federal Information Processing Standards Publication 198. +An HMAC is a cryptographic hash that uses a key to sign a message. +The receiver verifies the hash by recomputing it using the same key. + +Receivers should be careful to use Equal to compare MACs in order to avoid +timing side-channels: + + // CheckMAC returns true if messageMAC is a valid HMAC tag for message. + func CheckMAC(message, messageMAC, key []byte) bool { + mac := hmac.New(sha256.New, key) + mac.Write(message) + expectedMAC := mac.Sum(nil) + return hmac.Equal(messageMAC, expectedMAC) + } +*/ package hmac import ( + "crypto/subtle" "hash" ) @@ -57,7 +71,7 @@ func (h *hmac) BlockSize() int { return h.blocksize } func (h *hmac) Reset() { h.inner.Reset() h.tmpPad(0x36) - h.inner.Write(h.tmp[0:h.blocksize]) + h.inner.Write(h.tmp[:h.blocksize]) } // New returns a new HMAC hash using the given hash.Hash type and key. @@ -78,3 +92,11 @@ func New(h func() hash.Hash, key []byte) hash.Hash { hm.Reset() return hm } + +// Equal compares two MACs for equality without leaking timing information. +func Equal(mac1, mac2 []byte) bool { + // We don't have to be constant time if the lengths of the MACs are + // different as that suggests that a completely different hash function + // was used. + return len(mac1) == len(mac2) && subtle.ConstantTimeCompare(mac1, mac2) == 1 +} diff --git a/src/pkg/crypto/hmac/hmac_test.go b/src/pkg/crypto/hmac/hmac_test.go index 07957414c..d4860424e 100644 --- a/src/pkg/crypto/hmac/hmac_test.go +++ b/src/pkg/crypto/hmac/hmac_test.go @@ -491,3 +491,22 @@ func TestHMAC(t *testing.T) { } } } + +func TestEqual(t *testing.T) { + a := []byte("test") + b := []byte("test1") + c := []byte("test2") + + if !Equal(b, b) { + t.Error("Equal failed with equal arguments") + } + if Equal(a, b) { + t.Error("Equal accepted a prefix of the second argument") + } + if Equal(b, a) { + t.Error("Equal accepted a prefix of the first argument") + } + if Equal(b, c) { + t.Error("Equal accepted unequal slices") + } +} diff --git a/src/pkg/crypto/md5/gen.go b/src/pkg/crypto/md5/gen.go new file mode 100644 index 000000000..966bdae26 --- /dev/null +++ b/src/pkg/crypto/md5/gen.go @@ -0,0 +1,300 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// This program generates md5block.go +// Invoke as +// +// go run gen.go [-full] |gofmt >md5block.go +// +// The -full flag causes the generated code to do a full +// (16x) unrolling instead of a 4x unrolling. + +package main + +import ( + "flag" + "log" + "os" + "strings" + "text/template" +) + +func main() { + flag.Parse() + + t := template.Must(template.New("main").Funcs(funcs).Parse(program)) + if err := t.Execute(os.Stdout, data); err != nil { + log.Fatal(err) + } +} + +type Data struct { + a, b, c, d string + Shift1 []int + Shift2 []int + Shift3 []int + Shift4 []int + Table1 []uint32 + Table2 []uint32 + Table3 []uint32 + Table4 []uint32 + Full bool +} + +var funcs = template.FuncMap{ + "dup": dup, + "relabel": relabel, + "rotate": rotate, +} + +func dup(count int, x []int) []int { + var out []int + for i := 0; i < count; i++ { + out = append(out, x...) + } + return out +} + +func relabel(s string) string { + return strings.NewReplacer("a", data.a, "b", data.b, "c", data.c, "d", data.d).Replace(s) +} + +func rotate() string { + data.a, data.b, data.c, data.d = data.d, data.a, data.b, data.c + return "" // no output +} + +func init() { + flag.BoolVar(&data.Full, "full", false, "complete unrolling") +} + +var data = Data{ + a: "a", + b: "b", + c: "c", + d: "d", + Shift1: []int{7, 12, 17, 22}, + Shift2: []int{5, 9, 14, 20}, + Shift3: []int{4, 11, 16, 23}, + Shift4: []int{6, 10, 15, 21}, + + // table[i] = int((1<<32) * abs(sin(i+1 radians))). + Table1: []uint32{ + // round 1 + 0xd76aa478, + 0xe8c7b756, + 0x242070db, + 0xc1bdceee, + 0xf57c0faf, + 0x4787c62a, + 0xa8304613, + 0xfd469501, + 0x698098d8, + 0x8b44f7af, + 0xffff5bb1, + 0x895cd7be, + 0x6b901122, + 0xfd987193, + 0xa679438e, + 0x49b40821, + }, + Table2: []uint32{ + // round 2 + 0xf61e2562, + 0xc040b340, + 0x265e5a51, + 0xe9b6c7aa, + 0xd62f105d, + 0x2441453, + 0xd8a1e681, + 0xe7d3fbc8, + 0x21e1cde6, + 0xc33707d6, + 0xf4d50d87, + 0x455a14ed, + 0xa9e3e905, + 0xfcefa3f8, + 0x676f02d9, + 0x8d2a4c8a, + }, + Table3: []uint32{ + // round3 + 0xfffa3942, + 0x8771f681, + 0x6d9d6122, + 0xfde5380c, + 0xa4beea44, + 0x4bdecfa9, + 0xf6bb4b60, + 0xbebfbc70, + 0x289b7ec6, + 0xeaa127fa, + 0xd4ef3085, + 0x4881d05, + 0xd9d4d039, + 0xe6db99e5, + 0x1fa27cf8, + 0xc4ac5665, + }, + Table4: []uint32{ + // round 4 + 0xf4292244, + 0x432aff97, + 0xab9423a7, + 0xfc93a039, + 0x655b59c3, + 0x8f0ccc92, + 0xffeff47d, + 0x85845dd1, + 0x6fa87e4f, + 0xfe2ce6e0, + 0xa3014314, + 0x4e0811a1, + 0xf7537e82, + 0xbd3af235, + 0x2ad7d2bb, + 0xeb86d391, + }, +} + +var program = ` +package md5 + +import ( + "unsafe" + "runtime" +) + +{{if not .Full}} + var t1 = [...]uint32{ + {{range .Table1}}{{printf "\t%#x,\n" .}}{{end}} + } + + var t2 = [...]uint32{ + {{range .Table2}}{{printf "\t%#x,\n" .}}{{end}} + } + + var t3 = [...]uint32{ + {{range .Table3}}{{printf "\t%#x,\n" .}}{{end}} + } + + var t4 = [...]uint32{ + {{range .Table4}}{{printf "\t%#x,\n" .}}{{end}} + } +{{end}} + +func block(dig *digest, p []byte) { + a := dig.s[0] + b := dig.s[1] + c := dig.s[2] + d := dig.s[3] + var X *[16]uint32 + var xbuf [16]uint32 + for len(p) >= chunk { + aa, bb, cc, dd := a, b, c, d + + // This is a constant condition - it is not evaluated on each iteration. + if runtime.GOARCH == "amd64" || runtime.GOARCH == "386" { + // MD5 was designed so that x86 processors can just iterate + // over the block data directly as uint32s, and we generate + // less code and run 1.3x faster if we take advantage of that. + // My apologies. + X = (*[16]uint32)(unsafe.Pointer(&p[0])) + } else if uintptr(unsafe.Pointer(&p[0]))&(unsafe.Alignof(uint32(0))-1) == 0 { + X = (*[16]uint32)(unsafe.Pointer(&p[0])) + } else { + X = &xbuf + j := 0 + for i := 0; i < 16; i++ { + X[i&15] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 + j += 4 + } + } + + {{if .Full}} + // Round 1. + {{range $i, $s := dup 4 .Shift1}} + {{index $.Table1 $i | printf "a += (((c^d)&b)^d) + X[%d] + %d" $i | relabel}} + {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}} + {{rotate}} + {{end}} + + // Round 2. + {{range $i, $s := dup 4 .Shift2}} + {{index $.Table2 $i | printf "a += (((b^c)&d)^c) + X[(1+5*%d)&15] + %d" $i | relabel}} + {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}} + {{rotate}} + {{end}} + + // Round 3. + {{range $i, $s := dup 4 .Shift3}} + {{index $.Table3 $i | printf "a += (b^c^d) + X[(5+3*%d)&15] + %d" $i | relabel}} + {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}} + {{rotate}} + {{end}} + + // Round 4. + {{range $i, $s := dup 4 .Shift4}} + {{index $.Table4 $i | printf "a += (c^(b|^d)) + X[(7*%d)&15] + %d" $i | relabel}} + {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}} + {{rotate}} + {{end}} + {{else}} + // Round 1. + for i := uint(0); i < 16; { + {{range $s := .Shift1}} + {{printf "a += (((c^d)&b)^d) + X[i&15] + t1[i&15]" | relabel}} + {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}} + i++ + {{rotate}} + {{end}} + } + + // Round 2. + for i := uint(0); i < 16; { + {{range $s := .Shift2}} + {{printf "a += (((b^c)&d)^c) + X[(1+5*i)&15] + t2[i&15]" | relabel}} + {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}} + i++ + {{rotate}} + {{end}} + } + + // Round 3. + for i := uint(0); i < 16; { + {{range $s := .Shift3}} + {{printf "a += (b^c^d) + X[(5+3*i)&15] + t3[i&15]" | relabel}} + {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}} + i++ + {{rotate}} + {{end}} + } + + // Round 4. + for i := uint(0); i < 16; { + {{range $s := .Shift4}} + {{printf "a += (c^(b|^d)) + X[(7*i)&15] + t4[i&15]" | relabel}} + {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}} + i++ + {{rotate}} + {{end}} + } + {{end}} + + a += aa + b += bb + c += cc + d += dd + + p = p[chunk:] + } + + dig.s[0] = a + dig.s[1] = b + dig.s[2] = c + dig.s[3] = d +} +` diff --git a/src/pkg/crypto/md5/md5.go b/src/pkg/crypto/md5/md5.go index cfb728c94..825e5c8a2 100644 --- a/src/pkg/crypto/md5/md5.go +++ b/src/pkg/crypto/md5/md5.go @@ -21,26 +21,26 @@ const Size = 16 const BlockSize = 64 const ( - _Chunk = 64 - _Init0 = 0x67452301 - _Init1 = 0xEFCDAB89 - _Init2 = 0x98BADCFE - _Init3 = 0x10325476 + chunk = 64 + init0 = 0x67452301 + init1 = 0xEFCDAB89 + init2 = 0x98BADCFE + init3 = 0x10325476 ) // digest represents the partial evaluation of a checksum. type digest struct { s [4]uint32 - x [_Chunk]byte + x [chunk]byte nx int len uint64 } func (d *digest) Reset() { - d.s[0] = _Init0 - d.s[1] = _Init1 - d.s[2] = _Init2 - d.s[3] = _Init3 + d.s[0] = init0 + d.s[1] = init1 + d.s[2] = init2 + d.s[3] = init3 d.nx = 0 d.len = 0 } @@ -61,21 +61,24 @@ func (d *digest) Write(p []byte) (nn int, err error) { d.len += uint64(nn) if d.nx > 0 { n := len(p) - if n > _Chunk-d.nx { - n = _Chunk - d.nx + if n > chunk-d.nx { + n = chunk - d.nx } for i := 0; i < n; i++ { d.x[d.nx+i] = p[i] } d.nx += n - if d.nx == _Chunk { - _Block(d, d.x[0:]) + if d.nx == chunk { + block(d, d.x[0:chunk]) d.nx = 0 } p = p[n:] } - n := _Block(d, p) - p = p[n:] + if len(p) >= chunk { + n := len(p) &^ (chunk - 1) + block(d, p[:n]) + p = p[n:] + } if len(p) > 0 { d.nx = copy(d.x[:], p) } diff --git a/src/pkg/crypto/md5/md5_test.go b/src/pkg/crypto/md5/md5_test.go index aae875464..3ef4519b9 100644 --- a/src/pkg/crypto/md5/md5_test.go +++ b/src/pkg/crypto/md5/md5_test.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package md5_test +package md5 import ( - "crypto/md5" "fmt" "io" "testing" + "unsafe" ) type md5Test struct { @@ -53,14 +53,20 @@ var golden = []md5Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] - c := md5.New() - for j := 0; j < 3; j++ { + c := New() + buf := make([]byte, len(g.in)+4) + for j := 0; j < 3+4; j++ { if j < 2 { io.WriteString(c, g.in) - } else { + } else if j == 2 { io.WriteString(c, g.in[0:len(g.in)/2]) c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) + } else if j > 2 { + // test unaligned write + buf = buf[1:] + copy(buf, g.in) + c.Write(buf[:len(g.in)]) } s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { @@ -72,9 +78,53 @@ func TestGolden(t *testing.T) { } func ExampleNew() { - h := md5.New() + h := New() io.WriteString(h, "The fog is getting thicker!") io.WriteString(h, "And Leon's getting laaarger!") fmt.Printf("%x", h.Sum(nil)) // Output: e2c569be17396eca2a2e3c11578123ed } + +var bench = New() +var buf = make([]byte, 8192+1) +var sum = make([]byte, bench.Size()) + +func benchmarkSize(b *testing.B, size int, unaligned bool) { + b.SetBytes(int64(size)) + buf := buf + if unaligned { + if uintptr(unsafe.Pointer(&buf[0]))&(unsafe.Alignof(uint32(0))-1) == 0 { + buf = buf[1:] + } + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + bench.Reset() + bench.Write(buf[:size]) + bench.Sum(sum[:0]) + } +} + +func BenchmarkHash8Bytes(b *testing.B) { + benchmarkSize(b, 8, false) +} + +func BenchmarkHash1K(b *testing.B) { + benchmarkSize(b, 1024, false) +} + +func BenchmarkHash8K(b *testing.B) { + benchmarkSize(b, 8192, false) +} + +func BenchmarkHash8BytesUnaligned(b *testing.B) { + benchmarkSize(b, 8, true) +} + +func BenchmarkHash1KUnaligned(b *testing.B) { + benchmarkSize(b, 1024, true) +} + +func BenchmarkHash8KUnaligned(b *testing.B) { + benchmarkSize(b, 8192, true) +} diff --git a/src/pkg/crypto/md5/md5block.go b/src/pkg/crypto/md5/md5block.go index a887e2e05..0ca421774 100644 --- a/src/pkg/crypto/md5/md5block.go +++ b/src/pkg/crypto/md5/md5block.go @@ -1,172 +1,258 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +package md5 -// MD5 block step. -// In its own file so that a faster assembly or C version -// can be substituted easily. +import ( + "runtime" + "unsafe" +) -package md5 +const x86 = runtime.GOARCH == "amd64" || runtime.GOARCH == "386" -// table[i] = int((1<<32) * abs(sin(i+1 radians))). -var table = []uint32{ - // round 1 - 0xd76aa478, - 0xe8c7b756, - 0x242070db, - 0xc1bdceee, - 0xf57c0faf, - 0x4787c62a, - 0xa8304613, - 0xfd469501, - 0x698098d8, - 0x8b44f7af, - 0xffff5bb1, - 0x895cd7be, - 0x6b901122, - 0xfd987193, - 0xa679438e, - 0x49b40821, - - // round 2 - 0xf61e2562, - 0xc040b340, - 0x265e5a51, - 0xe9b6c7aa, - 0xd62f105d, - 0x2441453, - 0xd8a1e681, - 0xe7d3fbc8, - 0x21e1cde6, - 0xc33707d6, - 0xf4d50d87, - 0x455a14ed, - 0xa9e3e905, - 0xfcefa3f8, - 0x676f02d9, - 0x8d2a4c8a, - - // round3 - 0xfffa3942, - 0x8771f681, - 0x6d9d6122, - 0xfde5380c, - 0xa4beea44, - 0x4bdecfa9, - 0xf6bb4b60, - 0xbebfbc70, - 0x289b7ec6, - 0xeaa127fa, - 0xd4ef3085, - 0x4881d05, - 0xd9d4d039, - 0xe6db99e5, - 0x1fa27cf8, - 0xc4ac5665, - - // round 4 - 0xf4292244, - 0x432aff97, - 0xab9423a7, - 0xfc93a039, - 0x655b59c3, - 0x8f0ccc92, - 0xffeff47d, - 0x85845dd1, - 0x6fa87e4f, - 0xfe2ce6e0, - 0xa3014314, - 0x4e0811a1, - 0xf7537e82, - 0xbd3af235, - 0x2ad7d2bb, - 0xeb86d391, -} +var littleEndian bool -var shift1 = []uint{7, 12, 17, 22} -var shift2 = []uint{5, 9, 14, 20} -var shift3 = []uint{4, 11, 16, 23} -var shift4 = []uint{6, 10, 15, 21} +func init() { + x := uint32(0x04030201) + y := [4]byte{0x1, 0x2, 0x3, 0x4} + littleEndian = *(*[4]byte)(unsafe.Pointer(&x)) == y +} -func _Block(dig *digest, p []byte) int { +func block(dig *digest, p []byte) { a := dig.s[0] b := dig.s[1] c := dig.s[2] d := dig.s[3] - n := 0 - var X [16]uint32 - for len(p) >= _Chunk { + var X *[16]uint32 + var xbuf [16]uint32 + for len(p) >= chunk { aa, bb, cc, dd := a, b, c, d - j := 0 - for i := 0; i < 16; i++ { - X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 - j += 4 + // This is a constant condition - it is not evaluated on each iteration. + if x86 { + // MD5 was designed so that x86 processors can just iterate + // over the block data directly as uint32s, and we generate + // less code and run 1.3x faster if we take advantage of that. + // My apologies. + X = (*[16]uint32)(unsafe.Pointer(&p[0])) + } else if littleEndian && uintptr(unsafe.Pointer(&p[0]))&(unsafe.Alignof(uint32(0))-1) == 0 { + X = (*[16]uint32)(unsafe.Pointer(&p[0])) + } else { + X = &xbuf + j := 0 + for i := 0; i < 16; i++ { + X[i&15] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 + j += 4 + } } - // If this needs to be made faster in the future, - // the usual trick is to unroll each of these - // loops by a factor of 4; that lets you replace - // the shift[] lookups with constants and, - // with suitable variable renaming in each - // unrolled body, delete the a, b, c, d = d, a, b, c - // (or you can let the optimizer do the renaming). - // - // The index variables are uint so that % by a power - // of two can be optimized easily by a compiler. - // Round 1. - for i := uint(0); i < 16; i++ { - x := i - s := shift1[i%4] - f := ((c ^ d) & b) ^ d - a += f + X[x] + table[i] - a = a<>(32-s) + b - a, b, c, d = d, a, b, c - } + + a += (((c ^ d) & b) ^ d) + X[0] + 3614090360 + a = a<<7 | a>>(32-7) + b + + d += (((b ^ c) & a) ^ c) + X[1] + 3905402710 + d = d<<12 | d>>(32-12) + a + + c += (((a ^ b) & d) ^ b) + X[2] + 606105819 + c = c<<17 | c>>(32-17) + d + + b += (((d ^ a) & c) ^ a) + X[3] + 3250441966 + b = b<<22 | b>>(32-22) + c + + a += (((c ^ d) & b) ^ d) + X[4] + 4118548399 + a = a<<7 | a>>(32-7) + b + + d += (((b ^ c) & a) ^ c) + X[5] + 1200080426 + d = d<<12 | d>>(32-12) + a + + c += (((a ^ b) & d) ^ b) + X[6] + 2821735955 + c = c<<17 | c>>(32-17) + d + + b += (((d ^ a) & c) ^ a) + X[7] + 4249261313 + b = b<<22 | b>>(32-22) + c + + a += (((c ^ d) & b) ^ d) + X[8] + 1770035416 + a = a<<7 | a>>(32-7) + b + + d += (((b ^ c) & a) ^ c) + X[9] + 2336552879 + d = d<<12 | d>>(32-12) + a + + c += (((a ^ b) & d) ^ b) + X[10] + 4294925233 + c = c<<17 | c>>(32-17) + d + + b += (((d ^ a) & c) ^ a) + X[11] + 2304563134 + b = b<<22 | b>>(32-22) + c + + a += (((c ^ d) & b) ^ d) + X[12] + 1804603682 + a = a<<7 | a>>(32-7) + b + + d += (((b ^ c) & a) ^ c) + X[13] + 4254626195 + d = d<<12 | d>>(32-12) + a + + c += (((a ^ b) & d) ^ b) + X[14] + 2792965006 + c = c<<17 | c>>(32-17) + d + + b += (((d ^ a) & c) ^ a) + X[15] + 1236535329 + b = b<<22 | b>>(32-22) + c // Round 2. - for i := uint(0); i < 16; i++ { - x := (1 + 5*i) % 16 - s := shift2[i%4] - g := ((b ^ c) & d) ^ c - a += g + X[x] + table[i+16] - a = a<>(32-s) + b - a, b, c, d = d, a, b, c - } + + a += (((b ^ c) & d) ^ c) + X[(1+5*0)&15] + 4129170786 + a = a<<5 | a>>(32-5) + b + + d += (((a ^ b) & c) ^ b) + X[(1+5*1)&15] + 3225465664 + d = d<<9 | d>>(32-9) + a + + c += (((d ^ a) & b) ^ a) + X[(1+5*2)&15] + 643717713 + c = c<<14 | c>>(32-14) + d + + b += (((c ^ d) & a) ^ d) + X[(1+5*3)&15] + 3921069994 + b = b<<20 | b>>(32-20) + c + + a += (((b ^ c) & d) ^ c) + X[(1+5*4)&15] + 3593408605 + a = a<<5 | a>>(32-5) + b + + d += (((a ^ b) & c) ^ b) + X[(1+5*5)&15] + 38016083 + d = d<<9 | d>>(32-9) + a + + c += (((d ^ a) & b) ^ a) + X[(1+5*6)&15] + 3634488961 + c = c<<14 | c>>(32-14) + d + + b += (((c ^ d) & a) ^ d) + X[(1+5*7)&15] + 3889429448 + b = b<<20 | b>>(32-20) + c + + a += (((b ^ c) & d) ^ c) + X[(1+5*8)&15] + 568446438 + a = a<<5 | a>>(32-5) + b + + d += (((a ^ b) & c) ^ b) + X[(1+5*9)&15] + 3275163606 + d = d<<9 | d>>(32-9) + a + + c += (((d ^ a) & b) ^ a) + X[(1+5*10)&15] + 4107603335 + c = c<<14 | c>>(32-14) + d + + b += (((c ^ d) & a) ^ d) + X[(1+5*11)&15] + 1163531501 + b = b<<20 | b>>(32-20) + c + + a += (((b ^ c) & d) ^ c) + X[(1+5*12)&15] + 2850285829 + a = a<<5 | a>>(32-5) + b + + d += (((a ^ b) & c) ^ b) + X[(1+5*13)&15] + 4243563512 + d = d<<9 | d>>(32-9) + a + + c += (((d ^ a) & b) ^ a) + X[(1+5*14)&15] + 1735328473 + c = c<<14 | c>>(32-14) + d + + b += (((c ^ d) & a) ^ d) + X[(1+5*15)&15] + 2368359562 + b = b<<20 | b>>(32-20) + c // Round 3. - for i := uint(0); i < 16; i++ { - x := (5 + 3*i) % 16 - s := shift3[i%4] - h := b ^ c ^ d - a += h + X[x] + table[i+32] - a = a<>(32-s) + b - a, b, c, d = d, a, b, c - } + + a += (b ^ c ^ d) + X[(5+3*0)&15] + 4294588738 + a = a<<4 | a>>(32-4) + b + + d += (a ^ b ^ c) + X[(5+3*1)&15] + 2272392833 + d = d<<11 | d>>(32-11) + a + + c += (d ^ a ^ b) + X[(5+3*2)&15] + 1839030562 + c = c<<16 | c>>(32-16) + d + + b += (c ^ d ^ a) + X[(5+3*3)&15] + 4259657740 + b = b<<23 | b>>(32-23) + c + + a += (b ^ c ^ d) + X[(5+3*4)&15] + 2763975236 + a = a<<4 | a>>(32-4) + b + + d += (a ^ b ^ c) + X[(5+3*5)&15] + 1272893353 + d = d<<11 | d>>(32-11) + a + + c += (d ^ a ^ b) + X[(5+3*6)&15] + 4139469664 + c = c<<16 | c>>(32-16) + d + + b += (c ^ d ^ a) + X[(5+3*7)&15] + 3200236656 + b = b<<23 | b>>(32-23) + c + + a += (b ^ c ^ d) + X[(5+3*8)&15] + 681279174 + a = a<<4 | a>>(32-4) + b + + d += (a ^ b ^ c) + X[(5+3*9)&15] + 3936430074 + d = d<<11 | d>>(32-11) + a + + c += (d ^ a ^ b) + X[(5+3*10)&15] + 3572445317 + c = c<<16 | c>>(32-16) + d + + b += (c ^ d ^ a) + X[(5+3*11)&15] + 76029189 + b = b<<23 | b>>(32-23) + c + + a += (b ^ c ^ d) + X[(5+3*12)&15] + 3654602809 + a = a<<4 | a>>(32-4) + b + + d += (a ^ b ^ c) + X[(5+3*13)&15] + 3873151461 + d = d<<11 | d>>(32-11) + a + + c += (d ^ a ^ b) + X[(5+3*14)&15] + 530742520 + c = c<<16 | c>>(32-16) + d + + b += (c ^ d ^ a) + X[(5+3*15)&15] + 3299628645 + b = b<<23 | b>>(32-23) + c // Round 4. - for i := uint(0); i < 16; i++ { - x := (7 * i) % 16 - s := shift4[i%4] - j := c ^ (b | ^d) - a += j + X[x] + table[i+48] - a = a<>(32-s) + b - a, b, c, d = d, a, b, c - } + + a += (c ^ (b | ^d)) + X[(7*0)&15] + 4096336452 + a = a<<6 | a>>(32-6) + b + + d += (b ^ (a | ^c)) + X[(7*1)&15] + 1126891415 + d = d<<10 | d>>(32-10) + a + + c += (a ^ (d | ^b)) + X[(7*2)&15] + 2878612391 + c = c<<15 | c>>(32-15) + d + + b += (d ^ (c | ^a)) + X[(7*3)&15] + 4237533241 + b = b<<21 | b>>(32-21) + c + + a += (c ^ (b | ^d)) + X[(7*4)&15] + 1700485571 + a = a<<6 | a>>(32-6) + b + + d += (b ^ (a | ^c)) + X[(7*5)&15] + 2399980690 + d = d<<10 | d>>(32-10) + a + + c += (a ^ (d | ^b)) + X[(7*6)&15] + 4293915773 + c = c<<15 | c>>(32-15) + d + + b += (d ^ (c | ^a)) + X[(7*7)&15] + 2240044497 + b = b<<21 | b>>(32-21) + c + + a += (c ^ (b | ^d)) + X[(7*8)&15] + 1873313359 + a = a<<6 | a>>(32-6) + b + + d += (b ^ (a | ^c)) + X[(7*9)&15] + 4264355552 + d = d<<10 | d>>(32-10) + a + + c += (a ^ (d | ^b)) + X[(7*10)&15] + 2734768916 + c = c<<15 | c>>(32-15) + d + + b += (d ^ (c | ^a)) + X[(7*11)&15] + 1309151649 + b = b<<21 | b>>(32-21) + c + + a += (c ^ (b | ^d)) + X[(7*12)&15] + 4149444226 + a = a<<6 | a>>(32-6) + b + + d += (b ^ (a | ^c)) + X[(7*13)&15] + 3174756917 + d = d<<10 | d>>(32-10) + a + + c += (a ^ (d | ^b)) + X[(7*14)&15] + 718787259 + c = c<<15 | c>>(32-15) + d + + b += (d ^ (c | ^a)) + X[(7*15)&15] + 3951481745 + b = b<<21 | b>>(32-21) + c a += aa b += bb c += cc d += dd - p = p[_Chunk:] - n += _Chunk + p = p[chunk:] } dig.s[0] = a dig.s[1] = b dig.s[2] = c dig.s[3] = d - return n } diff --git a/src/pkg/crypto/rand/example_test.go b/src/pkg/crypto/rand/example_test.go new file mode 100644 index 000000000..5af8e46f5 --- /dev/null +++ b/src/pkg/crypto/rand/example_test.go @@ -0,0 +1,29 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand_test + +import ( + "bytes" + "crypto/rand" + "fmt" + "io" +) + +// This example reads 10 cryptographically secure pseudorandom numbers from +// rand.Reader and writes them to a byte slice. +func ExampleRead() { + c := 10 + b := make([]byte, c) + n, err := io.ReadFull(rand.Reader, b) + if n != len(b) || err != nil { + fmt.Println("error:", err) + return + } + // The slice should now contain random bytes instead of only zeroes. + fmt.Println(bytes.Equal(b, make([]byte, c))) + + // Output: + // false +} diff --git a/src/pkg/crypto/rand/rand_test.go b/src/pkg/crypto/rand/rand_test.go index be3a5a221..e46e61d37 100644 --- a/src/pkg/crypto/rand/rand_test.go +++ b/src/pkg/crypto/rand/rand_test.go @@ -7,6 +7,7 @@ package rand import ( "bytes" "compress/flate" + "io" "testing" ) @@ -16,9 +17,9 @@ func TestRead(t *testing.T) { n = 1e5 } b := make([]byte, n) - n, err := Read(b) + n, err := io.ReadFull(Reader, b) if n != len(b) || err != nil { - t.Fatalf("Read(buf) = %d, %s", n, err) + t.Fatalf("ReadFull(buf) = %d, %s", n, err) } var z bytes.Buffer @@ -29,3 +30,14 @@ func TestRead(t *testing.T) { t.Fatalf("Compressed %d -> %d", len(b), z.Len()) } } + +func TestReadEmpty(t *testing.T) { + n, err := Reader.Read(make([]byte, 0)) + if n != 0 || err != nil { + t.Fatalf("Read(make([]byte, 0)) = %d, %v", n, err) + } + n, err = Reader.Read(nil) + if n != 0 || err != nil { + t.Fatalf("Read(nil) = %d, %v", n, err) + } +} diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go index 5eb4cda2b..18f482472 100644 --- a/src/pkg/crypto/rand/rand_unix.go +++ b/src/pkg/crypto/rand/rand_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd +// +build darwin freebsd linux netbsd openbsd plan9 // Unix cryptographically secure pseudorandom number // generator. @@ -15,6 +15,7 @@ import ( "crypto/cipher" "io" "os" + "runtime" "sync" "time" ) @@ -22,7 +23,13 @@ import ( // Easy implementation: read from /dev/urandom. // This is sufficient on Linux, OS X, and FreeBSD. -func init() { Reader = &devReader{name: "/dev/urandom"} } +func init() { + if runtime.GOOS == "plan9" { + Reader = newReader(nil) + } else { + Reader = &devReader{name: "/dev/urandom"} + } +} // A devReader satisfies reads by reading the file named name. type devReader struct { @@ -39,14 +46,17 @@ func (r *devReader) Read(b []byte) (n int, err error) { if f == nil { return 0, err } - r.f = bufio.NewReader(f) + if runtime.GOOS == "plan9" { + r.f = f + } else { + r.f = bufio.NewReader(f) + } } return r.f.Read(b) } // Alternate pseudo-random implementation for use on -// systems without a reliable /dev/urandom. So far we -// haven't needed it. +// systems without a reliable /dev/urandom. // newReader returns a new pseudorandom generator that // seeds itself by reading from entropy. If entropy == nil, diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go index 2b2bd4bba..82b39b64a 100644 --- a/src/pkg/crypto/rand/rand_windows.go +++ b/src/pkg/crypto/rand/rand_windows.go @@ -35,6 +35,10 @@ func (r *rngReader) Read(b []byte) (n int, err error) { } } r.mu.Unlock() + + if len(b) == 0 { + return 0, nil + } err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) if err != nil { return 0, os.NewSyscallError("CryptGenRandom", err) diff --git a/src/pkg/crypto/rand/util.go b/src/pkg/crypto/rand/util.go index 5391c1829..50e5b162b 100644 --- a/src/pkg/crypto/rand/util.go +++ b/src/pkg/crypto/rand/util.go @@ -10,6 +10,21 @@ import ( "math/big" ) +// smallPrimes is a list of small, prime numbers that allows us to rapidly +// exclude some fraction of composite candidates when searching for a random +// prime. This list is truncated at the point where smallPrimesProduct exceeds +// a uint64. It does not include two because we ensure that the candidates are +// odd by construction. +var smallPrimes = []uint8{ + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, +} + +// smallPrimesProduct is the product of the values in smallPrimes and allows us +// to reduce a candidate prime by this number and then determine whether it's +// coprime to all the elements of smallPrimes without further big.Int +// operations. +var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365) + // Prime returns a number, p, of the given size, such that p is prime // with high probability. func Prime(rand io.Reader, bits int) (p *big.Int, err error) { @@ -25,6 +40,8 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err error) { bytes := make([]byte, (bits+7)/8) p = new(big.Int) + bigMod := new(big.Int) + for { _, err = io.ReadFull(rand, bytes) if err != nil { @@ -33,13 +50,51 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err error) { // Clear bits in the first byte to make sure the candidate has a size <= bits. bytes[0] &= uint8(int(1<= 2 { + bytes[0] |= 3 << (b - 2) + } else { + // Here b==1, because b cannot be zero. + bytes[0] |= 1 + if len(bytes) > 1 { + bytes[1] |= 0x80 + } + } // Make the value odd since an even number this large certainly isn't prime. bytes[len(bytes)-1] |= 1 p.SetBytes(bytes) - if p.ProbablyPrime(20) { + + // Calculate the value mod the product of smallPrimes. If it's + // a multiple of any of these primes we add two until it isn't. + // The probability of overflowing is minimal and can be ignored + // because we still perform Miller-Rabin tests on the result. + bigMod.Mod(p, smallPrimesProduct) + mod := bigMod.Uint64() + + NextDelta: + for delta := uint64(0); delta < 1<<20; delta += 2 { + m := mod + delta + for _, prime := range smallPrimes { + if m%uint64(prime) == 0 { + continue NextDelta + } + } + + if delta > 0 { + bigMod.SetUint64(delta) + p.Add(p, bigMod) + } + break + } + + // There is a tiny possibility that, by adding delta, we caused + // the number to be one bit too long. Thus we check BitLen + // here. + if p.ProbablyPrime(20) && p.BitLen() == bits { return } } diff --git a/src/pkg/crypto/rc4/rc4.go b/src/pkg/crypto/rc4/rc4.go index 1bb278f74..e0c33fa4b 100644 --- a/src/pkg/crypto/rc4/rc4.go +++ b/src/pkg/crypto/rc4/rc4.go @@ -42,17 +42,6 @@ func NewCipher(key []byte) (*Cipher, error) { return &c, nil } -// XORKeyStream sets dst to the result of XORing src with the key stream. -// Dst and src may be the same slice but otherwise should not overlap. -func (c *Cipher) XORKeyStream(dst, src []byte) { - for i := range src { - c.i += 1 - c.j += c.s[c.i] - c.s[c.i], c.s[c.j] = c.s[c.j], c.s[c.i] - dst[i] = src[i] ^ c.s[c.s[c.i]+c.s[c.j]] - } -} - // Reset zeros the key data so that it will no longer appear in the // process's memory. func (c *Cipher) Reset() { diff --git a/src/pkg/crypto/rc4/rc4_amd64.s b/src/pkg/crypto/rc4/rc4_amd64.s new file mode 100644 index 000000000..ffe9ada85 --- /dev/null +++ b/src/pkg/crypto/rc4/rc4_amd64.s @@ -0,0 +1,53 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8) +TEXT ·xorKeyStream(SB),7,$0 + MOVQ dst+0(FP), DI + MOVQ src+8(FP), SI + MOVQ n+16(FP), CX + MOVQ state+24(FP), R8 + + MOVQ xPtr+32(FP), AX + MOVBQZX (AX), AX + MOVQ yPtr+40(FP), BX + MOVBQZX (BX), BX + +loop: + CMPQ CX, $0 + JE done + + // c.i += 1 + INCB AX + + // c.j += c.s[c.i] + MOVB (R8)(AX*1), R9 + ADDB R9, BX + + MOVBQZX (R8)(BX*1), R10 + + MOVB R10, (R8)(AX*1) + MOVB R9, (R8)(BX*1) + + // R11 = c.s[c.i]+c.s[c.j] + MOVQ R10, R11 + ADDB R9, R11 + + MOVB (R8)(R11*1), R11 + MOVB (SI), R12 + XORB R11, R12 + MOVB R12, (DI) + + INCQ SI + INCQ DI + DECQ CX + + JMP loop +done: + MOVQ xPtr+32(FP), R8 + MOVB AX, (R8) + MOVQ yPtr+40(FP), R8 + MOVB BX, (R8) + + RET diff --git a/src/pkg/crypto/rc4/rc4_arm.s b/src/pkg/crypto/rc4/rc4_arm.s new file mode 100644 index 000000000..51a332f62 --- /dev/null +++ b/src/pkg/crypto/rc4/rc4_arm.s @@ -0,0 +1,58 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Registers +dst = 0 +src = 1 +n = 2 +state = 3 +pi = 4 +pj = 5 +i = 6 +j = 7 +k = 8 +t = 11 +t2 = 12 + +// func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8) +TEXT ·xorKeyStream(SB),7,$0 + MOVW 0(FP), R(dst) + MOVW 4(FP), R(src) + MOVW 8(FP), R(n) + MOVW 12(FP), R(state) + MOVW 16(FP), R(pi) + MOVW 20(FP), R(pj) + MOVBU (R(pi)), R(i) + MOVBU (R(pj)), R(j) + MOVW $0, R(k) + +loop: + // i += 1; j += state[i] + ADD $1, R(i) + AND $0xff, R(i) + MOVBU R(i)<<0(R(state)), R(t) + ADD R(t), R(j) + AND $0xff, R(j) + + // swap state[i] <-> state[j] + MOVBU R(j)<<0(R(state)), R(t2) + MOVB R(t2), R(i)<<0(R(state)) + MOVB R(t), R(j)<<0(R(state)) + + // dst[k] = src[k] ^ state[state[i] + state[j]] + ADD R(t2), R(t) + AND $0xff, R(t) + MOVBU R(t)<<0(R(state)), R(t) + MOVBU R(k)<<0(R(src)), R(t2) + EOR R(t), R(t2) + MOVB R(t2), R(k)<<0(R(dst)) + + ADD $1, R(k) + CMP R(k), R(n) + BNE loop + +done: + MOVB R(i), (R(pi)) + MOVB R(j), (R(pj)) + RET diff --git a/src/pkg/crypto/rc4/rc4_asm.go b/src/pkg/crypto/rc4/rc4_asm.go new file mode 100644 index 000000000..0b66e4a9e --- /dev/null +++ b/src/pkg/crypto/rc4/rc4_asm.go @@ -0,0 +1,18 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64 arm + +package rc4 + +func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8) + +// XORKeyStream sets dst to the result of XORing src with the key stream. +// Dst and src may be the same slice but otherwise should not overlap. +func (c *Cipher) XORKeyStream(dst, src []byte) { + if len(src) == 0 { + return + } + xorKeyStream(&dst[0], &src[0], len(src), &c.s, &c.i, &c.j) +} diff --git a/src/pkg/crypto/rc4/rc4_ref.go b/src/pkg/crypto/rc4/rc4_ref.go new file mode 100644 index 000000000..1018548c2 --- /dev/null +++ b/src/pkg/crypto/rc4/rc4_ref.go @@ -0,0 +1,20 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64,!arm + +package rc4 + +// XORKeyStream sets dst to the result of XORing src with the key stream. +// Dst and src may be the same slice but otherwise should not overlap. +func (c *Cipher) XORKeyStream(dst, src []byte) { + i, j := c.i, c.j + for k, v := range src { + i += 1 + j += c.s[i] + c.s[i], c.s[j] = c.s[j], c.s[i] + dst[k] = v ^ c.s[c.s[i]+c.s[j]] + } + c.i, c.j = i, j +} diff --git a/src/pkg/crypto/rc4/rc4_test.go b/src/pkg/crypto/rc4/rc4_test.go index 6265d9408..9e12789f7 100644 --- a/src/pkg/crypto/rc4/rc4_test.go +++ b/src/pkg/crypto/rc4/rc4_test.go @@ -37,6 +37,39 @@ var golden = []rc4Test{ []byte{0x57, 0x69, 0x6b, 0x69}, []byte{0x60, 0x44, 0xdb, 0x6d, 0x41, 0xb7}, }, + { + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + []byte{ + 0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a, + 0x8a, 0x06, 0x1e, 0x67, 0x57, 0x6e, 0x92, 0x6d, + 0xc7, 0x1a, 0x7f, 0xa3, 0xf0, 0xcc, 0xeb, 0x97, + 0x45, 0x2b, 0x4d, 0x32, 0x27, 0x96, 0x5f, 0x9e, + 0xa8, 0xcc, 0x75, 0x07, 0x6d, 0x9f, 0xb9, 0xc5, + 0x41, 0x7a, 0xa5, 0xcb, 0x30, 0xfc, 0x22, 0x19, + 0x8b, 0x34, 0x98, 0x2d, 0xbb, 0x62, 0x9e, 0xc0, + 0x4b, 0x4f, 0x8b, 0x05, 0xa0, 0x71, 0x08, 0x50, + 0x92, 0xa0, 0xc3, 0x58, 0x4a, 0x48, 0xe4, 0xa3, + 0x0a, 0x39, 0x7b, 0x8a, 0xcd, 0x1d, 0x00, 0x9e, + 0xc8, 0x7d, 0x68, 0x11, 0xf2, 0x2c, 0xf4, 0x9c, + 0xa3, 0xe5, 0x93, 0x54, 0xb9, 0x45, 0x15, 0x35, + 0xa2, 0x18, 0x7a, 0x86, 0x42, 0x6c, 0xca, 0x7d, + 0x5e, 0x82, 0x3e, 0xba, 0x00, 0x44, 0x12, 0x67, + 0x12, 0x57, 0xb8, 0xd8, 0x60, 0xae, 0x4c, 0xbd, + 0x4c, 0x49, 0x06, 0xbb, 0xc5, 0x35, 0xef, 0xe1, + 0x58, 0x7f, 0x08, 0xdb, 0x33, 0x95, 0x5c, 0xdb, + 0xcb, 0xad, 0x9b, 0x10, 0xf5, 0x3f, 0xc4, 0xe5, + 0x2c, 0x59, 0x15, 0x65, 0x51, 0x84, 0x87, 0xfe, + 0x08, 0x4d, 0x0e, 0x3f, 0x03, 0xde, 0xbc, 0xc9, + 0xda, 0x1c, 0xe9, 0x0d, 0x08, 0x5c, 0x2d, 0x8a, + 0x19, 0xd8, 0x37, 0x30, 0x86, 0x16, 0x36, 0x92, + 0x14, 0x2b, 0xd8, 0xfc, 0x5d, 0x7a, 0x73, 0x49, + 0x6a, 0x8e, 0x59, 0xee, 0x7e, 0xcf, 0x6b, 0x94, + 0x06, 0x63, 0xf4, 0xa6, 0xbe, 0xe6, 0x5b, 0xd2, + 0xc8, 0x5c, 0x46, 0x98, 0x6c, 0x1b, 0xef, 0x34, + 0x90, 0xd3, 0x7b, 0x38, 0xda, 0x85, 0xd3, 0x2e, + 0x97, 0x39, 0xcb, 0x23, 0x4a, 0x2b, 0xe7, 0x40, + }, + }, } func TestGolden(t *testing.T) { @@ -51,9 +84,34 @@ func TestGolden(t *testing.T) { c.XORKeyStream(keystream, keystream) for j, v := range keystream { if g.keystream[j] != v { - t.Errorf("Failed at golden index %d", i) + t.Errorf("Failed at golden index %d:\n%x\nvs\n%x", i, keystream, g.keystream) break } } } } + +func benchmark(b *testing.B, size int64) { + buf := make([]byte, size) + c, err := NewCipher(golden[0].key) + if err != nil { + panic(err) + } + b.SetBytes(size) + + for i := 0; i < b.N; i++ { + c.XORKeyStream(buf, buf) + } +} + +func BenchmarkRC4_128(b *testing.B) { + benchmark(b, 128) +} + +func BenchmarkRC4_1K(b *testing.B) { + benchmark(b, 1024) +} + +func BenchmarkRC4_8K(b *testing.B) { + benchmark(b, 8096) +} diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go index a32236e47..28ca5d73b 100644 --- a/src/pkg/crypto/rsa/pkcs1v15.go +++ b/src/pkg/crypto/rsa/pkcs1v15.go @@ -19,16 +19,19 @@ import ( // WARNING: use of this function to encrypt plaintexts other than session keys // is dangerous. Use RSA OAEP in new protocols. func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error) { + if err := checkPub(pub); err != nil { + return nil, err + } k := (pub.N.BitLen() + 7) / 8 if len(msg) > k-11 { err = ErrMessageTooLong return } - // EM = 0x02 || PS || 0x00 || M - em := make([]byte, k-1) - em[0] = 2 - ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] + // EM = 0x00 || 0x02 || PS || 0x00 || M + em := make([]byte, k) + em[1] = 2 + ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):] err = nonZeroRandomBytes(ps, rand) if err != nil { return @@ -38,13 +41,18 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, er m := new(big.Int).SetBytes(em) c := encrypt(new(big.Int), pub, m) - out = c.Bytes() + + copyWithLeftPad(em, c.Bytes()) + out = em return } // DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5. // If rand != nil, it uses RSA blinding to avoid timing side-channel attacks. func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error) { + if err := checkPub(&priv.PublicKey); err != nil { + return nil, err + } valid, out, err := decryptPKCS1v15(rand, priv, ciphertext) if err == nil && valid == 0 { err = ErrDecryption @@ -67,6 +75,9 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out [ // Encryption Standard PKCS #1'', Daniel Bleichenbacher, Advances in Cryptology // (Crypto '98). func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err error) { + if err := checkPub(&priv.PublicKey); err != nil { + return err + } k := (priv.N.BitLen() + 7) / 8 if k-(len(key)+3+8) < 0 { err = ErrDecryption @@ -185,9 +196,12 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b m := new(big.Int).SetBytes(em) c, err := decrypt(rand, priv, m) - if err == nil { - s = c.Bytes() + if err != nil { + return } + + copyWithLeftPad(em, c.Bytes()) + s = em return } @@ -233,11 +247,21 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) { hashLen = hash.Size() if inLen != hashLen { - return 0, nil, errors.New("input must be hashed message") + return 0, nil, errors.New("crypto/rsa: input must be hashed message") } prefix, ok := hashPrefixes[hash] if !ok { - return 0, nil, errors.New("unsupported hash function") + return 0, nil, errors.New("crypto/rsa: unsupported hash function") } return } + +// copyWithLeftPad copies src to the end of dest, padding with zero bytes as +// needed. +func copyWithLeftPad(dest, src []byte) { + numPaddingBytes := len(dest) - len(src) + for i := 0; i < numPaddingBytes; i++ { + dest[i] = 0 + } + copy(dest[numPaddingBytes:], src) +} diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go index 58d5fda19..bf9219bae 100644 --- a/src/pkg/crypto/rsa/pkcs1v15_test.go +++ b/src/pkg/crypto/rsa/pkcs1v15_test.go @@ -57,7 +57,7 @@ func TestDecryptPKCS1v15(t *testing.T) { t.Errorf("#%d error decrypting", i) } want := []byte(test.out) - if bytes.Compare(out, want) != 0 { + if !bytes.Equal(out, want) { t.Errorf("#%d got:%#v want:%#v", i, out, want) } } @@ -90,7 +90,7 @@ func TestEncryptPKCS1v15(t *testing.T) { return false } - if bytes.Compare(plaintext, in) != 0 { + if !bytes.Equal(plaintext, in) { t.Errorf("output mismatch: %#v %#v", plaintext, in) return false } @@ -132,7 +132,7 @@ func TestEncryptPKCS1v15SessionKey(t *testing.T) { t.Errorf("#%d error decrypting", i) } want := []byte(test.out) - if bytes.Compare(key, want) != 0 { + if !bytes.Equal(key, want) { t.Errorf("#%d got:%#v want:%#v", i, key, want) } } @@ -176,7 +176,7 @@ func TestSignPKCS1v15(t *testing.T) { } expected, _ := hex.DecodeString(test.out) - if bytes.Compare(s, expected) != 0 { + if !bytes.Equal(s, expected) { t.Errorf("#%d got: %x want: %x", i, s, expected) } } diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index ec77e6869..35a5f7c3c 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -25,6 +25,30 @@ type PublicKey struct { E int // public exponent } +var ( + errPublicModulus = errors.New("crypto/rsa: missing public modulus") + errPublicExponentSmall = errors.New("crypto/rsa: public exponent too small") + errPublicExponentLarge = errors.New("crypto/rsa: public exponent too large") +) + +// checkPub sanity checks the public key before we use it. +// We require pub.E to fit into a 32-bit integer so that we +// do not have different behavior depending on whether +// int is 32 or 64 bits. See also +// http://www.imperialviolet.org/2012/03/16/rsae.html. +func checkPub(pub *PublicKey) error { + if pub.N == nil { + return errPublicModulus + } + if pub.E < 2 { + return errPublicExponentSmall + } + if pub.E > 1<<31-1 { + return errPublicExponentLarge + } + return nil +} + // A PrivateKey represents an RSA key type PrivateKey struct { PublicKey // public part. @@ -37,7 +61,7 @@ type PrivateKey struct { } type PrecomputedValues struct { - Dp, Dq *big.Int // D mod (P-1) (or mod Q-1) + Dp, Dq *big.Int // D mod (P-1) (or mod Q-1) Qinv *big.Int // Q^-1 mod Q // CRTValues is used for the 3rd and subsequent primes. Due to a @@ -57,13 +81,17 @@ type CRTValue struct { // Validate performs basic sanity checks on the key. // It returns nil if the key is valid, or else an error describing a problem. func (priv *PrivateKey) Validate() error { + if err := checkPub(&priv.PublicKey); err != nil { + return err + } + // Check that the prime factors are actually prime. Note that this is // just a sanity check. Since the random witnesses chosen by // ProbablyPrime are deterministic, given the candidate number, it's // easy for an attack to generate composites that pass this test. for _, prime := range priv.Primes { if !prime.ProbablyPrime(20) { - return errors.New("prime factor is composite") + return errors.New("crypto/rsa: prime factor is composite") } } @@ -73,27 +101,23 @@ func (priv *PrivateKey) Validate() error { modulus.Mul(modulus, prime) } if modulus.Cmp(priv.N) != 0 { - return errors.New("invalid modulus") + return errors.New("crypto/rsa: invalid modulus") } - // Check that e and totient(Πprimes) are coprime. - totient := new(big.Int).Set(bigOne) + + // Check that de ≡ 1 mod p-1, for each prime. + // This implies that e is coprime to each p-1 as e has a multiplicative + // inverse. Therefore e is coprime to lcm(p-1,q-1,r-1,...) = + // exponent(ℤ/nℤ). It also implies that a^de ≡ a mod p as a^(p-1) ≡ 1 + // mod p. Thus a^de ≡ a mod n for all a coprime to n, as required. + congruence := new(big.Int) + de := new(big.Int).SetInt64(int64(priv.E)) + de.Mul(de, priv.D) for _, prime := range priv.Primes { pminus1 := new(big.Int).Sub(prime, bigOne) - totient.Mul(totient, pminus1) - } - e := big.NewInt(int64(priv.E)) - gcd := new(big.Int) - x := new(big.Int) - y := new(big.Int) - gcd.GCD(x, y, totient, e) - if gcd.Cmp(bigOne) != 0 { - return errors.New("invalid public exponent E") - } - // Check that de ≡ 1 (mod totient(Πprimes)) - de := new(big.Int).Mul(priv.D, e) - de.Mod(de, totient) - if de.Cmp(bigOne) != 0 { - return errors.New("invalid private exponent D") + congruence.Mod(de, pminus1) + if congruence.Cmp(bigOne) != 0 { + return errors.New("crypto/rsa: invalid exponents") + } } return nil } @@ -118,7 +142,7 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva priv.E = 65537 if nprimes < 2 { - return nil, errors.New("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") + return nil, errors.New("crypto/rsa: GenerateMultiPrimeKey: nprimes must be >= 2") } primes := make([]*big.Int, nprimes) @@ -126,6 +150,20 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva NextSetOfPrimes: for { todo := bits + // crypto/rand should set the top two bits in each prime. + // Thus each prime has the form + // p_i = 2^bitlen(p_i) × 0.11... (in base 2). + // And the product is: + // P = 2^todo × α + // where α is the product of nprimes numbers of the form 0.11... + // + // If α < 1/2 (which can happen for nprimes > 2), we need to + // shift todo to compensate for lost bits: the mean value of 0.11... + // is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2 + // will give good results. + if nprimes >= 7 { + todo += (nprimes - 2) / 5 + } for i := 0; i < nprimes; i++ { primes[i], err = rand.Prime(random, todo/(nprimes-i)) if err != nil { @@ -151,6 +189,12 @@ NextSetOfPrimes: pminus1.Sub(prime, bigOne) totient.Mul(totient, pminus1) } + if n.BitLen() != bits { + // This should never happen for nprimes == 2 because + // crypto/rand should set the top two bits in each prime. + // For nprimes > 2 we hope it does not happen often. + continue NextSetOfPrimes + } g := new(big.Int) priv.D = new(big.Int) @@ -220,6 +264,9 @@ func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int { // The message must be no longer than the length of the public modulus less // twice the hash length plus 2. func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err error) { + if err := checkPub(pub); err != nil { + return nil, err + } hash.Reset() k := (pub.N.BitLen() + 7) / 8 if len(msg) > k-2*hash.Size()-2 { @@ -406,6 +453,9 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er // DecryptOAEP decrypts ciphertext using RSA-OAEP. // If random != nil, DecryptOAEP uses RSA blinding to avoid timing side-channel attacks. func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err error) { + if err := checkPub(&priv.PublicKey); err != nil { + return nil, err + } k := (priv.N.BitLen() + 7) / 8 if len(ciphertext) > k || k < hash.Size()*2+2 { diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go index 0fb9875d0..f08cfe73c 100644 --- a/src/pkg/crypto/rsa/rsa_test.go +++ b/src/pkg/crypto/rsa/rsa_test.go @@ -21,15 +21,18 @@ func TestKeyGeneration(t *testing.T) { if err != nil { t.Errorf("failed to generate key") } + if bits := priv.N.BitLen(); bits != size { + t.Errorf("key too short (%d vs %d)", bits, size) + } testKeyBasics(t, priv) } func Test3PrimeKeyGeneration(t *testing.T) { + size := 768 if testing.Short() { - return + size = 256 } - size := 768 priv, err := GenerateMultiPrimeKey(rand.Reader, 3, size) if err != nil { t.Errorf("failed to generate key") @@ -38,11 +41,11 @@ func Test3PrimeKeyGeneration(t *testing.T) { } func Test4PrimeKeyGeneration(t *testing.T) { + size := 768 if testing.Short() { - return + size = 256 } - size := 768 priv, err := GenerateMultiPrimeKey(rand.Reader, 4, size) if err != nil { t.Errorf("failed to generate key") @@ -50,6 +53,42 @@ func Test4PrimeKeyGeneration(t *testing.T) { testKeyBasics(t, priv) } +func TestNPrimeKeyGeneration(t *testing.T) { + primeSize := 64 + maxN := 24 + if testing.Short() { + primeSize = 16 + maxN = 16 + } + // Test that generation of N-prime keys works for N > 4. + for n := 5; n < maxN; n++ { + priv, err := GenerateMultiPrimeKey(rand.Reader, n, 64+n*primeSize) + if err == nil { + testKeyBasics(t, priv) + } else { + t.Errorf("failed to generate %d-prime key", n) + } + } +} + +func TestGnuTLSKey(t *testing.T) { + // This is a key generated by `certtool --generate-privkey --bits 128`. + // It's such that de ≢ 1 mod φ(n), but is congruent mod the order of + // the group. + priv := &PrivateKey{ + PublicKey: PublicKey{ + N: fromBase10("290684273230919398108010081414538931343"), + E: 65537, + }, + D: fromBase10("31877380284581499213530787347443987241"), + Primes: []*big.Int{ + fromBase10("16775196964030542637"), + fromBase10("17328218193455850539"), + }, + } + testKeyBasics(t, priv) +} + func testKeyBasics(t *testing.T, priv *PrivateKey) { if err := priv.Validate(); err != nil { t.Errorf("Validate() failed: %s", err) @@ -98,7 +137,7 @@ func BenchmarkRSA2048Decrypt(b *testing.B) { } priv.Precompute() - c := fromBase10("1000") + c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313") b.StartTimer() @@ -123,7 +162,7 @@ func Benchmark3PrimeRSA2048Decrypt(b *testing.B) { } priv.Precompute() - c := fromBase10("1000") + c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313") b.StartTimer() @@ -158,7 +197,7 @@ func TestEncryptOAEP(t *testing.T) { if err != nil { t.Errorf("#%d,%d error: %s", i, j, err) } - if bytes.Compare(out, message.out) != 0 { + if !bytes.Equal(out, message.out) { t.Errorf("#%d,%d bad result: %x (want %x)", i, j, out, message.out) } } @@ -182,7 +221,7 @@ func TestDecryptOAEP(t *testing.T) { out, err := DecryptOAEP(sha1, nil, private, message.out, nil) if err != nil { t.Errorf("#%d,%d error: %s", i, j, err) - } else if bytes.Compare(out, message.in) != 0 { + } else if !bytes.Equal(out, message.in) { t.Errorf("#%d,%d bad result: %#v (want %#v)", i, j, out, message.in) } @@ -190,7 +229,7 @@ func TestDecryptOAEP(t *testing.T) { out, err = DecryptOAEP(sha1, random, private, message.out, nil) if err != nil { t.Errorf("#%d,%d (blind) error: %s", i, j, err) - } else if bytes.Compare(out, message.in) != 0 { + } else if !bytes.Equal(out, message.in) { t.Errorf("#%d,%d (blind) bad result: %#v (want %#v)", i, j, out, message.in) } } diff --git a/src/pkg/crypto/sha1/sha1.go b/src/pkg/crypto/sha1/sha1.go index 876e7992a..7cfde47dc 100644 --- a/src/pkg/crypto/sha1/sha1.go +++ b/src/pkg/crypto/sha1/sha1.go @@ -21,28 +21,28 @@ const Size = 20 const BlockSize = 64 const ( - _Chunk = 64 - _Init0 = 0x67452301 - _Init1 = 0xEFCDAB89 - _Init2 = 0x98BADCFE - _Init3 = 0x10325476 - _Init4 = 0xC3D2E1F0 + chunk = 64 + init0 = 0x67452301 + init1 = 0xEFCDAB89 + init2 = 0x98BADCFE + init3 = 0x10325476 + init4 = 0xC3D2E1F0 ) // digest represents the partial evaluation of a checksum. type digest struct { h [5]uint32 - x [_Chunk]byte + x [chunk]byte nx int len uint64 } func (d *digest) Reset() { - d.h[0] = _Init0 - d.h[1] = _Init1 - d.h[2] = _Init2 - d.h[3] = _Init3 - d.h[4] = _Init4 + d.h[0] = init0 + d.h[1] = init1 + d.h[2] = init2 + d.h[3] = init3 + d.h[4] = init4 d.nx = 0 d.len = 0 } @@ -63,21 +63,24 @@ func (d *digest) Write(p []byte) (nn int, err error) { d.len += uint64(nn) if d.nx > 0 { n := len(p) - if n > _Chunk-d.nx { - n = _Chunk - d.nx + if n > chunk-d.nx { + n = chunk - d.nx } for i := 0; i < n; i++ { d.x[d.nx+i] = p[i] } d.nx += n - if d.nx == _Chunk { - _Block(d, d.x[0:]) + if d.nx == chunk { + block(d, d.x[0:]) d.nx = 0 } p = p[n:] } - n := _Block(d, p) - p = p[n:] + if len(p) >= chunk { + n := len(p) &^ (chunk - 1) + block(d, p[:n]) + p = p[n:] + } if len(p) > 0 { d.nx = copy(d.x[:], p) } diff --git a/src/pkg/crypto/sha1/sha1_test.go b/src/pkg/crypto/sha1/sha1_test.go index 2dc14ac98..57cd4313e 100644 --- a/src/pkg/crypto/sha1/sha1_test.go +++ b/src/pkg/crypto/sha1/sha1_test.go @@ -4,10 +4,9 @@ // SHA1 hash algorithm. See RFC 3174. -package sha1_test +package sha1 import ( - "crypto/sha1" "fmt" "io" "testing" @@ -55,7 +54,7 @@ var golden = []sha1Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] - c := sha1.New() + c := New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(c, g.in) @@ -74,8 +73,33 @@ func TestGolden(t *testing.T) { } func ExampleNew() { - h := sha1.New() + h := New() io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.") fmt.Printf("% x", h.Sum(nil)) // Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd } + +var bench = New() +var buf = make([]byte, 8192) + +func benchmarkSize(b *testing.B, size int) { + b.SetBytes(int64(size)) + sum := make([]byte, bench.Size()) + for i := 0; i < b.N; i++ { + bench.Reset() + bench.Write(buf[:size]) + bench.Sum(sum[:0]) + } +} + +func BenchmarkHash8Bytes(b *testing.B) { + benchmarkSize(b, 8) +} + +func BenchmarkHash1K(b *testing.B) { + benchmarkSize(b, 1024) +} + +func BenchmarkHash8K(b *testing.B) { + benchmarkSize(b, 8192) +} diff --git a/src/pkg/crypto/sha1/sha1block.go b/src/pkg/crypto/sha1/sha1block.go index b5d32af70..1c9507c68 100644 --- a/src/pkg/crypto/sha1/sha1block.go +++ b/src/pkg/crypto/sha1/sha1block.go @@ -15,54 +15,67 @@ const ( _K3 = 0xCA62C1D6 ) -func _Block(dig *digest, p []byte) int { - var w [80]uint32 +func block(dig *digest, p []byte) { + var w [16]uint32 - n := 0 h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] - for len(p) >= _Chunk { + for len(p) >= chunk { // Can interlace the computation of w with the // rounds below if needed for speed. for i := 0; i < 16; i++ { j := i * 4 w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) } - for i := 16; i < 80; i++ { - tmp := w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16] - w[i] = tmp<<1 | tmp>>(32-1) - } a, b, c, d, e := h0, h1, h2, h3, h4 // Each of the four 20-iteration rounds // differs only in the computation of f and // the choice of K (_K0, _K1, etc). - for i := 0; i < 20; i++ { + i := 0 + for ; i < 16; i++ { + f := b&c | (^b)&d + a5 := a<<5 | a>>(32-5) + b30 := b<<30 | b>>(32-30) + t := a5 + f + e + w[i&0xf] + _K0 + a, b, c, d, e = t, a, b30, c, d + } + for ; i < 20; i++ { + tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] + w[i&0xf] = tmp<<1 | tmp>>(32-1) + f := b&c | (^b)&d a5 := a<<5 | a>>(32-5) b30 := b<<30 | b>>(32-30) - t := a5 + f + e + w[i] + _K0 + t := a5 + f + e + w[i&0xf] + _K0 a, b, c, d, e = t, a, b30, c, d } - for i := 20; i < 40; i++ { + for ; i < 40; i++ { + tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] + w[i&0xf] = tmp<<1 | tmp>>(32-1) f := b ^ c ^ d a5 := a<<5 | a>>(32-5) b30 := b<<30 | b>>(32-30) - t := a5 + f + e + w[i] + _K1 + t := a5 + f + e + w[i&0xf] + _K1 a, b, c, d, e = t, a, b30, c, d } - for i := 40; i < 60; i++ { - f := b&c | b&d | c&d + for ; i < 60; i++ { + tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] + w[i&0xf] = tmp<<1 | tmp>>(32-1) + f := ((b | c) & d) | (b & c) + a5 := a<<5 | a>>(32-5) b30 := b<<30 | b>>(32-30) - t := a5 + f + e + w[i] + _K2 + t := a5 + f + e + w[i&0xf] + _K2 a, b, c, d, e = t, a, b30, c, d } - for i := 60; i < 80; i++ { + for ; i < 80; i++ { + tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] + w[i&0xf] = tmp<<1 | tmp>>(32-1) f := b ^ c ^ d a5 := a<<5 | a>>(32-5) b30 := b<<30 | b>>(32-30) - t := a5 + f + e + w[i] + _K3 + t := a5 + f + e + w[i&0xf] + _K3 a, b, c, d, e = t, a, b30, c, d } @@ -72,10 +85,8 @@ func _Block(dig *digest, p []byte) int { h3 += d h4 += e - p = p[_Chunk:] - n += _Chunk + p = p[chunk:] } dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4 - return n } diff --git a/src/pkg/crypto/sha256/sha256.go b/src/pkg/crypto/sha256/sha256.go index a61e30b42..dc0e18f50 100644 --- a/src/pkg/crypto/sha256/sha256.go +++ b/src/pkg/crypto/sha256/sha256.go @@ -26,29 +26,29 @@ const Size224 = 28 const BlockSize = 64 const ( - _Chunk = 64 - _Init0 = 0x6A09E667 - _Init1 = 0xBB67AE85 - _Init2 = 0x3C6EF372 - _Init3 = 0xA54FF53A - _Init4 = 0x510E527F - _Init5 = 0x9B05688C - _Init6 = 0x1F83D9AB - _Init7 = 0x5BE0CD19 - _Init0_224 = 0xC1059ED8 - _Init1_224 = 0x367CD507 - _Init2_224 = 0x3070DD17 - _Init3_224 = 0xF70E5939 - _Init4_224 = 0xFFC00B31 - _Init5_224 = 0x68581511 - _Init6_224 = 0x64F98FA7 - _Init7_224 = 0xBEFA4FA4 + chunk = 64 + init0 = 0x6A09E667 + init1 = 0xBB67AE85 + init2 = 0x3C6EF372 + init3 = 0xA54FF53A + init4 = 0x510E527F + init5 = 0x9B05688C + init6 = 0x1F83D9AB + init7 = 0x5BE0CD19 + init0_224 = 0xC1059ED8 + init1_224 = 0x367CD507 + init2_224 = 0x3070DD17 + init3_224 = 0xF70E5939 + init4_224 = 0xFFC00B31 + init5_224 = 0x68581511 + init6_224 = 0x64F98FA7 + init7_224 = 0xBEFA4FA4 ) // digest represents the partial evaluation of a checksum. type digest struct { h [8]uint32 - x [_Chunk]byte + x [chunk]byte nx int len uint64 is224 bool // mark if this digest is SHA-224 @@ -56,23 +56,23 @@ type digest struct { func (d *digest) Reset() { if !d.is224 { - d.h[0] = _Init0 - d.h[1] = _Init1 - d.h[2] = _Init2 - d.h[3] = _Init3 - d.h[4] = _Init4 - d.h[5] = _Init5 - d.h[6] = _Init6 - d.h[7] = _Init7 + d.h[0] = init0 + d.h[1] = init1 + d.h[2] = init2 + d.h[3] = init3 + d.h[4] = init4 + d.h[5] = init5 + d.h[6] = init6 + d.h[7] = init7 } else { - d.h[0] = _Init0_224 - d.h[1] = _Init1_224 - d.h[2] = _Init2_224 - d.h[3] = _Init3_224 - d.h[4] = _Init4_224 - d.h[5] = _Init5_224 - d.h[6] = _Init6_224 - d.h[7] = _Init7_224 + d.h[0] = init0_224 + d.h[1] = init1_224 + d.h[2] = init2_224 + d.h[3] = init3_224 + d.h[4] = init4_224 + d.h[5] = init5_224 + d.h[6] = init6_224 + d.h[7] = init7_224 } d.nx = 0 d.len = 0 @@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) { d.len += uint64(nn) if d.nx > 0 { n := len(p) - if n > _Chunk-d.nx { - n = _Chunk - d.nx + if n > chunk-d.nx { + n = chunk - d.nx } for i := 0; i < n; i++ { d.x[d.nx+i] = p[i] } d.nx += n - if d.nx == _Chunk { - _Block(d, d.x[0:]) + if d.nx == chunk { + block(d, d.x[0:]) d.nx = 0 } p = p[n:] } - n := _Block(d, p) - p = p[n:] + if len(p) >= chunk { + n := len(p) &^ (chunk - 1) + block(d, p[:n]) + p = p[n:] + } if len(p) > 0 { d.nx = copy(d.x[:], p) } diff --git a/src/pkg/crypto/sha256/sha256_test.go b/src/pkg/crypto/sha256/sha256_test.go index a6efb3754..29bf1619a 100644 --- a/src/pkg/crypto/sha256/sha256_test.go +++ b/src/pkg/crypto/sha256/sha256_test.go @@ -123,3 +123,28 @@ func TestGolden(t *testing.T) { } } } + +var bench = New() +var buf = make([]byte, 8192) + +func benchmarkSize(b *testing.B, size int) { + b.SetBytes(int64(size)) + sum := make([]byte, bench.Size()) + for i := 0; i < b.N; i++ { + bench.Reset() + bench.Write(buf[:size]) + bench.Sum(sum[:0]) + } +} + +func BenchmarkHash8Bytes(b *testing.B) { + benchmarkSize(b, 8) +} + +func BenchmarkHash1K(b *testing.B) { + benchmarkSize(b, 1024) +} + +func BenchmarkHash8K(b *testing.B) { + benchmarkSize(b, 8192) +} diff --git a/src/pkg/crypto/sha256/sha256block.go b/src/pkg/crypto/sha256/sha256block.go index 7b0f55444..2ac49100a 100644 --- a/src/pkg/crypto/sha256/sha256block.go +++ b/src/pkg/crypto/sha256/sha256block.go @@ -75,11 +75,10 @@ var _K = []uint32{ 0xc67178f2, } -func _Block(dig *digest, p []byte) int { +func block(dig *digest, p []byte) { var w [64]uint32 - n := 0 h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] - for len(p) >= _Chunk { + for len(p) >= chunk { // Can interlace the computation of w with the // rounds below if needed for speed. for i := 0; i < 16; i++ { @@ -87,10 +86,10 @@ func _Block(dig *digest, p []byte) int { w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) } for i := 16; i < 64; i++ { - t1 := (w[i-2]>>17 | w[i-2]<<(32-17)) ^ (w[i-2]>>19 | w[i-2]<<(32-19)) ^ (w[i-2] >> 10) - - t2 := (w[i-15]>>7 | w[i-15]<<(32-7)) ^ (w[i-15]>>18 | w[i-15]<<(32-18)) ^ (w[i-15] >> 3) - + v1 := w[i-2] + t1 := (v1>>17 | v1<<(32-17)) ^ (v1>>19 | v1<<(32-19)) ^ (v1 >> 10) + v2 := w[i-15] + t2 := (v2>>7 | v2<<(32-7)) ^ (v2>>18 | v2<<(32-18)) ^ (v2 >> 3) w[i] = t1 + w[i-7] + t2 + w[i-16] } @@ -120,10 +119,8 @@ func _Block(dig *digest, p []byte) int { h6 += g h7 += h - p = p[_Chunk:] - n += _Chunk + p = p[chunk:] } dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 - return n } diff --git a/src/pkg/crypto/sha512/sha512.go b/src/pkg/crypto/sha512/sha512.go index a245fd68e..4aec52938 100644 --- a/src/pkg/crypto/sha512/sha512.go +++ b/src/pkg/crypto/sha512/sha512.go @@ -26,29 +26,29 @@ const Size384 = 48 const BlockSize = 128 const ( - _Chunk = 128 - _Init0 = 0x6a09e667f3bcc908 - _Init1 = 0xbb67ae8584caa73b - _Init2 = 0x3c6ef372fe94f82b - _Init3 = 0xa54ff53a5f1d36f1 - _Init4 = 0x510e527fade682d1 - _Init5 = 0x9b05688c2b3e6c1f - _Init6 = 0x1f83d9abfb41bd6b - _Init7 = 0x5be0cd19137e2179 - _Init0_384 = 0xcbbb9d5dc1059ed8 - _Init1_384 = 0x629a292a367cd507 - _Init2_384 = 0x9159015a3070dd17 - _Init3_384 = 0x152fecd8f70e5939 - _Init4_384 = 0x67332667ffc00b31 - _Init5_384 = 0x8eb44a8768581511 - _Init6_384 = 0xdb0c2e0d64f98fa7 - _Init7_384 = 0x47b5481dbefa4fa4 + chunk = 128 + init0 = 0x6a09e667f3bcc908 + init1 = 0xbb67ae8584caa73b + init2 = 0x3c6ef372fe94f82b + init3 = 0xa54ff53a5f1d36f1 + init4 = 0x510e527fade682d1 + init5 = 0x9b05688c2b3e6c1f + init6 = 0x1f83d9abfb41bd6b + init7 = 0x5be0cd19137e2179 + init0_384 = 0xcbbb9d5dc1059ed8 + init1_384 = 0x629a292a367cd507 + init2_384 = 0x9159015a3070dd17 + init3_384 = 0x152fecd8f70e5939 + init4_384 = 0x67332667ffc00b31 + init5_384 = 0x8eb44a8768581511 + init6_384 = 0xdb0c2e0d64f98fa7 + init7_384 = 0x47b5481dbefa4fa4 ) // digest represents the partial evaluation of a checksum. type digest struct { h [8]uint64 - x [_Chunk]byte + x [chunk]byte nx int len uint64 is384 bool // mark if this digest is SHA-384 @@ -56,23 +56,23 @@ type digest struct { func (d *digest) Reset() { if !d.is384 { - d.h[0] = _Init0 - d.h[1] = _Init1 - d.h[2] = _Init2 - d.h[3] = _Init3 - d.h[4] = _Init4 - d.h[5] = _Init5 - d.h[6] = _Init6 - d.h[7] = _Init7 + d.h[0] = init0 + d.h[1] = init1 + d.h[2] = init2 + d.h[3] = init3 + d.h[4] = init4 + d.h[5] = init5 + d.h[6] = init6 + d.h[7] = init7 } else { - d.h[0] = _Init0_384 - d.h[1] = _Init1_384 - d.h[2] = _Init2_384 - d.h[3] = _Init3_384 - d.h[4] = _Init4_384 - d.h[5] = _Init5_384 - d.h[6] = _Init6_384 - d.h[7] = _Init7_384 + d.h[0] = init0_384 + d.h[1] = init1_384 + d.h[2] = init2_384 + d.h[3] = init3_384 + d.h[4] = init4_384 + d.h[5] = init5_384 + d.h[6] = init6_384 + d.h[7] = init7_384 } d.nx = 0 d.len = 0 @@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) { d.len += uint64(nn) if d.nx > 0 { n := len(p) - if n > _Chunk-d.nx { - n = _Chunk - d.nx + if n > chunk-d.nx { + n = chunk - d.nx } for i := 0; i < n; i++ { d.x[d.nx+i] = p[i] } d.nx += n - if d.nx == _Chunk { - _Block(d, d.x[0:]) + if d.nx == chunk { + block(d, d.x[0:]) d.nx = 0 } p = p[n:] } - n := _Block(d, p) - p = p[n:] + if len(p) >= chunk { + n := len(p) &^ (chunk - 1) + block(d, p[:n]) + p = p[n:] + } if len(p) > 0 { d.nx = copy(d.x[:], p) } diff --git a/src/pkg/crypto/sha512/sha512_test.go b/src/pkg/crypto/sha512/sha512_test.go index a70f7c54e..6eafb1b5f 100644 --- a/src/pkg/crypto/sha512/sha512_test.go +++ b/src/pkg/crypto/sha512/sha512_test.go @@ -123,3 +123,28 @@ func TestGolden(t *testing.T) { } } } + +var bench = New() +var buf = make([]byte, 8192) + +func benchmarkSize(b *testing.B, size int) { + b.SetBytes(int64(size)) + sum := make([]byte, bench.Size()) + for i := 0; i < b.N; i++ { + bench.Reset() + bench.Write(buf[:size]) + bench.Sum(sum[:0]) + } +} + +func BenchmarkHash8Bytes(b *testing.B) { + benchmarkSize(b, 8) +} + +func BenchmarkHash1K(b *testing.B) { + benchmarkSize(b, 1024) +} + +func BenchmarkHash8K(b *testing.B) { + benchmarkSize(b, 8192) +} diff --git a/src/pkg/crypto/sha512/sha512block.go b/src/pkg/crypto/sha512/sha512block.go index 6b7506287..3577b4f3d 100644 --- a/src/pkg/crypto/sha512/sha512block.go +++ b/src/pkg/crypto/sha512/sha512block.go @@ -91,20 +91,20 @@ var _K = []uint64{ 0x6c44198c4a475817, } -func _Block(dig *digest, p []byte) int { +func block(dig *digest, p []byte) { var w [80]uint64 - n := 0 h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] - for len(p) >= _Chunk { + for len(p) >= chunk { for i := 0; i < 16; i++ { j := i * 8 w[i] = uint64(p[j])<<56 | uint64(p[j+1])<<48 | uint64(p[j+2])<<40 | uint64(p[j+3])<<32 | uint64(p[j+4])<<24 | uint64(p[j+5])<<16 | uint64(p[j+6])<<8 | uint64(p[j+7]) } for i := 16; i < 80; i++ { - t1 := (w[i-2]>>19 | w[i-2]<<(64-19)) ^ (w[i-2]>>61 | w[i-2]<<(64-61)) ^ (w[i-2] >> 6) - - t2 := (w[i-15]>>1 | w[i-15]<<(64-1)) ^ (w[i-15]>>8 | w[i-15]<<(64-8)) ^ (w[i-15] >> 7) + v1 := w[i-2] + t1 := (v1>>19 | v1<<(64-19)) ^ (v1>>61 | v1<<(64-61)) ^ (v1 >> 6) + v2 := w[i-15] + t2 := (v2>>1 | v2<<(64-1)) ^ (v2>>8 | v2<<(64-8)) ^ (v2 >> 7) w[i] = t1 + w[i-7] + t2 + w[i-16] } @@ -135,10 +135,8 @@ func _Block(dig *digest, p []byte) int { h6 += g h7 += h - p = p[_Chunk:] - n += _Chunk + p = p[chunk:] } dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 - return n } diff --git a/src/pkg/crypto/tls/cipher_suites.go b/src/pkg/crypto/tls/cipher_suites.go index 5039f319f..a647e19aa 100644 --- a/src/pkg/crypto/tls/cipher_suites.go +++ b/src/pkg/crypto/tls/cipher_suites.go @@ -55,9 +55,11 @@ var cipherSuites = []*cipherSuite{ {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1}, {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1}, + {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, false, cipherAES, macSHA1}, {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1}, {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1}, {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, + {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, } func cipherRC4(key, iv []byte, isRead bool) interface{} { @@ -182,7 +184,9 @@ const ( TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f + TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014 ) diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go index 4ba0bf874..a888df762 100644 --- a/src/pkg/crypto/tls/common.go +++ b/src/pkg/crypto/tls/common.go @@ -41,6 +41,7 @@ const ( const ( typeClientHello uint8 = 1 typeServerHello uint8 = 2 + typeNewSessionTicket uint8 = 4 typeCertificate uint8 = 11 typeServerKeyExchange uint8 = 12 typeCertificateRequest uint8 = 13 @@ -63,6 +64,7 @@ var ( extensionStatusRequest uint16 = 5 extensionSupportedCurves uint16 = 10 extensionSupportedPoints uint16 = 11 + extensionSessionTicket uint16 = 35 extensionNextProtoNeg uint16 = 13172 // not IANA assigned ) @@ -97,6 +99,7 @@ const ( // ConnectionState records basic TLS details about the connection. type ConnectionState struct { HandshakeComplete bool + DidResume bool CipherSuite uint16 NegotiatedProtocol string NegotiatedProtocolIsMutual bool @@ -180,6 +183,28 @@ type Config struct { // CipherSuites is a list of supported cipher suites. If CipherSuites // is nil, TLS uses a list of suites supported by the implementation. CipherSuites []uint16 + + // PreferServerCipherSuites controls whether the server selects the + // client's most preferred ciphersuite, or the server's most preferred + // ciphersuite. If true then the server's preference, as expressed in + // the order of elements in CipherSuites, is used. + PreferServerCipherSuites bool + + // SessionTicketsDisabled may be set to true to disable session ticket + // (resumption) support. + SessionTicketsDisabled bool + + // SessionTicketKey is used by TLS servers to provide session + // resumption. See RFC 5077. If zero, it will be filled with + // random data before the first server handshake. + // + // If multiple servers are terminating connections for the same host + // they should all have the same SessionTicketKey. If the + // SessionTicketKey leaks, previously recorded and future TLS + // connections using that key are compromised. + SessionTicketKey [32]byte + + serverInitOnce sync.Once } func (c *Config) rand() io.Reader { diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go index 2a5115dc6..d8c2be00a 100644 --- a/src/pkg/crypto/tls/conn.go +++ b/src/pkg/crypto/tls/conn.go @@ -31,6 +31,7 @@ type Conn struct { haveVers bool // version has been negotiated config *Config // configuration passed to constructor handshakeComplete bool + didResume bool // whether this connection was a session resumption cipherSuite uint16 ocspResponse []byte // stapled OCSP response peerCertificates []*x509.Certificate @@ -44,8 +45,7 @@ type Conn struct { clientProtocolFallback bool // first permanent error - errMutex sync.Mutex - err error + connErr // input/output in, out halfConn // in.Mutex < out.Mutex @@ -56,21 +56,25 @@ type Conn struct { tmp [16]byte } -func (c *Conn) setError(err error) error { - c.errMutex.Lock() - defer c.errMutex.Unlock() +type connErr struct { + mu sync.Mutex + value error +} + +func (e *connErr) setError(err error) error { + e.mu.Lock() + defer e.mu.Unlock() - if c.err == nil { - c.err = err + if e.value == nil { + e.value = err } return err } -func (c *Conn) error() error { - c.errMutex.Lock() - defer c.errMutex.Unlock() - - return c.err +func (e *connErr) error() error { + e.mu.Lock() + defer e.mu.Unlock() + return e.value } // Access to net.Conn methods. @@ -487,6 +491,16 @@ Again: return err } typ := recordType(b.data[0]) + + // No valid TLS record has a type of 0x80, however SSLv2 handshakes + // start with a uint16 length where the MSB is set and the first record + // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests + // an SSLv2 client. + if want == recordTypeHandshake && typ == 0x80 { + c.sendAlert(alertProtocolVersion) + return errors.New("tls: unsupported SSLv2 handshake received") + } + vers := uint16(b.data[1])<<8 | uint16(b.data[2]) n := int(b.data[3])<<8 | int(b.data[4]) if c.haveVers && vers != c.vers { @@ -499,7 +513,7 @@ Again: // First message, be extra suspicious: // this might not be a TLS client. // Bail out before reading a full 'body', if possible. - // The current max version is 3.1. + // The current max version is 3.1. // If the version is >= 16.0, it's probably not real. // Similarly, a clientHello message encodes in // well under a kilobyte. If the length is >= 12 kB, @@ -590,9 +604,11 @@ Again: // sendAlert sends a TLS alert message. // c.out.Mutex <= L. func (c *Conn) sendAlertLocked(err alert) error { - c.tmp[0] = alertLevelError - if err == alertNoRenegotiation { + switch err { + case alertNoRenegotiation, alertCloseNotify: c.tmp[0] = alertLevelWarning + default: + c.tmp[0] = alertLevelError } c.tmp[1] = byte(err) c.writeRecord(recordTypeAlert, c.tmp[0:2]) @@ -650,8 +666,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { c.tmp[0] = alertLevelError c.tmp[1] = byte(err.(alert)) c.writeRecord(recordTypeAlert, c.tmp[0:2]) - c.err = &net.OpError{Op: "local error", Err: err} - return n, c.err + return n, c.setError(&net.OpError{Op: "local error", Err: err}) } } return @@ -662,8 +677,8 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { // c.in.Mutex < L; c.out.Mutex < L. func (c *Conn) readHandshake() (interface{}, error) { for c.hand.Len() < 4 { - if c.err != nil { - return nil, c.err + if err := c.error(); err != nil { + return nil, err } if err := c.readRecord(recordTypeHandshake); err != nil { return nil, err @@ -674,11 +689,11 @@ func (c *Conn) readHandshake() (interface{}, error) { n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) if n > maxHandshake { c.sendAlert(alertInternalError) - return nil, c.err + return nil, c.error() } for c.hand.Len() < 4+n { - if c.err != nil { - return nil, c.err + if err := c.error(); err != nil { + return nil, err } if err := c.readRecord(recordTypeHandshake); err != nil { return nil, err @@ -728,12 +743,12 @@ func (c *Conn) readHandshake() (interface{}, error) { // Write writes data to the connection. func (c *Conn) Write(b []byte) (int, error) { - if c.err != nil { - return 0, c.err + if err := c.error(); err != nil { + return 0, err } - if c.err = c.Handshake(); c.err != nil { - return 0, c.err + if err := c.Handshake(); err != nil { + return 0, c.setError(err) } c.out.Lock() @@ -743,9 +758,28 @@ func (c *Conn) Write(b []byte) (int, error) { return 0, alertInternalError } - var n int - n, c.err = c.writeRecord(recordTypeApplicationData, b) - return n, c.err + // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext + // attack when using block mode ciphers due to predictable IVs. + // This can be prevented by splitting each Application Data + // record into two records, effectively randomizing the IV. + // + // http://www.openssl.org/~bodo/tls-cbc.txt + // https://bugzilla.mozilla.org/show_bug.cgi?id=665814 + // http://www.imperialviolet.org/2012/01/15/beastfollowup.html + + var m int + if len(b) > 1 && c.vers <= versionTLS10 { + if _, ok := c.out.cipher.(cipher.BlockMode); ok { + n, err := c.writeRecord(recordTypeApplicationData, b[:1]) + if err != nil { + return n, c.setError(err) + } + m, b = 1, b[1:] + } + } + + n, err := c.writeRecord(recordTypeApplicationData, b) + return n + m, c.setError(err) } // Read can be made to time out and return a net.Error with Timeout() == true @@ -758,14 +792,14 @@ func (c *Conn) Read(b []byte) (n int, err error) { c.in.Lock() defer c.in.Unlock() - for c.input == nil && c.err == nil { + for c.input == nil && c.error() == nil { if err := c.readRecord(recordTypeApplicationData); err != nil { // Soft error, like EAGAIN return 0, err } } - if c.err != nil { - return 0, c.err + if err := c.error(); err != nil { + return 0, err } n, err = c.input.Read(b) if c.input.off >= len(c.input.data) { @@ -819,6 +853,7 @@ func (c *Conn) ConnectionState() ConnectionState { state.HandshakeComplete = c.handshakeComplete if c.handshakeComplete { state.NegotiatedProtocol = c.clientProtocol + state.DidResume = c.didResume state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback state.CipherSuite = c.cipherSuite state.PeerCertificates = c.peerCertificates diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go index 84be5bfd8..215644d24 100644 --- a/src/pkg/crypto/tls/generate_cert.go +++ b/src/pkg/crypto/tls/generate_cert.go @@ -16,36 +16,80 @@ import ( "crypto/x509/pkix" "encoding/pem" "flag" + "fmt" "log" "math/big" + "net" "os" + "strings" "time" ) -var hostName *string = flag.String("host", "127.0.0.1", "Hostname to generate a certificate for") +var ( + host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for") + validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011") + validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for") + isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority") + rsaBits = flag.Int("rsa-bits", 1024, "Size of RSA key to generate") +) func main() { flag.Parse() - priv, err := rsa.GenerateKey(rand.Reader, 1024) + if len(*host) == 0 { + log.Fatalf("Missing required --host parameter") + } + + priv, err := rsa.GenerateKey(rand.Reader, *rsaBits) if err != nil { log.Fatalf("failed to generate private key: %s", err) return } - now := time.Now() + var notBefore time.Time + if len(*validFrom) == 0 { + notBefore = time.Now() + } else { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err) + os.Exit(1) + } + } + + notAfter := notBefore.Add(*validFor) + + // end of ASN.1 time + endOfTime := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC) + if notAfter.After(endOfTime) { + notAfter = endOfTime + } template := x509.Certificate{ SerialNumber: new(big.Int).SetInt64(0), Subject: pkix.Name{ - CommonName: *hostName, Organization: []string{"Acme Co"}, }, - NotBefore: now.Add(-5 * time.Minute).UTC(), - NotAfter: now.AddDate(1, 0, 0).UTC(), // valid for 1 year. + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(*host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } - SubjectKeyId: []byte{1, 2, 3, 4}, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + if *isCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go index 2877f1738..7db13bf70 100644 --- a/src/pkg/crypto/tls/handshake_client.go +++ b/src/pkg/crypto/tls/handshake_client.go @@ -278,8 +278,9 @@ func (c *Conn) clientHandshake() error { c.writeRecord(recordTypeHandshake, certVerify.marshal()) } - masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) + masterSecret := masterFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random) + clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := + keysFromMasterSecret(c.vers, masterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) clientHash := suite.mac(c.vers, clientMAC) @@ -306,8 +307,8 @@ func (c *Conn) clientHandshake() error { serverHash := suite.mac(c.vers, serverMAC) c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.readRecord(recordTypeChangeCipherSpec) - if c.err != nil { - return c.err + if err := c.error(); err != nil { + return err } msg, err = c.readHandshake() diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go index 8c56daaf6..9673947a4 100644 --- a/src/pkg/crypto/tls/handshake_client_test.go +++ b/src/pkg/crypto/tls/handshake_client_test.go @@ -9,6 +9,7 @@ import ( "flag" "io" "net" + "os" "testing" ) @@ -39,7 +40,15 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config * } func TestHandshakeClientRC4(t *testing.T) { - testClientScript(t, "RC4", rc4ClientScript, testConfig) + var config = *testConfig + config.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA} + testClientScript(t, "RC4", rc4ClientScript, &config) +} + +func TestHandshakeClientECDHEAES(t *testing.T) { + var config = *testConfig + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA} + testClientScript(t, "ECDHE-AES", ecdheAESClientScript, &config) } var connect = flag.Bool("connect", false, "connect to a TLS server on :10443") @@ -49,25 +58,33 @@ func TestRunClient(t *testing.T) { return } - testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA} - - conn, err := Dial("tcp", "127.0.0.1:10443", testConfig) + tcpConn, err := net.Dial("tcp", "127.0.0.1:10443") if err != nil { t.Fatal(err) } + record := &recordingConn{ + Conn: tcpConn, + } + + config := GetTestConfig() + conn := Client(record, config) + if err := conn.Handshake(); err != nil { + t.Fatalf("error from TLS handshake: %s", err) + } + conn.Write([]byte("hello\n")) conn.Close() + + record.WriteTo(os.Stdout) } // Script of interaction with gnutls implementation. // The values for this test are obtained by building and running in client mode: -// % go test -run "TestRunClient" -connect -// and then: -// % gnutls-serv -p 10443 --debug 100 --x509keyfile key.pem --x509certfile cert.pem -a > /tmp/log 2>&1 -// % python parse-gnutls-cli-debug-log.py < /tmp/log +// % go test -test.run "TestRunClient" -connect +// The recorded bytes are written to stdout. // -// Where key.pem is: +// The server private key is: // -----BEGIN RSA PRIVATE KEY----- // MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD // TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu @@ -78,17 +95,20 @@ func TestRunClient(t *testing.T) { // vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU= // -----END RSA PRIVATE KEY----- // -// and cert.pem is: +// and certificate is: // -----BEGIN CERTIFICATE----- -// MIIBoDCCAUoCAQAwDQYJKoZIhvcNAQEEBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV -// BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD -// VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw05NzA5MDkwMzQxMjZa -// Fw05NzEwMDkwMzQxMjZaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 -// YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNVBAMT -// DkVyaWMgdGhlIFlvdW5nMFEwCQYFKw4DAgwFAANEAAJBALVEqPODnpI4rShlY8S7 -// tB713JNvabvn6Gned7zylwLLiXQAo/PAT6mfdWPTyCX9RlId/Aroh1ou893BA32Q -// sggwDQYJKoZIhvcNAQEEBQADQQCU5SSgapJSdRXJoX+CpCvFy+JVh9HpSjCpSNKO -// 19raHv98hKAUJuP9HyM+SUsffO6mAIgitUaqW8/wDMePhEC3 +// MIICKzCCAdWgAwIBAgIJALE1E2URIMWSMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +// BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +// aWRnaXRzIFB0eSBMdGQwHhcNMTIwNDA2MTcxMDEzWhcNMTUwNDA2MTcxMDEzWjBF +// MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 +// ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ+z +// w4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVDTGiXav6ooKXfX3j/ +// 7tdkuD8Ey2//Kv7+ue0CAwEAAaOBpzCBpDAdBgNVHQ4EFgQUeKaXmmO1xaGlM7oi +// fCNuWxt6zCswdQYDVR0jBG4wbIAUeKaXmmO1xaGlM7oifCNuWxt6zCuhSaRHMEUx +// CzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl +// cm5ldCBXaWRnaXRzIFB0eSBMdGSCCQCxNRNlESDFkjAMBgNVHRMEBTADAQH/MA0G +// CSqGSIb3DQEBBQUAA0EAhTZAc8G7GtrUWZ8tonAxRnTsg26oyDxRrzms7EC86CJG +// HZnWRiok1IsFCEv7NRFukrt3uuQSu/TIXpyBqJdgTA== // -----END CERTIFICATE----- var rc4ClientScript = [][]byte{ { @@ -210,3 +230,179 @@ var rc4ClientScript = [][]byte{ 0x59, 0xac, 0xc6, 0xb5, 0x56, 0x55, 0x96, }, } + +var ecdheAESClientScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00, + 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x13, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x52, 0x02, 0x00, 0x00, + 0x4e, 0x03, 0x01, 0x50, 0xad, 0x72, 0xb1, 0x14, + 0x45, 0xce, 0x0a, 0x95, 0xf9, 0x63, 0xef, 0xa8, + 0xe5, 0x07, 0x34, 0x04, 0xe9, 0x08, 0x0f, 0x38, + 0xe4, 0x28, 0x27, 0x91, 0x07, 0x03, 0xe2, 0xfe, + 0xe3, 0x25, 0xf7, 0x20, 0x08, 0x42, 0xa2, 0x01, + 0x69, 0x53, 0xf0, 0xd9, 0x4c, 0xfa, 0x01, 0xa1, + 0xce, 0x4b, 0xf8, 0x28, 0x21, 0xad, 0x06, 0xbe, + 0xe0, 0x1b, 0x3b, 0xf7, 0xec, 0xd2, 0x52, 0xae, + 0x2a, 0x57, 0xb7, 0xa8, 0xc0, 0x13, 0x00, 0x00, + 0x06, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x16, + 0x03, 0x01, 0x02, 0x39, 0x0b, 0x00, 0x02, 0x35, + 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, 0x30, 0x82, + 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xb1, 0x35, + 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x32, 0x30, 0x34, 0x30, 0x36, 0x31, 0x37, + 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17, 0x0d, 0x31, + 0x35, 0x30, 0x34, 0x30, 0x36, 0x31, 0x37, 0x31, + 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, + 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3, 0xc3, 0x84, + 0x27, 0x95, 0xff, 0x12, 0x31, 0x52, 0x0f, 0x15, + 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80, 0xe6, 0x36, + 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61, 0x8d, 0xe0, + 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe, 0x55, 0x66, + 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a, 0xfe, 0xa8, + 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff, 0xee, 0xd7, + 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f, 0xff, 0x2a, + 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, 0xa4, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a, 0x63, 0xb5, + 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, 0x7c, 0x23, + 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, 0x30, 0x75, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x6e, 0x30, + 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97, 0x9a, 0x63, + 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, 0x7c, + 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, 0xa1, + 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1, 0x35, 0x13, + 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, 0x0c, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x03, 0x41, 0x00, 0x85, 0x36, 0x40, + 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4, 0x59, 0x9f, + 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74, 0xec, 0x83, + 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf, 0x39, 0xac, + 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46, 0x1d, 0x99, + 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b, 0x05, 0x08, + 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92, 0xbb, 0x77, + 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, 0x5e, 0x9c, + 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, 0x03, 0x01, + 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87, 0x03, 0x00, + 0x17, 0x41, 0x04, 0x1c, 0x8f, 0x9c, 0x6d, 0xe7, + 0xab, 0x3e, 0xf8, 0x0a, 0x5d, 0xe1, 0x86, 0xb4, + 0xe2, 0x8e, 0xb2, 0x1c, 0x3b, 0xd9, 0xb6, 0x08, + 0x80, 0x58, 0x21, 0xe9, 0x0e, 0xc6, 0x66, 0x67, + 0x97, 0xcb, 0xb9, 0x92, 0x07, 0x00, 0xc4, 0xe5, + 0xec, 0x5f, 0xb4, 0xe2, 0x20, 0xa9, 0xc9, 0x62, + 0xd0, 0x98, 0xd5, 0xe3, 0x53, 0xff, 0xd0, 0x0a, + 0x6e, 0x29, 0x69, 0x39, 0x2a, 0x4b, 0x5c, 0xd8, + 0x6c, 0xf5, 0xfe, 0x00, 0x40, 0x35, 0xa7, 0x26, + 0x2e, 0xc2, 0x48, 0x93, 0x32, 0xf7, 0x7d, 0x0f, + 0x0d, 0x77, 0x56, 0x9a, 0x85, 0x0c, 0xa6, 0x74, + 0x06, 0xb8, 0x3d, 0x90, 0x56, 0x12, 0x63, 0xff, + 0x00, 0x5e, 0x0f, 0xf7, 0x24, 0xf7, 0xdb, 0x48, + 0x71, 0xe9, 0x2e, 0x03, 0xd3, 0xfa, 0x3a, 0xae, + 0xa0, 0xc1, 0x77, 0x3c, 0x4c, 0x59, 0xce, 0x33, + 0x1a, 0xd2, 0x47, 0x83, 0xfa, 0xea, 0xd8, 0x1e, + 0x06, 0xe7, 0x7d, 0xa0, 0x9b, 0x16, 0x03, 0x01, + 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00, + 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d, + 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5, + 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd, + 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce, + 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e, + 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56, + 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49, + 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b, + 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xd9, 0xa7, + 0x80, 0x56, 0x3f, 0xa3, 0x8f, 0x96, 0x72, 0x4e, + 0x4e, 0x6e, 0x23, 0x41, 0x8f, 0xda, 0x91, 0xb2, + 0x9e, 0x63, 0x23, 0x82, 0x64, 0xcd, 0x07, 0x24, + 0xd3, 0x40, 0x20, 0x22, 0x4c, 0xe3, 0xff, 0x38, + 0xbb, 0x43, 0x9d, 0x57, 0x11, 0xd5, 0x46, 0xa5, + 0x05, 0x29, 0x92, 0x02, 0xce, 0xdf, + }, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x90, 0xe7, 0xba, 0x0e, 0xb1, 0xda, + 0x92, 0xb5, 0x77, 0x56, 0x38, 0xa6, 0x22, 0xc1, + 0x72, 0xeb, 0x8a, 0x68, 0x09, 0xb6, 0x74, 0xad, + 0xb3, 0x4a, 0xf2, 0xdd, 0x09, 0x9b, 0xc9, 0x4f, + 0x84, 0x73, 0x8b, 0xd6, 0x97, 0x50, 0x23, 0x1c, + 0xa0, 0xc2, 0x0c, 0x25, 0x18, 0xdd, 0x5e, 0x15, + 0x4d, 0xd9, 0xef, 0x4f, 0x6a, 0x43, 0x61, 0x9c, + 0x95, 0xde, 0x3c, 0x66, 0xc4, 0xc1, 0x33, 0x56, + 0xdd, 0x2f, 0x90, 0xaf, 0x68, 0x5c, 0x9c, 0xa4, + 0x90, 0x6d, 0xbf, 0x51, 0x1d, 0x68, 0xcb, 0x81, + 0x77, 0x52, 0xa0, 0x93, 0x2a, 0xf8, 0xc7, 0x61, + 0x87, 0x76, 0xca, 0x93, 0x9e, 0xd6, 0xee, 0x6f, + 0x3f, 0xeb, 0x7d, 0x06, 0xdd, 0x73, 0x4e, 0x27, + 0x16, 0x63, 0x92, 0xe4, 0xb2, 0x3f, 0x91, 0x23, + 0x21, 0x97, 0x90, 0xce, 0x53, 0xb8, 0xb0, 0x9d, + 0xbd, 0xbd, 0x33, 0x84, 0xad, 0x6b, 0x2e, 0x7b, + 0xf5, 0xeb, 0x1d, 0x64, 0x37, 0x2e, 0x29, 0x4e, + 0xb0, 0x93, 0xdb, 0x92, 0xc7, 0xaa, 0x94, 0xa5, + 0x3b, 0x64, 0xd0, + }, + { + 0x17, 0x03, 0x01, 0x00, 0x20, 0x11, 0xd8, 0x6b, + 0x3c, 0xf6, 0xbe, 0xf4, 0x54, 0x87, 0xec, 0x75, + 0x0c, 0x44, 0xdb, 0x92, 0xfc, 0xde, 0x7e, 0x0f, + 0x9f, 0x87, 0x87, 0x9c, 0x03, 0xd5, 0x07, 0x84, + 0xe0, 0x3a, 0xf8, 0xae, 0x14, 0x17, 0x03, 0x01, + 0x00, 0x20, 0xba, 0x54, 0xef, 0x5b, 0xce, 0xfd, + 0x47, 0x76, 0x6d, 0xa1, 0x8b, 0xfd, 0x48, 0xde, + 0x6e, 0x26, 0xc1, 0x0c, 0x9d, 0x54, 0xbf, 0x98, + 0xf6, 0x1c, 0x80, 0xb9, 0xca, 0x93, 0x81, 0x0a, + 0x2e, 0x06, 0x15, 0x03, 0x01, 0x00, 0x20, 0x93, + 0x3e, 0x38, 0x17, 0xc9, 0x0a, 0xc3, 0xea, 0xd3, + 0x92, 0x75, 0xa6, 0x53, 0x37, 0x4d, 0x74, 0x94, + 0xbe, 0x01, 0xdc, 0x5c, 0x5a, 0x0f, 0x09, 0xf6, + 0x57, 0x33, 0xc3, 0xbc, 0x3f, 0x7a, 0x4d, + }, +} diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go index 54c7a3e63..cdd491707 100644 --- a/src/pkg/crypto/tls/handshake_messages.go +++ b/src/pkg/crypto/tls/handshake_messages.go @@ -18,6 +18,8 @@ type clientHelloMsg struct { ocspStapling bool supportedCurves []uint16 supportedPoints []uint8 + ticketSupported bool + sessionTicket []uint8 } func (m *clientHelloMsg) equal(i interface{}) bool { @@ -36,7 +38,9 @@ func (m *clientHelloMsg) equal(i interface{}) bool { m.serverName == m1.serverName && m.ocspStapling == m1.ocspStapling && eqUint16s(m.supportedCurves, m1.supportedCurves) && - bytes.Equal(m.supportedPoints, m1.supportedPoints) + bytes.Equal(m.supportedPoints, m1.supportedPoints) && + m.ticketSupported == m1.ticketSupported && + bytes.Equal(m.sessionTicket, m1.sessionTicket) } func (m *clientHelloMsg) marshal() []byte { @@ -66,6 +70,10 @@ func (m *clientHelloMsg) marshal() []byte { extensionsLength += 1 + len(m.supportedPoints) numExtensions++ } + if m.ticketSupported { + extensionsLength += len(m.sessionTicket) + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength @@ -180,6 +188,17 @@ func (m *clientHelloMsg) marshal() []byte { z = z[1:] } } + if m.ticketSupported { + // http://tools.ietf.org/html/rfc5077#section-3.2 + z[0] = byte(extensionSessionTicket >> 8) + z[1] = byte(extensionSessionTicket) + l := len(m.sessionTicket) + z[2] = byte(l >> 8) + z[3] = byte(l) + z = z[4:] + copy(z, m.sessionTicket) + z = z[len(m.sessionTicket):] + } m.raw = x @@ -228,6 +247,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.nextProtoNeg = false m.serverName = "" m.ocspStapling = false + m.ticketSupported = false + m.sessionTicket = nil if len(data) == 0 { // ClientHello is optionally followed by extension data @@ -311,6 +332,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { } m.supportedPoints = make([]uint8, l) copy(m.supportedPoints, data[1:]) + case extensionSessionTicket: + // http://tools.ietf.org/html/rfc5077#section-3.2 + m.ticketSupported = true + m.sessionTicket = data[:length] } data = data[length:] } @@ -328,6 +353,7 @@ type serverHelloMsg struct { nextProtoNeg bool nextProtos []string ocspStapling bool + ticketSupported bool } func (m *serverHelloMsg) equal(i interface{}) bool { @@ -344,7 +370,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool { m.compressionMethod == m1.compressionMethod && m.nextProtoNeg == m1.nextProtoNeg && eqStrings(m.nextProtos, m1.nextProtos) && - m.ocspStapling == m1.ocspStapling + m.ocspStapling == m1.ocspStapling && + m.ticketSupported == m1.ticketSupported } func (m *serverHelloMsg) marshal() []byte { @@ -368,6 +395,9 @@ func (m *serverHelloMsg) marshal() []byte { if m.ocspStapling { numExtensions++ } + if m.ticketSupported { + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength @@ -416,6 +446,11 @@ func (m *serverHelloMsg) marshal() []byte { z[1] = byte(extensionStatusRequest) z = z[4:] } + if m.ticketSupported { + z[0] = byte(extensionSessionTicket >> 8) + z[1] = byte(extensionSessionTicket) + z = z[4:] + } m.raw = x @@ -445,6 +480,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { m.nextProtoNeg = false m.nextProtos = nil m.ocspStapling = false + m.ticketSupported = false if len(data) == 0 { // ServerHello is optionally followed by extension data @@ -474,14 +510,14 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { switch extension { case extensionNextProtoNeg: m.nextProtoNeg = true - d := data + d := data[:length] for len(d) > 0 { l := int(d[0]) d = d[1:] if l == 0 || l > len(d) { return false } - m.nextProtos = append(m.nextProtos, string(d[0:l])) + m.nextProtos = append(m.nextProtos, string(d[:l])) d = d[l:] } case extensionStatusRequest: @@ -489,6 +525,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { return false } m.ocspStapling = true + case extensionSessionTicket: + if length > 0 { + return false + } + m.ticketSupported = true } data = data[length:] } @@ -1030,6 +1071,65 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool { return true } +type newSessionTicketMsg struct { + raw []byte + ticket []byte +} + +func (m *newSessionTicketMsg) equal(i interface{}) bool { + m1, ok := i.(*newSessionTicketMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.ticket, m1.ticket) +} + +func (m *newSessionTicketMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See http://tools.ietf.org/html/rfc5077#section-3.3 + ticketLen := len(m.ticket) + length := 2 + 4 + ticketLen + x = make([]byte, 4+length) + x[0] = typeNewSessionTicket + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + x[8] = uint8(ticketLen >> 8) + x[9] = uint8(ticketLen) + copy(x[10:], m.ticket) + + m.raw = x + + return +} + +func (m *newSessionTicketMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 10 { + return false + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return false + } + + ticketLen := int(data[8])<<8 + int(data[9]) + if len(data)-10 != ticketLen { + return false + } + + m.ticket = data[10:] + + return true +} + func eqUint16s(x, y []uint16) bool { if len(x) != len(y) { return false diff --git a/src/pkg/crypto/tls/handshake_messages_test.go b/src/pkg/crypto/tls/handshake_messages_test.go index e62a9d581..3434bad9f 100644 --- a/src/pkg/crypto/tls/handshake_messages_test.go +++ b/src/pkg/crypto/tls/handshake_messages_test.go @@ -22,6 +22,8 @@ var tests = []interface{}{ &certificateStatusMsg{}, &clientKeyExchangeMsg{}, &nextProtoMsg{}, + &newSessionTicketMsg{}, + &sessionState{}, } type testMessage interface { @@ -127,6 +129,12 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { for i := range m.supportedCurves { m.supportedCurves[i] = uint16(rand.Intn(30000)) } + if rand.Intn(10) > 5 { + m.ticketSupported = true + if rand.Intn(10) > 5 { + m.sessionTicket = randomBytes(rand.Intn(300), rand) + } + } return reflect.ValueOf(m) } @@ -149,6 +157,13 @@ func (*serverHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { } } + if rand.Intn(10) > 5 { + m.ocspStapling = true + } + if rand.Intn(10) > 5 { + m.ticketSupported = true + } + return reflect.ValueOf(m) } @@ -207,3 +222,22 @@ func (*nextProtoMsg) Generate(rand *rand.Rand, size int) reflect.Value { m.proto = randomString(rand.Intn(255), rand) return reflect.ValueOf(m) } + +func (*newSessionTicketMsg) Generate(rand *rand.Rand, size int) reflect.Value { + m := &newSessionTicketMsg{} + m.ticket = randomBytes(rand.Intn(4), rand) + return reflect.ValueOf(m) +} + +func (*sessionState) Generate(rand *rand.Rand, size int) reflect.Value { + s := &sessionState{} + s.vers = uint16(rand.Intn(10000)) + s.cipherSuite = uint16(rand.Intn(10000)) + s.masterSecret = randomBytes(rand.Intn(100), rand) + numCerts := rand.Intn(20) + s.certificates = make([][]byte, numCerts) + for i := 0; i < numCerts; i++ { + s.certificates[i] = randomBytes(rand.Intn(10)+1, rand) + } + return reflect.ValueOf(s) +} diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index 76adc540c..730991016 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -13,31 +13,120 @@ import ( "io" ) +// serverHandshakeState contains details of a server handshake in progress. +// It's discarded once the handshake has completed. +type serverHandshakeState struct { + c *Conn + clientHello *clientHelloMsg + hello *serverHelloMsg + suite *cipherSuite + ellipticOk bool + sessionState *sessionState + finishedHash finishedHash + masterSecret []byte + certsFromClient [][]byte +} + +// serverHandshake performs a TLS handshake as a server. func (c *Conn) serverHandshake() error { config := c.config - msg, err := c.readHandshake() + + // If this is the first server handshake, we generate a random key to + // encrypt the tickets with. + config.serverInitOnce.Do(func() { + if config.SessionTicketsDisabled { + return + } + + // If the key has already been set then we have nothing to do. + for _, b := range config.SessionTicketKey { + if b != 0 { + return + } + } + + if _, err := io.ReadFull(config.rand(), config.SessionTicketKey[:]); err != nil { + config.SessionTicketsDisabled = true + } + }) + + hs := serverHandshakeState{ + c: c, + } + isResume, err := hs.readClientHello() if err != nil { return err } - clientHello, ok := msg.(*clientHelloMsg) + + // For an overview of TLS handshaking, see https://tools.ietf.org/html/rfc5246#section-7.3 + if isResume { + // The client has included a session ticket and so we do an abbreviated handshake. + if err := hs.doResumeHandshake(); err != nil { + return err + } + if err := hs.establishKeys(); err != nil { + return err + } + if err := hs.sendFinished(); err != nil { + return err + } + if err := hs.readFinished(); err != nil { + return err + } + c.didResume = true + } else { + // The client didn't include a session ticket, or it wasn't + // valid so we do a full handshake. + if err := hs.doFullHandshake(); err != nil { + return err + } + if err := hs.establishKeys(); err != nil { + return err + } + if err := hs.readFinished(); err != nil { + return err + } + if err := hs.sendSessionTicket(); err != nil { + return err + } + if err := hs.sendFinished(); err != nil { + return err + } + } + c.handshakeComplete = true + + return nil +} + +// readClientHello reads a ClientHello message from the client and decides +// whether we will perform session resumption. +func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { + config := hs.c.config + c := hs.c + + msg, err := c.readHandshake() + if err != nil { + return false, err + } + var ok bool + hs.clientHello, ok = msg.(*clientHelloMsg) if !ok { - return c.sendAlert(alertUnexpectedMessage) + return false, c.sendAlert(alertUnexpectedMessage) } - vers, ok := mutualVersion(clientHello.vers) + c.vers, ok = mutualVersion(hs.clientHello.vers) if !ok { - return c.sendAlert(alertProtocolVersion) + return false, c.sendAlert(alertProtocolVersion) } - c.vers = vers c.haveVers = true - finishedHash := newFinishedHash(vers) - finishedHash.Write(clientHello.marshal()) + hs.finishedHash = newFinishedHash(c.vers) + hs.finishedHash.Write(hs.clientHello.marshal()) - hello := new(serverHelloMsg) + hs.hello = new(serverHelloMsg) supportedCurve := false Curves: - for _, curve := range clientHello.supportedCurves { + for _, curve := range hs.clientHello.supportedCurves { switch curve { case curveP256, curveP384, curveP521: supportedCurve = true @@ -46,110 +135,182 @@ Curves: } supportedPointFormat := false - for _, pointFormat := range clientHello.supportedPoints { + for _, pointFormat := range hs.clientHello.supportedPoints { if pointFormat == pointFormatUncompressed { supportedPointFormat = true break } } - - ellipticOk := supportedCurve && supportedPointFormat - - var suite *cipherSuite -FindCipherSuite: - for _, id := range clientHello.cipherSuites { - for _, supported := range config.cipherSuites() { - if id == supported { - var candidate *cipherSuite - - for _, s := range cipherSuites { - if s.id == id { - candidate = s - break - } - } - if candidate == nil { - continue - } - // Don't select a ciphersuite which we can't - // support for this client. - if candidate.elliptic && !ellipticOk { - continue - } - suite = candidate - break FindCipherSuite - } - } - } + hs.ellipticOk = supportedCurve && supportedPointFormat foundCompression := false // We only support null compression, so check that the client offered it. - for _, compression := range clientHello.compressionMethods { + for _, compression := range hs.clientHello.compressionMethods { if compression == compressionNone { foundCompression = true break } } - if suite == nil || !foundCompression { - return c.sendAlert(alertHandshakeFailure) + if !foundCompression { + return false, c.sendAlert(alertHandshakeFailure) } - hello.vers = vers - hello.cipherSuite = suite.id + hs.hello.vers = c.vers t := uint32(config.time().Unix()) - hello.random = make([]byte, 32) - hello.random[0] = byte(t >> 24) - hello.random[1] = byte(t >> 16) - hello.random[2] = byte(t >> 8) - hello.random[3] = byte(t) - _, err = io.ReadFull(config.rand(), hello.random[4:]) + hs.hello.random = make([]byte, 32) + hs.hello.random[0] = byte(t >> 24) + hs.hello.random[1] = byte(t >> 16) + hs.hello.random[2] = byte(t >> 8) + hs.hello.random[3] = byte(t) + _, err = io.ReadFull(config.rand(), hs.hello.random[4:]) if err != nil { - return c.sendAlert(alertInternalError) + return false, c.sendAlert(alertInternalError) + } + hs.hello.compressionMethod = compressionNone + if len(hs.clientHello.serverName) > 0 { + c.serverName = hs.clientHello.serverName + } + if hs.clientHello.nextProtoNeg { + hs.hello.nextProtoNeg = true + hs.hello.nextProtos = config.NextProtos + } + + if hs.checkForResumption() { + return true, nil + } + + var preferenceList, supportedList []uint16 + if c.config.PreferServerCipherSuites { + preferenceList = c.config.cipherSuites() + supportedList = hs.clientHello.cipherSuites + } else { + preferenceList = hs.clientHello.cipherSuites + supportedList = c.config.cipherSuites() + } + + for _, id := range preferenceList { + if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk); hs.suite != nil { + break + } + } + + if hs.suite == nil { + return false, c.sendAlert(alertHandshakeFailure) + } + + return false, nil +} + +// checkForResumption returns true if we should perform resumption on this connection. +func (hs *serverHandshakeState) checkForResumption() bool { + c := hs.c + + var ok bool + if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok { + return false } - hello.compressionMethod = compressionNone - if clientHello.nextProtoNeg { - hello.nextProtoNeg = true - hello.nextProtos = config.NextProtos + + if hs.sessionState.vers > hs.clientHello.vers { + return false + } + if vers, ok := mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers { + return false + } + + cipherSuiteOk := false + // Check that the client is still offering the ciphersuite in the session. + for _, id := range hs.clientHello.cipherSuites { + if id == hs.sessionState.cipherSuite { + cipherSuiteOk = true + break + } + } + if !cipherSuiteOk { + return false + } + + // Check that we also support the ciphersuite from the session. + hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.ellipticOk) + if hs.suite == nil { + return false + } + + sessionHasClientCerts := len(hs.sessionState.certificates) != 0 + needClientCerts := c.config.ClientAuth == RequireAnyClientCert || c.config.ClientAuth == RequireAndVerifyClientCert + if needClientCerts && !sessionHasClientCerts { + return false + } + if sessionHasClientCerts && c.config.ClientAuth == NoClientCert { + return false } + return true +} + +func (hs *serverHandshakeState) doResumeHandshake() error { + c := hs.c + + hs.hello.cipherSuite = hs.suite.id + // We echo the client's session ID in the ServerHello to let it know + // that we're doing a resumption. + hs.hello.sessionId = hs.clientHello.sessionId + hs.finishedHash.Write(hs.hello.marshal()) + c.writeRecord(recordTypeHandshake, hs.hello.marshal()) + + if len(hs.sessionState.certificates) > 0 { + if _, err := hs.processCertsFromClient(hs.sessionState.certificates); err != nil { + return err + } + } + + hs.masterSecret = hs.sessionState.masterSecret + + return nil +} + +func (hs *serverHandshakeState) doFullHandshake() error { + config := hs.c.config + c := hs.c + if len(config.Certificates) == 0 { return c.sendAlert(alertInternalError) } cert := &config.Certificates[0] - if len(clientHello.serverName) > 0 { - c.serverName = clientHello.serverName - cert = config.getCertificateForName(clientHello.serverName) + if len(hs.clientHello.serverName) > 0 { + cert = config.getCertificateForName(hs.clientHello.serverName) } - if clientHello.ocspStapling && len(cert.OCSPStaple) > 0 { - hello.ocspStapling = true + if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 { + hs.hello.ocspStapling = true } - finishedHash.Write(hello.marshal()) - c.writeRecord(recordTypeHandshake, hello.marshal()) + hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled + hs.hello.cipherSuite = hs.suite.id + hs.finishedHash.Write(hs.hello.marshal()) + c.writeRecord(recordTypeHandshake, hs.hello.marshal()) certMsg := new(certificateMsg) certMsg.certificates = cert.Certificate - finishedHash.Write(certMsg.marshal()) + hs.finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) - if hello.ocspStapling { + if hs.hello.ocspStapling { certStatus := new(certificateStatusMsg) certStatus.statusType = statusTypeOCSP certStatus.response = cert.OCSPStaple - finishedHash.Write(certStatus.marshal()) + hs.finishedHash.Write(certStatus.marshal()) c.writeRecord(recordTypeHandshake, certStatus.marshal()) } - keyAgreement := suite.ka() - skx, err := keyAgreement.generateServerKeyExchange(config, cert, clientHello, hello) + keyAgreement := hs.suite.ka() + skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello) if err != nil { c.sendAlert(alertHandshakeFailure) return err } if skx != nil { - finishedHash.Write(skx.marshal()) + hs.finishedHash.Write(skx.marshal()) c.writeRecord(recordTypeHandshake, skx.marshal()) } @@ -166,28 +327,29 @@ FindCipherSuite: if config.ClientCAs != nil { certReq.certificateAuthorities = config.ClientCAs.Subjects() } - finishedHash.Write(certReq.marshal()) + hs.finishedHash.Write(certReq.marshal()) c.writeRecord(recordTypeHandshake, certReq.marshal()) } helloDone := new(serverHelloDoneMsg) - finishedHash.Write(helloDone.marshal()) + hs.finishedHash.Write(helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal()) var pub *rsa.PublicKey // public key for client auth, if any - msg, err = c.readHandshake() + msg, err := c.readHandshake() if err != nil { return err } + var ok bool // If we requested a client certificate, then the client must send a // certificate message, even if it's empty. if config.ClientAuth >= RequestClientCert { if certMsg, ok = msg.(*certificateMsg); !ok { return c.sendAlert(alertHandshakeFailure) } - finishedHash.Write(certMsg.marshal()) + hs.finishedHash.Write(certMsg.marshal()) if len(certMsg.certificates) == 0 { // The client didn't actually send a certificate @@ -198,54 +360,9 @@ FindCipherSuite: } } - certs := make([]*x509.Certificate, len(certMsg.certificates)) - for i, asn1Data := range certMsg.certificates { - if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to parse client certificate: " + err.Error()) - } - } - - if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { - opts := x509.VerifyOptions{ - Roots: c.config.ClientCAs, - CurrentTime: c.config.time(), - Intermediates: x509.NewCertPool(), - } - - for i, cert := range certs { - if i == 0 { - continue - } - opts.Intermediates.AddCert(cert) - } - - chains, err := certs[0].Verify(opts) - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to verify client's certificate: " + err.Error()) - } - - ok := false - for _, ku := range certs[0].ExtKeyUsage { - if ku == x509.ExtKeyUsageClientAuth { - ok = true - break - } - } - if !ok { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication") - } - - c.verifiedChains = chains - } - - if len(certs) > 0 { - if pub, ok = certs[0].PublicKey.(*rsa.PublicKey); !ok { - return c.sendAlert(alertUnsupportedCertificate) - } - c.peerCertificates = certs + pub, err = hs.processCertsFromClient(certMsg.certificates) + if err != nil { + return err } msg, err = c.readHandshake() @@ -259,7 +376,7 @@ FindCipherSuite: if !ok { return c.sendAlert(alertUnexpectedMessage) } - finishedHash.Write(ckx.marshal()) + hs.finishedHash.Write(ckx.marshal()) // If we received a client cert in response to our certificate request message, // the client will send us a certificateVerifyMsg immediately after the @@ -278,15 +395,15 @@ FindCipherSuite: } digest := make([]byte, 0, 36) - digest = finishedHash.serverMD5.Sum(digest) - digest = finishedHash.serverSHA1.Sum(digest) + digest = hs.finishedHash.serverMD5.Sum(digest) + digest = hs.finishedHash.serverSHA1.Sum(digest) err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) if err != nil { c.sendAlert(alertBadCertificate) return errors.New("could not validate signature of connection nonces: " + err.Error()) } - finishedHash.Write(certVerify.marshal()) + hs.finishedHash.Write(certVerify.marshal()) } preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers) @@ -294,20 +411,38 @@ FindCipherSuite: c.sendAlert(alertHandshakeFailure) return err } + hs.masterSecret = masterFromPreMasterSecret(c.vers, preMasterSecret, hs.clientHello.random, hs.hello.random) + + return nil +} + +func (hs *serverHandshakeState) establishKeys() error { + c := hs.c - masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) + clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := + keysFromMasterSecret(c.vers, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) - clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */) - clientHash := suite.mac(c.vers, clientMAC) + clientCipher := hs.suite.cipher(clientKey, clientIV, true /* for reading */) + clientHash := hs.suite.mac(c.vers, clientMAC) c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) + + serverCipher := hs.suite.cipher(serverKey, serverIV, false /* not for reading */) + serverHash := hs.suite.mac(c.vers, serverMAC) + c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) + + return nil +} + +func (hs *serverHandshakeState) readFinished() error { + c := hs.c + c.readRecord(recordTypeChangeCipherSpec) if err := c.error(); err != nil { return err } - if hello.nextProtoNeg { - msg, err = c.readHandshake() + if hs.hello.nextProtoNeg { + msg, err := c.readHandshake() if err != nil { return err } @@ -315,11 +450,11 @@ FindCipherSuite: if !ok { return c.sendAlert(alertUnexpectedMessage) } - finishedHash.Write(nextProto.marshal()) + hs.finishedHash.Write(nextProto.marshal()) c.clientProtocol = nextProto.proto } - msg, err = c.readHandshake() + msg, err := c.readHandshake() if err != nil { return err } @@ -328,25 +463,142 @@ FindCipherSuite: return c.sendAlert(alertUnexpectedMessage) } - verify := finishedHash.clientSum(masterSecret) + verify := hs.finishedHash.clientSum(hs.masterSecret) if len(verify) != len(clientFinished.verifyData) || subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { return c.sendAlert(alertHandshakeFailure) } - finishedHash.Write(clientFinished.marshal()) + hs.finishedHash.Write(clientFinished.marshal()) + return nil +} + +func (hs *serverHandshakeState) sendSessionTicket() error { + if !hs.hello.ticketSupported { + return nil + } + + c := hs.c + m := new(newSessionTicketMsg) + + var err error + state := sessionState{ + vers: c.vers, + cipherSuite: hs.suite.id, + masterSecret: hs.masterSecret, + certificates: hs.certsFromClient, + } + m.ticket, err = c.encryptTicket(&state) + if err != nil { + return err + } + + hs.finishedHash.Write(m.marshal()) + c.writeRecord(recordTypeHandshake, m.marshal()) + + return nil +} + +func (hs *serverHandshakeState) sendFinished() error { + c := hs.c - serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */) - serverHash := suite.mac(c.vers, serverMAC) - c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) finished := new(finishedMsg) - finished.verifyData = finishedHash.serverSum(masterSecret) + finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) + hs.finishedHash.Write(finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal()) - c.handshakeComplete = true - c.cipherSuite = suite.id + c.cipherSuite = hs.suite.id + + return nil +} + +// processCertsFromClient takes a chain of client certificates either from a +// Certificates message or from a sessionState and verifies them. It returns +// the public key of the leaf certificate. +func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*rsa.PublicKey, error) { + c := hs.c + + hs.certsFromClient = certificates + certs := make([]*x509.Certificate, len(certificates)) + var err error + for i, asn1Data := range certificates { + if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { + c.sendAlert(alertBadCertificate) + return nil, errors.New("tls: failed to parse client certificate: " + err.Error()) + } + } + + if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { + opts := x509.VerifyOptions{ + Roots: c.config.ClientCAs, + CurrentTime: c.config.time(), + Intermediates: x509.NewCertPool(), + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + + for _, cert := range certs[1:] { + opts.Intermediates.AddCert(cert) + } + + chains, err := certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return nil, errors.New("tls: failed to verify client's certificate: " + err.Error()) + } + + ok := false + for _, ku := range certs[0].ExtKeyUsage { + if ku == x509.ExtKeyUsageClientAuth { + ok = true + break + } + } + if !ok { + c.sendAlert(alertHandshakeFailure) + return nil, errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication") + } + + c.verifiedChains = chains + } + + if len(certs) > 0 { + pub, ok := certs[0].PublicKey.(*rsa.PublicKey) + if !ok { + return nil, c.sendAlert(alertUnsupportedCertificate) + } + c.peerCertificates = certs + return pub, nil + } + + return nil, nil +} + +// tryCipherSuite returns a cipherSuite with the given id if that cipher suite +// is acceptable to use. +func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk bool) *cipherSuite { + for _, supported := range supportedCipherSuites { + if id == supported { + var candidate *cipherSuite + + for _, s := range cipherSuites { + if s.id == id { + candidate = s + break + } + } + if candidate == nil { + continue + } + // Don't select a ciphersuite which we can't + // support for this client. + if candidate.elliptic && !ellipticOk { + continue + } + return candidate + } + } return nil } diff --git a/src/pkg/crypto/tls/handshake_server_test.go b/src/pkg/crypto/tls/handshake_server_test.go index 7c1267101..bf8cbe3ae 100644 --- a/src/pkg/crypto/tls/handshake_server_test.go +++ b/src/pkg/crypto/tls/handshake_server_test.go @@ -11,12 +11,15 @@ import ( "encoding/hex" "encoding/pem" "flag" + "fmt" "io" "log" "math/big" "net" + "os" "strconv" "strings" + "sync" "testing" "time" ) @@ -80,13 +83,20 @@ func TestRejectBadProtocolVersion(t *testing.T) { } func TestNoSuiteOverlap(t *testing.T) { - clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{0xff00}, []uint8{0}, false, "", false, nil, nil} + clientHello := &clientHelloMsg{ + vers: 0x0301, + cipherSuites: []uint16{0xff00}, + compressionMethods: []uint8{0}, + } testClientHelloFailure(t, clientHello, alertHandshakeFailure) - } func TestNoCompressionOverlap(t *testing.T) { - clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{TLS_RSA_WITH_RC4_128_SHA}, []uint8{0xff}, false, "", false, nil, nil} + clientHello := &clientHelloMsg{ + vers: 0x0301, + cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + compressionMethods: []uint8{0xff}, + } testClientHelloFailure(t, clientHello, alertHandshakeFailure) } @@ -115,6 +125,50 @@ func TestClose(t *testing.T) { } } +func testHandshake(clientConfig, serverConfig *Config) (state ConnectionState, err error) { + c, s := net.Pipe() + go func() { + cli := Client(c, clientConfig) + cli.Handshake() + c.Close() + }() + server := Server(s, serverConfig) + err = server.Handshake() + if err == nil { + state = server.ConnectionState() + } + s.Close() + return +} + +func TestCipherSuitePreference(t *testing.T) { + serverConfig := &Config{ + CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA}, + Certificates: testConfig.Certificates, + } + clientConfig := &Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_RC4_128_SHA}, + InsecureSkipVerify: true, + } + state, err := testHandshake(clientConfig, serverConfig) + if err != nil { + t.Fatalf("handshake failed: %s", err) + } + if state.CipherSuite != TLS_RSA_WITH_AES_128_CBC_SHA { + // By default the server should use the client's preference. + t.Fatalf("Client's preference was not used, got %x", state.CipherSuite) + } + + serverConfig.PreferServerCipherSuites = true + state, err = testHandshake(clientConfig, serverConfig) + if err != nil { + t.Fatalf("handshake failed: %s", err) + } + if state.CipherSuite != TLS_RSA_WITH_RC4_128_SHA { + t.Fatalf("Server's preference was not used, got %x", state.CipherSuite) + } +} + func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config, peers []*x509.Certificate) { c, s := net.Pipe() srv := Server(s, config) @@ -186,6 +240,11 @@ func TestHandshakeServerSNI(t *testing.T) { testServerScript(t, "SNI", selectCertificateBySNIScript, testConfig, nil) } +func TestResumption(t *testing.T) { + testServerScript(t, "IssueTicket", issueSessionTicketTest, testConfig, nil) + testServerScript(t, "Resume", serverResumeTest, testConfig, nil) +} + type clientauthTest struct { name string clientauth ClientAuthType @@ -203,55 +262,133 @@ func TestClientAuth(t *testing.T) { } } +// recordingConn is a net.Conn that records the traffic that passes through it. +// WriteTo can be used to produce Go code that contains the recorded traffic. +type recordingConn struct { + net.Conn + lock sync.Mutex + flows [][]byte + currentlyReading bool +} + +func (r *recordingConn) Read(b []byte) (n int, err error) { + if n, err = r.Conn.Read(b); n == 0 { + return + } + b = b[:n] + + r.lock.Lock() + defer r.lock.Unlock() + + if l := len(r.flows); l == 0 || !r.currentlyReading { + buf := make([]byte, len(b)) + copy(buf, b) + r.flows = append(r.flows, buf) + } else { + r.flows[l-1] = append(r.flows[l-1], b[:n]...) + } + r.currentlyReading = true + return +} + +func (r *recordingConn) Write(b []byte) (n int, err error) { + if n, err = r.Conn.Write(b); n == 0 { + return + } + b = b[:n] + + r.lock.Lock() + defer r.lock.Unlock() + + if l := len(r.flows); l == 0 || r.currentlyReading { + buf := make([]byte, len(b)) + copy(buf, b) + r.flows = append(r.flows, buf) + } else { + r.flows[l-1] = append(r.flows[l-1], b[:n]...) + } + r.currentlyReading = false + return +} + +// WriteTo writes Go source code to w that contains the recorded traffic. +func (r *recordingConn) WriteTo(w io.Writer) { + fmt.Fprintf(w, "var changeMe = [][]byte {\n") + for _, buf := range r.flows { + fmt.Fprintf(w, "\t{") + for i, b := range buf { + if i%8 == 0 { + fmt.Fprintf(w, "\n\t\t") + } + fmt.Fprintf(w, "0x%02x, ", b) + } + fmt.Fprintf(w, "\n\t},\n") + } + fmt.Fprintf(w, "}\n") +} + var serve = flag.Bool("serve", false, "run a TLS server on :10443") var testCipherSuites = flag.String("ciphersuites", "0x"+strconv.FormatInt(int64(TLS_RSA_WITH_RC4_128_SHA), 16), "cipher suites to accept in serving mode") var testClientAuth = flag.Int("clientauth", 0, "value for tls.Config.ClientAuth") -func TestRunServer(t *testing.T) { - if !*serve { - return - } - +func GetTestConfig() *Config { + var config = *testConfig suites := strings.Split(*testCipherSuites, ",") - testConfig.CipherSuites = make([]uint16, len(suites)) + config.CipherSuites = make([]uint16, len(suites)) for i := range suites { suite, err := strconv.ParseUint(suites[i], 0, 64) if err != nil { panic(err) } - testConfig.CipherSuites[i] = uint16(suite) + config.CipherSuites[i] = uint16(suite) } - testConfig.ClientAuth = ClientAuthType(*testClientAuth) + config.ClientAuth = ClientAuthType(*testClientAuth) + return &config +} + +func TestRunServer(t *testing.T) { + if !*serve { + return + } + + config := GetTestConfig() - l, err := Listen("tcp", ":10443", testConfig) + const addr = ":10443" + l, err := net.Listen("tcp", addr) if err != nil { t.Fatal(err) } + log.Printf("Now listening for connections on %s", addr) for { - c, err := l.Accept() + tcpConn, err := l.Accept() if err != nil { + log.Printf("error accepting connection: %s", err) + break + } + + record := &recordingConn{ + Conn: tcpConn, + } + + conn := Server(record, config) + if err := conn.Handshake(); err != nil { log.Printf("error from TLS handshake: %s", err) break } - _, err = c.Write([]byte("hello, world\n")) + _, err = conn.Write([]byte("hello, world\n")) if err != nil { - log.Printf("error from TLS: %s", err) + log.Printf("error from Write: %s", err) continue } - st := c.(*Conn).ConnectionState() - if len(st.PeerCertificates) > 0 { - log.Print("Handling request from client ", st.PeerCertificates[0].Subject.CommonName) - } else { - log.Print("Handling request from anon client") - } + conn.Close() - c.Close() + record.WriteTo(os.Stdout) } } @@ -296,30 +433,23 @@ func loadPEMCert(in string) *x509.Certificate { // Script of interaction with gnutls implementation. // The values for this test are obtained by building and running in server mode: -// % go test -run "TestRunServer" -serve -// and then: -// % gnutls-cli --insecure --debug 100 -p 10443 localhost > /tmp/log 2>&1 -// % python parse-gnutls-cli-debug-log.py < /tmp/log +// % go test -test.run "TestRunServer" -serve +// The recorded bytes are written to stdout. var rc4ServerScript = [][]byte{ { - 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, - 0x76, 0x03, 0x02, 0x4e, 0xdd, 0xe6, 0xa5, 0xf7, - 0x00, 0x36, 0xf7, 0x83, 0xec, 0x93, 0x7c, 0xd2, - 0x4d, 0xe7, 0x7b, 0xf5, 0x4c, 0xf7, 0xe3, 0x86, - 0xe8, 0xec, 0x3b, 0xbd, 0x2c, 0x9a, 0x3f, 0x57, - 0xf0, 0xa4, 0xd4, 0x00, 0x00, 0x34, 0x00, 0x33, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, - 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, - 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, - 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, - 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, - 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, - 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, - 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00, + 0x50, 0x03, 0x01, 0x50, 0x77, 0x3d, 0xbd, 0x32, + 0x13, 0xd7, 0xea, 0x33, 0x65, 0x02, 0xb8, 0x70, + 0xb7, 0x84, 0xc4, 0x05, 0x1f, 0xa4, 0x24, 0xc4, + 0x91, 0x69, 0x04, 0x32, 0x96, 0xfe, 0x5b, 0x49, + 0x71, 0x60, 0x9a, 0x00, 0x00, 0x28, 0x00, 0x39, + 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, + 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, + 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, + 0x00, }, - { 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -418,86 +548,145 @@ var rc4ServerScript = [][]byte{ 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, }, - { 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x39, 0xe2, 0x0f, 0x49, 0xa0, - 0xe6, 0xe4, 0x3b, 0x0c, 0x5f, 0xce, 0x39, 0x97, - 0x6c, 0xb6, 0x41, 0xd9, 0xe1, 0x52, 0x8f, 0x43, - 0xb3, 0xc6, 0x4f, 0x9a, 0xe2, 0x1e, 0xb9, 0x3b, - 0xe3, 0x72, 0x17, 0x68, 0xb2, 0x0d, 0x7b, 0x71, - 0x33, 0x96, 0x5c, 0xf9, 0xfe, 0x18, 0x8f, 0x2f, - 0x2b, 0x82, 0xec, 0x03, 0xf2, 0x16, 0xa8, 0xf8, - 0x39, 0xf9, 0xbb, 0x5a, 0xd3, 0x0c, 0xc1, 0x2a, - 0x52, 0xa1, 0x90, 0x20, 0x6b, 0x24, 0xc9, 0x55, - 0xee, 0x05, 0xd8, 0xb3, 0x43, 0x58, 0xf6, 0x7f, - 0x68, 0x2d, 0xb3, 0xd1, 0x1b, 0x30, 0xaa, 0xdf, - 0xfc, 0x85, 0xf1, 0xab, 0x14, 0x51, 0x91, 0x78, - 0x29, 0x35, 0x65, 0xe0, 0x9c, 0xf6, 0xb7, 0x35, - 0x33, 0xdb, 0x28, 0x93, 0x4d, 0x86, 0xbc, 0xfe, - 0xaa, 0xd1, 0xc0, 0x2e, 0x4d, 0xec, 0xa2, 0x98, - 0xca, 0x08, 0xb2, 0x91, 0x14, 0xde, 0x97, 0x3a, - 0xc4, 0x6b, 0x49, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0x7a, 0xcb, - 0x3b, 0x0e, 0xbb, 0x7a, 0x56, 0x39, 0xaf, 0x83, - 0xae, 0xfd, 0x25, 0xfd, 0x64, 0xb4, 0x0c, 0x0c, - 0x17, 0x46, 0x54, 0x2c, 0x6a, 0x07, 0x83, 0xc6, - 0x46, 0x08, 0x0b, 0xcd, 0x15, 0x53, 0xef, 0x40, - 0x4e, 0x56, + 0x82, 0x00, 0x80, 0x2d, 0x09, 0x7c, 0x7f, 0xfc, + 0x84, 0xce, 0xb3, 0x30, 0x9b, 0xf9, 0xb7, 0xc8, + 0xc3, 0xff, 0xee, 0x6f, 0x20, 0x8a, 0xf4, 0xfb, + 0x86, 0x55, 0x1f, 0x6a, 0xb4, 0x81, 0x50, 0x3a, + 0x46, 0x1b, 0xd3, 0xca, 0x4b, 0x11, 0xff, 0xef, + 0x02, 0xbc, 0x18, 0xb8, 0x4a, 0x7d, 0x43, 0x23, + 0x96, 0x92, 0x27, 0x7c, 0xca, 0xcf, 0xe6, 0x91, + 0xe8, 0x14, 0x97, 0x68, 0xb4, 0xe5, 0xc0, 0xc9, + 0x23, 0xdd, 0x54, 0x07, 0xa6, 0x2e, 0x8c, 0x98, + 0xfc, 0xc6, 0x8c, 0x04, 0x6b, 0x1b, 0x5f, 0xd5, + 0x3d, 0x8b, 0x6c, 0x55, 0x4f, 0x7a, 0xe6, 0x6c, + 0x74, 0x2c, 0x1e, 0x34, 0xdb, 0xfb, 0x00, 0xb1, + 0x4e, 0x10, 0x21, 0x16, 0xe0, 0x3e, 0xc5, 0x64, + 0x84, 0x28, 0x2b, 0x2b, 0x29, 0x47, 0x51, 0x34, + 0x76, 0x15, 0x20, 0x71, 0x0b, 0x30, 0xa1, 0x85, + 0xd5, 0x15, 0x18, 0x14, 0x64, 0x4b, 0x40, 0x7c, + 0x4f, 0xb3, 0x7b, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xab, 0xee, + 0xf5, 0x97, 0x5f, 0xc6, 0x78, 0xf3, 0xc6, 0x83, + 0x5b, 0x55, 0x4f, 0xcb, 0x45, 0x3f, 0xfa, 0xf7, + 0x05, 0x02, 0xc2, 0x63, 0x87, 0x18, 0xb5, 0x9a, + 0x62, 0xe2, 0x3f, 0x88, 0x5a, 0x60, 0x61, 0x72, + 0xfa, 0x9c, }, - { 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0xd3, 0x72, 0xeb, 0x29, 0xb9, - 0x15, 0x29, 0xb5, 0xe5, 0xb7, 0xef, 0x5c, 0xb2, - 0x9d, 0xf6, 0xc8, 0x47, 0xd6, 0xa0, 0x84, 0xf0, - 0x8c, 0xcb, 0xe6, 0xbe, 0xbc, 0xfb, 0x38, 0x90, - 0x89, 0x60, 0xa2, 0xe8, 0xaa, 0xb3, 0x12, 0x17, - 0x03, 0x01, 0x00, 0x21, 0x67, 0x4a, 0x3d, 0x31, - 0x6c, 0x5a, 0x1c, 0xf9, 0x6e, 0xf1, 0xd8, 0x12, - 0x0e, 0xb9, 0xfd, 0xfc, 0x66, 0x91, 0xd1, 0x1d, - 0x6e, 0xe4, 0x55, 0xdd, 0x11, 0xb9, 0xb8, 0xa2, - 0x65, 0xa1, 0x95, 0x64, 0x1c, 0x15, 0x03, 0x01, - 0x00, 0x16, 0x9b, 0xa0, 0x24, 0xe3, 0xcb, 0xae, - 0xad, 0x51, 0xb3, 0x63, 0x59, 0x78, 0x49, 0x24, - 0x06, 0x6e, 0xee, 0x7a, 0xd7, 0x74, 0x53, 0x04, + 0x01, 0x00, 0x24, 0x72, 0xa4, 0xe4, 0xaa, 0xd2, + 0xc4, 0x39, 0x7e, 0x2a, 0xc1, 0x6f, 0x34, 0x42, + 0x28, 0xcb, 0x9d, 0x7a, 0x09, 0xca, 0x96, 0xad, + 0x0e, 0x11, 0x51, 0x8a, 0x06, 0xb0, 0xe9, 0xca, + 0xeb, 0xce, 0xe2, 0xd5, 0x2e, 0xc1, 0x8d, 0x17, + 0x03, 0x01, 0x00, 0x21, 0x2e, 0x61, 0x86, 0x17, + 0xdb, 0xa6, 0x30, 0xe2, 0x62, 0x06, 0x2a, 0x8b, + 0x75, 0x2c, 0x2d, 0xcf, 0xf5, 0x01, 0x11, 0x52, + 0x81, 0x38, 0xcf, 0xd5, 0xf7, 0xdc, 0x52, 0x31, + 0x1f, 0x97, 0x43, 0xc2, 0x71, 0x15, 0x03, 0x01, + 0x00, 0x16, 0xe0, 0x21, 0xfe, 0x36, 0x2e, 0x68, + 0x2c, 0xf1, 0xbe, 0x04, 0xec, 0xd4, 0xc6, 0xdd, + 0xac, 0x6f, 0x4c, 0x85, 0x32, 0x3f, 0x87, 0x1b, }, } var des3ServerScript = [][]byte{ { - 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, - 0x76, 0x03, 0x02, 0x4e, 0x84, 0xf4, 0x3c, 0xe4, - 0xb8, 0xc7, 0xa0, 0x30, 0x55, 0x2a, 0xbc, 0xb7, - 0x04, 0x6b, 0x6f, 0x87, 0x93, 0x96, 0xbd, 0x1a, - 0x7a, 0x1e, 0xce, 0xd2, 0x0d, 0xf3, 0x01, 0x03, - 0xbe, 0x7b, 0x17, 0x00, 0x00, 0x34, 0x00, 0x33, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, - 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, - 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, - 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, - 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, - 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, - 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, - 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + 0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, + 0xc1, 0x03, 0x03, 0x50, 0xae, 0x5d, 0x38, 0xec, + 0xaa, 0x2f, 0x41, 0xf9, 0xd2, 0x7b, 0xa1, 0xfd, + 0x0f, 0xff, 0x4e, 0x54, 0x0e, 0x15, 0x57, 0xaf, + 0x2c, 0x91, 0xb5, 0x35, 0x5b, 0x2e, 0xb0, 0xec, + 0x20, 0xe5, 0xd2, 0x00, 0x00, 0x50, 0xc0, 0x09, + 0xc0, 0x23, 0xc0, 0x2b, 0xc0, 0x0a, 0xc0, 0x24, + 0xc0, 0x2c, 0xc0, 0x08, 0xc0, 0x13, 0xc0, 0x27, + 0xc0, 0x2f, 0xc0, 0x14, 0xc0, 0x30, 0xc0, 0x12, + 0x00, 0x33, 0x00, 0x67, 0x00, 0x45, 0x00, 0x9e, + 0x00, 0x39, 0x00, 0x6b, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x40, 0x00, 0x44, 0x00, 0xa2, + 0x00, 0x38, 0x00, 0x6a, 0x00, 0x87, 0x00, 0x13, + 0x00, 0x66, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x41, + 0x00, 0x9c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x84, + 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x48, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, + 0x00, 0x0a, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17, + 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a, + 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x05, 0x01, + 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x03, 0x01, + 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x03, }, - { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, @@ -506,172 +695,207 @@ var des3ServerScript = [][]byte{ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, - 0x00, 0x00, 0x00, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, + 0x00, }, - { 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0xae, 0xcf, 0x4f, 0x70, 0x0e, - 0xe5, 0xe7, 0xba, 0xef, 0x0c, 0x66, 0xe9, 0xae, - 0x76, 0xf4, 0xe0, 0xbc, 0x1c, 0x22, 0x5b, 0x72, - 0xc9, 0x68, 0x63, 0x44, 0xec, 0x72, 0xc2, 0xca, - 0xac, 0xc2, 0xf5, 0x5c, 0x28, 0xa1, 0xaf, 0xd0, - 0xc2, 0xf7, 0x79, 0x71, 0x32, 0x73, 0x86, 0xea, - 0x39, 0xf6, 0x04, 0x26, 0x19, 0x84, 0x1d, 0x7d, - 0xa1, 0x21, 0xa6, 0x88, 0xbf, 0x33, 0x5a, 0x64, - 0xb0, 0xc2, 0xcc, 0x19, 0x7a, 0x8b, 0x6e, 0x94, - 0x9e, 0x2e, 0x20, 0xbe, 0xdc, 0xe9, 0x8e, 0xae, - 0x5c, 0x39, 0xc8, 0xcd, 0x0e, 0x19, 0x9a, 0xa2, - 0xfc, 0x3f, 0x61, 0x9a, 0xca, 0x58, 0x69, 0x0d, - 0xa8, 0x7b, 0xbe, 0x98, 0x8f, 0xb9, 0x9d, 0x8b, - 0x68, 0x65, 0xa9, 0x74, 0xcc, 0x8d, 0x0c, 0xb2, - 0xc4, 0x0f, 0xdc, 0x56, 0x3e, 0x44, 0x61, 0x0a, - 0x26, 0x93, 0x99, 0xef, 0x67, 0xff, 0x6e, 0x73, - 0x01, 0xa1, 0x90, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x60, 0x49, 0x36, - 0xc8, 0x38, 0x95, 0xe4, 0x5d, 0x8e, 0x80, 0x10, - 0x26, 0x9f, 0x87, 0x7d, 0xcd, 0xb9, 0x32, 0x6c, - 0xff, 0xaa, 0xe0, 0x07, 0xec, 0x33, 0xe2, 0x36, - 0x9d, 0xd5, 0x83, 0x2c, 0xf0, 0x0a, 0xa0, 0xa8, - 0x12, 0x9f, 0xca, 0x72, 0xda, 0x70, 0x7d, 0x76, - 0x80, 0x12, 0x88, 0x07, 0xaa, 0x27, 0x62, 0x33, - 0xab, 0x55, 0xad, 0x3c, 0x2b, 0x54, 0xc4, 0x1c, - 0x91, 0xfd, 0x8f, 0x9c, 0xa7, 0x8b, 0x75, 0x10, - 0xa8, 0x6e, 0xfc, 0x30, 0x52, 0x8a, 0x61, 0x02, - 0xdb, 0x9c, 0x6f, 0xc8, 0x19, 0x93, 0x5d, 0x41, - 0x1d, 0x36, 0x68, 0x0b, 0xec, 0x30, 0xae, 0xfb, - 0x90, 0xdb, 0x6d, 0x83, 0xb0, 0xf2, + 0x82, 0x00, 0x80, 0x51, 0x04, 0xf1, 0x7a, 0xbf, + 0xe8, 0xa5, 0x86, 0x09, 0xa7, 0xf3, 0xcc, 0x93, + 0x00, 0x10, 0x5b, 0xb8, 0xc1, 0x51, 0x0d, 0x5b, + 0xcd, 0xed, 0x26, 0x01, 0x69, 0x73, 0xf4, 0x05, + 0x8a, 0x6a, 0xc3, 0xb1, 0x9e, 0x84, 0x4e, 0x39, + 0xcf, 0x5e, 0x55, 0xa9, 0x70, 0x19, 0x96, 0x91, + 0xcd, 0x2c, 0x78, 0x3c, 0xa2, 0x6d, 0xb0, 0x49, + 0x86, 0xf6, 0xd1, 0x3a, 0xde, 0x00, 0x4b, 0xa6, + 0x25, 0xbf, 0x85, 0x39, 0xce, 0xb1, 0xcf, 0xbc, + 0x16, 0xc7, 0x66, 0xac, 0xf8, 0xd2, 0x3b, 0xd1, + 0xcc, 0x16, 0xac, 0x63, 0x3c, 0xbe, 0xd9, 0xb6, + 0x6a, 0xe4, 0x13, 0x8a, 0xf4, 0x56, 0x2f, 0x92, + 0x54, 0xd8, 0xf0, 0x84, 0x01, 0x32, 0x1a, 0xa9, + 0x2d, 0xaf, 0x82, 0x0e, 0x00, 0xfa, 0x07, 0x88, + 0xd9, 0x87, 0xe7, 0xdc, 0x9e, 0xe9, 0x72, 0x49, + 0xb8, 0xfa, 0x8c, 0x7b, 0x07, 0x0b, 0x03, 0x7c, + 0x10, 0x8c, 0x8a, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0xa8, 0x61, 0xa4, + 0xf4, 0x5f, 0x8a, 0x1f, 0x5c, 0x92, 0x3f, 0x8c, + 0xdb, 0xd6, 0x10, 0xcd, 0x9e, 0xe7, 0xf0, 0xc4, + 0x3c, 0xb6, 0x1c, 0x9a, 0x56, 0x73, 0x7f, 0xa6, + 0x14, 0x24, 0xcb, 0x96, 0x1f, 0xe0, 0xaf, 0xcd, + 0x3c, 0x66, 0x43, 0xb7, 0x37, 0x65, 0x34, 0x47, + 0xf8, 0x43, 0xf1, 0xcc, 0x15, 0xb8, 0xdc, 0x35, + 0xe0, 0xa4, 0x2d, 0x78, 0x94, 0xe0, 0x02, 0xf3, + 0x76, 0x46, 0xf7, 0x9b, 0x8d, 0x0d, 0x5d, 0x0b, + 0xd3, 0xdd, 0x9a, 0x9e, 0x62, 0x2e, 0xc5, 0x98, + 0x75, 0x63, 0x0c, 0xbf, 0x8e, 0x49, 0x33, 0x23, + 0x7c, 0x00, 0xcf, 0xfb, 0xcf, 0xba, 0x0f, 0x41, + 0x39, 0x89, 0xb9, 0xcc, 0x59, 0xd0, 0x2b, 0xb6, + 0xec, 0x04, 0xe2, 0xc0, 0x52, 0xc7, 0xcf, 0x71, + 0x47, 0xff, 0x70, 0x7e, 0xa9, 0xbd, 0x1c, 0xdd, + 0x17, 0xa5, 0x6c, 0xb7, 0x10, 0x4f, 0x42, 0x18, + 0x37, 0x69, 0xa9, 0xd2, 0xb3, 0x18, 0x84, 0x92, + 0xa7, 0x47, 0x21, 0xf6, 0x95, 0x63, 0x29, 0xd6, + 0xa5, 0xb6, 0xda, 0x65, 0x67, 0x69, 0xc4, 0x26, + 0xac, 0x8b, 0x08, 0x58, 0xdd, 0x3c, 0x31, 0x20, + 0xd5, 0x0c, 0x88, 0x72, 0x18, 0x16, 0x88, 0x1e, + 0x4a, 0x0f, 0xe1, 0xcf, 0x95, 0x24, }, - { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x28, 0x07, 0xf3, 0x33, 0x84, 0xb1, - 0x5d, 0x2b, 0x52, 0xa4, 0x63, 0x3c, 0x32, 0xe0, - 0x0d, 0x22, 0xf5, 0x23, 0xec, 0xf9, 0xa6, 0xec, - 0xc0, 0x12, 0x69, 0x88, 0xf6, 0x7d, 0x37, 0xcd, - 0xc2, 0x74, 0x2f, 0xef, 0xf6, 0x49, 0x15, 0xea, - 0x88, 0x3f, 0x55, 0x17, 0x03, 0x01, 0x00, 0x28, - 0xaf, 0x00, 0x84, 0xff, 0x11, 0x01, 0x6d, 0xba, - 0x39, 0x5e, 0x45, 0xe1, 0x52, 0x5e, 0xc1, 0xab, - 0xde, 0x5b, 0x16, 0xdd, 0xd6, 0x61, 0x57, 0xb8, - 0x66, 0x8b, 0x2d, 0xde, 0x51, 0x41, 0xc5, 0x09, - 0xb3, 0x6a, 0x06, 0x43, 0xb4, 0x73, 0x5c, 0xf1, - 0x15, 0x03, 0x01, 0x00, 0x18, 0xbd, 0x65, 0xb2, - 0xce, 0x77, 0x2e, 0xf9, 0x11, 0xc4, 0x80, 0x43, - 0x5a, 0x73, 0x8b, 0x73, 0xdd, 0xf0, 0x54, 0x44, - 0x7c, 0x56, 0x19, 0x54, 0xda, + 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x4b, 0xde, 0xef, 0xba, 0x3e, 0x18, 0x1c, + 0x1e, 0x5e, 0xbc, 0x87, 0xf1, 0x87, 0x8d, 0x72, + 0xe3, 0xbe, 0x0f, 0xdf, 0xfd, 0xd0, 0xb2, 0x89, + 0xf8, 0x05, 0x9a, 0x52, 0x47, 0x77, 0x9e, 0xe8, + 0xb1, 0x1d, 0x18, 0xed, 0x6a, 0x4b, 0x63, 0x1d, + 0xf1, 0x62, 0xd2, 0x65, 0x21, 0x26, 0x73, 0xd4, + 0x35, 0x5b, 0x95, 0x89, 0x12, 0x59, 0x23, 0x8c, + 0xc3, 0xfc, 0xf9, 0x4d, 0x21, 0x79, 0xa0, 0xbd, + 0xff, 0x33, 0xa2, 0x3d, 0x0b, 0x6f, 0x89, 0xc9, + 0x23, 0xe4, 0xe7, 0x9f, 0x1d, 0x98, 0xf6, 0xed, + 0x02, 0x8d, 0xac, 0x1a, 0xf9, 0xcb, 0xa5, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x28, 0x91, 0x56, 0x80, 0xe2, 0x6d, 0x51, + 0x88, 0x03, 0xf8, 0x49, 0xe6, 0x6a, 0x5a, 0xfb, + 0x2f, 0x0b, 0xb5, 0xa1, 0x0d, 0x63, 0x83, 0xae, + 0xb9, 0xbc, 0x05, 0xf0, 0x81, 0x00, 0x61, 0x83, + 0x38, 0xda, 0x14, 0xf6, 0xea, 0xd8, 0x78, 0x65, + 0xc7, 0x26, 0x17, 0x03, 0x01, 0x00, 0x18, 0x81, + 0x30, 0x8b, 0x22, 0x5a, 0xd3, 0x7f, 0xc8, 0xf2, + 0x8a, 0x6b, 0xa3, 0xba, 0x4d, 0xe7, 0x6e, 0xd2, + 0xfd, 0xbf, 0xf2, 0xc5, 0x28, 0xa0, 0x62, 0x17, + 0x03, 0x01, 0x00, 0x28, 0x17, 0x83, 0x3c, 0x78, + 0x18, 0xfa, 0x8d, 0x58, 0x5c, 0xaa, 0x05, 0x7d, + 0x67, 0x96, 0x11, 0x60, 0x11, 0xc0, 0x1e, 0x0d, + 0x6a, 0x6e, 0x5f, 0x1d, 0x98, 0x4b, 0xff, 0x82, + 0xee, 0x21, 0x06, 0x29, 0xd3, 0x8b, 0x80, 0x78, + 0x39, 0x05, 0x34, 0x9b, 0x15, 0x03, 0x01, 0x00, + 0x18, 0xa9, 0x38, 0x18, 0x4f, 0x9d, 0x84, 0x75, + 0x88, 0x53, 0xd6, 0x85, 0xc2, 0x15, 0x4b, 0xe3, + 0xe3, 0x35, 0x9a, 0x74, 0xc9, 0x3e, 0x13, 0xc1, + 0x8c, }, } var aesServerScript = [][]byte{ { - 0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00, - 0x7b, 0x03, 0x02, 0x4d, 0x08, 0x2d, 0x0b, 0xb3, - 0x57, 0x85, 0x71, 0x4b, 0xfb, 0x34, 0xab, 0x16, - 0xd4, 0x92, 0x50, 0x81, 0x16, 0x95, 0x11, 0x28, - 0x1a, 0xcb, 0xff, 0x09, 0x4d, 0x23, 0xa6, 0xfe, - 0x2e, 0xbb, 0x78, 0x00, 0x00, 0x34, 0x00, 0x33, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, - 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, - 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, - 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, - 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, - 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, - 0x00, 0x8a, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x09, - 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0xff, - 0x01, 0x00, 0x01, 0x00, + 0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, + 0xc1, 0x03, 0x03, 0x50, 0xae, 0x5c, 0xe9, 0x5e, + 0x31, 0x93, 0x82, 0xa5, 0x6f, 0x51, 0x82, 0xc8, + 0x55, 0x4f, 0x1f, 0x2e, 0x90, 0x98, 0x81, 0x13, + 0x27, 0x80, 0x68, 0xb4, 0x2d, 0xba, 0x3a, 0x76, + 0xd8, 0xd7, 0x2c, 0x00, 0x00, 0x50, 0xc0, 0x09, + 0xc0, 0x23, 0xc0, 0x2b, 0xc0, 0x0a, 0xc0, 0x24, + 0xc0, 0x2c, 0xc0, 0x08, 0xc0, 0x13, 0xc0, 0x27, + 0xc0, 0x2f, 0xc0, 0x14, 0xc0, 0x30, 0xc0, 0x12, + 0x00, 0x33, 0x00, 0x67, 0x00, 0x45, 0x00, 0x9e, + 0x00, 0x39, 0x00, 0x6b, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x40, 0x00, 0x44, 0x00, 0xa2, + 0x00, 0x38, 0x00, 0x6a, 0x00, 0x87, 0x00, 0x13, + 0x00, 0x66, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x41, + 0x00, 0x9c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x84, + 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x48, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, + 0x00, 0x0a, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17, + 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a, + 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x05, 0x01, + 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x03, 0x01, + 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x03, }, - { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, @@ -680,136 +904,144 @@ var aesServerScript = [][]byte{ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, - 0x00, 0x00, 0x00, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, + 0x00, }, - { 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x71, 0x9c, 0xe7, 0x23, 0xfc, - 0xb9, 0x19, 0x29, 0x82, 0xbf, 0xef, 0x08, 0xf7, - 0x99, 0x36, 0xc3, 0x4c, 0x6f, 0x05, 0xd2, 0x8b, - 0x62, 0x2b, 0x19, 0x9b, 0x7f, 0xc0, 0xcc, 0x48, - 0x30, 0x5f, 0xcd, 0xc3, 0x70, 0x55, 0x53, 0x73, - 0xfa, 0x79, 0x74, 0xf3, 0xa3, 0x76, 0x9f, 0xa1, - 0x7f, 0x98, 0xc2, 0xc0, 0xe3, 0xc5, 0xa0, 0x31, - 0x2f, 0xa6, 0xe8, 0x1e, 0x61, 0x46, 0xb3, 0x9b, - 0x4b, 0x16, 0xf1, 0x2d, 0xc7, 0x63, 0x7f, 0x79, - 0x22, 0x30, 0xd1, 0xf2, 0xfc, 0x77, 0x98, 0x0a, - 0x16, 0x11, 0x63, 0x71, 0x7f, 0x70, 0xef, 0x16, - 0xbb, 0x39, 0x87, 0x34, 0xac, 0x49, 0xbd, 0x07, - 0x67, 0xcb, 0x9c, 0xcc, 0xde, 0xef, 0xb1, 0xe0, - 0xdb, 0x01, 0xb5, 0x35, 0xa9, 0xb3, 0x10, 0x0c, - 0x4b, 0xee, 0xb3, 0x4e, 0xfd, 0xbe, 0x15, 0x27, - 0xf0, 0x46, 0xb2, 0x38, 0xba, 0x5f, 0xcc, 0x89, - 0xec, 0x29, 0x82, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x3c, 0xfb, - 0xa4, 0x12, 0xcb, 0x00, 0xf9, 0x57, 0x7e, 0x9b, - 0xc9, 0xdc, 0x0c, 0xba, 0x9a, 0x81, 0x62, 0xfb, - 0x26, 0x13, 0x53, 0xfe, 0xaa, 0xcc, 0x82, 0xbb, - 0xb6, 0x67, 0x7f, 0x39, 0xbe, 0x4d, 0xbb, 0xc0, - 0x6c, 0x24, 0x31, 0x83, 0xa5, 0x50, 0x3a, 0x75, - 0x32, 0x64, 0xb5, 0xdb, 0xbe, 0x0a, + 0x82, 0x00, 0x80, 0x51, 0x2e, 0xec, 0x0d, 0x86, + 0xf3, 0x9f, 0xf2, 0x77, 0x04, 0x27, 0x2b, 0x0e, + 0x9c, 0xab, 0x35, 0x84, 0x65, 0xff, 0x36, 0xef, + 0xc0, 0x08, 0xc9, 0x1d, 0x9f, 0x29, 0xae, 0x8d, + 0xc5, 0x66, 0x81, 0x31, 0x92, 0x5e, 0x3d, 0xac, + 0xaa, 0x37, 0x28, 0x2c, 0x06, 0x91, 0xa6, 0xc2, + 0xd0, 0x83, 0x34, 0x24, 0x1c, 0x88, 0xfc, 0x0a, + 0xcf, 0xbf, 0xc2, 0x94, 0xe2, 0xed, 0xa7, 0x6a, + 0xa8, 0x8d, 0x3d, 0xf7, 0x06, 0x7d, 0x69, 0xf8, + 0x0d, 0xb2, 0xf7, 0xe4, 0x45, 0xcb, 0x0a, 0x25, + 0xcb, 0xb2, 0x2e, 0x38, 0x9a, 0x84, 0x75, 0xe8, + 0xe1, 0x42, 0x39, 0xa2, 0x18, 0x0e, 0x48, 0xca, + 0x33, 0x16, 0x4e, 0xf6, 0x2f, 0xec, 0x07, 0xe7, + 0x57, 0xe1, 0x20, 0x40, 0x40, 0x6d, 0x4e, 0x29, + 0x04, 0x1a, 0x8c, 0x99, 0xfb, 0x19, 0x3c, 0xaa, + 0x75, 0x64, 0xd3, 0xa6, 0xe6, 0xed, 0x3f, 0x5a, + 0xd2, 0xc9, 0x80, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x01, 0x10, 0xe9, 0x9e, + 0x06, 0x92, 0x18, 0xbf, 0x5e, 0xaf, 0x33, 0xc1, + 0xbf, 0x0e, 0x12, 0x07, 0x48, 0x4f, 0x6b, 0x6c, + 0xf5, 0x23, 0x5e, 0x87, 0xa7, 0xd3, 0x50, 0x79, + 0x38, 0xdc, 0xe0, 0x49, 0xd3, 0x81, 0x21, 0x12, + 0xd0, 0x3d, 0x9a, 0xfb, 0x83, 0xc1, 0x8b, 0xfc, + 0x14, 0xd5, 0xd5, 0xa7, 0xa3, 0x34, 0x14, 0x71, + 0xbe, 0xea, 0x37, 0x18, 0x12, 0x7f, 0x41, 0xfb, + 0xc5, 0x51, 0x17, 0x9d, 0x96, 0x58, 0x14, 0xfb, + 0x4f, 0xd7, 0xd3, 0x15, 0x0f, 0xec, 0x5a, 0x0d, + 0x35, 0xbb, 0x3c, 0x81, 0x5b, 0x3f, 0xdf, 0x52, + 0xa4, 0x4c, 0xcd, 0x13, 0xe1, 0x10, 0x37, 0x34, + 0xbf, 0xb4, 0x80, 0x1e, 0x8d, 0xe2, 0xc3, 0x7a, + 0x0f, 0x7b, 0x7d, 0x23, 0xeb, 0xd0, 0x99, 0x69, + 0xad, 0x0a, 0x2d, 0xb3, 0x6c, 0xd6, 0x80, 0x11, + 0x7f, 0x6c, 0xed, 0x1b, 0xcd, 0x08, 0x22, 0x56, + 0x90, 0x0e, 0xa4, 0xc3, 0x29, 0x33, 0x96, 0x30, + 0x34, 0x94, 0xa1, 0xeb, 0x9c, 0x1b, 0x5a, 0xd1, + 0x03, 0x61, 0xf9, 0xdd, 0xf3, 0x64, 0x8a, 0xfd, + 0x5f, 0x44, 0xdb, 0x2e, 0xa7, 0xfd, 0xe1, 0x1a, + 0x66, 0xc5, 0x01, 0x9c, 0xc7, 0xd1, 0xc4, 0xd3, + 0xea, 0x14, 0x3c, 0xed, 0x74, 0xbb, 0x1b, 0x97, + 0x8f, 0xf1, 0x29, 0x39, 0x33, 0x92, 0x93, 0x4e, + 0xf5, 0x87, 0x91, 0x61, 0x65, 0x8d, 0x27, 0x8d, + 0x76, 0xc1, 0xfa, 0x6a, 0x99, 0x80, 0xb1, 0x9b, + 0x29, 0x53, 0xce, 0x3e, 0xb6, 0x9a, 0xce, 0x3c, + 0x19, 0x5e, 0x48, 0x83, 0xaa, 0xa7, 0x66, 0x98, + 0x59, 0xf4, 0xbb, 0xf2, 0xbc, 0xd9, 0xc5, 0x9a, + 0xc8, 0x2c, 0x63, 0x58, 0xd5, 0xd4, 0xbc, 0x03, + 0xa9, 0x06, 0xa9, 0x80, 0x0d, 0xb3, 0x46, 0x2d, + 0xe3, 0xc6, 0xaf, 0x1a, 0x39, 0x18, 0x7e, 0x1e, + 0x83, 0x80, 0x46, 0x11, 0xd2, 0x13, 0x9f, 0xda, + 0xfc, 0x2d, 0x42, 0xaa, 0x5a, 0x1d, 0x4c, 0x31, + 0xe5, 0x58, 0x78, 0x5e, 0xe2, 0x04, 0xd6, 0x23, + 0x7f, 0x3f, 0x06, 0xc0, 0x54, 0xf8, }, - { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x30, 0x43, 0x24, 0x42, 0x55, 0x08, - 0xe4, 0xc2, 0x15, 0xc9, 0xdb, 0x71, 0x69, 0xee, - 0x09, 0xc5, 0x1c, 0xfd, 0x46, 0x10, 0xa0, 0x68, - 0x21, 0xf2, 0x48, 0xac, 0x6c, 0xc0, 0x2b, 0x62, - 0x07, 0x8f, 0x48, 0x33, 0x0a, 0x6b, 0x62, 0x28, - 0x2e, 0x2c, 0xad, 0xcb, 0x34, 0x85, 0xca, 0x2e, - 0xcd, 0x84, 0xf0, + 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x4b, 0xfb, 0xef, 0xba, 0xed, 0xc5, 0x36, + 0xc8, 0x5a, 0x41, 0x3f, 0x05, 0xfa, 0xfe, 0x48, + 0xc3, 0x91, 0x12, 0x8b, 0xe8, 0x32, 0x6a, 0x9f, + 0xdc, 0x97, 0xe2, 0x77, 0xb9, 0x96, 0x2d, 0xd4, + 0xe5, 0xbd, 0xa1, 0xfd, 0x94, 0xbb, 0x74, 0x63, + 0xb1, 0x0c, 0x38, 0xbc, 0x6f, 0x69, 0xaf, 0xa3, + 0x46, 0x9c, 0x96, 0x41, 0xde, 0x59, 0x23, 0xff, + 0x15, 0x6b, 0x3a, 0xef, 0x91, 0x6d, 0x92, 0x44, + 0xdc, 0x72, 0x1f, 0x40, 0x3d, 0xb5, 0x34, 0x8f, + 0x2a, 0xac, 0x21, 0x69, 0x05, 0x6f, 0xb2, 0x60, + 0x32, 0x5d, 0x3d, 0x97, 0xb4, 0x24, 0x99, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x30, 0x68, 0x27, 0x97, 0xca, 0x63, 0x09, + 0x22, 0xed, 0x0e, 0x61, 0x7c, 0x76, 0x31, 0x9c, + 0xbe, 0x27, 0xc9, 0xe6, 0x09, 0xc3, 0xc3, 0xc2, + 0xf4, 0xa2, 0x32, 0xba, 0x7c, 0xf2, 0x0f, 0xb8, + 0x3d, 0xcb, 0xe2, 0x4c, 0xc0, 0x7d, 0x8e, 0x5b, + 0x5a, 0xed, 0x05, 0x5c, 0x15, 0x96, 0x69, 0xc2, + 0x6f, 0x5f, 0x17, 0x03, 0x01, 0x00, 0x20, 0x5a, + 0xfe, 0x0b, 0xe1, 0x6f, 0xa8, 0x54, 0x19, 0x78, + 0xca, 0xba, 0x2e, 0x1e, 0x2e, 0xe1, 0x5d, 0x17, + 0xe5, 0x97, 0x05, 0x2c, 0x08, 0x0c, 0xff, 0xa8, + 0x59, 0xa9, 0xde, 0x5e, 0x21, 0x34, 0x04, 0x17, + 0x03, 0x01, 0x00, 0x30, 0x86, 0xb1, 0x3f, 0x88, + 0x43, 0xf0, 0x07, 0xee, 0xa8, 0xf4, 0xbc, 0xe7, + 0x5f, 0xc6, 0x8c, 0x86, 0x4c, 0xca, 0x70, 0x88, + 0xcc, 0x6a, 0xb4, 0x3d, 0x40, 0xe8, 0x54, 0x89, + 0x19, 0x43, 0x1f, 0x76, 0xe2, 0xac, 0xb2, 0x5b, + 0x92, 0xf8, 0x57, 0x39, 0x2a, 0xc3, 0x6d, 0x13, + 0x45, 0xfa, 0x36, 0x9e, 0x15, 0x03, 0x01, 0x00, + 0x20, 0x6d, 0xed, 0x7b, 0x59, 0x28, 0x2a, 0x27, + 0x04, 0x15, 0x07, 0x4e, 0xeb, 0x13, 0x00, 0xe3, + 0x3a, 0x3f, 0xf8, 0xaa, 0x2b, 0x3b, 0x1a, 0x8c, + 0x12, 0xd6, 0x4c, 0xec, 0x2a, 0xaf, 0x33, 0x60, + 0xaf, }, } var sslv3ServerScript = [][]byte{ { - 0x16, 0x03, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, - 0x3d, 0x03, 0x00, 0x4e, 0x70, 0xe2, 0x18, 0x86, - 0xd6, 0xc6, 0x6f, 0xf3, 0xc8, 0xf4, 0x02, 0xd6, - 0x4d, 0xee, 0x17, 0x32, 0x4b, 0xd2, 0x78, 0xd8, - 0xa1, 0x03, 0x5d, 0x68, 0x82, 0x89, 0xbe, 0xfd, - 0x12, 0xb9, 0x06, 0x00, 0x00, 0x16, 0x00, 0x33, - 0x00, 0x39, 0x00, 0x16, 0x00, 0x32, 0x00, 0x38, - 0x00, 0x13, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, - 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, + 0x16, 0x03, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, + 0x50, 0x03, 0x00, 0x50, 0x77, 0x3d, 0x42, 0xae, + 0x84, 0xbd, 0xc5, 0x07, 0xa5, 0xc4, 0xd6, 0x16, + 0x4e, 0xd5, 0xc5, 0xfa, 0x02, 0x7a, 0x0f, 0x1d, + 0xc1, 0xe1, 0xaa, 0xe3, 0x3b, 0x4b, 0x6f, 0x11, + 0xfa, 0x1a, 0xa4, 0x00, 0x00, 0x28, 0x00, 0x39, + 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, + 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, + 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, + 0x00, }, - { 0x16, 0x03, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -908,74 +1140,71 @@ var sslv3ServerScript = [][]byte{ 0xbd, 0xd9, 0x16, 0x03, 0x00, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, }, - { 0x16, 0x03, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00, - 0x80, 0x74, 0x0e, 0x3a, 0xcf, 0xba, 0x9f, 0x1a, - 0x9b, 0xb2, 0xa4, 0xc7, 0x5d, 0xf3, 0x0c, 0x80, - 0x06, 0x80, 0xf3, 0x57, 0xb2, 0xd9, 0x36, 0x24, - 0x6a, 0x06, 0x13, 0x40, 0xf9, 0x7c, 0xb9, 0x3e, - 0x4b, 0x68, 0x4f, 0x21, 0x90, 0x2d, 0xbd, 0xca, - 0xd4, 0x83, 0xf0, 0x7a, 0xeb, 0x7a, 0x74, 0x1b, - 0xcd, 0xfe, 0x69, 0xef, 0xc0, 0x86, 0xa0, 0x24, - 0x31, 0x65, 0x40, 0xd2, 0xdd, 0x6f, 0xb9, 0xd7, - 0x8d, 0xc1, 0x69, 0x60, 0x44, 0x7a, 0x75, 0xfb, - 0x42, 0x6a, 0x0f, 0x66, 0x45, 0x10, 0x73, 0xee, - 0x87, 0x28, 0x37, 0x83, 0x86, 0xd8, 0x5a, 0xc8, - 0x60, 0x87, 0xda, 0x33, 0x87, 0xaf, 0x34, 0x8b, - 0xf5, 0x61, 0x63, 0x7a, 0x5c, 0x60, 0x26, 0xb9, - 0xdb, 0xa1, 0xb7, 0xe3, 0x60, 0x38, 0x94, 0x5c, - 0x83, 0x23, 0xd6, 0x8d, 0xc2, 0x14, 0x4a, 0x0f, - 0x0e, 0x4f, 0xf9, 0x4e, 0x7b, 0x15, 0xcd, 0x18, - 0x04, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, - 0x03, 0x00, 0x00, 0x3c, 0xbd, 0xbc, 0xec, 0xdc, - 0x79, 0xb1, 0xae, 0x16, 0xc9, 0x26, 0x9a, 0xc0, - 0xc0, 0x2c, 0x33, 0x36, 0x13, 0x91, 0x58, 0x5d, - 0x7d, 0xee, 0x4e, 0xd8, 0x7e, 0xac, 0x88, 0x87, - 0x0a, 0x75, 0x66, 0xb1, 0x44, 0x79, 0x2f, 0x42, - 0xe8, 0x92, 0x74, 0x4c, 0xab, 0x36, 0xc8, 0x17, - 0x5f, 0x02, 0x8a, 0x20, 0x53, 0xe9, 0x1d, 0xb4, - 0xfe, 0x5c, 0x2b, 0xd9, 0x0a, 0xfb, 0xc6, 0x63, + 0x80, 0x4a, 0x8d, 0xc4, 0x38, 0x7a, 0x9c, 0xd6, + 0xe8, 0x72, 0x9e, 0xa3, 0xdf, 0x37, 0xb4, 0x6c, + 0x58, 0x33, 0x59, 0xd9, 0xc9, 0x4b, 0x50, 0x33, + 0x6c, 0xed, 0x73, 0x38, 0x2a, 0x46, 0x55, 0x31, + 0xa9, 0x8e, 0x8e, 0xfc, 0x0b, 0x5d, 0x5f, 0x3c, + 0x88, 0x28, 0x3f, 0x60, 0x51, 0x13, 0xf1, 0x59, + 0x0c, 0xa3, 0x5e, 0xe0, 0xa3, 0x35, 0x06, 0xb1, + 0x71, 0x59, 0x24, 0x4e, 0xed, 0x07, 0x15, 0x88, + 0x50, 0xef, 0xc2, 0xb2, 0x2a, 0x52, 0x30, 0x6a, + 0x7c, 0xbe, 0x2f, 0xc6, 0x8f, 0xa8, 0x83, 0xc5, + 0x80, 0x14, 0x62, 0x74, 0x7f, 0x96, 0x9f, 0x41, + 0x32, 0x74, 0xdd, 0x76, 0x2d, 0x7b, 0xeb, 0x7b, + 0xea, 0xd0, 0x4f, 0x0c, 0xcf, 0x9a, 0x9c, 0xc5, + 0x7a, 0xe4, 0xbc, 0xf8, 0xa6, 0xe1, 0x09, 0x8e, + 0x7c, 0x53, 0x3a, 0xe3, 0x30, 0x8f, 0x76, 0xee, + 0x58, 0xbb, 0xfd, 0x0b, 0x06, 0xb8, 0xdf, 0xb7, + 0x31, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, + 0x03, 0x00, 0x00, 0x3c, 0x13, 0x91, 0xc6, 0x4a, + 0x0c, 0x59, 0x25, 0xce, 0x54, 0xc0, 0x1d, 0xb9, + 0x2a, 0xff, 0x4d, 0xca, 0x26, 0x0c, 0x8c, 0x04, + 0x98, 0x7c, 0x7c, 0x38, 0xa3, 0xf5, 0xf9, 0x36, + 0x1c, 0x04, 0x32, 0x47, 0x2d, 0x48, 0x0e, 0x96, + 0xe8, 0x2b, 0x5e, 0x5a, 0xc6, 0x0a, 0x48, 0x41, + 0x34, 0x5e, 0x62, 0xd5, 0x68, 0x4e, 0x44, 0x1d, + 0xb2, 0xa1, 0x11, 0xad, 0x6e, 0x14, 0x85, 0x61, }, - { 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x00, 0x00, 0x3c, 0xaa, 0xa1, 0x98, 0xc4, 0x6b, - 0x5a, 0x16, 0x3f, 0x5f, 0xa4, 0x96, 0x3e, 0x78, - 0xe4, 0x6f, 0x49, 0x05, 0x47, 0xc4, 0x05, 0x60, - 0xeb, 0x0b, 0x45, 0xe3, 0xbc, 0x50, 0x11, 0x24, - 0x5f, 0x01, 0xd7, 0xb8, 0x8f, 0x60, 0x63, 0x66, - 0xbd, 0x3e, 0xd9, 0xa8, 0x80, 0x43, 0x9f, 0x0b, - 0x51, 0x61, 0xed, 0x13, 0xc6, 0x21, 0xd0, 0xfe, - 0xbc, 0x17, 0x3c, 0x36, 0xb0, 0x82, 0x7f, 0x17, - 0x03, 0x00, 0x00, 0x21, 0xee, 0x44, 0xf3, 0xa6, - 0x88, 0x9d, 0x78, 0x44, 0xde, 0xdf, 0xeb, 0xc5, - 0xad, 0xc4, 0xcc, 0x56, 0x5c, 0x54, 0x96, 0x52, - 0x3f, 0xd9, 0x40, 0x6e, 0x79, 0xd8, 0x58, 0x78, - 0x4f, 0x5a, 0xe9, 0x06, 0xef, 0x15, 0x03, 0x00, - 0x00, 0x16, 0xd3, 0xc2, 0x52, 0x99, 0x2a, 0x84, - 0xc4, 0x52, 0x5f, 0x3b, 0x19, 0xe7, 0xfc, 0x65, - 0xaf, 0xd3, 0xb7, 0xa3, 0xcc, 0x4a, 0x1d, 0x2e, + 0x00, 0x00, 0x3c, 0x88, 0xae, 0xa9, 0xd4, 0xa8, + 0x10, 0x8d, 0x65, 0xa6, 0x3e, 0x1e, 0xed, 0xd2, + 0xfc, 0xc4, 0x7c, 0xa8, 0x94, 0x4f, 0x11, 0xaf, + 0xa6, 0x87, 0x09, 0x37, 0x54, 0xf7, 0x69, 0xd1, + 0xb5, 0x25, 0x6b, 0xb5, 0xed, 0xcb, 0x25, 0x39, + 0x73, 0xeb, 0x53, 0x6c, 0xc7, 0xb4, 0x29, 0x8f, + 0xd6, 0x49, 0xd1, 0x95, 0x59, 0x80, 0x9a, 0x67, + 0x5c, 0xb2, 0xe0, 0xbd, 0x1e, 0xff, 0xaa, 0x17, + 0x03, 0x00, 0x00, 0x21, 0x65, 0x7b, 0x99, 0x09, + 0x02, 0xc3, 0x9d, 0x54, 0xd6, 0xe7, 0x32, 0x62, + 0xab, 0xc1, 0x09, 0x91, 0x30, 0x0a, 0xc9, 0xfa, + 0x70, 0xec, 0x06, 0x7b, 0xa3, 0xe1, 0x5f, 0xb4, + 0x63, 0xe6, 0x5c, 0xba, 0x1f, 0x15, 0x03, 0x00, + 0x00, 0x16, 0x40, 0x70, 0xbe, 0xe6, 0xa6, 0xee, + 0x8f, 0xd0, 0x87, 0xa0, 0x43, 0xa1, 0x92, 0xd7, + 0xd0, 0x1a, 0x0c, 0x20, 0x7c, 0xbf, 0xa2, 0xb5, }, } var selectCertificateBySNIScript = [][]byte{ { - 0x16, 0x03, 0x01, 0x00, 0x6e, 0x01, 0x00, 0x00, - 0x6a, 0x03, 0x01, 0x4f, 0x85, 0xc4, 0xc2, 0xb9, - 0x39, 0x80, 0x91, 0x66, 0x65, 0x56, 0x8e, 0xdd, - 0x48, 0xe9, 0xca, 0x34, 0x02, 0x3c, 0xaf, 0x0d, - 0x73, 0xb5, 0x2a, 0x05, 0x6e, 0xbd, 0x5e, 0x8f, - 0x38, 0xf9, 0xe5, 0x00, 0x00, 0x28, 0x00, 0x39, + 0x16, 0x03, 0x01, 0x00, 0x6a, 0x01, 0x00, 0x00, + 0x66, 0x03, 0x01, 0x50, 0x77, 0x3d, 0xfe, 0xfb, + 0x8d, 0xc2, 0x68, 0xeb, 0xf9, 0xfa, 0x54, 0x97, + 0x86, 0x45, 0xa2, 0xa3, 0xed, 0xb1, 0x91, 0xb8, + 0x28, 0xc0, 0x47, 0xaf, 0xfb, 0xcd, 0xdc, 0x0e, + 0xb3, 0xea, 0xa5, 0x00, 0x00, 0x28, 0x00, 0x39, 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, - 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, - 0x23, 0x00, 0x00, + 0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, }, { 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, @@ -1053,632 +1282,684 @@ var selectCertificateBySNIScript = [][]byte{ }, { 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x70, 0x1d, 0x34, 0x75, 0xa2, - 0xe7, 0xe3, 0x2f, 0x3d, 0xc1, 0x1d, 0xca, 0x0b, - 0xe3, 0x64, 0xb9, 0x1a, 0x00, 0x69, 0xc4, 0x14, - 0x05, 0x07, 0x7e, 0xc3, 0x51, 0x43, 0x52, 0x66, - 0xe3, 0xbd, 0xff, 0x1b, 0x1a, 0x6a, 0x84, 0xf2, - 0x07, 0x24, 0xd7, 0x12, 0xa8, 0x58, 0xcf, 0x8a, - 0x50, 0x30, 0xe8, 0xc8, 0xb2, 0xf9, 0x58, 0x1c, - 0x56, 0x53, 0x76, 0x21, 0xe0, 0x03, 0x7f, 0x77, - 0xa7, 0xf1, 0xad, 0x67, 0xd4, 0xe2, 0x8f, 0xa0, - 0x58, 0x6c, 0xe0, 0x28, 0x59, 0xf3, 0xd1, 0x53, - 0x2b, 0x21, 0xbd, 0xa3, 0x84, 0x31, 0x73, 0xbf, - 0x84, 0x0f, 0x83, 0xf4, 0xc4, 0xd0, 0xe5, 0x3c, - 0x2d, 0x3e, 0xf2, 0x8a, 0x1e, 0xe7, 0xe9, 0x1f, - 0x12, 0x13, 0xad, 0x29, 0xd6, 0x0c, 0xc7, 0xc6, - 0x05, 0x53, 0x7d, 0x5e, 0xc6, 0x92, 0x72, 0xba, - 0xd2, 0x93, 0x8f, 0x53, 0x84, 0x87, 0x44, 0x05, - 0x9f, 0x5d, 0x66, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xfc, 0x71, - 0xaa, 0xa8, 0x37, 0xa8, 0xbd, 0x63, 0xb7, 0xbc, - 0x95, 0xef, 0x0c, 0xcf, 0x39, 0x31, 0x93, 0xe6, - 0x86, 0xbd, 0x3f, 0x56, 0x9d, 0xf0, 0xb2, 0xb5, - 0xd1, 0xa7, 0xc6, 0x45, 0x89, 0x18, 0xfb, 0xa0, - 0x7f, 0xc1, + 0x82, 0x00, 0x80, 0x69, 0xc3, 0xd4, 0x0e, 0xcc, + 0xdc, 0xbc, 0x5e, 0xc2, 0x64, 0xa6, 0xde, 0x3c, + 0x0c, 0x7e, 0x0c, 0x6b, 0x80, 0x0f, 0xd4, 0x8f, + 0x02, 0x4b, 0xb2, 0xba, 0x8d, 0x01, 0xeb, 0x6b, + 0xa1, 0x2e, 0x79, 0x37, 0xba, 0xae, 0x24, 0xc2, + 0x26, 0x72, 0x51, 0xe1, 0x82, 0x8e, 0x51, 0x41, + 0x1c, 0x54, 0xa4, 0x26, 0xbe, 0x13, 0xcd, 0x1b, + 0xc6, 0xed, 0x3d, 0x1f, 0xfd, 0x72, 0x80, 0x90, + 0xdb, 0xbf, 0xd6, 0x39, 0x94, 0x5f, 0x48, 0xfb, + 0x25, 0x5a, 0xc9, 0x60, 0x9b, 0xd7, 0xc6, 0x20, + 0xa8, 0x66, 0x64, 0x13, 0xf3, 0x65, 0xc8, 0xb1, + 0xd5, 0x33, 0x21, 0x0e, 0x73, 0x41, 0xc0, 0x18, + 0x1a, 0x37, 0xfe, 0xcf, 0x28, 0x2a, 0xcd, 0xe4, + 0x0b, 0xac, 0xdd, 0x25, 0x5e, 0xcb, 0x17, 0x51, + 0x69, 0xd5, 0x8c, 0xf4, 0xb6, 0x21, 0x98, 0xef, + 0x20, 0xdb, 0x14, 0x67, 0xf3, 0x7c, 0x95, 0x6a, + 0x48, 0x2a, 0x6a, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0x36, 0x1b, + 0x09, 0xe5, 0xb9, 0xb9, 0x4d, 0x7d, 0xae, 0x87, + 0xb6, 0x0f, 0xaf, 0xec, 0x22, 0xba, 0x0d, 0xa5, + 0x96, 0x5e, 0x64, 0x65, 0xe7, 0xfb, 0xe3, 0xf3, + 0x6b, 0x72, 0xa8, 0xdb, 0xed, 0xd8, 0x69, 0x9c, + 0x08, 0xd8, }, { 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0xb8, 0x6d, 0x9a, 0x90, 0x3c, - 0x45, 0xe0, 0xff, 0x63, 0xba, 0xab, 0x3d, 0x7a, - 0xa6, 0x49, 0x5a, 0x13, 0xdc, 0x0e, 0xa3, 0xba, - 0x7f, 0x04, 0x19, 0x45, 0xfd, 0xfb, 0xbd, 0x00, - 0xa3, 0xa7, 0x78, 0x81, 0x38, 0x9f, 0x10, 0x17, - 0x03, 0x01, 0x00, 0x21, 0x43, 0xc3, 0x91, 0xb7, - 0xbf, 0x50, 0x0b, 0x04, 0xb4, 0x5d, 0xc6, 0x20, - 0x64, 0xb8, 0x01, 0x09, 0x25, 0x2c, 0x03, 0x30, - 0xc0, 0x77, 0xc9, 0x5e, 0xe6, 0xe0, 0x99, 0xdc, - 0xcd, 0x75, 0x9d, 0x51, 0x82, 0x15, 0x03, 0x01, - 0x00, 0x16, 0x2d, 0x7a, 0x89, 0x7b, 0x36, 0x85, - 0x2a, 0x93, 0xcb, 0x83, 0xa7, 0x2f, 0x9e, 0x91, - 0xfc, 0xad, 0x57, 0xca, 0xf5, 0xbc, 0x13, 0x2f, + 0x01, 0x00, 0x24, 0x60, 0xf7, 0x09, 0x5f, 0xd1, + 0xcb, 0xc9, 0xe1, 0x22, 0xb5, 0x2a, 0xcc, 0xde, + 0x7c, 0xa7, 0xb8, 0x85, 0x00, 0xbc, 0xfd, 0x85, + 0xe1, 0x91, 0x36, 0xbb, 0x07, 0x42, 0xad, 0x3d, + 0x29, 0x62, 0x69, 0xc1, 0x45, 0x92, 0x6f, 0x17, + 0x03, 0x01, 0x00, 0x21, 0x0d, 0xf9, 0xd5, 0x87, + 0xb9, 0x57, 0x3c, 0x50, 0x19, 0xe4, 0x3a, 0x50, + 0x45, 0xcc, 0x86, 0x89, 0xd4, 0x32, 0x79, 0x45, + 0x7c, 0x9f, 0x96, 0xd4, 0x54, 0x56, 0x0c, 0x63, + 0x72, 0x81, 0xc3, 0xd3, 0xe3, 0x15, 0x03, 0x01, + 0x00, 0x16, 0x84, 0xec, 0x2e, 0xf6, 0xaf, 0x4f, + 0xee, 0x48, 0x0f, 0xbe, 0xcd, 0x82, 0x5c, 0x56, + 0x16, 0xe4, 0xfb, 0x89, 0xc5, 0x57, 0x3e, 0x91, }, } -var clientauthTests = []clientauthTest{ - // Server doesn't asks for cert - // go test -run "TestRunServer" -serve -clientauth 0 - // gnutls-cli --insecure --debug 100 -p 10443 localhost 2>&1 | - // python parse-gnutls-cli-debug-log.py - {"NoClientCert", NoClientCert, nil, - [][]byte{{ - 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, - 0x76, 0x03, 0x02, 0x4e, 0xe0, 0x92, 0x5d, 0xcd, - 0xfe, 0x0c, 0x69, 0xd4, 0x7d, 0x8e, 0xa6, 0x88, - 0xde, 0x72, 0x04, 0x29, 0x6a, 0x4a, 0x16, 0x23, - 0xd7, 0x8f, 0xbc, 0xfa, 0x80, 0x73, 0x2e, 0x12, - 0xb7, 0x0b, 0x39, 0x00, 0x00, 0x34, 0x00, 0x33, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, - 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, - 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, - 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, - 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, - 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, - 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, - 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, - }, +var issueSessionTicketTest = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x5a, 0x01, 0x00, 0x00, + 0x56, 0x03, 0x01, 0x50, 0x77, 0x3e, 0x49, 0x7a, + 0xb7, 0x86, 0x5c, 0x27, 0xd2, 0x97, 0x61, 0xe3, + 0x49, 0x41, 0x48, 0xe7, 0x0e, 0xaa, 0x7e, 0x4d, + 0xb8, 0xdc, 0x01, 0x97, 0xfb, 0xab, 0x53, 0xb2, + 0x5e, 0x36, 0xf6, 0x00, 0x00, 0x28, 0x00, 0x39, + 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, + 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, + 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, + 0x00, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, + 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x80, 0x68, 0x10, 0xdc, 0x80, 0xbc, + 0xb3, 0x5a, 0x10, 0x75, 0x89, 0xcc, 0xe5, 0x9f, + 0xbf, 0xe2, 0xce, 0xa4, 0x9f, 0x7f, 0x60, 0xc4, + 0xfe, 0x5c, 0xb5, 0x02, 0x2d, 0xa5, 0xa9, 0x1e, + 0x2c, 0x10, 0x79, 0x15, 0x0f, 0xed, 0x96, 0xb3, + 0xa8, 0x5e, 0x21, 0xbc, 0x5b, 0xdc, 0x58, 0x04, + 0x7d, 0x37, 0xdb, 0xa0, 0x31, 0xe8, 0x4f, 0x04, + 0xbc, 0x46, 0x7c, 0xdb, 0x2e, 0x93, 0x07, 0xaf, + 0xa6, 0x36, 0xd3, 0x39, 0x8d, 0x1d, 0x95, 0xa8, + 0x50, 0x4b, 0xc4, 0x2b, 0xde, 0xd7, 0x04, 0x6d, + 0x77, 0x6c, 0x4d, 0x70, 0x51, 0x88, 0x16, 0x31, + 0x40, 0xb5, 0xba, 0x90, 0x47, 0x64, 0x0c, 0x87, + 0xa5, 0x19, 0xf9, 0x89, 0x24, 0x3c, 0x5e, 0x4b, + 0xaa, 0xe0, 0x60, 0x47, 0x0f, 0x2e, 0xcc, 0xc2, + 0xd5, 0x21, 0xed, 0x72, 0xd0, 0xa9, 0xdd, 0x2a, + 0x2b, 0xef, 0x08, 0x3a, 0x65, 0xea, 0x8b, 0x52, + 0x77, 0x2d, 0xcc, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xe2, 0x95, + 0x62, 0x3c, 0x18, 0xe5, 0xc7, 0x2c, 0xda, 0x16, + 0x9b, 0x28, 0x0d, 0xf7, 0x88, 0x7b, 0x5d, 0x33, + 0x55, 0x3b, 0x01, 0x73, 0xf2, 0xc6, 0x4e, 0x96, + 0x01, 0x01, 0x83, 0x65, 0xd4, 0xef, 0x12, 0x13, + 0x1d, 0x42, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x4b, 0xd1, 0xef, 0xba, 0xfb, 0x41, 0x92, + 0x6d, 0x37, 0x5f, 0xf8, 0x7d, 0x90, 0x0f, 0x01, + 0xf8, 0x8c, 0xee, 0xbc, 0xd9, 0x0c, 0x97, 0x7e, + 0x23, 0x46, 0xe2, 0x6b, 0x52, 0xc6, 0xc6, 0x97, + 0x1d, 0xab, 0xde, 0xa0, 0x86, 0x94, 0xc8, 0x2e, + 0x8b, 0x2e, 0x42, 0x5f, 0xc2, 0x70, 0x35, 0xc9, + 0xee, 0x37, 0xeb, 0x70, 0xaa, 0x59, 0x23, 0x6c, + 0xc8, 0xc1, 0x84, 0x89, 0x39, 0x87, 0x73, 0x0a, + 0x7e, 0xba, 0xca, 0xed, 0x63, 0xba, 0x4e, 0x4f, + 0xf3, 0x31, 0x4b, 0xf0, 0xee, 0x91, 0xa5, 0xb4, + 0x62, 0x01, 0x9e, 0xbd, 0xbc, 0xb3, 0x35, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x24, 0x3f, 0x66, 0xe4, 0x98, 0xc1, 0x3f, + 0xc6, 0x2c, 0x81, 0xfb, 0xa9, 0x9f, 0x27, 0xe9, + 0x63, 0x20, 0x1e, 0x0e, 0x4f, 0xfc, 0x5d, 0x12, + 0xee, 0x77, 0x73, 0xc6, 0x96, 0x51, 0xf2, 0x26, + 0x35, 0x3f, 0xce, 0x6a, 0xa9, 0xfd, 0x17, 0x03, + 0x01, 0x00, 0x21, 0x8d, 0xd5, 0x67, 0x60, 0x5d, + 0xa7, 0x93, 0xcc, 0x39, 0x78, 0x59, 0xab, 0xdb, + 0x10, 0x96, 0xf2, 0xad, 0xa2, 0x85, 0xe2, 0x93, + 0x43, 0x43, 0xcf, 0x82, 0xbd, 0x1f, 0xdc, 0x7a, + 0x72, 0xd6, 0x83, 0x3b, 0x15, 0x03, 0x01, 0x00, + 0x16, 0x89, 0x55, 0xf6, 0x42, 0x71, 0xa9, 0xe9, + 0x05, 0x68, 0xe8, 0xce, 0x0d, 0x21, 0xe9, 0xec, + 0xf2, 0x27, 0x67, 0xa7, 0x94, 0xf8, 0x34, + }, +} +var serverResumeTest = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0xc2, 0x01, 0x00, 0x00, + 0xbe, 0x03, 0x01, 0x50, 0x77, 0x3e, 0x4f, 0x1f, + 0x6f, 0xa5, 0x81, 0xeb, 0xb8, 0x80, 0x55, 0xa4, + 0x76, 0xc2, 0x7f, 0x27, 0xf2, 0xe7, 0xc9, 0x7a, + 0x01, 0x3c, 0xd8, 0xc1, 0xde, 0x99, 0x1f, 0x7c, + 0xab, 0x35, 0x98, 0x00, 0x00, 0x28, 0x00, 0x39, + 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, + 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, + 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, + 0x00, 0x00, 0x6c, 0x00, 0x23, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x4b, 0xd1, 0xef, 0xba, 0xfb, 0x41, 0x92, + 0x6d, 0x37, 0x5f, 0xf8, 0x7d, 0x90, 0x0f, 0x01, + 0xf8, 0x8c, 0xee, 0xbc, 0xd9, 0x0c, 0x97, 0x7e, + 0x23, 0x46, 0xe2, 0x6b, 0x52, 0xc6, 0xc6, 0x97, + 0x1d, 0xab, 0xde, 0xa0, 0x86, 0x94, 0xc8, 0x2e, + 0x8b, 0x2e, 0x42, 0x5f, 0xc2, 0x70, 0x35, 0xc9, + 0xee, 0x37, 0xeb, 0x70, 0xaa, 0x59, 0x23, 0x6c, + 0xc8, 0xc1, 0x84, 0x89, 0x39, 0x87, 0x73, 0x0a, + 0x7e, 0xba, 0xca, 0xed, 0x63, 0xba, 0x4e, 0x4f, + 0xf3, 0x31, 0x4b, 0xf0, 0xee, 0x91, 0xa5, 0xb4, + 0x62, 0x01, 0x9e, 0xbd, 0xbc, 0xb3, 0x35, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x24, 0xc5, 0x35, 0x74, 0x19, 0x05, 0xc5, + 0x85, 0x68, 0x48, 0xe8, 0xb5, 0xe9, 0xaf, 0x78, + 0xbd, 0x35, 0x6f, 0xe9, 0x79, 0x34, 0x1b, 0xf0, + 0x35, 0xd4, 0x4e, 0x55, 0x2e, 0x3c, 0xd5, 0xaf, + 0xfc, 0xba, 0xf5, 0x1e, 0x83, 0x32, + }, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0x27, 0x28, 0x88, 0xe1, 0x7e, + 0x0d, 0x9c, 0x12, 0x50, 0xf6, 0x7a, 0xa7, 0x32, + 0x21, 0x68, 0xba, 0xd8, 0x0a, 0xdc, 0x39, 0xef, + 0x68, 0x95, 0x82, 0xae, 0xbd, 0x12, 0x79, 0xa1, + 0x99, 0xfd, 0xd0, 0x10, 0x8e, 0x4b, 0xd8, + }, + { + 0x17, 0x03, 0x01, 0x00, 0x21, 0xc5, 0x7e, 0x0a, + 0x52, 0x6a, 0xb9, 0xaa, 0x1d, 0xae, 0x9e, 0x24, + 0x9c, 0x34, 0x1e, 0xdb, 0x50, 0x95, 0xee, 0x76, + 0xd7, 0x28, 0x88, 0x08, 0xe3, 0x2e, 0x58, 0xf7, + 0xdb, 0x34, 0x75, 0xa5, 0x7f, 0x9d, 0x15, 0x03, + 0x01, 0x00, 0x16, 0x2c, 0xc1, 0x29, 0x5f, 0x12, + 0x1d, 0x19, 0xab, 0xb3, 0xf4, 0x35, 0x1c, 0x62, + 0x6a, 0x80, 0x29, 0x0d, 0x0e, 0xef, 0x7d, 0x6e, + 0x50, + }, +} - { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, - 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, - 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, - 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, - 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, - 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, - 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, - 0x00, 0x00, 0x00, - }, - - { - 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x10, 0xe1, 0x00, 0x3d, 0x0a, - 0x6b, 0x02, 0x7f, 0x97, 0xde, 0xfb, 0x65, 0x46, - 0x1a, 0x50, 0x4e, 0x34, 0x9a, 0xae, 0x14, 0x7e, - 0xec, 0xef, 0x85, 0x15, 0x3b, 0x39, 0xc2, 0x45, - 0x04, 0x40, 0x92, 0x71, 0xd6, 0x7e, 0xf6, 0xfd, - 0x4d, 0x84, 0xf7, 0xc4, 0x77, 0x99, 0x3d, 0xe2, - 0xc3, 0x8d, 0xb0, 0x4c, 0x74, 0xc8, 0x51, 0xec, - 0xb2, 0xe8, 0x6b, 0xa1, 0xd2, 0x4d, 0xd8, 0x61, - 0x92, 0x7a, 0x24, 0x57, 0x44, 0x4f, 0xa2, 0x1e, - 0x74, 0x0b, 0x06, 0x4b, 0x80, 0x34, 0x8b, 0xfe, - 0xc2, 0x0e, 0xc1, 0xcd, 0xab, 0x0c, 0x3f, 0x54, - 0xe2, 0x44, 0xe9, 0x6c, 0x2b, 0xba, 0x7b, 0x64, - 0xf1, 0x93, 0x65, 0x75, 0xf2, 0x35, 0xff, 0x27, - 0x03, 0xd5, 0x64, 0xe6, 0x8e, 0xe7, 0x7b, 0x56, - 0xb6, 0x61, 0x73, 0xeb, 0xa2, 0xdc, 0xa4, 0x6e, - 0x52, 0xac, 0xbc, 0xba, 0x11, 0xa3, 0xd2, 0x61, - 0x4a, 0xe0, 0xbb, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xd2, 0x5a, - 0x0c, 0x2a, 0x27, 0x96, 0xba, 0xa9, 0x67, 0xd2, - 0x51, 0x68, 0x32, 0x68, 0x22, 0x1f, 0xb9, 0x27, - 0x79, 0x59, 0x28, 0xdf, 0x38, 0x1f, 0x92, 0x21, - 0x5d, 0x0f, 0xf4, 0xc0, 0xee, 0xb7, 0x10, 0x5a, - 0xa9, 0x45, - }, - - { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0x13, 0x6f, 0x6c, 0x71, 0x83, - 0x59, 0xcf, 0x32, 0x72, 0xe9, 0xce, 0xcc, 0x7a, - 0x6c, 0xf0, 0x72, 0x39, 0x16, 0xae, 0x40, 0x61, - 0xfa, 0x92, 0x4c, 0xe7, 0xf2, 0x1a, 0xd7, 0x0c, - 0x84, 0x76, 0x6c, 0xe9, 0x11, 0x43, 0x19, 0x17, - 0x03, 0x01, 0x00, 0x21, 0xc0, 0xa2, 0x13, 0x28, - 0x94, 0x8c, 0x5c, 0xd6, 0x79, 0xb9, 0xfe, 0xae, - 0x45, 0x4b, 0xc0, 0x7c, 0xae, 0x2d, 0xb4, 0x0d, - 0x31, 0xc4, 0xad, 0x22, 0xd7, 0x1e, 0x99, 0x1c, - 0x4c, 0x69, 0xab, 0x42, 0x61, 0x15, 0x03, 0x01, - 0x00, 0x16, 0xe1, 0x0c, 0x67, 0xf3, 0xf4, 0xb9, - 0x8e, 0x81, 0x8e, 0x01, 0xb8, 0xa0, 0x69, 0x8c, - 0x03, 0x11, 0x43, 0x3e, 0xee, 0xb7, 0x4d, 0x69, - }}}, +var clientauthTests = []clientauthTest{ // Server asks for cert with empty CA list, client doesn't give it. // go test -run "TestRunServer" -serve -clientauth 1 - // gnutls-cli --insecure --debug 100 -p 10443 localhost - {"RequestClientCert, none given", RequestClientCert, nil, - [][]byte{{ - 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, - 0x76, 0x03, 0x02, 0x4e, 0xe0, 0x93, 0xe2, 0x47, - 0x06, 0xa0, 0x61, 0x0c, 0x51, 0xdd, 0xf0, 0xef, - 0xf4, 0x30, 0x72, 0xe1, 0xa6, 0x50, 0x68, 0x82, - 0x3c, 0xfb, 0xcb, 0x72, 0x5e, 0x73, 0x9d, 0xda, - 0x27, 0x35, 0x72, 0x00, 0x00, 0x34, 0x00, 0x33, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, - 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, - 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, - 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, - 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, - 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, - 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, - 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + {"RequestClientCert, none given", RequestClientCert, nil, [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00, + 0x50, 0x03, 0x01, 0x50, 0x77, 0x43, 0x9e, 0x31, + 0xe6, 0x36, 0x5e, 0x5e, 0x24, 0xe4, 0x0d, 0x26, + 0x34, 0xa7, 0x1c, 0x2e, 0x59, 0x6d, 0xa5, 0x3e, + 0x72, 0xf3, 0xa3, 0x1c, 0xbc, 0xb3, 0x27, 0xaf, + 0x92, 0x5b, 0x7d, 0x00, 0x00, 0x28, 0x00, 0x39, + 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, + 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, + 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, + 0x00, }, - - { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, - 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, - 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, - 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, - 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, - 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, - 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d, - 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16, - 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, - }, - - { - 0x16, 0x03, 0x01, 0x00, 0x07, 0x0b, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, - 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x64, - 0x28, 0xb9, 0x3f, 0x48, 0xaf, 0x06, 0x22, 0x39, - 0x56, 0xd8, 0x6f, 0x63, 0x5d, 0x03, 0x48, 0x63, - 0x01, 0x13, 0xa2, 0xd6, 0x76, 0xc0, 0xab, 0xda, - 0x25, 0x30, 0x75, 0x6c, 0xaa, 0xb4, 0xdc, 0x35, - 0x72, 0xdc, 0xf2, 0x43, 0xe4, 0x1d, 0x82, 0xfb, - 0x6c, 0x64, 0xe2, 0xa7, 0x8f, 0x32, 0x67, 0x6b, - 0xcd, 0xd2, 0xb2, 0x36, 0x94, 0xbc, 0x6f, 0x46, - 0x79, 0x29, 0x42, 0xe3, 0x1a, 0xbf, 0xfb, 0x41, - 0xd5, 0xe3, 0xb4, 0x2a, 0xf6, 0x95, 0x6f, 0x0c, - 0x87, 0xb9, 0x03, 0x18, 0xa1, 0xea, 0x4a, 0xe2, - 0x2e, 0x0f, 0x50, 0x00, 0xc1, 0xe8, 0x8c, 0xc8, - 0xa2, 0xf6, 0xa4, 0x05, 0xf4, 0x38, 0x3e, 0xd9, - 0x6e, 0x63, 0x96, 0x0c, 0x34, 0x73, 0x90, 0x03, - 0x55, 0xa6, 0x34, 0xb0, 0x5e, 0x8c, 0x48, 0x40, - 0x25, 0x45, 0x84, 0xa6, 0x21, 0x3f, 0x81, 0x97, - 0xa7, 0x11, 0x09, 0x14, 0x95, 0xa5, 0xe5, 0x14, - 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, - 0x00, 0x24, 0x16, 0xaa, 0x01, 0x2c, 0xa8, 0xc1, - 0x28, 0xaf, 0x35, 0xc1, 0xc1, 0xf3, 0x0a, 0x25, - 0x66, 0x6e, 0x27, 0x11, 0xa3, 0xa4, 0xd9, 0xe9, - 0xea, 0x15, 0x09, 0x9d, 0x28, 0xe3, 0x5b, 0x2b, - 0xa6, 0x25, 0xa7, 0x14, 0x24, 0x3a, - }, - - { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0x9a, 0xa8, 0xd6, 0x77, 0x46, - 0x45, 0x68, 0x9d, 0x5d, 0xa9, 0x68, 0x03, 0xe5, - 0xaf, 0xe8, 0xc8, 0x21, 0xc5, 0xc6, 0xc1, 0x50, - 0xe0, 0xd8, 0x52, 0xce, 0xa3, 0x4f, 0x2d, 0xf4, - 0xe3, 0xa7, 0x7d, 0x35, 0x80, 0x84, 0x12, 0x17, - 0x03, 0x01, 0x00, 0x21, 0x8a, 0x82, 0x0c, 0x54, - 0x1b, 0xeb, 0x77, 0x90, 0x2c, 0x3e, 0xbc, 0xf0, - 0x23, 0xcc, 0xa8, 0x9f, 0x25, 0x08, 0x12, 0xed, - 0x43, 0xf1, 0xf9, 0x06, 0xad, 0xa9, 0x4b, 0x97, - 0x82, 0xb7, 0xc4, 0x0b, 0x4c, 0x15, 0x03, 0x01, - 0x00, 0x16, 0x05, 0x2d, 0x9d, 0x45, 0x03, 0xb7, - 0xc2, 0xd1, 0xb5, 0x1a, 0x43, 0xcf, 0x1a, 0x37, - 0xf4, 0x70, 0xcc, 0xb4, 0xed, 0x07, 0x76, 0x3a, - }}}, + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d, + 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16, + 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x07, 0x0b, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, + 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x04, + 0x58, 0x63, 0x26, 0x32, 0x1b, 0x34, 0xbe, 0x10, + 0xe4, 0xe4, 0x3e, 0xcd, 0x36, 0x7f, 0xa8, 0xa8, + 0xe0, 0x19, 0xe8, 0x94, 0x13, 0xd9, 0x35, 0xc4, + 0x71, 0xb4, 0x91, 0xd4, 0xbc, 0x74, 0x57, 0x9f, + 0x93, 0xb7, 0x5d, 0x3b, 0x9c, 0xff, 0x5d, 0x79, + 0xdb, 0x86, 0xfc, 0xdc, 0x74, 0x1e, 0x0c, 0xc6, + 0xe8, 0x93, 0xcf, 0xaf, 0xba, 0x1d, 0xfd, 0x8a, + 0xeb, 0xef, 0xbf, 0xfa, 0xa6, 0xe7, 0x53, 0x98, + 0x60, 0x4e, 0x0e, 0x60, 0x7d, 0xea, 0x40, 0x8d, + 0x1d, 0x8f, 0xa3, 0xc6, 0x83, 0xbc, 0xef, 0xb7, + 0x9a, 0x4a, 0xe7, 0x99, 0xee, 0x0b, 0xc7, 0x46, + 0x75, 0x45, 0x66, 0xe8, 0x5f, 0x4b, 0x08, 0xa4, + 0xc1, 0x36, 0xd0, 0x36, 0x2c, 0xf2, 0x9a, 0x44, + 0x1e, 0x5f, 0x22, 0xf4, 0xbe, 0x66, 0x66, 0x17, + 0xd8, 0xb6, 0x0a, 0x89, 0xed, 0x22, 0x80, 0xdb, + 0xad, 0x05, 0xd1, 0xb5, 0x93, 0xa1, 0x1c, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x24, 0x62, 0x6f, 0x3d, 0x30, 0x56, 0x97, + 0xde, 0x03, 0x67, 0xa9, 0x63, 0x21, 0xb6, 0xe6, + 0x05, 0x69, 0x94, 0xfb, 0x50, 0xc1, 0x99, 0xdd, + 0xf6, 0xe8, 0x60, 0xbd, 0xe6, 0xba, 0xe3, 0x50, + 0x0a, 0xcd, 0xde, 0x14, 0x16, 0xc4, + }, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0xf0, 0x21, 0xf6, 0x84, 0x6a, + 0xe3, 0x6b, 0x8a, 0xc5, 0x46, 0x50, 0xca, 0x40, + 0xea, 0x4e, 0x82, 0xc1, 0x70, 0x25, 0xd8, 0x7d, + 0x60, 0xf5, 0x51, 0x7f, 0x64, 0x03, 0x9f, 0x53, + 0xec, 0xfb, 0x57, 0xa9, 0xfc, 0x26, 0x15, 0x17, + 0x03, 0x01, 0x00, 0x21, 0xa6, 0xc6, 0x94, 0x2b, + 0xa9, 0xcb, 0x93, 0xff, 0xb6, 0xa6, 0xe7, 0xc5, + 0x37, 0x86, 0x15, 0x37, 0x57, 0xce, 0xef, 0x54, + 0x96, 0x5d, 0x50, 0xa0, 0x50, 0x69, 0x5e, 0x82, + 0x61, 0x8d, 0x42, 0xfb, 0x78, 0x15, 0x03, 0x01, + 0x00, 0x16, 0x45, 0xd1, 0x86, 0x68, 0x59, 0xc1, + 0xaf, 0xac, 0x5c, 0x46, 0x8a, 0x68, 0x69, 0x0c, + 0xd7, 0x67, 0xbf, 0xf0, 0x3e, 0xee, 0x45, 0x55, + }, + }}, // Server asks for cert with empty CA list, client gives one // go test -run "TestRunServer" -serve -clientauth 1 - // gnutls-cli --insecure --debug 100 -p 10443 localhost - {"RequestClientCert, client gives it", RequestClientCert, - []*x509.Certificate{clientCertificate}, - [][]byte{{ - 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, - 0x76, 0x03, 0x02, 0x4e, 0xe7, 0x44, 0xda, 0x58, - 0x7d, 0x46, 0x4a, 0x48, 0x97, 0x9f, 0xe5, 0x91, - 0x11, 0x64, 0xa7, 0x1e, 0x4d, 0xb7, 0xfe, 0x9b, - 0xc6, 0x63, 0xf8, 0xa4, 0xb5, 0x0b, 0x18, 0xb5, - 0xbd, 0x19, 0xb3, 0x00, 0x00, 0x34, 0x00, 0x33, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, - 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, - 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, - 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, - 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, - 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, - 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, - 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + {"RequestClientCert, client gives it", RequestClientCert, []*x509.Certificate{clientCertificate}, [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00, + 0x50, 0x03, 0x01, 0x50, 0x77, 0x43, 0x47, 0xfd, + 0x1d, 0xb0, 0x60, 0x4c, 0x25, 0x86, 0x45, 0x4a, + 0xe5, 0x3f, 0x80, 0x56, 0x18, 0x91, 0x5c, 0xe2, + 0x62, 0xc5, 0x77, 0xc2, 0x92, 0xdd, 0xdc, 0x39, + 0x23, 0x1d, 0xc5, 0x00, 0x00, 0x28, 0x00, 0x39, + 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, + 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, + 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, + 0x00, }, - - { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, - 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, - 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, - 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, - 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, - 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, - 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d, - 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16, - 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, - }, - - { - 0x16, 0x03, 0x01, 0x01, 0xfb, 0x0b, 0x00, 0x01, - 0xf7, 0x00, 0x01, 0xf4, 0x00, 0x01, 0xf1, 0x30, - 0x82, 0x01, 0xed, 0x30, 0x82, 0x01, 0x58, 0xa0, - 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30, - 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x30, 0x26, 0x31, 0x10, - 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, - 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, - 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, - 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, - 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x31, 0x31, 0x32, 0x30, 0x38, 0x30, 0x37, - 0x35, 0x35, 0x31, 0x32, 0x5a, 0x17, 0x0d, 0x31, - 0x32, 0x31, 0x32, 0x30, 0x37, 0x30, 0x38, 0x30, - 0x30, 0x31, 0x32, 0x5a, 0x30, 0x26, 0x31, 0x10, - 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, - 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, - 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, - 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, - 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9c, 0x30, - 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x01, 0x03, 0x81, 0x8c, 0x00, - 0x30, 0x81, 0x88, 0x02, 0x81, 0x80, 0x4e, 0xd0, - 0x7b, 0x31, 0xe3, 0x82, 0x64, 0xd9, 0x59, 0xc0, - 0xc2, 0x87, 0xa4, 0x5e, 0x1e, 0x8b, 0x73, 0x33, - 0xc7, 0x63, 0x53, 0xdf, 0x66, 0x92, 0x06, 0x84, - 0xf6, 0x64, 0xd5, 0x8f, 0xe4, 0x36, 0xa7, 0x1d, - 0x2b, 0xe8, 0xb3, 0x20, 0x36, 0x45, 0x23, 0xb5, - 0xe3, 0x95, 0xae, 0xed, 0xe0, 0xf5, 0x20, 0x9c, - 0x8d, 0x95, 0xdf, 0x7f, 0x5a, 0x12, 0xef, 0x87, - 0xe4, 0x5b, 0x68, 0xe4, 0xe9, 0x0e, 0x74, 0xec, - 0x04, 0x8a, 0x7f, 0xde, 0x93, 0x27, 0xc4, 0x01, - 0x19, 0x7a, 0xbd, 0xf2, 0xdc, 0x3d, 0x14, 0xab, - 0xd0, 0x54, 0xca, 0x21, 0x0c, 0xd0, 0x4d, 0x6e, - 0x87, 0x2e, 0x5c, 0xc5, 0xd2, 0xbb, 0x4d, 0x4b, - 0x4f, 0xce, 0xb6, 0x2c, 0xf7, 0x7e, 0x88, 0xec, - 0x7c, 0xd7, 0x02, 0x91, 0x74, 0xa6, 0x1e, 0x0c, - 0x1a, 0xda, 0xe3, 0x4a, 0x5a, 0x2e, 0xde, 0x13, - 0x9c, 0x4c, 0x40, 0x88, 0x59, 0x93, 0x02, 0x03, - 0x01, 0x00, 0x01, 0xa3, 0x32, 0x30, 0x30, 0x30, - 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, - 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0xa0, 0x30, - 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x06, - 0x04, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, 0x0f, - 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x08, 0x30, - 0x06, 0x80, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, - 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x03, 0x81, 0x81, 0x00, - 0x36, 0x1f, 0xb3, 0x7a, 0x0c, 0x75, 0xc9, 0x6e, - 0x37, 0x46, 0x61, 0x2b, 0xd5, 0xbd, 0xc0, 0xa7, - 0x4b, 0xcc, 0x46, 0x9a, 0x81, 0x58, 0x7c, 0x85, - 0x79, 0x29, 0xc8, 0xc8, 0xc6, 0x67, 0xdd, 0x32, - 0x56, 0x45, 0x2b, 0x75, 0xb6, 0xe9, 0x24, 0xa9, - 0x50, 0x9a, 0xbe, 0x1f, 0x5a, 0xfa, 0x1a, 0x15, - 0xd9, 0xcc, 0x55, 0x95, 0x72, 0x16, 0x83, 0xb9, - 0xc2, 0xb6, 0x8f, 0xfd, 0x88, 0x8c, 0x38, 0x84, - 0x1d, 0xab, 0x5d, 0x92, 0x31, 0x13, 0x4f, 0xfd, - 0x83, 0x3b, 0xc6, 0x9d, 0xf1, 0x11, 0x62, 0xb6, - 0x8b, 0xec, 0xab, 0x67, 0xbe, 0xc8, 0x64, 0xb0, - 0x11, 0x50, 0x46, 0x58, 0x17, 0x6b, 0x99, 0x1c, - 0xd3, 0x1d, 0xfc, 0x06, 0xf1, 0x0e, 0xe5, 0x96, - 0xa8, 0x0c, 0xf9, 0x78, 0x20, 0xb7, 0x44, 0x18, - 0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf, - 0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85, - 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0xa7, 0x2f, 0xed, 0xfa, 0xc2, - 0xbd, 0x46, 0xa1, 0xf2, 0x69, 0xc5, 0x1d, 0xa1, - 0x34, 0xd6, 0xd0, 0x84, 0xf5, 0x5d, 0x8c, 0x82, - 0x8d, 0x98, 0x82, 0x9c, 0xd9, 0x07, 0xe0, 0xf7, - 0x55, 0x49, 0x4d, 0xa1, 0x48, 0x59, 0x02, 0xd3, - 0x84, 0x37, 0xaf, 0x01, 0xb3, 0x3a, 0xf4, 0xed, - 0x99, 0xbe, 0x67, 0x36, 0x19, 0x55, 0xf3, 0xf9, - 0xcb, 0x94, 0xe5, 0x7b, 0x8b, 0x77, 0xf2, 0x5f, - 0x4c, 0xfe, 0x01, 0x1f, 0x7b, 0xd7, 0x23, 0x49, - 0x0c, 0xcb, 0x6c, 0xb0, 0xe7, 0x77, 0xd6, 0xcf, - 0xa8, 0x7d, 0xdb, 0xa7, 0x14, 0xe2, 0xf5, 0xf3, - 0xff, 0xba, 0x23, 0xd2, 0x9a, 0x36, 0x14, 0x60, - 0x2a, 0x91, 0x5d, 0x2b, 0x35, 0x3b, 0xb6, 0xdd, - 0xcb, 0x6b, 0xdc, 0x18, 0xdc, 0x33, 0xb8, 0xb3, - 0xc7, 0x27, 0x7e, 0xfc, 0xd2, 0xf7, 0x97, 0x90, - 0x5e, 0x17, 0xac, 0x14, 0x8e, 0x0f, 0xca, 0xb5, - 0x6f, 0xc9, 0x2d, 0x16, 0x03, 0x01, 0x00, 0x86, - 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x44, 0x7f, - 0xa2, 0x59, 0x60, 0x0b, 0x5a, 0xc4, 0xaf, 0x1e, - 0x60, 0xa5, 0x24, 0xea, 0xc1, 0xc3, 0x22, 0x21, - 0x6b, 0x22, 0x8b, 0x2a, 0x11, 0x82, 0x68, 0x7d, - 0xb9, 0xdd, 0x9c, 0x27, 0x4c, 0xc2, 0xc8, 0xa2, - 0x8b, 0x6b, 0x77, 0x8d, 0x3a, 0x2b, 0x8d, 0x2f, - 0x6a, 0x2b, 0x43, 0xd2, 0xd1, 0xc6, 0x41, 0x79, - 0xa2, 0x4f, 0x2b, 0xc2, 0xf7, 0xb2, 0x10, 0xad, - 0xa6, 0x01, 0x51, 0x51, 0x25, 0xe7, 0x58, 0x7a, - 0xcf, 0x3b, 0xc4, 0x29, 0xb5, 0xe5, 0xa7, 0x83, - 0xe6, 0xcb, 0x1e, 0xf3, 0x02, 0x0f, 0x53, 0x3b, - 0xb5, 0x39, 0xef, 0x9c, 0x42, 0xe0, 0xa6, 0x9b, - 0x2b, 0xdd, 0x60, 0xae, 0x0a, 0x73, 0x35, 0xbe, - 0x26, 0x10, 0x1b, 0xe9, 0xe9, 0x61, 0xab, 0x20, - 0xa5, 0x48, 0xc6, 0x60, 0xa6, 0x50, 0x3c, 0xfb, - 0xa7, 0xca, 0xb0, 0x80, 0x95, 0x1e, 0xce, 0xc7, - 0xbb, 0x68, 0x44, 0xdc, 0x0e, 0x0e, 0x14, 0x03, - 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, - 0x24, 0xb6, 0xcd, 0x0c, 0x78, 0xfd, 0xd6, 0xff, - 0xbe, 0x97, 0xd5, 0x0a, 0x7d, 0x4f, 0xa1, 0x03, - 0x78, 0xc8, 0x61, 0x6f, 0xf2, 0x4b, 0xa8, 0x56, - 0x4f, 0x3c, 0xa2, 0xd9, 0xd0, 0x20, 0x13, 0x1b, - 0x8b, 0x36, 0xb7, 0x33, 0x9c, - }, - - { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0xa3, 0x43, 0x94, 0xe7, 0xdf, - 0xb6, 0xc3, 0x03, 0x9f, 0xc1, 0x59, 0x0c, 0xc3, - 0x13, 0xae, 0xed, 0xcf, 0xff, 0xf1, 0x80, 0xf3, - 0x13, 0x63, 0x1c, 0xf0, 0xca, 0xad, 0x9e, 0x71, - 0x46, 0x5f, 0x6b, 0xeb, 0x10, 0x3f, 0xe3, 0x17, - 0x03, 0x01, 0x00, 0x21, 0xe9, 0x80, 0x95, 0x6e, - 0x05, 0x55, 0x2f, 0xed, 0x4d, 0xde, 0x17, 0x3a, - 0x32, 0x9b, 0x2a, 0x74, 0x30, 0x4f, 0xe0, 0x9f, - 0x4e, 0xd3, 0x06, 0xbd, 0x3a, 0x43, 0x75, 0x8b, - 0x5b, 0x9a, 0xd8, 0x2e, 0x56, 0x15, 0x03, 0x01, - 0x00, 0x16, 0x53, 0xf5, 0xff, 0xe0, 0xa1, 0x6c, - 0x33, 0xf4, 0x4e, 0x89, 0x68, 0xe1, 0xf7, 0x61, - 0x13, 0xb3, 0x12, 0xa1, 0x8e, 0x5a, 0x7a, 0x02, - }, + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d, + 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16, + 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, }, - }, + { + 0x16, 0x03, 0x01, 0x01, 0xfb, 0x0b, 0x00, 0x01, + 0xf7, 0x00, 0x01, 0xf4, 0x00, 0x01, 0xf1, 0x30, + 0x82, 0x01, 0xed, 0x30, 0x82, 0x01, 0x58, 0xa0, + 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x30, 0x26, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, + 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x31, 0x31, 0x32, 0x30, 0x38, 0x30, 0x37, + 0x35, 0x35, 0x31, 0x32, 0x5a, 0x17, 0x0d, 0x31, + 0x32, 0x31, 0x32, 0x30, 0x37, 0x30, 0x38, 0x30, + 0x30, 0x31, 0x32, 0x5a, 0x30, 0x26, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, + 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9c, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x03, 0x81, 0x8c, 0x00, + 0x30, 0x81, 0x88, 0x02, 0x81, 0x80, 0x4e, 0xd0, + 0x7b, 0x31, 0xe3, 0x82, 0x64, 0xd9, 0x59, 0xc0, + 0xc2, 0x87, 0xa4, 0x5e, 0x1e, 0x8b, 0x73, 0x33, + 0xc7, 0x63, 0x53, 0xdf, 0x66, 0x92, 0x06, 0x84, + 0xf6, 0x64, 0xd5, 0x8f, 0xe4, 0x36, 0xa7, 0x1d, + 0x2b, 0xe8, 0xb3, 0x20, 0x36, 0x45, 0x23, 0xb5, + 0xe3, 0x95, 0xae, 0xed, 0xe0, 0xf5, 0x20, 0x9c, + 0x8d, 0x95, 0xdf, 0x7f, 0x5a, 0x12, 0xef, 0x87, + 0xe4, 0x5b, 0x68, 0xe4, 0xe9, 0x0e, 0x74, 0xec, + 0x04, 0x8a, 0x7f, 0xde, 0x93, 0x27, 0xc4, 0x01, + 0x19, 0x7a, 0xbd, 0xf2, 0xdc, 0x3d, 0x14, 0xab, + 0xd0, 0x54, 0xca, 0x21, 0x0c, 0xd0, 0x4d, 0x6e, + 0x87, 0x2e, 0x5c, 0xc5, 0xd2, 0xbb, 0x4d, 0x4b, + 0x4f, 0xce, 0xb6, 0x2c, 0xf7, 0x7e, 0x88, 0xec, + 0x7c, 0xd7, 0x02, 0x91, 0x74, 0xa6, 0x1e, 0x0c, + 0x1a, 0xda, 0xe3, 0x4a, 0x5a, 0x2e, 0xde, 0x13, + 0x9c, 0x4c, 0x40, 0x88, 0x59, 0x93, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x32, 0x30, 0x30, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0xa0, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x06, + 0x04, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x08, 0x30, + 0x06, 0x80, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x03, 0x81, 0x81, 0x00, + 0x36, 0x1f, 0xb3, 0x7a, 0x0c, 0x75, 0xc9, 0x6e, + 0x37, 0x46, 0x61, 0x2b, 0xd5, 0xbd, 0xc0, 0xa7, + 0x4b, 0xcc, 0x46, 0x9a, 0x81, 0x58, 0x7c, 0x85, + 0x79, 0x29, 0xc8, 0xc8, 0xc6, 0x67, 0xdd, 0x32, + 0x56, 0x45, 0x2b, 0x75, 0xb6, 0xe9, 0x24, 0xa9, + 0x50, 0x9a, 0xbe, 0x1f, 0x5a, 0xfa, 0x1a, 0x15, + 0xd9, 0xcc, 0x55, 0x95, 0x72, 0x16, 0x83, 0xb9, + 0xc2, 0xb6, 0x8f, 0xfd, 0x88, 0x8c, 0x38, 0x84, + 0x1d, 0xab, 0x5d, 0x92, 0x31, 0x13, 0x4f, 0xfd, + 0x83, 0x3b, 0xc6, 0x9d, 0xf1, 0x11, 0x62, 0xb6, + 0x8b, 0xec, 0xab, 0x67, 0xbe, 0xc8, 0x64, 0xb0, + 0x11, 0x50, 0x46, 0x58, 0x17, 0x6b, 0x99, 0x1c, + 0xd3, 0x1d, 0xfc, 0x06, 0xf1, 0x0e, 0xe5, 0x96, + 0xa8, 0x0c, 0xf9, 0x78, 0x20, 0xb7, 0x44, 0x18, + 0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf, + 0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85, + 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x80, 0x81, 0x46, 0x43, 0xf9, 0xe7, + 0xda, 0x8c, 0x92, 0x3a, 0x78, 0x1a, 0x86, 0xb3, + 0xbe, 0x83, 0x22, 0xb6, 0xaa, 0x57, 0x37, 0x68, + 0x9e, 0x54, 0x3f, 0xd3, 0xce, 0x4d, 0x5e, 0x2a, + 0xdc, 0xb0, 0x49, 0x02, 0xbb, 0xc0, 0x45, 0x58, + 0x79, 0x10, 0xc7, 0x94, 0x60, 0x9f, 0x1b, 0x5f, + 0x18, 0x31, 0x37, 0x9c, 0xe0, 0xe6, 0xdf, 0x5e, + 0x70, 0x44, 0xf6, 0x8b, 0xdf, 0xf1, 0xf6, 0x43, + 0xc8, 0x2f, 0xd1, 0xce, 0xd0, 0xd6, 0x64, 0x4f, + 0xe8, 0x2b, 0xfa, 0xd3, 0xd0, 0xd1, 0x2e, 0xaa, + 0x9b, 0x1d, 0x13, 0x5c, 0xbe, 0x57, 0x41, 0x6c, + 0x5e, 0x8d, 0xea, 0xa9, 0x3c, 0x58, 0xa0, 0x30, + 0x92, 0x77, 0x7a, 0xed, 0x64, 0x58, 0xe5, 0x7f, + 0x6a, 0x93, 0x89, 0x66, 0x3d, 0x13, 0x16, 0x56, + 0xa0, 0xad, 0xdc, 0x68, 0x95, 0x87, 0x81, 0xd0, + 0x90, 0x4d, 0x5f, 0xfe, 0x3e, 0x83, 0x15, 0x2e, + 0x50, 0x3c, 0xdd, 0x16, 0x03, 0x01, 0x00, 0x86, + 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x2b, 0xf8, + 0x56, 0x48, 0xbb, 0x02, 0x37, 0x15, 0x02, 0x74, + 0x33, 0x53, 0x65, 0xa7, 0x7c, 0x2f, 0xc6, 0x5d, + 0x80, 0x59, 0xc1, 0xc2, 0x3b, 0xa9, 0xde, 0x4e, + 0x70, 0x51, 0xd2, 0xde, 0x58, 0x7f, 0xd8, 0xb9, + 0xb6, 0x3b, 0xc8, 0xaa, 0xfc, 0x3d, 0x53, 0x2d, + 0x61, 0x4d, 0xf5, 0x60, 0x12, 0xc2, 0xa5, 0x39, + 0x0c, 0xa7, 0xc6, 0xac, 0x26, 0x4b, 0xf4, 0x5f, + 0xe9, 0xf4, 0xf2, 0x73, 0x48, 0xe4, 0x3b, 0xee, + 0xf2, 0xee, 0xc0, 0xee, 0xfb, 0x5b, 0x60, 0xc2, + 0x74, 0xe6, 0xf6, 0x43, 0x3e, 0xa4, 0xf7, 0x97, + 0x3d, 0xfc, 0xe9, 0x44, 0x21, 0x18, 0x46, 0x05, + 0x33, 0xf8, 0xfe, 0x35, 0x5b, 0xe6, 0x8f, 0xef, + 0x4d, 0x4c, 0x87, 0xf6, 0xb4, 0x6e, 0x6b, 0x39, + 0xd8, 0xaa, 0x1b, 0x33, 0xc9, 0x1c, 0x66, 0x48, + 0xbe, 0xfa, 0xb5, 0x92, 0x09, 0xfd, 0xb9, 0xb9, + 0xca, 0xe6, 0x6d, 0x71, 0xc6, 0x89, 0x14, 0x03, + 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, + 0x24, 0xe3, 0x2b, 0xef, 0x17, 0xd5, 0xa6, 0x4c, + 0x2e, 0x10, 0xac, 0x9c, 0xfe, 0x0f, 0x18, 0x43, + 0x95, 0x00, 0x81, 0xf7, 0x7c, 0x00, 0x5b, 0x89, + 0x52, 0x41, 0xe4, 0x8a, 0x8a, 0x34, 0x31, 0x09, + 0x48, 0x7c, 0xc5, 0xc3, 0x83, + }, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0x24, 0xaa, 0xaa, 0x56, 0x8b, + 0x41, 0x87, 0x01, 0xbe, 0x80, 0x05, 0x51, 0x36, + 0x08, 0xfc, 0xaf, 0xff, 0x7f, 0xf4, 0x74, 0x84, + 0x88, 0xdc, 0xb8, 0x8e, 0x70, 0x6c, 0x22, 0x04, + 0xee, 0x45, 0x8d, 0xda, 0xed, 0xc6, 0x05, 0x17, + 0x03, 0x01, 0x00, 0x21, 0x91, 0x49, 0x4b, 0xed, + 0xa3, 0x41, 0xe9, 0x88, 0x3b, 0xa3, 0x01, 0xee, + 0x77, 0x4e, 0x12, 0xb4, 0xcd, 0x5e, 0xcc, 0x45, + 0x02, 0x5a, 0x20, 0xd6, 0xe8, 0xac, 0xcb, 0x60, + 0xcb, 0x1b, 0xef, 0xf9, 0xc2, 0x15, 0x03, 0x01, + 0x00, 0x16, 0xd4, 0xcd, 0x92, 0x3c, 0x10, 0x93, + 0x68, 0xc3, 0xdd, 0xaf, 0xe9, 0xcb, 0x5d, 0x94, + 0x1a, 0x06, 0x81, 0xa7, 0x78, 0x0f, 0xc3, 0x03, + }, + }}, } // cert.pem and key.pem were generated with generate_cert.go @@ -1703,7 +1984,7 @@ GFGNEH5PlGffo05wc46QkYU= /* corresponding key.pem for cert.pem is: -----BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgE7QezHjgmTZWcDCh6ReHotzM8djU99mkgaE9mTVj+Q2px0r6LMg +MIICWgIBAAKBgE7QezHjgmTZWcDCh6ReHotzM8djU99mkgaE9mTVj+Q2px0r6LMg NkUjteOVru3g9SCcjZXff1oS74fkW2jk6Q507ASKf96TJ8QBGXq98tw9FKvQVMoh DNBNbocuXMXSu01LT862LPd+iOx81wKRdKYeDBra40paLt4TnExAiFmTAgMBAAEC gYBxvXd8yNteFTns8A/2yomEMC4yeosJJSpp1CsN3BJ7g8/qTnrVPxBy+RU+qr63 @@ -1715,6 +1996,6 @@ RkKVpenBHyrGg0oeN5La7URILWKj7CPXAkBKo6F+d+phNjwIFoN1Xb/RA32w/D1I saG9sF+UEhRt9AxUfW/U/tIQ9V0ZHHcSg1XaCM5Nvp934brdKdvTOKnJAkBD5h/3 Rybatlvg/fzBEaJFyq09zhngkxlZOUtBVTqzl17RVvY2orgH02U4HbCHy4phxOn7 qTdQRYlHRftgnWK1AkANibn9PRYJ7mJyJ9Dyj2QeNcSkSTzrt0tPvUMf4+meJymN -1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvAMAA= +1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvA -----END RSA PRIVATE KEY----- */ diff --git a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py b/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py deleted file mode 100644 index 5692bd32f..000000000 --- a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# This code is used to parse the debug log from gnutls-cli and generate a -# script of the handshake. This script is included in handshake_server_test.go. -# See the comments there for details. - -import sys - -blocks = [] - -READ = 1 -WRITE = 2 - -currentBlockType = 0 -currentBlock = [] -for line in sys.stdin.readlines(): - line = line[:-1] - if line.startswith("|<7>| WRITE: "): - if currentBlockType != WRITE: - if len(currentBlock) > 0: - blocks.append(currentBlock) - currentBlock = [] - currentBlockType = WRITE - elif line.startswith("|<7>| READ: "): - if currentBlockType != READ: - if len(currentBlock) > 0: - blocks.append(currentBlock) - currentBlock = [] - currentBlockType = READ - elif line.startswith("|<7>| 0"): - line = line[13:] - line = line.strip() - bs = line.split() - for b in bs: - currentBlock.append(int(b, 16)) - elif line.startswith("|<7>| RB-PEEK: Read 1 bytes"): - currentBlock = currentBlock[:-1] - -if len(currentBlock) > 0: - blocks.append(currentBlock) - -for block in blocks: - sys.stdout.write("\t{\n") - - i = 0 - for b in block: - if i % 8 == 0: - sys.stdout.write("\t\t") - sys.stdout.write("0x%02x," % b) - if i % 8 == 7: - sys.stdout.write("\n") - else: - sys.stdout.write(" ") - i += 1 - sys.stdout.write("\n\t},\n\n") diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go index 637ef03e2..df1eaad05 100644 --- a/src/pkg/crypto/tls/prf.go +++ b/src/pkg/crypto/tls/prf.go @@ -106,10 +106,9 @@ var keyExpansionLabel = []byte("key expansion") var clientFinishedLabel = []byte("client finished") var serverFinishedLabel = []byte("server finished") -// keysFromPreMasterSecret generates the connection keys from the pre master -// secret, given the lengths of the MAC key, cipher key and IV, as defined in -// RFC 2246, section 6.3. -func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { +// masterFromPreMasterSecret generates the master secret from the pre-master +// secret. See http://tools.ietf.org/html/rfc5246#section-8.1 +func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []byte { prf := pRF10 if version == versionSSL30 { prf = pRF30 @@ -118,9 +117,21 @@ func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serv var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], clientRandom) copy(seed[len(clientRandom):], serverRandom) - masterSecret = make([]byte, masterSecretLength) + masterSecret := make([]byte, masterSecretLength) prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) + return masterSecret +} +// keysFromMasterSecret generates the connection keys from the master +// secret, given the lengths of the MAC key, cipher key and IV, as defined in +// RFC 2246, section 6.3. +func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { + prf := pRF10 + if version == versionSSL30 { + prf = pRF30 + } + + var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], serverRandom) copy(seed[len(serverRandom):], clientRandom) diff --git a/src/pkg/crypto/tls/prf_test.go b/src/pkg/crypto/tls/prf_test.go index a32392cef..773a2b2ff 100644 --- a/src/pkg/crypto/tls/prf_test.go +++ b/src/pkg/crypto/tls/prf_test.go @@ -48,18 +48,23 @@ func TestKeysFromPreMasterSecret(t *testing.T) { in, _ := hex.DecodeString(test.preMasterSecret) clientRandom, _ := hex.DecodeString(test.clientRandom) serverRandom, _ := hex.DecodeString(test.serverRandom) - master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) - masterString := hex.EncodeToString(master) + + masterSecret := masterFromPreMasterSecret(test.version, in, clientRandom, serverRandom) + if s := hex.EncodeToString(masterSecret); s != test.masterSecret { + t.Errorf("#%d: bad master secret %s, want %s", i, s, test.masterSecret) + continue + } + + clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromMasterSecret(test.version, masterSecret, clientRandom, serverRandom, test.macLen, test.keyLen, 0) clientMACString := hex.EncodeToString(clientMAC) serverMACString := hex.EncodeToString(serverMAC) clientKeyString := hex.EncodeToString(clientKey) serverKeyString := hex.EncodeToString(serverKey) - if masterString != test.masterSecret || - clientMACString != test.clientMAC || + if clientMACString != test.clientMAC || serverMACString != test.serverMAC || clientKeyString != test.clientKey || serverKeyString != test.serverKey { - t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s, %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverKeyString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) + t.Errorf("#%d: got: (%s, %s, %s, %s) want: (%s, %s, %s, %s)", i, clientMACString, serverMACString, clientKeyString, serverKeyString, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) } } } diff --git a/src/pkg/crypto/tls/root_test.go b/src/pkg/crypto/tls/root_test.go deleted file mode 100644 index e61c21851..000000000 --- a/src/pkg/crypto/tls/root_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tls - -import ( - "crypto/x509" - "runtime" - "testing" -) - -var tlsServers = []string{ - "google.com", - "github.com", - "twitter.com", -} - -func TestOSCertBundles(t *testing.T) { - if testing.Short() { - t.Logf("skipping certificate tests in short mode") - return - } - - for _, addr := range tlsServers { - conn, err := Dial("tcp", addr+":443", &Config{ServerName: addr}) - if err != nil { - t.Errorf("unable to verify %v: %v", addr, err) - continue - } - err = conn.Close() - if err != nil { - t.Error(err) - } - } -} - -func TestCertHostnameVerifyWindows(t *testing.T) { - if runtime.GOOS != "windows" { - return - } - - if testing.Short() { - t.Logf("skipping certificate tests in short mode") - return - } - - for _, addr := range tlsServers { - cfg := &Config{ServerName: "example.com"} - conn, err := Dial("tcp", addr+":443", cfg) - if err == nil { - conn.Close() - t.Errorf("should fail to verify for example.com: %v", addr) - continue - } - _, ok := err.(x509.HostnameError) - if !ok { - t.Errorf("error type mismatch, got: %v", err) - } - } -} diff --git a/src/pkg/crypto/tls/ticket.go b/src/pkg/crypto/tls/ticket.go new file mode 100644 index 000000000..4cfc5a53f --- /dev/null +++ b/src/pkg/crypto/tls/ticket.go @@ -0,0 +1,182 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "crypto/subtle" + "errors" + "io" +) + +// sessionState contains the information that is serialized into a session +// ticket in order to later resume a connection. +type sessionState struct { + vers uint16 + cipherSuite uint16 + masterSecret []byte + certificates [][]byte +} + +func (s *sessionState) equal(i interface{}) bool { + s1, ok := i.(*sessionState) + if !ok { + return false + } + + if s.vers != s1.vers || + s.cipherSuite != s1.cipherSuite || + !bytes.Equal(s.masterSecret, s1.masterSecret) { + return false + } + + if len(s.certificates) != len(s1.certificates) { + return false + } + + for i := range s.certificates { + if !bytes.Equal(s.certificates[i], s1.certificates[i]) { + return false + } + } + + return true +} + +func (s *sessionState) marshal() []byte { + length := 2 + 2 + 2 + len(s.masterSecret) + 2 + for _, cert := range s.certificates { + length += 4 + len(cert) + } + + ret := make([]byte, length) + x := ret + x[0] = byte(s.vers >> 8) + x[1] = byte(s.vers) + x[2] = byte(s.cipherSuite >> 8) + x[3] = byte(s.cipherSuite) + x[4] = byte(len(s.masterSecret) >> 8) + x[5] = byte(len(s.masterSecret)) + x = x[6:] + copy(x, s.masterSecret) + x = x[len(s.masterSecret):] + + x[0] = byte(len(s.certificates) >> 8) + x[1] = byte(len(s.certificates)) + x = x[2:] + + for _, cert := range s.certificates { + x[0] = byte(len(cert) >> 24) + x[1] = byte(len(cert) >> 16) + x[2] = byte(len(cert) >> 8) + x[3] = byte(len(cert)) + copy(x[4:], cert) + x = x[4+len(cert):] + } + + return ret +} + +func (s *sessionState) unmarshal(data []byte) bool { + if len(data) < 8 { + return false + } + + s.vers = uint16(data[0])<<8 | uint16(data[1]) + s.cipherSuite = uint16(data[2])<<8 | uint16(data[3]) + masterSecretLen := int(data[4])<<8 | int(data[5]) + data = data[6:] + if len(data) < masterSecretLen { + return false + } + + s.masterSecret = data[:masterSecretLen] + data = data[masterSecretLen:] + + if len(data) < 2 { + return false + } + + numCerts := int(data[0])<<8 | int(data[1]) + data = data[2:] + + s.certificates = make([][]byte, numCerts) + for i := range s.certificates { + if len(data) < 4 { + return false + } + certLen := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + data = data[4:] + if certLen < 0 { + return false + } + if len(data) < certLen { + return false + } + s.certificates[i] = data[:certLen] + data = data[certLen:] + } + + if len(data) > 0 { + return false + } + + return true +} + +func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) { + serialized := state.marshal() + encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size) + iv := encrypted[:aes.BlockSize] + macBytes := encrypted[len(encrypted)-sha256.Size:] + + if _, err := io.ReadFull(c.config.rand(), iv); err != nil { + return nil, err + } + block, err := aes.NewCipher(c.config.SessionTicketKey[:16]) + if err != nil { + return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error()) + } + cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized) + + mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32]) + mac.Write(encrypted[:len(encrypted)-sha256.Size]) + mac.Sum(macBytes[:0]) + + return encrypted, nil +} + +func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) { + if len(encrypted) < aes.BlockSize+sha256.Size { + return nil, false + } + + iv := encrypted[:aes.BlockSize] + macBytes := encrypted[len(encrypted)-sha256.Size:] + + mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32]) + mac.Write(encrypted[:len(encrypted)-sha256.Size]) + expected := mac.Sum(nil) + + if subtle.ConstantTimeCompare(macBytes, expected) != 1 { + return nil, false + } + + block, err := aes.NewCipher(c.config.SessionTicketKey[:16]) + if err != nil { + return nil, false + } + ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size] + plaintext := ciphertext + cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext) + + state := new(sessionState) + ok := state.unmarshal(plaintext) + return state, ok +} diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index 09df5ad44..9230656d6 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -6,6 +6,8 @@ package tls import ( + "crypto" + "crypto/ecdsa" "crypto/rsa" "crypto/x509" "encoding/pem" @@ -146,30 +148,22 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) return } - keyDERBlock, _ := pem.Decode(keyPEMBlock) - if keyDERBlock == nil { - err = errors.New("crypto/tls: failed to parse key PEM data") - return - } - - // OpenSSL 0.9.8 generates PKCS#1 private keys by default, while - // OpenSSL 1.0.0 generates PKCS#8 keys. We try both. - var key *rsa.PrivateKey - if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil { - var privKey interface{} - if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil { - err = errors.New("crypto/tls: failed to parse key: " + err.Error()) + var keyDERBlock *pem.Block + for { + keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock) + if keyDERBlock == nil { + err = errors.New("crypto/tls: failed to parse key PEM data") return } - - var ok bool - if key, ok = privKey.(*rsa.PrivateKey); !ok { - err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping") - return + if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { + break } } - cert.PrivateKey = key + cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) + if err != nil { + return + } // We don't need to parse the public key for TLS, but we so do anyway // to check that it looks sane and matches the private key. @@ -178,10 +172,54 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) return } - if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 { - err = errors.New("crypto/tls: private key does not match public key") + switch pub := x509Cert.PublicKey.(type) { + case *rsa.PublicKey: + priv, ok := cert.PrivateKey.(*rsa.PrivateKey) + if !ok { + err = errors.New("crypto/tls: private key type does not match public key type") + return + } + if pub.N.Cmp(priv.N) != 0 { + err = errors.New("crypto/tls: private key does not match public key") + return + } + case *ecdsa.PublicKey: + priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey) + if !ok { + err = errors.New("crypto/tls: private key type does not match public key type") + return + + } + if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 { + err = errors.New("crypto/tls: private key does not match public key") + return + } + default: + err = errors.New("crypto/tls: unknown public key algorithm") return } return } + +// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates +// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys. +// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. +func parsePrivateKey(der []byte) (crypto.PrivateKey, error) { + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey: + return key, nil + default: + return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping") + } + } + if key, err := x509.ParseECPrivateKey(der); err == nil { + return key, nil + } + + return nil, errors.New("crypto/tls: failed to parse private key") +} diff --git a/src/pkg/crypto/tls/tls_test.go b/src/pkg/crypto/tls/tls_test.go new file mode 100644 index 000000000..38229014c --- /dev/null +++ b/src/pkg/crypto/tls/tls_test.go @@ -0,0 +1,107 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "testing" +) + +var rsaCertPEM = `-----BEGIN CERTIFICATE----- +MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ +hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa +rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv +zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW +r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V +-----END CERTIFICATE----- +` + +var rsaKeyPEM = `-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo +k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G +6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N +MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW +SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T +xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi +D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g== +-----END RSA PRIVATE KEY----- +` + +// keyPEM is the same as rsaKeyPEM, but declares itself as just +// "PRIVATE KEY", not "RSA PRIVATE KEY". http://golang.org/issue/4477 +var keyPEM = `-----BEGIN PRIVATE KEY----- +MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo +k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G +6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N +MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW +SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T +xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi +D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g== +-----END PRIVATE KEY----- +` + +var ecdsaCertPEM = `-----BEGIN CERTIFICATE----- +MIIB/jCCAWICCQDscdUxw16XFDAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw +EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQwHhcNMTIxMTE0MTI0MDQ4WhcNMTUxMTE0MTI0MDQ4WjBFMQswCQYDVQQG +EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lk +Z2l0cyBQdHkgTHRkMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBY9+my9OoeSUR +lDQdV/x8LsOuLilthhiS1Tz4aGDHIPwC1mlvnf7fg5lecYpMCrLLhauAc1UJXcgl +01xoLuzgtAEAgv2P/jgytzRSpUYvgLBt1UA0leLYBy6mQQbrNEuqT3INapKIcUv8 +XxYP0xMEUksLPq6Ca+CRSqTtrd/23uTnapkwCQYHKoZIzj0EAQOBigAwgYYCQXJo +A7Sl2nLVf+4Iu/tAX/IF4MavARKC4PPHK3zfuGfPR3oCCcsAoz3kAzOeijvd0iXb +H5jBImIxPL4WxQNiBTexAkF8D1EtpYuWdlVQ80/h/f4pBcGiXPqX5h2PQSQY7hP1 ++jwM1FGS4fREIOvlBYr/SzzQRtwrvrzGYxDEDbsC0ZGRnA== +-----END CERTIFICATE----- +` + +var ecdsaKeyPEM = `-----BEGIN EC PARAMETERS----- +BgUrgQQAIw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBrsoKp0oqcv6/JovJJDoDVSGWdirrkgCWxrprGlzB9o0X8fV675X0 +NwuBenXFfeZvVcwluO7/Q9wkYoPd/t3jGImgBwYFK4EEACOhgYkDgYYABAFj36bL +06h5JRGUNB1X/Hwuw64uKW2GGJLVPPhoYMcg/ALWaW+d/t+DmV5xikwKssuFq4Bz +VQldyCXTXGgu7OC0AQCC/Y/+ODK3NFKlRi+AsG3VQDSV4tgHLqZBBus0S6pPcg1q +kohxS/xfFg/TEwRSSws+roJr4JFKpO2t3/be5OdqmQ== +-----END EC PRIVATE KEY----- +` + +var keyPairTests = []struct { + algo string + cert string + key string +}{ + {"ECDSA", ecdsaCertPEM, ecdsaKeyPEM}, + {"RSA", rsaCertPEM, rsaKeyPEM}, + {"RSA-untyped", rsaCertPEM, keyPEM}, // golang.org/issue/4477 +} + +func TestX509KeyPair(t *testing.T) { + var pem []byte + for _, test := range keyPairTests { + pem = []byte(test.cert + test.key) + if _, err := X509KeyPair(pem, pem); err != nil { + t.Errorf("Failed to load %s cert followed by %s key: %s", test.algo, test.algo, err) + } + pem = []byte(test.key + test.cert) + if _, err := X509KeyPair(pem, pem); err != nil { + t.Errorf("Failed to load %s key followed by %s cert: %s", test.algo, test.algo, err) + } + } +} + +func TestX509MixedKeyPair(t *testing.T) { + if _, err := X509KeyPair([]byte(rsaCertPEM), []byte(ecdsaKeyPEM)); err == nil { + t.Error("Load of RSA certificate succeeded with ECDSA private key") + } + if _, err := X509KeyPair([]byte(ecdsaCertPEM), []byte(rsaKeyPEM)); err == nil { + t.Error("Load of ECDSA certificate succeeded with RSA private key") + } +} diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go index 616a0b3c1..505f4d4f7 100644 --- a/src/pkg/crypto/x509/cert_pool.go +++ b/src/pkg/crypto/x509/cert_pool.go @@ -103,7 +103,7 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { } // Subjects returns a list of the DER-encoded subjects of -// all of the certificates in the pool. +// all of the certificates in the pool. func (s *CertPool) Subjects() (res [][]byte) { res = make([][]byte, len(s.certs)) for i, c := range s.certs { diff --git a/src/pkg/crypto/x509/pem_decrypt.go b/src/pkg/crypto/x509/pem_decrypt.go new file mode 100644 index 000000000..194c81bf6 --- /dev/null +++ b/src/pkg/crypto/x509/pem_decrypt.go @@ -0,0 +1,233 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +// RFC 1423 describes the encryption of PEM blocks. The algorithm used to +// generate a key from the password was derived by looking at the OpenSSL +// implementation. + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/md5" + "encoding/hex" + "encoding/pem" + "errors" + "io" + "strings" +) + +type PEMCipher int + +// Possible values for the EncryptPEMBlock encryption algorithm. +const ( + _ PEMCipher = iota + PEMCipherDES + PEMCipher3DES + PEMCipherAES128 + PEMCipherAES192 + PEMCipherAES256 +) + +// rfc1423Algo holds a method for enciphering a PEM block. +type rfc1423Algo struct { + cipher PEMCipher + name string + cipherFunc func(key []byte) (cipher.Block, error) + keySize int + blockSize int +} + +// rfc1423Algos holds a slice of the possible ways to encrypt a PEM +// block. The ivSize numbers were taken from the OpenSSL source. +var rfc1423Algos = []rfc1423Algo{{ + cipher: PEMCipherDES, + name: "DES-CBC", + cipherFunc: des.NewCipher, + keySize: 8, + blockSize: des.BlockSize, +}, { + cipher: PEMCipher3DES, + name: "DES-EDE3-CBC", + cipherFunc: des.NewTripleDESCipher, + keySize: 24, + blockSize: des.BlockSize, +}, { + cipher: PEMCipherAES128, + name: "AES-128-CBC", + cipherFunc: aes.NewCipher, + keySize: 16, + blockSize: aes.BlockSize, +}, { + cipher: PEMCipherAES192, + name: "AES-192-CBC", + cipherFunc: aes.NewCipher, + keySize: 24, + blockSize: aes.BlockSize, +}, { + cipher: PEMCipherAES256, + name: "AES-256-CBC", + cipherFunc: aes.NewCipher, + keySize: 32, + blockSize: aes.BlockSize, +}, +} + +// deriveKey uses a key derivation function to stretch the password into a key +// with the number of bits our cipher requires. This algorithm was derived from +// the OpenSSL source. +func (c rfc1423Algo) deriveKey(password, salt []byte) []byte { + hash := md5.New() + out := make([]byte, c.keySize) + var digest []byte + + for i := 0; i < len(out); i += len(digest) { + hash.Reset() + hash.Write(digest) + hash.Write(password) + hash.Write(salt) + digest = hash.Sum(digest[:0]) + copy(out[i:], digest) + } + return out +} + +// IsEncryptedPEMBlock returns if the PEM block is password encrypted. +func IsEncryptedPEMBlock(b *pem.Block) bool { + _, ok := b.Headers["DEK-Info"] + return ok +} + +// IncorrectPasswordError is returned when an incorrect password is detected. +var IncorrectPasswordError = errors.New("x509: decryption password incorrect") + +// DecryptPEMBlock takes a password encrypted PEM block and the password used to +// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects +// the DEK-Info header to determine the algorithm used for decryption. If no +// DEK-Info header is present, an error is returned. If an incorrect password +// is detected an IncorrectPasswordError is returned. +func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { + dek, ok := b.Headers["DEK-Info"] + if !ok { + return nil, errors.New("x509: no DEK-Info header in block") + } + + idx := strings.Index(dek, ",") + if idx == -1 { + return nil, errors.New("x509: malformed DEK-Info header") + } + + mode, hexIV := dek[:idx], dek[idx+1:] + ciph := cipherByName(mode) + if ciph == nil { + return nil, errors.New("x509: unknown encryption mode") + } + iv, err := hex.DecodeString(hexIV) + if err != nil { + return nil, err + } + if len(iv) != ciph.blockSize { + return nil, errors.New("x509: incorrect IV size") + } + + // Based on the OpenSSL implementation. The salt is the first 8 bytes + // of the initialization vector. + key := ciph.deriveKey(password, iv[:8]) + block, err := ciph.cipherFunc(key) + if err != nil { + return nil, err + } + + data := make([]byte, len(b.Bytes)) + dec := cipher.NewCBCDecrypter(block, iv) + dec.CryptBlocks(data, b.Bytes) + + // Blocks are padded using a scheme where the last n bytes of padding are all + // equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423. + // For example: + // [x y z 2 2] + // [x y 7 7 7 7 7 7 7] + // If we detect a bad padding, we assume it is an invalid password. + dlen := len(data) + if dlen == 0 || dlen%ciph.blockSize != 0 { + return nil, errors.New("x509: invalid padding") + } + last := int(data[dlen-1]) + if dlen < last { + return nil, IncorrectPasswordError + } + if last == 0 || last > ciph.blockSize { + return nil, IncorrectPasswordError + } + for _, val := range data[dlen-last:] { + if int(val) != last { + return nil, IncorrectPasswordError + } + } + return data[:dlen-last], nil +} + +// EncryptPEMBlock returns a PEM block of the specified type holding the +// given DER-encoded data encrypted with the specified algorithm and +// password. +func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) { + ciph := cipherByKey(alg) + if ciph == nil { + return nil, errors.New("x509: unknown encryption mode") + } + iv := make([]byte, ciph.blockSize) + if _, err := io.ReadFull(rand, iv); err != nil { + return nil, errors.New("x509: cannot generate IV: " + err.Error()) + } + // The salt is the first 8 bytes of the initialization vector, + // matching the key derivation in DecryptPEMBlock. + key := ciph.deriveKey(password, iv[:8]) + block, err := ciph.cipherFunc(key) + if err != nil { + return nil, err + } + enc := cipher.NewCBCEncrypter(block, iv) + pad := ciph.blockSize - len(data)%ciph.blockSize + encrypted := make([]byte, len(data), len(data)+pad) + // We could save this copy by encrypting all the whole blocks in + // the data separately, but it doesn't seem worth the additional + // code. + copy(encrypted, data) + // See RFC 1423, section 1.1 + for i := 0; i < pad; i++ { + encrypted = append(encrypted, byte(pad)) + } + enc.CryptBlocks(encrypted, encrypted) + + return &pem.Block{ + Type: blockType, + Headers: map[string]string{ + "Proc-Type": "4,ENCRYPTED", + "DEK-Info": ciph.name + "," + hex.EncodeToString(iv), + }, + Bytes: encrypted, + }, nil +} + +func cipherByName(name string) *rfc1423Algo { + for i := range rfc1423Algos { + alg := &rfc1423Algos[i] + if alg.name == name { + return alg + } + } + return nil +} + +func cipherByKey(key PEMCipher) *rfc1423Algo { + for i := range rfc1423Algos { + alg := &rfc1423Algos[i] + if alg.cipher == key { + return alg + } + } + return nil +} diff --git a/src/pkg/crypto/x509/pem_decrypt_test.go b/src/pkg/crypto/x509/pem_decrypt_test.go new file mode 100644 index 000000000..59ba6f900 --- /dev/null +++ b/src/pkg/crypto/x509/pem_decrypt_test.go @@ -0,0 +1,223 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "bytes" + "crypto/rand" + "encoding/base64" + "encoding/pem" + "testing" +) + +func TestDecrypt(t *testing.T) { + for i, data := range testData { + t.Logf("test %d. %s", i, data.kind) + block, rest := pem.Decode(data.pemData) + if len(rest) > 0 { + t.Error("extra data") + } + der, err := DecryptPEMBlock(block, data.password) + if err != nil { + t.Error("decrypt failed: ", err) + continue + } + if _, err := ParsePKCS1PrivateKey(der); err != nil { + t.Error("invalid private key: ", err) + } + plainDER, err := base64.StdEncoding.DecodeString(data.plainDER) + if err != nil { + t.Fatal("cannot decode test DER data: ", err) + } + if !bytes.Equal(der, plainDER) { + t.Error("data mismatch") + } + } +} + +func TestEncrypt(t *testing.T) { + for i, data := range testData { + t.Logf("test %d. %s", i, data.kind) + plainDER, err := base64.StdEncoding.DecodeString(data.plainDER) + if err != nil { + t.Fatal("cannot decode test DER data: ", err) + } + password := []byte("kremvax1") + block, err := EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", plainDER, password, data.kind) + if err != nil { + t.Error("encrypt: ", err) + continue + } + if !IsEncryptedPEMBlock(block) { + t.Error("PEM block does not appear to be encrypted") + } + if block.Type != "RSA PRIVATE KEY" { + t.Errorf("unexpected block type; got %q want %q", block.Type, "RSA PRIVATE KEY") + } + if block.Headers["Proc-Type"] != "4,ENCRYPTED" { + t.Errorf("block does not have correct Proc-Type header") + } + der, err := DecryptPEMBlock(block, password) + if err != nil { + t.Error("decrypt: ", err) + continue + } + if !bytes.Equal(der, plainDER) { + t.Errorf("data mismatch") + } + } +} + +var testData = []struct { + kind PEMCipher + password []byte + pemData []byte + plainDER string +}{ + { + kind: PEMCipherDES, + password: []byte("asdf"), + pemData: []byte(` +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-CBC,34F09A4FC8DE22B5 + +WXxy8kbZdiZvANtKvhmPBLV7eVFj2A5z6oAxvI9KGyhG0ZK0skfnt00C24vfU7m5 +ICXeoqP67lzJ18xCzQfHjDaBNs53DSDT+Iz4e8QUep1xQ30+8QKX2NA2coee3nwc +6oM1cuvhNUDemBH2i3dKgMVkfaga0zQiiOq6HJyGSncCMSruQ7F9iWEfRbFcxFCx +qtHb1kirfGKEtgWTF+ynyco6+2gMXNu70L7nJcnxnV/RLFkHt7AUU1yrclxz7eZz +XOH9VfTjb52q/I8Suozq9coVQwg4tXfIoYUdT//O+mB7zJb9HI9Ps77b9TxDE6Gm +4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD +r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug== +-----END RSA PRIVATE KEY-----`), + plainDER: ` +MIIBPAIBAAJBAPASZe+tCPU6p80AjHhDkVsLYa51D35e/YGa8QcZyooeZM8EHozo +KD0fNiKI+53bHdy07N+81VQ8/ejPcRoXPlsCAwEAAQJBAMTxIuSq27VpR+zZ7WJf +c6fvv1OBvpMZ0/d1pxL/KnOAgq2rD5hDtk9b0LGhTPgQAmrrMTKuSeGoIuYE+gKQ +QvkCIQD+GC1m+/do+QRurr0uo46Kx1LzLeSCrjBk34wiOp2+dwIhAPHfTLRXS2fv +7rljm0bYa4+eDZpz+E8RcXEgzhhvcQQ9AiAI5eHZJGOyml3MXnQjiPi55WcDOw0w +glcRgT6QCEtz2wIhANSyqaFtosIkHKqrDUGfz/bb5tqMYTAnBruVPaf/WEOBAiEA +9xORWeRG1tRpso4+dYy4KdDkuLPIO01KY6neYGm3BCM=`, + }, + { + kind: PEMCipher3DES, + password: []byte("asdf"), + pemData: []byte(` +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,C1F4A6A03682C2C7 + +0JqVdBEH6iqM7drTkj+e2W/bE3LqakaiWhb9WUVonFkhyu8ca/QzebY3b5gCvAZQ +YwBvDcT/GHospKqPx+cxDHJNsUASDZws6bz8ZXWJGwZGExKzr0+Qx5fgXn44Ms3x +8g1ENFuTXtxo+KoNK0zuAMAqp66Llcds3Fjl4XR18QaD0CrVNAfOdgATWZm5GJxk +Fgx5f84nT+/ovvreG+xeOzWgvtKo0UUZVrhGOgfKLpa57adumcJ6SkUuBtEFpZFB +ldw5w7WC7d13x2LsRkwo8ZrDKgIV+Y9GNvhuCCkTzNP0V3gNeJpd201HZHR+9n3w +3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq +gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk= +-----END RSA PRIVATE KEY-----`), + plainDER: ` +MIIBOwIBAAJBANOCXKdoNS/iP/MAbl9cf1/SF3P+Ns7ZeNL27CfmDh0O6Zduaax5 +NBiumd2PmjkaCu7lQ5JOibHfWn+xJsc3kw0CAwEAAQJANX/W8d1Q/sCqzkuAn4xl +B5a7qfJWaLHndu1QRLNTRJPn0Ee7OKJ4H0QKOhQM6vpjRrz+P2u9thn6wUxoPsef +QQIhAP/jCkfejFcy4v15beqKzwz08/tslVjF+Yq41eJGejmxAiEA05pMoqfkyjcx +fyvGhpoOyoCp71vSGUfR2I9CR65oKh0CIC1Msjs66LlfJtQctRq6bCEtFCxEcsP+ +eEjYo/Sk6WphAiEAxpgWPMJeU/shFT28gS+tmhjPZLpEoT1qkVlC14u0b3ECIQDX +tZZZxCtPAm7shftEib0VU77Lk8MsXJcx2C4voRsjEw==`, + }, + { + kind: PEMCipherAES128, + password: []byte("asdf"), + pemData: []byte(` +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,D4492E793FC835CC038A728ED174F78A + +EyfQSzXSjv6BaNH+NHdXRlkHdimpF9izWlugVJAPApgXrq5YldPe2aGIOFXyJ+QE +ZIG20DYqaPzJRjTEbPNZ6Es0S2JJ5yCpKxwJuDkgJZKtF39Q2i36JeGbSZQIuWJE +GZbBpf1jDH/pr0iGonuAdl2PCCZUiy+8eLsD2tyviHUkFLOB+ykYoJ5t8ngZ/B6D +33U43LLb7+9zD4y3Q9OVHqBFGyHcxCY9+9Qh4ZnFp7DTf6RY5TNEvE3s4g6aDpBs +3NbvRVvYTgs8K9EPk4K+5R+P2kD8J8KvEIGxVa1vz8QoCJ/jr7Ka2rvNgPCex5/E +080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo +AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0= +-----END RSA PRIVATE KEY-----`), + plainDER: ` +MIIBOgIBAAJBAMBlj5FxYtqbcy8wY89d/S7n0+r5MzD9F63BA/Lpl78vQKtdJ5dT +cDGh/rBt1ufRrNp0WihcmZi7Mpl/3jHjiWECAwEAAQJABNOHYnKhtDIqFYj1OAJ3 +k3GlU0OlERmIOoeY/cL2V4lgwllPBEs7r134AY4wMmZSBUj8UR/O4SNO668ElKPE +cQIhAOuqY7/115x5KCdGDMWi+jNaMxIvI4ETGwV40ykGzqlzAiEA0P9oEC3m9tHB +kbpjSTxaNkrXxDgdEOZz8X0uOUUwHNsCIAwzcSCiGLyYJTULUmP1ESERfW1mlV78 +XzzESaJpIM/zAiBQkSTcl9VhcJreQqvjn5BnPZLP4ZHS4gPwJAGdsj5J4QIhAOVR +B3WlRNTXR2WsJ5JdByezg9xzdXzULqmga0OE339a`, + }, + { + kind: PEMCipherAES192, + password: []byte("asdf"), + pemData: []byte(` +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-192-CBC,E2C9FB02BCA23ADE1829F8D8BC5F5369 + +cqVslvHqDDM6qwU6YjezCRifXmKsrgEev7ng6Qs7UmDJOpHDgJQZI9fwMFUhIyn5 +FbCu1SHkLMW52Ld3CuEqMnzWMlhPrW8tFvUOrMWPYSisv7nNq88HobZEJcUNL2MM +Y15XmHW6IJwPqhKyLHpWXyOCVEh4ODND2nV15PCoi18oTa475baxSk7+1qH7GuIs +Rb7tshNTMqHbCpyo9Rn3UxeFIf9efdl8YLiMoIqc7J8E5e9VlbeQSdLMQOgDAQJG +ReUtTw8exmKsY4gsSjhkg5uiw7/ZB1Ihto0qnfQJgjGc680qGkT1d6JfvOfeYAk6 +xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t +Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw= +-----END RSA PRIVATE KEY-----`), + plainDER: ` +MIIBOwIBAAJBAMGcRrZiNNmtF20zyS6MQ7pdGx17aFDl+lTl+qnLuJRUCMUG05xs +OmxmL/O1Qlf+bnqR8Bgg65SfKg21SYuLhiMCAwEAAQJBAL94uuHyO4wux2VC+qpj +IzPykjdU7XRcDHbbvksf4xokSeUFjjD3PB0Qa83M94y89ZfdILIqS9x5EgSB4/lX +qNkCIQD6cCIqLfzq/lYbZbQgAAjpBXeQVYsbvVtJrPrXJAlVVQIhAMXpDKMeFPMn +J0g2rbx1gngx0qOa5r5iMU5w/noN4W2XAiBjf+WzCG5yFvazD+dOx3TC0A8+4x3P +uZ3pWbaXf5PNuQIgAcdXarvhelH2w2piY1g3BPeFqhzBSCK/yLGxR82KIh8CIQDD ++qGKsd09NhQ/G27y/DARzOYtml1NvdmCQAgsDIIOLA==`, + }, + { + kind: PEMCipherAES256, + password: []byte("asdf"), + pemData: []byte(` +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,8E7ED5CD731902CE938957A886A5FFBD + +4Mxr+KIzRVwoOP0wwq6caSkvW0iS+GE2h2Ov/u+n9ZTMwL83PRnmjfjzBgfRZLVf +JFPXxUK26kMNpIdssNnqGOds+DhB+oSrsNKoxgxSl5OBoYv9eJTVYm7qOyAFIsjr +DRKAcjYCmzfesr7PVTowwy0RtHmYwyXMGDlAzzZrEvaiySFFmMyKKvtoavwaFoc7 +Pz3RZScwIuubzTGJ1x8EzdffYOsdCa9Mtgpp3L136+23dOd6L/qK2EG2fzrJSHs/ +2XugkleBFSMKzEp9mxXKRfa++uidQvMZTFLDK9w5YjrRvMBo/l2BoZIsq0jAIE1N +sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk +clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0= +-----END RSA PRIVATE KEY-----`), + plainDER: ` +MIIBOgIBAAJBAKy3GFkstoCHIEeUU/qO8207m8WSrjksR+p9B4tf1w5k+2O1V/GY +AQ5WFCApItcOkQe/I0yZZJk/PmCqMzSxrc8CAwEAAQJAOCAz0F7AW9oNelVQSP8F +Sfzx7O1yom+qWyAQQJF/gFR11gpf9xpVnnyu1WxIRnDUh1LZwUsjwlDYb7MB74id +oQIhANPcOiLwOPT4sIUpRM5HG6BF1BI7L77VpyGVk8xNP7X/AiEA0LMHZtk4I+lJ +nClgYp4Yh2JZ1Znbu7IoQMCEJCjwKDECIGd8Dzm5tViTkUW6Hs3Tlf73nNs65duF +aRnSglss8I3pAiEAonEnKruawgD8RavDFR+fUgmQiPz4FnGGeVgfwpGG1JECIBYq +PXHYtPqxQIbD2pScR5qum7iGUh11lEUPkmt+2uqS`, + }, + { + // generated with: + // openssl genrsa -aes128 -passout pass:asdf -out server.orig.key 128 + kind: PEMCipherAES128, + password: []byte("asdf"), + pemData: []byte(` +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7 + +6ei/MlytjE0FFgZOGQ+jrwomKfpl8kdefeE0NSt/DMRrw8OacHAzBNi3pPEa0eX3 +eND9l7C9meCirWovjj9QWVHrXyugFuDIqgdhQ8iHTgCfF3lrmcttVrbIfMDw+smD +hTP8O1mS/MHl92NE0nhv0w== +-----END RSA PRIVATE KEY-----`), + plainDER: ` +MGMCAQACEQC6ssxmYuauuHGOCDAI54RdAgMBAAECEQCWIn6Yv2O+kBcDF7STctKB +AgkA8SEfu/2i3g0CCQDGNlXbBHX7kQIIK3Ww5o0cYbECCQDCimPb0dYGsQIIeQ7A +jryIst8=`, + }, +} diff --git a/src/pkg/crypto/x509/pkcs8.go b/src/pkg/crypto/x509/pkcs8.go index 4d8e0518e..30caacb3c 100644 --- a/src/pkg/crypto/x509/pkcs8.go +++ b/src/pkg/crypto/x509/pkcs8.go @@ -11,8 +11,9 @@ import ( "fmt" ) -// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See -// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn. +// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See +// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn +// and RFC5208. type pkcs8 struct { Version int Algo pkix.AlgorithmIdentifier @@ -21,19 +22,32 @@ type pkcs8 struct { } // ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See -// http://www.rsa.com/rsalabs/node.asp?id=2130 +// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208. func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { var privKey pkcs8 if _, err := asn1.Unmarshal(der, &privKey); err != nil { return nil, err } switch { - case privKey.Algo.Algorithm.Equal(oidRSA): + case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA): key, err = ParsePKCS1PrivateKey(privKey.PrivateKey) if err != nil { return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) } return key, nil + + case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA): + bytes := privKey.Algo.Parameters.FullBytes + namedCurveOID := new(asn1.ObjectIdentifier) + if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil { + namedCurveOID = nil + } + key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey) + if err != nil { + return nil, errors.New("crypto/x509: failed to parse EC private key embedded in PKCS#8: " + err.Error()) + } + return key, nil + default: return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) } diff --git a/src/pkg/crypto/x509/pkcs8_test.go b/src/pkg/crypto/x509/pkcs8_test.go index 372005f90..4114efd0e 100644 --- a/src/pkg/crypto/x509/pkcs8_test.go +++ b/src/pkg/crypto/x509/pkcs8_test.go @@ -9,12 +9,20 @@ import ( "testing" ) -var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` +var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` + +// Generated using: +// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt +var pkcs8ECPrivateKeyHex = `3081ed020100301006072a8648ce3d020106052b810400230481d53081d20201010441850d81618c5da1aec74c2eed608ba816038506975e6427237c2def150c96a3b13efbfa1f89f1be15cdf4d0ac26422e680e65a0ddd4ad3541ad76165fbf54d6e34ba18189038186000400da97bcedba1eb6d30aeb93c9f9a1454598fa47278df27d6f60ea73eb672d8dc528a9b67885b5b5dcef93c9824f7449ab512ee6a27e76142f56b94b474cfd697e810046c8ca70419365245c1d7d44d0db82c334073835d002232714548abbae6e5700f5ef315ee08b929d8581383dcf2d1c98c2f8a9fccbf79c9579f7b2fd8a90115ac2` func TestPKCS8(t *testing.T) { - derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex) - _, err := ParsePKCS8PrivateKey(derBytes) - if err != nil { - t.Errorf("failed to decode PKCS8 key: %s", err) + derBytes, _ := hex.DecodeString(pkcs8RSAPrivateKeyHex) + if _, err := ParsePKCS8PrivateKey(derBytes); err != nil { + t.Errorf("failed to decode PKCS8 with RSA private key: %s", err) + } + + derBytes, _ = hex.DecodeString(pkcs8ECPrivateKeyHex) + if _, err := ParsePKCS8PrivateKey(derBytes); err != nil { + t.Errorf("failed to decode PKCS8 with EC private key: %s", err) } } diff --git a/src/pkg/crypto/x509/root_darwin.go b/src/pkg/crypto/x509/root_darwin.go index 0f99581e8..ad3bfb4b4 100644 --- a/src/pkg/crypto/x509/root_darwin.go +++ b/src/pkg/crypto/x509/root_darwin.go @@ -70,11 +70,12 @@ func initSystemRoots() { var data C.CFDataRef = nil err := C.FetchPEMRoots(&data) - if err != -1 { - defer C.CFRelease(C.CFTypeRef(data)) - buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data))) - roots.AppendCertsFromPEM(buf) + if err == -1 { + return } + defer C.CFRelease(C.CFTypeRef(data)) + buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data))) + roots.AppendCertsFromPEM(buf) systemRoots = roots } diff --git a/src/pkg/crypto/x509/root_plan9.go b/src/pkg/crypto/x509/root_plan9.go new file mode 100644 index 000000000..9965caade --- /dev/null +++ b/src/pkg/crypto/x509/root_plan9.go @@ -0,0 +1,33 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build plan9 + +package x509 + +import "io/ioutil" + +// Possible certificate files; stop after finding one. +var certFiles = []string{ + "/sys/lib/tls/ca.pem", +} + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} + +func initSystemRoots() { + roots := NewCertPool() + for _, file := range certFiles { + data, err := ioutil.ReadFile(file) + if err == nil { + roots.AppendCertsFromPEM(data) + systemRoots = roots + return + } + } + + // All of the files failed to load. systemRoots will be nil which will + // trigger a specific error at verification time. +} diff --git a/src/pkg/crypto/x509/root_stub.go b/src/pkg/crypto/x509/root_stub.go index 568004108..4c742ccc3 100644 --- a/src/pkg/crypto/x509/root_stub.go +++ b/src/pkg/crypto/x509/root_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build plan9 darwin,!cgo +// +build darwin,!cgo package x509 @@ -11,5 +11,4 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate } func initSystemRoots() { - systemRoots = NewCertPool() } diff --git a/src/pkg/crypto/x509/root_unix.go b/src/pkg/crypto/x509/root_unix.go index 76e79f494..1b25a94d0 100644 --- a/src/pkg/crypto/x509/root_unix.go +++ b/src/pkg/crypto/x509/root_unix.go @@ -27,9 +27,11 @@ func initSystemRoots() { data, err := ioutil.ReadFile(file) if err == nil { roots.AppendCertsFromPEM(data) - break + systemRoots = roots + return } } - systemRoots = roots + // All of the files failed to load. systemRoots will be nil which will + // trigger a specific error at verification time. } diff --git a/src/pkg/crypto/x509/root_windows.go b/src/pkg/crypto/x509/root_windows.go index 7e8f2af4b..e8f70a49d 100644 --- a/src/pkg/crypto/x509/root_windows.go +++ b/src/pkg/crypto/x509/root_windows.go @@ -98,9 +98,13 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for // use as a certificate chain for a SSL/TLS server. func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error { + servernamep, err := syscall.UTF16PtrFromString(opts.DNSName) + if err != nil { + return err + } sslPara := &syscall.SSLExtraCertChainPolicyPara{ AuthType: syscall.AUTHTYPE_SERVER, - ServerName: syscall.StringToUTF16Ptr(opts.DNSName), + ServerName: servernamep, } sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) @@ -110,7 +114,7 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex para.Size = uint32(unsafe.Sizeof(*para)) status := syscall.CertChainPolicyStatus{} - err := syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status) + err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status) if err != nil { return err } @@ -222,5 +226,4 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate } func initSystemRoots() { - systemRoots = NewCertPool() } diff --git a/src/pkg/crypto/x509/sec1.go b/src/pkg/crypto/x509/sec1.go new file mode 100644 index 000000000..8a2840fbe --- /dev/null +++ b/src/pkg/crypto/x509/sec1.go @@ -0,0 +1,69 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "encoding/asn1" + "errors" + "fmt" + "math/big" +) + +const ecPrivKeyVersion = 1 + +// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure. +// References: +// RFC5915 +// SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf +// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in +// most cases it is not. +type ecPrivateKey struct { + Version int + PrivateKey []byte + NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"` + PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"` +} + +// ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure. +func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) { + return parseECPrivateKey(nil, der) +} + +// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure. +// The OID for the named curve may be provided from another source (such as +// the PKCS8 container) - if it is provided then use this instead of the OID +// that may exist in the EC private key structure. +func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) { + var privKey ecPrivateKey + if _, err := asn1.Unmarshal(der, &privKey); err != nil { + return nil, errors.New("crypto/x509: failed to parse EC private key: " + err.Error()) + } + if privKey.Version != ecPrivKeyVersion { + return nil, fmt.Errorf("crypto/x509: unknown EC private key version %d", privKey.Version) + } + + var curve elliptic.Curve + if namedCurveOID != nil { + curve = namedCurveFromOID(*namedCurveOID) + } else { + curve = namedCurveFromOID(privKey.NamedCurveOID) + } + if curve == nil { + return nil, errors.New("crypto/x509: unknown elliptic curve") + } + + k := new(big.Int).SetBytes(privKey.PrivateKey) + if k.Cmp(curve.Params().N) >= 0 { + return nil, errors.New("crypto/x509: invalid elliptic curve private key value") + } + priv := new(ecdsa.PrivateKey) + priv.Curve = curve + priv.D = k + priv.X, priv.Y = curve.ScalarBaseMult(privKey.PrivateKey) + + return priv, nil +} diff --git a/src/pkg/crypto/x509/sec1_test.go b/src/pkg/crypto/x509/sec1_test.go new file mode 100644 index 000000000..7135699d2 --- /dev/null +++ b/src/pkg/crypto/x509/sec1_test.go @@ -0,0 +1,22 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "encoding/hex" + "testing" +) + +// Generated using: +// openssl ecparam -genkey -name secp384r1 -outform PEM +var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50` + +func TestParseECPrivateKey(t *testing.T) { + derBytes, _ := hex.DecodeString(ecPrivateKeyHex) + _, err := ParseECPrivateKey(derBytes) + if err != nil { + t.Errorf("failed to decode EC private key: %s", err) + } +} diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go index 307c5ef03..b29ddbc80 100644 --- a/src/pkg/crypto/x509/verify.go +++ b/src/pkg/crypto/x509/verify.go @@ -5,6 +5,7 @@ package x509 import ( + "net" "runtime" "strings" "time" @@ -27,6 +28,9 @@ const ( // TooManyIntermediates results when a path length constraint is // violated. TooManyIntermediates + // IncompatibleUsage results when the certificate's key usage indicates + // that it may only be used for a different purpose. + IncompatibleUsage ) // CertificateInvalidError results when an odd error occurs. Users of this @@ -39,13 +43,15 @@ type CertificateInvalidError struct { func (e CertificateInvalidError) Error() string { switch e.Reason { case NotAuthorizedToSign: - return "x509: certificate is not authorized to sign other other certificates" + return "x509: certificate is not authorized to sign other certificates" case Expired: return "x509: certificate has expired or is not yet valid" case CANotAuthorizedForThisName: return "x509: a root or intermediate certificate is not authorized to sign in this domain" case TooManyIntermediates: return "x509: too many intermediates for path length constraint" + case IncompatibleUsage: + return "x509: certificate specifies an incompatible key usage" } return "x509: unknown error" } @@ -58,14 +64,28 @@ type HostnameError struct { } func (h HostnameError) Error() string { - var valid string c := h.Certificate - if len(c.DNSNames) > 0 { - valid = strings.Join(c.DNSNames, ", ") + + var valid string + if ip := net.ParseIP(h.Host); ip != nil { + // Trying to validate an IP + if len(c.IPAddresses) == 0 { + return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs" + } + for _, san := range c.IPAddresses { + if len(valid) > 0 { + valid += ", " + } + valid += san.String() + } } else { - valid = c.Subject.CommonName + if len(c.DNSNames) > 0 { + valid = strings.Join(c.DNSNames, ", ") + } else { + valid = c.Subject.CommonName + } } - return "certificate is valid for " + valid + ", not " + h.Host + return "x509: certificate is valid for " + valid + ", not " + h.Host } // UnknownAuthorityError results when the certificate issuer is unknown @@ -77,6 +97,14 @@ func (e UnknownAuthorityError) Error() string { return "x509: certificate signed by unknown authority" } +// SystemRootsError results when we fail to load the system root certificates. +type SystemRootsError struct { +} + +func (e SystemRootsError) Error() string { + return "x509: failed to load system roots and no roots provided" +} + // VerifyOptions contains parameters for Certificate.Verify. It's a structure // because other PKIX verification APIs have ended up needing many options. type VerifyOptions struct { @@ -84,6 +112,11 @@ type VerifyOptions struct { Intermediates *CertPool Roots *CertPool // if nil, the system roots are used CurrentTime time.Time // if zero, the current time is used + // KeyUsage specifies which Extended Key Usage values are acceptable. + // An empty list means ExtKeyUsageServerAuth. Key usage is considered a + // constraint down the chain which mirrors Windows CryptoAPI behaviour, + // but not the spec. To accept any key usage, include ExtKeyUsageAny. + KeyUsages []ExtKeyUsage } const ( @@ -160,6 +193,9 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e if opts.Roots == nil { opts.Roots = systemRootsPool() + if opts.Roots == nil { + return nil, SystemRootsError{} + } } err = c.isValid(leafCertificate, nil, &opts) @@ -174,7 +210,35 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e } } - return c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts) + candidateChains, err := c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts) + if err != nil { + return + } + + keyUsages := opts.KeyUsages + if len(keyUsages) == 0 { + keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} + } + + // If any key usage is acceptable then we're done. + for _, usage := range keyUsages { + if usage == ExtKeyUsageAny { + chains = candidateChains + return + } + } + + for _, candidate := range candidateChains { + if checkChainForKeyUsage(candidate, keyUsages) { + chains = append(chains, candidate) + } + } + + if len(chains) == 0 { + err = CertificateInvalidError{c, IncompatibleUsage} + } + + return } func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate { @@ -285,6 +349,22 @@ func toLowerCaseASCII(in string) string { // VerifyHostname returns nil if c is a valid certificate for the named host. // Otherwise it returns an error describing the mismatch. func (c *Certificate) VerifyHostname(h string) error { + // IP addresses may be written in [ ]. + candidateIP := h + if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' { + candidateIP = h[1 : len(h)-1] + } + if ip := net.ParseIP(candidateIP); ip != nil { + // We only match IP addresses against IP SANs. + // https://tools.ietf.org/html/rfc6125#appendix-B.2 + for _, candidate := range c.IPAddresses { + if ip.Equal(candidate) { + return nil + } + } + return HostnameError{c, candidateIP} + } + lowered := toLowerCaseASCII(h) if len(c.DNSNames) > 0 { @@ -300,3 +380,64 @@ func (c *Certificate) VerifyHostname(h string) error { return HostnameError{c, h} } + +func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool { + usages := make([]ExtKeyUsage, len(keyUsages)) + copy(usages, keyUsages) + + if len(chain) == 0 { + return false + } + + usagesRemaining := len(usages) + + // We walk down the list and cross out any usages that aren't supported + // by each certificate. If we cross out all the usages, then the chain + // is unacceptable. + + for i := len(chain) - 1; i >= 0; i-- { + cert := chain[i] + if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 { + // The certificate doesn't have any extended key usage specified. + continue + } + + for _, usage := range cert.ExtKeyUsage { + if usage == ExtKeyUsageAny { + // The certificate is explicitly good for any usage. + continue + } + } + + const invalidUsage ExtKeyUsage = -1 + + NextRequestedUsage: + for i, requestedUsage := range usages { + if requestedUsage == invalidUsage { + continue + } + + for _, usage := range cert.ExtKeyUsage { + if requestedUsage == usage { + continue NextRequestedUsage + } else if requestedUsage == ExtKeyUsageServerAuth && + (usage == ExtKeyUsageNetscapeServerGatedCrypto || + usage == ExtKeyUsageMicrosoftServerGatedCrypto) { + // In order to support COMODO + // certificate chains, we have to + // accept Netscape or Microsoft SGC + // usages as equal to ServerAuth. + continue NextRequestedUsage + } + } + + usages[i] = invalidUsage + usagesRemaining-- + if usagesRemaining == 0 { + return false + } + } + } + + return true +} diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go index 7b171b291..5103ed814 100644 --- a/src/pkg/crypto/x509/verify_test.go +++ b/src/pkg/crypto/x509/verify_test.go @@ -15,18 +15,31 @@ import ( ) type verifyTest struct { - leaf string - intermediates []string - roots []string - currentTime int64 - dnsName string - systemSkip bool + leaf string + intermediates []string + roots []string + currentTime int64 + dnsName string + systemSkip bool + keyUsages []ExtKeyUsage + testSystemRootsError bool errorCallback func(*testing.T, int, error) bool expectedChains [][]string } var verifyTests = []verifyTest{ + { + leaf: googleLeaf, + intermediates: []string{thawteIntermediate}, + currentTime: 1302726541, + dnsName: "www.google.com", + testSystemRootsError: true, + + // Without any roots specified we should get a system roots + // error. + errorCallback: expectSystemRootsError, + }, { leaf: googleLeaf, intermediates: []string{thawteIntermediate}, @@ -113,6 +126,51 @@ var verifyTests = []verifyTest{ {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"}, }, }, + { + // The default configuration should reject an S/MIME chain. + leaf: smimeLeaf, + roots: []string{smimeIntermediate}, + currentTime: 1339436154, + + // Key usage not implemented for Windows yet. + systemSkip: true, + errorCallback: expectUsageError, + }, + { + leaf: smimeLeaf, + roots: []string{smimeIntermediate}, + currentTime: 1339436154, + keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth}, + + // Key usage not implemented for Windows yet. + systemSkip: true, + errorCallback: expectUsageError, + }, + { + leaf: smimeLeaf, + roots: []string{smimeIntermediate}, + currentTime: 1339436154, + keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection}, + + // Key usage not implemented for Windows yet. + systemSkip: true, + expectedChains: [][]string{ + {"Ryan Hurst", "GlobalSign PersonalSign 2 CA - G2"}, + }, + }, + { + leaf: megaLeaf, + intermediates: []string{comodoIntermediate1}, + roots: []string{comodoRoot}, + currentTime: 1360431182, + + // CryptoAPI can find alternative validation paths so we don't + // perform this test with system validation. + systemSkip: true, + expectedChains: [][]string{ + {"mega.co.nz", "EssentialSSL CA", "COMODO Certification Authority"}, + }, + }, } func expectHostnameError(t *testing.T, i int, err error) (ok bool) { @@ -131,6 +189,14 @@ func expectExpired(t *testing.T, i int, err error) (ok bool) { return true } +func expectUsageError(t *testing.T, i int, err error) (ok bool) { + if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage { + t.Errorf("#%d: error was not IncompatibleUsage: %s", i, err) + return false + } + return true +} + func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) { if _, ok := err.(UnknownAuthorityError); !ok { t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err) @@ -139,6 +205,14 @@ func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) { return true } +func expectSystemRootsError(t *testing.T, i int, err error) bool { + if _, ok := err.(SystemRootsError); !ok { + t.Errorf("#%d: error was not SystemRootsError: %s", i, err) + return false + } + return true +} + func certificateFromPEM(pemBytes string) (*Certificate, error) { block, _ := pem.Decode([]byte(pemBytes)) if block == nil { @@ -152,11 +226,15 @@ func testVerify(t *testing.T, useSystemRoots bool) { if useSystemRoots && test.systemSkip { continue } + if runtime.GOOS == "windows" && test.testSystemRootsError { + continue + } opts := VerifyOptions{ Intermediates: NewCertPool(), DNSName: test.dnsName, CurrentTime: time.Unix(test.currentTime, 0), + KeyUsages: test.keyUsages, } if !useSystemRoots { @@ -184,8 +262,19 @@ func testVerify(t *testing.T, useSystemRoots bool) { return } + var oldSystemRoots *CertPool + if test.testSystemRootsError { + oldSystemRoots = systemRootsPool() + systemRoots = nil + opts.Roots = nil + } + chains, err := leaf.Verify(opts) + if test.testSystemRootsError { + systemRoots = oldSystemRoots + } + if test.errorCallback == nil && err != nil { t.Errorf("#%d: unexpected error: %s", i, err) } @@ -233,8 +322,7 @@ func TestGoVerify(t *testing.T) { func TestSystemVerify(t *testing.T) { if runtime.GOOS != "windows" { - t.Logf("skipping verify test using system APIs on %q", runtime.GOOS) - return + t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS) } testVerify(t, true) @@ -433,3 +521,145 @@ O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= -----END CERTIFICATE-----` + +const smimeLeaf = `-----BEGIN CERTIFICATE----- +MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA +MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD +VQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAyIENBIC0gRzIwHhcNMTIwMTIz +MTYzNjU5WhcNMTUwMTIzMTYzNjU5WjCBlDELMAkGA1UEBhMCVVMxFjAUBgNVBAgT +DU5ldyBIYW1zcGhpcmUxEzARBgNVBAcTClBvcnRzbW91dGgxGTAXBgNVBAoTEEds +b2JhbFNpZ24sIEluYy4xEzARBgNVBAMTClJ5YW4gSHVyc3QxKDAmBgkqhkiG9w0B +CQEWGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQC4ASSTvavmsFQAob60ukSSwOAL9nT/s99ltNUCAf5fPH5j +NceMKxaQse2miOmRRIXaykcq1p/TbI70Ztce38r2mbOwqDHHPVi13GxJEyUXWgaR +BteDMu5OGyWNG1kchVsGWpbstT0Z4v0md5m1BYFnxB20ebJyOR2lXDxsFK28nnKV ++5eMj76U8BpPQ4SCH7yTMG6y0XXsB3cCrBKr2o3TOYgEKv+oNnbaoMt3UxMt9nSf +9jyIshjqfnT5Aew3CUNMatO55g5FXXdIukAweg1YSb1ls05qW3sW00T3d7dQs9/7 +NuxCg/A2elmVJSoy8+MLR8JSFEf/aMgjO/TyLg/jAgMBAAGjggGPMIIBizAOBgNV +HQ8BAf8EBAMCBaAwTQYDVR0gBEYwRDBCBgorBgEEAaAyASgKMDQwMgYIKwYBBQUH +AgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMCQGA1Ud +EQQdMBuBGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wCQYDVR0TBAIwADAdBgNV +HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 +cDovL2NybC5nbG9iYWxzaWduLmNvbS9ncy9nc3BlcnNvbmFsc2lnbjJnMi5jcmww +VQYIKwYBBQUHAQEESTBHMEUGCCsGAQUFBzAChjlodHRwOi8vc2VjdXJlLmdsb2Jh +bHNpZ24uY29tL2NhY2VydC9nc3BlcnNvbmFsc2lnbjJnMi5jcnQwHQYDVR0OBBYE +FFWiECe0/L72eVYqcWYnLV6SSjzhMB8GA1UdIwQYMBaAFD8V0m18L+cxnkMKBqiU +bCw7xe5lMA0GCSqGSIb3DQEBBQUAA4IBAQAhQi6hLPeudmf3IBF4IDzCvRI0FaYd +BKfprSk/H0PDea4vpsLbWpA0t0SaijiJYtxKjlM4bPd+2chb7ejatDdyrZIzmDVy +q4c30/xMninGKokpYA11/Ve+i2dvjulu65qasrtQRGybAuuZ67lrp/K3OMFgjV5N +C3AHYLzvNU4Dwc4QQ1BaMOg6KzYSrKbABRZajfrpC9uiePsv7mDIXLx/toBPxWNl +a5vJm5DrZdn7uHdvBCE6kMykbOLN5pmEK0UIlwKh6Qi5XD0pzlVkEZliFkBMJgub +d/eF7xeg7TKPWC5xyOFp9SdMolJM7LTC3wnSO3frBAev+q/nGs9Xxyvs +-----END CERTIFICATE-----` + +const smimeIntermediate = `-----BEGIN CERTIFICATE----- +MIIEFjCCAv6gAwIBAgILBAAAAAABL07hL1IwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw +MDBaFw0xOTA0MTMxMDAwMDBaMFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMSowKAYDVQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAy +IENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBa0H5Nez4 +En3dIlFpX7e5E0YndxQ74xOBbz7kdBd+DLX0LOQMjVPU3DAgKL9ujhH+ZhHkURbH +3X/94TQSUL/z2JjsaQvS0NqyZXHhM5eeuquzOJRzEQ8+odETzHg2G0Erv7yjSeww +gkwDWDJnYUDlOjYTDUEG6+i+8Mn425reo4I0E277wD542kmVWeW7+oHv5dZo9e1Q +yWwiKTEP6BEQVVSBgThXMG4traSSDRUt3T1eQTZx5EObpiBEBO4OTqiBTJfg4vEI +YgkXzKLpnfszTB6YMDpR9/QS6p3ANB3kfAb+t6udSO3WCst0DGrwHDLBFGDR4UeY +T5KGGnI7cWL7AgMBAAGjgeUwgeIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI +MAYBAf8CAQAwHQYDVR0OBBYEFD8V0m18L+cxnkMKBqiUbCw7xe5lMEcGA1UdIARA +MD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu +LmNvbS9yZXBvc2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmds +b2JhbHNpZ24ubmV0L3Jvb3QuY3JsMB8GA1UdIwQYMBaAFGB7ZhpFDZfKiVAvfQTN +NKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQBDc3nMpMxJMQMcYUCB3+C73UpvwDE8 +eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX +eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX +YEvTWbWwGdPytDFPYIl3/6OqNSXSnZ7DxPcdLJq2uyiga8PB/TTIIHYkdM2+1DE0 +7y3rH/7TjwDVD7SLu5/SdOfKskuMPTjOEvz3K161mymW06klVhubCIWOro/Gx1Q2 +2FQOZ7/2k4uYoOdBTSlb8kTAuzZNgIE0rB2BIYCTz/P6zZIKW0ogbRSH +-----END CERTIFICATE-----` + +var megaLeaf = `-----BEGIN CERTIFICATE----- +MIIFOjCCBCKgAwIBAgIQWYE8Dup170kZ+k11Lg51OjANBgkqhkiG9w0BAQUFADBy +MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD +VQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEYMBYGA1UE +AxMPRXNzZW50aWFsU1NMIENBMB4XDTEyMTIxNDAwMDAwMFoXDTE0MTIxNDIzNTk1 +OVowfzEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMS4wLAYDVQQL +EyVIb3N0ZWQgYnkgSW5zdHJhIENvcnBvcmF0aW9uIFB0eS4gTFREMRUwEwYDVQQL +EwxFc3NlbnRpYWxTU0wxEzARBgNVBAMTCm1lZ2EuY28ubnowggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDcxMCClae8BQIaJHBUIVttlLvhbK4XhXPk3RQ3 +G5XA6tLZMBQ33l3F9knYJ0YErXtr8IdfYoulRQFmKFMJl9GtWyg4cGQi2Rcr5VN5 +S5dA1vu4oyJBxE9fPELcK6Yz1vqaf+n6za+mYTiQYKggVdS8/s8hmNuXP9Zk1pIn ++q0pGsf8NAcSHMJgLqPQrTDw+zae4V03DvcYfNKjuno88d2226ld7MAmQZ7uRNsI +/CnkdelVs+akZsXf0szefSqMJlf08SY32t2jj4Ra7RApVYxOftD9nij/aLfuqOU6 +ow6IgIcIG2ZvXLZwK87c5fxL7UAsTTV+M1sVv8jA33V2oKLhAgMBAAGjggG9MIIB +uTAfBgNVHSMEGDAWgBTay+qtWwhdzP/8JlTOSeVVxjj0+DAdBgNVHQ4EFgQUmP9l +6zhyrZ06Qj4zogt+6LKFk4AwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAw +NAYDVR0lBC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3CgMDBglghkgB +hvhCBAEwTwYDVR0gBEgwRjA6BgsrBgEEAbIxAQICBzArMCkGCCsGAQUFBwIBFh1o +dHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQUzAIBgZngQwBAgEwOwYDVR0fBDQw +MjAwoC6gLIYqaHR0cDovL2NybC5jb21vZG9jYS5jb20vRXNzZW50aWFsU1NMQ0Eu +Y3JsMG4GCCsGAQUFBwEBBGIwYDA4BggrBgEFBQcwAoYsaHR0cDovL2NydC5jb21v +ZG9jYS5jb20vRXNzZW50aWFsU1NMQ0FfMi5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6 +Ly9vY3NwLmNvbW9kb2NhLmNvbTAlBgNVHREEHjAcggptZWdhLmNvLm56gg53d3cu +bWVnYS5jby5uejANBgkqhkiG9w0BAQUFAAOCAQEAcYhrsPSvDuwihMOh0ZmRpbOE +Gw6LqKgLNTmaYUPQhzi2cyIjhUhNvugXQQlP5f0lp5j8cixmArafg1dTn4kQGgD3 +ivtuhBTgKO1VYB/VRoAt6Lmswg3YqyiS7JiLDZxjoV7KoS5xdiaINfHDUaBBY4ZH +j2BUlPniNBjCqXe/HndUTVUewlxbVps9FyCmH+C4o9DWzdGBzDpCkcmo5nM+cp7q +ZhTIFTvZfo3zGuBoyu8BzuopCJcFRm3cRiXkpI7iOMUIixO1szkJS6WpL1sKdT73 +UXp08U0LBqoqG130FbzEJBBV3ixbvY6BWMHoCWuaoF12KJnC5kHt2RoWAAgMXA== +-----END CERTIFICATE-----` + +var comodoIntermediate1 = `-----BEGIN CERTIFICATE----- +MIIFAzCCA+ugAwIBAgIQGLLLuqME8aAPwfLzJkYqSjANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0xOTEyMzEyMzU5NTlaMHIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVh +dGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9E +TyBDQSBMaW1pdGVkMRgwFgYDVQQDEw9Fc3NlbnRpYWxTU0wgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt8AiwcsargxIxF3CJhakgEtSYau2A1NHf +5I5ZLdOWIY120j8YC0YZYwvHIPPlC92AGvFaoL0dds23Izp0XmEbdaqb1IX04XiR +0y3hr/yYLgbSeT1awB8hLRyuIVPGOqchfr7tZ291HRqfalsGs2rjsQuqag7nbWzD +ypWMN84hHzWQfdvaGlyoiBSyD8gSIF/F03/o4Tjg27z5H6Gq1huQByH6RSRQXScq +oChBRVt9vKCiL6qbfltTxfEFFld+Edc7tNkBdtzffRDPUanlOPJ7FAB1WfnwWdsX +Pvev5gItpHnBXaIcw5rIp6gLSApqLn8tl2X2xQScRMiZln5+pN0vAgMBAAGjggGD +MIIBfzAfBgNVHSMEGDAWgBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAdBgNVHQ4EFgQU +2svqrVsIXcz//CZUzknlVcY49PgwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI +MAYBAf8CAQAwIAYDVR0lBBkwFwYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMD4GA1Ud +IAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21v +ZG8uY29tL0NQUzBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9kb2Nh +LmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBsBggrBgEFBQcB +AQRgMF4wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NvbW9k +b1VUTlNHQ0NBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2Eu +Y29tMA0GCSqGSIb3DQEBBQUAA4IBAQAtlzR6QDLqcJcvgTtLeRJ3rvuq1xqo2l/z +odueTZbLN3qo6u6bldudu+Ennv1F7Q5Slqz0J790qpL0pcRDAB8OtXj5isWMcL2a +ejGjKdBZa0wztSz4iw+SY1dWrCRnilsvKcKxudokxeRiDn55w/65g+onO7wdQ7Vu +F6r7yJiIatnyfKH2cboZT7g440LX8NqxwCPf3dfxp+0Jj1agq8MLy6SSgIGSH6lv ++Wwz3D5XxqfyH8wqfOQsTEZf6/Nh9yvENZ+NWPU6g0QO2JOsTGvMd/QDzczc4BxL +XSXaPV7Od4rhPsbXlM1wSTz/Dr0ISKvlUhQVnQ6cGodWaK2cCQBk +-----END CERTIFICATE-----` + +var comodoRoot = `-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE-----` diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index c4d85e67f..b802bf4eb 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -9,6 +9,8 @@ import ( "bytes" "crypto" "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rsa" "crypto/sha1" "crypto/x509/pkix" @@ -17,6 +19,8 @@ import ( "errors" "io" "math/big" + "net" + "strconv" "time" ) @@ -106,6 +110,8 @@ type dsaSignature struct { R, S *big.Int } +type ecdsaSignature dsaSignature + type validity struct { NotBefore, NotAfter time.Time } @@ -133,6 +139,10 @@ const ( SHA512WithRSA DSAWithSHA1 DSAWithSHA256 + ECDSAWithSHA1 + ECDSAWithSHA256 + ECDSAWithSHA384 + ECDSAWithSHA512 ) type PublicKeyAlgorithm int @@ -141,14 +151,15 @@ const ( UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota RSA DSA + ECDSA ) // OIDs for signature algorithms // // pkcs-1 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } -// -// +// +// // RFC 3279 2.2.1 RSA Signature Algorithms // // md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } @@ -156,13 +167,19 @@ const ( // md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } // // sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } -// +// // dsaWithSha1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } +// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } +// +// RFC 3279 2.2.3 ECDSA Signature Algorithm +// +// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-x962(10045) +// signatures(4) ecdsa-with-SHA1(1)} // // // RFC 4055 5 PKCS #1 Version 1.5 -// +// // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } // // sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } @@ -176,15 +193,30 @@ const ( // joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) // csor(3) algorithms(4) id-dsa-with-sha2(3) 2} // +// RFC 5758 3.2 ECDSA Signature Algorithm +// +// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } +// +// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } +// +// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } + var ( - oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} - oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} - oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} - oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} - oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} - oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} - oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} - oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} ) func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm { @@ -205,6 +237,14 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm return DSAWithSHA1 case oid.Equal(oidSignatureDSAWithSHA256): return DSAWithSHA256 + case oid.Equal(oidSignatureECDSAWithSHA1): + return ECDSAWithSHA1 + case oid.Equal(oidSignatureECDSAWithSHA256): + return ECDSAWithSHA256 + case oid.Equal(oidSignatureECDSAWithSHA384): + return ECDSAWithSHA384 + case oid.Equal(oidSignatureECDSAWithSHA512): + return ECDSAWithSHA512 } return UnknownSignatureAlgorithm } @@ -218,21 +258,81 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm // // id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) // x9-57(10040) x9cm(4) 1 } +// +// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters +// +// id-ecPublicKey OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } var ( - oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} - oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} + oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} + oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} + oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} ) func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm { switch { - case oid.Equal(oidPublicKeyRsa): + case oid.Equal(oidPublicKeyRSA): return RSA - case oid.Equal(oidPublicKeyDsa): + case oid.Equal(oidPublicKeyDSA): return DSA + case oid.Equal(oidPublicKeyECDSA): + return ECDSA } return UnknownPublicKeyAlgorithm } +// RFC 5480, 2.1.1.1. Named Curve +// +// secp224r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 33 } +// +// secp256r1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +// prime(1) 7 } +// +// secp384r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 34 } +// +// secp521r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 35 } +// +// NB: secp256r1 is equivalent to prime256v1 +var ( + oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33} + oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} + oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} + oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} +) + +func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve { + switch { + case oid.Equal(oidNamedCurveP224): + return elliptic.P224() + case oid.Equal(oidNamedCurveP256): + return elliptic.P256() + case oid.Equal(oidNamedCurveP384): + return elliptic.P384() + case oid.Equal(oidNamedCurveP521): + return elliptic.P521() + } + return nil +} + +func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { + switch curve { + case elliptic.P224(): + return oidNamedCurveP224, true + case elliptic.P256(): + return oidNamedCurveP256, true + case elliptic.P384(): + return oidNamedCurveP384, true + case elliptic.P521(): + return oidNamedCurveP521, true + } + + return nil, false +} + // KeyUsage represents the set of actions that are valid for a given key. It's // a bitmap of the KeyUsage* constants. type KeyUsage int @@ -262,13 +362,18 @@ const ( // id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } // id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } var ( - oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} - oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} - oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} - oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} - oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} - oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} - oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} + oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} + oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} + oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} + oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} + oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} + oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5} + oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6} + oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7} + oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} + oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} + oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3} + oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1} ) // ExtKeyUsage represents an extended set of actions that are valid for a given key. @@ -281,10 +386,52 @@ const ( ExtKeyUsageClientAuth ExtKeyUsageCodeSigning ExtKeyUsageEmailProtection + ExtKeyUsageIPSECEndSystem + ExtKeyUsageIPSECTunnel + ExtKeyUsageIPSECUser ExtKeyUsageTimeStamping ExtKeyUsageOCSPSigning + ExtKeyUsageMicrosoftServerGatedCrypto + ExtKeyUsageNetscapeServerGatedCrypto ) +// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID. +var extKeyUsageOIDs = []struct { + extKeyUsage ExtKeyUsage + oid asn1.ObjectIdentifier +}{ + {ExtKeyUsageAny, oidExtKeyUsageAny}, + {ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth}, + {ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth}, + {ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning}, + {ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection}, + {ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem}, + {ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel}, + {ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser}, + {ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping}, + {ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning}, + {ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto}, + {ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto}, +} + +func extKeyUsageFromOID(oid asn1.ObjectIdentifier) (eku ExtKeyUsage, ok bool) { + for _, pair := range extKeyUsageOIDs { + if oid.Equal(pair.oid) { + return pair.extKeyUsage, true + } + } + return +} + +func oidFromExtKeyUsage(eku ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) { + for _, pair := range extKeyUsageOIDs { + if eku == pair.extKeyUsage { + return pair.oid, true + } + } + return +} + // A Certificate represents an X.509 certificate. type Certificate struct { Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). @@ -319,6 +466,7 @@ type Certificate struct { // Subject Alternate Name values DNSNames []string EmailAddresses []string + IPAddresses []net.IP // Name constraints PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical. @@ -344,6 +492,55 @@ func (c *Certificate) Equal(other *Certificate) bool { return bytes.Equal(c.Raw, other.Raw) } +// Entrust have a broken root certificate (CN=Entrust.net Certification +// Authority (2048)) which isn't marked as a CA certificate and is thus invalid +// according to PKIX. +// We recognise this certificate by its SubjectPublicKeyInfo and exempt it +// from the Basic Constraints requirement. +// See http://www.entrust.net/knowledge-base/technote.cfm?tn=7869 +// +// TODO(agl): remove this hack once their reissued root is sufficiently +// widespread. +var entrustBrokenSPKI = []byte{ + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde, 0x05, + 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3, + 0x7f, 0xc7, 0x4b, 0x7e, 0x5a, 0x9f, 0xb3, 0xff, + 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba, 0x10, + 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff, + 0x28, 0xce, 0xc0, 0xe6, 0x0e, 0x06, 0x91, 0x50, + 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb, 0xd8, + 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6, + 0x96, 0xdc, 0xbc, 0xaa, 0xfa, 0x52, 0x77, 0x04, + 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd, 0x3c, + 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65, + 0xf9, 0xc1, 0xb1, 0x3f, 0x72, 0x86, 0xf2, 0x38, + 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28, 0xda, + 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9, + 0xc1, 0x65, 0x77, 0x76, 0x24, 0x4c, 0x98, 0xf7, + 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb, 0x37, + 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde, + 0x20, 0x09, 0x49, 0x36, 0x24, 0x69, 0x42, 0xf6, + 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9, 0x3c, + 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a, + 0xd7, 0xf7, 0x0a, 0x6f, 0xef, 0x2e, 0xd8, 0xd5, + 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68, 0xe2, + 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc, + 0x51, 0x43, 0x9d, 0xe0, 0xb2, 0xc4, 0x67, 0xb4, + 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f, 0x4b, + 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e, + 0x6c, 0x78, 0x90, 0x95, 0xde, 0xca, 0x3a, 0x48, + 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75, 0x05, + 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09, + 0xe4, 0x1a, 0x15, 0xdc, 0x87, 0x23, 0xaa, 0xb2, + 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8, 0x3d, + 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68, + 0x55, 0x02, 0x03, 0x01, 0x00, 0x01, +} + // CheckSignatureFrom verifies that the signature on c is a valid signature // from parent. func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) { @@ -352,8 +549,10 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) { // certificate, or the extension is present but the cA boolean is not // asserted, then the certified public key MUST NOT be used to verify // certificate signatures." - if parent.Version == 3 && !parent.BasicConstraintsValid || - parent.BasicConstraintsValid && !parent.IsCA { + // (except for Entrust, see comment above entrustBrokenSPKI) + if (parent.Version == 3 && !parent.BasicConstraintsValid || + parent.BasicConstraintsValid && !parent.IsCA) && + !bytes.Equal(c.RawSubjectPublicKeyInfo, entrustBrokenSPKI) { return ConstraintViolationError{} } @@ -376,13 +575,13 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature var hashType crypto.Hash switch algo { - case SHA1WithRSA, DSAWithSHA1: + case SHA1WithRSA, DSAWithSHA1, ECDSAWithSHA1: hashType = crypto.SHA1 - case SHA256WithRSA, DSAWithSHA256: + case SHA256WithRSA, DSAWithSHA256, ECDSAWithSHA256: hashType = crypto.SHA256 - case SHA384WithRSA: + case SHA384WithRSA, ECDSAWithSHA384: hashType = crypto.SHA384 - case SHA512WithRSA: + case SHA512WithRSA, ECDSAWithSHA512: hashType = crypto.SHA512 default: return ErrUnsupportedAlgorithm @@ -411,6 +610,18 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature return errors.New("DSA verification failure") } return + case *ecdsa.PublicKey: + ecdsaSig := new(ecdsaSignature) + if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("crypto/x509: ECDSA signature contained zero or negative values") + } + if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) { + return errors.New("crypto/x509: ECDSA verification failure") + } + return } return ErrUnsupportedAlgorithm } @@ -446,8 +657,6 @@ type nameConstraints struct { type generalSubtree struct { Name string `asn1:"tag:2,optional,ia5"` - Min int `asn1:"optional,tag:0"` - Max int `asn1:"optional,tag:1"` } func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { @@ -460,6 +669,13 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ return nil, err } + if p.N.Sign() <= 0 { + return nil, errors.New("x509: RSA modulus is not a positive number") + } + if p.E <= 0 { + return nil, errors.New("x509: RSA public exponent is not a positive number") + } + pub := &rsa.PublicKey{ E: p.E, N: p.N, @@ -489,6 +705,27 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ Y: p, } return pub, nil + case ECDSA: + paramsData := keyData.Algorithm.Parameters.FullBytes + namedCurveOID := new(asn1.ObjectIdentifier) + _, err := asn1.Unmarshal(paramsData, namedCurveOID) + if err != nil { + return nil, err + } + namedCurve := namedCurveFromOID(*namedCurveOID) + if namedCurve == nil { + return nil, errors.New("crypto/x509: unsupported elliptic curve") + } + x, y := elliptic.Unmarshal(namedCurve, asn1Data) + if x == nil { + return nil, errors.New("crypto/x509: failed to unmarshal elliptic curve point") + } + pub := &ecdsa.PublicKey{ + Curve: namedCurve, + X: x, + Y: y, + } + return pub, nil default: return nil, nil } @@ -607,6 +844,13 @@ func parseCertificate(in *certificate) (*Certificate, error) { case 2: out.DNSNames = append(out.DNSNames, string(v.Bytes)) parsedName = true + case 7: + switch len(v.Bytes) { + case net.IPv4len, net.IPv6len: + out.IPAddresses = append(out.IPAddresses, v.Bytes) + default: + return nil, errors.New("x509: certificate contained IP address of length " + strconv.Itoa(len(v.Bytes))) + } } } @@ -643,7 +887,7 @@ func parseCertificate(in *certificate) (*Certificate, error) { } for _, subtree := range constraints.Permitted { - if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 { + if len(subtree.Name) == 0 { if e.Critical { return out, UnhandledCriticalExtension{} } @@ -679,22 +923,9 @@ func parseCertificate(in *certificate) (*Certificate, error) { } for _, u := range keyUsage { - switch { - case u.Equal(oidExtKeyUsageAny): - out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageAny) - case u.Equal(oidExtKeyUsageServerAuth): - out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageServerAuth) - case u.Equal(oidExtKeyUsageClientAuth): - out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageClientAuth) - case u.Equal(oidExtKeyUsageCodeSigning): - out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageCodeSigning) - case u.Equal(oidExtKeyUsageEmailProtection): - out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageEmailProtection) - case u.Equal(oidExtKeyUsageTimeStamping): - out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageTimeStamping) - case u.Equal(oidExtKeyUsageOCSPSigning): - out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageOCSPSigning) - default: + if extKeyUsage, ok := extKeyUsageFromOID(u); ok { + out.ExtKeyUsage = append(out.ExtKeyUsage, extKeyUsage) + } else { out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u) } } @@ -783,6 +1014,7 @@ func reverseBitsInAByte(in byte) byte { var ( oidExtensionSubjectKeyId = []int{2, 5, 29, 14} oidExtensionKeyUsage = []int{2, 5, 29, 15} + oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37} oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} oidExtensionBasicConstraints = []int{2, 5, 29, 19} oidExtensionSubjectAltName = []int{2, 5, 29, 17} @@ -791,7 +1023,7 @@ var ( ) func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { - ret = make([]pkix.Extension, 7 /* maximum number of elements. */) + ret = make([]pkix.Extension, 8 /* maximum number of elements. */) n := 0 if template.KeyUsage != 0 { @@ -814,6 +1046,27 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } + if len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0 { + ret[n].Id = oidExtensionExtendedKeyUsage + + var oids []asn1.ObjectIdentifier + for _, u := range template.ExtKeyUsage { + if oid, ok := oidFromExtKeyUsage(u); ok { + oids = append(oids, oid) + } else { + panic("internal error") + } + } + + oids = append(oids, template.UnknownExtKeyUsage...) + + ret[n].Value, err = asn1.Marshal(oids) + if err != nil { + return + } + n++ + } + if template.BasicConstraintsValid { ret[n].Id = oidExtensionBasicConstraints ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen}) @@ -842,11 +1095,22 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } - if len(template.DNSNames) > 0 { + if len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 { ret[n].Id = oidExtensionSubjectAltName - rawValues := make([]asn1.RawValue, len(template.DNSNames)) - for i, name := range template.DNSNames { - rawValues[i] = asn1.RawValue{Tag: 2, Class: 2, Bytes: []byte(name)} + var rawValues []asn1.RawValue + for _, name := range template.DNSNames { + rawValues = append(rawValues, asn1.RawValue{Tag: 2, Class: 2, Bytes: []byte(name)}) + } + for _, email := range template.EmailAddresses { + rawValues = append(rawValues, asn1.RawValue{Tag: 1, Class: 2, Bytes: []byte(email)}) + } + for _, rawIP := range template.IPAddresses { + // If possible, we always want to encode IPv4 addresses in 4 bytes. + ip := rawIP.To4() + if ip == nil { + ip = rawIP + } + rawValues = append(rawValues, asn1.RawValue{Tag: 7, Class: 2, Bytes: ip}) } ret[n].Value, err = asn1.Marshal(rawValues) if err != nil { @@ -890,11 +1154,6 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { return ret[0:n], nil } -var ( - oidSHA1WithRSA = []int{1, 2, 840, 113549, 1, 1, 5} - oidRSA = []int{1, 2, 840, 113549, 1, 1, 1} -) - func subjectBytes(cert *Certificate) ([]byte, error) { if len(cert.RawSubject) > 0 { return cert.RawSubject, nil @@ -905,8 +1164,9 @@ func subjectBytes(cert *Certificate) ([]byte, error) { // CreateCertificate creates a new certificate based on a template. The // following members of template are used: SerialNumber, Subject, NotBefore, -// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId, -// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains. +// NotAfter, KeyUsage, ExtKeyUsage, UnknownExtKeyUsage, BasicConstraintsValid, +// IsCA, MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical, +// PermittedDNSDomains. // // The certificate is signed by parent. If parent is equal to template then the // certificate is self-signed. The parameter pub is the public key of the @@ -914,23 +1174,61 @@ func subjectBytes(cert *Certificate) ([]byte, error) { // // The returned slice is the certificate in DER encoding. // -// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey -// for priv). +// The only supported key types are RSA and ECDSA (*rsa.PublicKey or +// *ecdsa.PublicKey for pub, *rsa.PrivateKey or *ecdsa.PublicKey for priv). func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) { - rsaPub, ok := pub.(*rsa.PublicKey) - if !ok { - return nil, errors.New("x509: non-RSA public keys not supported") + var publicKeyBytes []byte + var publicKeyAlgorithm pkix.AlgorithmIdentifier + + switch pub := pub.(type) { + case *rsa.PublicKey: + publicKeyBytes, err = asn1.Marshal(rsaPublicKey{ + N: pub.N, + E: pub.E, + }) + publicKeyAlgorithm.Algorithm = oidPublicKeyRSA + case *ecdsa.PublicKey: + oid, ok := oidFromNamedCurve(pub.Curve) + if !ok { + return nil, errors.New("x509: unknown elliptic curve") + } + publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA + var paramBytes []byte + paramBytes, err = asn1.Marshal(oid) + if err != nil { + return + } + publicKeyAlgorithm.Parameters.FullBytes = paramBytes + publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) + default: + return nil, errors.New("x509: only RSA and ECDSA public keys supported") } - rsaPriv, ok := priv.(*rsa.PrivateKey) - if !ok { - return nil, errors.New("x509: non-RSA private keys not supported") + var signatureAlgorithm pkix.AlgorithmIdentifier + var hashFunc crypto.Hash + + switch priv := priv.(type) { + case *rsa.PrivateKey: + signatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA + hashFunc = crypto.SHA1 + case *ecdsa.PrivateKey: + switch priv.Curve { + case elliptic.P224(), elliptic.P256(): + hashFunc = crypto.SHA256 + signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256 + case elliptic.P384(): + hashFunc = crypto.SHA384 + signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384 + case elliptic.P521(): + hashFunc = crypto.SHA512 + signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512 + default: + return nil, errors.New("x509: unknown elliptic curve") + } + default: + return nil, errors.New("x509: only RSA and ECDSA private keys supported") } - asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ - N: rsaPub.N, - E: rsaPub.E, - }) if err != nil { return } @@ -954,15 +1252,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf return } - encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} + encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes} c := tbsCertificate{ Version: 2, SerialNumber: template.SerialNumber, - SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, + SignatureAlgorithm: signatureAlgorithm, Issuer: asn1.RawValue{FullBytes: asn1Issuer}, - Validity: validity{template.NotBefore, template.NotAfter}, + Validity: validity{template.NotBefore.UTC(), template.NotAfter.UTC()}, Subject: asn1.RawValue{FullBytes: asn1Subject}, - PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, + PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey}, Extensions: extensions, } @@ -973,11 +1271,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf c.Raw = tbsCertContents - h := sha1.New() + h := hashFunc.New() h.Write(tbsCertContents) digest := h.Sum(nil) - signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) + var signature []byte + + switch priv := priv.(type) { + case *rsa.PrivateKey: + signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest) + case *ecdsa.PrivateKey: + var r, s *big.Int + if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil { + signature, err = asn1.Marshal(ecdsaSignature{r, s}) + } + default: + panic("internal error") + } + if err != nil { return } @@ -985,7 +1296,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf cert, err = asn1.Marshal(certificate{ nil, c, - pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, + signatureAlgorithm, asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, }) return @@ -1037,8 +1348,8 @@ func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts [ Algorithm: oidSignatureSHA1WithRSA, }, Issuer: c.Subject.ToRDNSequence(), - ThisUpdate: now, - NextUpdate: expiry, + ThisUpdate: now.UTC(), + NextUpdate: expiry.UTC(), RevokedCertificates: revokedCerts, } diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index f0327b012..abd4fe84d 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -7,14 +7,20 @@ package x509 import ( "bytes" "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/rsa" + _ "crypto/sha256" + _ "crypto/sha512" "crypto/x509/pkix" "encoding/asn1" "encoding/base64" "encoding/hex" "encoding/pem" "math/big" + "net" + "reflect" "testing" "time" ) @@ -169,6 +175,49 @@ func TestMatchHostnames(t *testing.T) { } } +func TestMatchIP(t *testing.T) { + // Check that pattern matching is working. + c := &Certificate{ + DNSNames: []string{"*.foo.bar.baz"}, + Subject: pkix.Name{ + CommonName: "*.foo.bar.baz", + }, + } + err := c.VerifyHostname("quux.foo.bar.baz") + if err != nil { + t.Fatalf("VerifyHostname(quux.foo.bar.baz): %v", err) + } + + // But check that if we change it to be matching against an IP address, + // it is rejected. + c = &Certificate{ + DNSNames: []string{"*.2.3.4"}, + Subject: pkix.Name{ + CommonName: "*.2.3.4", + }, + } + err = c.VerifyHostname("1.2.3.4") + if err == nil { + t.Fatalf("VerifyHostname(1.2.3.4) should have failed, did not") + } + + c = &Certificate{ + IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}, + } + err = c.VerifyHostname("127.0.0.1") + if err != nil { + t.Fatalf("VerifyHostname(127.0.0.1): %v", err) + } + err = c.VerifyHostname("::1") + if err != nil { + t.Fatalf("VerifyHostname(::1): %v", err) + } + err = c.VerifyHostname("[::1]") + if err != nil { + t.Fatalf("VerifyHostname([::1]): %v", err) + } +} + func TestCertificateParse(t *testing.T) { s, _ := hex.DecodeString(certBytes) certs, err := ParseCertificates(s) @@ -237,65 +286,220 @@ func TestCreateSelfSignedCertificate(t *testing.T) { random := rand.Reader block, _ := pem.Decode([]byte(pemPrivateKey)) - priv, err := ParsePKCS1PrivateKey(block.Bytes) + rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes) if err != nil { - t.Errorf("Failed to parse private key: %s", err) - return + t.Fatalf("Failed to parse private key: %s", err) } - commonName := "test.example.com" - template := Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: commonName, - Organization: []string{"Acme Co"}, - }, - NotBefore: time.Unix(1000, 0), - NotAfter: time.Unix(100000, 0), + ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("Failed to generate ECDSA key: %s", err) + } - SubjectKeyId: []byte{1, 2, 3, 4}, - KeyUsage: KeyUsageCertSign, + tests := []struct { + name string + pub, priv interface{} + checkSig bool + }{ + {"RSA/RSA", &rsaPriv.PublicKey, rsaPriv, true}, + {"RSA/ECDSA", &rsaPriv.PublicKey, ecdsaPriv, false}, + {"ECDSA/RSA", &ecdsaPriv.PublicKey, rsaPriv, false}, + {"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true}, + } - BasicConstraintsValid: true, - IsCA: true, - DNSNames: []string{"test.example.com"}, + testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth} + testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{3, 2, 1}} - PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, - PermittedDNSDomains: []string{".example.com", "example.com"}, - } + for _, test := range tests { + commonName := "test.example.com" + template := Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"Σ Acme Co"}, + }, + NotBefore: time.Unix(1000, 0), + NotAfter: time.Unix(100000, 0), - derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv) - if err != nil { - t.Errorf("Failed to create certificate: %s", err) - return - } + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: KeyUsageCertSign, - cert, err := ParseCertificate(derBytes) - if err != nil { - t.Errorf("Failed to parse certificate: %s", err) - return - } + ExtKeyUsage: testExtKeyUsage, + UnknownExtKeyUsage: testUnknownExtKeyUsage, - if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) { - t.Errorf("Failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers) - } + BasicConstraintsValid: true, + IsCA: true, - if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" { - t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains) - } + DNSNames: []string{"test.example.com"}, + EmailAddresses: []string{"gopher@golang.org"}, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")}, - if cert.Subject.CommonName != commonName { - t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName) - } + PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, + PermittedDNSDomains: []string{".example.com", "example.com"}, + } + + derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv) + if err != nil { + t.Errorf("%s: failed to create certificate: %s", test.name, err) + continue + } + + cert, err := ParseCertificate(derBytes) + if err != nil { + t.Errorf("%s: failed to parse certificate: %s", test.name, err) + continue + } + + if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) { + t.Errorf("%s: failed to parse policy identifiers: got:%#v want:%#v", test.name, cert.PolicyIdentifiers, template.PolicyIdentifiers) + } + + if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" { + t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains) + } + + if cert.Subject.CommonName != commonName { + t.Errorf("%s: subject wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Subject.CommonName, commonName) + } + + if cert.Issuer.CommonName != commonName { + t.Errorf("%s: issuer wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Issuer.CommonName, commonName) + } + + if !reflect.DeepEqual(cert.ExtKeyUsage, testExtKeyUsage) { + t.Errorf("%s: extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.ExtKeyUsage, testExtKeyUsage) + } - if cert.Issuer.CommonName != commonName { - t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName) + if !reflect.DeepEqual(cert.UnknownExtKeyUsage, testUnknownExtKeyUsage) { + t.Errorf("%s: unknown extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.UnknownExtKeyUsage, testUnknownExtKeyUsage) + } + + if !reflect.DeepEqual(cert.DNSNames, template.DNSNames) { + t.Errorf("%s: SAN DNS names differ from template. Got %v, want %v", test.name, cert.DNSNames, template.DNSNames) + } + + if !reflect.DeepEqual(cert.EmailAddresses, template.EmailAddresses) { + t.Errorf("%s: SAN emails differ from template. Got %v, want %v", test.name, cert.EmailAddresses, template.EmailAddresses) + } + + if !reflect.DeepEqual(cert.IPAddresses, template.IPAddresses) { + t.Errorf("%s: SAN IPs differ from template. Got %v, want %v", test.name, cert.IPAddresses, template.IPAddresses) + } + + if test.checkSig { + err = cert.CheckSignatureFrom(cert) + if err != nil { + t.Errorf("%s: signature verification failed: %s", test.name, err) + } + } } +} - err = cert.CheckSignatureFrom(cert) - if err != nil { - t.Errorf("Signature verification failed: %s", err) - return +// Self-signed certificate using ECDSA with SHA1 & secp256r1 +var ecdsaSHA1CertPem = ` +-----BEGIN CERTIFICATE----- +MIICDjCCAbUCCQDF6SfN0nsnrjAJBgcqhkjOPQQBMIGPMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMG +A1UECgwMR29vZ2xlLCBJbmMuMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG +CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIwMjAyMDUw +WhcNMjIwNTE4MjAyMDUwWjCBjzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm +b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTATBgNVBAoMDEdvb2dsZSwg +SW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAhBgkqhkiG9w0BCQEWFGdv +bGFuZy1kZXZAZ21haWwuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/Wgn +WQDo5+bz71T0327ERgd5SDDXFbXLpzIZDXTkjpe8QTEbsF+ezsQfrekrpDPC4Cd3 +P9LY0tG+aI8IyVKdUjAJBgcqhkjOPQQBA0gAMEUCIGlsqMcRqWVIWTD6wXwe6Jk2 +DKxL46r/FLgJYnzBEH99AiEA3fBouObsvV1R3oVkb4BQYnD4/4LeId6lAT43YvyV +a/A= +-----END CERTIFICATE----- +` + +// Self-signed certificate using ECDSA with SHA256 & secp256r1 +var ecdsaSHA256p256CertPem = ` +-----BEGIN CERTIFICATE----- +MIICDzCCAbYCCQDlsuMWvgQzhTAKBggqhkjOPQQDAjCBjzELMAkGA1UEBhMCVVMx +EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT +BgNVBAoMDEdvb2dsZSwgSW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAh +BgkqhkiG9w0BCQEWFGdvbGFuZy1kZXZAZ21haWwuY29tMB4XDTEyMDUyMTAwMTkx +NloXDTIyMDUxOTAwMTkxNlowgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp +Zm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYDVQQKDAxHb29nbGUs +IEluYy4xFzAVBgNVBAMMDnd3dy5nb29nbGUuY29tMSMwIQYJKoZIhvcNAQkBFhRn +b2xhbmctZGV2QGdtYWlsLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPMt +2ErhxAty5EJRu9yM+MTy+hUXm3pdW1ensAv382KoGExSXAFWP7pjJnNtHO+XSwVm +YNtqjcAGFKpweoN//kQwCgYIKoZIzj0EAwIDRwAwRAIgIYSaUA/IB81gjbIw/hUV +70twxJr5EcgOo0hLp3Jm+EYCIFDO3NNcgmURbJ1kfoS3N/0O+irUtoPw38YoNkqJ +h5wi +-----END CERTIFICATE----- +` + +// Self-signed certificate using ECDSA with SHA256 & secp384r1 +var ecdsaSHA256p384CertPem = ` +-----BEGIN CERTIFICATE----- +MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx +EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS +BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG +CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0 +WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm +b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg +SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s +YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK +jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze +qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI +zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr +PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh +3yILeYQzllt/g0rKVRk= +-----END CERTIFICATE----- +` + +// Self-signed certificate using ECDSA with SHA384 & secp521r1 +var ecdsaSHA384p521CertPem = ` +-----BEGIN CERTIFICATE----- +MIICljCCAfcCCQDhp1AFD/ahKjAKBggqhkjOPQQDAzCBjjELMAkGA1UEBhMCVVMx +EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS +BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG +CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMTUwNDI5 +WhcNMjIwNTE5MTUwNDI5WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm +b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg +SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s +YW5nLWRldkBnbWFpbC5jb20wgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABACqx9Rv +IssRs1LWYcNN+WffwlHw4Tv3y8/LIAA9MF1ZScIonU9nRMxt4a2uGJVCPDw6JHpz +PaYc0E9puLoE9AfKpwFr59Jkot7dBg55SKPEFkddoip/rvmN7NPAWjMBirOwjOkm +8FPthvPhGPqsu9AvgVuHu3PosWiHGNrhh379pva8MzAKBggqhkjOPQQDAwOBjAAw +gYgCQgEHNmswkUdPpHqrVxp9PvLVl+xxPuHBkT+75z9JizyxtqykHQo9Uh6SWCYH +BF9KLolo01wMt8DjoYP5Fb3j5MH7xwJCAbWZzTOp4l4DPkIvAh4LeC4VWbwPPyqh +kBg71w/iEcSY3wUKgHGcJJrObZw7wys91I5kENljqw/Samdr3ka+jBJa +-----END CERTIFICATE----- +` + +var ecdsaTests = []struct { + sigAlgo SignatureAlgorithm + pemCert string +}{ + {ECDSAWithSHA1, ecdsaSHA1CertPem}, + {ECDSAWithSHA256, ecdsaSHA256p256CertPem}, + {ECDSAWithSHA256, ecdsaSHA256p384CertPem}, + {ECDSAWithSHA384, ecdsaSHA384p521CertPem}, +} + +func TestECDSA(t *testing.T) { + for i, test := range ecdsaTests { + pemBlock, _ := pem.Decode([]byte(test.pemCert)) + cert, err := ParseCertificate(pemBlock.Bytes) + if err != nil { + t.Errorf("%d: failed to parse certificate: %s", i, err) + continue + } + if sa := cert.SignatureAlgorithm; sa != test.sigAlgo { + t.Errorf("%d: signature algorithm is %v, want %v", i, sa, test.sigAlgo) + } + if parsedKey, ok := cert.PublicKey.(*ecdsa.PublicKey); !ok { + t.Errorf("%d: wanted an ECDSA public key but found: %#v", i, parsedKey) + } + if pka := cert.PublicKeyAlgorithm; pka != ECDSA { + t.Errorf("%d: public key algorithm is %v, want ECDSA", i, pka) + } + if err = cert.CheckSignatureFrom(cert); err != nil { + t.Errorf("%d: certificate verification failed: %s", i, err) + } } } @@ -374,7 +578,7 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) { } // test cert is self-signed if err = cert.CheckSignatureFrom(cert); err != nil { - t.Fatalf("DSA Certificate verfication failed: %s", err) + t.Fatalf("DSA Certificate verification failed: %s", err) } } diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go index bfcb03ccf..853a7826c 100644 --- a/src/pkg/database/sql/convert.go +++ b/src/pkg/database/sql/convert.go @@ -14,53 +14,115 @@ import ( "strconv" ) -// subsetTypeArgs takes a slice of arguments from callers of the sql -// package and converts them into a slice of the driver package's -// "subset types". -func subsetTypeArgs(args []interface{}) ([]driver.Value, error) { - out := make([]driver.Value, len(args)) +var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error + +// driverArgs converts arguments from callers of Stmt.Exec and +// Stmt.Query into driver Values. +// +// The statement si may be nil, if no statement is available. +func driverArgs(si driver.Stmt, args []interface{}) ([]driver.Value, error) { + dargs := make([]driver.Value, len(args)) + cc, ok := si.(driver.ColumnConverter) + + // Normal path, for a driver.Stmt that is not a ColumnConverter. + if !ok { + for n, arg := range args { + var err error + dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) + } + } + return dargs, nil + } + + // Let the Stmt convert its own arguments. for n, arg := range args { + // First, see if the value itself knows how to convert + // itself to a driver type. For example, a NullString + // struct changing into a string or nil. + if svi, ok := arg.(driver.Valuer); ok { + sv, err := svi.Value() + if err != nil { + return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err) + } + if !driver.IsValue(sv) { + return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv) + } + arg = sv + } + + // Second, ask the column to sanity check itself. For + // example, drivers might use this to make sure that + // an int64 values being inserted into a 16-bit + // integer field is in range (before getting + // truncated), or that a nil can't go into a NOT NULL + // column before going across the network to get the + // same error. var err error - out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg) if err != nil { - return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err) + return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n, err) + } + if !driver.IsValue(dargs[n]) { + return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T", + arg, dargs[n]) } } - return out, nil + + return dargs, nil } // convertAssign copies to dest the value in src, converting it if possible. // An error is returned if the copy would result in loss of information. // dest should be a pointer type. func convertAssign(dest, src interface{}) error { - // Common cases, without reflect. Fall through. + // Common cases, without reflect. switch s := src.(type) { case string: switch d := dest.(type) { case *string: + if d == nil { + return errNilPtr + } *d = s return nil case *[]byte: + if d == nil { + return errNilPtr + } *d = []byte(s) return nil } case []byte: switch d := dest.(type) { case *string: + if d == nil { + return errNilPtr + } *d = string(s) return nil case *interface{}: + if d == nil { + return errNilPtr + } bcopy := make([]byte, len(s)) copy(bcopy, s) *d = bcopy return nil case *[]byte: + if d == nil { + return errNilPtr + } *d = s return nil } case nil: switch d := dest.(type) { case *[]byte: + if d == nil { + return errNilPtr + } *d = nil return nil } @@ -98,6 +160,9 @@ func convertAssign(dest, src interface{}) error { if dpv.Kind() != reflect.Ptr { return errors.New("destination not a pointer") } + if dpv.IsNil() { + return errNilPtr + } if !sv.IsValid() { sv = reflect.ValueOf(src) diff --git a/src/pkg/database/sql/doc.txt b/src/pkg/database/sql/doc.txt index fb1659548..405c5ed2a 100644 --- a/src/pkg/database/sql/doc.txt +++ b/src/pkg/database/sql/doc.txt @@ -21,7 +21,7 @@ Goals of the sql and sql/driver packages: Database Driver -> sql (to register) + sql/driver (implement interfaces) * Make type casting/conversions consistent between all drivers. To - achieve this, most of the conversions are done in the db package, + achieve this, most of the conversions are done in the sql package, not in each driver. The drivers then only have to deal with a smaller set of types. diff --git a/src/pkg/database/sql/driver/driver.go b/src/pkg/database/sql/driver/driver.go index 2f5280db8..2434e419b 100644 --- a/src/pkg/database/sql/driver/driver.go +++ b/src/pkg/database/sql/driver/driver.go @@ -56,7 +56,7 @@ var ErrBadConn = errors.New("driver: bad connection") // Execer is an optional interface that may be implemented by a Conn. // -// If a Conn does not implement Execer, the db package's DB.Exec will +// If a Conn does not implement Execer, the sql package's DB.Exec will // first prepare a query, execute the statement, and then close the // statement. // @@ -65,6 +65,17 @@ type Execer interface { Exec(query string, args []Value) (Result, error) } +// Queryer is an optional interface that may be implemented by a Conn. +// +// If a Conn does not implement Queryer, the sql package's DB.Query will +// first prepare a query, execute the statement, and then close the +// statement. +// +// Query may return ErrSkip. +type Queryer interface { + Query(query string, args []Value) (Rows, error) +} + // Conn is a connection to a database. It is not used concurrently // by multiple goroutines. // @@ -104,23 +115,8 @@ type Result interface { type Stmt interface { // Close closes the statement. // - // Closing a statement should not interrupt any outstanding - // query created from that statement. That is, the following - // order of operations is valid: - // - // * create a driver statement - // * call Query on statement, returning Rows - // * close the statement - // * read from Rows - // - // If closing a statement invalidates currently-running - // queries, the final step above will incorrectly fail. - // - // TODO(bradfitz): possibly remove the restriction above, if - // enough driver authors object and find it complicates their - // code too much. The sql package could be smarter about - // refcounting the statement and closing it at the appropriate - // time. + // As of Go 1.1, a Stmt will not be closed if it's in use + // by any queries. Close() error // NumInput returns the number of placeholder parameters. diff --git a/src/pkg/database/sql/example_test.go b/src/pkg/database/sql/example_test.go new file mode 100644 index 000000000..d47eed50c --- /dev/null +++ b/src/pkg/database/sql/example_test.go @@ -0,0 +1,45 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sql_test + +import ( + "database/sql" + "fmt" + "log" +) + +var db *sql.DB + +func ExampleDB_Query() { + age := 27 + rows, err := db.Query("SELECT name FROM users WHERE age=?", age) + if err != nil { + log.Fatal(err) + } + for rows.Next() { + var name string + if err := rows.Scan(&name); err != nil { + log.Fatal(err) + } + fmt.Printf("%s is %d\n", name, age) + } + if err := rows.Err(); err != nil { + log.Fatal(err) + } +} + +func ExampleDB_QueryRow() { + id := 123 + var username string + err := db.QueryRow("SELECT username FROM users WHERE id=?", id).Scan(&username) + switch { + case err == sql.ErrNoRows: + log.Printf("No user with that ID.") + case err != nil: + log.Fatal(err) + default: + fmt.Printf("Username is %s\n", username) + } +} diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go index 184e7756c..55597f7de 100644 --- a/src/pkg/database/sql/fakedb_test.go +++ b/src/pkg/database/sql/fakedb_test.go @@ -31,7 +31,7 @@ var _ = log.Printf // INSERT||col=val,col2=val2,col3=? // SELECT||projectcol1,projectcol2|filtercol=?,filtercol2=? // -// When opening a a fakeDriver's database, it starts empty with no +// When opening a fakeDriver's database, it starts empty with no // tables. All tables and data are stored in memory only. type fakeDriver struct { mu sync.Mutex @@ -42,9 +42,10 @@ type fakeDriver struct { type fakeDB struct { name string - mu sync.Mutex - free []*fakeConn - tables map[string]*table + mu sync.Mutex + free []*fakeConn + tables map[string]*table + badConn bool } type table struct { @@ -83,6 +84,7 @@ type fakeConn struct { stmtsMade int stmtsClosed int numPrepare int + bad bool } func (c *fakeConn) incrStat(v *int) { @@ -122,7 +124,9 @@ func init() { // Supports dsn forms: // -// ; (no currently supported options) +// ; (only currently supported option is `badConn`, +// which causes driver.ErrBadConn to be returned on +// every other conn.Begin()) func (d *fakeDriver) Open(dsn string) (driver.Conn, error) { parts := strings.Split(dsn, ";") if len(parts) < 1 { @@ -135,7 +139,12 @@ func (d *fakeDriver) Open(dsn string) (driver.Conn, error) { d.mu.Lock() d.openCount++ d.mu.Unlock() - return &fakeConn{db: db}, nil + conn := &fakeConn{db: db} + + if len(parts) >= 2 && parts[1] == "badConn" { + conn.bad = true + } + return conn, nil } func (d *fakeDriver) getDB(name string) *fakeDB { @@ -199,7 +208,20 @@ func (db *fakeDB) columnType(table, column string) (typ string, ok bool) { return "", false } +func (c *fakeConn) isBad() bool { + // if not simulating bad conn, do nothing + if !c.bad { + return false + } + // alternate between bad conn and not bad conn + c.db.badConn = !c.db.badConn + return c.db.badConn +} + func (c *fakeConn) Begin() (driver.Tx, error) { + if c.isBad() { + return nil, driver.ErrBadConn + } if c.currTx != nil { return nil, errors.New("already in a transaction") } @@ -234,7 +256,19 @@ func checkSubsetTypes(args []driver.Value) error { func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) { // This is an optional interface, but it's implemented here - // just to check that all the args of of the proper types. + // just to check that all the args are of the proper types. + // ErrSkip is returned so the caller acts as if we didn't + // implement this at all. + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + return nil, driver.ErrSkip +} + +func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) { + // This is an optional interface, but it's implemented here + // just to check that all the args are of the proper types. // ErrSkip is returned so the caller acts as if we didn't // implement this at all. err := checkSubsetTypes(args) @@ -249,7 +283,7 @@ func errf(msg string, args ...interface{}) error { } // parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=? -// (note that where where columns must always contain ? marks, +// (note that where columns must always contain ? marks, // just a limitation for fakedb) func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) { if len(parts) != 3 { @@ -383,6 +417,9 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) { } func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter { + if len(s.placeholderConverter) == 0 { + return driver.DefaultParameterConverter + } return s.placeholderConverter[idx] } @@ -598,6 +635,28 @@ func (rc *rowsCursor) Next(dest []driver.Value) error { return nil } +// fakeDriverString is like driver.String, but indirects pointers like +// DefaultValueConverter. +// +// This could be surprising behavior to retroactively apply to +// driver.String now that Go1 is out, but this is convenient for +// our TestPointerParamsAndScans. +// +type fakeDriverString struct{} + +func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) { + switch c := v.(type) { + case string, []byte: + return v, nil + case *string: + if c == nil { + return nil, nil + } + return *c, nil + } + return fmt.Sprintf("%v", v), nil +} + func converterForType(typ string) driver.ValueConverter { switch typ { case "bool": @@ -607,9 +666,9 @@ func converterForType(typ string) driver.ValueConverter { case "int32": return driver.Int32 case "string": - return driver.NotNull{Converter: driver.String} + return driver.NotNull{Converter: fakeDriverString{}} case "nullstring": - return driver.Null{Converter: driver.String} + return driver.Null{Converter: fakeDriverString{}} case "int64": // TODO(coopernurse): add type-specific converter return driver.NotNull{Converter: driver.DefaultParameterConverter} diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go index 89136ef6e..4faaa11b1 100644 --- a/src/pkg/database/sql/sql.go +++ b/src/pkg/database/sql/sql.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "io" + "runtime" "sync" ) @@ -189,9 +190,66 @@ type DB struct { driver driver.Driver dsn string - mu sync.Mutex // protects freeConn and closed - freeConn []driver.Conn - closed bool + mu sync.Mutex // protects following fields + outConn map[driver.Conn]bool // whether the conn is in use + freeConn []driver.Conn + closed bool + dep map[finalCloser]depSet + onConnPut map[driver.Conn][]func() // code (with mu held) run when conn is next returned + lastPut map[driver.Conn]string // stacktrace of last conn's put; debug only +} + +// depSet is a finalCloser's outstanding dependencies +type depSet map[interface{}]bool // set of true bools + +// The finalCloser interface is used by (*DB).addDep and (*DB).get +type finalCloser interface { + // finalClose is called when the reference count of an object + // goes to zero. (*DB).mu is not held while calling it. + finalClose() error +} + +// addDep notes that x now depends on dep, and x's finalClose won't be +// called until all of x's dependencies are removed with removeDep. +func (db *DB) addDep(x finalCloser, dep interface{}) { + //println(fmt.Sprintf("addDep(%T %p, %T %p)", x, x, dep, dep)) + db.mu.Lock() + defer db.mu.Unlock() + if db.dep == nil { + db.dep = make(map[finalCloser]depSet) + } + xdep := db.dep[x] + if xdep == nil { + xdep = make(depSet) + db.dep[x] = xdep + } + xdep[dep] = true +} + +// removeDep notes that x no longer depends on dep. +// If x still has dependencies, nil is returned. +// If x no longer has any dependencies, its finalClose method will be +// called and its error value will be returned. +func (db *DB) removeDep(x finalCloser, dep interface{}) error { + //println(fmt.Sprintf("removeDep(%T %p, %T %p)", x, x, dep, dep)) + done := false + + db.mu.Lock() + xdep := db.dep[x] + if xdep != nil { + delete(xdep, dep) + if len(xdep) == 0 { + delete(db.dep, x) + done = true + } + } + db.mu.Unlock() + + if !done { + return nil + } + //println(fmt.Sprintf("calling final close on %T %v (%#v)", x, x, x)) + return x.finalClose() } // Open opens a database specified by its database driver name and a @@ -201,11 +259,20 @@ type DB struct { // Most users will open a database via a driver-specific connection // helper function that returns a *DB. func Open(driverName, dataSourceName string) (*DB, error) { - driver, ok := drivers[driverName] + driveri, ok := drivers[driverName] if !ok { return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName) } - return &DB{driver: driver, dsn: dataSourceName}, nil + // TODO: optionally proactively connect to a Conn to check + // the dataSourceName: golang.org/issue/4804 + db := &DB{ + driver: driveri, + dsn: dataSourceName, + outConn: make(map[driver.Conn]bool), + lastPut: make(map[driver.Conn]string), + onConnPut: make(map[driver.Conn][]func()), + } + return db, nil } // Close closes the database, releasing any open resources. @@ -241,22 +308,38 @@ func (db *DB) conn() (driver.Conn, error) { if n := len(db.freeConn); n > 0 { conn := db.freeConn[n-1] db.freeConn = db.freeConn[:n-1] + db.outConn[conn] = true db.mu.Unlock() return conn, nil } db.mu.Unlock() - return db.driver.Open(db.dsn) + conn, err := db.driver.Open(db.dsn) + if err == nil { + db.mu.Lock() + db.outConn[conn] = true + db.mu.Unlock() + } + return conn, err } +// connIfFree returns (wanted, true) if wanted is still a valid conn and +// isn't in use. +// +// If wanted is valid but in use, connIfFree returns (wanted, false). +// If wanted is invalid, connIfFre returns (nil, false). func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) { db.mu.Lock() defer db.mu.Unlock() + if db.outConn[wanted] { + return conn, false + } for i, conn := range db.freeConn { if conn != wanted { continue } db.freeConn[i] = db.freeConn[len(db.freeConn)-1] db.freeConn = db.freeConn[:len(db.freeConn)-1] + db.outConn[wanted] = true return wanted, true } return nil, false @@ -265,14 +348,52 @@ func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) { // putConnHook is a hook for testing. var putConnHook func(*DB, driver.Conn) +// noteUnusedDriverStatement notes that si is no longer used and should +// be closed whenever possible (when c is next not in use), unless c is +// already closed. +func (db *DB) noteUnusedDriverStatement(c driver.Conn, si driver.Stmt) { + db.mu.Lock() + defer db.mu.Unlock() + if db.outConn[c] { + db.onConnPut[c] = append(db.onConnPut[c], func() { + si.Close() + }) + } else { + si.Close() + } +} + +// debugGetPut determines whether getConn & putConn calls' stack traces +// are returned for more verbose crashes. +const debugGetPut = false + // putConn adds a connection to the db's free pool. -// err is optionally the last error that occured on this connection. +// err is optionally the last error that occurred on this connection. func (db *DB) putConn(c driver.Conn, err error) { + db.mu.Lock() + if !db.outConn[c] { + if debugGetPut { + fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", c, stack(), db.lastPut[c]) + } + panic("sql: connection returned that was never out") + } + if debugGetPut { + db.lastPut[c] = stack() + } + delete(db.outConn, c) + + if fns, ok := db.onConnPut[c]; ok { + for _, fn := range fns { + fn() + } + delete(db.onConnPut, c) + } + if err == driver.ErrBadConn { // Don't reuse bad connections. + db.mu.Unlock() return } - db.mu.Lock() if putConnHook != nil { putConnHook(db, c) } @@ -287,7 +408,9 @@ func (db *DB) putConn(c driver.Conn, err error) { c.Close() } -// Prepare creates a prepared statement for later execution. +// Prepare creates a prepared statement for later queries or executions. +// Multiple queries or executions may be run concurrently from the +// returned statement. func (db *DB) Prepare(query string) (*Stmt, error) { var stmt *Stmt var err error @@ -300,7 +423,7 @@ func (db *DB) Prepare(query string) (*Stmt, error) { return stmt, err } -func (db *DB) prepare(query string) (stmt *Stmt, err error) { +func (db *DB) prepare(query string) (*Stmt, error) { // TODO: check if db.driver supports an optional // driver.Preparer interface and call that instead, if so, // otherwise we make a prepared statement that's bound @@ -311,28 +434,27 @@ func (db *DB) prepare(query string) (stmt *Stmt, err error) { if err != nil { return nil, err } - defer db.putConn(ci, err) si, err := ci.Prepare(query) if err != nil { + db.putConn(ci, err) return nil, err } - stmt = &Stmt{ + stmt := &Stmt{ db: db, query: query, css: []connStmt{{ci, si}}, } + db.addDep(stmt, stmt) return stmt, nil } // Exec executes a query without returning any rows. +// The args are for any placeholder parameters in the query. func (db *DB) Exec(query string, args ...interface{}) (Result, error) { - sargs, err := subsetTypeArgs(args) - if err != nil { - return nil, err - } var res Result + var err error for i := 0; i < 10; i++ { - res, err = db.exec(query, sargs) + res, err = db.exec(query, args) if err != driver.ErrBadConn { break } @@ -340,15 +462,21 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) { return res, err } -func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) { +func (db *DB) exec(query string, args []interface{}) (res Result, err error) { ci, err := db.conn() if err != nil { return nil, err } - defer db.putConn(ci, err) + defer func() { + db.putConn(ci, err) + }() if execer, ok := ci.(driver.Execer); ok { - resi, err := execer.Exec(query, sargs) + dargs, err := driverArgs(nil, args) + if err != nil { + return nil, err + } + resi, err := execer.Exec(query, dargs) if err != driver.ErrSkip { if err != nil { return nil, err @@ -363,25 +491,83 @@ func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) { } defer sti.Close() - resi, err := sti.Exec(sargs) + return resultFromStatement(sti, args...) +} + +// Query executes a query that returns rows, typically a SELECT. +// The args are for any placeholder parameters in the query. +func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { + var rows *Rows + var err error + for i := 0; i < 10; i++ { + rows, err = db.query(query, args) + if err != driver.ErrBadConn { + break + } + } + return rows, err +} + +func (db *DB) query(query string, args []interface{}) (*Rows, error) { + ci, err := db.conn() if err != nil { return nil, err } - return result{resi}, nil + + releaseConn := func(err error) { db.putConn(ci, err) } + + return db.queryConn(ci, releaseConn, query, args) } -// Query executes a query that returns rows, typically a SELECT. -func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { - stmt, err := db.Prepare(query) +// queryConn executes a query on the given connection. +// The connection gets released by the releaseConn function. +func (db *DB) queryConn(ci driver.Conn, releaseConn func(error), query string, args []interface{}) (*Rows, error) { + if queryer, ok := ci.(driver.Queryer); ok { + dargs, err := driverArgs(nil, args) + if err != nil { + releaseConn(err) + return nil, err + } + rowsi, err := queryer.Query(query, dargs) + if err != driver.ErrSkip { + if err != nil { + releaseConn(err) + return nil, err + } + // Note: ownership of ci passes to the *Rows, to be freed + // with releaseConn. + rows := &Rows{ + db: db, + ci: ci, + releaseConn: releaseConn, + rowsi: rowsi, + } + return rows, nil + } + } + + sti, err := ci.Prepare(query) if err != nil { + releaseConn(err) return nil, err } - rows, err := stmt.Query(args...) + + rowsi, err := rowsiFromStatement(sti, args...) if err != nil { - stmt.Close() + releaseConn(err) + sti.Close() return nil, err } - rows.closeStmt = stmt + + // Note: ownership of ci passes to the *Rows, to be freed + // with releaseConn. + rows := &Rows{ + db: db, + ci: ci, + releaseConn: releaseConn, + rowsi: rowsi, + closeStmt: sti, + } return rows, nil } @@ -415,7 +601,7 @@ func (db *DB) begin() (tx *Tx, err error) { txi, err := ci.Begin() if err != nil { db.putConn(ci, err) - return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err) + return nil, err } return &Tx{ db: db, @@ -577,13 +763,12 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { } defer tx.releaseConn() - sargs, err := subsetTypeArgs(args) - if err != nil { - return nil, err - } - if execer, ok := ci.(driver.Execer); ok { - resi, err := execer.Exec(query, sargs) + dargs, err := driverArgs(nil, args) + if err != nil { + return nil, err + } + resi, err := execer.Exec(query, dargs) if err == nil { return result{resi}, nil } @@ -598,29 +783,19 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { } defer sti.Close() - resi, err := sti.Exec(sargs) - if err != nil { - return nil, err - } - return result{resi}, nil + return resultFromStatement(sti, args...) } // Query executes a query that returns rows, typically a SELECT. func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { - if tx.done { - return nil, ErrTxDone - } - stmt, err := tx.Prepare(query) - if err != nil { - return nil, err - } - rows, err := stmt.Query(args...) + ci, err := tx.grabConn() if err != nil { - stmt.Close() return nil, err } - rows.closeStmt = stmt - return rows, err + + releaseConn := func(err error) { tx.releaseConn() } + + return tx.db.queryConn(ci, releaseConn, query, args) } // QueryRow executes a query that is expected to return at most one row. @@ -644,6 +819,8 @@ type Stmt struct { query string // that created the Stmt stickyErr error // if non-nil, this error is returned for all operations + closemu sync.RWMutex // held exclusively during close, for read otherwise. + // If in a transaction, else both nil: tx *Tx txsi driver.Stmt @@ -661,12 +838,18 @@ type Stmt struct { // Exec executes a prepared statement with the given arguments and // returns a Result summarizing the effect of the statement. func (s *Stmt) Exec(args ...interface{}) (Result, error) { + s.closemu.RLock() + defer s.closemu.RUnlock() _, releaseConn, si, err := s.connStmt() if err != nil { return nil, err } defer releaseConn(nil) + return resultFromStatement(si, args...) +} + +func resultFromStatement(si driver.Stmt, args ...interface{}) (Result, error) { // -1 means the driver doesn't know how to count the number of // placeholders, so we won't sanity check input here and instead let the // driver deal with errors. @@ -674,51 +857,12 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) { return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args)) } - sargs := make([]driver.Value, len(args)) - - // Convert args to subset types. - if cc, ok := si.(driver.ColumnConverter); ok { - for n, arg := range args { - // First, see if the value itself knows how to convert - // itself to a driver type. For example, a NullString - // struct changing into a string or nil. - if svi, ok := arg.(driver.Valuer); ok { - sv, err := svi.Value() - if err != nil { - return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err) - } - if !driver.IsValue(sv) { - return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv) - } - arg = sv - } - - // Second, ask the column to sanity check itself. For - // example, drivers might use this to make sure that - // an int64 values being inserted into a 16-bit - // integer field is in range (before getting - // truncated), or that a nil can't go into a NOT NULL - // column before going across the network to get the - // same error. - sargs[n], err = cc.ColumnConverter(n).ConvertValue(arg) - if err != nil { - return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) - } - if !driver.IsValue(sargs[n]) { - return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T", - arg, sargs[n]) - } - } - } else { - for n, arg := range args { - sargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg) - if err != nil { - return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) - } - } + dargs, err := driverArgs(si, args) + if err != nil { + return nil, err } - resi, err := si.Exec(sargs) + resi, err := si.Exec(dargs) if err != nil { return nil, err } @@ -794,35 +938,54 @@ func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(error), si driver.St // Query executes a prepared query statement with the given arguments // and returns the query results as a *Rows. func (s *Stmt) Query(args ...interface{}) (*Rows, error) { + s.closemu.RLock() + defer s.closemu.RUnlock() + ci, releaseConn, si, err := s.connStmt() if err != nil { return nil, err } + rowsi, err := rowsiFromStatement(si, args...) + if err != nil { + releaseConn(err) + return nil, err + } + + // Note: ownership of ci passes to the *Rows, to be freed + // with releaseConn. + rows := &Rows{ + db: s.db, + ci: ci, + rowsi: rowsi, + // releaseConn set below + } + s.db.addDep(s, rows) + rows.releaseConn = func(err error) { + releaseConn(err) + s.db.removeDep(s, rows) + } + return rows, nil +} + +func rowsiFromStatement(si driver.Stmt, args ...interface{}) (driver.Rows, error) { // -1 means the driver doesn't know how to count the number of // placeholders, so we won't sanity check input here and instead let the // driver deal with errors. if want := si.NumInput(); want != -1 && len(args) != want { return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", si.NumInput(), len(args)) } - sargs, err := subsetTypeArgs(args) + + dargs, err := driverArgs(si, args) if err != nil { return nil, err } - rowsi, err := si.Query(sargs) + + rowsi, err := si.Query(dargs) if err != nil { - releaseConn(err) return nil, err } - // Note: ownership of ci passes to the *Rows, to be freed - // with releaseConn. - rows := &Rows{ - db: s.db, - ci: ci, - releaseConn: releaseConn, - rowsi: rowsi, - } - return rows, nil + return rowsi, nil } // QueryRow executes a prepared query statement with the given arguments. @@ -846,6 +1009,9 @@ func (s *Stmt) QueryRow(args ...interface{}) *Row { // Close closes the statement. func (s *Stmt) Close() error { + s.closemu.Lock() + defer s.closemu.Unlock() + if s.stickyErr != nil { return s.stickyErr } @@ -858,18 +1024,17 @@ func (s *Stmt) Close() error { if s.tx != nil { s.txsi.Close() - } else { - for _, v := range s.css { - if ci, match := s.db.connIfFree(v.ci); match { - v.si.Close() - s.db.putConn(ci, nil) - } else { - // TODO(bradfitz): care that we can't close - // this statement because the statement's - // connection is in use? - } - } + return nil + } + + return s.db.removeDep(s, s) +} + +func (s *Stmt) finalClose() error { + for _, v := range s.css { + s.db.noteUnusedDriverStatement(v.ci, v.si) } + s.css = nil return nil } @@ -888,14 +1053,14 @@ func (s *Stmt) Close() error { // ... type Rows struct { db *DB - ci driver.Conn // owned; must call putconn when closed to release + ci driver.Conn // owned; must call releaseConn when closed to release releaseConn func(error) rowsi driver.Rows closed bool lastcols []driver.Value lasterr error - closeStmt *Stmt // if non-nil, statement to Close on close + closeStmt driver.Stmt // if non-nil, statement to Close on close } // Next prepares the next result row for reading with the Scan method. @@ -1064,3 +1229,8 @@ type Result interface { type result struct { driver.Result } + +func stack() string { + var buf [1024]byte + return string(buf[:runtime.Stack(buf[:], false)]) +} diff --git a/src/pkg/database/sql/sql_test.go b/src/pkg/database/sql/sql_test.go index b29670586..53b229600 100644 --- a/src/pkg/database/sql/sql_test.go +++ b/src/pkg/database/sql/sql_test.go @@ -8,7 +8,6 @@ import ( "database/sql/driver" "fmt" "reflect" - "runtime" "strings" "testing" "time" @@ -63,6 +62,10 @@ func exec(t *testing.T, db *DB, query string, args ...interface{}) { } func closeDB(t *testing.T, db *DB) { + if e := recover(); e != nil { + fmt.Printf("Panic: %v\n", e) + panic(e) + } err := db.Close() if err != nil { t.Fatalf("error closing DB: %v", err) @@ -270,6 +273,35 @@ func TestStatementQueryRow(t *testing.T) { } +// golang.org/issue/3734 +func TestStatementQueryRowConcurrent(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + stmt, err := db.Prepare("SELECT|people|age|name=?") + if err != nil { + t.Fatalf("Prepare: %v", err) + } + defer stmt.Close() + + const n = 10 + ch := make(chan error, n) + for i := 0; i < n; i++ { + go func() { + var age int + err := stmt.QueryRow("Alice").Scan(&age) + if err == nil && age != 1 { + err = fmt.Errorf("unexpected age %d", age) + } + ch <- err + }() + } + for i := 0; i < n; i++ { + if err := <-ch; err != nil { + t.Error(err) + } + } +} + // just a test of fakedb itself func TestBogusPreboundParameters(t *testing.T) { db := newTestDB(t, "foo") @@ -306,8 +338,8 @@ func TestExec(t *testing.T) { {[]interface{}{7, 9}, ""}, // Invalid conversions: - {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"}, - {[]interface{}{"Brad", "strconv fail"}, "sql: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"}, + {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting argument #1's type: sql/driver: value 4294967295 overflows int32"}, + {[]interface{}{"Brad", "strconv fail"}, "sql: converting argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"}, // Wrong number of args: {[]interface{}{}, "sql: expected 2 arguments, got 0"}, @@ -402,6 +434,39 @@ func TestTxQueryInvalid(t *testing.T) { } } +// Tests fix for issue 4433, that retries in Begin happen when +// conn.Begin() returns ErrBadConn +func TestTxErrBadConn(t *testing.T) { + db, err := Open("test", fakeDBName+";badConn") + if err != nil { + t.Fatalf("Open: %v", err) + } + if _, err := db.Exec("WIPE"); err != nil { + t.Fatalf("exec wipe: %v", err) + } + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + stmt, err := db.Prepare("INSERT|t1|name=?,age=?") + if err != nil { + t.Fatalf("Stmt, err = %v, %v", stmt, err) + } + defer stmt.Close() + tx, err := db.Begin() + if err != nil { + t.Fatalf("Begin = %v", err) + } + txs := tx.Stmt(stmt) + defer txs.Close() + _, err = txs.Exec("Bobby", 7) + if err != nil { + t.Fatalf("Exec = %v", err) + } + err = tx.Commit() + if err != nil { + t.Fatalf("Commit = %v", err) + } +} + // Tests fix for issue 2542, that we release a lock when querying on // a closed connection. func TestIssue2542Deadlock(t *testing.T) { @@ -415,6 +480,30 @@ func TestIssue2542Deadlock(t *testing.T) { } } +// From golang.org/issue/3865 +func TestCloseStmtBeforeRows(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + + s, err := db.Prepare("SELECT|people|name|") + if err != nil { + t.Fatal(err) + } + + r, err := s.Query() + if err != nil { + s.Close() + t.Fatal(err) + } + + err = s.Close() + if err != nil { + t.Fatal(err) + } + + r.Close() +} + // Tests fix for issue 2788, that we bind nil to a []byte if the // value in the column is sql null func TestNullByteSlice(t *testing.T) { @@ -608,7 +697,14 @@ func nullTestRun(t *testing.T, spec nullTestSpec) { } } -func stack() string { - buf := make([]byte, 1024) - return string(buf[:runtime.Stack(buf, false)]) +// golang.org/issue/4859 +func TestQueryRowNilScanDest(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + var name *string // nil pointer + err := db.QueryRow("SELECT|people|name|").Scan(name) + want := "sql: Scan error on column index 0: destination pointer is nil" + if err == nil || err.Error() != want { + t.Errorf("error = %q; want %q", err.Error(), want) + } } diff --git a/src/pkg/debug/dwarf/const.go b/src/pkg/debug/dwarf/const.go index 918b153d0..ad696dc32 100644 --- a/src/pkg/debug/dwarf/const.go +++ b/src/pkg/debug/dwarf/const.go @@ -207,6 +207,7 @@ const ( formRef8 format = 0x14 formRefUdata format = 0x15 formIndirect format = 0x16 + formFlagPresent format = 0x19 ) // A Tag is the classification (the type) of an Entry. diff --git a/src/pkg/debug/dwarf/entry.go b/src/pkg/debug/dwarf/entry.go index 2885d8fa2..f376e4088 100644 --- a/src/pkg/debug/dwarf/entry.go +++ b/src/pkg/debug/dwarf/entry.go @@ -185,6 +185,10 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { // flag case formFlag: val = b.uint8() == 1 + case formFlagPresent: + // The attribute is implicitly indicated as present, and no value is + // encoded in the debugging information entry itself. + val = true // reference to other entry case formRefAddr: diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index 184ca8375..acb9817af 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -31,6 +31,7 @@ type FileHeader struct { ByteOrder binary.ByteOrder Type Type Machine Machine + Entry uint64 } // A File represents an open ELF file. @@ -240,6 +241,7 @@ func NewFile(r io.ReaderAt) (*File, error) { } f.Type = Type(hdr.Type) f.Machine = Machine(hdr.Machine) + f.Entry = uint64(hdr.Entry) if v := Version(hdr.Version); v != f.Version { return nil, &FormatError{0, "mismatched ELF version", v} } @@ -258,6 +260,7 @@ func NewFile(r io.ReaderAt) (*File, error) { } f.Type = Type(hdr.Type) f.Machine = Machine(hdr.Machine) + f.Entry = uint64(hdr.Entry) if v := Version(hdr.Version); v != f.Version { return nil, &FormatError{0, "mismatched ELF version", v} } @@ -269,7 +272,8 @@ func NewFile(r io.ReaderAt) (*File, error) { shnum = int(hdr.Shnum) shstrndx = int(hdr.Shstrndx) } - if shstrndx < 0 || shstrndx >= shnum { + + if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) { return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx} } @@ -364,6 +368,10 @@ func NewFile(r io.ReaderAt) (*File, error) { f.Sections[i] = s } + if len(f.Sections) == 0 { + return f, nil + } + // Load section header string table. shstrtab, err := f.Sections[shstrndx].Data() if err != nil { @@ -414,10 +422,6 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { return nil, nil, errors.New("cannot load string table section") } - // The first entry is all zeros. - var skip [Sym32Size]byte - symtab.Read(skip[0:]) - symbols := make([]Symbol, symtab.Len()/Sym32Size) i := 0 @@ -457,10 +461,6 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { return nil, nil, errors.New("cannot load string table section") } - // The first entry is all zeros. - var skip [Sym64Size]byte - symtab.Read(skip[0:]) - symbols := make([]Symbol, symtab.Len()/Sym64Size) i := 0 @@ -705,8 +705,8 @@ func (f *File) gnuVersionInit(str []byte) { // gnuVersion adds Library and Version information to sym, // which came from offset i of the symbol table. func (f *File) gnuVersion(i int, sym *ImportedSymbol) { - // Each entry is two bytes; skip undef entry at beginning. - i = (i + 1) * 2 + // Each entry is two bytes. + i = i * 2 if i >= len(f.gnuVersym) { return } @@ -723,6 +723,20 @@ func (f *File) gnuVersion(i int, sym *ImportedSymbol) { // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. func (f *File) ImportedLibraries() ([]string, error) { + return f.DynString(DT_NEEDED) +} + +// DynString returns the strings listed for the given tag in the file's dynamic +// section. +// +// The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or +// DT_RUNPATH. +func (f *File) DynString(tag DynTag) ([]string, error) { + switch tag { + case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH: + default: + return nil, fmt.Errorf("non-string-valued tag %v", tag) + } ds := f.SectionByType(SHT_DYNAMIC) if ds == nil { // not dynamic, so no libraries @@ -738,25 +752,24 @@ func (f *File) ImportedLibraries() ([]string, error) { } var all []string for len(d) > 0 { - var tag DynTag - var value uint64 + var t DynTag + var v uint64 switch f.Class { case ELFCLASS32: - tag = DynTag(f.ByteOrder.Uint32(d[0:4])) - value = uint64(f.ByteOrder.Uint32(d[4:8])) + t = DynTag(f.ByteOrder.Uint32(d[0:4])) + v = uint64(f.ByteOrder.Uint32(d[4:8])) d = d[8:] case ELFCLASS64: - tag = DynTag(f.ByteOrder.Uint64(d[0:8])) - value = f.ByteOrder.Uint64(d[8:16]) + t = DynTag(f.ByteOrder.Uint64(d[0:8])) + v = f.ByteOrder.Uint64(d[8:16]) d = d[16:] } - if tag == DT_NEEDED { - s, ok := getString(str, int(value)) + if t == tag { + s, ok := getString(str, int(v)) if ok { all = append(all, s) } } } - return all, nil } diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 98f2723c8..f9aa7265a 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -5,10 +5,14 @@ package elf import ( + "bytes" + "compress/gzip" "debug/dwarf" "encoding/binary" + "io" "net" "os" + "path" "reflect" "runtime" "testing" @@ -19,12 +23,13 @@ type fileTest struct { hdr FileHeader sections []SectionHeader progs []ProgHeader + needed []string } var fileTests = []fileTest{ { "testdata/gcc-386-freebsd-exec", - FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386}, + FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386, 0x80483cc}, []SectionHeader{ {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, {".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0}, @@ -64,10 +69,11 @@ var fileTests = []fileTest{ {PT_LOAD, PF_R + PF_W, 0x5fc, 0x80495fc, 0x80495fc, 0xd8, 0xf8, 0x1000}, {PT_DYNAMIC, PF_R + PF_W, 0x60c, 0x804960c, 0x804960c, 0x98, 0x98, 0x4}, }, + []string{"libc.so.6"}, }, { "testdata/gcc-amd64-linux-exec", - FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64}, + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64, 0x4003e0}, []SectionHeader{ {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, {".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0}, @@ -117,6 +123,32 @@ var fileTests = []fileTest{ {PT_LOOS + 0x474E550, PF_R, 0x5b8, 0x4005b8, 0x4005b8, 0x24, 0x24, 0x4}, {PT_LOOS + 0x474E551, PF_R + PF_W, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8}, }, + []string{"libc.so.6"}, + }, + { + "testdata/hello-world-core.gz", + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_CORE, EM_X86_64, 0x0}, + []SectionHeader{}, + []ProgHeader{ + {Type: PT_NOTE, Flags: 0x0, Off: 0x3f8, Vaddr: 0x0, Paddr: 0x0, Filesz: 0x8ac, Memsz: 0x0, Align: 0x0}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x1000, Vaddr: 0x400000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x1000, Vaddr: 0x401000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x2000, Vaddr: 0x402000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3000, Vaddr: 0x7f54078b8000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1b5000, Align: 0x1000}, + {Type: PT_LOAD, Flags: 0x0, Off: 0x3000, Vaddr: 0x7f5407a6d000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1ff000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x3000, Vaddr: 0x7f5407c6c000, Paddr: 0x0, Filesz: 0x4000, Memsz: 0x4000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x7000, Vaddr: 0x7f5407c70000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x9000, Vaddr: 0x7f5407c72000, Paddr: 0x0, Filesz: 0x5000, Memsz: 0x5000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0xe000, Vaddr: 0x7f5407c77000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x22000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0xe000, Vaddr: 0x7f5407e81000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x11000, Vaddr: 0x7f5407e96000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x14000, Vaddr: 0x7f5407e99000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x15000, Vaddr: 0x7f5407e9a000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x17000, Vaddr: 0x7fff79972000, Paddr: 0x0, Filesz: 0x23000, Memsz: 0x23000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3a000, Vaddr: 0x7fff799f8000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3b000, Vaddr: 0xffffffffff600000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + }, + nil, }, } @@ -124,9 +156,18 @@ func TestOpen(t *testing.T) { for i := range fileTests { tt := &fileTests[i] - f, err := Open(tt.file) + var f *File + var err error + if path.Ext(tt.file) == ".gz" { + var r io.ReaderAt + if r, err = decompress(tt.file); err == nil { + f, err = NewFile(r) + } + } else { + f, err = Open(tt.file) + } if err != nil { - t.Error(err) + t.Errorf("cannot open file %s: %v", tt.file, err) continue } if !reflect.DeepEqual(f.FileHeader, tt.hdr) { @@ -161,26 +202,69 @@ func TestOpen(t *testing.T) { if tn != fn { t.Errorf("open %s: len(Progs) = %d, want %d", tt.file, fn, tn) } + tl := tt.needed + fl, err := f.ImportedLibraries() + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(tl, fl) { + t.Errorf("open %s: DT_NEEDED = %v, want %v", tt.file, tl, fl) + } } } +// elf.NewFile requires io.ReaderAt, which compress/gzip cannot +// provide. Decompress the file to a bytes.Reader. +func decompress(gz string) (io.ReaderAt, error) { + in, err := os.Open(gz) + if err != nil { + return nil, err + } + defer in.Close() + r, err := gzip.NewReader(in) + if err != nil { + return nil, err + } + var out bytes.Buffer + _, err = io.Copy(&out, r) + return bytes.NewReader(out.Bytes()), err +} + +type relocationTestEntry struct { + entryNumber int + entry *dwarf.Entry +} + type relocationTest struct { - file string - firstEntry *dwarf.Entry + file string + entries []relocationTestEntry } var relocationTests = []relocationTest{ { "testdata/go-relocation-test-gcc441-x86-64.obj", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, }, { "testdata/go-relocation-test-gcc441-x86.obj", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, }, { "testdata/go-relocation-test-gcc424-x86-64.obj", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/gcc-amd64-openbsd-debug-with-rela.obj", + []relocationTestEntry{ + {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(236)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}}}}}, + {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(237)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}}}}}, + }, }, } @@ -196,20 +280,24 @@ func TestDWARFRelocations(t *testing.T) { t.Error(err) continue } - reader := dwarf.Reader() - // Checking only the first entry is sufficient since it has - // many different strings. If the relocation had failed, all - // the string offsets would be zero and all the strings would - // end up being the same. - firstEntry, err := reader.Next() - if err != nil { - t.Error(err) - continue - } - - if !reflect.DeepEqual(test.firstEntry, firstEntry) { - t.Errorf("#%d: mismatch: got:%#v want:%#v", i, firstEntry, test.firstEntry) - continue + for _, testEntry := range test.entries { + reader := dwarf.Reader() + for j := 0; j < testEntry.entryNumber; j++ { + entry, err := reader.Next() + if entry == nil || err != nil { + t.Errorf("Failed to skip to entry %d: %v", testEntry.entryNumber, err) + continue + } + } + entry, err := reader.Next() + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(testEntry.entry, entry) { + t.Errorf("#%d/%d: mismatch: got:%#v want:%#v", i, testEntry.entryNumber, entry, testEntry.entry) + continue + } } } } diff --git a/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj b/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj new file mode 100644 index 000000000..f62b1ea1c Binary files /dev/null and b/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj differ diff --git a/src/pkg/debug/elf/testdata/hello-world-core.gz b/src/pkg/debug/elf/testdata/hello-world-core.gz new file mode 100644 index 000000000..806af6edb Binary files /dev/null and b/src/pkg/debug/elf/testdata/hello-world-core.gz differ diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go index ade704335..20acba612 100644 --- a/src/pkg/debug/gosym/pclntab_test.go +++ b/src/pkg/debug/gosym/pclntab_test.go @@ -52,6 +52,14 @@ func dotest() bool { return true } +func endtest() { + if pclineTempDir != "" { + os.RemoveAll(pclineTempDir) + pclineTempDir = "" + pclinetestBinary = "" + } +} + func getTable(t *testing.T) *Table { f, tab := crack(os.Args[0], t) f.Close() @@ -95,6 +103,7 @@ func TestLineFromAline(t *testing.T) { if !dotest() { return } + defer endtest() tab := getTable(t) @@ -129,7 +138,7 @@ func TestLineFromAline(t *testing.T) { if !ok { t.Errorf("file %s starts on line %d", path, line) } else if line != ll+1 { - t.Errorf("expected next line of file %s to be %d, got %d", path, ll+1, line) + t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line) } lastline[path] = line } @@ -142,6 +151,7 @@ func TestLineAline(t *testing.T) { if !dotest() { return } + defer endtest() tab := getTable(t) @@ -183,7 +193,7 @@ func TestPCLine(t *testing.T) { if !dotest() { return } - defer os.RemoveAll(pclineTempDir) + defer endtest() f, tab := crack(pclinetestBinary, t) text := f.Section(".text") diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go index 52d7d55a3..81ed4fb27 100644 --- a/src/pkg/debug/gosym/symtab.go +++ b/src/pkg/debug/gosym/symtab.go @@ -13,6 +13,7 @@ package gosym // and the Go format is the runtime source, specifically ../../runtime/symtab.c. import ( + "bytes" "encoding/binary" "fmt" "strconv" @@ -98,24 +99,116 @@ type Table struct { } type sym struct { - value uint32 - gotype uint32 + value uint64 + gotype uint64 typ byte name []byte } +var littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} +var bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} + +var oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} + func walksymtab(data []byte, fn func(sym) error) error { + var order binary.ByteOrder = binary.BigEndian + newTable := false + switch { + case bytes.HasPrefix(data, oldLittleEndianSymtab): + // Same as Go 1.0, but little endian. + // Format was used during interim development between Go 1.0 and Go 1.1. + // Should not be widespread, but easy to support. + data = data[6:] + order = binary.LittleEndian + case bytes.HasPrefix(data, bigEndianSymtab): + newTable = true + case bytes.HasPrefix(data, littleEndianSymtab): + newTable = true + order = binary.LittleEndian + } + var ptrsz int + if newTable { + if len(data) < 8 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + ptrsz = int(data[7]) + if ptrsz != 4 && ptrsz != 8 { + return &DecodingError{7, "invalid pointer size", ptrsz} + } + data = data[8:] + } var s sym p := data - for len(p) >= 6 { - s.value = binary.BigEndian.Uint32(p[0:4]) - typ := p[4] - if typ&0x80 == 0 { - return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + for len(p) >= 4 { + var typ byte + if newTable { + // Symbol type, value, Go type. + typ = p[0] & 0x3F + wideValue := p[0]&0x40 != 0 + goType := p[0]&0x80 != 0 + if typ < 26 { + typ += 'A' + } else { + typ += 'a' - 26 + } + s.typ = typ + p = p[1:] + if wideValue { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width value + if ptrsz == 8 { + s.value = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.value = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } else { + // varint value + s.value = 0 + shift := uint(0) + for len(p) > 0 && p[0]&0x80 != 0 { + s.value |= uint64(p[0]&0x7F) << shift + shift += 7 + p = p[1:] + } + if len(p) == 0 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.value |= uint64(p[0]) << shift + p = p[1:] + } + if goType { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width go type + if ptrsz == 8 { + s.gotype = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.gotype = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } + } else { + // Value, symbol type. + s.value = uint64(order.Uint32(p[0:4])) + if len(p) < 5 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + typ = p[4] + if typ&0x80 == 0 { + return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + } + typ &^= 0x80 + s.typ = typ + p = p[5:] } - typ &^= 0x80 - s.typ = typ - p = p[5:] + + // Name. var i int var nnul int for i = 0; i < len(p); i++ { @@ -134,13 +227,21 @@ func walksymtab(data []byte, fn func(sym) error) error { } } } - if i+nnul+4 > len(p) { + if len(p) < i+nnul { return &DecodingError{len(data), "unexpected EOF", nil} } s.name = p[0:i] i += nnul - s.gotype = binary.BigEndian.Uint32(p[i : i+4]) - p = p[i+4:] + p = p[i:] + + if !newTable { + if len(p) < 4 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // Go type. + s.gotype = uint64(order.Uint32(p[:4])) + p = p[4:] + } fn(s) } return nil diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go index 6b98a5f45..f521566ef 100644 --- a/src/pkg/debug/pe/file.go +++ b/src/pkg/debug/pe/file.go @@ -19,6 +19,7 @@ import ( type File struct { FileHeader Sections []*Section + Symbols []*Symbol closer io.Closer } @@ -49,6 +50,14 @@ type Section struct { sr *io.SectionReader } +type Symbol struct { + Name string + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 +} + type ImportDirectory struct { OriginalFirstThunk uint32 TimeDateStamp uint32 @@ -122,12 +131,13 @@ func NewFile(r io.ReaderAt) (*File, error) { } var base int64 if dosheader[0] == 'M' && dosheader[1] == 'Z' { + signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:])) var sign [4]byte - r.ReadAt(sign[0:], int64(dosheader[0x3c])) + r.ReadAt(sign[:], signoff) if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { return nil, errors.New("Invalid PE File Format.") } - base = int64(dosheader[0x3c]) + 4 + base = signoff + 4 } else { base = int64(0) } @@ -138,16 +148,52 @@ func NewFile(r io.ReaderAt) (*File, error) { if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { return nil, errors.New("Invalid PE File Format.") } - // get symbol string table - sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), os.SEEK_SET) - var l uint32 - if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { - return nil, err - } - ss := make([]byte, l) - if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols)); err != nil { - return nil, err + + var ss []byte + if f.FileHeader.NumberOfSymbols > 0 { + // Get COFF string table, which is located at the end of the COFF symbol table. + sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET) + var l uint32 + if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { + return nil, err + } + ss = make([]byte, l) + if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil { + return nil, err + } + + // Process COFF symbol table. + sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET) + aux := uint8(0) + for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { + cs := new(COFFSymbol) + if err := binary.Read(sr, binary.LittleEndian, cs); err != nil { + return nil, err + } + if aux > 0 { + aux-- + continue + } + var name string + if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 { + si := int(binary.LittleEndian.Uint32(cs.Name[4:])) + name, _ = getString(ss, si) + } else { + name = cstring(cs.Name[:]) + } + aux = cs.NumberOfAuxSymbols + s := &Symbol{ + Name: name, + Value: cs.Value, + SectionNumber: cs.SectionNumber, + Type: cs.Type, + StorageClass: cs.StorageClass, + } + f.Symbols = append(f.Symbols, s) + } } + + // Process sections. sr.Seek(base, os.SEEK_SET) binary.Read(sr, binary.LittleEndian, &f.FileHeader) sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), os.SEEK_CUR) //Skip OptionalHeader diff --git a/src/pkg/debug/pe/file_test.go b/src/pkg/debug/pe/file_test.go index 2815d720b..c0f9fcb95 100644 --- a/src/pkg/debug/pe/file_test.go +++ b/src/pkg/debug/pe/file_test.go @@ -13,6 +13,7 @@ type fileTest struct { file string hdr FileHeader sections []*SectionHeader + symbols []*Symbol } var fileTests = []fileTest{ @@ -33,6 +34,24 @@ var fileTests = []fileTest{ {".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, {".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, }, + []*Symbol{ + {".file", 0x0, -2, 0x0, 0x67}, + {"_main", 0x0, 1, 0x20, 0x2}, + {".text", 0x0, 1, 0x0, 0x3}, + {".data", 0x0, 2, 0x0, 0x3}, + {".bss", 0x0, 3, 0x0, 0x3}, + {".debug_abbrev", 0x0, 4, 0x0, 0x3}, + {".debug_info", 0x0, 5, 0x0, 0x3}, + {".debug_line", 0x0, 6, 0x0, 0x3}, + {".rdata", 0x0, 7, 0x0, 0x3}, + {".debug_frame", 0x0, 8, 0x0, 0x3}, + {".debug_loc", 0x0, 9, 0x0, 0x3}, + {".debug_pubnames", 0x0, 10, 0x0, 0x3}, + {".debug_pubtypes", 0x0, 11, 0x0, 0x3}, + {".debug_aranges", 0x0, 12, 0x0, 0x3}, + {"___main", 0x0, 0, 0x20, 0x2}, + {"_puts", 0x0, 0, 0x20, 0x2}, + }, }, { "testdata/gcc-386-mingw-exec", @@ -54,6 +73,7 @@ var fileTests = []fileTest{ {Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000}, {Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, }, + []*Symbol{}, }, } @@ -86,7 +106,15 @@ func TestOpen(t *testing.T) { if tn != fn { t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) } - + for i, have := range f.Symbols { + if i >= len(tt.symbols) { + break + } + want := tt.symbols[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } } } diff --git a/src/pkg/debug/pe/pe.go b/src/pkg/debug/pe/pe.go index b3dab739a..0606217b3 100644 --- a/src/pkg/debug/pe/pe.go +++ b/src/pkg/debug/pe/pe.go @@ -27,6 +27,17 @@ type SectionHeader32 struct { Characteristics uint32 } +const COFFSymbolSize = 18 + +type COFFSymbol struct { + Name [8]uint8 + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 + NumberOfAuxSymbols uint8 +} + const ( IMAGE_FILE_MACHINE_UNKNOWN = 0x0 IMAGE_FILE_MACHINE_AM33 = 0x1d3 diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go index ac2b5f8da..cac9d64b5 100644 --- a/src/pkg/encoding/asn1/asn1.go +++ b/src/pkg/encoding/asn1/asn1.go @@ -77,15 +77,15 @@ func parseInt64(bytes []byte) (ret int64, err error) { // parseInt treats the given bytes as a big-endian, signed integer and returns // the result. -func parseInt(bytes []byte) (int, error) { +func parseInt32(bytes []byte) (int32, error) { ret64, err := parseInt64(bytes) if err != nil { return 0, err } - if ret64 != int64(int(ret64)) { + if ret64 != int64(int32(ret64)) { return 0, StructuralError{"integer too large"} } - return int(ret64), nil + return int32(ret64), nil } var bigOne = big.NewInt(1) @@ -670,7 +670,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam err = err1 return case enumeratedType: - parsedInt, err1 := parseInt(innerBytes) + parsedInt, err1 := parseInt32(innerBytes) if err1 == nil { v.SetInt(int64(parsedInt)) } @@ -692,19 +692,20 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam } err = err1 return - case reflect.Int, reflect.Int32: - parsedInt, err1 := parseInt(innerBytes) - if err1 == nil { - val.SetInt(int64(parsedInt)) - } - err = err1 - return - case reflect.Int64: - parsedInt, err1 := parseInt64(innerBytes) - if err1 == nil { - val.SetInt(parsedInt) + case reflect.Int, reflect.Int32, reflect.Int64: + if val.Type().Size() == 4 { + parsedInt, err1 := parseInt32(innerBytes) + if err1 == nil { + val.SetInt(int64(parsedInt)) + } + err = err1 + } else { + parsedInt, err1 := parseInt64(innerBytes) + if err1 == nil { + val.SetInt(parsedInt) + } + err = err1 } - err = err1 return // TODO(dfc) Add support for the remaining integer types case reflect.Struct: diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go index eb848bdb4..6e98dcf0b 100644 --- a/src/pkg/encoding/asn1/asn1_test.go +++ b/src/pkg/encoding/asn1/asn1_test.go @@ -64,7 +64,7 @@ var int32TestData = []int32Test{ func TestParseInt32(t *testing.T) { for i, test := range int32TestData { - ret, err := parseInt(test.in) + ret, err := parseInt32(test.in) if (err == nil) != test.ok { t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) } @@ -124,7 +124,7 @@ func TestBitString(t *testing.T) { t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) } if err == nil { - if test.bitLength != ret.BitLength || bytes.Compare(ret.Bytes, test.out) != 0 { + if test.bitLength != ret.BitLength || !bytes.Equal(ret.Bytes, test.out) { t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength) } } @@ -166,7 +166,7 @@ func TestBitStringRightAlign(t *testing.T) { for i, test := range bitStringRightAlignTests { bs := BitString{test.in, test.inlen} out := bs.RightAlign() - if bytes.Compare(out, test.out) != 0 { + if !bytes.Equal(out, test.out) { t.Errorf("#%d got: %x want: %x", i, out, test.out) } } @@ -477,7 +477,7 @@ func TestRawStructs(t *testing.T) { if s.A != 0x50 { t.Errorf("bad value for A: got %d want %d", s.A, 0x50) } - if bytes.Compare([]byte(s.Raw), input) != 0 { + if !bytes.Equal([]byte(s.Raw), input) { t.Errorf("bad value for Raw: got %x want %x", s.Raw, input) } } diff --git a/src/pkg/encoding/asn1/common.go b/src/pkg/encoding/asn1/common.go index 03856bc55..33a117ece 100644 --- a/src/pkg/encoding/asn1/common.go +++ b/src/pkg/encoding/asn1/common.go @@ -98,6 +98,8 @@ func parseFieldParameters(str string) (ret fieldParameters) { ret.stringType = tagIA5String case part == "printable": ret.stringType = tagPrintableString + case part == "utf8": + ret.stringType = tagUTF8String case strings.HasPrefix(part, "default:"): i, err := strconv.ParseInt(part[8:], 10, 64) if err == nil { diff --git a/src/pkg/encoding/asn1/marshal.go b/src/pkg/encoding/asn1/marshal.go index 163bca575..0c216fdb3 100644 --- a/src/pkg/encoding/asn1/marshal.go +++ b/src/pkg/encoding/asn1/marshal.go @@ -6,11 +6,13 @@ package asn1 import ( "bytes" + "errors" "fmt" "io" "math/big" "reflect" "time" + "unicode/utf8" ) // A forkableWriter is an in-memory buffer that can be @@ -280,6 +282,11 @@ func marshalIA5String(out *forkableWriter, s string) (err error) { return } +func marshalUTF8String(out *forkableWriter, s string) (err error) { + _, err = out.Write([]byte(s)) + return +} + func marshalTwoDigits(out *forkableWriter, v int) (err error) { err = out.WriteByte(byte('0' + (v/10)%10)) if err != nil { @@ -289,8 +296,7 @@ func marshalTwoDigits(out *forkableWriter, v int) (err error) { } func marshalUTCTime(out *forkableWriter, t time.Time) (err error) { - utc := t.UTC() - year, month, day := utc.Date() + year, month, day := t.Date() switch { case 1950 <= year && year < 2000: @@ -314,7 +320,7 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) { return } - hour, min, sec := utc.Clock() + hour, min, sec := t.Clock() err = marshalTwoDigits(out, hour) if err != nil { @@ -446,10 +452,13 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter } return case reflect.String: - if params.stringType == tagIA5String { + switch params.stringType { + case tagIA5String: return marshalIA5String(out, v.String()) - } else { + case tagPrintableString: return marshalPrintableString(out, v.String()) + default: + return marshalUTF8String(out, v.String()) } return } @@ -492,11 +501,27 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) } class := classUniversal - if params.stringType != 0 { - if tag != tagPrintableString { - return StructuralError{"Explicit string type given to non-string member"} + if params.stringType != 0 && tag != tagPrintableString { + return StructuralError{"Explicit string type given to non-string member"} + } + + if tag == tagPrintableString { + if params.stringType == 0 { + // This is a string without an explicit string type. We'll use + // a PrintableString if the character set in the string is + // sufficiently limited, otherwise we'll use a UTF8String. + for _, r := range v.String() { + if r >= utf8.RuneSelf || !isPrintable(byte(r)) { + if !utf8.ValidString(v.String()) { + return errors.New("asn1: string not valid UTF-8") + } + tag = tagUTF8String + break + } + } + } else { + tag = params.stringType } - tag = params.stringType } if params.set { diff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go index f43bcae68..b4dbe71ef 100644 --- a/src/pkg/encoding/asn1/marshal_test.go +++ b/src/pkg/encoding/asn1/marshal_test.go @@ -82,7 +82,7 @@ var marshalTests = []marshalTest{ {explicitTagTest{64}, "3005a503020140"}, {time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"}, {time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"}, - {time.Unix(1258325776, 0).In(PST), "17113039313131353232353631362d30383030"}, + {time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"}, {BitString{[]byte{0x80}, 1}, "03020780"}, {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, @@ -122,6 +122,7 @@ var marshalTests = []marshalTest{ {testSET([]int{10}), "310302010a"}, {omitEmptyTest{[]string{}}, "3000"}, {omitEmptyTest{[]string{"1"}}, "30053003130131"}, + {"Σ", "0c02cea3"}, } func TestMarshal(t *testing.T) { @@ -131,9 +132,16 @@ func TestMarshal(t *testing.T) { t.Errorf("#%d failed: %s", i, err) } out, _ := hex.DecodeString(test.out) - if bytes.Compare(out, data) != 0 { + if !bytes.Equal(out, data) { t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out) } } } + +func TestInvalidUTF8(t *testing.T) { + _, err := Marshal(string([]byte{0xff, 0xff})) + if err == nil { + t.Errorf("invalid UTF8 string was accepted") + } +} diff --git a/src/pkg/encoding/base32/base32.go b/src/pkg/encoding/base32/base32.go index 71da6e22b..dbefc48fa 100644 --- a/src/pkg/encoding/base32/base32.go +++ b/src/pkg/encoding/base32/base32.go @@ -237,7 +237,6 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { dlen := 8 // do the top bytes contain any data? - dbufloop: for j := 0; j < 8; { if len(src) == 0 { return n, false, CorruptInputError(len(osrc) - len(src) - j) @@ -258,7 +257,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { } dlen = j end = true - break dbufloop + break } dbuf[j] = enc.decodeMap[in] if dbuf[j] == 0xFF { diff --git a/src/pkg/encoding/base32/example_test.go b/src/pkg/encoding/base32/example_test.go new file mode 100644 index 000000000..f6128d900 --- /dev/null +++ b/src/pkg/encoding/base32/example_test.go @@ -0,0 +1,45 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Keep in sync with ../base64/example_test.go. + +package base32_test + +import ( + "encoding/base32" + "fmt" + "os" +) + +func ExampleEncoding_EncodeToString() { + data := []byte("any + old & data") + str := base32.StdEncoding.EncodeToString(data) + fmt.Println(str) + // Output: + // MFXHSIBLEBXWYZBAEYQGIYLUME====== +} + +func ExampleEncoding_DecodeString() { + str := "ONXW2ZJAMRQXIYJAO5UXI2BAAAQGC3TEEDX3XPY=" + data, err := base32.StdEncoding.DecodeString(str) + if err != nil { + fmt.Println("error:", err) + return + } + fmt.Printf("%q\n", data) + // Output: + // "some data with \x00 and \ufeff" +} + +func ExampleNewEncoder() { + input := []byte("foo\x00bar") + encoder := base32.NewEncoder(base32.StdEncoding, os.Stdout) + encoder.Write(input) + // Must close the encoder when finished to flush any partial blocks. + // If you comment out the following line, the last partial block "r" + // won't be encoded. + encoder.Close() + // Output: + // MZXW6ADCMFZA==== +} diff --git a/src/pkg/encoding/base64/base64.go b/src/pkg/encoding/base64/base64.go index 0b842f066..e66672a1c 100644 --- a/src/pkg/encoding/base64/base64.go +++ b/src/pkg/encoding/base64/base64.go @@ -216,7 +216,6 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { var dbuf [4]byte dlen := 4 - dbufloop: for j := 0; j < 4; { if len(src) == 0 { return n, false, CorruptInputError(len(osrc) - len(src) - j) @@ -240,7 +239,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { } dlen = j end = true - break dbufloop + break } dbuf[j] = enc.decodeMap[in] if dbuf[j] == 0xFF { diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go index f9b863c36..2166abd7a 100644 --- a/src/pkg/encoding/base64/base64_test.go +++ b/src/pkg/encoding/base64/base64_test.go @@ -257,6 +257,7 @@ func TestDecoderIssue3577(t *testing.T) { wantErr := errors.New("my error") next <- nextRead{5, nil} next <- nextRead{10, wantErr} + next <- nextRead{0, wantErr} d := NewDecoder(StdEncoding, &faultInjectReader{ source: "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", // twas brillig... nextc: next, diff --git a/src/pkg/encoding/base64/example_test.go b/src/pkg/encoding/base64/example_test.go new file mode 100644 index 000000000..d18b856a0 --- /dev/null +++ b/src/pkg/encoding/base64/example_test.go @@ -0,0 +1,45 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Keep in sync with ../base32/example_test.go. + +package base64_test + +import ( + "encoding/base64" + "fmt" + "os" +) + +func ExampleEncoding_EncodeToString() { + data := []byte("any + old & data") + str := base64.StdEncoding.EncodeToString(data) + fmt.Println(str) + // Output: + // YW55ICsgb2xkICYgZGF0YQ== +} + +func ExampleEncoding_DecodeString() { + str := "c29tZSBkYXRhIHdpdGggACBhbmQg77u/" + data, err := base64.StdEncoding.DecodeString(str) + if err != nil { + fmt.Println("error:", err) + return + } + fmt.Printf("%q\n", data) + // Output: + // "some data with \x00 and \ufeff" +} + +func ExampleNewEncoder() { + input := []byte("foo\x00bar") + encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout) + encoder.Write(input) + // Must close the encoder when finished to flush any partial blocks. + // If you comment out the following line, the last partial block "r" + // won't be encoded. + encoder.Close() + // Output: + // Zm9vAGJhcg== +} diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go index 712e490e6..edbac197d 100644 --- a/src/pkg/encoding/binary/binary.go +++ b/src/pkg/encoding/binary/binary.go @@ -125,6 +125,9 @@ func (bigEndian) GoString() string { return "binary.BigEndian" } // of fixed-size values. // Bytes read from r are decoded using the specified byte order // and written to successive fields of the data. +// When reading into structs, the field data for fields with +// blank (_) field names is skipped; i.e., blank field names +// may be used for padding. func Read(r io.Reader, order ByteOrder, data interface{}) error { // Fast path for basic types. if n := intDestSize(data); n != 0 { @@ -154,7 +157,7 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error { return nil } - // Fallback to reflect-based. + // Fallback to reflect-based decoding. var v reflect.Value switch d := reflect.ValueOf(data); d.Kind() { case reflect.Ptr: @@ -164,9 +167,9 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error { default: return errors.New("binary.Read: invalid type " + d.Type().String()) } - size := dataSize(v) - if size < 0 { - return errors.New("binary.Read: invalid type " + v.Type().String()) + size, err := dataSize(v) + if err != nil { + return errors.New("binary.Read: " + err.Error()) } d := &decoder{order: order, buf: make([]byte, size)} if _, err := io.ReadFull(r, d.buf); err != nil { @@ -181,6 +184,8 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error { // values, or a pointer to such data. // Bytes written to w are encoded using the specified byte order // and read from successive fields of the data. +// When writing structs, zero values are written for fields +// with blank (_) field names. func Write(w io.Writer, order ByteOrder, data interface{}) error { // Fast path for basic types. var b [8]byte @@ -239,76 +244,80 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error { _, err := w.Write(bs) return err } + + // Fallback to reflect-based encoding. v := reflect.Indirect(reflect.ValueOf(data)) - size := dataSize(v) - if size < 0 { - return errors.New("binary.Write: invalid type " + v.Type().String()) + size, err := dataSize(v) + if err != nil { + return errors.New("binary.Write: " + err.Error()) } buf := make([]byte, size) e := &encoder{order: order, buf: buf} e.value(v) - _, err := w.Write(buf) + _, err = w.Write(buf) return err } // Size returns how many bytes Write would generate to encode the value v, which // must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. func Size(v interface{}) int { - return dataSize(reflect.Indirect(reflect.ValueOf(v))) + n, err := dataSize(reflect.Indirect(reflect.ValueOf(v))) + if err != nil { + return -1 + } + return n } // dataSize returns the number of bytes the actual data represented by v occupies in memory. // For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice // it returns the length of the slice times the element size and does not count the memory // occupied by the header. -func dataSize(v reflect.Value) int { +func dataSize(v reflect.Value) (int, error) { if v.Kind() == reflect.Slice { - elem := sizeof(v.Type().Elem()) - if elem < 0 { - return -1 + elem, err := sizeof(v.Type().Elem()) + if err != nil { + return 0, err } - return v.Len() * elem + return v.Len() * elem, nil } return sizeof(v.Type()) } -func sizeof(t reflect.Type) int { +func sizeof(t reflect.Type) (int, error) { switch t.Kind() { case reflect.Array: - n := sizeof(t.Elem()) - if n < 0 { - return -1 + n, err := sizeof(t.Elem()) + if err != nil { + return 0, err } - return t.Len() * n + return t.Len() * n, nil case reflect.Struct: sum := 0 for i, n := 0, t.NumField(); i < n; i++ { - s := sizeof(t.Field(i).Type) - if s < 0 { - return -1 + s, err := sizeof(t.Field(i).Type) + if err != nil { + return 0, err } sum += s } - return sum + return sum, nil case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: - return int(t.Size()) + return int(t.Size()), nil } - return -1 + return 0, errors.New("invalid type " + t.String()) } -type decoder struct { +type coder struct { order ByteOrder buf []byte } -type encoder struct { - order ByteOrder - buf []byte -} +type decoder coder +type encoder coder func (d *decoder) uint8() uint8 { x := d.buf[0] @@ -379,9 +388,19 @@ func (d *decoder) value(v reflect.Value) { } case reflect.Struct: + t := v.Type() l := v.NumField() for i := 0; i < l; i++ { - d.value(v.Field(i)) + // Note: Calling v.CanSet() below is an optimization. + // It would be sufficient to check the field name, + // but creating the StructField info for each field is + // costly (run "go test -bench=ReadStruct" and compare + // results when making changes to this code). + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + d.value(v) + } else { + d.skip(v) + } } case reflect.Slice: @@ -435,9 +454,15 @@ func (e *encoder) value(v reflect.Value) { } case reflect.Struct: + t := v.Type() l := v.NumField() for i := 0; i < l; i++ { - e.value(v.Field(i)) + // see comment for corresponding code in decoder.value() + if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { + e.value(v) + } else { + e.skip(v) + } } case reflect.Slice: @@ -492,6 +517,19 @@ func (e *encoder) value(v reflect.Value) { } } +func (d *decoder) skip(v reflect.Value) { + n, _ := dataSize(v) + d.buf = d.buf[n:] +} + +func (e *encoder) skip(v reflect.Value) { + n, _ := dataSize(v) + for i := range e.buf[0:n] { + e.buf[i] = 0 + } + e.buf = e.buf[n:] +} + // intDestSize returns the size of the integer that ptrType points to, // or 0 if the type is not supported. func intDestSize(ptrType interface{}) int { diff --git a/src/pkg/encoding/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go index ff361b7e3..056f0998f 100644 --- a/src/pkg/encoding/binary/binary_test.go +++ b/src/pkg/encoding/binary/binary_test.go @@ -9,6 +9,7 @@ import ( "io" "math" "reflect" + "strings" "testing" ) @@ -120,18 +121,14 @@ func testWrite(t *testing.T, order ByteOrder, b []byte, s1 interface{}) { checkResult(t, "Write", order, err, buf.Bytes(), b) } -func TestBigEndianRead(t *testing.T) { testRead(t, BigEndian, big, s) } - -func TestLittleEndianRead(t *testing.T) { testRead(t, LittleEndian, little, s) } - -func TestBigEndianWrite(t *testing.T) { testWrite(t, BigEndian, big, s) } - -func TestLittleEndianWrite(t *testing.T) { testWrite(t, LittleEndian, little, s) } +func TestLittleEndianRead(t *testing.T) { testRead(t, LittleEndian, little, s) } +func TestLittleEndianWrite(t *testing.T) { testWrite(t, LittleEndian, little, s) } +func TestLittleEndianPtrWrite(t *testing.T) { testWrite(t, LittleEndian, little, &s) } +func TestBigEndianRead(t *testing.T) { testRead(t, BigEndian, big, s) } +func TestBigEndianWrite(t *testing.T) { testWrite(t, BigEndian, big, s) } func TestBigEndianPtrWrite(t *testing.T) { testWrite(t, BigEndian, big, &s) } -func TestLittleEndianPtrWrite(t *testing.T) { testWrite(t, LittleEndian, little, &s) } - func TestReadSlice(t *testing.T) { slice := make([]int32, 2) err := Read(bytes.NewBuffer(src), BigEndian, slice) @@ -147,20 +144,81 @@ func TestWriteSlice(t *testing.T) { func TestWriteT(t *testing.T) { buf := new(bytes.Buffer) ts := T{} - err := Write(buf, BigEndian, ts) - if err == nil { - t.Errorf("WriteT: have nil, want non-nil") + if err := Write(buf, BigEndian, ts); err == nil { + t.Errorf("WriteT: have err == nil, want non-nil") } tv := reflect.Indirect(reflect.ValueOf(ts)) for i, n := 0, tv.NumField(); i < n; i++ { - err = Write(buf, BigEndian, tv.Field(i).Interface()) - if err == nil { - t.Errorf("WriteT.%v: have nil, want non-nil", tv.Field(i).Type()) + typ := tv.Field(i).Type().String() + if typ == "[4]int" { + typ = "int" // the problem is int, not the [4] + } + if err := Write(buf, BigEndian, tv.Field(i).Interface()); err == nil { + t.Errorf("WriteT.%v: have err == nil, want non-nil", tv.Field(i).Type()) + } else if !strings.Contains(err.Error(), typ) { + t.Errorf("WriteT: have err == %q, want it to mention %s", err, typ) } } } +type BlankFields struct { + A uint32 + _ int32 + B float64 + _ [4]int16 + C byte + _ [7]byte + _ struct { + f [8]float32 + } +} + +type BlankFieldsProbe struct { + A uint32 + P0 int32 + B float64 + P1 [4]int16 + C byte + P2 [7]byte + P3 struct { + F [8]float32 + } +} + +func TestBlankFields(t *testing.T) { + buf := new(bytes.Buffer) + b1 := BlankFields{A: 1234567890, B: 2.718281828, C: 42} + if err := Write(buf, LittleEndian, &b1); err != nil { + t.Error(err) + } + + // zero values must have been written for blank fields + var p BlankFieldsProbe + if err := Read(buf, LittleEndian, &p); err != nil { + t.Error(err) + } + + // quick test: only check first value of slices + if p.P0 != 0 || p.P1[0] != 0 || p.P2[0] != 0 || p.P3.F[0] != 0 { + t.Errorf("non-zero values for originally blank fields: %#v", p) + } + + // write p and see if we can probe only some fields + if err := Write(buf, LittleEndian, &p); err != nil { + t.Error(err) + } + + // read should ignore blank fields in b2 + var b2 BlankFields + if err := Read(buf, LittleEndian, &b2); err != nil { + t.Error(err) + } + if b1.A != b2.A || b1.B != b2.B || b1.C != b2.C { + t.Errorf("%#v != %#v", b1, b2) + } +} + type byteSliceReader struct { remain []byte } @@ -187,7 +245,7 @@ func BenchmarkReadStruct(b *testing.B) { bsr := &byteSliceReader{} var buf bytes.Buffer Write(&buf, BigEndian, &s) - n := dataSize(reflect.ValueOf(s)) + n, _ := dataSize(reflect.ValueOf(s)) b.SetBytes(int64(n)) t := s b.ResetTimer() diff --git a/src/pkg/encoding/binary/varint.go b/src/pkg/encoding/binary/varint.go index b756afdd0..7035529f2 100644 --- a/src/pkg/encoding/binary/varint.go +++ b/src/pkg/encoding/binary/varint.go @@ -123,7 +123,7 @@ func ReadUvarint(r io.ByteReader) (uint64, error) { panic("unreachable") } -// ReadVarint reads an encoded unsigned integer from r and returns it as a uint64. +// ReadVarint reads an encoded signed integer from r and returns it as an int64. func ReadVarint(r io.ByteReader) (int64, error) { ux, err := ReadUvarint(r) // ok to continue in presence of error x := int64(ux >> 1) diff --git a/src/pkg/encoding/csv/writer.go b/src/pkg/encoding/csv/writer.go index c4dcba566..1faecb664 100644 --- a/src/pkg/encoding/csv/writer.go +++ b/src/pkg/encoding/csv/writer.go @@ -22,7 +22,7 @@ import ( // // If UseCRLF is true, the Writer ends each record with \r\n instead of \n. type Writer struct { - Comma rune // Field delimiter (set to to ',' by NewWriter) + Comma rune // Field delimiter (set to ',' by NewWriter) UseCRLF bool // True to use \r\n as the line terminator w *bufio.Writer } @@ -92,20 +92,26 @@ func (w *Writer) Write(record []string) (err error) { } // Flush writes any buffered data to the underlying io.Writer. +// To check if an error occurred during the Flush, call Error. func (w *Writer) Flush() { w.w.Flush() } +// Error reports any error that has occurred during a previous Write or Flush. +func (w *Writer) Error() error { + _, err := w.w.Write(nil) + return err +} + // WriteAll writes multiple CSV records to w using Write and then calls Flush. func (w *Writer) WriteAll(records [][]string) (err error) { for _, record := range records { err = w.Write(record) if err != nil { - break + return err } } - w.Flush() - return nil + return w.w.Flush() } // fieldNeedsQuotes returns true if our field must be enclosed in quotes. diff --git a/src/pkg/encoding/csv/writer_test.go b/src/pkg/encoding/csv/writer_test.go index 578959007..03ca6b093 100644 --- a/src/pkg/encoding/csv/writer_test.go +++ b/src/pkg/encoding/csv/writer_test.go @@ -6,6 +6,7 @@ package csv import ( "bytes" + "errors" "testing" ) @@ -42,3 +43,30 @@ func TestWrite(t *testing.T) { } } } + +type errorWriter struct{} + +func (e errorWriter) Write(b []byte) (int, error) { + return 0, errors.New("Test") +} + +func TestError(t *testing.T) { + b := &bytes.Buffer{} + f := NewWriter(b) + f.Write([]string{"abc"}) + f.Flush() + err := f.Error() + + if err != nil { + t.Errorf("Unexpected error: %s\n", err) + } + + f = NewWriter(errorWriter{}) + f.Write([]string{"abc"}) + f.Flush() + err = f.Error() + + if err == nil { + t.Error("Error should not be nil") + } +} diff --git a/src/pkg/encoding/gob/codec_test.go b/src/pkg/encoding/gob/codec_test.go index ebcbb78eb..482212b74 100644 --- a/src/pkg/encoding/gob/codec_test.go +++ b/src/pkg/encoding/gob/codec_test.go @@ -7,6 +7,7 @@ package gob import ( "bytes" "errors" + "flag" "math" "math/rand" "reflect" @@ -16,6 +17,8 @@ import ( "unsafe" ) +var doFuzzTests = flag.Bool("gob.fuzz", false, "run the fuzz tests, which are large and very slow") + // Guarantee encoding format by comparing some encodings to hand-written values type EncodeT struct { x uint64 @@ -1434,7 +1437,8 @@ func encFuzzDec(rng *rand.Rand, in interface{}) error { // This does some "fuzz testing" by attempting to decode a sequence of random bytes. func TestFuzz(t *testing.T) { - if testing.Short() { + if !*doFuzzTests { + t.Logf("disabled; run with -gob.fuzz to enable") return } @@ -1453,11 +1457,16 @@ func TestFuzz(t *testing.T) { } func TestFuzzRegressions(t *testing.T) { + if !*doFuzzTests { + t.Logf("disabled; run with -gob.fuzz to enable") + return + } + // An instance triggering a type name of length ~102 GB. testFuzz(t, 1328492090837718000, 100, new(float32)) // An instance triggering a type name of 1.6 GB. - // Commented out because it takes 5m to run. - //testFuzz(t, 1330522872628565000, 100, new(int)) + // Note: can take several minutes to run. + testFuzz(t, 1330522872628565000, 100, new(int)) } func testFuzz(t *testing.T, seed int64, n int, input ...interface{}) { diff --git a/src/pkg/encoding/gob/decode.go b/src/pkg/encoding/gob/decode.go index e32a178ab..a80d9f919 100644 --- a/src/pkg/encoding/gob/decode.go +++ b/src/pkg/encoding/gob/decode.go @@ -62,15 +62,15 @@ func overflow(name string) error { // Used only by the Decoder to read the message length. func decodeUintReader(r io.Reader, buf []byte) (x uint64, width int, err error) { width = 1 - _, err = r.Read(buf[0:width]) - if err != nil { + n, err := io.ReadFull(r, buf[0:width]) + if n == 0 { return } b := buf[0] if b <= 0x7f { return uint64(b), width, nil } - n := -int(int8(b)) + n = -int(int8(b)) if n > uint64Size { err = errBadUint return @@ -562,6 +562,9 @@ func (dec *Decoder) ignoreSingle(engine *decEngine) { func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) { instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl} for i := 0; i < length; i++ { + if state.b.Len() == 0 { + errorf("decoding array or slice: length exceeds input size (%d elements)", length) + } up := unsafe.Pointer(p) if elemIndir > 1 { up = decIndirect(up, elemIndir) @@ -652,9 +655,6 @@ func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) { // Slices are encoded as an unsigned length followed by the elements. func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl error) { nr := state.decodeUint() - if nr > uint64(state.b.Len()) { - errorf("length of slice exceeds input size (%d elements)", nr) - } n := int(nr) if indir > 0 { up := unsafe.Pointer(p) @@ -717,7 +717,9 @@ func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p ui errorf("name too long (%d bytes): %.20q...", len(name), name) } // The concrete type must be registered. + registerLock.RLock() typ, ok := nameToConcreteType[name] + registerLock.RUnlock() if !ok { errorf("name not registered for interface: %q", name) } diff --git a/src/pkg/encoding/gob/decoder.go b/src/pkg/encoding/gob/decoder.go index c5c7d3fdb..04f706ca5 100644 --- a/src/pkg/encoding/gob/decoder.go +++ b/src/pkg/encoding/gob/decoder.go @@ -87,21 +87,38 @@ func (dec *Decoder) recvMessage() bool { // readMessage reads the next nbytes bytes from the input. func (dec *Decoder) readMessage(nbytes int) { - // Allocate the buffer. - if cap(dec.tmp) < nbytes { - dec.tmp = make([]byte, nbytes+100) // room to grow + // Allocate the dec.tmp buffer, up to 10KB. + const maxBuf = 10 * 1024 + nTmp := nbytes + if nTmp > maxBuf { + nTmp = maxBuf } - dec.tmp = dec.tmp[:nbytes] + if cap(dec.tmp) < nTmp { + nAlloc := nTmp + 100 // A little extra for growth. + if nAlloc > maxBuf { + nAlloc = maxBuf + } + dec.tmp = make([]byte, nAlloc) + } + dec.tmp = dec.tmp[:nTmp] // Read the data - _, dec.err = io.ReadFull(dec.r, dec.tmp) - if dec.err != nil { - if dec.err == io.EOF { - dec.err = io.ErrUnexpectedEOF + dec.buf.Grow(nbytes) + for nbytes > 0 { + if nbytes < nTmp { + dec.tmp = dec.tmp[:nbytes] } - return + var nRead int + nRead, dec.err = io.ReadFull(dec.r, dec.tmp) + if dec.err != nil { + if dec.err == io.EOF { + dec.err = io.ErrUnexpectedEOF + } + return + } + dec.buf.Write(dec.tmp) + nbytes -= nRead } - dec.buf.Write(dec.tmp) } // toInt turns an encoded uint64 into an int, according to the marshaling rules. diff --git a/src/pkg/encoding/gob/doc.go b/src/pkg/encoding/gob/doc.go index 821d9a3fe..5bd61b12e 100644 --- a/src/pkg/encoding/gob/doc.go +++ b/src/pkg/encoding/gob/doc.go @@ -67,11 +67,13 @@ point values may be received into any floating point variable. However, the destination variable must be able to represent the value or the decode operation will fail. -Structs, arrays and slices are also supported. Strings and arrays of bytes are -supported with a special, efficient representation (see below). When a slice is -decoded, if the existing slice has capacity the slice will be extended in place; -if not, a new array is allocated. Regardless, the length of the resulting slice -reports the number of elements decoded. +Structs, arrays and slices are also supported. Structs encode and +decode only exported fields. Strings and arrays of bytes are supported +with a special, efficient representation (see below). When a slice +is decoded, if the existing slice has capacity the slice will be +extended in place; if not, a new array is allocated. Regardless, +the length of the resulting slice reports the number of elements +decoded. Functions and channels cannot be sent in a gob. Attempting to encode a value that contains one will fail. @@ -118,7 +120,7 @@ elements using the standard gob encoding for their type, recursively. Maps are sent as an unsigned count followed by that many key, element pairs. Empty but non-nil maps are sent, so if the sender has allocated -a map, the receiver will allocate a map even no elements are +a map, the receiver will allocate a map even if no elements are transmitted. Structs are sent as a sequence of (field number, field value) pairs. The field @@ -328,7 +330,7 @@ reserved). 01 // Add 1 to get field number 0: field[1].name 01 // 1 byte 59 // structType.field[1].name = "Y" - 01 // Add 1 to get field number 1: field[0].id + 01 // Add 1 to get field number 1: field[1].id 04 // struct.Type.field[1].typeId is 2 (signed int). 00 // End of structType.field[1]; end of structType.field. 00 // end of wireType.structType structure diff --git a/src/pkg/encoding/gob/encode.go b/src/pkg/encoding/gob/encode.go index 168e08b13..ea37a6cbd 100644 --- a/src/pkg/encoding/gob/encode.go +++ b/src/pkg/encoding/gob/encode.go @@ -426,6 +426,12 @@ func (enc *Encoder) encodeMap(b *bytes.Buffer, mv reflect.Value, keyOp, elemOp e // by the concrete value. A nil value gets sent as the empty string for the name, // followed by no value. func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { + // Gobs can encode nil interface values but not typed interface + // values holding nil pointers, since nil pointers point to no value. + elem := iv.Elem() + if elem.Kind() == reflect.Ptr && elem.IsNil() { + errorf("gob: cannot encode nil pointer of type %s inside interface", iv.Elem().Type()) + } state := enc.newEncoderState(b) state.fieldnum = -1 state.sendZero = true @@ -435,7 +441,9 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { } ut := userType(iv.Elem().Type()) + registerLock.RLock() name, ok := concreteTypeToName[ut.base] + registerLock.RUnlock() if !ok { errorf("type not registered for interface: %s", ut.base) } @@ -454,7 +462,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { enc.pushWriter(b) data := new(bytes.Buffer) data.Write(spaceForLength) - enc.encode(data, iv.Elem(), ut) + enc.encode(data, elem, ut) if enc.err != nil { error_(enc.err) } @@ -698,9 +706,20 @@ func (enc *Encoder) getEncEngine(ut *userTypeInfo) *encEngine { error_(err1) } if info.encoder == nil { - // mark this engine as underway before compiling to handle recursive types. + // Assign the encEngine now, so recursive types work correctly. But... info.encoder = new(encEngine) + // ... if we fail to complete building the engine, don't cache the half-built machine. + // Doing this here means we won't cache a type that is itself OK but + // that contains a nested type that won't compile. The result is consistent + // error behavior when Encode is called multiple times on the top-level type. + ok := false + defer func() { + if !ok { + info.encoder = nil + } + }() info.encoder = enc.compileEnc(ut) + ok = true } return info.encoder } diff --git a/src/pkg/encoding/gob/encoder.go b/src/pkg/encoding/gob/encoder.go index a15b5a1f9..f669c3d5b 100644 --- a/src/pkg/encoding/gob/encoder.go +++ b/src/pkg/encoding/gob/encoder.go @@ -132,13 +132,13 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp return true } -// sendType sends the type info to the other side, if necessary. +// sendType sends the type info to the other side, if necessary. func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { ut := userType(origt) if ut.isGobEncoder { // The rules are different: regardless of the underlying type's representation, - // we need to tell the other side that this exact type is a GobEncoder. - return enc.sendActualType(w, state, ut, ut.user) + // we need to tell the other side that the base type is a GobEncoder. + return enc.sendActualType(w, state, ut, ut.base) } // It's a concrete value, so drill down to the base type. @@ -218,6 +218,12 @@ func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) { // EncodeValue transmits the data item represented by the reflection value, // guaranteeing that all necessary type information has been transmitted first. func (enc *Encoder) EncodeValue(value reflect.Value) error { + // Gobs contain values. They cannot represent nil pointers, which + // have no value to encode. + if value.Kind() == reflect.Ptr && value.IsNil() { + panic("gob: cannot encode nil pointer of type " + value.Type().String()) + } + // Make sure we're single-threaded through here, so multiple // goroutines can share an encoder. enc.mutex.Lock() diff --git a/src/pkg/encoding/gob/encoder_test.go b/src/pkg/encoding/gob/encoder_test.go index c4947cbb8..b684772c6 100644 --- a/src/pkg/encoding/gob/encoder_test.go +++ b/src/pkg/encoding/gob/encoder_test.go @@ -736,3 +736,109 @@ func TestPtrToMapOfMap(t *testing.T) { t.Fatalf("expected %v got %v", data, newData) } } + +// A top-level nil pointer generates a panic with a helpful string-valued message. +func TestTopLevelNilPointer(t *testing.T) { + errMsg := topLevelNilPanic(t) + if errMsg == "" { + t.Fatal("top-level nil pointer did not panic") + } + if !strings.Contains(errMsg, "nil pointer") { + t.Fatal("expected nil pointer error, got:", errMsg) + } +} + +func topLevelNilPanic(t *testing.T) (panicErr string) { + defer func() { + e := recover() + if err, ok := e.(string); ok { + panicErr = err + } + }() + var ip *int + buf := new(bytes.Buffer) + if err := NewEncoder(buf).Encode(ip); err != nil { + t.Fatal("error in encode:", err) + } + return +} + +func TestNilPointerInsideInterface(t *testing.T) { + var ip *int + si := struct { + I interface{} + }{ + I: ip, + } + buf := new(bytes.Buffer) + err := NewEncoder(buf).Encode(si) + if err == nil { + t.Fatal("expected error, got none") + } + errMsg := err.Error() + if !strings.Contains(errMsg, "nil pointer") || !strings.Contains(errMsg, "interface") { + t.Fatal("expected error about nil pointer and interface, got:", errMsg) + } +} + +type Bug4Public struct { + Name string + Secret Bug4Secret +} + +type Bug4Secret struct { + a int // error: no exported fields. +} + +// Test that a failed compilation doesn't leave around an executable encoder. +// Issue 3273. +func TestMutipleEncodingsOfBadType(t *testing.T) { + x := Bug4Public{ + Name: "name", + Secret: Bug4Secret{1}, + } + buf := new(bytes.Buffer) + enc := NewEncoder(buf) + err := enc.Encode(x) + if err == nil { + t.Fatal("first encoding: expected error") + } + buf.Reset() + enc = NewEncoder(buf) + err = enc.Encode(x) + if err == nil { + t.Fatal("second encoding: expected error") + } + if !strings.Contains(err.Error(), "no exported fields") { + t.Errorf("expected error about no exported fields; got %v", err) + } +} + +// There was an error check comparing the length of the input with the +// length of the slice being decoded. It was wrong because the next +// thing in the input might be a type definition, which would lead to +// an incorrect length check. This test reproduces the corner case. + +type Z struct { +} + +func Test29ElementSlice(t *testing.T) { + Register(Z{}) + src := make([]interface{}, 100) // Size needs to be bigger than size of type definition. + for i := range src { + src[i] = Z{} + } + buf := new(bytes.Buffer) + err := NewEncoder(buf).Encode(src) + if err != nil { + t.Fatalf("encode: %v", err) + return + } + + var dst []interface{} + err = NewDecoder(buf).Decode(&dst) + if err != nil { + t.Errorf("decode: %v", err) + return + } +} diff --git a/src/pkg/encoding/gob/gobencdec_test.go b/src/pkg/encoding/gob/gobencdec_test.go index 45240d764..ddcd80b1a 100644 --- a/src/pkg/encoding/gob/gobencdec_test.go +++ b/src/pkg/encoding/gob/gobencdec_test.go @@ -1,4 +1,4 @@ -// Copyright 20011 The Go Authors. All rights reserved. +// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -142,6 +142,18 @@ type GobTest5 struct { V *ValueGobber } +type GobTest6 struct { + X int // guarantee we have something in common with GobTest* + V ValueGobber + W *ValueGobber +} + +type GobTest7 struct { + X int // guarantee we have something in common with GobTest* + V *ValueGobber + W ValueGobber +} + type GobTestIgnoreEncoder struct { X int // guarantee we have something in common with GobTest* } @@ -336,7 +348,7 @@ func TestGobEncoderFieldsOfDifferentType(t *testing.T) { t.Fatal("decode error:", err) } if y.G.s != "XYZ" { - t.Fatalf("expected `XYZ` got %c", y.G.s) + t.Fatalf("expected `XYZ` got %q", y.G.s) } } @@ -360,6 +372,61 @@ func TestGobEncoderValueEncoder(t *testing.T) { } } +// Test that we can use a value then a pointer type of a GobEncoder +// in the same encoded value. Bug 4647. +func TestGobEncoderValueThenPointer(t *testing.T) { + v := ValueGobber("forty-two") + w := ValueGobber("six-by-nine") + + // this was a bug: encoding a GobEncoder by value before a GobEncoder + // pointer would cause duplicate type definitions to be sent. + + b := new(bytes.Buffer) + enc := NewEncoder(b) + if err := enc.Encode(GobTest6{42, v, &w}); err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest6) + if err := dec.Decode(x); err != nil { + t.Fatal("decode error:", err) + } + if got, want := x.V, v; got != want { + t.Errorf("v = %q, want %q", got, want) + } + if got, want := x.W, w; got == nil { + t.Errorf("w = nil, want %q", want) + } else if *got != want { + t.Errorf("w = %q, want %q", *got, want) + } +} + +// Test that we can use a pointer then a value type of a GobEncoder +// in the same encoded value. +func TestGobEncoderPointerThenValue(t *testing.T) { + v := ValueGobber("forty-two") + w := ValueGobber("six-by-nine") + + b := new(bytes.Buffer) + enc := NewEncoder(b) + if err := enc.Encode(GobTest7{42, &v, w}); err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest7) + if err := dec.Decode(x); err != nil { + t.Fatal("decode error:", err) + } + if got, want := x.V, v; got == nil { + t.Errorf("v = nil, want %q", want) + } else if *got != want { + t.Errorf("v = %q, want %q", got, want) + } + if got, want := x.W, w; got != want { + t.Errorf("w = %q, want %q", got, want) + } +} + func TestGobEncoderFieldTypeError(t *testing.T) { // GobEncoder to non-decoder: error b := new(bytes.Buffer) diff --git a/src/pkg/encoding/gob/timing_test.go b/src/pkg/encoding/gob/timing_test.go index b9371c423..13eb11925 100644 --- a/src/pkg/encoding/gob/timing_test.go +++ b/src/pkg/encoding/gob/timing_test.go @@ -9,7 +9,6 @@ import ( "fmt" "io" "os" - "runtime" "testing" ) @@ -50,47 +49,43 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) { } func TestCountEncodeMallocs(t *testing.T) { + const N = 1000 + var buf bytes.Buffer enc := NewEncoder(&buf) bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")} - memstats := new(runtime.MemStats) - runtime.ReadMemStats(memstats) - mallocs := 0 - memstats.Mallocs - const count = 1000 - for i := 0; i < count; i++ { + + allocs := testing.AllocsPerRun(N, func() { err := enc.Encode(bench) if err != nil { t.Fatal("encode:", err) } - } - runtime.ReadMemStats(memstats) - mallocs += memstats.Mallocs - fmt.Printf("mallocs per encode of type Bench: %d\n", mallocs/count) + }) + fmt.Printf("mallocs per encode of type Bench: %v\n", allocs) } func TestCountDecodeMallocs(t *testing.T) { + const N = 1000 + var buf bytes.Buffer enc := NewEncoder(&buf) bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")} - const count = 1000 - for i := 0; i < count; i++ { + + // Fill the buffer with enough to decode + testing.AllocsPerRun(N, func() { err := enc.Encode(bench) if err != nil { t.Fatal("encode:", err) } - } + }) + dec := NewDecoder(&buf) - memstats := new(runtime.MemStats) - runtime.ReadMemStats(memstats) - mallocs := 0 - memstats.Mallocs - for i := 0; i < count; i++ { + allocs := testing.AllocsPerRun(N, func() { *bench = Bench{} err := dec.Decode(&bench) if err != nil { t.Fatal("decode:", err) } - } - runtime.ReadMemStats(memstats) - mallocs += memstats.Mallocs - fmt.Printf("mallocs per decode of type Bench: %d\n", mallocs/count) + }) + fmt.Printf("mallocs per decode of type Bench: %v\n", allocs) } diff --git a/src/pkg/encoding/gob/type.go b/src/pkg/encoding/gob/type.go index 0dd7a0a77..ea0db4eac 100644 --- a/src/pkg/encoding/gob/type.go +++ b/src/pkg/encoding/gob/type.go @@ -712,6 +712,7 @@ type GobDecoder interface { } var ( + registerLock sync.RWMutex nameToConcreteType = make(map[string]reflect.Type) concreteTypeToName = make(map[reflect.Type]string) ) @@ -723,6 +724,8 @@ func RegisterName(name string, value interface{}) { // reserved for nil panic("attempt to register empty name") } + registerLock.Lock() + defer registerLock.Unlock() ut := userType(reflect.TypeOf(value)) // Check for incompatible duplicates. The name must refer to the // same user type, and vice versa. @@ -749,12 +752,28 @@ func Register(value interface{}) { rt := reflect.TypeOf(value) name := rt.String() - // But for named types (or pointers to them), qualify with import path. + // But for named types (or pointers to them), qualify with import path (but see inner comment). // Dereference one pointer looking for a named type. star := "" if rt.Name() == "" { if pt := rt; pt.Kind() == reflect.Ptr { star = "*" + // NOTE: The following line should be rt = pt.Elem() to implement + // what the comment above claims, but fixing it would break compatibility + // with existing gobs. + // + // Given package p imported as "full/p" with these definitions: + // package p + // type T1 struct { ... } + // this table shows the intended and actual strings used by gob to + // name the types: + // + // Type Correct string Actual string + // + // T1 full/p.T1 full/p.T1 + // *T1 *full/p.T1 *p.T1 + // + // The missing full path cannot be fixed without breaking existing gob decoders. rt = pt } } diff --git a/src/pkg/encoding/gob/type_test.go b/src/pkg/encoding/gob/type_test.go index 42bdb4cf7..e230d22d4 100644 --- a/src/pkg/encoding/gob/type_test.go +++ b/src/pkg/encoding/gob/type_test.go @@ -5,6 +5,7 @@ package gob import ( + "bytes" "reflect" "testing" ) @@ -159,3 +160,63 @@ func TestRegistration(t *testing.T) { Register(new(T)) Register(new(T)) } + +type N1 struct{} +type N2 struct{} + +// See comment in type.go/Register. +func TestRegistrationNaming(t *testing.T) { + testCases := []struct { + t interface{} + name string + }{ + {&N1{}, "*gob.N1"}, + {N2{}, "encoding/gob.N2"}, + } + + for _, tc := range testCases { + Register(tc.t) + + tct := reflect.TypeOf(tc.t) + registerLock.RLock() + ct := nameToConcreteType[tc.name] + registerLock.RUnlock() + if ct != tct { + t.Errorf("nameToConcreteType[%q] = %v, want %v", tc.name, ct, tct) + } + // concreteTypeToName is keyed off the base type. + if tct.Kind() == reflect.Ptr { + tct = tct.Elem() + } + if n := concreteTypeToName[tct]; n != tc.name { + t.Errorf("concreteTypeToName[%v] got %v, want %v", tct, n, tc.name) + } + } +} + +func TestStressParallel(t *testing.T) { + type T2 struct{ A int } + c := make(chan bool) + const N = 10 + for i := 0; i < N; i++ { + go func() { + p := new(T2) + Register(p) + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(p) + if err != nil { + t.Error("encoder fail:", err) + } + dec := NewDecoder(b) + err = dec.Decode(p) + if err != nil { + t.Error("decoder fail:", err) + } + c <- true + }() + } + for i := 0; i < N; i++ { + <-c + } +} diff --git a/src/pkg/encoding/hex/hex_test.go b/src/pkg/encoding/hex/hex_test.go index 456f9eac7..356f590f0 100644 --- a/src/pkg/encoding/hex/hex_test.go +++ b/src/pkg/encoding/hex/hex_test.go @@ -65,7 +65,7 @@ func TestDecodeString(t *testing.T) { t.Errorf("#%d: unexpected err value: %s", i, err) continue } - if bytes.Compare(dst, test.dec) != 0 { + if !bytes.Equal(dst, test.dec) { t.Errorf("#%d: got: %#v want: #%v", i, dst, test.dec) } } diff --git a/src/pkg/encoding/json/bench_test.go b/src/pkg/encoding/json/bench_test.go index 333c1c0ce..29dbc26d4 100644 --- a/src/pkg/encoding/json/bench_test.go +++ b/src/pkg/encoding/json/bench_test.go @@ -153,5 +153,37 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) { b.Fatal("Unmmarshal:", err) } } - b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkUnmarshalString(b *testing.B) { + data := []byte(`"hello, world"`) + var s string + + for i := 0; i < b.N; i++ { + if err := Unmarshal(data, &s); err != nil { + b.Fatal("Unmarshal:", err) + } + } +} + +func BenchmarkUnmarshalFloat64(b *testing.B) { + var f float64 + data := []byte(`3.14`) + + for i := 0; i < b.N; i++ { + if err := Unmarshal(data, &f); err != nil { + b.Fatal("Unmarshal:", err) + } + } +} + +func BenchmarkUnmarshalInt64(b *testing.B) { + var x int64 + data := []byte(`3`) + + for i := 0; i < b.N; i++ { + if err := Unmarshal(data, &x); err != nil { + b.Fatal("Unmarshal:", err) + } + } } diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go index d61f88706..f2ec9cb67 100644 --- a/src/pkg/encoding/json/decode.go +++ b/src/pkg/encoding/json/decode.go @@ -33,6 +33,10 @@ import ( // the value pointed at by the pointer. If the pointer is nil, Unmarshal // allocates a new value for it to point to. // +// To unmarshal JSON into a struct, Unmarshal matches incoming object +// keys to the keys used by Marshal (either the struct field name or its tag), +// preferring an exact match but also accepting a case-insensitive match. +// // To unmarshal JSON into an interface value, Unmarshal unmarshals // the JSON into the concrete value contained in the interface value. // If the interface value is nil, that is, has no concrete value stored in it, @@ -51,24 +55,29 @@ import ( // If no more serious errors are encountered, Unmarshal returns // an UnmarshalTypeError describing the earliest such error. // +// When unmarshaling quoted strings, invalid UTF-8 or +// invalid UTF-16 surrogate pairs are not treated as an error. +// Instead, they are replaced by the Unicode replacement +// character U+FFFD. +// func Unmarshal(data []byte, v interface{}) error { - d := new(decodeState).init(data) - - // Quick check for well-formedness. + // Check for well-formedness. // Avoids filling out half a data structure // before discovering a JSON syntax error. + var d decodeState err := checkValid(data, &d.scan) if err != nil { return err } + d.init(data) return d.unmarshal(v) } // Unmarshaler is the interface implemented by objects // that can unmarshal a JSON description of themselves. -// The input can be assumed to be a valid JSON object -// encoding. UnmarshalJSON must copy the JSON data +// The input can be assumed to be a valid encoding of +// a JSON value. UnmarshalJSON must copy the JSON data // if it wishes to retain the data after returning. type Unmarshaler interface { UnmarshalJSON([]byte) error @@ -87,6 +96,7 @@ func (e *UnmarshalTypeError) Error() string { // An UnmarshalFieldError describes a JSON object key that // led to an unexported (and therefore unwritable) struct field. +// (No longer used; kept for compatibility.) type UnmarshalFieldError struct { Key string Type reflect.Type @@ -125,18 +135,33 @@ func (d *decodeState) unmarshal(v interface{}) (err error) { }() rv := reflect.ValueOf(v) - pv := rv - if pv.Kind() != reflect.Ptr || pv.IsNil() { + if rv.Kind() != reflect.Ptr || rv.IsNil() { return &InvalidUnmarshalError{reflect.TypeOf(v)} } d.scan.reset() - // We decode rv not pv.Elem because the Unmarshaler interface + // We decode rv not rv.Elem because the Unmarshaler interface // test must be applied at the top level of the value. d.value(rv) return d.savedError } +// A Number represents a JSON number literal. +type Number string + +// String returns the literal text of the number. +func (n Number) String() string { return string(n) } + +// Float64 returns the number as a float64. +func (n Number) Float64() (float64, error) { + return strconv.ParseFloat(string(n), 64) +} + +// Int64 returns the number as an int64. +func (n Number) Int64() (int64, error) { + return strconv.ParseInt(string(n), 10, 64) +} + // decodeState represents the state while decoding a JSON value. type decodeState struct { data []byte @@ -145,6 +170,7 @@ type decodeState struct { nextscan scanner // for calls to nextValue savedError error tempstr string // scratch space to avoid some allocations + useNumber bool } // errPhase is used for errors that should not happen unless @@ -265,47 +291,32 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, v = v.Addr() } for { - var isUnmarshaler bool - if v.Type().NumMethod() > 0 { - // Remember that this is an unmarshaler, - // but wait to return it until after allocating - // the pointer (if necessary). - _, isUnmarshaler = v.Interface().(Unmarshaler) - } - // Load value from interface, but only if the result will be // usefully addressable. - if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() { - e := iv.Elem() + if v.Kind() == reflect.Interface && !v.IsNil() { + e := v.Elem() if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { v = e continue } } - pv := v - if pv.Kind() != reflect.Ptr { + if v.Kind() != reflect.Ptr { break } - if pv.Elem().Kind() != reflect.Ptr && decodingNull && pv.CanSet() { - return nil, pv + if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { + break } - if pv.IsNil() { - pv.Set(reflect.New(pv.Type().Elem())) + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) } - if isUnmarshaler { - // Using v.Interface().(Unmarshaler) - // here means that we have to use a pointer - // as the struct field. We cannot use a value inside - // a pointer to a struct, because in that case - // v.Interface() is the value (x.f) not the pointer (&x.f). - // This is an unfortunate consequence of reflect. - // An alternative would be to look up the - // UnmarshalJSON method and return a FuncValue. - return v.Interface().(Unmarshaler), reflect.Value{} + if v.Type().NumMethod() > 0 { + if unmarshaler, ok := v.Interface().(Unmarshaler); ok { + return unmarshaler, reflect.Value{} + } } - v = pv.Elem() + v = v.Elem() } return nil, v } @@ -327,15 +338,19 @@ func (d *decodeState) array(v reflect.Value) { // Check type of target. switch v.Kind() { + case reflect.Interface: + if v.NumMethod() == 0 { + // Decoding into nil interface? Switch to non-reflect code. + v.Set(reflect.ValueOf(d.arrayInterface())) + return + } + // Otherwise it's invalid. + fallthrough default: d.saveError(&UnmarshalTypeError{"array", v.Type()}) d.off-- d.next() return - case reflect.Interface: - // Decoding into nil interface? Switch to non-reflect code. - v.Set(reflect.ValueOf(d.arrayInterface())) - return case reflect.Array: case reflect.Slice: break @@ -421,36 +436,27 @@ func (d *decodeState) object(v reflect.Value) { v = pv // Decoding into nil interface? Switch to non-reflect code. - iv := v - if iv.Kind() == reflect.Interface { - iv.Set(reflect.ValueOf(d.objectInterface())) + if v.Kind() == reflect.Interface && v.NumMethod() == 0 { + v.Set(reflect.ValueOf(d.objectInterface())) return } // Check type of target: struct or map[string]T - var ( - mv reflect.Value - sv reflect.Value - ) switch v.Kind() { case reflect.Map: - // map must have string type + // map must have string kind t := v.Type() - if t.Key() != reflect.TypeOf("") { + if t.Key().Kind() != reflect.String { d.saveError(&UnmarshalTypeError{"object", v.Type()}) break } - mv = v - if mv.IsNil() { - mv.Set(reflect.MakeMap(t)) + if v.IsNil() { + v.Set(reflect.MakeMap(t)) } case reflect.Struct: - sv = v + default: d.saveError(&UnmarshalTypeError{"object", v.Type()}) - } - - if !mv.IsValid() && !sv.IsValid() { d.off-- d.next() // skip over { } in input return @@ -482,8 +488,8 @@ func (d *decodeState) object(v reflect.Value) { var subv reflect.Value destring := false // whether the value is wrapped in a string to be decoded first - if mv.IsValid() { - elemType := mv.Type().Elem() + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() if !mapElem.IsValid() { mapElem = reflect.New(elemType).Elem() } else { @@ -491,51 +497,30 @@ func (d *decodeState) object(v reflect.Value) { } subv = mapElem } else { - var f reflect.StructField - var ok bool - st := sv.Type() - for i := 0; i < sv.NumField(); i++ { - sf := st.Field(i) - tag := sf.Tag.Get("json") - if tag == "-" { - // Pretend this field doesn't exist. - continue - } - if sf.Anonymous { - // Pretend this field doesn't exist, - // so that we can do a good job with - // these in a later version. - continue - } - // First, tag match - tagName, _ := parseTag(tag) - if tagName == key { - f = sf - ok = true - break // no better match possible - } - // Second, exact field name match - if sf.Name == key { - f = sf - ok = true + var f *field + fields := cachedTypeFields(v.Type()) + for i := range fields { + ff := &fields[i] + if ff.name == key { + f = ff + break } - // Third, case-insensitive field name match, - // but only if a better match hasn't already been seen - if !ok && strings.EqualFold(sf.Name, key) { - f = sf - ok = true + if f == nil && strings.EqualFold(ff.name, key) { + f = ff } } - - // Extract value; name must be exported. - if ok { - if f.PkgPath != "" { - d.saveError(&UnmarshalFieldError{key, st, f}) - } else { - subv = sv.FieldByIndex(f.Index) + if f != nil { + subv = v + destring = f.quoted + for _, i := range f.index { + if subv.Kind() == reflect.Ptr { + if subv.IsNil() { + subv.Set(reflect.New(subv.Type().Elem())) + } + subv = subv.Elem() + } + subv = subv.Field(i) } - _, opts := parseTag(f.Tag.Get("json")) - destring = opts.Contains("string") } } @@ -554,10 +539,12 @@ func (d *decodeState) object(v reflect.Value) { } else { d.value(subv) } + // Write value back to map; // if using struct, subv points into struct already. - if mv.IsValid() { - mv.SetMapIndex(reflect.ValueOf(key), subv) + if v.Kind() == reflect.Map { + kv := reflect.ValueOf(key).Convert(v.Type().Key()) + v.SetMapIndex(kv, subv) } // Next token must be , or }. @@ -586,6 +573,21 @@ func (d *decodeState) literal(v reflect.Value) { d.literalStore(d.data[start:d.off], v, false) } +// convertNumber converts the number literal s to a float64 or a Number +// depending on the setting of d.useNumber. +func (d *decodeState) convertNumber(s string) (interface{}, error) { + if d.useNumber { + return Number(s), nil + } + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0)} + } + return f, nil +} + +var numberType = reflect.TypeOf(Number("")) + // literalStore decodes a literal stored in item into v. // // fromQuoted indicates whether this literal came from unwrapping a @@ -612,12 +614,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool switch c := item[0]; c { case 'n': // null switch v.Kind() { - default: - d.saveError(&UnmarshalTypeError{"null", v.Type()}) case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: v.Set(reflect.Zero(v.Type())) + // otherwise, ignore null for primitives/string } - case 't', 'f': // true, false value := c == 't' switch v.Kind() { @@ -630,7 +630,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Bool: v.SetBool(value) case reflect.Interface: - v.Set(reflect.ValueOf(value)) + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(value)) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + } } case '"': // string @@ -660,7 +664,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.String: v.SetString(string(s)) case reflect.Interface: - v.Set(reflect.ValueOf(string(s))) + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(string(s))) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + } } default: // number @@ -674,15 +682,23 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool s := string(item) switch v.Kind() { default: + if v.Kind() == reflect.String && v.Type() == numberType { + v.SetString(s) + break + } if fromQuoted { d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) } else { d.error(&UnmarshalTypeError{"number", v.Type()}) } case reflect.Interface: - n, err := strconv.ParseFloat(s, 64) + n, err := d.convertNumber(s) if err != nil { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + d.saveError(err) + break + } + if v.NumMethod() != 0 { + d.saveError(&UnmarshalTypeError{"number", v.Type()}) break } v.Set(reflect.ValueOf(n)) @@ -735,7 +751,7 @@ func (d *decodeState) valueInterface() interface{} { // arrayInterface is like array but returns []interface{}. func (d *decodeState) arrayInterface() []interface{} { - var v []interface{} + var v = make([]interface{}, 0) for { // Look ahead for ] - can only happen on first iteration. op := d.scanWhile(scanSkipSpace) @@ -836,9 +852,9 @@ func (d *decodeState) literalInterface() interface{} { if c != '-' && (c < '0' || c > '9') { d.error(errPhase) } - n, err := strconv.ParseFloat(string(item), 64) + n, err := d.convertNumber(string(item)) if err != nil { - d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.TypeOf(0.0)}) + d.saveError(err) } return n } @@ -979,11 +995,3 @@ func unquoteBytes(s []byte) (t []byte, ok bool) { } return b[0:w], true } - -// The following is issue 3069. - -// BUG(rsc): This package ignores anonymous (embedded) struct fields -// during encoding and decoding. A future version may assign meaning -// to them. To force an anonymous field to be ignored in all future -// versions of this package, use an explicit `json:"-"` tag in the struct -// definition. diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go index 6fac22c4a..e1bd918dd 100644 --- a/src/pkg/encoding/json/decode_test.go +++ b/src/pkg/encoding/json/decode_test.go @@ -7,9 +7,11 @@ package json import ( "bytes" "fmt" + "image" "reflect" "strings" "testing" + "time" ) type T struct { @@ -18,6 +20,32 @@ type T struct { Z int `json:"-"` } +type U struct { + Alphabet string `json:"alpha"` +} + +type V struct { + F1 interface{} + F2 int32 + F3 Number +} + +// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshalling with and +// without UseNumber +var ifaceNumAsFloat64 = map[string]interface{}{ + "k1": float64(1), + "k2": "s", + "k3": []interface{}{float64(1), float64(2.0), float64(3e-3)}, + "k4": map[string]interface{}{"kk1": "s", "kk2": float64(2)}, +} + +var ifaceNumAsNumber = map[string]interface{}{ + "k1": Number("1"), + "k2": "s", + "k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")}, + "k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")}, +} + type tx struct { x int } @@ -48,55 +76,297 @@ var ( umstruct = ustruct{unmarshaler{true}} ) +// Test data structures for anonymous fields. + +type Point struct { + Z int +} + +type Top struct { + Level0 int + Embed0 + *Embed0a + *Embed0b `json:"e,omitempty"` // treated as named + Embed0c `json:"-"` // ignored + Loop + Embed0p // has Point with X, Y, used + Embed0q // has Point with Z, used +} + +type Embed0 struct { + Level1a int // overridden by Embed0a's Level1a with json tag + Level1b int // used because Embed0a's Level1b is renamed + Level1c int // used because Embed0a's Level1c is ignored + Level1d int // annihilated by Embed0a's Level1d + Level1e int `json:"x"` // annihilated by Embed0a.Level1e +} + +type Embed0a struct { + Level1a int `json:"Level1a,omitempty"` + Level1b int `json:"LEVEL1B,omitempty"` + Level1c int `json:"-"` + Level1d int // annihilated by Embed0's Level1d + Level1f int `json:"x"` // annihilated by Embed0's Level1e +} + +type Embed0b Embed0 + +type Embed0c Embed0 + +type Embed0p struct { + image.Point +} + +type Embed0q struct { + Point +} + +type Loop struct { + Loop1 int `json:",omitempty"` + Loop2 int `json:",omitempty"` + *Loop +} + +// From reflect test: +// The X in S6 and S7 annihilate, but they also block the X in S8.S9. +type S5 struct { + S6 + S7 + S8 +} + +type S6 struct { + X int +} + +type S7 S6 + +type S8 struct { + S9 +} + +type S9 struct { + X int + Y int +} + +// From reflect test: +// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. +type S10 struct { + S11 + S12 + S13 +} + +type S11 struct { + S6 +} + +type S12 struct { + S6 +} + +type S13 struct { + S8 +} + type unmarshalTest struct { - in string - ptr interface{} - out interface{} - err error + in string + ptr interface{} + out interface{} + err error + useNumber bool +} + +type Ambig struct { + // Given "hello", the first match should win. + First int `json:"HELLO"` + Second int `json:"Hello"` } var unmarshalTests = []unmarshalTest{ // basic types - {`true`, new(bool), true, nil}, - {`1`, new(int), 1, nil}, - {`1.2`, new(float64), 1.2, nil}, - {`-5`, new(int16), int16(-5), nil}, - {`"a\u1234"`, new(string), "a\u1234", nil}, - {`"http:\/\/"`, new(string), "http://", nil}, - {`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E", nil}, - {`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD", nil}, - {"null", new(interface{}), nil, nil}, - {`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.TypeOf("")}}, - {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}}, + {in: `true`, ptr: new(bool), out: true}, + {in: `1`, ptr: new(int), out: 1}, + {in: `1.2`, ptr: new(float64), out: 1.2}, + {in: `-5`, ptr: new(int16), out: int16(-5)}, + {in: `2`, ptr: new(Number), out: Number("2"), useNumber: true}, + {in: `2`, ptr: new(Number), out: Number("2")}, + {in: `2`, ptr: new(interface{}), out: float64(2.0)}, + {in: `2`, ptr: new(interface{}), out: Number("2"), useNumber: true}, + {in: `"a\u1234"`, ptr: new(string), out: "a\u1234"}, + {in: `"http:\/\/"`, ptr: new(string), out: "http://"}, + {in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"}, + {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"}, + {in: "null", ptr: new(interface{}), out: nil}, + {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf("")}}, + {in: `{"x": 1}`, ptr: new(tx), out: tx{}}, + {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}}, + {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true}, + {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64}, + {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true}, + + // raw values with whitespace + {in: "\n true ", ptr: new(bool), out: true}, + {in: "\t 1 ", ptr: new(int), out: 1}, + {in: "\r 1.2 ", ptr: new(float64), out: 1.2}, + {in: "\t -5 \n", ptr: new(int16), out: int16(-5)}, + {in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"}, // Z has a "-" tag. - {`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil}, + {in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}}, + + {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}}, + {in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}}, + {in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}}, // syntax errors - {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, - {`[1, 2, 3+]`, nil, nil, &SyntaxError{"invalid character '+' after array element", 9}}, + {in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}}, + {in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}}, + {in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true}, + + // raw value errors + {in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}}, + {in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}}, + {in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}}, + {in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}}, // array tests - {`[1, 2, 3]`, new([3]int), [3]int{1, 2, 3}, nil}, - {`[1, 2, 3]`, new([1]int), [1]int{1}, nil}, - {`[1, 2, 3]`, new([5]int), [5]int{1, 2, 3, 0, 0}, nil}, + {in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}}, + {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}}, + {in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}}, + + // empty array to interface test + {in: `[]`, ptr: new([]interface{}), out: []interface{}{}}, + {in: `null`, ptr: new([]interface{}), out: []interface{}(nil)}, + {in: `{"T":[]}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}}, + {in: `{"T":null}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": interface{}(nil)}}, // composite tests - {allValueIndent, new(All), allValue, nil}, - {allValueCompact, new(All), allValue, nil}, - {allValueIndent, new(*All), &allValue, nil}, - {allValueCompact, new(*All), &allValue, nil}, - {pallValueIndent, new(All), pallValue, nil}, - {pallValueCompact, new(All), pallValue, nil}, - {pallValueIndent, new(*All), &pallValue, nil}, - {pallValueCompact, new(*All), &pallValue, nil}, + {in: allValueIndent, ptr: new(All), out: allValue}, + {in: allValueCompact, ptr: new(All), out: allValue}, + {in: allValueIndent, ptr: new(*All), out: &allValue}, + {in: allValueCompact, ptr: new(*All), out: &allValue}, + {in: pallValueIndent, ptr: new(All), out: pallValue}, + {in: pallValueCompact, ptr: new(All), out: pallValue}, + {in: pallValueIndent, ptr: new(*All), out: &pallValue}, + {in: pallValueCompact, ptr: new(*All), out: &pallValue}, // unmarshal interface test - {`{"T":false}`, &um0, umtrue, nil}, // use "false" so test will fail if custom unmarshaler is not called - {`{"T":false}`, &ump, &umtrue, nil}, - {`[{"T":false}]`, &umslice, umslice, nil}, - {`[{"T":false}]`, &umslicep, &umslice, nil}, - {`{"M":{"T":false}}`, &umstruct, umstruct, nil}, + {in: `{"T":false}`, ptr: &um0, out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called + {in: `{"T":false}`, ptr: &ump, out: &umtrue}, + {in: `[{"T":false}]`, ptr: &umslice, out: umslice}, + {in: `[{"T":false}]`, ptr: &umslicep, out: &umslice}, + {in: `{"M":{"T":false}}`, ptr: &umstruct, out: umstruct}, + + { + in: `{ + "Level0": 1, + "Level1b": 2, + "Level1c": 3, + "x": 4, + "Level1a": 5, + "LEVEL1B": 6, + "e": { + "Level1a": 8, + "Level1b": 9, + "Level1c": 10, + "Level1d": 11, + "x": 12 + }, + "Loop1": 13, + "Loop2": 14, + "X": 15, + "Y": 16, + "Z": 17 + }`, + ptr: new(Top), + out: Top{ + Level0: 1, + Embed0: Embed0{ + Level1b: 2, + Level1c: 3, + }, + Embed0a: &Embed0a{ + Level1a: 5, + Level1b: 6, + }, + Embed0b: &Embed0b{ + Level1a: 8, + Level1b: 9, + Level1c: 10, + Level1d: 11, + Level1e: 12, + }, + Loop: Loop{ + Loop1: 13, + Loop2: 14, + }, + Embed0p: Embed0p{ + Point: image.Point{X: 15, Y: 16}, + }, + Embed0q: Embed0q{ + Point: Point{Z: 17}, + }, + }, + }, + { + in: `{"hello": 1}`, + ptr: new(Ambig), + out: Ambig{First: 1}, + }, + + { + in: `{"X": 1,"Y":2}`, + ptr: new(S5), + out: S5{S8: S8{S9: S9{Y: 2}}}, + }, + { + in: `{"X": 1,"Y":2}`, + ptr: new(S10), + out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}}, + }, + + // invalid UTF-8 is coerced to valid UTF-8. + { + in: "\"hello\xffworld\"", + ptr: new(string), + out: "hello\ufffdworld", + }, + { + in: "\"hello\xc2\xc2world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\xc2\xffworld\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\\ud800world\"", + ptr: new(string), + out: "hello\ufffdworld", + }, + { + in: "\"hello\\ud800\\ud800world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\\ud800\\ud800world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"", + ptr: new(string), + out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld", + }, } func TestMarshal(t *testing.T) { @@ -135,6 +405,18 @@ func TestMarshalBadUTF8(t *testing.T) { } } +func TestMarshalNumberZeroVal(t *testing.T) { + var n Number + out, err := Marshal(n) + if err != nil { + t.Fatal(err) + } + outStr := string(out) + if outStr != "0" { + t.Fatalf("Invalid zero val for Number: %q", outStr) + } +} + func TestUnmarshal(t *testing.T) { for i, tt := range unmarshalTests { var scan scanner @@ -150,7 +432,11 @@ func TestUnmarshal(t *testing.T) { } // v = new(right-type) v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) - if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) { + dec := NewDecoder(bytes.NewBuffer(in)) + if tt.useNumber { + dec.UseNumber() + } + if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) { t.Errorf("#%d: %v want %v", i, err, tt.err) continue } @@ -162,6 +448,28 @@ func TestUnmarshal(t *testing.T) { println(string(data)) continue } + + // Check round trip. + if tt.err == nil { + enc, err := Marshal(v.Interface()) + if err != nil { + t.Errorf("#%d: error re-marshaling: %v", i, err) + continue + } + vv := reflect.New(reflect.TypeOf(tt.ptr).Elem()) + dec = NewDecoder(bytes.NewBuffer(enc)) + if tt.useNumber { + dec.UseNumber() + } + if err := dec.Decode(vv.Interface()); err != nil { + t.Errorf("#%d: error re-unmarshaling: %v", i, err) + continue + } + if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) { + t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface()) + continue + } + } } } @@ -175,13 +483,45 @@ func TestUnmarshalMarshal(t *testing.T) { if err != nil { t.Fatalf("Marshal: %v", err) } - if bytes.Compare(jsonBig, b) != 0 { + if !bytes.Equal(jsonBig, b) { t.Errorf("Marshal jsonBig") diff(t, b, jsonBig) return } } +var numberTests = []struct { + in string + i int64 + intErr string + f float64 + floatErr string +}{ + {in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1}, + {in: "-12", i: -12, f: -12.0}, + {in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"}, +} + +// Independent of Decode, basic coverage of the accessors in Number +func TestNumberAccessors(t *testing.T) { + for _, tt := range numberTests { + n := Number(tt.in) + if s := n.String(); s != tt.in { + t.Errorf("Number(%q).String() is %q", tt.in, s) + } + if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i { + t.Errorf("Number(%q).Int64() is %d", tt.in, i) + } else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) { + t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err) + } + if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f { + t.Errorf("Number(%q).Float64() is %g", tt.in, f) + } else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) { + t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err) + } + } +} + func TestLargeByteSlice(t *testing.T) { s0 := make([]byte, 2000) for i := range s0 { @@ -195,7 +535,7 @@ func TestLargeByteSlice(t *testing.T) { if err := Unmarshal(b, &s1); err != nil { t.Fatalf("Unmarshal: %v", err) } - if bytes.Compare(s0, s1) != 0 { + if !bytes.Equal(s0, s1) { t.Errorf("Marshal large byte slice") diff(t, s0, s1) } @@ -610,35 +950,6 @@ func TestRefUnmarshal(t *testing.T) { } } -// Test that anonymous fields are ignored. -// We may assign meaning to them later. -func TestAnonymous(t *testing.T) { - type S struct { - T - N int - } - - data, err := Marshal(new(S)) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - want := `{"N":0}` - if string(data) != want { - t.Fatalf("Marshal = %#q, want %#q", string(data), want) - } - - var s S - if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if s.N != 2 { - t.Fatal("Unmarshal: did not set N") - } - if s.T.Y != 0 { - t.Fatal("Unmarshal: did set T.Y") - } -} - // Test that the empty string doesn't panic decoding when ,string is specified // Issue 3450 func TestEmptyString(t *testing.T) { @@ -703,3 +1014,167 @@ func TestInterfaceSet(t *testing.T) { } } } + +// JSON null values should be ignored for primitives and string values instead of resulting in an error. +// Issue 2540 +func TestUnmarshalNulls(t *testing.T) { + jsonData := []byte(`{ + "Bool" : null, + "Int" : null, + "Int8" : null, + "Int16" : null, + "Int32" : null, + "Int64" : null, + "Uint" : null, + "Uint8" : null, + "Uint16" : null, + "Uint32" : null, + "Uint64" : null, + "Float32" : null, + "Float64" : null, + "String" : null}`) + + nulls := All{ + Bool: true, + Int: 2, + Int8: 3, + Int16: 4, + Int32: 5, + Int64: 6, + Uint: 7, + Uint8: 8, + Uint16: 9, + Uint32: 10, + Uint64: 11, + Float32: 12.1, + Float64: 13.1, + String: "14"} + + err := Unmarshal(jsonData, &nulls) + if err != nil { + t.Errorf("Unmarshal of null values failed: %v", err) + } + if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 || + nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 || + nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" { + + t.Errorf("Unmarshal of null values affected primitives") + } +} + +func TestStringKind(t *testing.T) { + type stringKind string + type aMap map[stringKind]int + + var m1, m2 map[stringKind]int + m1 = map[stringKind]int{ + "foo": 42, + } + + data, err := Marshal(m1) + if err != nil { + t.Errorf("Unexpected error marshalling: %v", err) + } + + err = Unmarshal(data, &m2) + if err != nil { + t.Errorf("Unexpected error unmarshalling: %v", err) + } + + if !reflect.DeepEqual(m1, m2) { + t.Error("Items should be equal after encoding and then decoding") + } + +} + +var decodeTypeErrorTests = []struct { + dest interface{} + src string +}{ + {new(string), `{"user": "name"}`}, // issue 4628. + {new(error), `{}`}, // issue 4222 + {new(error), `[]`}, + {new(error), `""`}, + {new(error), `123`}, + {new(error), `true`}, +} + +func TestUnmarshalTypeError(t *testing.T) { + for _, item := range decodeTypeErrorTests { + err := Unmarshal([]byte(item.src), item.dest) + if _, ok := err.(*UnmarshalTypeError); !ok { + t.Errorf("expected type error for Unmarshal(%q, type %T): got %T", + item.src, item.dest, err) + } + } +} + +var unmarshalSyntaxTests = []string{ + "tru", + "fals", + "nul", + "123e", + `"hello`, + `[1,2,3`, + `{"key":1`, + `{"key":1,`, +} + +func TestUnmarshalSyntax(t *testing.T) { + var x interface{} + for _, src := range unmarshalSyntaxTests { + err := Unmarshal([]byte(src), &x) + if _, ok := err.(*SyntaxError); !ok { + t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err) + } + } +} + +// Test handling of unexported fields that should be ignored. +// Issue 4660 +type unexportedFields struct { + Name string + m map[string]interface{} `json:"-"` + m2 map[string]interface{} `json:"abcd"` +} + +func TestUnmarshalUnexported(t *testing.T) { + input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}}` + want := &unexportedFields{Name: "Bob"} + + out := &unexportedFields{} + err := Unmarshal([]byte(input), out) + if err != nil { + t.Errorf("got error %v, expected nil", err) + } + if !reflect.DeepEqual(out, want) { + t.Errorf("got %q, want %q", out, want) + } +} + +// Time3339 is a time.Time which encodes to and from JSON +// as an RFC 3339 time in UTC. +type Time3339 time.Time + +func (t *Time3339) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { + return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b) + } + tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1])) + if err != nil { + return err + } + *t = Time3339(tm) + return nil +} + +func TestUnmarshalJSONLiteralError(t *testing.T) { + var t3 Time3339 + err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3) + if err == nil { + t.Fatalf("expected error; got time %v", time.Time(t3)) + } + if !strings.Contains(err.Error(), "range") { + t.Errorf("got err = %v; want out of range error", err) + } +} diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go index b6e1cb16e..fb57f1d51 100644 --- a/src/pkg/encoding/json/encode.go +++ b/src/pkg/encoding/json/encode.go @@ -36,7 +36,7 @@ import ( // // Boolean values encode as JSON booleans. // -// Floating point and integer values encode as JSON numbers. +// Floating point, integer, and Number values encode as JSON numbers. // // String values encode as JSON strings, with each invalid UTF-8 sequence // replaced by the encoding of the Unicode replacement character U+FFFD. @@ -55,7 +55,7 @@ import ( // nil pointer or interface value, and any array, slice, map, or string of // length zero. The object's default key string is the struct field name // but can be specified in the struct field's tag value. The "json" key in -// struct field's tag value is the key name, followed by an optional comma +// the struct field's tag value is the key name, followed by an optional comma // and options. Examples: // // // Field is ignored by this package. @@ -75,8 +75,9 @@ import ( // Field int `json:",omitempty"` // // The "string" option signals that a field is stored as JSON inside a -// JSON-encoded string. This extra level of encoding is sometimes -// used when communicating with JavaScript programs: +// JSON-encoded string. It applies only to fields of string, floating point, +// or integer types. This extra level of encoding is sometimes used when +// communicating with JavaScript programs: // // Int64String int64 `json:",string"` // @@ -84,6 +85,16 @@ import ( // only Unicode letters, digits, dollar signs, percent signs, hyphens, // underscores and slashes. // +// Anonymous struct fields are usually marshaled as if their inner exported fields +// were fields in the outer struct, subject to the usual Go visibility rules. +// An anonymous struct field with a name given in its JSON tag is treated as +// having that name instead of as anonymous. +// +// Handling of anonymous struct fields is new in Go 1.1. +// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of +// an anonymous struct field in both current and earlier versions, give the field +// a JSON tag of "-". +// // Map values encode as JSON objects. // The map's key type must be string; the object keys are used directly // as map keys. @@ -312,6 +323,14 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { e.Write(b) } case reflect.String: + if v.Type() == numberType { + numStr := v.String() + if numStr == "" { + numStr = "0" // Number's zero-val + } + e.WriteString(numStr) + break + } if quoted { sb, err := Marshal(v.String()) if err != nil { @@ -325,9 +344,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { case reflect.Struct: e.WriteByte('{') first := true - for _, ef := range encodeFields(v.Type()) { - fieldValue := v.Field(ef.i) - if ef.omitEmpty && isEmptyValue(fieldValue) { + for _, f := range cachedTypeFields(v.Type()) { + fv := fieldByIndex(v, f.index) + if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { continue } if first { @@ -335,9 +354,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { } else { e.WriteByte(',') } - e.string(ef.tag) + e.string(f.name) e.WriteByte(':') - e.reflectValueQuoted(fieldValue, ef.quoted) + e.reflectValueQuoted(fv, f.quoted) } e.WriteByte('}') @@ -419,7 +438,7 @@ func isValidTag(s string) bool { } for _, c := range s { switch { - case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~", c): + case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. @@ -432,6 +451,19 @@ func isValidTag(s string) bool { return true } +func fieldByIndex(v reflect.Value, index []int) reflect.Value { + for _, i := range index { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return reflect.Value{} + } + v = v.Elem() + } + v = v.Field(i) + } + return v +} + // stringValues is a slice of reflect.Value holding *reflect.StringValue. // It implements the methods to sort by string. type stringValues []reflect.Value @@ -490,67 +522,185 @@ func (e *encodeState) string(s string) (int, error) { return e.Len() - len0, nil } -// encodeField contains information about how to encode a field of a -// struct. -type encodeField struct { - i int // field index in struct - tag string - quoted bool +// A field represents a single field found in a struct. +type field struct { + name string + tag bool + index []int + typ reflect.Type omitEmpty bool + quoted bool } -var ( - typeCacheLock sync.RWMutex - encodeFieldsCache = make(map[reflect.Type][]encodeField) -) +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from json tag", then +// breaking ties with index sequence. +type byName []field -// encodeFields returns a slice of encodeField for a given -// struct type. -func encodeFields(t reflect.Type) []encodeField { - typeCacheLock.RLock() - fs, ok := encodeFieldsCache[t] - typeCacheLock.RUnlock() - if ok { - return fs - } +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - typeCacheLock.Lock() - defer typeCacheLock.Unlock() - fs, ok = encodeFieldsCache[t] - if ok { - return fs +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} - v := reflect.Zero(t) - n := v.NumField() - for i := 0; i < n; i++ { - f := t.Field(i) - if f.PkgPath != "" { - continue +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false } - if f.Anonymous { - // We want to do a better job with these later, - // so for now pretend they don't exist. - continue + if xik != x[j].index[k] { + return xik < x[j].index[k] } - var ef encodeField - ef.i = i - ef.tag = f.Name + } + return len(x[i].index) < len(x[j].index) +} + +// typeFields returns a list of fields that JSON should recognize for the given type. +// The algorithm is breadth-first search over the set of structs to include - the top struct +// and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + count := map[reflect.Type]int{} + nextCount := map[reflect.Type]int{} + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} - tv := f.Tag.Get("json") - if tv != "" { - if tv == "-" { + for _, f := range current { + if visited[f.typ] { continue } - name, opts := parseTag(tv) - if isValidTag(name) { - ef.tag = name + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" { // unexported + continue + } + tag := sf.Tag.Get("json") + if tag == "-" { + continue + } + name, opts := parseTag(tag) + if !isValidTag(name) { + name = "" + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := name != "" + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft, + opts.Contains("omitempty"), opts.Contains("string")}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + next = append(next, field{name: ft.Name(), index: index, typ: ft}) + } } - ef.omitEmpty = opts.Contains("omitempty") - ef.quoted = opts.Contains("string") } - fs = append(fs, ef) } - encodeFieldsCache[t] = fs - return fs + + sort.Sort(byName(fields)) + + // Remove fields with annihilating name collisions + // and also fields shadowed by fields with explicit JSON tags. + name := "" + out := fields[:0] + for _, f := range fields { + if f.name != name { + name = f.name + out = append(out, f) + continue + } + if n := len(out); n > 0 && out[n-1].name == name && (!out[n-1].tag || f.tag) { + out = out[:n-1] + } + } + fields = out + + sort.Sort(byIndex(fields)) + + return fields +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f } diff --git a/src/pkg/encoding/json/encode_test.go b/src/pkg/encoding/json/encode_test.go index cb1c77eb5..be74c997c 100644 --- a/src/pkg/encoding/json/encode_test.go +++ b/src/pkg/encoding/json/encode_test.go @@ -186,3 +186,23 @@ func TestMarshalerEscaping(t *testing.T) { t.Errorf("got %q, want %q", got, want) } } + +type IntType int + +type MyStruct struct { + IntType +} + +func TestAnonymousNonstruct(t *testing.T) { + var i IntType = 11 + a := MyStruct{i} + const want = `{"IntType":11}` + + b, err := Marshal(a) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if got := string(b); got != want { + t.Errorf("got %q, want %q", got, want) + } +} diff --git a/src/pkg/encoding/json/scanner_test.go b/src/pkg/encoding/json/scanner_test.go index 14d850865..77d3455d3 100644 --- a/src/pkg/encoding/json/scanner_test.go +++ b/src/pkg/encoding/json/scanner_test.go @@ -92,7 +92,7 @@ func TestCompactBig(t *testing.T) { t.Fatalf("Compact: %v", err) } b := buf.Bytes() - if bytes.Compare(b, jsonBig) != 0 { + if !bytes.Equal(b, jsonBig) { t.Error("Compact(jsonBig) != jsonBig") diff(t, b, jsonBig) return @@ -118,7 +118,7 @@ func TestIndentBig(t *testing.T) { t.Fatalf("Indent2: %v", err) } b1 := buf1.Bytes() - if bytes.Compare(b1, b) != 0 { + if !bytes.Equal(b1, b) { t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)") diff(t, b1, b) return @@ -130,7 +130,7 @@ func TestIndentBig(t *testing.T) { t.Fatalf("Compact: %v", err) } b1 = buf1.Bytes() - if bytes.Compare(b1, jsonBig) != 0 { + if !bytes.Equal(b1, jsonBig) { t.Error("Compact(Indent(jsonBig)) != jsonBig") diff(t, b1, jsonBig) return @@ -277,9 +277,6 @@ func genArray(n int) []interface{} { if f > n { f = n } - if n > 0 && f == 0 { - f = 1 - } x := make([]interface{}, f) for i := range x { x[i] = genValue(((i+1)*n)/f - (i*n)/f) diff --git a/src/pkg/encoding/json/stream.go b/src/pkg/encoding/json/stream.go index 7d1cc5f11..00f4726cf 100644 --- a/src/pkg/encoding/json/stream.go +++ b/src/pkg/encoding/json/stream.go @@ -5,6 +5,7 @@ package json import ( + "bytes" "errors" "io" ) @@ -26,6 +27,10 @@ func NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r} } +// UseNumber causes the Decoder to unmarshal a number into an interface{} as a +// Number instead of as a float64. +func (dec *Decoder) UseNumber() { dec.d.useNumber = true } + // Decode reads the next JSON-encoded value from its // input and stores it in the value pointed to by v. // @@ -54,6 +59,12 @@ func (dec *Decoder) Decode(v interface{}) error { return err } +// Buffered returns a reader of the data remaining in the Decoder's +// buffer. The reader is valid until the next call to Decode. +func (dec *Decoder) Buffered() io.Reader { + return bytes.NewReader(dec.buf) +} + // readValue reads a JSON value into dec.buf. // It returns the length of the encoding. func (dec *Decoder) readValue() (int, error) { @@ -74,7 +85,7 @@ Input: // scanEnd is delayed one byte. // We might block trying to get that byte from src, // so instead invent a space byte. - if v == scanEndObject && dec.scan.step(&dec.scan, ' ') == scanEnd { + if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd { scanp += i + 1 break Input } diff --git a/src/pkg/encoding/json/stream_test.go b/src/pkg/encoding/json/stream_test.go index ce5a7e6d6..07c9e1d39 100644 --- a/src/pkg/encoding/json/stream_test.go +++ b/src/pkg/encoding/json/stream_test.go @@ -6,7 +6,10 @@ package json import ( "bytes" + "io/ioutil" + "net" "reflect" + "strings" "testing" ) @@ -82,6 +85,28 @@ func TestDecoder(t *testing.T) { } } +func TestDecoderBuffered(t *testing.T) { + r := strings.NewReader(`{"Name": "Gopher"} extra `) + var m struct { + Name string + } + d := NewDecoder(r) + err := d.Decode(&m) + if err != nil { + t.Fatal(err) + } + if m.Name != "Gopher" { + t.Errorf("Name = %q; want Gopher", m.Name) + } + rest, err := ioutil.ReadAll(d.Buffered()) + if err != nil { + t.Fatal(err) + } + if g, w := string(rest), " extra "; g != w { + t.Errorf("Remaining = %q; want %q", g, w) + } +} + func nlines(s string, n int) string { if n <= 0 { return "" @@ -145,3 +170,24 @@ func TestNullRawMessage(t *testing.T) { t.Fatalf("Marshal: have %#q want %#q", b, msg) } } + +var blockingTests = []string{ + `{"x": 1}`, + `[1, 2, 3]`, +} + +func TestBlocking(t *testing.T) { + for _, enc := range blockingTests { + r, w := net.Pipe() + go w.Write([]byte(enc)) + var val interface{} + + // If Decode reads beyond what w.Write writes above, + // it will block, and the test will deadlock. + if err := NewDecoder(r).Decode(&val); err != nil { + t.Errorf("decoding %s: %v", enc, err) + } + r.Close() + w.Close() + } +} diff --git a/src/pkg/encoding/json/tagkey_test.go b/src/pkg/encoding/json/tagkey_test.go index da8b12bd8..23e71c752 100644 --- a/src/pkg/encoding/json/tagkey_test.go +++ b/src/pkg/encoding/json/tagkey_test.go @@ -60,6 +60,14 @@ type badCodeTag struct { Z string `json:" !\"#&'()*+,."` } +type spaceTag struct { + Q string `json:"With space"` +} + +type unicodeTag struct { + W string `json:"Ελλάδα"` +} + var structTagObjectKeyTests = []struct { raw interface{} value string @@ -78,6 +86,8 @@ var structTagObjectKeyTests = []struct { {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, {percentSlashTag{"brut"}, "brut", "text/html%"}, {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, + {spaceTag{"Perreddu"}, "Perreddu", "With space"}, + {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, } func TestStructTagObjectKey(t *testing.T) { diff --git a/src/pkg/encoding/pem/pem.go b/src/pkg/encoding/pem/pem.go index 3c1f5ab70..8ff7ee8c3 100644 --- a/src/pkg/encoding/pem/pem.go +++ b/src/pkg/encoding/pem/pem.go @@ -11,6 +11,7 @@ import ( "bytes" "encoding/base64" "io" + "sort" ) // A Block represents a PEM encoded structure. @@ -209,26 +210,46 @@ func (l *lineBreaker) Close() (err error) { return } -func Encode(out io.Writer, b *Block) (err error) { - _, err = out.Write(pemStart[1:]) - if err != nil { - return +func writeHeader(out io.Writer, k, v string) error { + _, err := out.Write([]byte(k + ": " + v + "\n")) + return err +} + +func Encode(out io.Writer, b *Block) error { + if _, err := out.Write(pemStart[1:]); err != nil { + return err } - _, err = out.Write([]byte(b.Type + "-----\n")) - if err != nil { - return + if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil { + return err } if len(b.Headers) > 0 { - for k, v := range b.Headers { - _, err = out.Write([]byte(k + ": " + v + "\n")) - if err != nil { - return + const procType = "Proc-Type" + h := make([]string, 0, len(b.Headers)) + hasProcType := false + for k := range b.Headers { + if k == procType { + hasProcType = true + continue } + h = append(h, k) } - _, err = out.Write([]byte{'\n'}) - if err != nil { - return + // The Proc-Type header must be written first. + // See RFC 1421, section 4.6.1.1 + if hasProcType { + if err := writeHeader(out, procType, b.Headers[procType]); err != nil { + return err + } + } + // For consistency of output, write other headers sorted by key. + sort.Strings(h) + for _, k := range h { + if err := writeHeader(out, k, b.Headers[k]); err != nil { + return err + } + } + if _, err := out.Write([]byte{'\n'}); err != nil { + return err } } @@ -236,19 +257,17 @@ func Encode(out io.Writer, b *Block) (err error) { breaker.out = out b64 := base64.NewEncoder(base64.StdEncoding, &breaker) - _, err = b64.Write(b.Bytes) - if err != nil { - return + if _, err := b64.Write(b.Bytes); err != nil { + return err } b64.Close() breaker.Close() - _, err = out.Write(pemEnd[1:]) - if err != nil { - return + if _, err := out.Write(pemEnd[1:]); err != nil { + return err } - _, err = out.Write([]byte(b.Type + "-----\n")) - return + _, err := out.Write([]byte(b.Type + "-----\n")) + return err } func EncodeToMemory(b *Block) []byte { diff --git a/src/pkg/encoding/pem/pem_test.go b/src/pkg/encoding/pem/pem_test.go index 613353483..ccce42cf1 100644 --- a/src/pkg/encoding/pem/pem_test.go +++ b/src/pkg/encoding/pem/pem_test.go @@ -43,7 +43,7 @@ func TestDecode(t *testing.T) { if !reflect.DeepEqual(result, privateKey) { t.Errorf("#1 got:%#v want:%#v", result, privateKey) } - result, _ = Decode([]byte(pemPrivateKey)) + result, _ = Decode([]byte(pemPrivateKey2)) if !reflect.DeepEqual(result, privateKey2) { t.Errorf("#2 got:%#v want:%#v", result, privateKey2) } @@ -51,8 +51,8 @@ func TestDecode(t *testing.T) { func TestEncode(t *testing.T) { r := EncodeToMemory(privateKey2) - if string(r) != pemPrivateKey { - t.Errorf("got:%s want:%s", r, pemPrivateKey) + if string(r) != pemPrivateKey2 { + t.Errorf("got:%s want:%s", r, pemPrivateKey2) } } @@ -341,50 +341,64 @@ var privateKey = &Block{Type: "RSA PRIVATE KEY", }, } -var privateKey2 = &Block{Type: "RSA PRIVATE KEY", - Headers: map[string]string{}, - Bytes: []uint8{0x30, 0x82, 0x1, 0x3a, 0x2, 0x1, 0x0, 0x2, - 0x41, 0x0, 0xb2, 0x99, 0xf, 0x49, 0xc4, 0x7d, 0xfa, 0x8c, - 0xd4, 0x0, 0xae, 0x6a, 0x4d, 0x1b, 0x8a, 0x3b, 0x6a, 0x13, - 0x64, 0x2b, 0x23, 0xf2, 0x8b, 0x0, 0x3b, 0xfb, 0x97, 0x79, - 0xa, 0xde, 0x9a, 0x4c, 0xc8, 0x2b, 0x8b, 0x2a, 0x81, 0x74, - 0x7d, 0xde, 0xc0, 0x8b, 0x62, 0x96, 0xe5, 0x3a, 0x8, 0xc3, - 0x31, 0x68, 0x7e, 0xf2, 0x5c, 0x4b, 0xf4, 0x93, 0x6b, 0xa1, - 0xc0, 0xe6, 0x4, 0x1e, 0x9d, 0x15, 0x2, 0x3, 0x1, 0x0, 0x1, - 0x2, 0x41, 0x0, 0x8a, 0xbd, 0x6a, 0x69, 0xf4, 0xd1, 0xa4, - 0xb4, 0x87, 0xf0, 0xab, 0x8d, 0x7a, 0xae, 0xfd, 0x38, 0x60, - 0x94, 0x5, 0xc9, 0x99, 0x98, 0x4e, 0x30, 0xf5, 0x67, 0xe1, - 0xe8, 0xae, 0xef, 0xf4, 0x4e, 0x8b, 0x18, 0xbd, 0xb1, 0xec, - 0x78, 0xdf, 0xa3, 0x1a, 0x55, 0xe3, 0x2a, 0x48, 0xd7, 0xfb, - 0x13, 0x1f, 0x5a, 0xf1, 0xf4, 0x4d, 0x7d, 0x6b, 0x2c, 0xed, - 0x2a, 0x9d, 0xf5, 0xe5, 0xae, 0x45, 0x35, 0x2, 0x21, 0x0, - 0xda, 0xb2, 0xf1, 0x80, 0x48, 0xba, 0xa6, 0x8d, 0xe7, 0xdf, - 0x4, 0xd2, 0xd3, 0x5d, 0x5d, 0x80, 0xe6, 0xe, 0x2d, 0xfa, - 0x42, 0xd5, 0xa, 0x9b, 0x4, 0x21, 0x90, 0x32, 0x71, 0x5e, - 0x46, 0xb3, 0x2, 0x21, 0x0, 0xd1, 0xf, 0x2e, 0x66, 0xb1, - 0xd0, 0xc1, 0x3f, 0x10, 0xef, 0x99, 0x27, 0xbf, 0x53, 0x24, - 0xa3, 0x79, 0xca, 0x21, 0x81, 0x46, 0xcb, 0xf9, 0xca, 0xfc, - 0x79, 0x52, 0x21, 0xf1, 0x6a, 0x31, 0x17, 0x2, 0x20, 0x21, - 0x2, 0x89, 0x79, 0x37, 0x81, 0x14, 0xca, 0xae, 0x88, 0xf7, - 0xd, 0x6b, 0x61, 0xd8, 0x4f, 0x30, 0x6a, 0x4b, 0x7e, 0x4e, - 0xc0, 0x21, 0x4d, 0xac, 0x9d, 0xf4, 0x49, 0xe8, 0xda, 0xb6, - 0x9, 0x2, 0x20, 0x16, 0xb3, 0xec, 0x59, 0x10, 0xa4, 0x57, - 0xe8, 0xe, 0x61, 0xc6, 0xa3, 0xf, 0x5e, 0xeb, 0x12, 0xa9, - 0xae, 0x2e, 0xb7, 0x48, 0x45, 0xec, 0x69, 0x83, 0xc3, 0x75, - 0xc, 0xe4, 0x97, 0xa0, 0x9f, 0x2, 0x20, 0x69, 0x52, 0xb4, - 0x6, 0xe8, 0x50, 0x60, 0x71, 0x4c, 0x3a, 0xb7, 0x66, 0xba, - 0xd, 0x8a, 0xc9, 0xb7, 0xd, 0xa3, 0x8, 0x6c, 0xa3, 0xf2, - 0x62, 0xb0, 0x2a, 0x84, 0xaa, 0x2f, 0xd6, 0x1e, 0x55, +var privateKey2 = &Block{ + Type: "RSA PRIVATE KEY", + Headers: map[string]string{ + "Proc-Type": "4,ENCRYPTED", + "DEK-Info": "AES-128-CBC,BFCD243FEDBB40A4AA6DDAA1335473A4", + "Content-Domain": "RFC822", + }, + Bytes: []uint8{ + 0xa8, 0x35, 0xcc, 0x2b, 0xb9, 0xcb, 0x21, 0xab, 0xc0, + 0x9d, 0x76, 0x61, 0x0, 0xf4, 0x81, 0xad, 0x69, 0xd2, + 0xc0, 0x42, 0x41, 0x3b, 0xe4, 0x3c, 0xaf, 0x59, 0x5e, + 0x6d, 0x2a, 0x3c, 0x9c, 0xa1, 0xa4, 0x5e, 0x68, 0x37, + 0xc4, 0x8c, 0x70, 0x1c, 0xa9, 0x18, 0xe6, 0xc2, 0x2b, + 0x8a, 0x91, 0xdc, 0x2d, 0x1f, 0x8, 0x23, 0x39, 0xf1, + 0x4b, 0x8b, 0x1b, 0x2f, 0x46, 0xb, 0xb2, 0x26, 0xba, + 0x4f, 0x40, 0x80, 0x39, 0xc4, 0xb1, 0xcb, 0x3b, 0xb4, + 0x65, 0x3f, 0x1b, 0xb2, 0xf7, 0x8, 0xd2, 0xc6, 0xd5, + 0xa8, 0x9f, 0x23, 0x69, 0xb6, 0x3d, 0xf9, 0xac, 0x1c, + 0xb3, 0x13, 0x87, 0x64, 0x4, 0x37, 0xdb, 0x40, 0xc8, + 0x82, 0xc, 0xd0, 0xf8, 0x21, 0x7c, 0xdc, 0xbd, 0x9, 0x4, + 0x20, 0x16, 0xb0, 0x97, 0xe2, 0x6d, 0x56, 0x1d, 0xe3, + 0xec, 0xf0, 0xfc, 0xe2, 0x56, 0xad, 0xa4, 0x3, 0x70, + 0x6d, 0x63, 0x3c, 0x1, 0xbe, 0x3e, 0x28, 0x38, 0x6f, + 0xc0, 0xe6, 0xfd, 0x85, 0xd1, 0x53, 0xa8, 0x9b, 0xcb, + 0xd4, 0x4, 0xb1, 0x73, 0xb9, 0x73, 0x32, 0xd6, 0x7a, + 0xc6, 0x29, 0x25, 0xa5, 0xda, 0x17, 0x93, 0x7a, 0x10, + 0xe8, 0x41, 0xfb, 0xa5, 0x17, 0x20, 0xf8, 0x4e, 0xe9, + 0xe3, 0x8f, 0x51, 0x20, 0x13, 0xbb, 0xde, 0xb7, 0x93, + 0xae, 0x13, 0x8a, 0xf6, 0x9, 0xf4, 0xa6, 0x41, 0xe0, + 0x2b, 0x51, 0x1a, 0x30, 0x38, 0xd, 0xb1, 0x3b, 0x67, + 0x87, 0x64, 0xf5, 0xca, 0x32, 0x67, 0xd1, 0xc8, 0xa5, + 0x3d, 0x23, 0x72, 0xc4, 0x6, 0xaf, 0x8f, 0x7b, 0x26, + 0xac, 0x3c, 0x75, 0x91, 0xa1, 0x0, 0x13, 0xc6, 0x5c, + 0x49, 0xd5, 0x3c, 0xe7, 0xb2, 0xb2, 0x99, 0xe0, 0xd5, + 0x25, 0xfa, 0xe2, 0x12, 0x80, 0x37, 0x85, 0xcf, 0x92, + 0xca, 0x1b, 0x9f, 0xf3, 0x4e, 0xd8, 0x80, 0xef, 0x3c, + 0xce, 0xcd, 0xf5, 0x90, 0x9e, 0xf9, 0xa7, 0xb2, 0xc, + 0x49, 0x4, 0xf1, 0x9, 0x8f, 0xea, 0x63, 0xd2, 0x70, + 0xbb, 0x86, 0xbf, 0x34, 0xab, 0xb2, 0x3, 0xb1, 0x59, + 0x33, 0x16, 0x17, 0xb0, 0xdb, 0x77, 0x38, 0xf4, 0xb4, + 0x94, 0xb, 0x25, 0x16, 0x7e, 0x22, 0xd4, 0xf9, 0x22, + 0xb9, 0x78, 0xa3, 0x4, 0x84, 0x4, 0xd2, 0xda, 0x84, + 0x2d, 0x63, 0xdd, 0xf8, 0x50, 0x6a, 0xf6, 0xe3, 0xf5, + 0x65, 0x40, 0x7c, 0xa9, }, } -var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY----- -MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 -fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu -/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu -RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/ -EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A -IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS -tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V +var pemPrivateKey2 = `-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +Content-Domain: RFC822 +DEK-Info: AES-128-CBC,BFCD243FEDBB40A4AA6DDAA1335473A4 + +qDXMK7nLIavAnXZhAPSBrWnSwEJBO+Q8r1lebSo8nKGkXmg3xIxwHKkY5sIripHc +LR8IIznxS4sbL0YLsia6T0CAOcSxyzu0ZT8bsvcI0sbVqJ8jabY9+awcsxOHZAQ3 +20DIggzQ+CF83L0JBCAWsJfibVYd4+zw/OJWraQDcG1jPAG+Pig4b8Dm/YXRU6ib +y9QEsXO5czLWesYpJaXaF5N6EOhB+6UXIPhO6eOPUSATu963k64TivYJ9KZB4CtR +GjA4DbE7Z4dk9coyZ9HIpT0jcsQGr497Jqw8dZGhABPGXEnVPOeyspng1SX64hKA +N4XPksobn/NO2IDvPM7N9ZCe+aeyDEkE8QmP6mPScLuGvzSrsgOxWTMWF7Dbdzj0 +tJQLJRZ+ItT5Irl4owSEBNLahC1j3fhQavbj9WVAfKk= -----END RSA PRIVATE KEY----- ` diff --git a/src/pkg/encoding/xml/example_test.go b/src/pkg/encoding/xml/example_test.go index 97c8c0b0d..becedd583 100644 --- a/src/pkg/encoding/xml/example_test.go +++ b/src/pkg/encoding/xml/example_test.go @@ -50,6 +50,46 @@ func ExampleMarshalIndent() { // } +func ExampleEncoder() { + type Address struct { + City, State string + } + type Person struct { + XMLName xml.Name `xml:"person"` + Id int `xml:"id,attr"` + FirstName string `xml:"name>first"` + LastName string `xml:"name>last"` + Age int `xml:"age"` + Height float32 `xml:"height,omitempty"` + Married bool + Address + Comment string `xml:",comment"` + } + + v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42} + v.Comment = " Need more details. " + v.Address = Address{"Hanga Roa", "Easter Island"} + + enc := xml.NewEncoder(os.Stdout) + enc.Indent(" ", " ") + if err := enc.Encode(v); err != nil { + fmt.Printf("error: %v\n", err) + } + + // Output: + // + // + // John + // Doe + // + // 42 + // false + // Hanga Roa + // Easter Island + // + // +} + // This example demonstrates unmarshaling an XML excerpt into a value with // some preset fields. Note that the Phone field isn't modified and that // the XML element is ignored. Also, the Groups field is assigned diff --git a/src/pkg/encoding/xml/marshal.go b/src/pkg/encoding/xml/marshal.go index 6c3170bdd..ea58ce254 100644 --- a/src/pkg/encoding/xml/marshal.go +++ b/src/pkg/encoding/xml/marshal.go @@ -45,7 +45,7 @@ const ( // - a field with tag "name,attr" becomes an attribute with // the given name in the XML element. // - a field with tag ",attr" becomes an attribute with the -// field name in the in the XML element. +// field name in the XML element. // - a field with tag ",chardata" is written as character data, // not as an XML element. // - a field with tag ",innerxml" is written verbatim, not subject @@ -57,8 +57,8 @@ const ( // if the field value is empty. The empty values are false, 0, any // nil pointer or interface value, and any array, slice, map, or // string of length zero. -// - a non-pointer anonymous struct field is handled as if the -// fields of its value were part of the outer struct. +// - an anonymous struct field is handled as if the fields of its +// value were part of the outer struct. // // If a field uses a tag "a>b>c", then the element c will be nested inside // parent elements a and b. Fields that appear next to each other that name @@ -81,11 +81,8 @@ func Marshal(v interface{}) ([]byte, error) { func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { var b bytes.Buffer enc := NewEncoder(&b) - enc.prefix = prefix - enc.indent = indent - err := enc.marshalValue(reflect.ValueOf(v), nil) - enc.Flush() - if err != nil { + enc.Indent(prefix, indent) + if err := enc.Encode(v); err != nil { return nil, err } return b.Bytes(), nil @@ -101,14 +98,24 @@ func NewEncoder(w io.Writer) *Encoder { return &Encoder{printer{Writer: bufio.NewWriter(w)}} } +// Indent sets the encoder to generate XML in which each element +// begins on a new indented line that starts with prefix and is followed by +// one or more copies of indent according to the nesting depth. +func (enc *Encoder) Indent(prefix, indent string) { + enc.prefix = prefix + enc.indent = indent +} + // Encode writes the XML encoding of v to the stream. // // See the documentation for Marshal for details about the conversion // of Go values to XML. func (enc *Encoder) Encode(v interface{}) error { err := enc.marshalValue(reflect.ValueOf(v), nil) - enc.Flush() - return err + if err != nil { + return err + } + return enc.Flush() } type printer struct { @@ -117,6 +124,7 @@ type printer struct { prefix string depth int indentedIn bool + putNewline bool } // marshalValue writes one or more XML elements representing val. @@ -164,7 +172,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { xmlname := tinfo.xmlname if xmlname.name != "" { xmlns, name = xmlname.xmlns, xmlname.name - } else if v, ok := val.FieldByIndex(xmlname.idx).Interface().(Name); ok && v.Local != "" { + } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" { xmlns, name = v.Space, v.Local } } @@ -185,7 +193,9 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { if xmlns != "" { p.WriteString(` xmlns="`) // TODO: EscapeString, to avoid the allocation. - Escape(p, []byte(xmlns)) + if err := EscapeText(p, []byte(xmlns)); err != nil { + return err + } p.WriteByte('"') } @@ -195,7 +205,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { if finfo.flags&fAttr == 0 { continue } - fv := val.FieldByIndex(finfo.idx) + fv := finfo.value(val) if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) { continue } @@ -224,7 +234,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { p.WriteString(name) p.WriteByte('>') - return nil + return p.cachedWriteError() } var timeType = reflect.TypeOf(time.Time{}) @@ -241,50 +251,70 @@ func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p.WriteString(strconv.FormatUint(val.Uint(), 10)) case reflect.Float32, reflect.Float64: - p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, 64)) + p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())) case reflect.String: // TODO: Add EscapeString. - Escape(p, []byte(val.String())) + EscapeText(p, []byte(val.String())) case reflect.Bool: p.WriteString(strconv.FormatBool(val.Bool())) case reflect.Array: // will be [...]byte - bytes := make([]byte, val.Len()) - for i := range bytes { - bytes[i] = val.Index(i).Interface().(byte) + var bytes []byte + if val.CanAddr() { + bytes = val.Slice(0, val.Len()).Bytes() + } else { + bytes = make([]byte, val.Len()) + reflect.Copy(reflect.ValueOf(bytes), val) } - Escape(p, bytes) + EscapeText(p, bytes) case reflect.Slice: // will be []byte - Escape(p, val.Bytes()) + EscapeText(p, val.Bytes()) default: return &UnsupportedTypeError{typ} } - return nil + return p.cachedWriteError() } var ddBytes = []byte("--") func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { if val.Type() == timeType { - p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano)) - return nil + _, err := p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano)) + return err } s := parentStack{printer: p} for i := range tinfo.fields { finfo := &tinfo.fields[i] - if finfo.flags&(fAttr|fAny) != 0 { + if finfo.flags&(fAttr) != 0 { continue } - vf := val.FieldByIndex(finfo.idx) + vf := finfo.value(val) switch finfo.flags & fMode { case fCharData: + var scratch [64]byte switch vf.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10)) + case reflect.Float32, reflect.Float64: + Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits())) + case reflect.Bool: + Escape(p, strconv.AppendBool(scratch[:0], vf.Bool())) case reflect.String: - Escape(p, []byte(vf.String())) + if err := EscapeText(p, []byte(vf.String())); err != nil { + return err + } case reflect.Slice: if elem, ok := vf.Interface().([]byte); ok { - Escape(p, elem) + if err := EscapeText(p, elem); err != nil { + return err + } + } + case reflect.Struct: + if vf.Type() == timeType { + Escape(p, []byte(vf.Interface().(time.Time).Format(time.RFC3339Nano))) } } continue @@ -340,7 +370,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { continue } - case fElement: + case fElement, fElement | fAny: s.trim(finfo.parents) if len(finfo.parents) > len(s.stack) { if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() { @@ -353,7 +383,13 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { } } s.trim(nil) - return nil + return p.cachedWriteError() +} + +// return the bufio Writer's cached write error +func (p *printer) cachedWriteError() error { + _, err := p.Write(nil) + return err } func (p *printer) writeIndent(depthDelta int) { @@ -368,7 +404,11 @@ func (p *printer) writeIndent(depthDelta int) { } p.indentedIn = false } - p.WriteByte('\n') + if p.putNewline { + p.WriteByte('\n') + } else { + p.putNewline = true + } if len(p.prefix) > 0 { p.WriteString(p.prefix) } diff --git a/src/pkg/encoding/xml/marshal_test.go b/src/pkg/encoding/xml/marshal_test.go index b6978a1e6..3a190def6 100644 --- a/src/pkg/encoding/xml/marshal_test.go +++ b/src/pkg/encoding/xml/marshal_test.go @@ -5,6 +5,10 @@ package xml import ( + "bytes" + "errors" + "fmt" + "io" "reflect" "strconv" "strings" @@ -56,6 +60,36 @@ type Book struct { Title string `xml:",chardata"` } +type Event struct { + XMLName struct{} `xml:"event"` + Year int `xml:",chardata"` +} + +type Movie struct { + XMLName struct{} `xml:"movie"` + Length uint `xml:",chardata"` +} + +type Pi struct { + XMLName struct{} `xml:"pi"` + Approximation float32 `xml:",chardata"` +} + +type Universe struct { + XMLName struct{} `xml:"universe"` + Visible float64 `xml:",chardata"` +} + +type Particle struct { + XMLName struct{} `xml:"particle"` + HasMass bool `xml:",chardata"` +} + +type Departure struct { + XMLName struct{} `xml:"departure"` + When time.Time `xml:",chardata"` +} + type SecretAgent struct { XMLName struct{} `xml:"agent"` Handle string `xml:"handle,attr"` @@ -108,7 +142,7 @@ type EmbedA struct { type EmbedB struct { FieldB string - EmbedC + *EmbedC } type EmbedC struct { @@ -185,6 +219,18 @@ type AnyTest struct { AnyField AnyHolder `xml:",any"` } +type AnyOmitTest struct { + XMLName struct{} `xml:"a"` + Nested string `xml:"nested>value"` + AnyField *AnyHolder `xml:",any,omitempty"` +} + +type AnySliceTest struct { + XMLName struct{} `xml:"a"` + Nested string `xml:"nested>value"` + AnyField []AnyHolder `xml:",any"` +} + type AnyHolder struct { XMLName Name XML string `xml:",innerxml"` @@ -330,6 +376,12 @@ var marshalTests = []struct { {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `google.com&friends`}, {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `google.com`}, {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `Pride & Prejudice`}, + {Value: &Event{Year: -3114}, ExpectXML: `-3114`}, + {Value: &Movie{Length: 13440}, ExpectXML: `13440`}, + {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `3.1415927`}, + {Value: &Universe{Visible: 9.3e13}, ExpectXML: `9.3e+13`}, + {Value: &Particle{HasMass: true}, ExpectXML: `true`}, + {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `2013-01-09T00:15:00-09:00`}, {Value: atomValue, ExpectXML: atomXml}, { Value: &Ship{ @@ -493,7 +545,7 @@ var marshalTests = []struct { }, EmbedB: EmbedB{ FieldB: "A.B.B", - EmbedC: EmbedC{ + EmbedC: &EmbedC{ FieldA1: "A.B.C.A1", FieldA2: "A.B.C.A2", FieldB: "", // Shadowed by A.B.B @@ -649,12 +701,43 @@ var marshalTests = []struct { XML: "unknown", }, }, - UnmarshalOnly: true, }, { - Value: &AnyTest{Nested: "known", AnyField: AnyHolder{XML: ""}}, - ExpectXML: `known`, - MarshalOnly: true, + Value: &AnyTest{Nested: "known", + AnyField: AnyHolder{ + XML: "", + XMLName: Name{Local: "AnyField"}, + }, + }, + ExpectXML: `known`, + }, + { + ExpectXML: `b`, + Value: &AnyOmitTest{ + Nested: "b", + }, + }, + { + ExpectXML: `bei`, + Value: &AnySliceTest{ + Nested: "b", + AnyField: []AnyHolder{ + { + XMLName: Name{Local: "c"}, + XML: "e", + }, + { + XMLName: Name{Space: "f", Local: "g"}, + XML: "i", + }, + }, + }, + }, + { + ExpectXML: `b`, + Value: &AnySliceTest{ + Nested: "b", + }, }, // Test recursive types. @@ -684,6 +767,29 @@ var marshalTests = []struct { Value: &IgnoreTest{}, UnmarshalOnly: true, }, + + // Test escaping. + { + ExpectXML: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`, + Value: &AnyTest{ + Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`, + AnyField: AnyHolder{XMLName: Name{Local: "empty"}}, + }, + }, + { + ExpectXML: `newline: ; cr: ; tab: ;`, + Value: &AnyTest{ + Nested: "newline: \n; cr: \r; tab: \t;", + AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}}, + }, + }, + { + ExpectXML: "1\r2\r\n3\n\r4\n5", + Value: &AnyTest{ + Nested: "1\n2\n3\n\n4\n5", + }, + UnmarshalOnly: true, + }, } func TestMarshal(t *testing.T) { @@ -735,6 +841,24 @@ var marshalErrorTests = []struct { }, } +var marshalIndentTests = []struct { + Value interface{} + Prefix string + Indent string + ExpectXML string +}{ + { + Value: &SecretAgent{ + Handle: "007", + Identity: "James Bond", + Obfuscate: "", + }, + Prefix: "", + Indent: "\t", + ExpectXML: fmt.Sprintf("\n\tJames Bond\n"), + }, +} + func TestMarshalErrors(t *testing.T) { for idx, test := range marshalErrorTests { _, err := Marshal(test.Value) @@ -779,6 +903,78 @@ func TestUnmarshal(t *testing.T) { } } +func TestMarshalIndent(t *testing.T) { + for i, test := range marshalIndentTests { + data, err := MarshalIndent(test.Value, test.Prefix, test.Indent) + if err != nil { + t.Errorf("#%d: Error: %s", i, err) + continue + } + if got, want := string(data), test.ExpectXML; got != want { + t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want) + } + } +} + +type limitedBytesWriter struct { + w io.Writer + remain int // until writes fail +} + +func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) { + if lw.remain <= 0 { + println("error") + return 0, errors.New("write limit hit") + } + if len(p) > lw.remain { + p = p[:lw.remain] + n, _ = lw.w.Write(p) + lw.remain = 0 + return n, errors.New("write limit hit") + } + n, err = lw.w.Write(p) + lw.remain -= n + return n, err +} + +func TestMarshalWriteErrors(t *testing.T) { + var buf bytes.Buffer + const writeCap = 1024 + w := &limitedBytesWriter{&buf, writeCap} + enc := NewEncoder(w) + var err error + var i int + const n = 4000 + for i = 1; i <= n; i++ { + err = enc.Encode(&Passenger{ + Name: []string{"Alice", "Bob"}, + Weight: 5, + }) + if err != nil { + break + } + } + if err == nil { + t.Error("expected an error") + } + if i == n { + t.Errorf("expected to fail before the end") + } + if buf.Len() != writeCap { + t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap) + } +} + +func TestMarshalWriteIOErrors(t *testing.T) { + enc := NewEncoder(errWriter{}) + + expectErr := "unwritable" + err := enc.Encode(&Passenger{}) + if err == nil || err.Error() != expectErr { + t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr) + } +} + func BenchmarkMarshal(b *testing.B) { for i := 0; i < b.N; i++ { Marshal(atomValue) diff --git a/src/pkg/encoding/xml/read.go b/src/pkg/encoding/xml/read.go index c21682420..344ab514e 100644 --- a/src/pkg/encoding/xml/read.go +++ b/src/pkg/encoding/xml/read.go @@ -81,8 +81,8 @@ import ( // of the above rules and the struct has a field with tag ",any", // unmarshal maps the sub-element to that struct field. // -// * A non-pointer anonymous struct field is handled as if the -// fields of its value were part of the outer struct. +// * An anonymous struct field is handled as if the fields of its +// value were part of the outer struct. // // * A struct field with tag "-" is never unmarshalled into. // @@ -248,7 +248,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { } return UnmarshalError(e) } - fv := sv.FieldByIndex(finfo.idx) + fv := finfo.value(sv) if _, ok := fv.Interface().(Name); ok { fv.Set(reflect.ValueOf(start.Name)) } @@ -260,7 +260,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { finfo := &tinfo.fields[i] switch finfo.flags & fMode { case fAttr: - strv := sv.FieldByIndex(finfo.idx) + strv := finfo.value(sv) // Look for attribute. for _, a := range start.Attr { if a.Name.Local == finfo.name { @@ -271,22 +271,22 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { case fCharData: if !saveData.IsValid() { - saveData = sv.FieldByIndex(finfo.idx) + saveData = finfo.value(sv) } case fComment: if !saveComment.IsValid() { - saveComment = sv.FieldByIndex(finfo.idx) + saveComment = finfo.value(sv) } - case fAny: + case fAny, fAny | fElement: if !saveAny.IsValid() { - saveAny = sv.FieldByIndex(finfo.idx) + saveAny = finfo.value(sv) } case fInnerXml: if !saveXML.IsValid() { - saveXML = sv.FieldByIndex(finfo.idx) + saveXML = finfo.value(sv) if p.saved == nil { saveXMLIndex = 0 p.saved = new(bytes.Buffer) @@ -374,68 +374,58 @@ Loop: } func copyValue(dst reflect.Value, src []byte) (err error) { - // Helper functions for integer and unsigned integer conversions - var itmp int64 - getInt64 := func() bool { - itmp, err = strconv.ParseInt(string(src), 10, 64) - // TODO: should check sizes - return err == nil - } - var utmp uint64 - getUint64 := func() bool { - utmp, err = strconv.ParseUint(string(src), 10, 64) - // TODO: check for overflow? - return err == nil - } - var ftmp float64 - getFloat64 := func() bool { - ftmp, err = strconv.ParseFloat(string(src), 64) - // TODO: check for overflow? - return err == nil + if dst.Kind() == reflect.Ptr { + if dst.IsNil() { + dst.Set(reflect.New(dst.Type().Elem())) + } + dst = dst.Elem() } // Save accumulated data. - switch t := dst; t.Kind() { + switch dst.Kind() { case reflect.Invalid: - // Probably a comment. + // Probably a commendst. default: - return errors.New("cannot happen: unknown type " + t.Type().String()) + return errors.New("cannot happen: unknown type " + dst.Type().String()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if !getInt64() { + itmp, err := strconv.ParseInt(string(src), 10, dst.Type().Bits()) + if err != nil { return err } - t.SetInt(itmp) + dst.SetInt(itmp) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - if !getUint64() { + utmp, err := strconv.ParseUint(string(src), 10, dst.Type().Bits()) + if err != nil { return err } - t.SetUint(utmp) + dst.SetUint(utmp) case reflect.Float32, reflect.Float64: - if !getFloat64() { + ftmp, err := strconv.ParseFloat(string(src), dst.Type().Bits()) + if err != nil { return err } - t.SetFloat(ftmp) + dst.SetFloat(ftmp) case reflect.Bool: value, err := strconv.ParseBool(strings.TrimSpace(string(src))) if err != nil { return err } - t.SetBool(value) + dst.SetBool(value) case reflect.String: - t.SetString(string(src)) + dst.SetString(string(src)) case reflect.Slice: if len(src) == 0 { // non-nil to flag presence src = []byte{} } - t.SetBytes(src) + dst.SetBytes(src) case reflect.Struct: - if t.Type() == timeType { + if dst.Type() == timeType { tv, err := time.Parse(time.RFC3339, string(src)) if err != nil { return err } - t.Set(reflect.ValueOf(tv)) + dst.Set(reflect.ValueOf(tv)) } } return nil @@ -461,7 +451,7 @@ Loop: } if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local { // It's a perfect match, unmarshal the field. - return true, p.unmarshal(sv.FieldByIndex(finfo.idx), start) + return true, p.unmarshal(finfo.value(sv), start) } if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local { // It's a prefix for the field. Break and recurse diff --git a/src/pkg/encoding/xml/read_test.go b/src/pkg/encoding/xml/read_test.go index 8df09b3cc..b45e2f0e6 100644 --- a/src/pkg/encoding/xml/read_test.go +++ b/src/pkg/encoding/xml/read_test.go @@ -355,3 +355,47 @@ func TestUnmarshalWithoutNameType(t *testing.T) { t.Fatalf("have %v\nwant %v", x.Attr, OK) } } + +func TestUnmarshalAttr(t *testing.T) { + type ParamVal struct { + Int int `xml:"int,attr"` + } + + type ParamPtr struct { + Int *int `xml:"int,attr"` + } + + type ParamStringPtr struct { + Int *string `xml:"int,attr"` + } + + x := []byte(``) + + p1 := &ParamPtr{} + if err := Unmarshal(x, p1); err != nil { + t.Fatalf("Unmarshal: %s", err) + } + if p1.Int == nil { + t.Fatalf("Unmarshal failed in to *int field") + } else if *p1.Int != 1 { + t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1) + } + + p2 := &ParamVal{} + if err := Unmarshal(x, p2); err != nil { + t.Fatalf("Unmarshal: %s", err) + } + if p2.Int != 1 { + t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1) + } + + p3 := &ParamStringPtr{} + if err := Unmarshal(x, p3); err != nil { + t.Fatalf("Unmarshal: %s", err) + } + if p3.Int == nil { + t.Fatalf("Unmarshal failed in to *string field") + } else if *p3.Int != "1" { + t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1) + } +} diff --git a/src/pkg/encoding/xml/typeinfo.go b/src/pkg/encoding/xml/typeinfo.go index 8e2e4508b..bbeb28d87 100644 --- a/src/pkg/encoding/xml/typeinfo.go +++ b/src/pkg/encoding/xml/typeinfo.go @@ -66,10 +66,14 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) { // For embedded structs, embed its fields. if f.Anonymous { - if f.Type.Kind() != reflect.Struct { + t := f.Type + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() != reflect.Struct { continue } - inner, err := getTypeInfo(f.Type) + inner, err := getTypeInfo(t) if err != nil { return nil, err } @@ -150,6 +154,9 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro // This will also catch multiple modes in a single field. valid = false } + if finfo.flags&fMode == fAny { + finfo.flags |= fElement + } if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 { valid = false } @@ -327,3 +334,22 @@ type TagPathError struct { func (e *TagPathError) Error() string { return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2) } + +// value returns v's field value corresponding to finfo. +// It's equivalent to v.FieldByIndex(finfo.idx), but initializes +// and dereferences pointers as necessary. +func (finfo *fieldInfo) value(v reflect.Value) reflect.Value { + for i, x := range finfo.idx { + if i > 0 { + t := v.Type() + if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + } + v = v.Field(x) + } + return v +} diff --git a/src/pkg/encoding/xml/xml.go b/src/pkg/encoding/xml/xml.go index 5066f5c01..143fec554 100644 --- a/src/pkg/encoding/xml/xml.go +++ b/src/pkg/encoding/xml/xml.go @@ -181,7 +181,6 @@ type Decoder struct { ns map[string]string err error line int - tmp [32]byte } // NewDecoder creates a new XML parser reading from r. @@ -584,6 +583,7 @@ func (d *Decoder) RawToken() (Token, error) { if inquote == 0 && b == '>' && depth == 0 { break } + HandleB: d.buf.WriteByte(b) switch { case b == inquote: @@ -599,7 +599,35 @@ func (d *Decoder) RawToken() (Token, error) { depth-- case b == '<' && inquote == 0: - depth++ + // Look for ` +var testEntity = map[string]string{"何": "What", "is-it": "is it?"} + var rawTokens = []Token{ CharData("\n"), ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, @@ -40,6 +44,10 @@ var rawTokens = []Token{ CharData("World <>'\" 白鵬翔"), EndElement{Name{"", "hello"}}, CharData("\n "), + StartElement{Name{"", "query"}, []Attr{}}, + CharData("What is it?"), + EndElement{Name{"", "query"}}, + CharData("\n "), StartElement{Name{"", "goodbye"}, []Attr{}}, EndElement{Name{"", "goodbye"}}, CharData("\n "), @@ -73,6 +81,10 @@ var cookedTokens = []Token{ CharData("World <>'\" 白鵬翔"), EndElement{Name{"ns2", "hello"}}, CharData("\n "), + StartElement{Name{"ns2", "query"}, []Attr{}}, + CharData("What is it?"), + EndElement{Name{"ns2", "query"}}, + CharData("\n "), StartElement{Name{"ns2", "goodbye"}, []Attr{}}, EndElement{Name{"ns2", "goodbye"}}, CharData("\n "), @@ -155,9 +167,65 @@ var xmlInput = []string{ func TestRawToken(t *testing.T) { d := NewDecoder(strings.NewReader(testInput)) + d.Entity = testEntity testRawToken(t, d, rawTokens) } +const nonStrictInput = ` +non&entity +&unknown;entity +{ +&#zzz; +&なまえ3; +<-gt; +&; +&0a; +` + +var nonStringEntity = map[string]string{"": "oops!", "0a": "oops!"} + +var nonStrictTokens = []Token{ + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("non&entity"), + EndElement{Name{"", "tag"}}, + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("&unknown;entity"), + EndElement{Name{"", "tag"}}, + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("{"), + EndElement{Name{"", "tag"}}, + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("&#zzz;"), + EndElement{Name{"", "tag"}}, + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("&なまえ3;"), + EndElement{Name{"", "tag"}}, + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("<-gt;"), + EndElement{Name{"", "tag"}}, + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("&;"), + EndElement{Name{"", "tag"}}, + CharData("\n"), + StartElement{Name{"", "tag"}, []Attr{}}, + CharData("&0a;"), + EndElement{Name{"", "tag"}}, + CharData("\n"), +} + +func TestNonStrictRawToken(t *testing.T) { + d := NewDecoder(strings.NewReader(nonStrictInput)) + d.Strict = false + testRawToken(t, d, nonStrictTokens) +} + type downCaser struct { t *testing.T r io.ByteReader @@ -219,7 +287,18 @@ func testRawToken(t *testing.T, d *Decoder, rawTokens []Token) { t.Fatalf("token %d: unexpected error: %s", i, err) } if !reflect.DeepEqual(have, want) { - t.Errorf("token %d = %#v want %#v", i, have, want) + var shave, swant string + if _, ok := have.(CharData); ok { + shave = fmt.Sprintf("CharData(%q)", have) + } else { + shave = fmt.Sprintf("%#v", have) + } + if _, ok := want.(CharData); ok { + swant = fmt.Sprintf("CharData(%q)", want) + } else { + swant = fmt.Sprintf("%#v", want) + } + t.Errorf("token %d = %s, want %s", i, shave, swant) } } } @@ -272,6 +351,7 @@ func TestNestedDirectives(t *testing.T) { func TestToken(t *testing.T) { d := NewDecoder(strings.NewReader(testInput)) + d.Entity = testEntity for i, want := range cookedTokens { have, err := d.Token() @@ -531,8 +611,8 @@ var characterTests = []struct { {"\xef\xbf\xbe", "illegal character code U+FFFE"}, {"\r\n\x07", "illegal character code U+0007"}, {"what's up", "expected attribute name in element"}, - {"&\x01;", "invalid character entity &;"}, - {"&\xef\xbf\xbe;", "invalid character entity &;"}, + {"&\x01;", "invalid character entity & (no semicolon)"}, + {"&\xef\xbf\xbe;", "invalid character entity & (no semicolon)"}, } func TestDisallowedCharacters(t *testing.T) { @@ -576,3 +656,50 @@ func TestProcInstEncoding(t *testing.T) { } } } + +// Ensure that directives with comments include the complete +// text of any nested directives. + +var directivesWithCommentsInput = ` +]> +]> + --> --> []> +` + +var directivesWithCommentsTokens = []Token{ + CharData("\n"), + Directive(`DOCTYPE []`), + CharData("\n"), + Directive(`DOCTYPE []`), + CharData("\n"), + Directive(`DOCTYPE []`), + CharData("\n"), +} + +func TestDirectivesWithComments(t *testing.T) { + d := NewDecoder(strings.NewReader(directivesWithCommentsInput)) + + for i, want := range directivesWithCommentsTokens { + have, err := d.Token() + if err != nil { + t.Fatalf("token %d: unexpected error: %s", i, err) + } + if !reflect.DeepEqual(have, want) { + t.Errorf("token %d = %#v want %#v", i, have, want) + } + } +} + +// Writer whose Write method always returns an error. +type errWriter struct{} + +func (errWriter) Write(p []byte) (n int, err error) { return 0, fmt.Errorf("unwritable") } + +func TestEscapeTextIOErrors(t *testing.T) { + expectErr := "unwritable" + err := EscapeText(errWriter{}, []byte{'A'}) + + if err == nil || err.Error() != expectErr { + t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr) + } +} diff --git a/src/pkg/exp/norm/Makefile b/src/pkg/exp/norm/Makefile new file mode 100644 index 000000000..f278eb02f --- /dev/null +++ b/src/pkg/exp/norm/Makefile @@ -0,0 +1,30 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +maketables: maketables.go triegen.go + go build $^ + +maketesttables: maketesttables.go triegen.go + go build $^ + +normregtest: normregtest.go + go build $^ + +tables: maketables + ./maketables > tables.go + gofmt -w tables.go + +trietesttables: maketesttables + ./maketesttables > triedata_test.go + gofmt -w triedata_test.go + +# Downloads from www.unicode.org, so not part +# of standard test scripts. +test: testtables regtest + +testtables: maketables + ./maketables -test -tables= + +regtest: normregtest + ./normregtest diff --git a/src/pkg/exp/norm/composition.go b/src/pkg/exp/norm/composition.go new file mode 100644 index 000000000..2d52f99dc --- /dev/null +++ b/src/pkg/exp/norm/composition.go @@ -0,0 +1,382 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import "unicode/utf8" + +const ( + maxCombiningChars = 30 + maxBufferSize = maxCombiningChars + 2 // +1 to hold starter +1 to hold CGJ + maxBackRunes = maxCombiningChars - 1 + maxNFCExpansion = 3 // NFC(0x1D160) + maxNFKCExpansion = 18 // NFKC(0xFDFA) + + maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128 +) + +// reorderBuffer is used to normalize a single segment. Characters inserted with +// insert are decomposed and reordered based on CCC. The compose method can +// be used to recombine characters. Note that the byte buffer does not hold +// the UTF-8 characters in order. Only the rune array is maintained in sorted +// order. flush writes the resulting segment to a byte array. +type reorderBuffer struct { + rune [maxBufferSize]Properties // Per character info. + byte [maxByteBufferSize]byte // UTF-8 buffer. Referenced by runeInfo.pos. + nrune int // Number of runeInfos. + nbyte uint8 // Number or bytes. + f formInfo + + src input + nsrc int + tmpBytes input +} + +func (rb *reorderBuffer) init(f Form, src []byte) { + rb.f = *formTable[f] + rb.src.setBytes(src) + rb.nsrc = len(src) +} + +func (rb *reorderBuffer) initString(f Form, src string) { + rb.f = *formTable[f] + rb.src.setString(src) + rb.nsrc = len(src) +} + +// reset discards all characters from the buffer. +func (rb *reorderBuffer) reset() { + rb.nrune = 0 + rb.nbyte = 0 +} + +// flush appends the normalized segment to out and resets rb. +func (rb *reorderBuffer) flush(out []byte) []byte { + for i := 0; i < rb.nrune; i++ { + start := rb.rune[i].pos + end := start + rb.rune[i].size + out = append(out, rb.byte[start:end]...) + } + rb.reset() + return out +} + +// flushCopy copies the normalized segment to buf and resets rb. +// It returns the number of bytes written to buf. +func (rb *reorderBuffer) flushCopy(buf []byte) int { + p := 0 + for i := 0; i < rb.nrune; i++ { + runep := rb.rune[i] + p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size]) + } + rb.reset() + return p +} + +// insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class. +// It returns false if the buffer is not large enough to hold the rune. +// It is used internally by insert and insertString only. +func (rb *reorderBuffer) insertOrdered(info Properties) bool { + n := rb.nrune + if n >= maxCombiningChars+1 { + return false + } + b := rb.rune[:] + cc := info.ccc + if cc > 0 { + // Find insertion position + move elements to make room. + for ; n > 0; n-- { + if b[n-1].ccc <= cc { + break + } + b[n] = b[n-1] + } + } + rb.nrune += 1 + pos := uint8(rb.nbyte) + rb.nbyte += utf8.UTFMax + info.pos = pos + b[n] = info + return true +} + +// insert inserts the given rune in the buffer ordered by CCC. +// It returns true if the buffer was large enough to hold the decomposed rune. +func (rb *reorderBuffer) insert(src input, i int, info Properties) bool { + if rune := src.hangul(i); rune != 0 { + return rb.decomposeHangul(rune) + } + if info.hasDecomposition() { + return rb.insertDecomposed(info.Decomposition()) + } + return rb.insertSingle(src, i, info) +} + +// insertDecomposed inserts an entry in to the reorderBuffer for each rune +// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes. +func (rb *reorderBuffer) insertDecomposed(dcomp []byte) bool { + saveNrune, saveNbyte := rb.nrune, rb.nbyte + rb.tmpBytes.setBytes(dcomp) + for i := 0; i < len(dcomp); { + info := rb.f.info(rb.tmpBytes, i) + pos := rb.nbyte + if !rb.insertOrdered(info) { + rb.nrune, rb.nbyte = saveNrune, saveNbyte + return false + } + i += copy(rb.byte[pos:], dcomp[i:i+int(info.size)]) + } + return true +} + +// insertSingle inserts an entry in the reorderBuffer for the rune at +// position i. info is the runeInfo for the rune at position i. +func (rb *reorderBuffer) insertSingle(src input, i int, info Properties) bool { + // insertOrder changes nbyte + pos := rb.nbyte + if !rb.insertOrdered(info) { + return false + } + src.copySlice(rb.byte[pos:], i, i+int(info.size)) + return true +} + +// appendRune inserts a rune at the end of the buffer. It is used for Hangul. +func (rb *reorderBuffer) appendRune(r rune) { + bn := rb.nbyte + sz := utf8.EncodeRune(rb.byte[bn:], rune(r)) + rb.nbyte += utf8.UTFMax + rb.rune[rb.nrune] = Properties{pos: bn, size: uint8(sz)} + rb.nrune++ +} + +// assignRune sets a rune at position pos. It is used for Hangul and recomposition. +func (rb *reorderBuffer) assignRune(pos int, r rune) { + bn := rb.rune[pos].pos + sz := utf8.EncodeRune(rb.byte[bn:], rune(r)) + rb.rune[pos] = Properties{pos: bn, size: uint8(sz)} +} + +// runeAt returns the rune at position n. It is used for Hangul and recomposition. +func (rb *reorderBuffer) runeAt(n int) rune { + inf := rb.rune[n] + r, _ := utf8.DecodeRune(rb.byte[inf.pos : inf.pos+inf.size]) + return r +} + +// bytesAt returns the UTF-8 encoding of the rune at position n. +// It is used for Hangul and recomposition. +func (rb *reorderBuffer) bytesAt(n int) []byte { + inf := rb.rune[n] + return rb.byte[inf.pos : int(inf.pos)+int(inf.size)] +} + +// For Hangul we combine algorithmically, instead of using tables. +const ( + hangulBase = 0xAC00 // UTF-8(hangulBase) -> EA B0 80 + hangulBase0 = 0xEA + hangulBase1 = 0xB0 + hangulBase2 = 0x80 + + hangulEnd = hangulBase + jamoLVTCount // UTF-8(0xD7A4) -> ED 9E A4 + hangulEnd0 = 0xED + hangulEnd1 = 0x9E + hangulEnd2 = 0xA4 + + jamoLBase = 0x1100 // UTF-8(jamoLBase) -> E1 84 00 + jamoLBase0 = 0xE1 + jamoLBase1 = 0x84 + jamoLEnd = 0x1113 + jamoVBase = 0x1161 + jamoVEnd = 0x1176 + jamoTBase = 0x11A7 + jamoTEnd = 0x11C3 + + jamoTCount = 28 + jamoVCount = 21 + jamoVTCount = 21 * 28 + jamoLVTCount = 19 * 21 * 28 +) + +const hangulUTF8Size = 3 + +func isHangul(b []byte) bool { + if len(b) < hangulUTF8Size { + return false + } + b0 := b[0] + if b0 < hangulBase0 { + return false + } + b1 := b[1] + switch { + case b0 == hangulBase0: + return b1 >= hangulBase1 + case b0 < hangulEnd0: + return true + case b0 > hangulEnd0: + return false + case b1 < hangulEnd1: + return true + } + return b1 == hangulEnd1 && b[2] < hangulEnd2 +} + +func isHangulString(b string) bool { + if len(b) < hangulUTF8Size { + return false + } + b0 := b[0] + if b0 < hangulBase0 { + return false + } + b1 := b[1] + switch { + case b0 == hangulBase0: + return b1 >= hangulBase1 + case b0 < hangulEnd0: + return true + case b0 > hangulEnd0: + return false + case b1 < hangulEnd1: + return true + } + return b1 == hangulEnd1 && b[2] < hangulEnd2 +} + +// Caller must ensure len(b) >= 2. +func isJamoVT(b []byte) bool { + // True if (rune & 0xff00) == jamoLBase + return b[0] == jamoLBase0 && (b[1]&0xFC) == jamoLBase1 +} + +func isHangulWithoutJamoT(b []byte) bool { + c, _ := utf8.DecodeRune(b) + c -= hangulBase + return c < jamoLVTCount && c%jamoTCount == 0 +} + +// decomposeHangul writes the decomposed Hangul to buf and returns the number +// of bytes written. len(buf) should be at least 9. +func decomposeHangul(buf []byte, r rune) int { + const JamoUTF8Len = 3 + r -= hangulBase + x := r % jamoTCount + r /= jamoTCount + utf8.EncodeRune(buf, jamoLBase+r/jamoVCount) + utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount) + if x != 0 { + utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x) + return 3 * JamoUTF8Len + } + return 2 * JamoUTF8Len +} + +// decomposeHangul algorithmically decomposes a Hangul rune into +// its Jamo components. +// See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul. +func (rb *reorderBuffer) decomposeHangul(r rune) bool { + b := rb.rune[:] + n := rb.nrune + if n+3 > len(b) { + return false + } + r -= hangulBase + x := r % jamoTCount + r /= jamoTCount + rb.appendRune(jamoLBase + r/jamoVCount) + rb.appendRune(jamoVBase + r%jamoVCount) + if x != 0 { + rb.appendRune(jamoTBase + x) + } + return true +} + +// combineHangul algorithmically combines Jamo character components into Hangul. +// See http://unicode.org/reports/tr15/#Hangul for details on combining Hangul. +func (rb *reorderBuffer) combineHangul(s, i, k int) { + b := rb.rune[:] + bn := rb.nrune + for ; i < bn; i++ { + cccB := b[k-1].ccc + cccC := b[i].ccc + if cccB == 0 { + s = k - 1 + } + if s != k-1 && cccB >= cccC { + // b[i] is blocked by greater-equal cccX below it + b[k] = b[i] + k++ + } else { + l := rb.runeAt(s) // also used to compare to hangulBase + v := rb.runeAt(i) // also used to compare to jamoT + switch { + case jamoLBase <= l && l < jamoLEnd && + jamoVBase <= v && v < jamoVEnd: + // 11xx plus 116x to LV + rb.assignRune(s, hangulBase+ + (l-jamoLBase)*jamoVTCount+(v-jamoVBase)*jamoTCount) + case hangulBase <= l && l < hangulEnd && + jamoTBase < v && v < jamoTEnd && + ((l-hangulBase)%jamoTCount) == 0: + // ACxx plus 11Ax to LVT + rb.assignRune(s, l+v-jamoTBase) + default: + b[k] = b[i] + k++ + } + } + } + rb.nrune = k +} + +// compose recombines the runes in the buffer. +// It should only be used to recompose a single segment, as it will not +// handle alternations between Hangul and non-Hangul characters correctly. +func (rb *reorderBuffer) compose() { + // UAX #15, section X5 , including Corrigendum #5 + // "In any character sequence beginning with starter S, a character C is + // blocked from S if and only if there is some character B between S + // and C, and either B is a starter or it has the same or higher + // combining class as C." + bn := rb.nrune + if bn == 0 { + return + } + k := 1 + b := rb.rune[:] + for s, i := 0, 1; i < bn; i++ { + if isJamoVT(rb.bytesAt(i)) { + // Redo from start in Hangul mode. Necessary to support + // U+320E..U+321E in NFKC mode. + rb.combineHangul(s, i, k) + return + } + ii := b[i] + // We can only use combineForward as a filter if we later + // get the info for the combined character. This is more + // expensive than using the filter. Using combinesBackward() + // is safe. + if ii.combinesBackward() { + cccB := b[k-1].ccc + cccC := ii.ccc + blocked := false // b[i] blocked by starter or greater or equal CCC? + if cccB == 0 { + s = k - 1 + } else { + blocked = s != k-1 && cccB >= cccC + } + if !blocked { + combined := combine(rb.runeAt(s), rb.runeAt(i)) + if combined != 0 { + rb.assignRune(s, combined) + continue + } + } + } + b[k] = b[i] + k++ + } + rb.nrune = k +} diff --git a/src/pkg/exp/norm/composition_test.go b/src/pkg/exp/norm/composition_test.go new file mode 100644 index 000000000..976aa21ed --- /dev/null +++ b/src/pkg/exp/norm/composition_test.go @@ -0,0 +1,143 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import "testing" + +// TestCase is used for most tests. +type TestCase struct { + in []rune + out []rune +} + +type insertFunc func(rb *reorderBuffer, r rune) bool + +func insert(rb *reorderBuffer, r rune) bool { + src := inputString(string(r)) + return rb.insert(src, 0, rb.f.info(src, 0)) +} + +func runTests(t *testing.T, name string, fm Form, f insertFunc, tests []TestCase) { + rb := reorderBuffer{} + rb.init(fm, nil) + for i, test := range tests { + rb.reset() + for j, rune := range test.in { + b := []byte(string(rune)) + src := inputBytes(b) + if !rb.insert(src, 0, rb.f.info(src, 0)) { + t.Errorf("%s:%d: insert failed for rune %d", name, i, j) + } + } + if rb.f.composing { + rb.compose() + } + if rb.nrune != len(test.out) { + t.Errorf("%s:%d: length = %d; want %d", name, i, rb.nrune, len(test.out)) + continue + } + for j, want := range test.out { + found := rune(rb.runeAt(j)) + if found != want { + t.Errorf("%s:%d: runeAt(%d) = %U; want %U", name, i, j, found, want) + } + } + } +} + +type flushFunc func(rb *reorderBuffer) []byte + +func testFlush(t *testing.T, name string, fn flushFunc) { + rb := reorderBuffer{} + rb.init(NFC, nil) + out := fn(&rb) + if len(out) != 0 { + t.Errorf("%s: wrote bytes on flush of empty buffer. (len(out) = %d)", name, len(out)) + } + + for _, r := range []rune("world!") { + insert(&rb, r) + } + + out = []byte("Hello ") + out = rb.flush(out) + want := "Hello world!" + if string(out) != want { + t.Errorf(`%s: output after flush was "%s"; want "%s"`, name, string(out), want) + } + if rb.nrune != 0 { + t.Errorf("%s: non-null size of info buffer (rb.nrune == %d)", name, rb.nrune) + } + if rb.nbyte != 0 { + t.Errorf("%s: non-null size of byte buffer (rb.nbyte == %d)", name, rb.nbyte) + } +} + +func flushF(rb *reorderBuffer) []byte { + out := make([]byte, 0) + return rb.flush(out) +} + +func flushCopyF(rb *reorderBuffer) []byte { + out := make([]byte, maxByteBufferSize) + n := rb.flushCopy(out) + return out[:n] +} + +func TestFlush(t *testing.T) { + testFlush(t, "flush", flushF) + testFlush(t, "flushCopy", flushCopyF) +} + +var insertTests = []TestCase{ + {[]rune{'a'}, []rune{'a'}}, + {[]rune{0x300}, []rune{0x300}}, + {[]rune{0x300, 0x316}, []rune{0x316, 0x300}}, // CCC(0x300)==230; CCC(0x316)==220 + {[]rune{0x316, 0x300}, []rune{0x316, 0x300}}, + {[]rune{0x41, 0x316, 0x300}, []rune{0x41, 0x316, 0x300}}, + {[]rune{0x41, 0x300, 0x316}, []rune{0x41, 0x316, 0x300}}, + {[]rune{0x300, 0x316, 0x41}, []rune{0x316, 0x300, 0x41}}, + {[]rune{0x41, 0x300, 0x40, 0x316}, []rune{0x41, 0x300, 0x40, 0x316}}, +} + +func TestInsert(t *testing.T) { + runTests(t, "TestInsert", NFD, insert, insertTests) +} + +var decompositionNFDTest = []TestCase{ + {[]rune{0xC0}, []rune{0x41, 0x300}}, + {[]rune{0xAC00}, []rune{0x1100, 0x1161}}, + {[]rune{0x01C4}, []rune{0x01C4}}, + {[]rune{0x320E}, []rune{0x320E}}, + {[]rune("음ẻ과"), []rune{0x110B, 0x1173, 0x11B7, 0x65, 0x309, 0x1100, 0x116A}}, +} + +var decompositionNFKDTest = []TestCase{ + {[]rune{0xC0}, []rune{0x41, 0x300}}, + {[]rune{0xAC00}, []rune{0x1100, 0x1161}}, + {[]rune{0x01C4}, []rune{0x44, 0x5A, 0x030C}}, + {[]rune{0x320E}, []rune{0x28, 0x1100, 0x1161, 0x29}}, +} + +func TestDecomposition(t *testing.T) { + runTests(t, "TestDecompositionNFD", NFD, insert, decompositionNFDTest) + runTests(t, "TestDecompositionNFKD", NFKD, insert, decompositionNFKDTest) +} + +var compositionTest = []TestCase{ + {[]rune{0x41, 0x300}, []rune{0xC0}}, + {[]rune{0x41, 0x316}, []rune{0x41, 0x316}}, + {[]rune{0x41, 0x300, 0x35D}, []rune{0xC0, 0x35D}}, + {[]rune{0x41, 0x316, 0x300}, []rune{0xC0, 0x316}}, + // blocking starter + {[]rune{0x41, 0x316, 0x40, 0x300}, []rune{0x41, 0x316, 0x40, 0x300}}, + {[]rune{0x1100, 0x1161}, []rune{0xAC00}}, + // parenthesized Hangul, alternate between ASCII and Hangul. + {[]rune{0x28, 0x1100, 0x1161, 0x29}, []rune{0x28, 0xAC00, 0x29}}, +} + +func TestComposition(t *testing.T) { + runTests(t, "TestComposition", NFC, insert, compositionTest) +} diff --git a/src/pkg/exp/norm/example_iter_test.go b/src/pkg/exp/norm/example_iter_test.go new file mode 100644 index 000000000..edb9fcf55 --- /dev/null +++ b/src/pkg/exp/norm/example_iter_test.go @@ -0,0 +1,81 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm_test + +import ( + "bytes" + "exp/norm" + "fmt" + "unicode/utf8" +) + +// EqualSimple uses a norm.Iter to compare two non-normalized +// strings for equivalence. +func EqualSimple(a, b string) bool { + var ia, ib norm.Iter + ia.InitString(norm.NFKD, a) + ib.InitString(norm.NFKD, b) + for !ia.Done() && !ib.Done() { + if !bytes.Equal(ia.Next(), ib.Next()) { + return false + } + } + return ia.Done() && ib.Done() +} + +// FindPrefix finds the longest common prefix of ASCII characters +// of a and b. +func FindPrefix(a, b string) int { + i := 0 + for ; i < len(a) && i < len(b) && a[i] < utf8.RuneSelf && a[i] == b[i]; i++ { + } + return i +} + +// EqualOpt is like EqualSimple, but optimizes the special +// case for ASCII characters. +func EqualOpt(a, b string) bool { + n := FindPrefix(a, b) + a, b = a[n:], b[n:] + var ia, ib norm.Iter + ia.InitString(norm.NFKD, a) + ib.InitString(norm.NFKD, b) + for !ia.Done() && !ib.Done() { + if !bytes.Equal(ia.Next(), ib.Next()) { + return false + } + if n := int64(FindPrefix(a[ia.Pos():], b[ib.Pos():])); n != 0 { + ia.Seek(n, 1) + ib.Seek(n, 1) + } + } + return ia.Done() && ib.Done() +} + +var compareTests = []struct{ a, b string }{ + {"aaa", "aaa"}, + {"aaa", "aab"}, + {"a\u0300a", "\u00E0a"}, + {"a\u0300\u0320b", "a\u0320\u0300b"}, + {"\u1E0A\u0323", "\x44\u0323\u0307"}, + // A character that decomposes into multiple segments + // spans several iterations. + {"\u3304", "\u30A4\u30CB\u30F3\u30AF\u3099"}, +} + +func ExampleIter() { + for i, t := range compareTests { + r0 := EqualSimple(t.a, t.b) + r1 := EqualOpt(t.a, t.b) + fmt.Printf("%d: %v %v\n", i, r0, r1) + } + // Output: + // 0: true true + // 1: false false + // 2: true true + // 3: true true + // 4: true true + // 5: true true +} diff --git a/src/pkg/exp/norm/forminfo.go b/src/pkg/exp/norm/forminfo.go new file mode 100644 index 000000000..7f7ee72e8 --- /dev/null +++ b/src/pkg/exp/norm/forminfo.go @@ -0,0 +1,229 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +// This file contains Form-specific logic and wrappers for data in tables.go. + +// Rune info is stored in a separate trie per composing form. A composing form +// and its corresponding decomposing form share the same trie. Each trie maps +// a rune to a uint16. The values take two forms. For v >= 0x8000: +// bits +// 0..8: ccc +// 9..12: qcInfo (see below). isYesD is always true (no decompostion). +// 16: 1 +// For v < 0x8000, the respective rune has a decomposition and v is an index +// into a byte array of UTF-8 decomposition sequences and additional info and +// has the form: +//
* [ []] +// The header contains the number of bytes in the decomposition (excluding this +// length byte). The two most significant bits of this length byte correspond +// to bit 2 and 3 of qcIfo (see below). The byte sequence itself starts at v+1. +// The byte sequence is followed by a trailing and leading CCC if the values +// for these are not zero. The value of v determines which ccc are appended +// to the sequences. For v < firstCCC, there are none, for v >= firstCCC, +// the sequence is followed by a trailing ccc, and for v >= firstLeadingCC +// there is an additional leading ccc. + +const ( + qcInfoMask = 0xF // to clear all but the relevant bits in a qcInfo + headerLenMask = 0x3F // extract the length value from the header byte + headerFlagsMask = 0xC0 // extract the qcInfo bits from the header byte +) + +// Properties provides access to normalization properties of a rune. +type Properties struct { + pos uint8 // start position in reorderBuffer; used in composition.go + size uint8 // length of UTF-8 encoding of this rune + ccc uint8 // leading canonical combining class (ccc if not decomposition) + tccc uint8 // trailing canonical combining class (ccc if not decomposition) + flags qcInfo // quick check flags + index uint16 +} + +// functions dispatchable per form +type lookupFunc func(b input, i int) Properties + +// formInfo holds Form-specific functions and tables. +type formInfo struct { + form Form + composing, compatibility bool // form type + info lookupFunc + nextMain iterFunc +} + +var formTable []*formInfo + +func init() { + formTable = make([]*formInfo, 4) + + for i := range formTable { + f := &formInfo{} + formTable[i] = f + f.form = Form(i) + if Form(i) == NFKD || Form(i) == NFKC { + f.compatibility = true + f.info = lookupInfoNFKC + } else { + f.info = lookupInfoNFC + } + f.nextMain = nextDecomposed + if Form(i) == NFC || Form(i) == NFKC { + f.nextMain = nextComposed + f.composing = true + } + } +} + +// We do not distinguish between boundaries for NFC, NFD, etc. to avoid +// unexpected behavior for the user. For example, in NFD, there is a boundary +// after 'a'. However, 'a' might combine with modifiers, so from the application's +// perspective it is not a good boundary. We will therefore always use the +// boundaries for the combining variants. + +// BoundaryBefore returns true if this rune starts a new segment and +// cannot combine with any rune on the left. +func (p Properties) BoundaryBefore() bool { + if p.ccc == 0 && !p.combinesBackward() { + return true + } + // We assume that the CCC of the first character in a decomposition + // is always non-zero if different from info.ccc and that we can return + // false at this point. This is verified by maketables. + return false +} + +// BoundaryAfter returns true if this rune cannot combine with runes to the right +// and always denotes the end of a segment. +func (p Properties) BoundaryAfter() bool { + return p.isInert() +} + +// We pack quick check data in 4 bits: +// 0: NFD_QC Yes (0) or No (1). No also means there is a decomposition. +// 1..2: NFC_QC Yes(00), No (10), or Maybe (11) +// 3: Combines forward (0 == false, 1 == true) +// +// When all 4 bits are zero, the character is inert, meaning it is never +// influenced by normalization. +type qcInfo uint8 + +func (p Properties) isYesC() bool { return p.flags&0x4 == 0 } +func (p Properties) isYesD() bool { return p.flags&0x1 == 0 } + +func (p Properties) combinesForward() bool { return p.flags&0x8 != 0 } +func (p Properties) combinesBackward() bool { return p.flags&0x2 != 0 } // == isMaybe +func (p Properties) hasDecomposition() bool { return p.flags&0x1 != 0 } // == isNoD + +func (p Properties) isInert() bool { + return p.flags&0xf == 0 && p.ccc == 0 +} + +func (p Properties) multiSegment() bool { + return p.index >= firstMulti && p.index < endMulti +} + +// Decomposition returns the decomposition for the underlying rune +// or nil if there is none. +func (p Properties) Decomposition() []byte { + if p.index == 0 { + return nil + } + i := p.index + n := decomps[i] & headerLenMask + i++ + return decomps[i : i+uint16(n)] +} + +// Size returns the length of UTF-8 encoding of the rune. +func (p Properties) Size() int { + return int(p.size) +} + +// CCC returns the canonical combining class of the underlying rune. +func (p Properties) CCC() uint8 { + if p.index > firstCCCZeroExcept { + return 0 + } + return p.ccc +} + +// LeadCCC returns the CCC of the first rune in the decomposition. +// If there is no decomposition, LeadCCC equals CCC. +func (p Properties) LeadCCC() uint8 { + return p.ccc +} + +// TrailCCC returns the CCC of the last rune in the decomposition. +// If there is no decomposition, TrailCCC equals CCC. +func (p Properties) TrailCCC() uint8 { + return p.tccc +} + +// Recomposition +// We use 32-bit keys instead of 64-bit for the two codepoint keys. +// This clips off the bits of three entries, but we know this will not +// result in a collision. In the unlikely event that changes to +// UnicodeData.txt introduce collisions, the compiler will catch it. +// Note that the recomposition map for NFC and NFKC are identical. + +// combine returns the combined rune or 0 if it doesn't exist. +func combine(a, b rune) rune { + key := uint32(uint16(a))<<16 + uint32(uint16(b)) + return recompMap[key] +} + +func lookupInfoNFC(b input, i int) Properties { + v, sz := b.charinfoNFC(i) + return compInfo(v, sz) +} + +func lookupInfoNFKC(b input, i int) Properties { + v, sz := b.charinfoNFKC(i) + return compInfo(v, sz) +} + +// Properties returns properties for the first rune in s. +func (f Form) Properties(s []byte) Properties { + if f == NFC || f == NFD { + return compInfo(nfcTrie.lookup(s)) + } + return compInfo(nfkcTrie.lookup(s)) +} + +// PropertiesString returns properties for the first rune in s. +func (f Form) PropertiesString(s string) Properties { + if f == NFC || f == NFD { + return compInfo(nfcTrie.lookupString(s)) + } + return compInfo(nfkcTrie.lookupString(s)) +} + +// compInfo converts the information contained in v and sz +// to a Properties. See the comment at the top of the file +// for more information on the format. +func compInfo(v uint16, sz int) Properties { + if v == 0 { + return Properties{size: uint8(sz)} + } else if v >= 0x8000 { + return Properties{ + size: uint8(sz), + ccc: uint8(v), + tccc: uint8(v), + flags: qcInfo(v>>8) & qcInfoMask, + } + } + // has decomposition + h := decomps[v] + f := (qcInfo(h&headerFlagsMask) >> 4) | 0x1 + ri := Properties{size: uint8(sz), flags: f, index: v} + if v >= firstCCC { + v += uint16(h&headerLenMask) + 1 + ri.tccc = decomps[v] + if v >= firstLeadingCCC { + ri.ccc = decomps[v+1] + } + } + return ri +} diff --git a/src/pkg/exp/norm/input.go b/src/pkg/exp/norm/input.go new file mode 100644 index 000000000..d0177a14a --- /dev/null +++ b/src/pkg/exp/norm/input.go @@ -0,0 +1,105 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import "unicode/utf8" + +type input struct { + str string + bytes []byte +} + +func inputBytes(str []byte) input { + return input{bytes: str} +} + +func inputString(str string) input { + return input{str: str} +} + +func (in *input) setBytes(str []byte) { + in.str = "" + in.bytes = str +} + +func (in *input) setString(str string) { + in.str = str + in.bytes = nil +} + +func (in *input) _byte(p int) byte { + if in.bytes == nil { + return in.str[p] + } + return in.bytes[p] +} + +func (in *input) skipASCII(p, max int) int { + if in.bytes == nil { + for ; p < max && in.str[p] < utf8.RuneSelf; p++ { + } + } else { + for ; p < max && in.bytes[p] < utf8.RuneSelf; p++ { + } + } + return p +} + +func (in *input) skipNonStarter(p int) int { + if in.bytes == nil { + for ; p < len(in.str) && !utf8.RuneStart(in.str[p]); p++ { + } + } else { + for ; p < len(in.bytes) && !utf8.RuneStart(in.bytes[p]); p++ { + } + } + return p +} + +func (in *input) appendSlice(buf []byte, b, e int) []byte { + if in.bytes != nil { + return append(buf, in.bytes[b:e]...) + } + for i := b; i < e; i++ { + buf = append(buf, in.str[i]) + } + return buf +} + +func (in *input) copySlice(buf []byte, b, e int) int { + if in.bytes == nil { + return copy(buf, in.str[b:e]) + } + return copy(buf, in.bytes[b:e]) +} + +func (in *input) charinfoNFC(p int) (uint16, int) { + if in.bytes == nil { + return nfcTrie.lookupString(in.str[p:]) + } + return nfcTrie.lookup(in.bytes[p:]) +} + +func (in *input) charinfoNFKC(p int) (uint16, int) { + if in.bytes == nil { + return nfkcTrie.lookupString(in.str[p:]) + } + return nfkcTrie.lookup(in.bytes[p:]) +} + +func (in *input) hangul(p int) (r rune) { + if in.bytes == nil { + if !isHangulString(in.str[p:]) { + return 0 + } + r, _ = utf8.DecodeRuneInString(in.str[p:]) + } else { + if !isHangul(in.bytes[p:]) { + return 0 + } + r, _ = utf8.DecodeRune(in.bytes[p:]) + } + return r +} diff --git a/src/pkg/exp/norm/iter.go b/src/pkg/exp/norm/iter.go new file mode 100644 index 000000000..a9546247c --- /dev/null +++ b/src/pkg/exp/norm/iter.go @@ -0,0 +1,401 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "fmt" + "unicode/utf8" +) + +const MaxSegmentSize = maxByteBufferSize + +// An Iter iterates over a string or byte slice, while normalizing it +// to a given Form. +type Iter struct { + rb reorderBuffer + buf [maxByteBufferSize]byte + info Properties // first character saved from previous iteration + next iterFunc // implementation of next depends on form + asciiF iterFunc + + p int // current position in input source + multiSeg []byte // remainder of multi-segment decomposition +} + +type iterFunc func(*Iter) []byte + +// Init initializes i to iterate over src after normalizing it to Form f. +func (i *Iter) Init(f Form, src []byte) { + i.p = 0 + if len(src) == 0 { + i.setDone() + i.rb.nsrc = 0 + return + } + i.multiSeg = nil + i.rb.init(f, src) + i.next = i.rb.f.nextMain + i.asciiF = nextASCIIBytes + i.info = i.rb.f.info(i.rb.src, i.p) +} + +// InitString initializes i to iterate over src after normalizing it to Form f. +func (i *Iter) InitString(f Form, src string) { + i.p = 0 + if len(src) == 0 { + i.setDone() + i.rb.nsrc = 0 + return + } + i.multiSeg = nil + i.rb.initString(f, src) + i.next = i.rb.f.nextMain + i.asciiF = nextASCIIString + i.info = i.rb.f.info(i.rb.src, i.p) +} + +// Seek sets the segment to be returned by the next call to Next to start +// at position p. It is the responsibility of the caller to set p to the +// start of a UTF8 rune. +func (i *Iter) Seek(offset int64, whence int) (int64, error) { + var abs int64 + switch whence { + case 0: + abs = offset + case 1: + abs = int64(i.p) + offset + case 2: + abs = int64(i.rb.nsrc) + offset + default: + return 0, fmt.Errorf("norm: invalid whence") + } + if abs < 0 { + return 0, fmt.Errorf("norm: negative position") + } + if int(abs) >= i.rb.nsrc { + i.setDone() + return int64(i.p), nil + } + i.p = int(abs) + i.multiSeg = nil + i.next = i.rb.f.nextMain + i.info = i.rb.f.info(i.rb.src, i.p) + return abs, nil +} + +// returnSlice returns a slice of the underlying input type as a byte slice. +// If the underlying is of type []byte, it will simply return a slice. +// If the underlying is of type string, it will copy the slice to the buffer +// and return that. +func (i *Iter) returnSlice(a, b int) []byte { + if i.rb.src.bytes == nil { + return i.buf[:copy(i.buf[:], i.rb.src.str[a:b])] + } + return i.rb.src.bytes[a:b] +} + +// Pos returns the byte position at which the next call to Next will commence processing. +func (i *Iter) Pos() int { + return i.p +} + +func (i *Iter) setDone() { + i.next = nextDone + i.p = i.rb.nsrc +} + +// Done returns true if there is no more input to process. +func (i *Iter) Done() bool { + return i.p >= i.rb.nsrc +} + +// Next returns f(i.input[i.Pos():n]), where n is a boundary of i.input. +// For any input a and b for which f(a) == f(b), subsequent calls +// to Next will return the same segments. +// Modifying runes are grouped together with the preceding starter, if such a starter exists. +// Although not guaranteed, n will typically be the smallest possible n. +func (i *Iter) Next() []byte { + return i.next(i) +} + +func nextASCIIBytes(i *Iter) []byte { + p := i.p + 1 + if p >= i.rb.nsrc { + i.setDone() + return i.rb.src.bytes[i.p:p] + } + if i.rb.src.bytes[p] < utf8.RuneSelf { + p0 := i.p + i.p = p + return i.rb.src.bytes[p0:p] + } + i.info = i.rb.f.info(i.rb.src, i.p) + i.next = i.rb.f.nextMain + return i.next(i) +} + +func nextASCIIString(i *Iter) []byte { + p := i.p + 1 + if p >= i.rb.nsrc { + i.buf[0] = i.rb.src.str[i.p] + i.setDone() + return i.buf[:1] + } + if i.rb.src.str[p] < utf8.RuneSelf { + i.buf[0] = i.rb.src.str[i.p] + i.p = p + return i.buf[:1] + } + i.info = i.rb.f.info(i.rb.src, i.p) + i.next = i.rb.f.nextMain + return i.next(i) +} + +func nextHangul(i *Iter) []byte { + if r := i.rb.src.hangul(i.p); r != 0 { + i.p += hangulUTF8Size + if i.p >= i.rb.nsrc { + i.setDone() + } + return i.buf[:decomposeHangul(i.buf[:], r)] + } + i.info = i.rb.f.info(i.rb.src, i.p) + i.next = i.rb.f.nextMain + return i.next(i) +} + +func nextDone(i *Iter) []byte { + return nil +} + +// nextMulti is used for iterating over multi-segment decompositions +// for decomposing normal forms. +func nextMulti(i *Iter) []byte { + j := 0 + d := i.multiSeg + // skip first rune + for j = 1; j < len(d) && !utf8.RuneStart(d[j]); j++ { + } + for j < len(d) { + info := i.rb.f.info(input{bytes: d}, j) + if info.ccc == 0 { + i.multiSeg = d[j:] + return d[:j] + } + j += int(info.size) + } + // treat last segment as normal decomposition + i.next = i.rb.f.nextMain + return i.next(i) +} + +// nextMultiNorm is used for iterating over multi-segment decompositions +// for composing normal forms. +func nextMultiNorm(i *Iter) []byte { + j := 0 + d := i.multiSeg + // skip first rune + for j = 1; j < len(d) && !utf8.RuneStart(d[j]); j++ { + } + for j < len(d) { + info := i.rb.f.info(input{bytes: d}, j) + if info.ccc == 0 { + i.multiSeg = d[j:] + return d[:j] + } + j += int(info.size) + } + i.multiSeg = nil + i.next = nextComposed + i.p++ // restore old valud of i.p. See nextComposed. + if i.p >= i.rb.nsrc { + i.setDone() + } + return d +} + +// nextDecomposed is the implementation of Next for forms NFD and NFKD. +func nextDecomposed(i *Iter) (next []byte) { + startp, outp := i.p, 0 + inCopyStart, outCopyStart := i.p, 0 + for { + if sz := int(i.info.size); sz <= 1 { + p := i.p + i.p++ // ASCII or illegal byte. Either way, advance by 1. + if i.p >= i.rb.nsrc { + i.setDone() + return i.returnSlice(p, i.p) + } else if i.rb.src._byte(i.p) < utf8.RuneSelf { + i.next = i.asciiF + return i.returnSlice(p, i.p) + } + outp++ + } else if d := i.info.Decomposition(); d != nil { + // Note: If leading CCC != 0, then len(d) == 2 and last is also non-zero. + // Case 1: there is a leftover to copy. In this case the decomposition + // must begin with a modifier and should always be appended. + // Case 2: no leftover. Simply return d if followed by a ccc == 0 value. + p := outp + len(d) + if outp > 0 { + i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) + if p > len(i.buf) { + return i.buf[:outp] + } + } else if i.info.multiSegment() { + // outp must be 0 as multi-segment decompositions always + // start a new segment. + if i.multiSeg == nil { + i.multiSeg = d + i.next = nextMulti + return nextMulti(i) + } + // We are in the last segment. Treat as normal decomposition. + d = i.multiSeg + i.multiSeg = nil + p = len(d) + } + prevCC := i.info.tccc + if i.p += sz; i.p >= i.rb.nsrc { + i.setDone() + i.info = Properties{} // Force BoundaryBefore to succeed. + } else { + i.info = i.rb.f.info(i.rb.src, i.p) + } + if i.info.BoundaryBefore() { + if outp > 0 { + copy(i.buf[outp:], d) + return i.buf[:p] + } + return d + } + copy(i.buf[outp:], d) + outp = p + inCopyStart, outCopyStart = i.p, outp + if i.info.ccc < prevCC { + goto doNorm + } + continue + } else if r := i.rb.src.hangul(i.p); r != 0 { + i.next = nextHangul + i.p += hangulUTF8Size + if i.p >= i.rb.nsrc { + i.setDone() + } + return i.buf[:decomposeHangul(i.buf[:], r)] + } else { + p := outp + sz + if p > len(i.buf) { + break + } + outp = p + i.p += sz + } + if i.p >= i.rb.nsrc { + i.setDone() + break + } + prevCC := i.info.tccc + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.BoundaryBefore() { + break + } else if i.info.ccc < prevCC { + goto doNorm + } + } + if outCopyStart == 0 { + return i.returnSlice(inCopyStart, i.p) + } else if inCopyStart < i.p { + i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) + } + return i.buf[:outp] +doNorm: + // Insert what we have decomposed so far in the reorderBuffer. + // As we will only reorder, there will always be enough room. + i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) + if !i.rb.insertDecomposed(i.buf[0:outp]) { + // Start over to prevent decompositions from crossing segment boundaries. + // This is a rare occurrence. + i.p = startp + i.info = i.rb.f.info(i.rb.src, i.p) + } + for { + if !i.rb.insert(i.rb.src, i.p, i.info) { + break + } + if i.p += int(i.info.size); i.p >= i.rb.nsrc { + i.setDone() + break + } + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.ccc == 0 { + break + } + } + // new segment or too many combining characters: exit normalization + return i.buf[:i.rb.flushCopy(i.buf[:])] +} + +// nextComposed is the implementation of Next for forms NFC and NFKC. +func nextComposed(i *Iter) []byte { + outp, startp := 0, i.p + var prevCC uint8 + for { + if !i.info.isYesC() { + goto doNorm + } + if cc := i.info.ccc; cc == 0 && outp > 0 { + break + } else if cc < prevCC { + goto doNorm + } + prevCC = i.info.tccc + sz := int(i.info.size) + if sz == 0 { + sz = 1 // illegal rune: copy byte-by-byte + } + p := outp + sz + if p > len(i.buf) { + break + } + outp = p + i.p += sz + if i.p >= i.rb.nsrc { + i.setDone() + break + } else if i.rb.src._byte(i.p) < utf8.RuneSelf { + i.next = i.asciiF + break + } + i.info = i.rb.f.info(i.rb.src, i.p) + } + return i.returnSlice(startp, i.p) +doNorm: + multi := false + i.p = startp + i.info = i.rb.f.info(i.rb.src, i.p) + for { + if !i.rb.insert(i.rb.src, i.p, i.info) { + break + } + multi = multi || i.info.multiSegment() + if i.p += int(i.info.size); i.p >= i.rb.nsrc { + i.setDone() + break + } + i.info = i.rb.f.info(i.rb.src, i.p) + if i.info.BoundaryBefore() { + break + } + } + i.rb.compose() + seg := i.buf[:i.rb.flushCopy(i.buf[:])] + if multi { + i.p-- // fake not being done yet + i.multiSeg = seg + i.next = nextMultiNorm + return nextMultiNorm(i) + } + return seg +} diff --git a/src/pkg/exp/norm/iter_test.go b/src/pkg/exp/norm/iter_test.go new file mode 100644 index 000000000..826119362 --- /dev/null +++ b/src/pkg/exp/norm/iter_test.go @@ -0,0 +1,188 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "strings" + "testing" +) + +func doIterNorm(f Form, s string) []byte { + acc := []byte{} + i := Iter{} + i.InitString(f, s) + for !i.Done() { + acc = append(acc, i.Next()...) + } + return acc +} + +func runIterTests(t *testing.T, name string, f Form, tests []AppendTest, norm bool) { + for i, test := range tests { + in := test.left + test.right + gold := test.out + if norm { + gold = string(f.AppendString(nil, test.out)) + } + out := string(doIterNorm(f, in)) + if len(out) != len(gold) { + const msg = "%s:%d: length is %d; want %d" + t.Errorf(msg, name, i, len(out), len(gold)) + } + if out != gold { + // Find first rune that differs and show context. + ir := []rune(out) + ig := []rune(gold) + t.Errorf("\n%X != \n%X", ir, ig) + for j := 0; j < len(ir) && j < len(ig); j++ { + if ir[j] == ig[j] { + continue + } + if j -= 3; j < 0 { + j = 0 + } + for e := j + 7; j < e && j < len(ir) && j < len(ig); j++ { + const msg = "%s:%d: runeAt(%d) = %U; want %U" + t.Errorf(msg, name, i, j, ir[j], ig[j]) + } + break + } + } + } +} + +func rep(r rune, n int) string { + return strings.Repeat(string(r), n) +} + +const segSize = maxByteBufferSize + +var iterTests = []AppendTest{ + {"", ascii, ascii}, + {"", txt_all, txt_all}, + {"", "a" + rep(0x0300, segSize/2), "a" + rep(0x0300, segSize/2)}, +} + +var iterTestsD = []AppendTest{ + { // segment overflow on unchanged character + "", + "a" + rep(0x0300, segSize/2) + "\u0316", + "a" + rep(0x0300, segSize/2-1) + "\u0316\u0300", + }, + { // segment overflow on unchanged character + start value + "", + "a" + rep(0x0300, segSize/2+maxCombiningChars+4) + "\u0316", + "a" + rep(0x0300, segSize/2+maxCombiningChars) + "\u0316" + rep(0x300, 4), + }, + { // segment overflow on decomposition + "", + "a" + rep(0x0300, segSize/2-1) + "\u0340", + "a" + rep(0x0300, segSize/2), + }, + { // segment overflow on decomposition + start value + "", + "a" + rep(0x0300, segSize/2-1) + "\u0340" + rep(0x300, maxCombiningChars+4) + "\u0320", + "a" + rep(0x0300, segSize/2-1) + rep(0x300, maxCombiningChars+1) + "\u0320" + rep(0x300, 4), + }, + { // start value after ASCII overflow + "", + rep('a', segSize) + rep(0x300, maxCombiningChars+2) + "\u0320", + rep('a', segSize) + rep(0x300, maxCombiningChars) + "\u0320\u0300\u0300", + }, + { // start value after Hangul overflow + "", + rep(0xAC00, segSize/6) + rep(0x300, maxCombiningChars+2) + "\u0320", + strings.Repeat("\u1100\u1161", segSize/6) + rep(0x300, maxCombiningChars+1) + "\u0320" + rep(0x300, 1), + }, + { // start value after cc=0 + "", + "您您" + rep(0x300, maxCombiningChars+4) + "\u0320", + "您您" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4), + }, + { // start value after normalization + "", + "\u0300\u0320a" + rep(0x300, maxCombiningChars+4) + "\u0320", + "\u0320\u0300a" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4), + }, +} + +var iterTestsC = []AppendTest{ + { // ordering of non-composing combining characters + "", + "\u0305\u0316", + "\u0316\u0305", + }, + { // segment overflow + "", + "a" + rep(0x0305, segSize/2+4) + "\u0316", + "a" + rep(0x0305, segSize/2-1) + "\u0316" + rep(0x305, 5), + }, +} + +func TestIterNextD(t *testing.T) { + runIterTests(t, "IterNextD1", NFKD, appendTests, true) + runIterTests(t, "IterNextD2", NFKD, iterTests, true) + runIterTests(t, "IterNextD3", NFKD, iterTestsD, false) +} + +func TestIterNextC(t *testing.T) { + runIterTests(t, "IterNextC1", NFKC, appendTests, true) + runIterTests(t, "IterNextC2", NFKC, iterTests, true) + runIterTests(t, "IterNextC3", NFKC, iterTestsC, false) +} + +type SegmentTest struct { + in string + out []string +} + +var segmentTests = []SegmentTest{ + {"\u1E0A\u0323a", []string{"\x44\u0323\u0307", "a", ""}}, + {rep('a', segSize), append(strings.Split(rep('a', segSize), ""), "")}, + {rep('a', segSize+2), append(strings.Split(rep('a', segSize+2), ""), "")}, + {rep('a', segSize) + "\u0300aa", + append(strings.Split(rep('a', segSize-1), ""), "a\u0300", "a", "a", "")}, +} + +var segmentTestsK = []SegmentTest{ + {"\u3332", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u3099", ""}}, + // last segment of multi-segment decomposition needs normalization + {"\u3332\u093C", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u093C\u3099", ""}}, + // Hangul and Jamo are grouped togeter. + {"\uAC00", []string{"\u1100\u1161", ""}}, + {"\uAC01", []string{"\u1100\u1161\u11A8", ""}}, + {"\u1100\u1161", []string{"\u1100\u1161", ""}}, +} + +// Note that, by design, segmentation is equal for composing and decomposing forms. +func TestIterSegmentation(t *testing.T) { + segmentTest(t, "SegmentTestD", NFD, segmentTests) + segmentTest(t, "SegmentTestC", NFC, segmentTests) + segmentTest(t, "SegmentTestD", NFKD, segmentTestsK) + segmentTest(t, "SegmentTestC", NFKC, segmentTestsK) +} + +func segmentTest(t *testing.T, name string, f Form, tests []SegmentTest) { + iter := Iter{} + for i, tt := range tests { + iter.InitString(f, tt.in) + for j, seg := range tt.out { + if seg == "" { + if !iter.Done() { + res := string(iter.Next()) + t.Errorf(`%s:%d:%d: expected Done()==true, found segment "%s"`, name, i, j, res) + } + continue + } + if iter.Done() { + t.Errorf("%s:%d:%d: Done()==true, want false", name, i, j) + } + seg = f.String(seg) + if res := string(iter.Next()); res != seg { + t.Errorf(`%s:%d:%d" segment was "%s" (%d); want "%s" (%d) %X %X`, name, i, j, res, len(res), seg, len(seg), []rune(res), []rune(seg)) + } + } + } +} diff --git a/src/pkg/exp/norm/maketables.go b/src/pkg/exp/norm/maketables.go new file mode 100644 index 000000000..50c0c310a --- /dev/null +++ b/src/pkg/exp/norm/maketables.go @@ -0,0 +1,923 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Normalization table generator. +// Data read from the web. +// See forminfo.go for a description of the trie values associated with each rune. + +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "regexp" + "sort" + "strconv" + "strings" + "unicode" +) + +func main() { + flag.Parse() + loadUnicodeData() + loadCompositionExclusions() + completeCharFields(FCanonical) + completeCharFields(FCompatibility) + verifyComputed() + printChars() + makeTables() + testDerived() +} + +var url = flag.String("url", + "http://www.unicode.org/Public/"+unicode.Version+"/ucd/", + "URL of Unicode database directory") +var tablelist = flag.String("tables", + "all", + "comma-separated list of which tables to generate; "+ + "can be 'decomp', 'recomp', 'info' and 'all'") +var test = flag.Bool("test", + false, + "test existing tables; can be used to compare web data with package data") +var verbose = flag.Bool("verbose", + false, + "write data to stdout as it is parsed") +var localFiles = flag.Bool("local", + false, + "data files have been copied to the current directory; for debugging only") + +var logger = log.New(os.Stderr, "", log.Lshortfile) + +// UnicodeData.txt has form: +// 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; +// 007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A +// See http://unicode.org/reports/tr44/ for full explanation +// The fields: +const ( + FCodePoint = iota + FName + FGeneralCategory + FCanonicalCombiningClass + FBidiClass + FDecompMapping + FDecimalValue + FDigitValue + FNumericValue + FBidiMirrored + FUnicode1Name + FISOComment + FSimpleUppercaseMapping + FSimpleLowercaseMapping + FSimpleTitlecaseMapping + NumField + + MaxChar = 0x10FFFF // anything above this shouldn't exist +) + +// Quick Check properties of runes allow us to quickly +// determine whether a rune may occur in a normal form. +// For a given normal form, a rune may be guaranteed to occur +// verbatim (QC=Yes), may or may not combine with another +// rune (QC=Maybe), or may not occur (QC=No). +type QCResult int + +const ( + QCUnknown QCResult = iota + QCYes + QCNo + QCMaybe +) + +func (r QCResult) String() string { + switch r { + case QCYes: + return "Yes" + case QCNo: + return "No" + case QCMaybe: + return "Maybe" + } + return "***UNKNOWN***" +} + +const ( + FCanonical = iota // NFC or NFD + FCompatibility // NFKC or NFKD + FNumberOfFormTypes +) + +const ( + MComposed = iota // NFC or NFKC + MDecomposed // NFD or NFKD + MNumberOfModes +) + +// This contains only the properties we're interested in. +type Char struct { + name string + codePoint rune // if zero, this index is not a valid code point. + ccc uint8 // canonical combining class + excludeInComp bool // from CompositionExclusions.txt + compatDecomp bool // it has a compatibility expansion + + forms [FNumberOfFormTypes]FormInfo // For FCanonical and FCompatibility + + state State +} + +var chars = make([]Char, MaxChar+1) + +func (c Char) String() string { + buf := new(bytes.Buffer) + + fmt.Fprintf(buf, "%U [%s]:\n", c.codePoint, c.name) + fmt.Fprintf(buf, " ccc: %v\n", c.ccc) + fmt.Fprintf(buf, " excludeInComp: %v\n", c.excludeInComp) + fmt.Fprintf(buf, " compatDecomp: %v\n", c.compatDecomp) + fmt.Fprintf(buf, " state: %v\n", c.state) + fmt.Fprintf(buf, " NFC:\n") + fmt.Fprint(buf, c.forms[FCanonical]) + fmt.Fprintf(buf, " NFKC:\n") + fmt.Fprint(buf, c.forms[FCompatibility]) + + return buf.String() +} + +// In UnicodeData.txt, some ranges are marked like this: +// 3400;;Lo;0;L;;;;;N;;;;; +// 4DB5;;Lo;0;L;;;;;N;;;;; +// parseCharacter keeps a state variable indicating the weirdness. +type State int + +const ( + SNormal State = iota // known to be zero for the type + SFirst + SLast + SMissing +) + +var lastChar = rune('\u0000') + +func (c Char) isValid() bool { + return c.codePoint != 0 && c.state != SMissing +} + +type FormInfo struct { + quickCheck [MNumberOfModes]QCResult // index: MComposed or MDecomposed + verified [MNumberOfModes]bool // index: MComposed or MDecomposed + + combinesForward bool // May combine with rune on the right + combinesBackward bool // May combine with rune on the left + isOneWay bool // Never appears in result + inDecomp bool // Some decompositions result in this char. + decomp Decomposition + expandedDecomp Decomposition +} + +func (f FormInfo) String() string { + buf := bytes.NewBuffer(make([]byte, 0)) + + fmt.Fprintf(buf, " quickCheck[C]: %v\n", f.quickCheck[MComposed]) + fmt.Fprintf(buf, " quickCheck[D]: %v\n", f.quickCheck[MDecomposed]) + fmt.Fprintf(buf, " cmbForward: %v\n", f.combinesForward) + fmt.Fprintf(buf, " cmbBackward: %v\n", f.combinesBackward) + fmt.Fprintf(buf, " isOneWay: %v\n", f.isOneWay) + fmt.Fprintf(buf, " inDecomp: %v\n", f.inDecomp) + fmt.Fprintf(buf, " decomposition: %X\n", f.decomp) + fmt.Fprintf(buf, " expandedDecomp: %X\n", f.expandedDecomp) + + return buf.String() +} + +type Decomposition []rune + +func openReader(file string) (input io.ReadCloser) { + if *localFiles { + f, err := os.Open(file) + if err != nil { + logger.Fatal(err) + } + input = f + } else { + path := *url + file + resp, err := http.Get(path) + if err != nil { + logger.Fatal(err) + } + if resp.StatusCode != 200 { + logger.Fatal("bad GET status for "+file, resp.Status) + } + input = resp.Body + } + return +} + +func parseDecomposition(s string, skipfirst bool) (a []rune, e error) { + decomp := strings.Split(s, " ") + if len(decomp) > 0 && skipfirst { + decomp = decomp[1:] + } + for _, d := range decomp { + point, err := strconv.ParseUint(d, 16, 64) + if err != nil { + return a, err + } + a = append(a, rune(point)) + } + return a, nil +} + +func parseCharacter(line string) { + field := strings.Split(line, ";") + if len(field) != NumField { + logger.Fatalf("%5s: %d fields (expected %d)\n", line, len(field), NumField) + } + x, err := strconv.ParseUint(field[FCodePoint], 16, 64) + point := int(x) + if err != nil { + logger.Fatalf("%.5s...: %s", line, err) + } + if point == 0 { + return // not interesting and we use 0 as unset + } + if point > MaxChar { + logger.Fatalf("%5s: Rune %X > MaxChar (%X)", line, point, MaxChar) + return + } + state := SNormal + switch { + case strings.Index(field[FName], ", First>") > 0: + state = SFirst + case strings.Index(field[FName], ", Last>") > 0: + state = SLast + } + firstChar := lastChar + 1 + lastChar = rune(point) + if state != SLast { + firstChar = lastChar + } + x, err = strconv.ParseUint(field[FCanonicalCombiningClass], 10, 64) + if err != nil { + logger.Fatalf("%U: bad ccc field: %s", int(x), err) + } + ccc := uint8(x) + decmap := field[FDecompMapping] + exp, e := parseDecomposition(decmap, false) + isCompat := false + if e != nil { + if len(decmap) > 0 { + exp, e = parseDecomposition(decmap, true) + if e != nil { + logger.Fatalf(`%U: bad decomp |%v|: "%s"`, int(x), decmap, e) + } + isCompat = true + } + } + for i := firstChar; i <= lastChar; i++ { + char := &chars[i] + char.name = field[FName] + char.codePoint = i + char.forms[FCompatibility].decomp = exp + if !isCompat { + char.forms[FCanonical].decomp = exp + } else { + char.compatDecomp = true + } + if len(decmap) > 0 { + char.forms[FCompatibility].decomp = exp + } + char.ccc = ccc + char.state = SMissing + if i == lastChar { + char.state = state + } + } + return +} + +func loadUnicodeData() { + f := openReader("UnicodeData.txt") + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + parseCharacter(scanner.Text()) + } + if scanner.Err() != nil { + logger.Fatal(scanner.Err()) + } +} + +var singlePointRe = regexp.MustCompile(`^([0-9A-F]+) *$`) + +// CompositionExclusions.txt has form: +// 0958 # ... +// See http://unicode.org/reports/tr44/ for full explanation +func parseExclusion(line string) int { + comment := strings.Index(line, "#") + if comment >= 0 { + line = line[0:comment] + } + if len(line) == 0 { + return 0 + } + matches := singlePointRe.FindStringSubmatch(line) + if len(matches) != 2 { + logger.Fatalf("%s: %d matches (expected 1)\n", line, len(matches)) + } + point, err := strconv.ParseUint(matches[1], 16, 64) + if err != nil { + logger.Fatalf("%.5s...: %s", line, err) + } + return int(point) +} + +func loadCompositionExclusions() { + f := openReader("CompositionExclusions.txt") + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + point := parseExclusion(scanner.Text()) + if point == 0 { + continue + } + c := &chars[point] + if c.excludeInComp { + logger.Fatalf("%U: Duplicate entry in exclusions.", c.codePoint) + } + c.excludeInComp = true + } + if scanner.Err() != nil { + log.Fatal(scanner.Err()) + } +} + +// hasCompatDecomp returns true if any of the recursive +// decompositions contains a compatibility expansion. +// In this case, the character may not occur in NFK*. +func hasCompatDecomp(r rune) bool { + c := &chars[r] + if c.compatDecomp { + return true + } + for _, d := range c.forms[FCompatibility].decomp { + if hasCompatDecomp(d) { + return true + } + } + return false +} + +// Hangul related constants. +const ( + HangulBase = 0xAC00 + HangulEnd = 0xD7A4 // hangulBase + Jamo combinations (19 * 21 * 28) + + JamoLBase = 0x1100 + JamoLEnd = 0x1113 + JamoVBase = 0x1161 + JamoVEnd = 0x1176 + JamoTBase = 0x11A8 + JamoTEnd = 0x11C3 +) + +func isHangul(r rune) bool { + return HangulBase <= r && r < HangulEnd +} + +func ccc(r rune) uint8 { + return chars[r].ccc +} + +// Insert a rune in a buffer, ordered by Canonical Combining Class. +func insertOrdered(b Decomposition, r rune) Decomposition { + n := len(b) + b = append(b, 0) + cc := ccc(r) + if cc > 0 { + // Use bubble sort. + for ; n > 0; n-- { + if ccc(b[n-1]) <= cc { + break + } + b[n] = b[n-1] + } + } + b[n] = r + return b +} + +// Recursively decompose. +func decomposeRecursive(form int, r rune, d Decomposition) Decomposition { + if isHangul(r) { + return d + } + dcomp := chars[r].forms[form].decomp + if len(dcomp) == 0 { + return insertOrdered(d, r) + } + for _, c := range dcomp { + d = decomposeRecursive(form, c, d) + } + return d +} + +func completeCharFields(form int) { + // Phase 0: pre-expand decomposition. + for i := range chars { + f := &chars[i].forms[form] + if len(f.decomp) == 0 { + continue + } + exp := make(Decomposition, 0) + for _, c := range f.decomp { + exp = decomposeRecursive(form, c, exp) + } + f.expandedDecomp = exp + } + + // Phase 1: composition exclusion, mark decomposition. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + // Marks script-specific exclusions and version restricted. + f.isOneWay = c.excludeInComp + + // Singletons + f.isOneWay = f.isOneWay || len(f.decomp) == 1 + + // Non-starter decompositions + if len(f.decomp) > 1 { + chk := c.ccc != 0 || chars[f.decomp[0]].ccc != 0 + f.isOneWay = f.isOneWay || chk + } + + // Runes that decompose into more than two runes. + f.isOneWay = f.isOneWay || len(f.decomp) > 2 + + if form == FCompatibility { + f.isOneWay = f.isOneWay || hasCompatDecomp(c.codePoint) + } + + for _, r := range f.decomp { + chars[r].forms[form].inDecomp = true + } + } + + // Phase 2: forward and backward combining. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + if !f.isOneWay && len(f.decomp) == 2 { + f0 := &chars[f.decomp[0]].forms[form] + f1 := &chars[f.decomp[1]].forms[form] + if !f0.isOneWay { + f0.combinesForward = true + } + if !f1.isOneWay { + f1.combinesBackward = true + } + } + } + + // Phase 3: quick check values. + for i := range chars { + c := &chars[i] + f := &c.forms[form] + + switch { + case len(f.decomp) > 0: + f.quickCheck[MDecomposed] = QCNo + case isHangul(rune(i)): + f.quickCheck[MDecomposed] = QCNo + default: + f.quickCheck[MDecomposed] = QCYes + } + switch { + case f.isOneWay: + f.quickCheck[MComposed] = QCNo + case (i & 0xffff00) == JamoLBase: + f.quickCheck[MComposed] = QCYes + if JamoLBase <= i && i < JamoLEnd { + f.combinesForward = true + } + if JamoVBase <= i && i < JamoVEnd { + f.quickCheck[MComposed] = QCMaybe + f.combinesBackward = true + f.combinesForward = true + } + if JamoTBase <= i && i < JamoTEnd { + f.quickCheck[MComposed] = QCMaybe + f.combinesBackward = true + } + case !f.combinesBackward: + f.quickCheck[MComposed] = QCYes + default: + f.quickCheck[MComposed] = QCMaybe + } + } +} + +func printBytes(b []byte, name string) { + fmt.Printf("// %s: %d bytes\n", name, len(b)) + fmt.Printf("var %s = [...]byte {", name) + for i, c := range b { + switch { + case i%64 == 0: + fmt.Printf("\n// Bytes %x - %x\n", i, i+63) + case i%8 == 0: + fmt.Printf("\n") + } + fmt.Printf("0x%.2X, ", c) + } + fmt.Print("\n}\n\n") +} + +// See forminfo.go for format. +func makeEntry(f *FormInfo) uint16 { + e := uint16(0) + if f.combinesForward { + e |= 0x8 + } + if f.quickCheck[MDecomposed] == QCNo { + e |= 0x1 + } + switch f.quickCheck[MComposed] { + case QCYes: + case QCNo: + e |= 0x4 + case QCMaybe: + e |= 0x6 + default: + log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed]) + } + return e +} + +// decompSet keeps track of unique decompositions, grouped by whether +// the decomposition is followed by a trailing and/or leading CCC. +type decompSet [6]map[string]bool + +const ( + normalDecomp = iota + firstMulti + firstCCC + endMulti + firstLeadingCCC + firstCCCZeroExcept + lastDecomp +) + +var cname = []string{"firstMulti", "firstCCC", "endMulti", "firstLeadingCCC", "firstCCCZeroExcept", "lastDecomp"} + +func makeDecompSet() decompSet { + m := decompSet{} + for i := range m { + m[i] = make(map[string]bool) + } + return m +} +func (m *decompSet) insert(key int, s string) { + m[key][s] = true +} + +func printCharInfoTables() int { + mkstr := func(r rune, f *FormInfo) (int, string) { + d := f.expandedDecomp + s := string([]rune(d)) + if max := 1 << 6; len(s) >= max { + const msg = "%U: too many bytes in decomposition: %d >= %d" + logger.Fatalf(msg, r, len(s), max) + } + head := uint8(len(s)) + if f.quickCheck[MComposed] != QCYes { + head |= 0x40 + } + if f.combinesForward { + head |= 0x80 + } + s = string([]byte{head}) + s + + lccc := ccc(d[0]) + tccc := ccc(d[len(d)-1]) + cc := ccc(r) + if cc != 0 && lccc == 0 && tccc == 0 { + logger.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", r, cc) + } + if tccc < lccc && lccc != 0 { + const msg = "%U: lccc (%d) must be <= tcc (%d)" + logger.Fatalf(msg, r, lccc, tccc) + } + index := normalDecomp + if tccc > 0 || lccc > 0 { + s += string([]byte{tccc}) + index = endMulti + for _, r := range d[1:] { + if ccc(r) == 0 { + index = firstCCC + } + } + if lccc > 0 { + s += string([]byte{lccc}) + if index == firstCCC { + logger.Fatalf("%U: multi-segment decomposition not supported for decompositions with leading CCC != 0", r) + } + index = firstLeadingCCC + } + if cc != lccc { + if cc != 0 { + logger.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", r, cc) + } + index = firstCCCZeroExcept + } + } else if len(d) > 1 { + index = firstMulti + } + return index, s + } + + decompSet := makeDecompSet() + + // Store the uniqued decompositions in a byte buffer, + // preceded by their byte length. + for _, c := range chars { + for _, f := range c.forms { + if len(f.expandedDecomp) == 0 { + continue + } + if f.combinesBackward { + logger.Fatalf("%U: combinesBackward and decompose", c.codePoint) + } + index, s := mkstr(c.codePoint, &f) + decompSet.insert(index, s) + } + } + + decompositions := bytes.NewBuffer(make([]byte, 0, 10000)) + size := 0 + positionMap := make(map[string]uint16) + decompositions.WriteString("\000") + fmt.Println("const (") + for i, m := range decompSet { + sa := []string{} + for s := range m { + sa = append(sa, s) + } + sort.Strings(sa) + for _, s := range sa { + p := decompositions.Len() + decompositions.WriteString(s) + positionMap[s] = uint16(p) + } + if cname[i] != "" { + fmt.Printf("%s = 0x%X\n", cname[i], decompositions.Len()) + } + } + fmt.Println("maxDecomp = 0x8000") + fmt.Println(")") + b := decompositions.Bytes() + printBytes(b, "decomps") + size += len(b) + + varnames := []string{"nfc", "nfkc"} + for i := 0; i < FNumberOfFormTypes; i++ { + trie := newNode() + for r, c := range chars { + f := c.forms[i] + d := f.expandedDecomp + if len(d) != 0 { + _, key := mkstr(c.codePoint, &f) + trie.insert(rune(r), positionMap[key]) + if c.ccc != ccc(d[0]) { + // We assume the lead ccc of a decomposition !=0 in this case. + if ccc(d[0]) == 0 { + logger.Fatalf("Expected leading CCC to be non-zero; ccc is %d", c.ccc) + } + } + } else if v := makeEntry(&f)<<8 | uint16(c.ccc); v != 0 { + trie.insert(c.codePoint, 0x8000|v) + } + } + size += trie.printTables(varnames[i]) + } + return size +} + +func contains(sa []string, s string) bool { + for _, a := range sa { + if a == s { + return true + } + } + return false +} + +// Extract the version number from the URL. +func version() string { + // From http://www.unicode.org/standard/versions/#Version_Numbering: + // for the later Unicode versions, data files are located in + // versioned directories. + fields := strings.Split(*url, "/") + for _, f := range fields { + if match, _ := regexp.MatchString(`[0-9]\.[0-9]\.[0-9]`, f); match { + return f + } + } + logger.Fatal("unknown version") + return "Unknown" +} + +const fileHeader = `// Generated by running +// maketables --tables=%s --url=%s +// DO NOT EDIT + +package norm + +` + +func makeTables() { + size := 0 + if *tablelist == "" { + return + } + list := strings.Split(*tablelist, ",") + if *tablelist == "all" { + list = []string{"recomp", "info"} + } + fmt.Printf(fileHeader, *tablelist, *url) + + fmt.Println("// Version is the Unicode edition from which the tables are derived.") + fmt.Printf("const Version = %q\n\n", version()) + + if contains(list, "info") { + size += printCharInfoTables() + } + + if contains(list, "recomp") { + // Note that we use 32 bit keys, instead of 64 bit. + // This clips the bits of three entries, but we know + // this won't cause a collision. The compiler will catch + // any changes made to UnicodeData.txt that introduces + // a collision. + // Note that the recomposition map for NFC and NFKC + // are identical. + + // Recomposition map + nrentries := 0 + for _, c := range chars { + f := c.forms[FCanonical] + if !f.isOneWay && len(f.decomp) > 0 { + nrentries++ + } + } + sz := nrentries * 8 + size += sz + fmt.Printf("// recompMap: %d bytes (entries only)\n", sz) + fmt.Println("var recompMap = map[uint32]rune{") + for i, c := range chars { + f := c.forms[FCanonical] + d := f.decomp + if !f.isOneWay && len(d) > 0 { + key := uint32(uint16(d[0]))<<16 + uint32(uint16(d[1])) + fmt.Printf("0x%.8X: 0x%.4X,\n", key, i) + } + } + fmt.Printf("}\n\n") + } + + fmt.Printf("// Total size of tables: %dKB (%d bytes)\n", (size+512)/1024, size) +} + +func printChars() { + if *verbose { + for _, c := range chars { + if !c.isValid() || c.state == SMissing { + continue + } + fmt.Println(c) + } + } +} + +// verifyComputed does various consistency tests. +func verifyComputed() { + for i, c := range chars { + for _, f := range c.forms { + isNo := (f.quickCheck[MDecomposed] == QCNo) + if (len(f.decomp) > 0) != isNo && !isHangul(rune(i)) { + log.Fatalf("%U: NF*D must be no if rune decomposes", i) + } + + isMaybe := f.quickCheck[MComposed] == QCMaybe + if f.combinesBackward != isMaybe { + log.Fatalf("%U: NF*C must be maybe if combinesBackward", i) + } + } + nfc := c.forms[FCanonical] + nfkc := c.forms[FCompatibility] + if nfc.combinesBackward != nfkc.combinesBackward { + logger.Fatalf("%U: Cannot combine combinesBackward\n", c.codePoint) + } + } +} + +var qcRe = regexp.MustCompile(`([0-9A-F\.]+) *; (NF.*_QC); ([YNM]) #.*`) + +// Use values in DerivedNormalizationProps.txt to compare against the +// values we computed. +// DerivedNormalizationProps.txt has form: +// 00C0..00C5 ; NFD_QC; N # ... +// 0374 ; NFD_QC; N # ... +// See http://unicode.org/reports/tr44/ for full explanation +func testDerived() { + if !*test { + return + } + f := openReader("DerivedNormalizationProps.txt") + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + qc := qcRe.FindStringSubmatch(line) + if qc == nil { + continue + } + rng := strings.Split(qc[1], "..") + i, err := strconv.ParseUint(rng[0], 16, 64) + if err != nil { + log.Fatal(err) + } + j := i + if len(rng) > 1 { + j, err = strconv.ParseUint(rng[1], 16, 64) + if err != nil { + log.Fatal(err) + } + } + var ftype, mode int + qt := strings.TrimSpace(qc[2]) + switch qt { + case "NFC_QC": + ftype, mode = FCanonical, MComposed + case "NFD_QC": + ftype, mode = FCanonical, MDecomposed + case "NFKC_QC": + ftype, mode = FCompatibility, MComposed + case "NFKD_QC": + ftype, mode = FCompatibility, MDecomposed + default: + log.Fatalf(`Unexpected quick check type "%s"`, qt) + } + var qr QCResult + switch qc[3] { + case "Y": + qr = QCYes + case "N": + qr = QCNo + case "M": + qr = QCMaybe + default: + log.Fatalf(`Unexpected quick check value "%s"`, qc[3]) + } + var lastFailed bool + // Verify current + for ; i <= j; i++ { + c := &chars[int(i)] + c.forms[ftype].verified[mode] = true + curqr := c.forms[ftype].quickCheck[mode] + if curqr != qr { + if !lastFailed { + logger.Printf("%s: %.4X..%.4X -- %s\n", + qt, int(i), int(j), line[0:50]) + } + logger.Printf("%U: FAILED %s (was %v need %v)\n", + int(i), qt, curqr, qr) + lastFailed = true + } + } + } + if scanner.Err() != nil { + logger.Fatal(scanner.Err()) + } + // Any unspecified value must be QCYes. Verify this. + for i, c := range chars { + for j, fd := range c.forms { + for k, qr := range fd.quickCheck { + if !fd.verified[k] && qr != QCYes { + m := "%U: FAIL F:%d M:%d (was %v need Yes) %s\n" + logger.Printf(m, i, j, k, qr, c.name) + } + } + } + } +} diff --git a/src/pkg/exp/norm/maketesttables.go b/src/pkg/exp/norm/maketesttables.go new file mode 100644 index 000000000..6d11ec069 --- /dev/null +++ b/src/pkg/exp/norm/maketesttables.go @@ -0,0 +1,45 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Generate test data for trie code. + +package main + +import ( + "fmt" +) + +func main() { + printTestTables() +} + +// We take the smallest, largest and an arbitrary value for each +// of the UTF-8 sequence lengths. +var testRunes = []rune{ + 0x01, 0x0C, 0x7F, // 1-byte sequences + 0x80, 0x100, 0x7FF, // 2-byte sequences + 0x800, 0x999, 0xFFFF, // 3-byte sequences + 0x10000, 0x10101, 0x10FFFF, // 4-byte sequences + 0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block +} + +const fileHeader = `// Generated by running +// maketesttables +// DO NOT EDIT + +package norm + +` + +func printTestTables() { + fmt.Print(fileHeader) + fmt.Printf("var testRunes = %#v\n\n", testRunes) + t := newNode() + for i, r := range testRunes { + t.insert(r, uint16(i)) + } + t.printTables("testdata") +} diff --git a/src/pkg/exp/norm/norm_test.go b/src/pkg/exp/norm/norm_test.go new file mode 100644 index 000000000..12dacfcf3 --- /dev/null +++ b/src/pkg/exp/norm/norm_test.go @@ -0,0 +1,14 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm_test + +import ( + "testing" +) + +func TestPlaceHolder(t *testing.T) { + // Does nothing, just allows the Makefile to be canonical + // while waiting for the package itself to be written. +} diff --git a/src/pkg/exp/norm/normalize.go b/src/pkg/exp/norm/normalize.go new file mode 100644 index 000000000..1c3e49b77 --- /dev/null +++ b/src/pkg/exp/norm/normalize.go @@ -0,0 +1,478 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package norm contains types and functions for normalizing Unicode strings. +package norm + +import "unicode/utf8" + +// A Form denotes a canonical representation of Unicode code points. +// The Unicode-defined normalization and equivalence forms are: +// +// NFC Unicode Normalization Form C +// NFD Unicode Normalization Form D +// NFKC Unicode Normalization Form KC +// NFKD Unicode Normalization Form KD +// +// For a Form f, this documentation uses the notation f(x) to mean +// the bytes or string x converted to the given form. +// A position n in x is called a boundary if conversion to the form can +// proceed independently on both sides: +// f(x) == append(f(x[0:n]), f(x[n:])...) +// +// References: http://unicode.org/reports/tr15/ and +// http://unicode.org/notes/tn5/. +type Form int + +const ( + NFC Form = iota + NFD + NFKC + NFKD +) + +// Bytes returns f(b). May return b if f(b) = b. +func (f Form) Bytes(b []byte) []byte { + rb := reorderBuffer{} + rb.init(f, b) + n := quickSpan(&rb, 0) + if n == len(b) { + return b + } + out := make([]byte, n, len(b)) + copy(out, b[0:n]) + return doAppend(&rb, out, n) +} + +// String returns f(s). +func (f Form) String(s string) string { + rb := reorderBuffer{} + rb.initString(f, s) + n := quickSpan(&rb, 0) + if n == len(s) { + return s + } + out := make([]byte, n, len(s)) + copy(out, s[0:n]) + return string(doAppend(&rb, out, n)) +} + +// IsNormal returns true if b == f(b). +func (f Form) IsNormal(b []byte) bool { + rb := reorderBuffer{} + rb.init(f, b) + bp := quickSpan(&rb, 0) + if bp == len(b) { + return true + } + for bp < len(b) { + decomposeSegment(&rb, bp) + if rb.f.composing { + rb.compose() + } + for i := 0; i < rb.nrune; i++ { + info := rb.rune[i] + if bp+int(info.size) > len(b) { + return false + } + p := info.pos + pe := p + info.size + for ; p < pe; p++ { + if b[bp] != rb.byte[p] { + return false + } + bp++ + } + } + rb.reset() + bp = quickSpan(&rb, bp) + } + return true +} + +// IsNormalString returns true if s == f(s). +func (f Form) IsNormalString(s string) bool { + rb := reorderBuffer{} + rb.initString(f, s) + bp := quickSpan(&rb, 0) + if bp == len(s) { + return true + } + for bp < len(s) { + decomposeSegment(&rb, bp) + if rb.f.composing { + rb.compose() + } + for i := 0; i < rb.nrune; i++ { + info := rb.rune[i] + if bp+int(info.size) > len(s) { + return false + } + p := info.pos + pe := p + info.size + for ; p < pe; p++ { + if s[bp] != rb.byte[p] { + return false + } + bp++ + } + } + rb.reset() + bp = quickSpan(&rb, bp) + } + return true +} + +// patchTail fixes a case where a rune may be incorrectly normalized +// if it is followed by illegal continuation bytes. It returns the +// patched buffer and whether there were trailing continuation bytes. +func patchTail(rb *reorderBuffer, buf []byte) ([]byte, bool) { + info, p := lastRuneStart(&rb.f, buf) + if p == -1 || info.size == 0 { + return buf, false + } + end := p + int(info.size) + extra := len(buf) - end + if extra > 0 { + // Potentially allocating memory. However, this only + // happens with ill-formed UTF-8. + x := make([]byte, 0) + x = append(x, buf[len(buf)-extra:]...) + buf = decomposeToLastBoundary(rb, buf[:end]) + if rb.f.composing { + rb.compose() + } + buf = rb.flush(buf) + return append(buf, x...), true + } + return buf, false +} + +func appendQuick(rb *reorderBuffer, dst []byte, i int) ([]byte, int) { + if rb.nsrc == i { + return dst, i + } + end := quickSpan(rb, i) + return rb.src.appendSlice(dst, i, end), end +} + +// Append returns f(append(out, b...)). +// The buffer out must be nil, empty, or equal to f(out). +func (f Form) Append(out []byte, src ...byte) []byte { + if len(src) == 0 { + return out + } + rb := reorderBuffer{} + rb.init(f, src) + return doAppend(&rb, out, 0) +} + +func doAppend(rb *reorderBuffer, out []byte, p int) []byte { + src, n := rb.src, rb.nsrc + doMerge := len(out) > 0 + if q := src.skipNonStarter(p); q > p { + // Move leading non-starters to destination. + out = src.appendSlice(out, p, q) + buf, endsInError := patchTail(rb, out) + if endsInError { + out = buf + doMerge = false // no need to merge, ends with illegal UTF-8 + } else { + out = decomposeToLastBoundary(rb, buf) // force decomposition + } + p = q + } + fd := &rb.f + if doMerge { + var info Properties + if p < n { + info = fd.info(src, p) + if p == 0 && !info.BoundaryBefore() { + out = decomposeToLastBoundary(rb, out) + } + } + if info.size == 0 || info.BoundaryBefore() { + if fd.composing { + rb.compose() + } + out = rb.flush(out) + if info.size == 0 { + // Append incomplete UTF-8 encoding. + return src.appendSlice(out, p, n) + } + } + } + if rb.nrune == 0 { + out, p = appendQuick(rb, out, p) + } + for p < n { + p = decomposeSegment(rb, p) + if fd.composing { + rb.compose() + } + out = rb.flush(out) + out, p = appendQuick(rb, out, p) + } + return out +} + +// AppendString returns f(append(out, []byte(s))). +// The buffer out must be nil, empty, or equal to f(out). +func (f Form) AppendString(out []byte, src string) []byte { + if len(src) == 0 { + return out + } + rb := reorderBuffer{} + rb.initString(f, src) + return doAppend(&rb, out, 0) +} + +// QuickSpan returns a boundary n such that b[0:n] == f(b[0:n]). +// It is not guaranteed to return the largest such n. +func (f Form) QuickSpan(b []byte) int { + rb := reorderBuffer{} + rb.init(f, b) + n := quickSpan(&rb, 0) + return n +} + +func quickSpan(rb *reorderBuffer, i int) int { + var lastCC uint8 + var nc int + lastSegStart := i + src, n := rb.src, rb.nsrc + for i < n { + if j := src.skipASCII(i, n); i != j { + i = j + lastSegStart = i - 1 + lastCC = 0 + nc = 0 + continue + } + info := rb.f.info(src, i) + if info.size == 0 { + // include incomplete runes + return n + } + cc := info.ccc + if rb.f.composing { + if !info.isYesC() { + break + } + } else { + if !info.isYesD() { + break + } + } + if cc == 0 { + lastSegStart = i + nc = 0 + } else { + if nc >= maxCombiningChars { + lastSegStart = i + lastCC = cc + nc = 1 + } else { + if lastCC > cc { + return lastSegStart + } + nc++ + } + } + lastCC = cc + i += int(info.size) + } + if i == n { + return n + } + if rb.f.composing { + return lastSegStart + } + return i +} + +// QuickSpanString returns a boundary n such that b[0:n] == f(s[0:n]). +// It is not guaranteed to return the largest such n. +func (f Form) QuickSpanString(s string) int { + rb := reorderBuffer{} + rb.initString(f, s) + return quickSpan(&rb, 0) +} + +// FirstBoundary returns the position i of the first boundary in b +// or -1 if b contains no boundary. +func (f Form) FirstBoundary(b []byte) int { + rb := reorderBuffer{} + rb.init(f, b) + return firstBoundary(&rb) +} + +func firstBoundary(rb *reorderBuffer) int { + src, nsrc := rb.src, rb.nsrc + i := src.skipNonStarter(0) + if i >= nsrc { + return -1 + } + fd := &rb.f + info := fd.info(src, i) + for n := 0; info.size != 0 && !info.BoundaryBefore(); { + i += int(info.size) + if n++; n >= maxCombiningChars { + return i + } + if i >= nsrc { + if !info.BoundaryAfter() { + return -1 + } + return nsrc + } + info = fd.info(src, i) + } + if info.size == 0 { + return -1 + } + return i +} + +// FirstBoundaryInString returns the position i of the first boundary in s +// or -1 if s contains no boundary. +func (f Form) FirstBoundaryInString(s string) int { + rb := reorderBuffer{} + rb.initString(f, s) + return firstBoundary(&rb) +} + +// LastBoundary returns the position i of the last boundary in b +// or -1 if b contains no boundary. +func (f Form) LastBoundary(b []byte) int { + return lastBoundary(formTable[f], b) +} + +func lastBoundary(fd *formInfo, b []byte) int { + i := len(b) + info, p := lastRuneStart(fd, b) + if p == -1 { + return -1 + } + if info.size == 0 { // ends with incomplete rune + if p == 0 { // starts with incomplete rune + return -1 + } + i = p + info, p = lastRuneStart(fd, b[:i]) + if p == -1 { // incomplete UTF-8 encoding or non-starter bytes without a starter + return i + } + } + if p+int(info.size) != i { // trailing non-starter bytes: illegal UTF-8 + return i + } + if info.BoundaryAfter() { + return i + } + i = p + for n := 0; i >= 0 && !info.BoundaryBefore(); { + info, p = lastRuneStart(fd, b[:i]) + if n++; n >= maxCombiningChars { + return len(b) + } + if p+int(info.size) != i { + if p == -1 { // no boundary found + return -1 + } + return i // boundary after an illegal UTF-8 encoding + } + i = p + } + return i +} + +// decomposeSegment scans the first segment in src into rb. +// It returns the number of bytes consumed from src. +// TODO(mpvl): consider inserting U+034f (Combining Grapheme Joiner) +// when we detect a sequence of 30+ non-starter chars. +func decomposeSegment(rb *reorderBuffer, sp int) int { + // Force one character to be consumed. + info := rb.f.info(rb.src, sp) + if info.size == 0 { + return 0 + } + for rb.insert(rb.src, sp, info) { + sp += int(info.size) + if sp >= rb.nsrc { + break + } + info = rb.f.info(rb.src, sp) + bound := info.BoundaryBefore() + if bound || info.size == 0 { + break + } + } + return sp +} + +// lastRuneStart returns the runeInfo and position of the last +// rune in buf or the zero runeInfo and -1 if no rune was found. +func lastRuneStart(fd *formInfo, buf []byte) (Properties, int) { + p := len(buf) - 1 + for ; p >= 0 && !utf8.RuneStart(buf[p]); p-- { + } + if p < 0 { + return Properties{}, -1 + } + return fd.info(inputBytes(buf), p), p +} + +// decomposeToLastBoundary finds an open segment at the end of the buffer +// and scans it into rb. Returns the buffer minus the last segment. +func decomposeToLastBoundary(rb *reorderBuffer, buf []byte) []byte { + fd := &rb.f + info, i := lastRuneStart(fd, buf) + if int(info.size) != len(buf)-i { + // illegal trailing continuation bytes + return buf + } + if info.BoundaryAfter() { + return buf + } + var add [maxBackRunes]Properties // stores runeInfo in reverse order + add[0] = info + padd := 1 + n := 1 + p := len(buf) - int(info.size) + for ; p >= 0 && !info.BoundaryBefore(); p -= int(info.size) { + info, i = lastRuneStart(fd, buf[:p]) + if int(info.size) != p-i { + break + } + // Check that decomposition doesn't result in overflow. + if info.hasDecomposition() { + if isHangul(buf) { + i += int(info.size) + n++ + } else { + dcomp := info.Decomposition() + for i := 0; i < len(dcomp); { + inf := rb.f.info(inputBytes(dcomp), i) + i += int(inf.size) + n++ + } + } + } else { + n++ + } + if n > maxBackRunes { + break + } + add[padd] = info + padd++ + } + pp := p + for padd--; padd >= 0; padd-- { + info = add[padd] + rb.insert(inputBytes(buf), pp, info) + pp += int(info.size) + } + return buf[:p] +} diff --git a/src/pkg/exp/norm/normalize_test.go b/src/pkg/exp/norm/normalize_test.go new file mode 100644 index 000000000..9a6b46e41 --- /dev/null +++ b/src/pkg/exp/norm/normalize_test.go @@ -0,0 +1,750 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "bytes" + "io" + "strings" + "testing" +) + +type PositionTest struct { + input string + pos int + buffer string // expected contents of reorderBuffer, if applicable +} + +type positionFunc func(rb *reorderBuffer, s string) int + +func runPosTests(t *testing.T, name string, f Form, fn positionFunc, tests []PositionTest) { + rb := reorderBuffer{} + rb.init(f, nil) + for i, test := range tests { + rb.reset() + rb.src = inputString(test.input) + rb.nsrc = len(test.input) + pos := fn(&rb, test.input) + if pos != test.pos { + t.Errorf("%s:%d: position is %d; want %d", name, i, pos, test.pos) + } + runes := []rune(test.buffer) + if rb.nrune != len(runes) { + t.Errorf("%s:%d: reorder buffer length is %d; want %d", name, i, rb.nrune, len(runes)) + continue + } + for j, want := range runes { + found := rune(rb.runeAt(j)) + if found != want { + t.Errorf("%s:%d: rune at %d is %U; want %U", name, i, j, found, want) + } + } + } +} + +var decomposeSegmentTests = []PositionTest{ + // illegal runes + {"\xC0", 0, ""}, + {"\u00E0\x80", 2, "\u0061\u0300"}, + // starter + {"a", 1, "a"}, + {"ab", 1, "a"}, + // starter + composing + {"a\u0300", 3, "a\u0300"}, + {"a\u0300b", 3, "a\u0300"}, + // with decomposition + {"\u00C0", 2, "A\u0300"}, + {"\u00C0b", 2, "A\u0300"}, + // long + {strings.Repeat("\u0300", 31), 62, strings.Repeat("\u0300", 31)}, + // ends with incomplete UTF-8 encoding + {"\xCC", 0, ""}, + {"\u0300\xCC", 2, "\u0300"}, +} + +func decomposeSegmentF(rb *reorderBuffer, s string) int { + rb.src = inputString(s) + rb.nsrc = len(s) + return decomposeSegment(rb, 0) +} + +func TestDecomposeSegment(t *testing.T) { + runPosTests(t, "TestDecomposeSegment", NFC, decomposeSegmentF, decomposeSegmentTests) +} + +var firstBoundaryTests = []PositionTest{ + // no boundary + {"", -1, ""}, + {"\u0300", -1, ""}, + {"\x80\x80", -1, ""}, + // illegal runes + {"\xff", 0, ""}, + {"\u0300\xff", 2, ""}, + {"\u0300\xc0\x80\x80", 2, ""}, + // boundaries + {"a", 0, ""}, + {"\u0300a", 2, ""}, + // Hangul + {"\u1103\u1161", 0, ""}, + {"\u110B\u1173\u11B7", 0, ""}, + {"\u1161\u110B\u1173\u11B7", 3, ""}, + {"\u1173\u11B7\u1103\u1161", 6, ""}, + // too many combining characters. + {strings.Repeat("\u0300", maxCombiningChars-1), -1, ""}, + {strings.Repeat("\u0300", maxCombiningChars), 60, ""}, + {strings.Repeat("\u0300", maxCombiningChars+1), 60, ""}, +} + +func firstBoundaryF(rb *reorderBuffer, s string) int { + return rb.f.form.FirstBoundary([]byte(s)) +} + +func firstBoundaryStringF(rb *reorderBuffer, s string) int { + return rb.f.form.FirstBoundaryInString(s) +} + +func TestFirstBoundary(t *testing.T) { + runPosTests(t, "TestFirstBoundary", NFC, firstBoundaryF, firstBoundaryTests) + runPosTests(t, "TestFirstBoundaryInString", NFC, firstBoundaryStringF, firstBoundaryTests) +} + +var decomposeToLastTests = []PositionTest{ + // ends with inert character + {"Hello!", 6, ""}, + {"\u0632", 2, ""}, + {"a\u0301\u0635", 5, ""}, + // ends with non-inert starter + {"a", 0, "a"}, + {"a\u0301a", 3, "a"}, + {"a\u0301\u03B9", 3, "\u03B9"}, + {"a\u0327", 0, "a\u0327"}, + // illegal runes + {"\xFF", 1, ""}, + {"aa\xFF", 3, ""}, + {"\xC0\x80\x80", 3, ""}, + {"\xCC\x80\x80", 3, ""}, + // ends with incomplete UTF-8 encoding + {"a\xCC", 2, ""}, + // ends with combining characters + {"\u0300\u0301", 0, "\u0300\u0301"}, + {"a\u0300\u0301", 0, "a\u0300\u0301"}, + {"a\u0301\u0308", 0, "a\u0301\u0308"}, + {"a\u0308\u0301", 0, "a\u0308\u0301"}, + {"aaaa\u0300\u0301", 3, "a\u0300\u0301"}, + {"\u0300a\u0300\u0301", 2, "a\u0300\u0301"}, + {"\u00C0", 0, "A\u0300"}, + {"a\u00C0", 1, "A\u0300"}, + // decomposing + {"a\u0300\uFDC0", 3, "\u0645\u062C\u064A"}, + {"\uFDC0" + strings.Repeat("\u0300", 26), 0, "\u0645\u062C\u064A" + strings.Repeat("\u0300", 26)}, + // Hangul + {"a\u1103", 1, "\u1103"}, + {"a\u110B", 1, "\u110B"}, + {"a\u110B\u1173", 1, "\u110B\u1173"}, + // See comment in composition.go:compBoundaryAfter. + {"a\u110B\u1173\u11B7", 1, "\u110B\u1173\u11B7"}, + {"a\uC73C", 1, "\u110B\u1173"}, + {"다음", 3, "\u110B\u1173\u11B7"}, + {"다", 0, "\u1103\u1161"}, + {"\u1103\u1161\u110B\u1173\u11B7", 6, "\u110B\u1173\u11B7"}, + {"\u110B\u1173\u11B7\u1103\u1161", 9, "\u1103\u1161"}, + {"다음음", 6, "\u110B\u1173\u11B7"}, + {"음다다", 6, "\u1103\u1161"}, + // buffer overflow + {"a" + strings.Repeat("\u0300", 30), 3, strings.Repeat("\u0300", 29)}, + {"\uFDFA" + strings.Repeat("\u0300", 14), 3, strings.Repeat("\u0300", 14)}, + // weird UTF-8 + {"a\u0300\u11B7", 0, "a\u0300\u11B7"}, +} + +func decomposeToLast(rb *reorderBuffer, s string) int { + buf := decomposeToLastBoundary(rb, []byte(s)) + return len(buf) +} + +func TestDecomposeToLastBoundary(t *testing.T) { + runPosTests(t, "TestDecomposeToLastBoundary", NFKC, decomposeToLast, decomposeToLastTests) +} + +var lastBoundaryTests = []PositionTest{ + // ends with inert character + {"Hello!", 6, ""}, + {"\u0632", 2, ""}, + // ends with non-inert starter + {"a", 0, ""}, + // illegal runes + {"\xff", 1, ""}, + {"aa\xff", 3, ""}, + {"a\xff\u0300", 1, ""}, + {"\xc0\x80\x80", 3, ""}, + {"\xc0\x80\x80\u0300", 3, ""}, + // ends with incomplete UTF-8 encoding + {"\xCC", -1, ""}, + {"\xE0\x80", -1, ""}, + {"\xF0\x80\x80", -1, ""}, + {"a\xCC", 0, ""}, + {"\x80\xCC", 1, ""}, + {"\xCC\xCC", 1, ""}, + // ends with combining characters + {"a\u0300\u0301", 0, ""}, + {"aaaa\u0300\u0301", 3, ""}, + {"\u0300a\u0300\u0301", 2, ""}, + {"\u00C0", 0, ""}, + {"a\u00C0", 1, ""}, + // decomposition may recombine + {"\u0226", 0, ""}, + // no boundary + {"", -1, ""}, + {"\u0300\u0301", -1, ""}, + {"\u0300", -1, ""}, + {"\x80\x80", -1, ""}, + {"\x80\x80\u0301", -1, ""}, + // Hangul + {"다음", 3, ""}, + {"다", 0, ""}, + {"\u1103\u1161\u110B\u1173\u11B7", 6, ""}, + {"\u110B\u1173\u11B7\u1103\u1161", 9, ""}, + // too many combining characters. + {strings.Repeat("\u0300", maxCombiningChars-1), -1, ""}, + {strings.Repeat("\u0300", maxCombiningChars), 60, ""}, + {strings.Repeat("\u0300", maxCombiningChars+1), 62, ""}, +} + +func lastBoundaryF(rb *reorderBuffer, s string) int { + return rb.f.form.LastBoundary([]byte(s)) +} + +func TestLastBoundary(t *testing.T) { + runPosTests(t, "TestLastBoundary", NFC, lastBoundaryF, lastBoundaryTests) +} + +var quickSpanTests = []PositionTest{ + {"", 0, ""}, + // starters + {"a", 1, ""}, + {"abc", 3, ""}, + {"\u043Eb", 3, ""}, + // incomplete last rune. + {"\xCC", 1, ""}, + {"a\xCC", 2, ""}, + // incorrectly ordered combining characters + {"\u0300\u0316", 0, ""}, + {"\u0300\u0316cd", 0, ""}, + // have a maximum number of combining characters. + {strings.Repeat("\u035D", 30) + "\u035B", 62, ""}, + {"a" + strings.Repeat("\u035D", 30) + "\u035B", 63, ""}, + {"Ɵ" + strings.Repeat("\u035D", 30) + "\u035B", 64, ""}, + {"aa" + strings.Repeat("\u035D", 30) + "\u035B", 64, ""}, +} + +var quickSpanNFDTests = []PositionTest{ + // needs decomposing + {"\u00C0", 0, ""}, + {"abc\u00C0", 3, ""}, + // correctly ordered combining characters + {"\u0300", 2, ""}, + {"ab\u0300", 4, ""}, + {"ab\u0300cd", 6, ""}, + {"\u0300cd", 4, ""}, + {"\u0316\u0300", 4, ""}, + {"ab\u0316\u0300", 6, ""}, + {"ab\u0316\u0300cd", 8, ""}, + {"ab\u0316\u0300\u00C0", 6, ""}, + {"\u0316\u0300cd", 6, ""}, + {"\u043E\u0308b", 5, ""}, + // incorrectly ordered combining characters + {"ab\u0300\u0316", 1, ""}, // TODO: we could skip 'b' as well. + {"ab\u0300\u0316cd", 1, ""}, + // Hangul + {"같은", 0, ""}, +} + +var quickSpanNFCTests = []PositionTest{ + // okay composed + {"\u00C0", 2, ""}, + {"abc\u00C0", 5, ""}, + // correctly ordered combining characters + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u00C0\u035D", 4, ""}, + // we do not special case leading combining characters + {"\u0300cd", 0, ""}, + {"\u0300", 0, ""}, + {"\u0316\u0300", 0, ""}, + {"\u0316\u0300cd", 0, ""}, + // incorrectly ordered combining characters + {"ab\u0300\u0316", 1, ""}, + {"ab\u0300\u0316cd", 1, ""}, + // Hangul + {"같은", 6, ""}, +} + +func doQuickSpan(rb *reorderBuffer, s string) int { + return rb.f.form.QuickSpan([]byte(s)) +} + +func doQuickSpanString(rb *reorderBuffer, s string) int { + return rb.f.form.QuickSpanString(s) +} + +func TestQuickSpan(t *testing.T) { + runPosTests(t, "TestQuickSpanNFD1", NFD, doQuickSpan, quickSpanTests) + runPosTests(t, "TestQuickSpanNFD2", NFD, doQuickSpan, quickSpanNFDTests) + runPosTests(t, "TestQuickSpanNFC1", NFC, doQuickSpan, quickSpanTests) + runPosTests(t, "TestQuickSpanNFC2", NFC, doQuickSpan, quickSpanNFCTests) + + runPosTests(t, "TestQuickSpanStringNFD1", NFD, doQuickSpanString, quickSpanTests) + runPosTests(t, "TestQuickSpanStringNFD2", NFD, doQuickSpanString, quickSpanNFDTests) + runPosTests(t, "TestQuickSpanStringNFC1", NFC, doQuickSpanString, quickSpanTests) + runPosTests(t, "TestQuickSpanStringNFC2", NFC, doQuickSpanString, quickSpanNFCTests) +} + +var isNormalTests = []PositionTest{ + {"", 1, ""}, + // illegal runes + {"\xff", 1, ""}, + // starters + {"a", 1, ""}, + {"abc", 1, ""}, + {"\u043Eb", 1, ""}, + // incorrectly ordered combining characters + {"\u0300\u0316", 0, ""}, + {"ab\u0300\u0316", 0, ""}, + {"ab\u0300\u0316cd", 0, ""}, + {"\u0300\u0316cd", 0, ""}, +} +var isNormalNFDTests = []PositionTest{ + // needs decomposing + {"\u00C0", 0, ""}, + {"abc\u00C0", 0, ""}, + // correctly ordered combining characters + {"\u0300", 1, ""}, + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"\u0300cd", 1, ""}, + {"\u0316\u0300", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u0316\u0300cd", 1, ""}, + {"\u043E\u0308b", 1, ""}, + // Hangul + {"같은", 0, ""}, +} +var isNormalNFCTests = []PositionTest{ + // okay composed + {"\u00C0", 1, ""}, + {"abc\u00C0", 1, ""}, + // need reordering + {"a\u0300", 0, ""}, + {"a\u0300cd", 0, ""}, + {"a\u0316\u0300", 0, ""}, + {"a\u0316\u0300cd", 0, ""}, + // correctly ordered combining characters + {"ab\u0300", 1, ""}, + {"ab\u0300cd", 1, ""}, + {"ab\u0316\u0300", 1, ""}, + {"ab\u0316\u0300cd", 1, ""}, + {"\u00C0\u035D", 1, ""}, + {"\u0300", 1, ""}, + {"\u0316\u0300cd", 1, ""}, + // Hangul + {"같은", 1, ""}, +} + +func isNormalF(rb *reorderBuffer, s string) int { + if rb.f.form.IsNormal([]byte(s)) { + return 1 + } + return 0 +} + +func TestIsNormal(t *testing.T) { + runPosTests(t, "TestIsNormalNFD1", NFD, isNormalF, isNormalTests) + runPosTests(t, "TestIsNormalNFD2", NFD, isNormalF, isNormalNFDTests) + runPosTests(t, "TestIsNormalNFC1", NFC, isNormalF, isNormalTests) + runPosTests(t, "TestIsNormalNFC2", NFC, isNormalF, isNormalNFCTests) +} + +type AppendTest struct { + left string + right string + out string +} + +type appendFunc func(f Form, out []byte, s string) []byte + +func runAppendTests(t *testing.T, name string, f Form, fn appendFunc, tests []AppendTest) { + for i, test := range tests { + out := []byte(test.left) + out = fn(f, out, test.right) + outs := string(out) + if len(outs) != len(test.out) { + t.Errorf("%s:%d: length is %d; want %d", name, i, len(outs), len(test.out)) + } + if outs != test.out { + // Find first rune that differs and show context. + ir := []rune(outs) + ig := []rune(test.out) + for j := 0; j < len(ir) && j < len(ig); j++ { + if ir[j] == ig[j] { + continue + } + if j -= 3; j < 0 { + j = 0 + } + for e := j + 7; j < e && j < len(ir) && j < len(ig); j++ { + t.Errorf("%s:%d: runeAt(%d) = %U; want %U", name, i, j, ir[j], ig[j]) + } + break + } + } + } +} + +var appendTests = []AppendTest{ + // empty buffers + {"", "", ""}, + {"a", "", "a"}, + {"", "a", "a"}, + {"", "\u0041\u0307\u0304", "\u01E0"}, + // segment split across buffers + {"", "a\u0300b", "\u00E0b"}, + {"a", "\u0300b", "\u00E0b"}, + {"a", "\u0300\u0316", "\u00E0\u0316"}, + {"a", "\u0316\u0300", "\u00E0\u0316"}, + {"a", "\u0300a\u0300", "\u00E0\u00E0"}, + {"a", "\u0300a\u0300a\u0300", "\u00E0\u00E0\u00E0"}, + {"a", "\u0300aaa\u0300aaa\u0300", "\u00E0aa\u00E0aa\u00E0"}, + {"a\u0300", "\u0327", "\u00E0\u0327"}, + {"a\u0327", "\u0300", "\u00E0\u0327"}, + {"a\u0316", "\u0300", "\u00E0\u0316"}, + {"\u0041\u0307", "\u0304", "\u01E0"}, + // Hangul + {"", "\u110B\u1173", "\uC73C"}, + {"", "\u1103\u1161", "\uB2E4"}, + {"", "\u110B\u1173\u11B7", "\uC74C"}, + {"", "\u320E", "\x28\uAC00\x29"}, + {"", "\x28\u1100\u1161\x29", "\x28\uAC00\x29"}, + {"\u1103", "\u1161", "\uB2E4"}, + {"\u110B", "\u1173\u11B7", "\uC74C"}, + {"\u110B\u1173", "\u11B7", "\uC74C"}, + {"\uC73C", "\u11B7", "\uC74C"}, + // UTF-8 encoding split across buffers + {"a\xCC", "\x80", "\u00E0"}, + {"a\xCC", "\x80b", "\u00E0b"}, + {"a\xCC", "\x80a\u0300", "\u00E0\u00E0"}, + {"a\xCC", "\x80\x80", "\u00E0\x80"}, + {"a\xCC", "\x80\xCC", "\u00E0\xCC"}, + {"a\u0316\xCC", "\x80a\u0316\u0300", "\u00E0\u0316\u00E0\u0316"}, + // ending in incomplete UTF-8 encoding + {"", "\xCC", "\xCC"}, + {"a", "\xCC", "a\xCC"}, + {"a", "b\xCC", "ab\xCC"}, + {"\u0226", "\xCC", "\u0226\xCC"}, + // illegal runes + {"", "\x80", "\x80"}, + {"", "\x80\x80\x80", "\x80\x80\x80"}, + {"", "\xCC\x80\x80\x80", "\xCC\x80\x80\x80"}, + {"", "a\x80", "a\x80"}, + {"", "a\x80\x80\x80", "a\x80\x80\x80"}, + {"", "a\x80\x80\x80\x80\x80\x80", "a\x80\x80\x80\x80\x80\x80"}, + {"a", "\x80\x80\x80", "a\x80\x80\x80"}, + // overflow + {"", strings.Repeat("\x80", 33), strings.Repeat("\x80", 33)}, + {strings.Repeat("\x80", 33), "", strings.Repeat("\x80", 33)}, + {strings.Repeat("\x80", 33), strings.Repeat("\x80", 33), strings.Repeat("\x80", 66)}, + // overflow of combining characters + {strings.Repeat("\u0300", 33), "", strings.Repeat("\u0300", 33)}, + // weird UTF-8 + {"\u00E0\xE1", "\x86", "\u00E0\xE1\x86"}, + {"a\u0300\u11B7", "\u0300", "\u00E0\u11B7\u0300"}, + {"a\u0300\u11B7\u0300", "\u0300", "\u00E0\u11B7\u0300\u0300"}, + {"\u0300", "\xF8\x80\x80\x80\x80\u0300", "\u0300\xF8\x80\x80\x80\x80\u0300"}, + {"\u0300", "\xFC\x80\x80\x80\x80\x80\u0300", "\u0300\xFC\x80\x80\x80\x80\x80\u0300"}, + {"\xF8\x80\x80\x80\x80\u0300", "\u0300", "\xF8\x80\x80\x80\x80\u0300\u0300"}, + {"\xFC\x80\x80\x80\x80\x80\u0300", "\u0300", "\xFC\x80\x80\x80\x80\x80\u0300\u0300"}, + {"\xF8\x80\x80\x80", "\x80\u0300\u0300", "\xF8\x80\x80\x80\x80\u0300\u0300"}, +} + +func appendF(f Form, out []byte, s string) []byte { + return f.Append(out, []byte(s)...) +} + +func appendStringF(f Form, out []byte, s string) []byte { + return f.AppendString(out, s) +} + +func bytesF(f Form, out []byte, s string) []byte { + buf := []byte{} + buf = append(buf, out...) + buf = append(buf, s...) + return f.Bytes(buf) +} + +func stringF(f Form, out []byte, s string) []byte { + outs := string(out) + s + return []byte(f.String(outs)) +} + +func TestAppend(t *testing.T) { + runAppendTests(t, "TestAppend", NFKC, appendF, appendTests) + runAppendTests(t, "TestAppendString", NFKC, appendStringF, appendTests) + runAppendTests(t, "TestBytes", NFKC, bytesF, appendTests) + runAppendTests(t, "TestString", NFKC, stringF, appendTests) +} + +func appendBench(f Form, in []byte) func() { + buf := make([]byte, 0, 4*len(in)) + return func() { + f.Append(buf, in...) + } +} + +func iterBench(f Form, in []byte) func() { + iter := Iter{} + return func() { + iter.Init(f, in) + for !iter.Done() { + iter.Next() + } + } +} + +func readerBench(f Form, in []byte) func() { + buf := make([]byte, 4*len(in)) + return func() { + r := f.Reader(bytes.NewReader(in)) + var err error + for err == nil { + _, err = r.Read(buf) + } + if err != io.EOF { + panic("") + } + } +} + +func writerBench(f Form, in []byte) func() { + buf := make([]byte, 0, 4*len(in)) + return func() { + r := f.Writer(bytes.NewBuffer(buf)) + if _, err := r.Write(in); err != nil { + panic("") + } + } +} + +func appendBenchmarks(bm []func(), f Form, in []byte) []func() { + //bm = append(bm, appendBench(f, in)) + bm = append(bm, iterBench(f, in)) + //bm = append(bm, readerBench(f, in)) + //bm = append(bm, writerBench(f, in)) + return bm +} + +func doFormBenchmark(b *testing.B, inf, f Form, s string) { + b.StopTimer() + in := inf.Bytes([]byte(s)) + bm := appendBenchmarks(nil, f, in) + b.SetBytes(int64(len(in) * len(bm))) + b.StartTimer() + for i := 0; i < b.N; i++ { + for _, fn := range bm { + fn() + } + } +} + +var ascii = strings.Repeat("There is nothing to change here! ", 500) + +func BenchmarkNormalizeAsciiNFC(b *testing.B) { + doFormBenchmark(b, NFC, NFC, ascii) +} +func BenchmarkNormalizeAsciiNFD(b *testing.B) { + doFormBenchmark(b, NFC, NFD, ascii) +} +func BenchmarkNormalizeAsciiNFKC(b *testing.B) { + doFormBenchmark(b, NFC, NFKC, ascii) +} +func BenchmarkNormalizeAsciiNFKD(b *testing.B) { + doFormBenchmark(b, NFC, NFKD, ascii) +} + +func BenchmarkNormalizeNFC2NFC(b *testing.B) { + doFormBenchmark(b, NFC, NFC, txt_all) +} +func BenchmarkNormalizeNFC2NFD(b *testing.B) { + doFormBenchmark(b, NFC, NFD, txt_all) +} +func BenchmarkNormalizeNFD2NFC(b *testing.B) { + doFormBenchmark(b, NFD, NFC, txt_all) +} +func BenchmarkNormalizeNFD2NFD(b *testing.B) { + doFormBenchmark(b, NFD, NFD, txt_all) +} + +// Hangul is often special-cased, so we test it separately. +func BenchmarkNormalizeHangulNFC2NFC(b *testing.B) { + doFormBenchmark(b, NFC, NFC, txt_kr) +} +func BenchmarkNormalizeHangulNFC2NFD(b *testing.B) { + doFormBenchmark(b, NFC, NFD, txt_kr) +} +func BenchmarkNormalizeHangulNFD2NFC(b *testing.B) { + doFormBenchmark(b, NFD, NFC, txt_kr) +} +func BenchmarkNormalizeHangulNFD2NFD(b *testing.B) { + doFormBenchmark(b, NFD, NFD, txt_kr) +} + +var forms = []Form{NFC, NFD, NFKC, NFKD} + +func doTextBenchmark(b *testing.B, s string) { + b.StopTimer() + in := []byte(s) + bm := []func(){} + for _, f := range forms { + bm = appendBenchmarks(bm, f, in) + } + b.SetBytes(int64(len(s) * len(bm))) + b.StartTimer() + for i := 0; i < b.N; i++ { + for _, f := range bm { + f() + } + } +} + +func BenchmarkCanonicalOrdering(b *testing.B) { + doTextBenchmark(b, txt_canon) +} +func BenchmarkExtendedLatin(b *testing.B) { + doTextBenchmark(b, txt_vn) +} +func BenchmarkMiscTwoByteUtf8(b *testing.B) { + doTextBenchmark(b, twoByteUtf8) +} +func BenchmarkMiscThreeByteUtf8(b *testing.B) { + doTextBenchmark(b, threeByteUtf8) +} +func BenchmarkHangul(b *testing.B) { + doTextBenchmark(b, txt_kr) +} +func BenchmarkJapanese(b *testing.B) { + doTextBenchmark(b, txt_jp) +} +func BenchmarkChinese(b *testing.B) { + doTextBenchmark(b, txt_cn) +} +func BenchmarkOverflow(b *testing.B) { + doTextBenchmark(b, overflow) +} + +var overflow = string(bytes.Repeat([]byte("\u035D"), 4096)) + "\u035B" + +// Tests sampled from the Canonical ordering tests (Part 2) of +// http://unicode.org/Public/UNIDATA/NormalizationTest.txt +const txt_canon = `\u0061\u0315\u0300\u05AE\u0300\u0062 \u0061\u0300\u0315\u0300\u05AE\u0062 +\u0061\u0302\u0315\u0300\u05AE\u0062 \u0061\u0307\u0315\u0300\u05AE\u0062 +\u0061\u0315\u0300\u05AE\u030A\u0062 \u0061\u059A\u0316\u302A\u031C\u0062 +\u0061\u032E\u059A\u0316\u302A\u0062 \u0061\u0338\u093C\u0334\u0062 +\u0061\u059A\u0316\u302A\u0339 \u0061\u0341\u0315\u0300\u05AE\u0062 +\u0061\u0348\u059A\u0316\u302A\u0062 \u0061\u0361\u0345\u035D\u035C\u0062 +\u0061\u0366\u0315\u0300\u05AE\u0062 \u0061\u0315\u0300\u05AE\u0486\u0062 +\u0061\u05A4\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0613\u0062 +\u0061\u0315\u0300\u05AE\u0615\u0062 \u0061\u0617\u0315\u0300\u05AE\u0062 +\u0061\u0619\u0618\u064D\u064E\u0062 \u0061\u0315\u0300\u05AE\u0654\u0062 +\u0061\u0315\u0300\u05AE\u06DC\u0062 \u0061\u0733\u0315\u0300\u05AE\u0062 +\u0061\u0744\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0745\u0062 +\u0061\u09CD\u05B0\u094D\u3099\u0062 \u0061\u0E38\u0E48\u0E38\u0C56\u0062 +\u0061\u0EB8\u0E48\u0E38\u0E49\u0062 \u0061\u0F72\u0F71\u0EC8\u0F71\u0062 +\u0061\u1039\u05B0\u094D\u3099\u0062 \u0061\u05B0\u094D\u3099\u1A60\u0062 +\u0061\u3099\u093C\u0334\u1BE6\u0062 \u0061\u3099\u093C\u0334\u1C37\u0062 +\u0061\u1CD9\u059A\u0316\u302A\u0062 \u0061\u2DED\u0315\u0300\u05AE\u0062 +\u0061\u2DEF\u0315\u0300\u05AE\u0062 \u0061\u302D\u302E\u059A\u0316\u0062` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/vn/ +const txt_vn = `Với các điều kiện sau: Ghi nhận công của tác giả. +Nếu bạn sử dụng, chuyển đổi, hoặc xây dựng dự án từ +nội dung được chia sẻ này, bạn phải áp dụng giấy phép này hoặc +một giấy phép khác có các điều khoản tương tự như giấy phép này +cho dự án của bạn. Hiểu rằng: Miễn — Bất kỳ các điều kiện nào +trên đây cũng có thể được miễn bỏ nếu bạn được sự cho phép của +người sở hữu bản quyền. Phạm vi công chúng — Khi tác phẩm hoặc +bất kỳ chương nào của tác phẩm đã trong vùng dành cho công +chúng theo quy định của pháp luật thì tình trạng của nó không +bị ảnh hưởng bởi giấy phép trong bất kỳ trường hợp nào.` + +// Taken from http://creativecommons.org/licenses/by-sa/1.0/deed.ru +const txt_ru = `При обязательном соблюдении следующих условий: +Attribution — Вы должны атрибутировать произведение (указывать +автора и источник) в порядке, предусмотренном автором или +лицензиаром (но только так, чтобы никоим образом не подразумевалось, +что они поддерживают вас или использование вами данного произведения). +Υπό τις ακόλουθες προϋποθέσεις:` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/gr/ +const txt_gr = `Αναφορά Δημιουργού — Θα πρέπει να κάνετε την αναφορά στο έργο με τον +τρόπο που έχει οριστεί από το δημιουργό ή το χορηγούντο την άδεια +(χωρίς όμως να εννοείται με οποιονδήποτε τρόπο ότι εγκρίνουν εσάς ή +τη χρήση του έργου από εσάς). Παρόμοια Διανομή — Εάν αλλοιώσετε, +τροποποιήσετε ή δημιουργήσετε περαιτέρω βασισμένοι στο έργο θα +μπορείτε να διανέμετε το έργο που θα προκύψει μόνο με την ίδια ή +παρόμοια άδεια.` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/deed.ar +const txt_ar = `بموجب الشروط التالية نسب المصنف — يجب عليك أن +تنسب العمل بالطريقة التي تحددها المؤلف أو المرخص (ولكن ليس بأي حال من +الأحوال أن توحي وتقترح بتحول أو استخدامك للعمل). +المشاركة على قدم المساواة — إذا كنت يعدل ، والتغيير ، أو الاستفادة +من هذا العمل ، قد ينتج عن توزيع العمل إلا في ظل تشابه او تطابق فى واحد +لهذا الترخيص.` + +// Taken from http://creativecommons.org/licenses/by-sa/1.0/il/ +const txt_il = `בכפוף לתנאים הבאים: ייחוס — עליך לייחס את היצירה (לתת קרדיט) באופן +המצויין על-ידי היוצר או מעניק הרישיון (אך לא בשום אופן המרמז על כך +שהם תומכים בך או בשימוש שלך ביצירה). שיתוף זהה — אם תחליט/י לשנות, +לעבד או ליצור יצירה נגזרת בהסתמך על יצירה זו, תוכל/י להפיץ את יצירתך +החדשה רק תחת אותו הרישיון או רישיון דומה לרישיון זה.` + +const twoByteUtf8 = txt_ru + txt_gr + txt_ar + txt_il + +// Taken from http://creativecommons.org/licenses/by-sa/2.0/kr/ +const txt_kr = `다음과 같은 조건을 따라야 합니다: 저작자표시 +(Attribution) — 저작자나 이용허락자가 정한 방법으로 저작물의 +원저작자를 표시하여야 합니다(그러나 원저작자가 이용자나 이용자의 +이용을 보증하거나 추천한다는 의미로 표시해서는 안됩니다). +동일조건변경허락 — 이 저작물을 이용하여 만든 이차적 저작물에는 본 +라이선스와 동일한 라이선스를 적용해야 합니다.` + +// Taken from http://creativecommons.org/licenses/by-sa/3.0/th/ +const txt_th = `ภายใต้เงื่อนไข ดังต่อไปนี้ : แสดงที่มา — คุณต้องแสดงที่ +มาของงานดังกล่าว ตามรูปแบบที่ผู้สร้างสรรค์หรือผู้อนุญาตกำหนด (แต่ +ไม่ใช่ในลักษณะที่ว่า พวกเขาสนับสนุนคุณหรือสนับสนุนการที่ +คุณนำงานไปใช้) อนุญาตแบบเดียวกัน — หากคุณดัดแปลง เปลี่ยนรูป หรื +อต่อเติมงานนี้ คุณต้องใช้สัญญาอนุญาตแบบเดียวกันหรือแบบที่เหมื +อนกับสัญญาอนุญาตที่ใช้กับงานนี้เท่านั้น` + +const threeByteUtf8 = txt_th + +// Taken from http://creativecommons.org/licenses/by-sa/2.0/jp/ +const txt_jp = `あなたの従うべき条件は以下の通りです。 +表示 — あなたは原著作者のクレジットを表示しなければなりません。 +継承 — もしあなたがこの作品を改変、変形または加工した場合、 +あなたはその結果生じた作品をこの作品と同一の許諾条件の下でのみ +頒布することができます。` + +// http://creativecommons.org/licenses/by-sa/2.5/cn/ +const txt_cn = `您可以自由: 复制、发行、展览、表演、放映、 +广播或通过信息网络传播本作品 创作演绎作品 +对本作品进行商业性使用 惟须遵守下列条件: +署名 — 您必须按照作者或者许可人指定的方式对作品进行署名。 +相同方式共享 — 如果您改变、转换本作品或者以本作品为基础进行创作, +您只能采用与本协议相同的许可协议发布基于本作品的演绎作品。` + +const txt_cjk = txt_cn + txt_jp + txt_kr +const txt_all = txt_vn + twoByteUtf8 + threeByteUtf8 + txt_cjk diff --git a/src/pkg/exp/norm/normregtest.go b/src/pkg/exp/norm/normregtest.go new file mode 100644 index 000000000..b77b5b545 --- /dev/null +++ b/src/pkg/exp/norm/normregtest.go @@ -0,0 +1,304 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "bufio" + "bytes" + "exp/norm" + "flag" + "fmt" + "log" + "net/http" + "os" + "path" + "regexp" + "runtime" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +func main() { + flag.Parse() + loadTestData() + CharacterByCharacterTests() + StandardTests() + PerformanceTest() + if errorCount == 0 { + fmt.Println("PASS") + } +} + +const file = "NormalizationTest.txt" + +var url = flag.String("url", + "http://www.unicode.org/Public/"+unicode.Version+"/ucd/"+file, + "URL of Unicode database directory") +var localFiles = flag.Bool("local", + false, + "data files have been copied to the current directory; for debugging only") + +var logger = log.New(os.Stderr, "", log.Lshortfile) + +// This regression test runs the test set in NormalizationTest.txt +// (taken from http://www.unicode.org/Public//ucd/). +// +// NormalizationTest.txt has form: +// @Part0 # Specific cases +// # +// 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE +// 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW +// +// Each test has 5 columns (c1, c2, c3, c4, c5), where +// (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1)) +// +// CONFORMANCE: +// 1. The following invariants must be true for all conformant implementations +// +// NFC +// c2 == NFC(c1) == NFC(c2) == NFC(c3) +// c4 == NFC(c4) == NFC(c5) +// +// NFD +// c3 == NFD(c1) == NFD(c2) == NFD(c3) +// c5 == NFD(c4) == NFD(c5) +// +// NFKC +// c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5) +// +// NFKD +// c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5) +// +// 2. For every code point X assigned in this version of Unicode that is not +// specifically listed in Part 1, the following invariants must be true +// for all conformant implementations: +// +// X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X) +// + +// Column types. +const ( + cRaw = iota + cNFC + cNFD + cNFKC + cNFKD + cMaxColumns +) + +// Holds data from NormalizationTest.txt +var part []Part + +type Part struct { + name string + number int + tests []Test +} + +type Test struct { + name string + partnr int + number int + r rune // used for character by character test + cols [cMaxColumns]string // Each has 5 entries, see below. +} + +func (t Test) Name() string { + if t.number < 0 { + return part[t.partnr].name + } + return fmt.Sprintf("%s:%d", part[t.partnr].name, t.number) +} + +var partRe = regexp.MustCompile(`@Part(\d) # (.*)$`) +var testRe = regexp.MustCompile(`^` + strings.Repeat(`([\dA-F ]+);`, 5) + ` # (.*)$`) + +var counter int + +// Load the data form NormalizationTest.txt +func loadTestData() { + if *localFiles { + pwd, _ := os.Getwd() + *url = "file://" + path.Join(pwd, file) + } + t := &http.Transport{} + t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) + c := &http.Client{Transport: t} + resp, err := c.Get(*url) + if err != nil { + logger.Fatal(err) + } + if resp.StatusCode != 200 { + logger.Fatal("bad GET status for "+file, resp.Status) + } + f := resp.Body + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if len(line) == 0 || line[0] == '#' { + continue + } + m := partRe.FindStringSubmatch(line) + if m != nil { + if len(m) < 3 { + logger.Fatal("Failed to parse Part: ", line) + } + i, err := strconv.Atoi(m[1]) + if err != nil { + logger.Fatal(err) + } + name := m[2] + part = append(part, Part{name: name[:len(name)-1], number: i}) + continue + } + m = testRe.FindStringSubmatch(line) + if m == nil || len(m) < 7 { + logger.Fatalf(`Failed to parse: "%s" result: %#v`, line, m) + } + test := Test{name: m[6], partnr: len(part) - 1, number: counter} + counter++ + for j := 1; j < len(m)-1; j++ { + for _, split := range strings.Split(m[j], " ") { + r, err := strconv.ParseUint(split, 16, 64) + if err != nil { + logger.Fatal(err) + } + if test.r == 0 { + // save for CharacterByCharacterTests + test.r = rune(r) + } + var buf [utf8.UTFMax]byte + sz := utf8.EncodeRune(buf[:], rune(r)) + test.cols[j-1] += string(buf[:sz]) + } + } + part := &part[len(part)-1] + part.tests = append(part.tests, test) + } + if scanner.Err() != nil { + logger.Fatal(scanner.Err()) + } +} + +var fstr = []string{"NFC", "NFD", "NFKC", "NFKD"} + +var errorCount int + +func cmpResult(t *Test, name string, f norm.Form, gold, test, result string) { + if gold != result { + errorCount++ + if errorCount > 20 { + return + } + st, sr, sg := []rune(test), []rune(result), []rune(gold) + logger.Printf("%s:%s: %s(%X)=%X; want:%X: %s", + t.Name(), name, fstr[f], st, sr, sg, t.name) + } +} + +func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bool) { + if result != want { + errorCount++ + if errorCount > 20 { + return + } + logger.Printf("%s:%s: %s(%X)=%v; want: %v", t.Name(), name, fstr[f], []rune(test), result, want) + } +} + +func doTest(t *Test, f norm.Form, gold, test string) { + result := f.Bytes([]byte(test)) + cmpResult(t, "Bytes", f, gold, test, string(result)) + sresult := f.String(test) + cmpResult(t, "String", f, gold, test, sresult) + acc := []byte{} + i := norm.Iter{} + i.InitString(f, test) + for !i.Done() { + acc = append(acc, i.Next()...) + } + cmpResult(t, "Iter.Next", f, gold, test, string(acc)) + for i := range test { + out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...) + cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out)) + } + cmpIsNormal(t, "IsNormal", f, test, f.IsNormal([]byte(test)), test == gold) +} + +func doConformanceTests(t *Test, partn int) { + for i := 0; i <= 2; i++ { + doTest(t, norm.NFC, t.cols[1], t.cols[i]) + doTest(t, norm.NFD, t.cols[2], t.cols[i]) + doTest(t, norm.NFKC, t.cols[3], t.cols[i]) + doTest(t, norm.NFKD, t.cols[4], t.cols[i]) + } + for i := 3; i <= 4; i++ { + doTest(t, norm.NFC, t.cols[3], t.cols[i]) + doTest(t, norm.NFD, t.cols[4], t.cols[i]) + doTest(t, norm.NFKC, t.cols[3], t.cols[i]) + doTest(t, norm.NFKD, t.cols[4], t.cols[i]) + } +} + +func CharacterByCharacterTests() { + tests := part[1].tests + var last rune = 0 + for i := 0; i <= len(tests); i++ { // last one is special case + var r rune + if i == len(tests) { + r = 0x2FA1E // Don't have to go to 0x10FFFF + } else { + r = tests[i].r + } + for last++; last < r; last++ { + // Check all characters that were not explicitly listed in the test. + t := &Test{partnr: 1, number: -1} + char := string(last) + doTest(t, norm.NFC, char, char) + doTest(t, norm.NFD, char, char) + doTest(t, norm.NFKC, char, char) + doTest(t, norm.NFKD, char, char) + } + if i < len(tests) { + doConformanceTests(&tests[i], 1) + } + } +} + +func StandardTests() { + for _, j := range []int{0, 2, 3} { + for _, test := range part[j].tests { + doConformanceTests(&test, j) + } + } +} + +// PerformanceTest verifies that normalization is O(n). If any of the +// code does not properly check for maxCombiningChars, normalization +// may exhibit O(n**2) behavior. +func PerformanceTest() { + runtime.GOMAXPROCS(2) + success := make(chan bool, 1) + go func() { + buf := bytes.Repeat([]byte("\u035D"), 1024*1024) + buf = append(buf, "\u035B"...) + norm.NFC.Append(nil, buf...) + success <- true + }() + timeout := time.After(1 * time.Second) + select { + case <-success: + // test completed before the timeout + case <-timeout: + errorCount++ + logger.Printf(`unexpectedly long time to complete PerformanceTest`) + } +} diff --git a/src/pkg/exp/norm/readwriter.go b/src/pkg/exp/norm/readwriter.go new file mode 100644 index 000000000..2682894de --- /dev/null +++ b/src/pkg/exp/norm/readwriter.go @@ -0,0 +1,126 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import "io" + +type normWriter struct { + rb reorderBuffer + w io.Writer + buf []byte +} + +// Write implements the standard write interface. If the last characters are +// not at a normalization boundary, the bytes will be buffered for the next +// write. The remaining bytes will be written on close. +func (w *normWriter) Write(data []byte) (n int, err error) { + // Process data in pieces to keep w.buf size bounded. + const chunk = 4000 + + for len(data) > 0 { + // Normalize into w.buf. + m := len(data) + if m > chunk { + m = chunk + } + w.rb.src = inputBytes(data[:m]) + w.rb.nsrc = m + w.buf = doAppend(&w.rb, w.buf, 0) + data = data[m:] + n += m + + // Write out complete prefix, save remainder. + // Note that lastBoundary looks back at most 30 runes. + i := lastBoundary(&w.rb.f, w.buf) + if i == -1 { + i = 0 + } + if i > 0 { + if _, err = w.w.Write(w.buf[:i]); err != nil { + break + } + bn := copy(w.buf, w.buf[i:]) + w.buf = w.buf[:bn] + } + } + return n, err +} + +// Close forces data that remains in the buffer to be written. +func (w *normWriter) Close() error { + if len(w.buf) > 0 { + _, err := w.w.Write(w.buf) + if err != nil { + return err + } + } + return nil +} + +// Writer returns a new writer that implements Write(b) +// by writing f(b) to w. The returned writer may use an +// an internal buffer to maintain state across Write calls. +// Calling its Close method writes any buffered data to w. +func (f Form) Writer(w io.Writer) io.WriteCloser { + wr := &normWriter{rb: reorderBuffer{}, w: w} + wr.rb.init(f, nil) + return wr +} + +type normReader struct { + rb reorderBuffer + r io.Reader + inbuf []byte + outbuf []byte + bufStart int + lastBoundary int + err error +} + +// Read implements the standard read interface. +func (r *normReader) Read(p []byte) (int, error) { + for { + if r.lastBoundary-r.bufStart > 0 { + n := copy(p, r.outbuf[r.bufStart:r.lastBoundary]) + r.bufStart += n + if r.lastBoundary-r.bufStart > 0 { + return n, nil + } + return n, r.err + } + if r.err != nil { + return 0, r.err + } + outn := copy(r.outbuf, r.outbuf[r.lastBoundary:]) + r.outbuf = r.outbuf[0:outn] + r.bufStart = 0 + + n, err := r.r.Read(r.inbuf) + r.rb.src = inputBytes(r.inbuf[0:n]) + r.rb.nsrc, r.err = n, err + if n > 0 { + r.outbuf = doAppend(&r.rb, r.outbuf, 0) + } + if err == io.EOF { + r.lastBoundary = len(r.outbuf) + } else { + r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf) + if r.lastBoundary == -1 { + r.lastBoundary = 0 + } + } + } + panic("should not reach here") +} + +// Reader returns a new reader that implements Read +// by reading data from r and returning f(data). +func (f Form) Reader(r io.Reader) io.Reader { + const chunk = 4000 + buf := make([]byte, chunk) + rr := &normReader{rb: reorderBuffer{}, r: r, inbuf: buf} + rr.rb.init(f, buf) + return rr +} diff --git a/src/pkg/exp/norm/readwriter_test.go b/src/pkg/exp/norm/readwriter_test.go new file mode 100644 index 000000000..3b49eb0a2 --- /dev/null +++ b/src/pkg/exp/norm/readwriter_test.go @@ -0,0 +1,68 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "bytes" + "fmt" + "strings" + "testing" +) + +var ioTests = []AppendTest{ + {"", strings.Repeat("a\u0316\u0300", 6), strings.Repeat("\u00E0\u0316", 6)}, + {"", strings.Repeat("a\u0300\u0316", 4000), strings.Repeat("\u00E0\u0316", 4000)}, + {"", strings.Repeat("\x80\x80", 4000), strings.Repeat("\x80\x80", 4000)}, + {"", "\u0041\u0307\u0304", "\u01E0"}, +} + +var bufSizes = []int{1, 2, 3, 4, 5, 6, 7, 8, 100, 101, 102, 103, 4000, 4001, 4002, 4003} + +func readFunc(size int) appendFunc { + return func(f Form, out []byte, s string) []byte { + out = append(out, s...) + r := f.Reader(bytes.NewBuffer(out)) + buf := make([]byte, size) + result := []byte{} + for n, err := 0, error(nil); err == nil; { + n, err = r.Read(buf) + result = append(result, buf[:n]...) + } + return result + } +} + +func TestReader(t *testing.T) { + for _, s := range bufSizes { + name := fmt.Sprintf("TestReader%da", s) + runAppendTests(t, name, NFKC, readFunc(s), appendTests) + name = fmt.Sprintf("TestReader%db", s) + runAppendTests(t, name, NFKC, readFunc(s), ioTests) + } +} + +func writeFunc(size int) appendFunc { + return func(f Form, out []byte, s string) []byte { + in := append(out, s...) + result := new(bytes.Buffer) + w := f.Writer(result) + buf := make([]byte, size) + for n := 0; len(in) > 0; in = in[n:] { + n = copy(buf, in) + _, _ = w.Write(buf[:n]) + } + w.Close() + return result.Bytes() + } +} + +func TestWriter(t *testing.T) { + for _, s := range bufSizes { + name := fmt.Sprintf("TestWriter%da", s) + runAppendTests(t, name, NFKC, writeFunc(s), appendTests) + name = fmt.Sprintf("TestWriter%db", s) + runAppendTests(t, name, NFKC, writeFunc(s), ioTests) + } +} diff --git a/src/pkg/exp/norm/tables.go b/src/pkg/exp/norm/tables.go new file mode 100644 index 000000000..fa33a34a1 --- /dev/null +++ b/src/pkg/exp/norm/tables.go @@ -0,0 +1,6779 @@ +// Generated by running +// maketables --tables=all --url=http://www.unicode.org/Public/6.2.0/ucd/ +// DO NOT EDIT + +package norm + +// Version is the Unicode edition from which the tables are derived. +const Version = "6.2.0" + +const ( + firstMulti = 0x18CF + firstCCC = 0x2E74 + endMulti = 0x2F4A + firstLeadingCCC = 0x4994 + firstCCCZeroExcept = 0x49AA + lastDecomp = 0x49D1 + maxDecomp = 0x8000 +) + +// decomps: 18897 bytes +var decomps = [...]byte{ + // Bytes 0 - 3f + 0x00, 0x41, 0x20, 0x41, 0x21, 0x41, 0x22, 0x41, + 0x23, 0x41, 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, + 0x27, 0x41, 0x28, 0x41, 0x29, 0x41, 0x2A, 0x41, + 0x2B, 0x41, 0x2C, 0x41, 0x2D, 0x41, 0x2E, 0x41, + 0x2F, 0x41, 0x30, 0x41, 0x31, 0x41, 0x32, 0x41, + 0x33, 0x41, 0x34, 0x41, 0x35, 0x41, 0x36, 0x41, + 0x37, 0x41, 0x38, 0x41, 0x39, 0x41, 0x3A, 0x41, + 0x3B, 0x41, 0x3C, 0x41, 0x3D, 0x41, 0x3E, 0x41, + // Bytes 40 - 7f + 0x3F, 0x41, 0x40, 0x41, 0x41, 0x41, 0x42, 0x41, + 0x43, 0x41, 0x44, 0x41, 0x45, 0x41, 0x46, 0x41, + 0x47, 0x41, 0x48, 0x41, 0x49, 0x41, 0x4A, 0x41, + 0x4B, 0x41, 0x4C, 0x41, 0x4D, 0x41, 0x4E, 0x41, + 0x4F, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, + 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, + 0x57, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x41, + 0x5B, 0x41, 0x5C, 0x41, 0x5D, 0x41, 0x5E, 0x41, + // Bytes 80 - bf + 0x5F, 0x41, 0x60, 0x41, 0x61, 0x41, 0x62, 0x41, + 0x63, 0x41, 0x64, 0x41, 0x65, 0x41, 0x66, 0x41, + 0x67, 0x41, 0x68, 0x41, 0x69, 0x41, 0x6A, 0x41, + 0x6B, 0x41, 0x6C, 0x41, 0x6D, 0x41, 0x6E, 0x41, + 0x6F, 0x41, 0x70, 0x41, 0x71, 0x41, 0x72, 0x41, + 0x73, 0x41, 0x74, 0x41, 0x75, 0x41, 0x76, 0x41, + 0x77, 0x41, 0x78, 0x41, 0x79, 0x41, 0x7A, 0x41, + 0x7B, 0x41, 0x7C, 0x41, 0x7D, 0x41, 0x7E, 0x42, + // Bytes c0 - ff + 0xC2, 0xA2, 0x42, 0xC2, 0xA3, 0x42, 0xC2, 0xA5, + 0x42, 0xC2, 0xA6, 0x42, 0xC2, 0xAC, 0x42, 0xC2, + 0xB4, 0x42, 0xC2, 0xB7, 0x42, 0xC3, 0x86, 0x42, + 0xC3, 0xB0, 0x42, 0xC4, 0xA6, 0x42, 0xC4, 0xA7, + 0x42, 0xC4, 0xB1, 0x42, 0xC5, 0x8B, 0x42, 0xC5, + 0x93, 0x42, 0xC6, 0x8E, 0x42, 0xC6, 0x90, 0x42, + 0xC6, 0xAB, 0x42, 0xC8, 0xA2, 0x42, 0xC8, 0xB7, + 0x42, 0xC9, 0x90, 0x42, 0xC9, 0x91, 0x42, 0xC9, + // Bytes 100 - 13f + 0x92, 0x42, 0xC9, 0x94, 0x42, 0xC9, 0x95, 0x42, + 0xC9, 0x99, 0x42, 0xC9, 0x9B, 0x42, 0xC9, 0x9C, + 0x42, 0xC9, 0x9F, 0x42, 0xC9, 0xA1, 0x42, 0xC9, + 0xA3, 0x42, 0xC9, 0xA5, 0x42, 0xC9, 0xA6, 0x42, + 0xC9, 0xA8, 0x42, 0xC9, 0xA9, 0x42, 0xC9, 0xAA, + 0x42, 0xC9, 0xAD, 0x42, 0xC9, 0xAF, 0x42, 0xC9, + 0xB0, 0x42, 0xC9, 0xB1, 0x42, 0xC9, 0xB2, 0x42, + 0xC9, 0xB3, 0x42, 0xC9, 0xB4, 0x42, 0xC9, 0xB5, + // Bytes 140 - 17f + 0x42, 0xC9, 0xB8, 0x42, 0xC9, 0xB9, 0x42, 0xC9, + 0xBB, 0x42, 0xCA, 0x81, 0x42, 0xCA, 0x82, 0x42, + 0xCA, 0x83, 0x42, 0xCA, 0x89, 0x42, 0xCA, 0x8A, + 0x42, 0xCA, 0x8B, 0x42, 0xCA, 0x8C, 0x42, 0xCA, + 0x90, 0x42, 0xCA, 0x91, 0x42, 0xCA, 0x92, 0x42, + 0xCA, 0x95, 0x42, 0xCA, 0x9D, 0x42, 0xCA, 0x9F, + 0x42, 0xCA, 0xB9, 0x42, 0xCE, 0x91, 0x42, 0xCE, + 0x92, 0x42, 0xCE, 0x93, 0x42, 0xCE, 0x94, 0x42, + // Bytes 180 - 1bf + 0xCE, 0x95, 0x42, 0xCE, 0x96, 0x42, 0xCE, 0x97, + 0x42, 0xCE, 0x98, 0x42, 0xCE, 0x99, 0x42, 0xCE, + 0x9A, 0x42, 0xCE, 0x9B, 0x42, 0xCE, 0x9C, 0x42, + 0xCE, 0x9D, 0x42, 0xCE, 0x9E, 0x42, 0xCE, 0x9F, + 0x42, 0xCE, 0xA0, 0x42, 0xCE, 0xA1, 0x42, 0xCE, + 0xA3, 0x42, 0xCE, 0xA4, 0x42, 0xCE, 0xA5, 0x42, + 0xCE, 0xA6, 0x42, 0xCE, 0xA7, 0x42, 0xCE, 0xA8, + 0x42, 0xCE, 0xA9, 0x42, 0xCE, 0xB1, 0x42, 0xCE, + // Bytes 1c0 - 1ff + 0xB2, 0x42, 0xCE, 0xB3, 0x42, 0xCE, 0xB4, 0x42, + 0xCE, 0xB5, 0x42, 0xCE, 0xB6, 0x42, 0xCE, 0xB7, + 0x42, 0xCE, 0xB8, 0x42, 0xCE, 0xB9, 0x42, 0xCE, + 0xBA, 0x42, 0xCE, 0xBB, 0x42, 0xCE, 0xBC, 0x42, + 0xCE, 0xBD, 0x42, 0xCE, 0xBE, 0x42, 0xCE, 0xBF, + 0x42, 0xCF, 0x80, 0x42, 0xCF, 0x81, 0x42, 0xCF, + 0x82, 0x42, 0xCF, 0x83, 0x42, 0xCF, 0x84, 0x42, + 0xCF, 0x85, 0x42, 0xCF, 0x86, 0x42, 0xCF, 0x87, + // Bytes 200 - 23f + 0x42, 0xCF, 0x88, 0x42, 0xCF, 0x89, 0x42, 0xCF, + 0x9C, 0x42, 0xCF, 0x9D, 0x42, 0xD0, 0xBD, 0x42, + 0xD7, 0x90, 0x42, 0xD7, 0x91, 0x42, 0xD7, 0x92, + 0x42, 0xD7, 0x93, 0x42, 0xD7, 0x94, 0x42, 0xD7, + 0x9B, 0x42, 0xD7, 0x9C, 0x42, 0xD7, 0x9D, 0x42, + 0xD7, 0xA2, 0x42, 0xD7, 0xA8, 0x42, 0xD7, 0xAA, + 0x42, 0xD8, 0xA1, 0x42, 0xD8, 0xA7, 0x42, 0xD8, + 0xA8, 0x42, 0xD8, 0xA9, 0x42, 0xD8, 0xAA, 0x42, + // Bytes 240 - 27f + 0xD8, 0xAB, 0x42, 0xD8, 0xAC, 0x42, 0xD8, 0xAD, + 0x42, 0xD8, 0xAE, 0x42, 0xD8, 0xAF, 0x42, 0xD8, + 0xB0, 0x42, 0xD8, 0xB1, 0x42, 0xD8, 0xB2, 0x42, + 0xD8, 0xB3, 0x42, 0xD8, 0xB4, 0x42, 0xD8, 0xB5, + 0x42, 0xD8, 0xB6, 0x42, 0xD8, 0xB7, 0x42, 0xD8, + 0xB8, 0x42, 0xD8, 0xB9, 0x42, 0xD8, 0xBA, 0x42, + 0xD9, 0x81, 0x42, 0xD9, 0x82, 0x42, 0xD9, 0x83, + 0x42, 0xD9, 0x84, 0x42, 0xD9, 0x85, 0x42, 0xD9, + // Bytes 280 - 2bf + 0x86, 0x42, 0xD9, 0x87, 0x42, 0xD9, 0x88, 0x42, + 0xD9, 0x89, 0x42, 0xD9, 0x8A, 0x42, 0xD9, 0xAE, + 0x42, 0xD9, 0xAF, 0x42, 0xD9, 0xB1, 0x42, 0xD9, + 0xB9, 0x42, 0xD9, 0xBA, 0x42, 0xD9, 0xBB, 0x42, + 0xD9, 0xBE, 0x42, 0xD9, 0xBF, 0x42, 0xDA, 0x80, + 0x42, 0xDA, 0x83, 0x42, 0xDA, 0x84, 0x42, 0xDA, + 0x86, 0x42, 0xDA, 0x87, 0x42, 0xDA, 0x88, 0x42, + 0xDA, 0x8C, 0x42, 0xDA, 0x8D, 0x42, 0xDA, 0x8E, + // Bytes 2c0 - 2ff + 0x42, 0xDA, 0x91, 0x42, 0xDA, 0x98, 0x42, 0xDA, + 0xA1, 0x42, 0xDA, 0xA4, 0x42, 0xDA, 0xA6, 0x42, + 0xDA, 0xA9, 0x42, 0xDA, 0xAD, 0x42, 0xDA, 0xAF, + 0x42, 0xDA, 0xB1, 0x42, 0xDA, 0xB3, 0x42, 0xDA, + 0xBA, 0x42, 0xDA, 0xBB, 0x42, 0xDA, 0xBE, 0x42, + 0xDB, 0x81, 0x42, 0xDB, 0x85, 0x42, 0xDB, 0x86, + 0x42, 0xDB, 0x87, 0x42, 0xDB, 0x88, 0x42, 0xDB, + 0x89, 0x42, 0xDB, 0x8B, 0x42, 0xDB, 0x8C, 0x42, + // Bytes 300 - 33f + 0xDB, 0x90, 0x42, 0xDB, 0x92, 0x43, 0xE0, 0xBC, + 0x8B, 0x43, 0xE1, 0x83, 0x9C, 0x43, 0xE1, 0x84, + 0x80, 0x43, 0xE1, 0x84, 0x81, 0x43, 0xE1, 0x84, + 0x82, 0x43, 0xE1, 0x84, 0x83, 0x43, 0xE1, 0x84, + 0x84, 0x43, 0xE1, 0x84, 0x85, 0x43, 0xE1, 0x84, + 0x86, 0x43, 0xE1, 0x84, 0x87, 0x43, 0xE1, 0x84, + 0x88, 0x43, 0xE1, 0x84, 0x89, 0x43, 0xE1, 0x84, + 0x8A, 0x43, 0xE1, 0x84, 0x8B, 0x43, 0xE1, 0x84, + // Bytes 340 - 37f + 0x8C, 0x43, 0xE1, 0x84, 0x8D, 0x43, 0xE1, 0x84, + 0x8E, 0x43, 0xE1, 0x84, 0x8F, 0x43, 0xE1, 0x84, + 0x90, 0x43, 0xE1, 0x84, 0x91, 0x43, 0xE1, 0x84, + 0x92, 0x43, 0xE1, 0x84, 0x94, 0x43, 0xE1, 0x84, + 0x95, 0x43, 0xE1, 0x84, 0x9A, 0x43, 0xE1, 0x84, + 0x9C, 0x43, 0xE1, 0x84, 0x9D, 0x43, 0xE1, 0x84, + 0x9E, 0x43, 0xE1, 0x84, 0xA0, 0x43, 0xE1, 0x84, + 0xA1, 0x43, 0xE1, 0x84, 0xA2, 0x43, 0xE1, 0x84, + // Bytes 380 - 3bf + 0xA3, 0x43, 0xE1, 0x84, 0xA7, 0x43, 0xE1, 0x84, + 0xA9, 0x43, 0xE1, 0x84, 0xAB, 0x43, 0xE1, 0x84, + 0xAC, 0x43, 0xE1, 0x84, 0xAD, 0x43, 0xE1, 0x84, + 0xAE, 0x43, 0xE1, 0x84, 0xAF, 0x43, 0xE1, 0x84, + 0xB2, 0x43, 0xE1, 0x84, 0xB6, 0x43, 0xE1, 0x85, + 0x80, 0x43, 0xE1, 0x85, 0x87, 0x43, 0xE1, 0x85, + 0x8C, 0x43, 0xE1, 0x85, 0x97, 0x43, 0xE1, 0x85, + 0x98, 0x43, 0xE1, 0x85, 0x99, 0x43, 0xE1, 0x85, + // Bytes 3c0 - 3ff + 0xA0, 0x43, 0xE1, 0x85, 0xA1, 0x43, 0xE1, 0x85, + 0xA2, 0x43, 0xE1, 0x85, 0xA3, 0x43, 0xE1, 0x85, + 0xA4, 0x43, 0xE1, 0x85, 0xA5, 0x43, 0xE1, 0x85, + 0xA6, 0x43, 0xE1, 0x85, 0xA7, 0x43, 0xE1, 0x85, + 0xA8, 0x43, 0xE1, 0x85, 0xA9, 0x43, 0xE1, 0x85, + 0xAA, 0x43, 0xE1, 0x85, 0xAB, 0x43, 0xE1, 0x85, + 0xAC, 0x43, 0xE1, 0x85, 0xAD, 0x43, 0xE1, 0x85, + 0xAE, 0x43, 0xE1, 0x85, 0xAF, 0x43, 0xE1, 0x85, + // Bytes 400 - 43f + 0xB0, 0x43, 0xE1, 0x85, 0xB1, 0x43, 0xE1, 0x85, + 0xB2, 0x43, 0xE1, 0x85, 0xB3, 0x43, 0xE1, 0x85, + 0xB4, 0x43, 0xE1, 0x85, 0xB5, 0x43, 0xE1, 0x86, + 0x84, 0x43, 0xE1, 0x86, 0x85, 0x43, 0xE1, 0x86, + 0x88, 0x43, 0xE1, 0x86, 0x91, 0x43, 0xE1, 0x86, + 0x92, 0x43, 0xE1, 0x86, 0x94, 0x43, 0xE1, 0x86, + 0x9E, 0x43, 0xE1, 0x86, 0xA1, 0x43, 0xE1, 0x86, + 0xAA, 0x43, 0xE1, 0x86, 0xAC, 0x43, 0xE1, 0x86, + // Bytes 440 - 47f + 0xAD, 0x43, 0xE1, 0x86, 0xB0, 0x43, 0xE1, 0x86, + 0xB1, 0x43, 0xE1, 0x86, 0xB2, 0x43, 0xE1, 0x86, + 0xB3, 0x43, 0xE1, 0x86, 0xB4, 0x43, 0xE1, 0x86, + 0xB5, 0x43, 0xE1, 0x87, 0x87, 0x43, 0xE1, 0x87, + 0x88, 0x43, 0xE1, 0x87, 0x8C, 0x43, 0xE1, 0x87, + 0x8E, 0x43, 0xE1, 0x87, 0x93, 0x43, 0xE1, 0x87, + 0x97, 0x43, 0xE1, 0x87, 0x99, 0x43, 0xE1, 0x87, + 0x9D, 0x43, 0xE1, 0x87, 0x9F, 0x43, 0xE1, 0x87, + // Bytes 480 - 4bf + 0xB1, 0x43, 0xE1, 0x87, 0xB2, 0x43, 0xE1, 0xB4, + 0x82, 0x43, 0xE1, 0xB4, 0x96, 0x43, 0xE1, 0xB4, + 0x97, 0x43, 0xE1, 0xB4, 0x9C, 0x43, 0xE1, 0xB4, + 0x9D, 0x43, 0xE1, 0xB4, 0xA5, 0x43, 0xE1, 0xB5, + 0xBB, 0x43, 0xE1, 0xB6, 0x85, 0x43, 0xE2, 0x80, + 0x82, 0x43, 0xE2, 0x80, 0x83, 0x43, 0xE2, 0x80, + 0x90, 0x43, 0xE2, 0x80, 0x93, 0x43, 0xE2, 0x80, + 0x94, 0x43, 0xE2, 0x82, 0xA9, 0x43, 0xE2, 0x86, + // Bytes 4c0 - 4ff + 0x90, 0x43, 0xE2, 0x86, 0x91, 0x43, 0xE2, 0x86, + 0x92, 0x43, 0xE2, 0x86, 0x93, 0x43, 0xE2, 0x88, + 0x82, 0x43, 0xE2, 0x88, 0x87, 0x43, 0xE2, 0x88, + 0x91, 0x43, 0xE2, 0x88, 0x92, 0x43, 0xE2, 0x94, + 0x82, 0x43, 0xE2, 0x96, 0xA0, 0x43, 0xE2, 0x97, + 0x8B, 0x43, 0xE2, 0xA6, 0x85, 0x43, 0xE2, 0xA6, + 0x86, 0x43, 0xE2, 0xB5, 0xA1, 0x43, 0xE3, 0x80, + 0x81, 0x43, 0xE3, 0x80, 0x82, 0x43, 0xE3, 0x80, + // Bytes 500 - 53f + 0x88, 0x43, 0xE3, 0x80, 0x89, 0x43, 0xE3, 0x80, + 0x8A, 0x43, 0xE3, 0x80, 0x8B, 0x43, 0xE3, 0x80, + 0x8C, 0x43, 0xE3, 0x80, 0x8D, 0x43, 0xE3, 0x80, + 0x8E, 0x43, 0xE3, 0x80, 0x8F, 0x43, 0xE3, 0x80, + 0x90, 0x43, 0xE3, 0x80, 0x91, 0x43, 0xE3, 0x80, + 0x92, 0x43, 0xE3, 0x80, 0x94, 0x43, 0xE3, 0x80, + 0x95, 0x43, 0xE3, 0x80, 0x96, 0x43, 0xE3, 0x80, + 0x97, 0x43, 0xE3, 0x82, 0xA1, 0x43, 0xE3, 0x82, + // Bytes 540 - 57f + 0xA2, 0x43, 0xE3, 0x82, 0xA3, 0x43, 0xE3, 0x82, + 0xA4, 0x43, 0xE3, 0x82, 0xA5, 0x43, 0xE3, 0x82, + 0xA6, 0x43, 0xE3, 0x82, 0xA7, 0x43, 0xE3, 0x82, + 0xA8, 0x43, 0xE3, 0x82, 0xA9, 0x43, 0xE3, 0x82, + 0xAA, 0x43, 0xE3, 0x82, 0xAB, 0x43, 0xE3, 0x82, + 0xAD, 0x43, 0xE3, 0x82, 0xAF, 0x43, 0xE3, 0x82, + 0xB1, 0x43, 0xE3, 0x82, 0xB3, 0x43, 0xE3, 0x82, + 0xB5, 0x43, 0xE3, 0x82, 0xB7, 0x43, 0xE3, 0x82, + // Bytes 580 - 5bf + 0xB9, 0x43, 0xE3, 0x82, 0xBB, 0x43, 0xE3, 0x82, + 0xBD, 0x43, 0xE3, 0x82, 0xBF, 0x43, 0xE3, 0x83, + 0x81, 0x43, 0xE3, 0x83, 0x83, 0x43, 0xE3, 0x83, + 0x84, 0x43, 0xE3, 0x83, 0x86, 0x43, 0xE3, 0x83, + 0x88, 0x43, 0xE3, 0x83, 0x8A, 0x43, 0xE3, 0x83, + 0x8B, 0x43, 0xE3, 0x83, 0x8C, 0x43, 0xE3, 0x83, + 0x8D, 0x43, 0xE3, 0x83, 0x8E, 0x43, 0xE3, 0x83, + 0x8F, 0x43, 0xE3, 0x83, 0x92, 0x43, 0xE3, 0x83, + // Bytes 5c0 - 5ff + 0x95, 0x43, 0xE3, 0x83, 0x98, 0x43, 0xE3, 0x83, + 0x9B, 0x43, 0xE3, 0x83, 0x9E, 0x43, 0xE3, 0x83, + 0x9F, 0x43, 0xE3, 0x83, 0xA0, 0x43, 0xE3, 0x83, + 0xA1, 0x43, 0xE3, 0x83, 0xA2, 0x43, 0xE3, 0x83, + 0xA3, 0x43, 0xE3, 0x83, 0xA4, 0x43, 0xE3, 0x83, + 0xA5, 0x43, 0xE3, 0x83, 0xA6, 0x43, 0xE3, 0x83, + 0xA7, 0x43, 0xE3, 0x83, 0xA8, 0x43, 0xE3, 0x83, + 0xA9, 0x43, 0xE3, 0x83, 0xAA, 0x43, 0xE3, 0x83, + // Bytes 600 - 63f + 0xAB, 0x43, 0xE3, 0x83, 0xAC, 0x43, 0xE3, 0x83, + 0xAD, 0x43, 0xE3, 0x83, 0xAF, 0x43, 0xE3, 0x83, + 0xB0, 0x43, 0xE3, 0x83, 0xB1, 0x43, 0xE3, 0x83, + 0xB2, 0x43, 0xE3, 0x83, 0xB3, 0x43, 0xE3, 0x83, + 0xBB, 0x43, 0xE3, 0x83, 0xBC, 0x43, 0xE3, 0x92, + 0x9E, 0x43, 0xE3, 0x92, 0xB9, 0x43, 0xE3, 0x92, + 0xBB, 0x43, 0xE3, 0x93, 0x9F, 0x43, 0xE3, 0x94, + 0x95, 0x43, 0xE3, 0x9B, 0xAE, 0x43, 0xE3, 0x9B, + // Bytes 640 - 67f + 0xBC, 0x43, 0xE3, 0x9E, 0x81, 0x43, 0xE3, 0xA0, + 0xAF, 0x43, 0xE3, 0xA1, 0xA2, 0x43, 0xE3, 0xA1, + 0xBC, 0x43, 0xE3, 0xA3, 0x87, 0x43, 0xE3, 0xA3, + 0xA3, 0x43, 0xE3, 0xA4, 0x9C, 0x43, 0xE3, 0xA4, + 0xBA, 0x43, 0xE3, 0xA8, 0xAE, 0x43, 0xE3, 0xA9, + 0xAC, 0x43, 0xE3, 0xAB, 0xA4, 0x43, 0xE3, 0xAC, + 0x88, 0x43, 0xE3, 0xAC, 0x99, 0x43, 0xE3, 0xAD, + 0x89, 0x43, 0xE3, 0xAE, 0x9D, 0x43, 0xE3, 0xB0, + // Bytes 680 - 6bf + 0x98, 0x43, 0xE3, 0xB1, 0x8E, 0x43, 0xE3, 0xB4, + 0xB3, 0x43, 0xE3, 0xB6, 0x96, 0x43, 0xE3, 0xBA, + 0xAC, 0x43, 0xE3, 0xBA, 0xB8, 0x43, 0xE3, 0xBC, + 0x9B, 0x43, 0xE3, 0xBF, 0xBC, 0x43, 0xE4, 0x80, + 0x88, 0x43, 0xE4, 0x80, 0x98, 0x43, 0xE4, 0x80, + 0xB9, 0x43, 0xE4, 0x81, 0x86, 0x43, 0xE4, 0x82, + 0x96, 0x43, 0xE4, 0x83, 0xA3, 0x43, 0xE4, 0x84, + 0xAF, 0x43, 0xE4, 0x88, 0x82, 0x43, 0xE4, 0x88, + // Bytes 6c0 - 6ff + 0xA7, 0x43, 0xE4, 0x8A, 0xA0, 0x43, 0xE4, 0x8C, + 0x81, 0x43, 0xE4, 0x8C, 0xB4, 0x43, 0xE4, 0x8D, + 0x99, 0x43, 0xE4, 0x8F, 0x95, 0x43, 0xE4, 0x8F, + 0x99, 0x43, 0xE4, 0x90, 0x8B, 0x43, 0xE4, 0x91, + 0xAB, 0x43, 0xE4, 0x94, 0xAB, 0x43, 0xE4, 0x95, + 0x9D, 0x43, 0xE4, 0x95, 0xA1, 0x43, 0xE4, 0x95, + 0xAB, 0x43, 0xE4, 0x97, 0x97, 0x43, 0xE4, 0x97, + 0xB9, 0x43, 0xE4, 0x98, 0xB5, 0x43, 0xE4, 0x9A, + // Bytes 700 - 73f + 0xBE, 0x43, 0xE4, 0x9B, 0x87, 0x43, 0xE4, 0xA6, + 0x95, 0x43, 0xE4, 0xA7, 0xA6, 0x43, 0xE4, 0xA9, + 0xAE, 0x43, 0xE4, 0xA9, 0xB6, 0x43, 0xE4, 0xAA, + 0xB2, 0x43, 0xE4, 0xAC, 0xB3, 0x43, 0xE4, 0xAF, + 0x8E, 0x43, 0xE4, 0xB3, 0x8E, 0x43, 0xE4, 0xB3, + 0xAD, 0x43, 0xE4, 0xB3, 0xB8, 0x43, 0xE4, 0xB5, + 0x96, 0x43, 0xE4, 0xB8, 0x80, 0x43, 0xE4, 0xB8, + 0x81, 0x43, 0xE4, 0xB8, 0x83, 0x43, 0xE4, 0xB8, + // Bytes 740 - 77f + 0x89, 0x43, 0xE4, 0xB8, 0x8A, 0x43, 0xE4, 0xB8, + 0x8B, 0x43, 0xE4, 0xB8, 0x8D, 0x43, 0xE4, 0xB8, + 0x99, 0x43, 0xE4, 0xB8, 0xA6, 0x43, 0xE4, 0xB8, + 0xA8, 0x43, 0xE4, 0xB8, 0xAD, 0x43, 0xE4, 0xB8, + 0xB2, 0x43, 0xE4, 0xB8, 0xB6, 0x43, 0xE4, 0xB8, + 0xB8, 0x43, 0xE4, 0xB8, 0xB9, 0x43, 0xE4, 0xB8, + 0xBD, 0x43, 0xE4, 0xB8, 0xBF, 0x43, 0xE4, 0xB9, + 0x81, 0x43, 0xE4, 0xB9, 0x99, 0x43, 0xE4, 0xB9, + // Bytes 780 - 7bf + 0x9D, 0x43, 0xE4, 0xBA, 0x82, 0x43, 0xE4, 0xBA, + 0x85, 0x43, 0xE4, 0xBA, 0x86, 0x43, 0xE4, 0xBA, + 0x8C, 0x43, 0xE4, 0xBA, 0x94, 0x43, 0xE4, 0xBA, + 0xA0, 0x43, 0xE4, 0xBA, 0xA4, 0x43, 0xE4, 0xBA, + 0xAE, 0x43, 0xE4, 0xBA, 0xBA, 0x43, 0xE4, 0xBB, + 0x80, 0x43, 0xE4, 0xBB, 0x8C, 0x43, 0xE4, 0xBB, + 0xA4, 0x43, 0xE4, 0xBC, 0x81, 0x43, 0xE4, 0xBC, + 0x91, 0x43, 0xE4, 0xBD, 0xA0, 0x43, 0xE4, 0xBE, + // Bytes 7c0 - 7ff + 0x80, 0x43, 0xE4, 0xBE, 0x86, 0x43, 0xE4, 0xBE, + 0x8B, 0x43, 0xE4, 0xBE, 0xAE, 0x43, 0xE4, 0xBE, + 0xBB, 0x43, 0xE4, 0xBE, 0xBF, 0x43, 0xE5, 0x80, + 0x82, 0x43, 0xE5, 0x80, 0xAB, 0x43, 0xE5, 0x81, + 0xBA, 0x43, 0xE5, 0x82, 0x99, 0x43, 0xE5, 0x83, + 0x8F, 0x43, 0xE5, 0x83, 0x9A, 0x43, 0xE5, 0x83, + 0xA7, 0x43, 0xE5, 0x84, 0xAA, 0x43, 0xE5, 0x84, + 0xBF, 0x43, 0xE5, 0x85, 0x80, 0x43, 0xE5, 0x85, + // Bytes 800 - 83f + 0x85, 0x43, 0xE5, 0x85, 0x8D, 0x43, 0xE5, 0x85, + 0x94, 0x43, 0xE5, 0x85, 0xA4, 0x43, 0xE5, 0x85, + 0xA5, 0x43, 0xE5, 0x85, 0xA7, 0x43, 0xE5, 0x85, + 0xA8, 0x43, 0xE5, 0x85, 0xA9, 0x43, 0xE5, 0x85, + 0xAB, 0x43, 0xE5, 0x85, 0xAD, 0x43, 0xE5, 0x85, + 0xB7, 0x43, 0xE5, 0x86, 0x80, 0x43, 0xE5, 0x86, + 0x82, 0x43, 0xE5, 0x86, 0x8D, 0x43, 0xE5, 0x86, + 0x92, 0x43, 0xE5, 0x86, 0x95, 0x43, 0xE5, 0x86, + // Bytes 840 - 87f + 0x96, 0x43, 0xE5, 0x86, 0x97, 0x43, 0xE5, 0x86, + 0x99, 0x43, 0xE5, 0x86, 0xA4, 0x43, 0xE5, 0x86, + 0xAB, 0x43, 0xE5, 0x86, 0xAC, 0x43, 0xE5, 0x86, + 0xB5, 0x43, 0xE5, 0x86, 0xB7, 0x43, 0xE5, 0x87, + 0x89, 0x43, 0xE5, 0x87, 0x8C, 0x43, 0xE5, 0x87, + 0x9C, 0x43, 0xE5, 0x87, 0x9E, 0x43, 0xE5, 0x87, + 0xA0, 0x43, 0xE5, 0x87, 0xB5, 0x43, 0xE5, 0x88, + 0x80, 0x43, 0xE5, 0x88, 0x83, 0x43, 0xE5, 0x88, + // Bytes 880 - 8bf + 0x87, 0x43, 0xE5, 0x88, 0x97, 0x43, 0xE5, 0x88, + 0x9D, 0x43, 0xE5, 0x88, 0xA9, 0x43, 0xE5, 0x88, + 0xBA, 0x43, 0xE5, 0x88, 0xBB, 0x43, 0xE5, 0x89, + 0x86, 0x43, 0xE5, 0x89, 0x8D, 0x43, 0xE5, 0x89, + 0xB2, 0x43, 0xE5, 0x89, 0xB7, 0x43, 0xE5, 0x8A, + 0x89, 0x43, 0xE5, 0x8A, 0x9B, 0x43, 0xE5, 0x8A, + 0xA3, 0x43, 0xE5, 0x8A, 0xB3, 0x43, 0xE5, 0x8A, + 0xB4, 0x43, 0xE5, 0x8B, 0x87, 0x43, 0xE5, 0x8B, + // Bytes 8c0 - 8ff + 0x89, 0x43, 0xE5, 0x8B, 0x92, 0x43, 0xE5, 0x8B, + 0x9E, 0x43, 0xE5, 0x8B, 0xA4, 0x43, 0xE5, 0x8B, + 0xB5, 0x43, 0xE5, 0x8B, 0xB9, 0x43, 0xE5, 0x8B, + 0xBA, 0x43, 0xE5, 0x8C, 0x85, 0x43, 0xE5, 0x8C, + 0x86, 0x43, 0xE5, 0x8C, 0x95, 0x43, 0xE5, 0x8C, + 0x97, 0x43, 0xE5, 0x8C, 0x9A, 0x43, 0xE5, 0x8C, + 0xB8, 0x43, 0xE5, 0x8C, 0xBB, 0x43, 0xE5, 0x8C, + 0xBF, 0x43, 0xE5, 0x8D, 0x81, 0x43, 0xE5, 0x8D, + // Bytes 900 - 93f + 0x84, 0x43, 0xE5, 0x8D, 0x85, 0x43, 0xE5, 0x8D, + 0x89, 0x43, 0xE5, 0x8D, 0x91, 0x43, 0xE5, 0x8D, + 0x94, 0x43, 0xE5, 0x8D, 0x9A, 0x43, 0xE5, 0x8D, + 0x9C, 0x43, 0xE5, 0x8D, 0xA9, 0x43, 0xE5, 0x8D, + 0xB0, 0x43, 0xE5, 0x8D, 0xB3, 0x43, 0xE5, 0x8D, + 0xB5, 0x43, 0xE5, 0x8D, 0xBD, 0x43, 0xE5, 0x8D, + 0xBF, 0x43, 0xE5, 0x8E, 0x82, 0x43, 0xE5, 0x8E, + 0xB6, 0x43, 0xE5, 0x8F, 0x83, 0x43, 0xE5, 0x8F, + // Bytes 940 - 97f + 0x88, 0x43, 0xE5, 0x8F, 0x8A, 0x43, 0xE5, 0x8F, + 0x8C, 0x43, 0xE5, 0x8F, 0x9F, 0x43, 0xE5, 0x8F, + 0xA3, 0x43, 0xE5, 0x8F, 0xA5, 0x43, 0xE5, 0x8F, + 0xAB, 0x43, 0xE5, 0x8F, 0xAF, 0x43, 0xE5, 0x8F, + 0xB1, 0x43, 0xE5, 0x8F, 0xB3, 0x43, 0xE5, 0x90, + 0x86, 0x43, 0xE5, 0x90, 0x88, 0x43, 0xE5, 0x90, + 0x8D, 0x43, 0xE5, 0x90, 0x8F, 0x43, 0xE5, 0x90, + 0x9D, 0x43, 0xE5, 0x90, 0xB8, 0x43, 0xE5, 0x90, + // Bytes 980 - 9bf + 0xB9, 0x43, 0xE5, 0x91, 0x82, 0x43, 0xE5, 0x91, + 0x88, 0x43, 0xE5, 0x91, 0xA8, 0x43, 0xE5, 0x92, + 0x9E, 0x43, 0xE5, 0x92, 0xA2, 0x43, 0xE5, 0x92, + 0xBD, 0x43, 0xE5, 0x93, 0xB6, 0x43, 0xE5, 0x94, + 0x90, 0x43, 0xE5, 0x95, 0x8F, 0x43, 0xE5, 0x95, + 0x93, 0x43, 0xE5, 0x95, 0x95, 0x43, 0xE5, 0x95, + 0xA3, 0x43, 0xE5, 0x96, 0x84, 0x43, 0xE5, 0x96, + 0x87, 0x43, 0xE5, 0x96, 0x99, 0x43, 0xE5, 0x96, + // Bytes 9c0 - 9ff + 0x9D, 0x43, 0xE5, 0x96, 0xAB, 0x43, 0xE5, 0x96, + 0xB3, 0x43, 0xE5, 0x96, 0xB6, 0x43, 0xE5, 0x97, + 0x80, 0x43, 0xE5, 0x97, 0x82, 0x43, 0xE5, 0x97, + 0xA2, 0x43, 0xE5, 0x98, 0x86, 0x43, 0xE5, 0x99, + 0x91, 0x43, 0xE5, 0x99, 0xA8, 0x43, 0xE5, 0x99, + 0xB4, 0x43, 0xE5, 0x9B, 0x97, 0x43, 0xE5, 0x9B, + 0x9B, 0x43, 0xE5, 0x9B, 0xB9, 0x43, 0xE5, 0x9C, + 0x96, 0x43, 0xE5, 0x9C, 0x97, 0x43, 0xE5, 0x9C, + // Bytes a00 - a3f + 0x9F, 0x43, 0xE5, 0x9C, 0xB0, 0x43, 0xE5, 0x9E, + 0x8B, 0x43, 0xE5, 0x9F, 0x8E, 0x43, 0xE5, 0x9F, + 0xB4, 0x43, 0xE5, 0xA0, 0x8D, 0x43, 0xE5, 0xA0, + 0xB1, 0x43, 0xE5, 0xA0, 0xB2, 0x43, 0xE5, 0xA1, + 0x80, 0x43, 0xE5, 0xA1, 0x9A, 0x43, 0xE5, 0xA1, + 0x9E, 0x43, 0xE5, 0xA2, 0xA8, 0x43, 0xE5, 0xA2, + 0xAC, 0x43, 0xE5, 0xA2, 0xB3, 0x43, 0xE5, 0xA3, + 0x98, 0x43, 0xE5, 0xA3, 0x9F, 0x43, 0xE5, 0xA3, + // Bytes a40 - a7f + 0xAB, 0x43, 0xE5, 0xA3, 0xAE, 0x43, 0xE5, 0xA3, + 0xB0, 0x43, 0xE5, 0xA3, 0xB2, 0x43, 0xE5, 0xA3, + 0xB7, 0x43, 0xE5, 0xA4, 0x82, 0x43, 0xE5, 0xA4, + 0x86, 0x43, 0xE5, 0xA4, 0x8A, 0x43, 0xE5, 0xA4, + 0x95, 0x43, 0xE5, 0xA4, 0x9A, 0x43, 0xE5, 0xA4, + 0x9C, 0x43, 0xE5, 0xA4, 0xA2, 0x43, 0xE5, 0xA4, + 0xA7, 0x43, 0xE5, 0xA4, 0xA9, 0x43, 0xE5, 0xA5, + 0x84, 0x43, 0xE5, 0xA5, 0x88, 0x43, 0xE5, 0xA5, + // Bytes a80 - abf + 0x91, 0x43, 0xE5, 0xA5, 0x94, 0x43, 0xE5, 0xA5, + 0xA2, 0x43, 0xE5, 0xA5, 0xB3, 0x43, 0xE5, 0xA7, + 0x98, 0x43, 0xE5, 0xA7, 0xAC, 0x43, 0xE5, 0xA8, + 0x9B, 0x43, 0xE5, 0xA8, 0xA7, 0x43, 0xE5, 0xA9, + 0xA2, 0x43, 0xE5, 0xA9, 0xA6, 0x43, 0xE5, 0xAA, + 0xB5, 0x43, 0xE5, 0xAC, 0x88, 0x43, 0xE5, 0xAC, + 0xA8, 0x43, 0xE5, 0xAC, 0xBE, 0x43, 0xE5, 0xAD, + 0x90, 0x43, 0xE5, 0xAD, 0x97, 0x43, 0xE5, 0xAD, + // Bytes ac0 - aff + 0xA6, 0x43, 0xE5, 0xAE, 0x80, 0x43, 0xE5, 0xAE, + 0x85, 0x43, 0xE5, 0xAE, 0x97, 0x43, 0xE5, 0xAF, + 0x83, 0x43, 0xE5, 0xAF, 0x98, 0x43, 0xE5, 0xAF, + 0xA7, 0x43, 0xE5, 0xAF, 0xAE, 0x43, 0xE5, 0xAF, + 0xB3, 0x43, 0xE5, 0xAF, 0xB8, 0x43, 0xE5, 0xAF, + 0xBF, 0x43, 0xE5, 0xB0, 0x86, 0x43, 0xE5, 0xB0, + 0x8F, 0x43, 0xE5, 0xB0, 0xA2, 0x43, 0xE5, 0xB0, + 0xB8, 0x43, 0xE5, 0xB0, 0xBF, 0x43, 0xE5, 0xB1, + // Bytes b00 - b3f + 0xA0, 0x43, 0xE5, 0xB1, 0xA2, 0x43, 0xE5, 0xB1, + 0xA4, 0x43, 0xE5, 0xB1, 0xA5, 0x43, 0xE5, 0xB1, + 0xAE, 0x43, 0xE5, 0xB1, 0xB1, 0x43, 0xE5, 0xB2, + 0x8D, 0x43, 0xE5, 0xB3, 0x80, 0x43, 0xE5, 0xB4, + 0x99, 0x43, 0xE5, 0xB5, 0x83, 0x43, 0xE5, 0xB5, + 0x90, 0x43, 0xE5, 0xB5, 0xAB, 0x43, 0xE5, 0xB5, + 0xAE, 0x43, 0xE5, 0xB5, 0xBC, 0x43, 0xE5, 0xB6, + 0xB2, 0x43, 0xE5, 0xB6, 0xBA, 0x43, 0xE5, 0xB7, + // Bytes b40 - b7f + 0x9B, 0x43, 0xE5, 0xB7, 0xA1, 0x43, 0xE5, 0xB7, + 0xA2, 0x43, 0xE5, 0xB7, 0xA5, 0x43, 0xE5, 0xB7, + 0xA6, 0x43, 0xE5, 0xB7, 0xB1, 0x43, 0xE5, 0xB7, + 0xBD, 0x43, 0xE5, 0xB7, 0xBE, 0x43, 0xE5, 0xB8, + 0xA8, 0x43, 0xE5, 0xB8, 0xBD, 0x43, 0xE5, 0xB9, + 0xA9, 0x43, 0xE5, 0xB9, 0xB2, 0x43, 0xE5, 0xB9, + 0xB4, 0x43, 0xE5, 0xB9, 0xBA, 0x43, 0xE5, 0xB9, + 0xBC, 0x43, 0xE5, 0xB9, 0xBF, 0x43, 0xE5, 0xBA, + // Bytes b80 - bbf + 0xA6, 0x43, 0xE5, 0xBA, 0xB0, 0x43, 0xE5, 0xBA, + 0xB3, 0x43, 0xE5, 0xBA, 0xB6, 0x43, 0xE5, 0xBB, + 0x89, 0x43, 0xE5, 0xBB, 0x8A, 0x43, 0xE5, 0xBB, + 0x92, 0x43, 0xE5, 0xBB, 0x93, 0x43, 0xE5, 0xBB, + 0x99, 0x43, 0xE5, 0xBB, 0xAC, 0x43, 0xE5, 0xBB, + 0xB4, 0x43, 0xE5, 0xBB, 0xBE, 0x43, 0xE5, 0xBC, + 0x84, 0x43, 0xE5, 0xBC, 0x8B, 0x43, 0xE5, 0xBC, + 0x93, 0x43, 0xE5, 0xBC, 0xA2, 0x43, 0xE5, 0xBD, + // Bytes bc0 - bff + 0x90, 0x43, 0xE5, 0xBD, 0x93, 0x43, 0xE5, 0xBD, + 0xA1, 0x43, 0xE5, 0xBD, 0xA2, 0x43, 0xE5, 0xBD, + 0xA9, 0x43, 0xE5, 0xBD, 0xAB, 0x43, 0xE5, 0xBD, + 0xB3, 0x43, 0xE5, 0xBE, 0x8B, 0x43, 0xE5, 0xBE, + 0x8C, 0x43, 0xE5, 0xBE, 0x97, 0x43, 0xE5, 0xBE, + 0x9A, 0x43, 0xE5, 0xBE, 0xA9, 0x43, 0xE5, 0xBE, + 0xAD, 0x43, 0xE5, 0xBF, 0x83, 0x43, 0xE5, 0xBF, + 0x8D, 0x43, 0xE5, 0xBF, 0x97, 0x43, 0xE5, 0xBF, + // Bytes c00 - c3f + 0xB5, 0x43, 0xE5, 0xBF, 0xB9, 0x43, 0xE6, 0x80, + 0x92, 0x43, 0xE6, 0x80, 0x9C, 0x43, 0xE6, 0x81, + 0xB5, 0x43, 0xE6, 0x82, 0x81, 0x43, 0xE6, 0x82, + 0x94, 0x43, 0xE6, 0x83, 0x87, 0x43, 0xE6, 0x83, + 0x98, 0x43, 0xE6, 0x83, 0xA1, 0x43, 0xE6, 0x84, + 0x88, 0x43, 0xE6, 0x85, 0x84, 0x43, 0xE6, 0x85, + 0x88, 0x43, 0xE6, 0x85, 0x8C, 0x43, 0xE6, 0x85, + 0x8E, 0x43, 0xE6, 0x85, 0xA0, 0x43, 0xE6, 0x85, + // Bytes c40 - c7f + 0xA8, 0x43, 0xE6, 0x85, 0xBA, 0x43, 0xE6, 0x86, + 0x8E, 0x43, 0xE6, 0x86, 0x90, 0x43, 0xE6, 0x86, + 0xA4, 0x43, 0xE6, 0x86, 0xAF, 0x43, 0xE6, 0x86, + 0xB2, 0x43, 0xE6, 0x87, 0x9E, 0x43, 0xE6, 0x87, + 0xB2, 0x43, 0xE6, 0x87, 0xB6, 0x43, 0xE6, 0x88, + 0x80, 0x43, 0xE6, 0x88, 0x88, 0x43, 0xE6, 0x88, + 0x90, 0x43, 0xE6, 0x88, 0x9B, 0x43, 0xE6, 0x88, + 0xAE, 0x43, 0xE6, 0x88, 0xB4, 0x43, 0xE6, 0x88, + // Bytes c80 - cbf + 0xB6, 0x43, 0xE6, 0x89, 0x8B, 0x43, 0xE6, 0x89, + 0x93, 0x43, 0xE6, 0x89, 0x9D, 0x43, 0xE6, 0x8A, + 0x95, 0x43, 0xE6, 0x8A, 0xB1, 0x43, 0xE6, 0x8B, + 0x89, 0x43, 0xE6, 0x8B, 0x8F, 0x43, 0xE6, 0x8B, + 0x93, 0x43, 0xE6, 0x8B, 0x94, 0x43, 0xE6, 0x8B, + 0xBC, 0x43, 0xE6, 0x8B, 0xBE, 0x43, 0xE6, 0x8C, + 0x87, 0x43, 0xE6, 0x8C, 0xBD, 0x43, 0xE6, 0x8D, + 0x90, 0x43, 0xE6, 0x8D, 0x95, 0x43, 0xE6, 0x8D, + // Bytes cc0 - cff + 0xA8, 0x43, 0xE6, 0x8D, 0xBB, 0x43, 0xE6, 0x8E, + 0x83, 0x43, 0xE6, 0x8E, 0xA0, 0x43, 0xE6, 0x8E, + 0xA9, 0x43, 0xE6, 0x8F, 0x84, 0x43, 0xE6, 0x8F, + 0x85, 0x43, 0xE6, 0x8F, 0xA4, 0x43, 0xE6, 0x90, + 0x9C, 0x43, 0xE6, 0x90, 0xA2, 0x43, 0xE6, 0x91, + 0x92, 0x43, 0xE6, 0x91, 0xA9, 0x43, 0xE6, 0x91, + 0xB7, 0x43, 0xE6, 0x91, 0xBE, 0x43, 0xE6, 0x92, + 0x9A, 0x43, 0xE6, 0x92, 0x9D, 0x43, 0xE6, 0x93, + // Bytes d00 - d3f + 0x84, 0x43, 0xE6, 0x94, 0xAF, 0x43, 0xE6, 0x94, + 0xB4, 0x43, 0xE6, 0x95, 0x8F, 0x43, 0xE6, 0x95, + 0x96, 0x43, 0xE6, 0x95, 0xAC, 0x43, 0xE6, 0x95, + 0xB8, 0x43, 0xE6, 0x96, 0x87, 0x43, 0xE6, 0x96, + 0x97, 0x43, 0xE6, 0x96, 0x99, 0x43, 0xE6, 0x96, + 0xA4, 0x43, 0xE6, 0x96, 0xB0, 0x43, 0xE6, 0x96, + 0xB9, 0x43, 0xE6, 0x97, 0x85, 0x43, 0xE6, 0x97, + 0xA0, 0x43, 0xE6, 0x97, 0xA2, 0x43, 0xE6, 0x97, + // Bytes d40 - d7f + 0xA3, 0x43, 0xE6, 0x97, 0xA5, 0x43, 0xE6, 0x98, + 0x93, 0x43, 0xE6, 0x98, 0xA0, 0x43, 0xE6, 0x99, + 0x89, 0x43, 0xE6, 0x99, 0xB4, 0x43, 0xE6, 0x9A, + 0x88, 0x43, 0xE6, 0x9A, 0x91, 0x43, 0xE6, 0x9A, + 0x9C, 0x43, 0xE6, 0x9A, 0xB4, 0x43, 0xE6, 0x9B, + 0x86, 0x43, 0xE6, 0x9B, 0xB0, 0x43, 0xE6, 0x9B, + 0xB4, 0x43, 0xE6, 0x9B, 0xB8, 0x43, 0xE6, 0x9C, + 0x80, 0x43, 0xE6, 0x9C, 0x88, 0x43, 0xE6, 0x9C, + // Bytes d80 - dbf + 0x89, 0x43, 0xE6, 0x9C, 0x97, 0x43, 0xE6, 0x9C, + 0x9B, 0x43, 0xE6, 0x9C, 0xA1, 0x43, 0xE6, 0x9C, + 0xA8, 0x43, 0xE6, 0x9D, 0x8E, 0x43, 0xE6, 0x9D, + 0x93, 0x43, 0xE6, 0x9D, 0x96, 0x43, 0xE6, 0x9D, + 0x9E, 0x43, 0xE6, 0x9D, 0xBB, 0x43, 0xE6, 0x9E, + 0x85, 0x43, 0xE6, 0x9E, 0x97, 0x43, 0xE6, 0x9F, + 0xB3, 0x43, 0xE6, 0x9F, 0xBA, 0x43, 0xE6, 0xA0, + 0x97, 0x43, 0xE6, 0xA0, 0x9F, 0x43, 0xE6, 0xA0, + // Bytes dc0 - dff + 0xAA, 0x43, 0xE6, 0xA1, 0x92, 0x43, 0xE6, 0xA2, + 0x81, 0x43, 0xE6, 0xA2, 0x85, 0x43, 0xE6, 0xA2, + 0x8E, 0x43, 0xE6, 0xA2, 0xA8, 0x43, 0xE6, 0xA4, + 0x94, 0x43, 0xE6, 0xA5, 0x82, 0x43, 0xE6, 0xA6, + 0xA3, 0x43, 0xE6, 0xA7, 0xAA, 0x43, 0xE6, 0xA8, + 0x82, 0x43, 0xE6, 0xA8, 0x93, 0x43, 0xE6, 0xAA, + 0xA8, 0x43, 0xE6, 0xAB, 0x93, 0x43, 0xE6, 0xAB, + 0x9B, 0x43, 0xE6, 0xAC, 0x84, 0x43, 0xE6, 0xAC, + // Bytes e00 - e3f + 0xA0, 0x43, 0xE6, 0xAC, 0xA1, 0x43, 0xE6, 0xAD, + 0x94, 0x43, 0xE6, 0xAD, 0xA2, 0x43, 0xE6, 0xAD, + 0xA3, 0x43, 0xE6, 0xAD, 0xB2, 0x43, 0xE6, 0xAD, + 0xB7, 0x43, 0xE6, 0xAD, 0xB9, 0x43, 0xE6, 0xAE, + 0x9F, 0x43, 0xE6, 0xAE, 0xAE, 0x43, 0xE6, 0xAE, + 0xB3, 0x43, 0xE6, 0xAE, 0xBA, 0x43, 0xE6, 0xAE, + 0xBB, 0x43, 0xE6, 0xAF, 0x8B, 0x43, 0xE6, 0xAF, + 0x8D, 0x43, 0xE6, 0xAF, 0x94, 0x43, 0xE6, 0xAF, + // Bytes e40 - e7f + 0x9B, 0x43, 0xE6, 0xB0, 0x8F, 0x43, 0xE6, 0xB0, + 0x94, 0x43, 0xE6, 0xB0, 0xB4, 0x43, 0xE6, 0xB1, + 0x8E, 0x43, 0xE6, 0xB1, 0xA7, 0x43, 0xE6, 0xB2, + 0x88, 0x43, 0xE6, 0xB2, 0xBF, 0x43, 0xE6, 0xB3, + 0x8C, 0x43, 0xE6, 0xB3, 0x8D, 0x43, 0xE6, 0xB3, + 0xA5, 0x43, 0xE6, 0xB3, 0xA8, 0x43, 0xE6, 0xB4, + 0x96, 0x43, 0xE6, 0xB4, 0x9B, 0x43, 0xE6, 0xB4, + 0x9E, 0x43, 0xE6, 0xB4, 0xB4, 0x43, 0xE6, 0xB4, + // Bytes e80 - ebf + 0xBE, 0x43, 0xE6, 0xB5, 0x81, 0x43, 0xE6, 0xB5, + 0xA9, 0x43, 0xE6, 0xB5, 0xAA, 0x43, 0xE6, 0xB5, + 0xB7, 0x43, 0xE6, 0xB5, 0xB8, 0x43, 0xE6, 0xB6, + 0x85, 0x43, 0xE6, 0xB7, 0x8B, 0x43, 0xE6, 0xB7, + 0x9A, 0x43, 0xE6, 0xB7, 0xAA, 0x43, 0xE6, 0xB7, + 0xB9, 0x43, 0xE6, 0xB8, 0x9A, 0x43, 0xE6, 0xB8, + 0xAF, 0x43, 0xE6, 0xB9, 0xAE, 0x43, 0xE6, 0xBA, + 0x80, 0x43, 0xE6, 0xBA, 0x9C, 0x43, 0xE6, 0xBA, + // Bytes ec0 - eff + 0xBA, 0x43, 0xE6, 0xBB, 0x87, 0x43, 0xE6, 0xBB, + 0x8B, 0x43, 0xE6, 0xBB, 0x91, 0x43, 0xE6, 0xBB, + 0x9B, 0x43, 0xE6, 0xBC, 0x8F, 0x43, 0xE6, 0xBC, + 0x94, 0x43, 0xE6, 0xBC, 0xA2, 0x43, 0xE6, 0xBC, + 0xA3, 0x43, 0xE6, 0xBD, 0xAE, 0x43, 0xE6, 0xBF, + 0x86, 0x43, 0xE6, 0xBF, 0xAB, 0x43, 0xE6, 0xBF, + 0xBE, 0x43, 0xE7, 0x80, 0x9B, 0x43, 0xE7, 0x80, + 0x9E, 0x43, 0xE7, 0x80, 0xB9, 0x43, 0xE7, 0x81, + // Bytes f00 - f3f + 0x8A, 0x43, 0xE7, 0x81, 0xAB, 0x43, 0xE7, 0x81, + 0xB0, 0x43, 0xE7, 0x81, 0xB7, 0x43, 0xE7, 0x81, + 0xBD, 0x43, 0xE7, 0x82, 0x99, 0x43, 0xE7, 0x82, + 0xAD, 0x43, 0xE7, 0x83, 0x88, 0x43, 0xE7, 0x83, + 0x99, 0x43, 0xE7, 0x84, 0xA1, 0x43, 0xE7, 0x85, + 0x85, 0x43, 0xE7, 0x85, 0x89, 0x43, 0xE7, 0x85, + 0xAE, 0x43, 0xE7, 0x86, 0x9C, 0x43, 0xE7, 0x87, + 0x8E, 0x43, 0xE7, 0x87, 0x90, 0x43, 0xE7, 0x88, + // Bytes f40 - f7f + 0x90, 0x43, 0xE7, 0x88, 0x9B, 0x43, 0xE7, 0x88, + 0xA8, 0x43, 0xE7, 0x88, 0xAA, 0x43, 0xE7, 0x88, + 0xAB, 0x43, 0xE7, 0x88, 0xB5, 0x43, 0xE7, 0x88, + 0xB6, 0x43, 0xE7, 0x88, 0xBB, 0x43, 0xE7, 0x88, + 0xBF, 0x43, 0xE7, 0x89, 0x87, 0x43, 0xE7, 0x89, + 0x90, 0x43, 0xE7, 0x89, 0x99, 0x43, 0xE7, 0x89, + 0x9B, 0x43, 0xE7, 0x89, 0xA2, 0x43, 0xE7, 0x89, + 0xB9, 0x43, 0xE7, 0x8A, 0x80, 0x43, 0xE7, 0x8A, + // Bytes f80 - fbf + 0x95, 0x43, 0xE7, 0x8A, 0xAC, 0x43, 0xE7, 0x8A, + 0xAF, 0x43, 0xE7, 0x8B, 0x80, 0x43, 0xE7, 0x8B, + 0xBC, 0x43, 0xE7, 0x8C, 0xAA, 0x43, 0xE7, 0x8D, + 0xB5, 0x43, 0xE7, 0x8D, 0xBA, 0x43, 0xE7, 0x8E, + 0x84, 0x43, 0xE7, 0x8E, 0x87, 0x43, 0xE7, 0x8E, + 0x89, 0x43, 0xE7, 0x8E, 0x8B, 0x43, 0xE7, 0x8E, + 0xA5, 0x43, 0xE7, 0x8E, 0xB2, 0x43, 0xE7, 0x8F, + 0x9E, 0x43, 0xE7, 0x90, 0x86, 0x43, 0xE7, 0x90, + // Bytes fc0 - fff + 0x89, 0x43, 0xE7, 0x90, 0xA2, 0x43, 0xE7, 0x91, + 0x87, 0x43, 0xE7, 0x91, 0x9C, 0x43, 0xE7, 0x91, + 0xA9, 0x43, 0xE7, 0x91, 0xB1, 0x43, 0xE7, 0x92, + 0x85, 0x43, 0xE7, 0x92, 0x89, 0x43, 0xE7, 0x92, + 0x98, 0x43, 0xE7, 0x93, 0x8A, 0x43, 0xE7, 0x93, + 0x9C, 0x43, 0xE7, 0x93, 0xA6, 0x43, 0xE7, 0x94, + 0x86, 0x43, 0xE7, 0x94, 0x98, 0x43, 0xE7, 0x94, + 0x9F, 0x43, 0xE7, 0x94, 0xA4, 0x43, 0xE7, 0x94, + // Bytes 1000 - 103f + 0xA8, 0x43, 0xE7, 0x94, 0xB0, 0x43, 0xE7, 0x94, + 0xB2, 0x43, 0xE7, 0x94, 0xB3, 0x43, 0xE7, 0x94, + 0xB7, 0x43, 0xE7, 0x94, 0xBB, 0x43, 0xE7, 0x94, + 0xBE, 0x43, 0xE7, 0x95, 0x99, 0x43, 0xE7, 0x95, + 0xA5, 0x43, 0xE7, 0x95, 0xB0, 0x43, 0xE7, 0x96, + 0x8B, 0x43, 0xE7, 0x96, 0x92, 0x43, 0xE7, 0x97, + 0xA2, 0x43, 0xE7, 0x98, 0x90, 0x43, 0xE7, 0x98, + 0x9D, 0x43, 0xE7, 0x98, 0x9F, 0x43, 0xE7, 0x99, + // Bytes 1040 - 107f + 0x82, 0x43, 0xE7, 0x99, 0xA9, 0x43, 0xE7, 0x99, + 0xB6, 0x43, 0xE7, 0x99, 0xBD, 0x43, 0xE7, 0x9A, + 0xAE, 0x43, 0xE7, 0x9A, 0xBF, 0x43, 0xE7, 0x9B, + 0x8A, 0x43, 0xE7, 0x9B, 0x9B, 0x43, 0xE7, 0x9B, + 0xA3, 0x43, 0xE7, 0x9B, 0xA7, 0x43, 0xE7, 0x9B, + 0xAE, 0x43, 0xE7, 0x9B, 0xB4, 0x43, 0xE7, 0x9C, + 0x81, 0x43, 0xE7, 0x9C, 0x9E, 0x43, 0xE7, 0x9C, + 0x9F, 0x43, 0xE7, 0x9D, 0x80, 0x43, 0xE7, 0x9D, + // Bytes 1080 - 10bf + 0x8A, 0x43, 0xE7, 0x9E, 0x8B, 0x43, 0xE7, 0x9E, + 0xA7, 0x43, 0xE7, 0x9F, 0x9B, 0x43, 0xE7, 0x9F, + 0xA2, 0x43, 0xE7, 0x9F, 0xB3, 0x43, 0xE7, 0xA1, + 0x8E, 0x43, 0xE7, 0xA1, 0xAB, 0x43, 0xE7, 0xA2, + 0x8C, 0x43, 0xE7, 0xA2, 0x91, 0x43, 0xE7, 0xA3, + 0x8A, 0x43, 0xE7, 0xA3, 0x8C, 0x43, 0xE7, 0xA3, + 0xBB, 0x43, 0xE7, 0xA4, 0xAA, 0x43, 0xE7, 0xA4, + 0xBA, 0x43, 0xE7, 0xA4, 0xBC, 0x43, 0xE7, 0xA4, + // Bytes 10c0 - 10ff + 0xBE, 0x43, 0xE7, 0xA5, 0x88, 0x43, 0xE7, 0xA5, + 0x89, 0x43, 0xE7, 0xA5, 0x90, 0x43, 0xE7, 0xA5, + 0x96, 0x43, 0xE7, 0xA5, 0x9D, 0x43, 0xE7, 0xA5, + 0x9E, 0x43, 0xE7, 0xA5, 0xA5, 0x43, 0xE7, 0xA5, + 0xBF, 0x43, 0xE7, 0xA6, 0x81, 0x43, 0xE7, 0xA6, + 0x8D, 0x43, 0xE7, 0xA6, 0x8E, 0x43, 0xE7, 0xA6, + 0x8F, 0x43, 0xE7, 0xA6, 0xAE, 0x43, 0xE7, 0xA6, + 0xB8, 0x43, 0xE7, 0xA6, 0xBE, 0x43, 0xE7, 0xA7, + // Bytes 1100 - 113f + 0x8A, 0x43, 0xE7, 0xA7, 0x98, 0x43, 0xE7, 0xA7, + 0xAB, 0x43, 0xE7, 0xA8, 0x9C, 0x43, 0xE7, 0xA9, + 0x80, 0x43, 0xE7, 0xA9, 0x8A, 0x43, 0xE7, 0xA9, + 0x8F, 0x43, 0xE7, 0xA9, 0xB4, 0x43, 0xE7, 0xA9, + 0xBA, 0x43, 0xE7, 0xAA, 0x81, 0x43, 0xE7, 0xAA, + 0xB1, 0x43, 0xE7, 0xAB, 0x8B, 0x43, 0xE7, 0xAB, + 0xAE, 0x43, 0xE7, 0xAB, 0xB9, 0x43, 0xE7, 0xAC, + 0xA0, 0x43, 0xE7, 0xAE, 0x8F, 0x43, 0xE7, 0xAF, + // Bytes 1140 - 117f + 0x80, 0x43, 0xE7, 0xAF, 0x86, 0x43, 0xE7, 0xAF, + 0x89, 0x43, 0xE7, 0xB0, 0xBE, 0x43, 0xE7, 0xB1, + 0xA0, 0x43, 0xE7, 0xB1, 0xB3, 0x43, 0xE7, 0xB1, + 0xBB, 0x43, 0xE7, 0xB2, 0x92, 0x43, 0xE7, 0xB2, + 0xBE, 0x43, 0xE7, 0xB3, 0x92, 0x43, 0xE7, 0xB3, + 0x96, 0x43, 0xE7, 0xB3, 0xA3, 0x43, 0xE7, 0xB3, + 0xA7, 0x43, 0xE7, 0xB3, 0xA8, 0x43, 0xE7, 0xB3, + 0xB8, 0x43, 0xE7, 0xB4, 0x80, 0x43, 0xE7, 0xB4, + // Bytes 1180 - 11bf + 0x90, 0x43, 0xE7, 0xB4, 0xA2, 0x43, 0xE7, 0xB4, + 0xAF, 0x43, 0xE7, 0xB5, 0x82, 0x43, 0xE7, 0xB5, + 0x9B, 0x43, 0xE7, 0xB5, 0xA3, 0x43, 0xE7, 0xB6, + 0xA0, 0x43, 0xE7, 0xB6, 0xBE, 0x43, 0xE7, 0xB7, + 0x87, 0x43, 0xE7, 0xB7, 0xB4, 0x43, 0xE7, 0xB8, + 0x82, 0x43, 0xE7, 0xB8, 0x89, 0x43, 0xE7, 0xB8, + 0xB7, 0x43, 0xE7, 0xB9, 0x81, 0x43, 0xE7, 0xB9, + 0x85, 0x43, 0xE7, 0xBC, 0xB6, 0x43, 0xE7, 0xBC, + // Bytes 11c0 - 11ff + 0xBE, 0x43, 0xE7, 0xBD, 0x91, 0x43, 0xE7, 0xBD, + 0xB2, 0x43, 0xE7, 0xBD, 0xB9, 0x43, 0xE7, 0xBD, + 0xBA, 0x43, 0xE7, 0xBE, 0x85, 0x43, 0xE7, 0xBE, + 0x8A, 0x43, 0xE7, 0xBE, 0x95, 0x43, 0xE7, 0xBE, + 0x9A, 0x43, 0xE7, 0xBE, 0xBD, 0x43, 0xE7, 0xBF, + 0xBA, 0x43, 0xE8, 0x80, 0x81, 0x43, 0xE8, 0x80, + 0x85, 0x43, 0xE8, 0x80, 0x8C, 0x43, 0xE8, 0x80, + 0x92, 0x43, 0xE8, 0x80, 0xB3, 0x43, 0xE8, 0x81, + // Bytes 1200 - 123f + 0x86, 0x43, 0xE8, 0x81, 0xA0, 0x43, 0xE8, 0x81, + 0xAF, 0x43, 0xE8, 0x81, 0xB0, 0x43, 0xE8, 0x81, + 0xBE, 0x43, 0xE8, 0x81, 0xBF, 0x43, 0xE8, 0x82, + 0x89, 0x43, 0xE8, 0x82, 0x8B, 0x43, 0xE8, 0x82, + 0xAD, 0x43, 0xE8, 0x82, 0xB2, 0x43, 0xE8, 0x84, + 0x83, 0x43, 0xE8, 0x84, 0xBE, 0x43, 0xE8, 0x87, + 0x98, 0x43, 0xE8, 0x87, 0xA3, 0x43, 0xE8, 0x87, + 0xA8, 0x43, 0xE8, 0x87, 0xAA, 0x43, 0xE8, 0x87, + // Bytes 1240 - 127f + 0xAD, 0x43, 0xE8, 0x87, 0xB3, 0x43, 0xE8, 0x87, + 0xBC, 0x43, 0xE8, 0x88, 0x81, 0x43, 0xE8, 0x88, + 0x84, 0x43, 0xE8, 0x88, 0x8C, 0x43, 0xE8, 0x88, + 0x98, 0x43, 0xE8, 0x88, 0x9B, 0x43, 0xE8, 0x88, + 0x9F, 0x43, 0xE8, 0x89, 0xAE, 0x43, 0xE8, 0x89, + 0xAF, 0x43, 0xE8, 0x89, 0xB2, 0x43, 0xE8, 0x89, + 0xB8, 0x43, 0xE8, 0x89, 0xB9, 0x43, 0xE8, 0x8A, + 0x8B, 0x43, 0xE8, 0x8A, 0x91, 0x43, 0xE8, 0x8A, + // Bytes 1280 - 12bf + 0x9D, 0x43, 0xE8, 0x8A, 0xB1, 0x43, 0xE8, 0x8A, + 0xB3, 0x43, 0xE8, 0x8A, 0xBD, 0x43, 0xE8, 0x8B, + 0xA5, 0x43, 0xE8, 0x8B, 0xA6, 0x43, 0xE8, 0x8C, + 0x9D, 0x43, 0xE8, 0x8C, 0xA3, 0x43, 0xE8, 0x8C, + 0xB6, 0x43, 0xE8, 0x8D, 0x92, 0x43, 0xE8, 0x8D, + 0x93, 0x43, 0xE8, 0x8D, 0xA3, 0x43, 0xE8, 0x8E, + 0xAD, 0x43, 0xE8, 0x8E, 0xBD, 0x43, 0xE8, 0x8F, + 0x89, 0x43, 0xE8, 0x8F, 0x8A, 0x43, 0xE8, 0x8F, + // Bytes 12c0 - 12ff + 0x8C, 0x43, 0xE8, 0x8F, 0x9C, 0x43, 0xE8, 0x8F, + 0xA7, 0x43, 0xE8, 0x8F, 0xAF, 0x43, 0xE8, 0x8F, + 0xB1, 0x43, 0xE8, 0x90, 0xBD, 0x43, 0xE8, 0x91, + 0x89, 0x43, 0xE8, 0x91, 0x97, 0x43, 0xE8, 0x93, + 0xAE, 0x43, 0xE8, 0x93, 0xB1, 0x43, 0xE8, 0x93, + 0xB3, 0x43, 0xE8, 0x93, 0xBC, 0x43, 0xE8, 0x94, + 0x96, 0x43, 0xE8, 0x95, 0xA4, 0x43, 0xE8, 0x97, + 0x8D, 0x43, 0xE8, 0x97, 0xBA, 0x43, 0xE8, 0x98, + // Bytes 1300 - 133f + 0x86, 0x43, 0xE8, 0x98, 0x92, 0x43, 0xE8, 0x98, + 0xAD, 0x43, 0xE8, 0x98, 0xBF, 0x43, 0xE8, 0x99, + 0x8D, 0x43, 0xE8, 0x99, 0x90, 0x43, 0xE8, 0x99, + 0x9C, 0x43, 0xE8, 0x99, 0xA7, 0x43, 0xE8, 0x99, + 0xA9, 0x43, 0xE8, 0x99, 0xAB, 0x43, 0xE8, 0x9A, + 0x88, 0x43, 0xE8, 0x9A, 0xA9, 0x43, 0xE8, 0x9B, + 0xA2, 0x43, 0xE8, 0x9C, 0x8E, 0x43, 0xE8, 0x9C, + 0xA8, 0x43, 0xE8, 0x9D, 0xAB, 0x43, 0xE8, 0x9D, + // Bytes 1340 - 137f + 0xB9, 0x43, 0xE8, 0x9E, 0x86, 0x43, 0xE8, 0x9E, + 0xBA, 0x43, 0xE8, 0x9F, 0xA1, 0x43, 0xE8, 0xA0, + 0x81, 0x43, 0xE8, 0xA0, 0x9F, 0x43, 0xE8, 0xA1, + 0x80, 0x43, 0xE8, 0xA1, 0x8C, 0x43, 0xE8, 0xA1, + 0xA0, 0x43, 0xE8, 0xA1, 0xA3, 0x43, 0xE8, 0xA3, + 0x82, 0x43, 0xE8, 0xA3, 0x8F, 0x43, 0xE8, 0xA3, + 0x97, 0x43, 0xE8, 0xA3, 0x9E, 0x43, 0xE8, 0xA3, + 0xA1, 0x43, 0xE8, 0xA3, 0xB8, 0x43, 0xE8, 0xA3, + // Bytes 1380 - 13bf + 0xBA, 0x43, 0xE8, 0xA4, 0x90, 0x43, 0xE8, 0xA5, + 0x81, 0x43, 0xE8, 0xA5, 0xA4, 0x43, 0xE8, 0xA5, + 0xBE, 0x43, 0xE8, 0xA6, 0x86, 0x43, 0xE8, 0xA6, + 0x8B, 0x43, 0xE8, 0xA6, 0x96, 0x43, 0xE8, 0xA7, + 0x92, 0x43, 0xE8, 0xA7, 0xA3, 0x43, 0xE8, 0xA8, + 0x80, 0x43, 0xE8, 0xAA, 0xA0, 0x43, 0xE8, 0xAA, + 0xAA, 0x43, 0xE8, 0xAA, 0xBF, 0x43, 0xE8, 0xAB, + 0x8B, 0x43, 0xE8, 0xAB, 0x92, 0x43, 0xE8, 0xAB, + // Bytes 13c0 - 13ff + 0x96, 0x43, 0xE8, 0xAB, 0xAD, 0x43, 0xE8, 0xAB, + 0xB8, 0x43, 0xE8, 0xAB, 0xBE, 0x43, 0xE8, 0xAC, + 0x81, 0x43, 0xE8, 0xAC, 0xB9, 0x43, 0xE8, 0xAD, + 0x98, 0x43, 0xE8, 0xAE, 0x80, 0x43, 0xE8, 0xAE, + 0x8A, 0x43, 0xE8, 0xB0, 0xB7, 0x43, 0xE8, 0xB1, + 0x86, 0x43, 0xE8, 0xB1, 0x88, 0x43, 0xE8, 0xB1, + 0x95, 0x43, 0xE8, 0xB1, 0xB8, 0x43, 0xE8, 0xB2, + 0x9D, 0x43, 0xE8, 0xB2, 0xA1, 0x43, 0xE8, 0xB2, + // Bytes 1400 - 143f + 0xA9, 0x43, 0xE8, 0xB2, 0xAB, 0x43, 0xE8, 0xB3, + 0x81, 0x43, 0xE8, 0xB3, 0x82, 0x43, 0xE8, 0xB3, + 0x87, 0x43, 0xE8, 0xB3, 0x88, 0x43, 0xE8, 0xB3, + 0x93, 0x43, 0xE8, 0xB4, 0x88, 0x43, 0xE8, 0xB4, + 0x9B, 0x43, 0xE8, 0xB5, 0xA4, 0x43, 0xE8, 0xB5, + 0xB0, 0x43, 0xE8, 0xB5, 0xB7, 0x43, 0xE8, 0xB6, + 0xB3, 0x43, 0xE8, 0xB6, 0xBC, 0x43, 0xE8, 0xB7, + 0x8B, 0x43, 0xE8, 0xB7, 0xAF, 0x43, 0xE8, 0xB7, + // Bytes 1440 - 147f + 0xB0, 0x43, 0xE8, 0xBA, 0xAB, 0x43, 0xE8, 0xBB, + 0x8A, 0x43, 0xE8, 0xBB, 0x94, 0x43, 0xE8, 0xBC, + 0xA6, 0x43, 0xE8, 0xBC, 0xAA, 0x43, 0xE8, 0xBC, + 0xB8, 0x43, 0xE8, 0xBC, 0xBB, 0x43, 0xE8, 0xBD, + 0xA2, 0x43, 0xE8, 0xBE, 0x9B, 0x43, 0xE8, 0xBE, + 0x9E, 0x43, 0xE8, 0xBE, 0xB0, 0x43, 0xE8, 0xBE, + 0xB5, 0x43, 0xE8, 0xBE, 0xB6, 0x43, 0xE9, 0x80, + 0xA3, 0x43, 0xE9, 0x80, 0xB8, 0x43, 0xE9, 0x81, + // Bytes 1480 - 14bf + 0x8A, 0x43, 0xE9, 0x81, 0xA9, 0x43, 0xE9, 0x81, + 0xB2, 0x43, 0xE9, 0x81, 0xBC, 0x43, 0xE9, 0x82, + 0x8F, 0x43, 0xE9, 0x82, 0x91, 0x43, 0xE9, 0x82, + 0x94, 0x43, 0xE9, 0x83, 0x8E, 0x43, 0xE9, 0x83, + 0x9E, 0x43, 0xE9, 0x83, 0xB1, 0x43, 0xE9, 0x83, + 0xBD, 0x43, 0xE9, 0x84, 0x91, 0x43, 0xE9, 0x84, + 0x9B, 0x43, 0xE9, 0x85, 0x89, 0x43, 0xE9, 0x85, + 0xAA, 0x43, 0xE9, 0x86, 0x99, 0x43, 0xE9, 0x86, + // Bytes 14c0 - 14ff + 0xB4, 0x43, 0xE9, 0x87, 0x86, 0x43, 0xE9, 0x87, + 0x8C, 0x43, 0xE9, 0x87, 0x8F, 0x43, 0xE9, 0x87, + 0x91, 0x43, 0xE9, 0x88, 0xB4, 0x43, 0xE9, 0x88, + 0xB8, 0x43, 0xE9, 0x89, 0xB6, 0x43, 0xE9, 0x89, + 0xBC, 0x43, 0xE9, 0x8B, 0x97, 0x43, 0xE9, 0x8B, + 0x98, 0x43, 0xE9, 0x8C, 0x84, 0x43, 0xE9, 0x8D, + 0x8A, 0x43, 0xE9, 0x8F, 0xB9, 0x43, 0xE9, 0x90, + 0x95, 0x43, 0xE9, 0x95, 0xB7, 0x43, 0xE9, 0x96, + // Bytes 1500 - 153f + 0x80, 0x43, 0xE9, 0x96, 0x8B, 0x43, 0xE9, 0x96, + 0xAD, 0x43, 0xE9, 0x96, 0xB7, 0x43, 0xE9, 0x98, + 0x9C, 0x43, 0xE9, 0x98, 0xAE, 0x43, 0xE9, 0x99, + 0x8B, 0x43, 0xE9, 0x99, 0x8D, 0x43, 0xE9, 0x99, + 0xB5, 0x43, 0xE9, 0x99, 0xB8, 0x43, 0xE9, 0x99, + 0xBC, 0x43, 0xE9, 0x9A, 0x86, 0x43, 0xE9, 0x9A, + 0xA3, 0x43, 0xE9, 0x9A, 0xB6, 0x43, 0xE9, 0x9A, + 0xB7, 0x43, 0xE9, 0x9A, 0xB8, 0x43, 0xE9, 0x9A, + // Bytes 1540 - 157f + 0xB9, 0x43, 0xE9, 0x9B, 0x83, 0x43, 0xE9, 0x9B, + 0xA2, 0x43, 0xE9, 0x9B, 0xA3, 0x43, 0xE9, 0x9B, + 0xA8, 0x43, 0xE9, 0x9B, 0xB6, 0x43, 0xE9, 0x9B, + 0xB7, 0x43, 0xE9, 0x9C, 0xA3, 0x43, 0xE9, 0x9C, + 0xB2, 0x43, 0xE9, 0x9D, 0x88, 0x43, 0xE9, 0x9D, + 0x91, 0x43, 0xE9, 0x9D, 0x96, 0x43, 0xE9, 0x9D, + 0x9E, 0x43, 0xE9, 0x9D, 0xA2, 0x43, 0xE9, 0x9D, + 0xA9, 0x43, 0xE9, 0x9F, 0x8B, 0x43, 0xE9, 0x9F, + // Bytes 1580 - 15bf + 0x9B, 0x43, 0xE9, 0x9F, 0xA0, 0x43, 0xE9, 0x9F, + 0xAD, 0x43, 0xE9, 0x9F, 0xB3, 0x43, 0xE9, 0x9F, + 0xBF, 0x43, 0xE9, 0xA0, 0x81, 0x43, 0xE9, 0xA0, + 0x85, 0x43, 0xE9, 0xA0, 0x8B, 0x43, 0xE9, 0xA0, + 0x98, 0x43, 0xE9, 0xA0, 0xA9, 0x43, 0xE9, 0xA0, + 0xBB, 0x43, 0xE9, 0xA1, 0x9E, 0x43, 0xE9, 0xA2, + 0xA8, 0x43, 0xE9, 0xA3, 0x9B, 0x43, 0xE9, 0xA3, + 0x9F, 0x43, 0xE9, 0xA3, 0xA2, 0x43, 0xE9, 0xA3, + // Bytes 15c0 - 15ff + 0xAF, 0x43, 0xE9, 0xA3, 0xBC, 0x43, 0xE9, 0xA4, + 0xA8, 0x43, 0xE9, 0xA4, 0xA9, 0x43, 0xE9, 0xA6, + 0x96, 0x43, 0xE9, 0xA6, 0x99, 0x43, 0xE9, 0xA6, + 0xA7, 0x43, 0xE9, 0xA6, 0xAC, 0x43, 0xE9, 0xA7, + 0x82, 0x43, 0xE9, 0xA7, 0xB1, 0x43, 0xE9, 0xA7, + 0xBE, 0x43, 0xE9, 0xA9, 0xAA, 0x43, 0xE9, 0xAA, + 0xA8, 0x43, 0xE9, 0xAB, 0x98, 0x43, 0xE9, 0xAB, + 0x9F, 0x43, 0xE9, 0xAC, 0x92, 0x43, 0xE9, 0xAC, + // Bytes 1600 - 163f + 0xA5, 0x43, 0xE9, 0xAC, 0xAF, 0x43, 0xE9, 0xAC, + 0xB2, 0x43, 0xE9, 0xAC, 0xBC, 0x43, 0xE9, 0xAD, + 0x9A, 0x43, 0xE9, 0xAD, 0xAF, 0x43, 0xE9, 0xB1, + 0x80, 0x43, 0xE9, 0xB1, 0x97, 0x43, 0xE9, 0xB3, + 0xA5, 0x43, 0xE9, 0xB3, 0xBD, 0x43, 0xE9, 0xB5, + 0xA7, 0x43, 0xE9, 0xB6, 0xB4, 0x43, 0xE9, 0xB7, + 0xBA, 0x43, 0xE9, 0xB8, 0x9E, 0x43, 0xE9, 0xB9, + 0xB5, 0x43, 0xE9, 0xB9, 0xBF, 0x43, 0xE9, 0xBA, + // Bytes 1640 - 167f + 0x97, 0x43, 0xE9, 0xBA, 0x9F, 0x43, 0xE9, 0xBA, + 0xA5, 0x43, 0xE9, 0xBA, 0xBB, 0x43, 0xE9, 0xBB, + 0x83, 0x43, 0xE9, 0xBB, 0x8D, 0x43, 0xE9, 0xBB, + 0x8E, 0x43, 0xE9, 0xBB, 0x91, 0x43, 0xE9, 0xBB, + 0xB9, 0x43, 0xE9, 0xBB, 0xBD, 0x43, 0xE9, 0xBB, + 0xBE, 0x43, 0xE9, 0xBC, 0x85, 0x43, 0xE9, 0xBC, + 0x8E, 0x43, 0xE9, 0xBC, 0x8F, 0x43, 0xE9, 0xBC, + 0x93, 0x43, 0xE9, 0xBC, 0x96, 0x43, 0xE9, 0xBC, + // Bytes 1680 - 16bf + 0xA0, 0x43, 0xE9, 0xBC, 0xBB, 0x43, 0xE9, 0xBD, + 0x83, 0x43, 0xE9, 0xBD, 0x8A, 0x43, 0xE9, 0xBD, + 0x92, 0x43, 0xE9, 0xBE, 0x8D, 0x43, 0xE9, 0xBE, + 0x8E, 0x43, 0xE9, 0xBE, 0x9C, 0x43, 0xE9, 0xBE, + 0x9F, 0x43, 0xE9, 0xBE, 0xA0, 0x43, 0xEA, 0x9D, + 0xAF, 0x44, 0xF0, 0xA0, 0x84, 0xA2, 0x44, 0xF0, + 0xA0, 0x94, 0x9C, 0x44, 0xF0, 0xA0, 0x94, 0xA5, + 0x44, 0xF0, 0xA0, 0x95, 0x8B, 0x44, 0xF0, 0xA0, + // Bytes 16c0 - 16ff + 0x98, 0xBA, 0x44, 0xF0, 0xA0, 0xA0, 0x84, 0x44, + 0xF0, 0xA0, 0xA3, 0x9E, 0x44, 0xF0, 0xA0, 0xA8, + 0xAC, 0x44, 0xF0, 0xA0, 0xAD, 0xA3, 0x44, 0xF0, + 0xA1, 0x93, 0xA4, 0x44, 0xF0, 0xA1, 0x9A, 0xA8, + 0x44, 0xF0, 0xA1, 0x9B, 0xAA, 0x44, 0xF0, 0xA1, + 0xA7, 0x88, 0x44, 0xF0, 0xA1, 0xAC, 0x98, 0x44, + 0xF0, 0xA1, 0xB4, 0x8B, 0x44, 0xF0, 0xA1, 0xB7, + 0xA4, 0x44, 0xF0, 0xA1, 0xB7, 0xA6, 0x44, 0xF0, + // Bytes 1700 - 173f + 0xA2, 0x86, 0x83, 0x44, 0xF0, 0xA2, 0x86, 0x9F, + 0x44, 0xF0, 0xA2, 0x8C, 0xB1, 0x44, 0xF0, 0xA2, + 0x9B, 0x94, 0x44, 0xF0, 0xA2, 0xA1, 0x84, 0x44, + 0xF0, 0xA2, 0xA1, 0x8A, 0x44, 0xF0, 0xA2, 0xAC, + 0x8C, 0x44, 0xF0, 0xA2, 0xAF, 0xB1, 0x44, 0xF0, + 0xA3, 0x80, 0x8A, 0x44, 0xF0, 0xA3, 0x8A, 0xB8, + 0x44, 0xF0, 0xA3, 0x8D, 0x9F, 0x44, 0xF0, 0xA3, + 0x8E, 0x93, 0x44, 0xF0, 0xA3, 0x8E, 0x9C, 0x44, + // Bytes 1740 - 177f + 0xF0, 0xA3, 0x8F, 0x83, 0x44, 0xF0, 0xA3, 0x8F, + 0x95, 0x44, 0xF0, 0xA3, 0x91, 0xAD, 0x44, 0xF0, + 0xA3, 0x9A, 0xA3, 0x44, 0xF0, 0xA3, 0xA2, 0xA7, + 0x44, 0xF0, 0xA3, 0xAA, 0x8D, 0x44, 0xF0, 0xA3, + 0xAB, 0xBA, 0x44, 0xF0, 0xA3, 0xB2, 0xBC, 0x44, + 0xF0, 0xA3, 0xB4, 0x9E, 0x44, 0xF0, 0xA3, 0xBB, + 0x91, 0x44, 0xF0, 0xA3, 0xBD, 0x9E, 0x44, 0xF0, + 0xA3, 0xBE, 0x8E, 0x44, 0xF0, 0xA4, 0x89, 0xA3, + // Bytes 1780 - 17bf + 0x44, 0xF0, 0xA4, 0x8B, 0xAE, 0x44, 0xF0, 0xA4, + 0x8E, 0xAB, 0x44, 0xF0, 0xA4, 0x98, 0x88, 0x44, + 0xF0, 0xA4, 0x9C, 0xB5, 0x44, 0xF0, 0xA4, 0xA0, + 0x94, 0x44, 0xF0, 0xA4, 0xB0, 0xB6, 0x44, 0xF0, + 0xA4, 0xB2, 0x92, 0x44, 0xF0, 0xA4, 0xBE, 0xA1, + 0x44, 0xF0, 0xA4, 0xBE, 0xB8, 0x44, 0xF0, 0xA5, + 0x81, 0x84, 0x44, 0xF0, 0xA5, 0x83, 0xB2, 0x44, + 0xF0, 0xA5, 0x83, 0xB3, 0x44, 0xF0, 0xA5, 0x84, + // Bytes 17c0 - 17ff + 0x99, 0x44, 0xF0, 0xA5, 0x84, 0xB3, 0x44, 0xF0, + 0xA5, 0x89, 0x89, 0x44, 0xF0, 0xA5, 0x90, 0x9D, + 0x44, 0xF0, 0xA5, 0x98, 0xA6, 0x44, 0xF0, 0xA5, + 0x9A, 0x9A, 0x44, 0xF0, 0xA5, 0x9B, 0x85, 0x44, + 0xF0, 0xA5, 0xA5, 0xBC, 0x44, 0xF0, 0xA5, 0xAA, + 0xA7, 0x44, 0xF0, 0xA5, 0xAE, 0xAB, 0x44, 0xF0, + 0xA5, 0xB2, 0x80, 0x44, 0xF0, 0xA5, 0xB3, 0x90, + 0x44, 0xF0, 0xA5, 0xBE, 0x86, 0x44, 0xF0, 0xA6, + // Bytes 1800 - 183f + 0x87, 0x9A, 0x44, 0xF0, 0xA6, 0x88, 0xA8, 0x44, + 0xF0, 0xA6, 0x89, 0x87, 0x44, 0xF0, 0xA6, 0x8B, + 0x99, 0x44, 0xF0, 0xA6, 0x8C, 0xBE, 0x44, 0xF0, + 0xA6, 0x93, 0x9A, 0x44, 0xF0, 0xA6, 0x94, 0xA3, + 0x44, 0xF0, 0xA6, 0x96, 0xA8, 0x44, 0xF0, 0xA6, + 0x9E, 0xA7, 0x44, 0xF0, 0xA6, 0x9E, 0xB5, 0x44, + 0xF0, 0xA6, 0xAC, 0xBC, 0x44, 0xF0, 0xA6, 0xB0, + 0xB6, 0x44, 0xF0, 0xA6, 0xB3, 0x95, 0x44, 0xF0, + // Bytes 1840 - 187f + 0xA6, 0xB5, 0xAB, 0x44, 0xF0, 0xA6, 0xBC, 0xAC, + 0x44, 0xF0, 0xA6, 0xBE, 0xB1, 0x44, 0xF0, 0xA7, + 0x83, 0x92, 0x44, 0xF0, 0xA7, 0x8F, 0x8A, 0x44, + 0xF0, 0xA7, 0x99, 0xA7, 0x44, 0xF0, 0xA7, 0xA2, + 0xAE, 0x44, 0xF0, 0xA7, 0xA5, 0xA6, 0x44, 0xF0, + 0xA7, 0xB2, 0xA8, 0x44, 0xF0, 0xA7, 0xBB, 0x93, + 0x44, 0xF0, 0xA7, 0xBC, 0xAF, 0x44, 0xF0, 0xA8, + 0x97, 0x92, 0x44, 0xF0, 0xA8, 0x97, 0xAD, 0x44, + // Bytes 1880 - 18bf + 0xF0, 0xA8, 0x9C, 0xAE, 0x44, 0xF0, 0xA8, 0xAF, + 0xBA, 0x44, 0xF0, 0xA8, 0xB5, 0xB7, 0x44, 0xF0, + 0xA9, 0x85, 0x85, 0x44, 0xF0, 0xA9, 0x87, 0x9F, + 0x44, 0xF0, 0xA9, 0x88, 0x9A, 0x44, 0xF0, 0xA9, + 0x90, 0x8A, 0x44, 0xF0, 0xA9, 0x92, 0x96, 0x44, + 0xF0, 0xA9, 0x96, 0xB6, 0x44, 0xF0, 0xA9, 0xAC, + 0xB0, 0x44, 0xF0, 0xAA, 0x83, 0x8E, 0x44, 0xF0, + 0xAA, 0x84, 0x85, 0x44, 0xF0, 0xAA, 0x88, 0x8E, + // Bytes 18c0 - 18ff + 0x44, 0xF0, 0xAA, 0x8A, 0x91, 0x44, 0xF0, 0xAA, + 0x8E, 0x92, 0x44, 0xF0, 0xAA, 0x98, 0x80, 0x06, + 0xE0, 0xA7, 0x87, 0xE0, 0xA6, 0xBE, 0x06, 0xE0, + 0xA7, 0x87, 0xE0, 0xA7, 0x97, 0x06, 0xE0, 0xAD, + 0x87, 0xE0, 0xAC, 0xBE, 0x06, 0xE0, 0xAD, 0x87, + 0xE0, 0xAD, 0x96, 0x06, 0xE0, 0xAD, 0x87, 0xE0, + 0xAD, 0x97, 0x06, 0xE0, 0xAE, 0x92, 0xE0, 0xAF, + 0x97, 0x06, 0xE0, 0xAF, 0x86, 0xE0, 0xAE, 0xBE, + // Bytes 1900 - 193f + 0x06, 0xE0, 0xAF, 0x86, 0xE0, 0xAF, 0x97, 0x06, + 0xE0, 0xAF, 0x87, 0xE0, 0xAE, 0xBE, 0x06, 0xE0, + 0xB2, 0xBF, 0xE0, 0xB3, 0x95, 0x06, 0xE0, 0xB3, + 0x86, 0xE0, 0xB3, 0x95, 0x06, 0xE0, 0xB3, 0x86, + 0xE0, 0xB3, 0x96, 0x06, 0xE0, 0xB5, 0x86, 0xE0, + 0xB4, 0xBE, 0x06, 0xE0, 0xB5, 0x86, 0xE0, 0xB5, + 0x97, 0x06, 0xE0, 0xB5, 0x87, 0xE0, 0xB4, 0xBE, + 0x06, 0xE0, 0xB7, 0x99, 0xE0, 0xB7, 0x9F, 0x06, + // Bytes 1940 - 197f + 0xE1, 0x80, 0xA5, 0xE1, 0x80, 0xAE, 0x06, 0xE1, + 0xAC, 0x85, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAC, + 0x87, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAC, 0x89, + 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAC, 0x8B, 0xE1, + 0xAC, 0xB5, 0x06, 0xE1, 0xAC, 0x8D, 0xE1, 0xAC, + 0xB5, 0x06, 0xE1, 0xAC, 0x91, 0xE1, 0xAC, 0xB5, + 0x06, 0xE1, 0xAC, 0xBA, 0xE1, 0xAC, 0xB5, 0x06, + 0xE1, 0xAC, 0xBC, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, + // Bytes 1980 - 19bf + 0xAC, 0xBE, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAC, + 0xBF, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAD, 0x82, + 0xE1, 0xAC, 0xB5, 0x08, 0xF0, 0x91, 0x84, 0xB1, + 0xF0, 0x91, 0x84, 0xA7, 0x08, 0xF0, 0x91, 0x84, + 0xB2, 0xF0, 0x91, 0x84, 0xA7, 0x09, 0xE0, 0xB3, + 0x86, 0xE0, 0xB3, 0x82, 0xE0, 0xB3, 0x95, 0x42, + 0x21, 0x21, 0x42, 0x21, 0x3F, 0x42, 0x2E, 0x2E, + 0x42, 0x30, 0x2C, 0x42, 0x30, 0x2E, 0x42, 0x31, + // Bytes 19c0 - 19ff + 0x2C, 0x42, 0x31, 0x2E, 0x42, 0x31, 0x30, 0x42, + 0x31, 0x31, 0x42, 0x31, 0x32, 0x42, 0x31, 0x33, + 0x42, 0x31, 0x34, 0x42, 0x31, 0x35, 0x42, 0x31, + 0x36, 0x42, 0x31, 0x37, 0x42, 0x31, 0x38, 0x42, + 0x31, 0x39, 0x42, 0x32, 0x2C, 0x42, 0x32, 0x2E, + 0x42, 0x32, 0x30, 0x42, 0x32, 0x31, 0x42, 0x32, + 0x32, 0x42, 0x32, 0x33, 0x42, 0x32, 0x34, 0x42, + 0x32, 0x35, 0x42, 0x32, 0x36, 0x42, 0x32, 0x37, + // Bytes 1a00 - 1a3f + 0x42, 0x32, 0x38, 0x42, 0x32, 0x39, 0x42, 0x33, + 0x2C, 0x42, 0x33, 0x2E, 0x42, 0x33, 0x30, 0x42, + 0x33, 0x31, 0x42, 0x33, 0x32, 0x42, 0x33, 0x33, + 0x42, 0x33, 0x34, 0x42, 0x33, 0x35, 0x42, 0x33, + 0x36, 0x42, 0x33, 0x37, 0x42, 0x33, 0x38, 0x42, + 0x33, 0x39, 0x42, 0x34, 0x2C, 0x42, 0x34, 0x2E, + 0x42, 0x34, 0x30, 0x42, 0x34, 0x31, 0x42, 0x34, + 0x32, 0x42, 0x34, 0x33, 0x42, 0x34, 0x34, 0x42, + // Bytes 1a40 - 1a7f + 0x34, 0x35, 0x42, 0x34, 0x36, 0x42, 0x34, 0x37, + 0x42, 0x34, 0x38, 0x42, 0x34, 0x39, 0x42, 0x35, + 0x2C, 0x42, 0x35, 0x2E, 0x42, 0x35, 0x30, 0x42, + 0x36, 0x2C, 0x42, 0x36, 0x2E, 0x42, 0x37, 0x2C, + 0x42, 0x37, 0x2E, 0x42, 0x38, 0x2C, 0x42, 0x38, + 0x2E, 0x42, 0x39, 0x2C, 0x42, 0x39, 0x2E, 0x42, + 0x3D, 0x3D, 0x42, 0x3F, 0x21, 0x42, 0x3F, 0x3F, + 0x42, 0x41, 0x55, 0x42, 0x42, 0x71, 0x42, 0x43, + // Bytes 1a80 - 1abf + 0x44, 0x42, 0x44, 0x4A, 0x42, 0x44, 0x5A, 0x42, + 0x44, 0x7A, 0x42, 0x47, 0x42, 0x42, 0x47, 0x79, + 0x42, 0x48, 0x50, 0x42, 0x48, 0x56, 0x42, 0x48, + 0x67, 0x42, 0x48, 0x7A, 0x42, 0x49, 0x49, 0x42, + 0x49, 0x4A, 0x42, 0x49, 0x55, 0x42, 0x49, 0x56, + 0x42, 0x49, 0x58, 0x42, 0x4B, 0x42, 0x42, 0x4B, + 0x4B, 0x42, 0x4B, 0x4D, 0x42, 0x4C, 0x4A, 0x42, + 0x4C, 0x6A, 0x42, 0x4D, 0x42, 0x42, 0x4D, 0x43, + // Bytes 1ac0 - 1aff + 0x42, 0x4D, 0x44, 0x42, 0x4D, 0x56, 0x42, 0x4D, + 0x57, 0x42, 0x4E, 0x4A, 0x42, 0x4E, 0x6A, 0x42, + 0x4E, 0x6F, 0x42, 0x50, 0x48, 0x42, 0x50, 0x52, + 0x42, 0x50, 0x61, 0x42, 0x52, 0x73, 0x42, 0x53, + 0x44, 0x42, 0x53, 0x4D, 0x42, 0x53, 0x53, 0x42, + 0x53, 0x76, 0x42, 0x54, 0x4D, 0x42, 0x56, 0x49, + 0x42, 0x57, 0x43, 0x42, 0x57, 0x5A, 0x42, 0x57, + 0x62, 0x42, 0x58, 0x49, 0x42, 0x63, 0x63, 0x42, + // Bytes 1b00 - 1b3f + 0x63, 0x64, 0x42, 0x63, 0x6D, 0x42, 0x64, 0x42, + 0x42, 0x64, 0x61, 0x42, 0x64, 0x6C, 0x42, 0x64, + 0x6D, 0x42, 0x64, 0x7A, 0x42, 0x65, 0x56, 0x42, + 0x66, 0x66, 0x42, 0x66, 0x69, 0x42, 0x66, 0x6C, + 0x42, 0x66, 0x6D, 0x42, 0x68, 0x61, 0x42, 0x69, + 0x69, 0x42, 0x69, 0x6A, 0x42, 0x69, 0x6E, 0x42, + 0x69, 0x76, 0x42, 0x69, 0x78, 0x42, 0x6B, 0x41, + 0x42, 0x6B, 0x56, 0x42, 0x6B, 0x57, 0x42, 0x6B, + // Bytes 1b40 - 1b7f + 0x67, 0x42, 0x6B, 0x6C, 0x42, 0x6B, 0x6D, 0x42, + 0x6B, 0x74, 0x42, 0x6C, 0x6A, 0x42, 0x6C, 0x6D, + 0x42, 0x6C, 0x6E, 0x42, 0x6C, 0x78, 0x42, 0x6D, + 0x32, 0x42, 0x6D, 0x33, 0x42, 0x6D, 0x41, 0x42, + 0x6D, 0x56, 0x42, 0x6D, 0x57, 0x42, 0x6D, 0x62, + 0x42, 0x6D, 0x67, 0x42, 0x6D, 0x6C, 0x42, 0x6D, + 0x6D, 0x42, 0x6D, 0x73, 0x42, 0x6E, 0x41, 0x42, + 0x6E, 0x46, 0x42, 0x6E, 0x56, 0x42, 0x6E, 0x57, + // Bytes 1b80 - 1bbf + 0x42, 0x6E, 0x6A, 0x42, 0x6E, 0x6D, 0x42, 0x6E, + 0x73, 0x42, 0x6F, 0x56, 0x42, 0x70, 0x41, 0x42, + 0x70, 0x46, 0x42, 0x70, 0x56, 0x42, 0x70, 0x57, + 0x42, 0x70, 0x63, 0x42, 0x70, 0x73, 0x42, 0x73, + 0x72, 0x42, 0x73, 0x74, 0x42, 0x76, 0x69, 0x42, + 0x78, 0x69, 0x43, 0x28, 0x31, 0x29, 0x43, 0x28, + 0x32, 0x29, 0x43, 0x28, 0x33, 0x29, 0x43, 0x28, + 0x34, 0x29, 0x43, 0x28, 0x35, 0x29, 0x43, 0x28, + // Bytes 1bc0 - 1bff + 0x36, 0x29, 0x43, 0x28, 0x37, 0x29, 0x43, 0x28, + 0x38, 0x29, 0x43, 0x28, 0x39, 0x29, 0x43, 0x28, + 0x41, 0x29, 0x43, 0x28, 0x42, 0x29, 0x43, 0x28, + 0x43, 0x29, 0x43, 0x28, 0x44, 0x29, 0x43, 0x28, + 0x45, 0x29, 0x43, 0x28, 0x46, 0x29, 0x43, 0x28, + 0x47, 0x29, 0x43, 0x28, 0x48, 0x29, 0x43, 0x28, + 0x49, 0x29, 0x43, 0x28, 0x4A, 0x29, 0x43, 0x28, + 0x4B, 0x29, 0x43, 0x28, 0x4C, 0x29, 0x43, 0x28, + // Bytes 1c00 - 1c3f + 0x4D, 0x29, 0x43, 0x28, 0x4E, 0x29, 0x43, 0x28, + 0x4F, 0x29, 0x43, 0x28, 0x50, 0x29, 0x43, 0x28, + 0x51, 0x29, 0x43, 0x28, 0x52, 0x29, 0x43, 0x28, + 0x53, 0x29, 0x43, 0x28, 0x54, 0x29, 0x43, 0x28, + 0x55, 0x29, 0x43, 0x28, 0x56, 0x29, 0x43, 0x28, + 0x57, 0x29, 0x43, 0x28, 0x58, 0x29, 0x43, 0x28, + 0x59, 0x29, 0x43, 0x28, 0x5A, 0x29, 0x43, 0x28, + 0x61, 0x29, 0x43, 0x28, 0x62, 0x29, 0x43, 0x28, + // Bytes 1c40 - 1c7f + 0x63, 0x29, 0x43, 0x28, 0x64, 0x29, 0x43, 0x28, + 0x65, 0x29, 0x43, 0x28, 0x66, 0x29, 0x43, 0x28, + 0x67, 0x29, 0x43, 0x28, 0x68, 0x29, 0x43, 0x28, + 0x69, 0x29, 0x43, 0x28, 0x6A, 0x29, 0x43, 0x28, + 0x6B, 0x29, 0x43, 0x28, 0x6C, 0x29, 0x43, 0x28, + 0x6D, 0x29, 0x43, 0x28, 0x6E, 0x29, 0x43, 0x28, + 0x6F, 0x29, 0x43, 0x28, 0x70, 0x29, 0x43, 0x28, + 0x71, 0x29, 0x43, 0x28, 0x72, 0x29, 0x43, 0x28, + // Bytes 1c80 - 1cbf + 0x73, 0x29, 0x43, 0x28, 0x74, 0x29, 0x43, 0x28, + 0x75, 0x29, 0x43, 0x28, 0x76, 0x29, 0x43, 0x28, + 0x77, 0x29, 0x43, 0x28, 0x78, 0x29, 0x43, 0x28, + 0x79, 0x29, 0x43, 0x28, 0x7A, 0x29, 0x43, 0x2E, + 0x2E, 0x2E, 0x43, 0x31, 0x30, 0x2E, 0x43, 0x31, + 0x31, 0x2E, 0x43, 0x31, 0x32, 0x2E, 0x43, 0x31, + 0x33, 0x2E, 0x43, 0x31, 0x34, 0x2E, 0x43, 0x31, + 0x35, 0x2E, 0x43, 0x31, 0x36, 0x2E, 0x43, 0x31, + // Bytes 1cc0 - 1cff + 0x37, 0x2E, 0x43, 0x31, 0x38, 0x2E, 0x43, 0x31, + 0x39, 0x2E, 0x43, 0x32, 0x30, 0x2E, 0x43, 0x3A, + 0x3A, 0x3D, 0x43, 0x3D, 0x3D, 0x3D, 0x43, 0x43, + 0x6F, 0x2E, 0x43, 0x46, 0x41, 0x58, 0x43, 0x47, + 0x48, 0x7A, 0x43, 0x47, 0x50, 0x61, 0x43, 0x49, + 0x49, 0x49, 0x43, 0x4C, 0x54, 0x44, 0x43, 0x4C, + 0xC2, 0xB7, 0x43, 0x4D, 0x48, 0x7A, 0x43, 0x4D, + 0x50, 0x61, 0x43, 0x4D, 0xCE, 0xA9, 0x43, 0x50, + // Bytes 1d00 - 1d3f + 0x50, 0x4D, 0x43, 0x50, 0x50, 0x56, 0x43, 0x50, + 0x54, 0x45, 0x43, 0x54, 0x45, 0x4C, 0x43, 0x54, + 0x48, 0x7A, 0x43, 0x56, 0x49, 0x49, 0x43, 0x58, + 0x49, 0x49, 0x43, 0x61, 0x2F, 0x63, 0x43, 0x61, + 0x2F, 0x73, 0x43, 0x61, 0xCA, 0xBE, 0x43, 0x62, + 0x61, 0x72, 0x43, 0x63, 0x2F, 0x6F, 0x43, 0x63, + 0x2F, 0x75, 0x43, 0x63, 0x61, 0x6C, 0x43, 0x63, + 0x6D, 0x32, 0x43, 0x63, 0x6D, 0x33, 0x43, 0x64, + // Bytes 1d40 - 1d7f + 0x6D, 0x32, 0x43, 0x64, 0x6D, 0x33, 0x43, 0x65, + 0x72, 0x67, 0x43, 0x66, 0x66, 0x69, 0x43, 0x66, + 0x66, 0x6C, 0x43, 0x67, 0x61, 0x6C, 0x43, 0x68, + 0x50, 0x61, 0x43, 0x69, 0x69, 0x69, 0x43, 0x6B, + 0x48, 0x7A, 0x43, 0x6B, 0x50, 0x61, 0x43, 0x6B, + 0x6D, 0x32, 0x43, 0x6B, 0x6D, 0x33, 0x43, 0x6B, + 0xCE, 0xA9, 0x43, 0x6C, 0x6F, 0x67, 0x43, 0x6C, + 0xC2, 0xB7, 0x43, 0x6D, 0x69, 0x6C, 0x43, 0x6D, + // Bytes 1d80 - 1dbf + 0x6D, 0x32, 0x43, 0x6D, 0x6D, 0x33, 0x43, 0x6D, + 0x6F, 0x6C, 0x43, 0x72, 0x61, 0x64, 0x43, 0x76, + 0x69, 0x69, 0x43, 0x78, 0x69, 0x69, 0x43, 0xC2, + 0xB0, 0x43, 0x43, 0xC2, 0xB0, 0x46, 0x43, 0xCA, + 0xBC, 0x6E, 0x43, 0xCE, 0xBC, 0x41, 0x43, 0xCE, + 0xBC, 0x46, 0x43, 0xCE, 0xBC, 0x56, 0x43, 0xCE, + 0xBC, 0x57, 0x43, 0xCE, 0xBC, 0x67, 0x43, 0xCE, + 0xBC, 0x6C, 0x43, 0xCE, 0xBC, 0x6D, 0x43, 0xCE, + // Bytes 1dc0 - 1dff + 0xBC, 0x73, 0x44, 0x28, 0x31, 0x30, 0x29, 0x44, + 0x28, 0x31, 0x31, 0x29, 0x44, 0x28, 0x31, 0x32, + 0x29, 0x44, 0x28, 0x31, 0x33, 0x29, 0x44, 0x28, + 0x31, 0x34, 0x29, 0x44, 0x28, 0x31, 0x35, 0x29, + 0x44, 0x28, 0x31, 0x36, 0x29, 0x44, 0x28, 0x31, + 0x37, 0x29, 0x44, 0x28, 0x31, 0x38, 0x29, 0x44, + 0x28, 0x31, 0x39, 0x29, 0x44, 0x28, 0x32, 0x30, + 0x29, 0x44, 0x30, 0xE7, 0x82, 0xB9, 0x44, 0x31, + // Bytes 1e00 - 1e3f + 0xE2, 0x81, 0x84, 0x44, 0x31, 0xE6, 0x97, 0xA5, + 0x44, 0x31, 0xE6, 0x9C, 0x88, 0x44, 0x31, 0xE7, + 0x82, 0xB9, 0x44, 0x32, 0xE6, 0x97, 0xA5, 0x44, + 0x32, 0xE6, 0x9C, 0x88, 0x44, 0x32, 0xE7, 0x82, + 0xB9, 0x44, 0x33, 0xE6, 0x97, 0xA5, 0x44, 0x33, + 0xE6, 0x9C, 0x88, 0x44, 0x33, 0xE7, 0x82, 0xB9, + 0x44, 0x34, 0xE6, 0x97, 0xA5, 0x44, 0x34, 0xE6, + 0x9C, 0x88, 0x44, 0x34, 0xE7, 0x82, 0xB9, 0x44, + // Bytes 1e40 - 1e7f + 0x35, 0xE6, 0x97, 0xA5, 0x44, 0x35, 0xE6, 0x9C, + 0x88, 0x44, 0x35, 0xE7, 0x82, 0xB9, 0x44, 0x36, + 0xE6, 0x97, 0xA5, 0x44, 0x36, 0xE6, 0x9C, 0x88, + 0x44, 0x36, 0xE7, 0x82, 0xB9, 0x44, 0x37, 0xE6, + 0x97, 0xA5, 0x44, 0x37, 0xE6, 0x9C, 0x88, 0x44, + 0x37, 0xE7, 0x82, 0xB9, 0x44, 0x38, 0xE6, 0x97, + 0xA5, 0x44, 0x38, 0xE6, 0x9C, 0x88, 0x44, 0x38, + 0xE7, 0x82, 0xB9, 0x44, 0x39, 0xE6, 0x97, 0xA5, + // Bytes 1e80 - 1ebf + 0x44, 0x39, 0xE6, 0x9C, 0x88, 0x44, 0x39, 0xE7, + 0x82, 0xB9, 0x44, 0x56, 0x49, 0x49, 0x49, 0x44, + 0x61, 0x2E, 0x6D, 0x2E, 0x44, 0x6B, 0x63, 0x61, + 0x6C, 0x44, 0x70, 0x2E, 0x6D, 0x2E, 0x44, 0x76, + 0x69, 0x69, 0x69, 0x44, 0xD5, 0xA5, 0xD6, 0x82, + 0x44, 0xD5, 0xB4, 0xD5, 0xA5, 0x44, 0xD5, 0xB4, + 0xD5, 0xAB, 0x44, 0xD5, 0xB4, 0xD5, 0xAD, 0x44, + 0xD5, 0xB4, 0xD5, 0xB6, 0x44, 0xD5, 0xBE, 0xD5, + // Bytes 1ec0 - 1eff + 0xB6, 0x44, 0xD7, 0x90, 0xD7, 0x9C, 0x44, 0xD8, + 0xA7, 0xD9, 0xB4, 0x44, 0xD8, 0xA8, 0xD8, 0xAC, + 0x44, 0xD8, 0xA8, 0xD8, 0xAD, 0x44, 0xD8, 0xA8, + 0xD8, 0xAE, 0x44, 0xD8, 0xA8, 0xD8, 0xB1, 0x44, + 0xD8, 0xA8, 0xD8, 0xB2, 0x44, 0xD8, 0xA8, 0xD9, + 0x85, 0x44, 0xD8, 0xA8, 0xD9, 0x86, 0x44, 0xD8, + 0xA8, 0xD9, 0x87, 0x44, 0xD8, 0xA8, 0xD9, 0x89, + 0x44, 0xD8, 0xA8, 0xD9, 0x8A, 0x44, 0xD8, 0xAA, + // Bytes 1f00 - 1f3f + 0xD8, 0xAC, 0x44, 0xD8, 0xAA, 0xD8, 0xAD, 0x44, + 0xD8, 0xAA, 0xD8, 0xAE, 0x44, 0xD8, 0xAA, 0xD8, + 0xB1, 0x44, 0xD8, 0xAA, 0xD8, 0xB2, 0x44, 0xD8, + 0xAA, 0xD9, 0x85, 0x44, 0xD8, 0xAA, 0xD9, 0x86, + 0x44, 0xD8, 0xAA, 0xD9, 0x87, 0x44, 0xD8, 0xAA, + 0xD9, 0x89, 0x44, 0xD8, 0xAA, 0xD9, 0x8A, 0x44, + 0xD8, 0xAB, 0xD8, 0xAC, 0x44, 0xD8, 0xAB, 0xD8, + 0xB1, 0x44, 0xD8, 0xAB, 0xD8, 0xB2, 0x44, 0xD8, + // Bytes 1f40 - 1f7f + 0xAB, 0xD9, 0x85, 0x44, 0xD8, 0xAB, 0xD9, 0x86, + 0x44, 0xD8, 0xAB, 0xD9, 0x87, 0x44, 0xD8, 0xAB, + 0xD9, 0x89, 0x44, 0xD8, 0xAB, 0xD9, 0x8A, 0x44, + 0xD8, 0xAC, 0xD8, 0xAD, 0x44, 0xD8, 0xAC, 0xD9, + 0x85, 0x44, 0xD8, 0xAC, 0xD9, 0x89, 0x44, 0xD8, + 0xAC, 0xD9, 0x8A, 0x44, 0xD8, 0xAD, 0xD8, 0xAC, + 0x44, 0xD8, 0xAD, 0xD9, 0x85, 0x44, 0xD8, 0xAD, + 0xD9, 0x89, 0x44, 0xD8, 0xAD, 0xD9, 0x8A, 0x44, + // Bytes 1f80 - 1fbf + 0xD8, 0xAE, 0xD8, 0xAC, 0x44, 0xD8, 0xAE, 0xD8, + 0xAD, 0x44, 0xD8, 0xAE, 0xD9, 0x85, 0x44, 0xD8, + 0xAE, 0xD9, 0x89, 0x44, 0xD8, 0xAE, 0xD9, 0x8A, + 0x44, 0xD8, 0xB3, 0xD8, 0xAC, 0x44, 0xD8, 0xB3, + 0xD8, 0xAD, 0x44, 0xD8, 0xB3, 0xD8, 0xAE, 0x44, + 0xD8, 0xB3, 0xD8, 0xB1, 0x44, 0xD8, 0xB3, 0xD9, + 0x85, 0x44, 0xD8, 0xB3, 0xD9, 0x87, 0x44, 0xD8, + 0xB3, 0xD9, 0x89, 0x44, 0xD8, 0xB3, 0xD9, 0x8A, + // Bytes 1fc0 - 1fff + 0x44, 0xD8, 0xB4, 0xD8, 0xAC, 0x44, 0xD8, 0xB4, + 0xD8, 0xAD, 0x44, 0xD8, 0xB4, 0xD8, 0xAE, 0x44, + 0xD8, 0xB4, 0xD8, 0xB1, 0x44, 0xD8, 0xB4, 0xD9, + 0x85, 0x44, 0xD8, 0xB4, 0xD9, 0x87, 0x44, 0xD8, + 0xB4, 0xD9, 0x89, 0x44, 0xD8, 0xB4, 0xD9, 0x8A, + 0x44, 0xD8, 0xB5, 0xD8, 0xAD, 0x44, 0xD8, 0xB5, + 0xD8, 0xAE, 0x44, 0xD8, 0xB5, 0xD8, 0xB1, 0x44, + 0xD8, 0xB5, 0xD9, 0x85, 0x44, 0xD8, 0xB5, 0xD9, + // Bytes 2000 - 203f + 0x89, 0x44, 0xD8, 0xB5, 0xD9, 0x8A, 0x44, 0xD8, + 0xB6, 0xD8, 0xAC, 0x44, 0xD8, 0xB6, 0xD8, 0xAD, + 0x44, 0xD8, 0xB6, 0xD8, 0xAE, 0x44, 0xD8, 0xB6, + 0xD8, 0xB1, 0x44, 0xD8, 0xB6, 0xD9, 0x85, 0x44, + 0xD8, 0xB6, 0xD9, 0x89, 0x44, 0xD8, 0xB6, 0xD9, + 0x8A, 0x44, 0xD8, 0xB7, 0xD8, 0xAD, 0x44, 0xD8, + 0xB7, 0xD9, 0x85, 0x44, 0xD8, 0xB7, 0xD9, 0x89, + 0x44, 0xD8, 0xB7, 0xD9, 0x8A, 0x44, 0xD8, 0xB8, + // Bytes 2040 - 207f + 0xD9, 0x85, 0x44, 0xD8, 0xB9, 0xD8, 0xAC, 0x44, + 0xD8, 0xB9, 0xD9, 0x85, 0x44, 0xD8, 0xB9, 0xD9, + 0x89, 0x44, 0xD8, 0xB9, 0xD9, 0x8A, 0x44, 0xD8, + 0xBA, 0xD8, 0xAC, 0x44, 0xD8, 0xBA, 0xD9, 0x85, + 0x44, 0xD8, 0xBA, 0xD9, 0x89, 0x44, 0xD8, 0xBA, + 0xD9, 0x8A, 0x44, 0xD9, 0x81, 0xD8, 0xAC, 0x44, + 0xD9, 0x81, 0xD8, 0xAD, 0x44, 0xD9, 0x81, 0xD8, + 0xAE, 0x44, 0xD9, 0x81, 0xD9, 0x85, 0x44, 0xD9, + // Bytes 2080 - 20bf + 0x81, 0xD9, 0x89, 0x44, 0xD9, 0x81, 0xD9, 0x8A, + 0x44, 0xD9, 0x82, 0xD8, 0xAD, 0x44, 0xD9, 0x82, + 0xD9, 0x85, 0x44, 0xD9, 0x82, 0xD9, 0x89, 0x44, + 0xD9, 0x82, 0xD9, 0x8A, 0x44, 0xD9, 0x83, 0xD8, + 0xA7, 0x44, 0xD9, 0x83, 0xD8, 0xAC, 0x44, 0xD9, + 0x83, 0xD8, 0xAD, 0x44, 0xD9, 0x83, 0xD8, 0xAE, + 0x44, 0xD9, 0x83, 0xD9, 0x84, 0x44, 0xD9, 0x83, + 0xD9, 0x85, 0x44, 0xD9, 0x83, 0xD9, 0x89, 0x44, + // Bytes 20c0 - 20ff + 0xD9, 0x83, 0xD9, 0x8A, 0x44, 0xD9, 0x84, 0xD8, + 0xA7, 0x44, 0xD9, 0x84, 0xD8, 0xAC, 0x44, 0xD9, + 0x84, 0xD8, 0xAD, 0x44, 0xD9, 0x84, 0xD8, 0xAE, + 0x44, 0xD9, 0x84, 0xD9, 0x85, 0x44, 0xD9, 0x84, + 0xD9, 0x87, 0x44, 0xD9, 0x84, 0xD9, 0x89, 0x44, + 0xD9, 0x84, 0xD9, 0x8A, 0x44, 0xD9, 0x85, 0xD8, + 0xA7, 0x44, 0xD9, 0x85, 0xD8, 0xAC, 0x44, 0xD9, + 0x85, 0xD8, 0xAD, 0x44, 0xD9, 0x85, 0xD8, 0xAE, + // Bytes 2100 - 213f + 0x44, 0xD9, 0x85, 0xD9, 0x85, 0x44, 0xD9, 0x85, + 0xD9, 0x89, 0x44, 0xD9, 0x85, 0xD9, 0x8A, 0x44, + 0xD9, 0x86, 0xD8, 0xAC, 0x44, 0xD9, 0x86, 0xD8, + 0xAD, 0x44, 0xD9, 0x86, 0xD8, 0xAE, 0x44, 0xD9, + 0x86, 0xD8, 0xB1, 0x44, 0xD9, 0x86, 0xD8, 0xB2, + 0x44, 0xD9, 0x86, 0xD9, 0x85, 0x44, 0xD9, 0x86, + 0xD9, 0x86, 0x44, 0xD9, 0x86, 0xD9, 0x87, 0x44, + 0xD9, 0x86, 0xD9, 0x89, 0x44, 0xD9, 0x86, 0xD9, + // Bytes 2140 - 217f + 0x8A, 0x44, 0xD9, 0x87, 0xD8, 0xAC, 0x44, 0xD9, + 0x87, 0xD9, 0x85, 0x44, 0xD9, 0x87, 0xD9, 0x89, + 0x44, 0xD9, 0x87, 0xD9, 0x8A, 0x44, 0xD9, 0x88, + 0xD9, 0xB4, 0x44, 0xD9, 0x8A, 0xD8, 0xAC, 0x44, + 0xD9, 0x8A, 0xD8, 0xAD, 0x44, 0xD9, 0x8A, 0xD8, + 0xAE, 0x44, 0xD9, 0x8A, 0xD8, 0xB1, 0x44, 0xD9, + 0x8A, 0xD8, 0xB2, 0x44, 0xD9, 0x8A, 0xD9, 0x85, + 0x44, 0xD9, 0x8A, 0xD9, 0x86, 0x44, 0xD9, 0x8A, + // Bytes 2180 - 21bf + 0xD9, 0x87, 0x44, 0xD9, 0x8A, 0xD9, 0x89, 0x44, + 0xD9, 0x8A, 0xD9, 0x8A, 0x44, 0xD9, 0x8A, 0xD9, + 0xB4, 0x44, 0xDB, 0x87, 0xD9, 0xB4, 0x45, 0x28, + 0xE1, 0x84, 0x80, 0x29, 0x45, 0x28, 0xE1, 0x84, + 0x82, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x83, 0x29, + 0x45, 0x28, 0xE1, 0x84, 0x85, 0x29, 0x45, 0x28, + 0xE1, 0x84, 0x86, 0x29, 0x45, 0x28, 0xE1, 0x84, + 0x87, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x89, 0x29, + // Bytes 21c0 - 21ff + 0x45, 0x28, 0xE1, 0x84, 0x8B, 0x29, 0x45, 0x28, + 0xE1, 0x84, 0x8C, 0x29, 0x45, 0x28, 0xE1, 0x84, + 0x8E, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x8F, 0x29, + 0x45, 0x28, 0xE1, 0x84, 0x90, 0x29, 0x45, 0x28, + 0xE1, 0x84, 0x91, 0x29, 0x45, 0x28, 0xE1, 0x84, + 0x92, 0x29, 0x45, 0x28, 0xE4, 0xB8, 0x80, 0x29, + 0x45, 0x28, 0xE4, 0xB8, 0x83, 0x29, 0x45, 0x28, + 0xE4, 0xB8, 0x89, 0x29, 0x45, 0x28, 0xE4, 0xB9, + // Bytes 2200 - 223f + 0x9D, 0x29, 0x45, 0x28, 0xE4, 0xBA, 0x8C, 0x29, + 0x45, 0x28, 0xE4, 0xBA, 0x94, 0x29, 0x45, 0x28, + 0xE4, 0xBB, 0xA3, 0x29, 0x45, 0x28, 0xE4, 0xBC, + 0x81, 0x29, 0x45, 0x28, 0xE4, 0xBC, 0x91, 0x29, + 0x45, 0x28, 0xE5, 0x85, 0xAB, 0x29, 0x45, 0x28, + 0xE5, 0x85, 0xAD, 0x29, 0x45, 0x28, 0xE5, 0x8A, + 0xB4, 0x29, 0x45, 0x28, 0xE5, 0x8D, 0x81, 0x29, + 0x45, 0x28, 0xE5, 0x8D, 0x94, 0x29, 0x45, 0x28, + // Bytes 2240 - 227f + 0xE5, 0x90, 0x8D, 0x29, 0x45, 0x28, 0xE5, 0x91, + 0xBC, 0x29, 0x45, 0x28, 0xE5, 0x9B, 0x9B, 0x29, + 0x45, 0x28, 0xE5, 0x9C, 0x9F, 0x29, 0x45, 0x28, + 0xE5, 0xAD, 0xA6, 0x29, 0x45, 0x28, 0xE6, 0x97, + 0xA5, 0x29, 0x45, 0x28, 0xE6, 0x9C, 0x88, 0x29, + 0x45, 0x28, 0xE6, 0x9C, 0x89, 0x29, 0x45, 0x28, + 0xE6, 0x9C, 0xA8, 0x29, 0x45, 0x28, 0xE6, 0xA0, + 0xAA, 0x29, 0x45, 0x28, 0xE6, 0xB0, 0xB4, 0x29, + // Bytes 2280 - 22bf + 0x45, 0x28, 0xE7, 0x81, 0xAB, 0x29, 0x45, 0x28, + 0xE7, 0x89, 0xB9, 0x29, 0x45, 0x28, 0xE7, 0x9B, + 0xA3, 0x29, 0x45, 0x28, 0xE7, 0xA4, 0xBE, 0x29, + 0x45, 0x28, 0xE7, 0xA5, 0x9D, 0x29, 0x45, 0x28, + 0xE7, 0xA5, 0xAD, 0x29, 0x45, 0x28, 0xE8, 0x87, + 0xAA, 0x29, 0x45, 0x28, 0xE8, 0x87, 0xB3, 0x29, + 0x45, 0x28, 0xE8, 0xB2, 0xA1, 0x29, 0x45, 0x28, + 0xE8, 0xB3, 0x87, 0x29, 0x45, 0x28, 0xE9, 0x87, + // Bytes 22c0 - 22ff + 0x91, 0x29, 0x45, 0x30, 0xE2, 0x81, 0x84, 0x33, + 0x45, 0x31, 0x30, 0xE6, 0x97, 0xA5, 0x45, 0x31, + 0x30, 0xE6, 0x9C, 0x88, 0x45, 0x31, 0x30, 0xE7, + 0x82, 0xB9, 0x45, 0x31, 0x31, 0xE6, 0x97, 0xA5, + 0x45, 0x31, 0x31, 0xE6, 0x9C, 0x88, 0x45, 0x31, + 0x31, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x32, 0xE6, + 0x97, 0xA5, 0x45, 0x31, 0x32, 0xE6, 0x9C, 0x88, + 0x45, 0x31, 0x32, 0xE7, 0x82, 0xB9, 0x45, 0x31, + // Bytes 2300 - 233f + 0x33, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x33, 0xE7, + 0x82, 0xB9, 0x45, 0x31, 0x34, 0xE6, 0x97, 0xA5, + 0x45, 0x31, 0x34, 0xE7, 0x82, 0xB9, 0x45, 0x31, + 0x35, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x35, 0xE7, + 0x82, 0xB9, 0x45, 0x31, 0x36, 0xE6, 0x97, 0xA5, + 0x45, 0x31, 0x36, 0xE7, 0x82, 0xB9, 0x45, 0x31, + 0x37, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x37, 0xE7, + 0x82, 0xB9, 0x45, 0x31, 0x38, 0xE6, 0x97, 0xA5, + // Bytes 2340 - 237f + 0x45, 0x31, 0x38, 0xE7, 0x82, 0xB9, 0x45, 0x31, + 0x39, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x39, 0xE7, + 0x82, 0xB9, 0x45, 0x31, 0xE2, 0x81, 0x84, 0x32, + 0x45, 0x31, 0xE2, 0x81, 0x84, 0x33, 0x45, 0x31, + 0xE2, 0x81, 0x84, 0x34, 0x45, 0x31, 0xE2, 0x81, + 0x84, 0x35, 0x45, 0x31, 0xE2, 0x81, 0x84, 0x36, + 0x45, 0x31, 0xE2, 0x81, 0x84, 0x37, 0x45, 0x31, + 0xE2, 0x81, 0x84, 0x38, 0x45, 0x31, 0xE2, 0x81, + // Bytes 2380 - 23bf + 0x84, 0x39, 0x45, 0x32, 0x30, 0xE6, 0x97, 0xA5, + 0x45, 0x32, 0x30, 0xE7, 0x82, 0xB9, 0x45, 0x32, + 0x31, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0x31, 0xE7, + 0x82, 0xB9, 0x45, 0x32, 0x32, 0xE6, 0x97, 0xA5, + 0x45, 0x32, 0x32, 0xE7, 0x82, 0xB9, 0x45, 0x32, + 0x33, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0x33, 0xE7, + 0x82, 0xB9, 0x45, 0x32, 0x34, 0xE6, 0x97, 0xA5, + 0x45, 0x32, 0x34, 0xE7, 0x82, 0xB9, 0x45, 0x32, + // Bytes 23c0 - 23ff + 0x35, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0x36, 0xE6, + 0x97, 0xA5, 0x45, 0x32, 0x37, 0xE6, 0x97, 0xA5, + 0x45, 0x32, 0x38, 0xE6, 0x97, 0xA5, 0x45, 0x32, + 0x39, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0xE2, 0x81, + 0x84, 0x33, 0x45, 0x32, 0xE2, 0x81, 0x84, 0x35, + 0x45, 0x33, 0x30, 0xE6, 0x97, 0xA5, 0x45, 0x33, + 0x31, 0xE6, 0x97, 0xA5, 0x45, 0x33, 0xE2, 0x81, + 0x84, 0x34, 0x45, 0x33, 0xE2, 0x81, 0x84, 0x35, + // Bytes 2400 - 243f + 0x45, 0x33, 0xE2, 0x81, 0x84, 0x38, 0x45, 0x34, + 0xE2, 0x81, 0x84, 0x35, 0x45, 0x35, 0xE2, 0x81, + 0x84, 0x36, 0x45, 0x35, 0xE2, 0x81, 0x84, 0x38, + 0x45, 0x37, 0xE2, 0x81, 0x84, 0x38, 0x45, 0x41, + 0xE2, 0x88, 0x95, 0x6D, 0x45, 0x56, 0xE2, 0x88, + 0x95, 0x6D, 0x45, 0x6D, 0xE2, 0x88, 0x95, 0x73, + 0x46, 0x31, 0xE2, 0x81, 0x84, 0x31, 0x30, 0x46, + 0x43, 0xE2, 0x88, 0x95, 0x6B, 0x67, 0x46, 0x6D, + // Bytes 2440 - 247f + 0xE2, 0x88, 0x95, 0x73, 0x32, 0x46, 0xD8, 0xA8, + 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xA8, 0xD8, + 0xAE, 0xD9, 0x8A, 0x46, 0xD8, 0xAA, 0xD8, 0xAC, + 0xD9, 0x85, 0x46, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9, + 0x89, 0x46, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9, 0x8A, + 0x46, 0xD8, 0xAA, 0xD8, 0xAD, 0xD8, 0xAC, 0x46, + 0xD8, 0xAA, 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD8, + 0xAA, 0xD8, 0xAE, 0xD9, 0x85, 0x46, 0xD8, 0xAA, + // Bytes 2480 - 24bf + 0xD8, 0xAE, 0xD9, 0x89, 0x46, 0xD8, 0xAA, 0xD8, + 0xAE, 0xD9, 0x8A, 0x46, 0xD8, 0xAA, 0xD9, 0x85, + 0xD8, 0xAC, 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, + 0xAD, 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAE, + 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD9, 0x89, 0x46, + 0xD8, 0xAA, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8, + 0xAC, 0xD8, 0xAD, 0xD9, 0x89, 0x46, 0xD8, 0xAC, + 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xAC, 0xD9, + // Bytes 24c0 - 24ff + 0x85, 0xD8, 0xAD, 0x46, 0xD8, 0xAC, 0xD9, 0x85, + 0xD9, 0x89, 0x46, 0xD8, 0xAC, 0xD9, 0x85, 0xD9, + 0x8A, 0x46, 0xD8, 0xAD, 0xD8, 0xAC, 0xD9, 0x8A, + 0x46, 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x89, 0x46, + 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8, + 0xB3, 0xD8, 0xAC, 0xD8, 0xAD, 0x46, 0xD8, 0xB3, + 0xD8, 0xAC, 0xD9, 0x89, 0x46, 0xD8, 0xB3, 0xD8, + 0xAD, 0xD8, 0xAC, 0x46, 0xD8, 0xB3, 0xD8, 0xAE, + // Bytes 2500 - 253f + 0xD9, 0x89, 0x46, 0xD8, 0xB3, 0xD8, 0xAE, 0xD9, + 0x8A, 0x46, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xAC, + 0x46, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xAD, 0x46, + 0xD8, 0xB3, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, + 0xB4, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD8, 0xB4, + 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD8, 0xB4, 0xD8, + 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xB4, 0xD9, 0x85, + 0xD8, 0xAE, 0x46, 0xD8, 0xB4, 0xD9, 0x85, 0xD9, + // Bytes 2540 - 257f + 0x85, 0x46, 0xD8, 0xB5, 0xD8, 0xAD, 0xD8, 0xAD, + 0x46, 0xD8, 0xB5, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, + 0xD8, 0xB5, 0xD9, 0x84, 0xD9, 0x89, 0x46, 0xD8, + 0xB5, 0xD9, 0x84, 0xDB, 0x92, 0x46, 0xD8, 0xB5, + 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, 0xB6, 0xD8, + 0xAD, 0xD9, 0x89, 0x46, 0xD8, 0xB6, 0xD8, 0xAD, + 0xD9, 0x8A, 0x46, 0xD8, 0xB6, 0xD8, 0xAE, 0xD9, + 0x85, 0x46, 0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xAD, + // Bytes 2580 - 25bf + 0x46, 0xD8, 0xB7, 0xD9, 0x85, 0xD9, 0x85, 0x46, + 0xD8, 0xB7, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8, + 0xB9, 0xD8, 0xAC, 0xD9, 0x85, 0x46, 0xD8, 0xB9, + 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, 0xB9, 0xD9, + 0x85, 0xD9, 0x89, 0x46, 0xD8, 0xB9, 0xD9, 0x85, + 0xD9, 0x8A, 0x46, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, + 0x85, 0x46, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x89, + 0x46, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x8A, 0x46, + // Bytes 25c0 - 25ff + 0xD9, 0x81, 0xD8, 0xAE, 0xD9, 0x85, 0x46, 0xD9, + 0x81, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x82, + 0xD9, 0x84, 0xDB, 0x92, 0x46, 0xD9, 0x82, 0xD9, + 0x85, 0xD8, 0xAD, 0x46, 0xD9, 0x82, 0xD9, 0x85, + 0xD9, 0x85, 0x46, 0xD9, 0x82, 0xD9, 0x85, 0xD9, + 0x8A, 0x46, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x85, + 0x46, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x8A, 0x46, + 0xD9, 0x84, 0xD8, 0xAC, 0xD8, 0xAC, 0x46, 0xD9, + // Bytes 2600 - 263f + 0x84, 0xD8, 0xAC, 0xD9, 0x85, 0x46, 0xD9, 0x84, + 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD9, 0x84, 0xD8, + 0xAD, 0xD9, 0x85, 0x46, 0xD9, 0x84, 0xD8, 0xAD, + 0xD9, 0x89, 0x46, 0xD9, 0x84, 0xD8, 0xAD, 0xD9, + 0x8A, 0x46, 0xD9, 0x84, 0xD8, 0xAE, 0xD9, 0x85, + 0x46, 0xD9, 0x84, 0xD9, 0x85, 0xD8, 0xAD, 0x46, + 0xD9, 0x84, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, + 0x85, 0xD8, 0xAC, 0xD8, 0xAD, 0x46, 0xD9, 0x85, + // Bytes 2640 - 267f + 0xD8, 0xAC, 0xD8, 0xAE, 0x46, 0xD9, 0x85, 0xD8, + 0xAC, 0xD9, 0x85, 0x46, 0xD9, 0x85, 0xD8, 0xAC, + 0xD9, 0x8A, 0x46, 0xD9, 0x85, 0xD8, 0xAD, 0xD8, + 0xAC, 0x46, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x85, + 0x46, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, + 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, 0x46, 0xD9, + 0x85, 0xD8, 0xAE, 0xD9, 0x85, 0x46, 0xD9, 0x85, + 0xD8, 0xAE, 0xD9, 0x8A, 0x46, 0xD9, 0x85, 0xD9, + // Bytes 2680 - 26bf + 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x86, 0xD8, 0xAC, + 0xD8, 0xAD, 0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, + 0x85, 0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x89, + 0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, + 0xD9, 0x86, 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD9, + 0x86, 0xD8, 0xAD, 0xD9, 0x89, 0x46, 0xD9, 0x86, + 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD9, 0x86, 0xD9, + 0x85, 0xD9, 0x89, 0x46, 0xD9, 0x86, 0xD9, 0x85, + // Bytes 26c0 - 26ff + 0xD9, 0x8A, 0x46, 0xD9, 0x87, 0xD9, 0x85, 0xD8, + 0xAC, 0x46, 0xD9, 0x87, 0xD9, 0x85, 0xD9, 0x85, + 0x46, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, + 0xD9, 0x8A, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD9, + 0x8A, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD9, 0x8A, + 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x8A, 0xD9, + 0x94, 0xD8, 0xA7, 0x46, 0xD9, 0x8A, 0xD9, 0x94, + 0xD8, 0xAC, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, + // Bytes 2700 - 273f + 0xAD, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAE, + 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xB1, 0x46, + 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xB2, 0x46, 0xD9, + 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0x46, 0xD9, 0x8A, + 0xD9, 0x94, 0xD9, 0x86, 0x46, 0xD9, 0x8A, 0xD9, + 0x94, 0xD9, 0x87, 0x46, 0xD9, 0x8A, 0xD9, 0x94, + 0xD9, 0x88, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, + 0x89, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x8A, + // Bytes 2740 - 277f + 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x86, 0x46, + 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x87, 0x46, 0xD9, + 0x8A, 0xD9, 0x94, 0xDB, 0x88, 0x46, 0xD9, 0x8A, + 0xD9, 0x94, 0xDB, 0x90, 0x46, 0xD9, 0x8A, 0xD9, + 0x94, 0xDB, 0x95, 0x46, 0xE0, 0xB9, 0x8D, 0xE0, + 0xB8, 0xB2, 0x46, 0xE0, 0xBA, 0xAB, 0xE0, 0xBA, + 0x99, 0x46, 0xE0, 0xBA, 0xAB, 0xE0, 0xBA, 0xA1, + 0x46, 0xE0, 0xBB, 0x8D, 0xE0, 0xBA, 0xB2, 0x46, + // Bytes 2780 - 27bf + 0xE0, 0xBD, 0x80, 0xE0, 0xBE, 0xB5, 0x46, 0xE0, + 0xBD, 0x82, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD, + 0x8C, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x91, + 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x96, 0xE0, + 0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x9B, 0xE0, 0xBE, + 0xB7, 0x46, 0xE0, 0xBE, 0x90, 0xE0, 0xBE, 0xB5, + 0x46, 0xE0, 0xBE, 0x92, 0xE0, 0xBE, 0xB7, 0x46, + 0xE0, 0xBE, 0x9C, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, + // Bytes 27c0 - 27ff + 0xBE, 0xA1, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBE, + 0xA6, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBE, 0xAB, + 0xE0, 0xBE, 0xB7, 0x46, 0xE1, 0x84, 0x80, 0xE1, + 0x85, 0xA1, 0x46, 0xE1, 0x84, 0x82, 0xE1, 0x85, + 0xA1, 0x46, 0xE1, 0x84, 0x83, 0xE1, 0x85, 0xA1, + 0x46, 0xE1, 0x84, 0x85, 0xE1, 0x85, 0xA1, 0x46, + 0xE1, 0x84, 0x86, 0xE1, 0x85, 0xA1, 0x46, 0xE1, + 0x84, 0x87, 0xE1, 0x85, 0xA1, 0x46, 0xE1, 0x84, + // Bytes 2800 - 283f + 0x89, 0xE1, 0x85, 0xA1, 0x46, 0xE1, 0x84, 0x8B, + 0xE1, 0x85, 0xA1, 0x46, 0xE1, 0x84, 0x8B, 0xE1, + 0x85, 0xAE, 0x46, 0xE1, 0x84, 0x8C, 0xE1, 0x85, + 0xA1, 0x46, 0xE1, 0x84, 0x8E, 0xE1, 0x85, 0xA1, + 0x46, 0xE1, 0x84, 0x8F, 0xE1, 0x85, 0xA1, 0x46, + 0xE1, 0x84, 0x90, 0xE1, 0x85, 0xA1, 0x46, 0xE1, + 0x84, 0x91, 0xE1, 0x85, 0xA1, 0x46, 0xE1, 0x84, + 0x92, 0xE1, 0x85, 0xA1, 0x46, 0xE2, 0x80, 0xB2, + // Bytes 2840 - 287f + 0xE2, 0x80, 0xB2, 0x46, 0xE2, 0x80, 0xB5, 0xE2, + 0x80, 0xB5, 0x46, 0xE2, 0x88, 0xAB, 0xE2, 0x88, + 0xAB, 0x46, 0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE, + 0x46, 0xE3, 0x81, 0xBB, 0xE3, 0x81, 0x8B, 0x46, + 0xE3, 0x82, 0x88, 0xE3, 0x82, 0x8A, 0x46, 0xE3, + 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0x46, 0xE3, 0x82, + 0xB3, 0xE3, 0x82, 0xB3, 0x46, 0xE3, 0x82, 0xB3, + 0xE3, 0x83, 0x88, 0x46, 0xE3, 0x83, 0x88, 0xE3, + // Bytes 2880 - 28bf + 0x83, 0xB3, 0x46, 0xE3, 0x83, 0x8A, 0xE3, 0x83, + 0x8E, 0x46, 0xE3, 0x83, 0x9B, 0xE3, 0x83, 0xB3, + 0x46, 0xE3, 0x83, 0x9F, 0xE3, 0x83, 0xAA, 0x46, + 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xA9, 0x46, 0xE3, + 0x83, 0xAC, 0xE3, 0x83, 0xA0, 0x46, 0xE5, 0xA4, + 0xA7, 0xE6, 0xAD, 0xA3, 0x46, 0xE5, 0xB9, 0xB3, + 0xE6, 0x88, 0x90, 0x46, 0xE6, 0x98, 0x8E, 0xE6, + 0xB2, 0xBB, 0x46, 0xE6, 0x98, 0xAD, 0xE5, 0x92, + // Bytes 28c0 - 28ff + 0x8C, 0x47, 0x72, 0x61, 0x64, 0xE2, 0x88, 0x95, + 0x73, 0x47, 0xE3, 0x80, 0x94, 0x53, 0xE3, 0x80, + 0x95, 0x48, 0x28, 0xE1, 0x84, 0x80, 0xE1, 0x85, + 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x82, 0xE1, + 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x83, + 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, + 0x85, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, + 0x84, 0x86, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, + // Bytes 2900 - 293f + 0xE1, 0x84, 0x87, 0xE1, 0x85, 0xA1, 0x29, 0x48, + 0x28, 0xE1, 0x84, 0x89, 0xE1, 0x85, 0xA1, 0x29, + 0x48, 0x28, 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xA1, + 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8C, 0xE1, 0x85, + 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8C, 0xE1, + 0x85, 0xAE, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8E, + 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, + 0x8F, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, + // Bytes 2940 - 297f + 0x84, 0x90, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, + 0xE1, 0x84, 0x91, 0xE1, 0x85, 0xA1, 0x29, 0x48, + 0x28, 0xE1, 0x84, 0x92, 0xE1, 0x85, 0xA1, 0x29, + 0x48, 0x72, 0x61, 0x64, 0xE2, 0x88, 0x95, 0x73, + 0x32, 0x48, 0xD8, 0xA7, 0xD9, 0x83, 0xD8, 0xA8, + 0xD8, 0xB1, 0x48, 0xD8, 0xA7, 0xD9, 0x84, 0xD9, + 0x84, 0xD9, 0x87, 0x48, 0xD8, 0xB1, 0xD8, 0xB3, + 0xD9, 0x88, 0xD9, 0x84, 0x48, 0xD8, 0xB1, 0xDB, + // Bytes 2980 - 29bf + 0x8C, 0xD8, 0xA7, 0xD9, 0x84, 0x48, 0xD8, 0xB5, + 0xD9, 0x84, 0xD8, 0xB9, 0xD9, 0x85, 0x48, 0xD8, + 0xB9, 0xD9, 0x84, 0xD9, 0x8A, 0xD9, 0x87, 0x48, + 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAF, + 0x48, 0xD9, 0x88, 0xD8, 0xB3, 0xD9, 0x84, 0xD9, + 0x85, 0x49, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2, + 0xE2, 0x80, 0xB2, 0x49, 0xE2, 0x80, 0xB5, 0xE2, + 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0x49, 0xE2, 0x88, + // Bytes 29c0 - 29ff + 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0x49, + 0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE, 0xE2, 0x88, + 0xAE, 0x49, 0xE3, 0x80, 0x94, 0xE4, 0xB8, 0x89, + 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE4, + 0xBA, 0x8C, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, + 0x94, 0xE5, 0x8B, 0x9D, 0xE3, 0x80, 0x95, 0x49, + 0xE3, 0x80, 0x94, 0xE5, 0xAE, 0x89, 0xE3, 0x80, + 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE6, 0x89, 0x93, + // Bytes 2a00 - 2a3f + 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE6, + 0x95, 0x97, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, + 0x94, 0xE6, 0x9C, 0xAC, 0xE3, 0x80, 0x95, 0x49, + 0xE3, 0x80, 0x94, 0xE7, 0x82, 0xB9, 0xE3, 0x80, + 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE7, 0x9B, 0x97, + 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x82, 0xA2, 0xE3, + 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x82, + 0xA4, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x81, 0x49, + // Bytes 2a40 - 2a7f + 0xE3, 0x82, 0xA6, 0xE3, 0x82, 0xA9, 0xE3, 0x83, + 0xB3, 0x49, 0xE3, 0x82, 0xAA, 0xE3, 0x83, 0xB3, + 0xE3, 0x82, 0xB9, 0x49, 0xE3, 0x82, 0xAA, 0xE3, + 0x83, 0xBC, 0xE3, 0x83, 0xA0, 0x49, 0xE3, 0x82, + 0xAB, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xAA, 0x49, + 0xE3, 0x82, 0xB1, 0xE3, 0x83, 0xBC, 0xE3, 0x82, + 0xB9, 0x49, 0xE3, 0x82, 0xB3, 0xE3, 0x83, 0xAB, + 0xE3, 0x83, 0x8A, 0x49, 0xE3, 0x82, 0xBB, 0xE3, + // Bytes 2a80 - 2abf + 0x83, 0xB3, 0xE3, 0x83, 0x81, 0x49, 0xE3, 0x82, + 0xBB, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, 0x49, + 0xE3, 0x83, 0x86, 0xE3, 0x82, 0x99, 0xE3, 0x82, + 0xB7, 0x49, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, + 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83, 0x8E, 0xE3, + 0x83, 0x83, 0xE3, 0x83, 0x88, 0x49, 0xE3, 0x83, + 0x8F, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0x84, 0x49, + 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x99, 0xE3, 0x83, + // Bytes 2ac0 - 2aff + 0xAB, 0x49, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A, + 0xE3, 0x82, 0xB3, 0x49, 0xE3, 0x83, 0x95, 0xE3, + 0x83, 0xA9, 0xE3, 0x83, 0xB3, 0x49, 0xE3, 0x83, + 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xBD, 0x49, + 0xE3, 0x83, 0x98, 0xE3, 0x83, 0xAB, 0xE3, 0x83, + 0x84, 0x49, 0xE3, 0x83, 0x9B, 0xE3, 0x83, 0xBC, + 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83, 0x9B, 0xE3, + 0x83, 0xBC, 0xE3, 0x83, 0xB3, 0x49, 0xE3, 0x83, + // Bytes 2b00 - 2b3f + 0x9E, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xAB, 0x49, + 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0x83, 0xE3, 0x83, + 0x8F, 0x49, 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0xAB, + 0xE3, 0x82, 0xAF, 0x49, 0xE3, 0x83, 0xA4, 0xE3, + 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83, + 0xA6, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xB3, 0x49, + 0xE3, 0x83, 0xAF, 0xE3, 0x83, 0x83, 0xE3, 0x83, + 0x88, 0x4C, 0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xAE, + // Bytes 2b40 - 2b7f + 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xB4, 0x4C, 0xE2, + 0x80, 0xB2, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2, + 0xE2, 0x80, 0xB2, 0x4C, 0xE2, 0x88, 0xAB, 0xE2, + 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, + 0x4C, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xAB, 0xE3, + 0x83, 0x95, 0xE3, 0x82, 0xA1, 0x4C, 0xE3, 0x82, + 0xA8, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xAB, 0xE3, + 0x83, 0xBC, 0x4C, 0xE3, 0x82, 0xAB, 0xE3, 0x82, + // Bytes 2b80 - 2bbf + 0x99, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xB3, 0x4C, + 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83, + 0xB3, 0xE3, 0x83, 0x9E, 0x4C, 0xE3, 0x82, 0xAB, + 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0x83, 0xE3, 0x83, + 0x88, 0x4C, 0xE3, 0x82, 0xAB, 0xE3, 0x83, 0xAD, + 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xBC, 0x4C, 0xE3, + 0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0x8B, + 0xE3, 0x83, 0xBC, 0x4C, 0xE3, 0x82, 0xAD, 0xE3, + // Bytes 2bc0 - 2bff + 0x83, 0xA5, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xBC, + 0x4C, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3, + 0x83, 0xA9, 0xE3, 0x83, 0xA0, 0x4C, 0xE3, 0x82, + 0xAF, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xBC, 0xE3, + 0x83, 0x8D, 0x4C, 0xE3, 0x82, 0xB5, 0xE3, 0x82, + 0xA4, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0x4C, + 0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0xE3, 0x83, + 0xBC, 0xE3, 0x82, 0xB9, 0x4C, 0xE3, 0x83, 0x8F, + // Bytes 2c00 - 2c3f + 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x83, + 0x84, 0x4C, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A, + 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0x4C, 0xE3, + 0x83, 0x95, 0xE3, 0x82, 0xA3, 0xE3, 0x83, 0xBC, + 0xE3, 0x83, 0x88, 0x4C, 0xE3, 0x83, 0x98, 0xE3, + 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xBF, + 0x4C, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3, + 0x83, 0x8B, 0xE3, 0x83, 0x92, 0x4C, 0xE3, 0x83, + // Bytes 2c40 - 2c7f + 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xB3, 0xE3, + 0x82, 0xB9, 0x4C, 0xE3, 0x83, 0x9B, 0xE3, 0x82, + 0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x88, 0x4C, + 0xE3, 0x83, 0x9E, 0xE3, 0x82, 0xA4, 0xE3, 0x82, + 0xAF, 0xE3, 0x83, 0xAD, 0x4C, 0xE3, 0x83, 0x9F, + 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAD, 0xE3, 0x83, + 0xB3, 0x4C, 0xE3, 0x83, 0xA1, 0xE3, 0x83, 0xBC, + 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0x4C, 0xE3, + // Bytes 2c80 - 2cbf + 0x83, 0xAA, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, + 0xE3, 0x83, 0xAB, 0x4C, 0xE3, 0x83, 0xAB, 0xE3, + 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, + 0x4C, 0xE6, 0xA0, 0xAA, 0xE5, 0xBC, 0x8F, 0xE4, + 0xBC, 0x9A, 0xE7, 0xA4, 0xBE, 0x4E, 0x28, 0xE1, + 0x84, 0x8B, 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x92, + 0xE1, 0x85, 0xAE, 0x29, 0x4F, 0xD8, 0xAC, 0xD9, + 0x84, 0x20, 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xA7, + // Bytes 2cc0 - 2cff + 0xD9, 0x84, 0xD9, 0x87, 0x4F, 0xE1, 0x84, 0x8E, + 0xE1, 0x85, 0xA1, 0xE1, 0x86, 0xB7, 0xE1, 0x84, + 0x80, 0xE1, 0x85, 0xA9, 0x4F, 0xE3, 0x82, 0xA2, + 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A, 0xE3, 0x83, + 0xBC, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x82, 0xA2, + 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x98, 0xE3, 0x82, + 0x9A, 0xE3, 0x82, 0xA2, 0x4F, 0xE3, 0x82, 0xAD, + 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xAF, 0xE3, 0x83, + // Bytes 2d00 - 2d3f + 0x83, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x82, 0xB5, + 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x81, 0xE3, 0x83, + 0xBC, 0xE3, 0x83, 0xA0, 0x4F, 0xE3, 0x83, 0x8F, + 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x83, + 0xAC, 0xE3, 0x83, 0xAB, 0x4F, 0xE3, 0x83, 0x98, + 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0xBF, 0xE3, 0x83, + 0xBC, 0xE3, 0x83, 0xAB, 0x4F, 0xE3, 0x83, 0x9B, + 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xA4, 0xE3, 0x83, + // Bytes 2d40 - 2d7f + 0xB3, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x83, 0x9E, + 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xB7, 0xE3, 0x83, + 0xA7, 0xE3, 0x83, 0xB3, 0x4F, 0xE3, 0x83, 0xA1, + 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83, + 0x88, 0xE3, 0x83, 0xB3, 0x4F, 0xE3, 0x83, 0xAB, + 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x95, 0xE3, 0x82, + 0x99, 0xE3, 0x83, 0xAB, 0x51, 0x28, 0xE1, 0x84, + 0x8B, 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x8C, 0xE1, + // Bytes 2d80 - 2dbf + 0x85, 0xA5, 0xE1, 0x86, 0xAB, 0x29, 0x52, 0xE3, + 0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB, + 0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0xE3, 0x83, + 0xBC, 0x52, 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xAD, + 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x83, + 0xA9, 0xE3, 0x83, 0xA0, 0x52, 0xE3, 0x82, 0xAD, + 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xA1, 0xE3, 0x83, + 0xBC, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0x52, + // Bytes 2dc0 - 2dff + 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x83, + 0xA9, 0xE3, 0x83, 0xA0, 0xE3, 0x83, 0x88, 0xE3, + 0x83, 0xB3, 0x52, 0xE3, 0x82, 0xAF, 0xE3, 0x83, + 0xAB, 0xE3, 0x82, 0xBB, 0xE3, 0x82, 0x99, 0xE3, + 0x82, 0xA4, 0xE3, 0x83, 0xAD, 0x52, 0xE3, 0x83, + 0x8F, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3, + 0x82, 0xBB, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, + 0x52, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3, + // Bytes 2e00 - 2e3f + 0x82, 0xA2, 0xE3, 0x82, 0xB9, 0xE3, 0x83, 0x88, + 0xE3, 0x83, 0xAB, 0x52, 0xE3, 0x83, 0x95, 0xE3, + 0x82, 0x99, 0xE3, 0x83, 0x83, 0xE3, 0x82, 0xB7, + 0xE3, 0x82, 0xA7, 0xE3, 0x83, 0xAB, 0x52, 0xE3, + 0x83, 0x9F, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0x8F, + 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x83, + 0xAB, 0x52, 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xB3, + 0xE3, 0x83, 0x88, 0xE3, 0x82, 0xB1, 0xE3, 0x82, + // Bytes 2e40 - 2e7f + 0x99, 0xE3, 0x83, 0xB3, 0x61, 0xD8, 0xB5, 0xD9, + 0x84, 0xD9, 0x89, 0x20, 0xD8, 0xA7, 0xD9, 0x84, + 0xD9, 0x84, 0xD9, 0x87, 0x20, 0xD8, 0xB9, 0xD9, + 0x84, 0xD9, 0x8A, 0xD9, 0x87, 0x20, 0xD9, 0x88, + 0xD8, 0xB3, 0xD9, 0x84, 0xD9, 0x85, 0x86, 0xE0, + 0xB3, 0x86, 0xE0, 0xB3, 0x82, 0x86, 0xE0, 0xB7, + 0x99, 0xE0, 0xB7, 0x8F, 0x09, 0xE0, 0xB7, 0x99, + 0xE0, 0xB7, 0x8F, 0xE0, 0xB7, 0x8A, 0x09, 0x44, + // Bytes 2e80 - 2ebf + 0x44, 0x5A, 0xCC, 0x8C, 0xE6, 0x44, 0x44, 0x7A, + 0xCC, 0x8C, 0xE6, 0x44, 0x64, 0x7A, 0xCC, 0x8C, + 0xE6, 0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x93, + 0xE6, 0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x94, + 0xE6, 0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x95, + 0xDC, 0x49, 0xE3, 0x83, 0xA1, 0xE3, 0x82, 0xAB, + 0xE3, 0x82, 0x99, 0x08, 0x4C, 0xE3, 0x82, 0xAD, + 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xAB, 0xE3, 0x82, + // Bytes 2ec0 - 2eff + 0x99, 0x08, 0x4C, 0xE3, 0x82, 0xB3, 0xE3, 0x83, + 0xBC, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A, 0x08, + 0x4C, 0xE3, 0x83, 0xA4, 0xE3, 0x83, 0xBC, 0xE3, + 0x83, 0x88, 0xE3, 0x82, 0x99, 0x08, 0x4F, 0xE3, + 0x82, 0xA4, 0xE3, 0x83, 0x8B, 0xE3, 0x83, 0xB3, + 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0x08, 0x4F, + 0xE3, 0x82, 0xB7, 0xE3, 0x83, 0xAA, 0xE3, 0x83, + 0xB3, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0x08, + // Bytes 2f00 - 2f3f + 0x4F, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3, + 0x83, 0xBC, 0xE3, 0x82, 0xB7, 0xE3, 0x82, 0x99, + 0x08, 0x4F, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A, + 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, 0xE3, 0x82, + 0x99, 0x08, 0x52, 0xE3, 0x82, 0xA8, 0xE3, 0x82, + 0xB9, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xBC, 0xE3, + 0x83, 0x88, 0xE3, 0x82, 0x99, 0x08, 0x52, 0xE3, + 0x83, 0x95, 0xE3, 0x82, 0xA1, 0xE3, 0x83, 0xA9, + // Bytes 2f40 - 2f7f + 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, 0xE3, 0x82, + 0x99, 0x08, 0x03, 0x3C, 0xCC, 0xB8, 0x01, 0x03, + 0x3D, 0xCC, 0xB8, 0x01, 0x03, 0x3E, 0xCC, 0xB8, + 0x01, 0x03, 0x41, 0xCC, 0x80, 0xE6, 0x03, 0x41, + 0xCC, 0x81, 0xE6, 0x03, 0x41, 0xCC, 0x83, 0xE6, + 0x03, 0x41, 0xCC, 0x84, 0xE6, 0x03, 0x41, 0xCC, + 0x89, 0xE6, 0x03, 0x41, 0xCC, 0x8C, 0xE6, 0x03, + 0x41, 0xCC, 0x8F, 0xE6, 0x03, 0x41, 0xCC, 0x91, + // Bytes 2f80 - 2fbf + 0xE6, 0x03, 0x41, 0xCC, 0xA5, 0xDC, 0x03, 0x41, + 0xCC, 0xA8, 0xCA, 0x03, 0x42, 0xCC, 0x87, 0xE6, + 0x03, 0x42, 0xCC, 0xA3, 0xDC, 0x03, 0x42, 0xCC, + 0xB1, 0xDC, 0x03, 0x43, 0xCC, 0x81, 0xE6, 0x03, + 0x43, 0xCC, 0x82, 0xE6, 0x03, 0x43, 0xCC, 0x87, + 0xE6, 0x03, 0x43, 0xCC, 0x8C, 0xE6, 0x03, 0x44, + 0xCC, 0x87, 0xE6, 0x03, 0x44, 0xCC, 0x8C, 0xE6, + 0x03, 0x44, 0xCC, 0xA3, 0xDC, 0x03, 0x44, 0xCC, + // Bytes 2fc0 - 2fff + 0xA7, 0xCA, 0x03, 0x44, 0xCC, 0xAD, 0xDC, 0x03, + 0x44, 0xCC, 0xB1, 0xDC, 0x03, 0x45, 0xCC, 0x80, + 0xE6, 0x03, 0x45, 0xCC, 0x81, 0xE6, 0x03, 0x45, + 0xCC, 0x83, 0xE6, 0x03, 0x45, 0xCC, 0x86, 0xE6, + 0x03, 0x45, 0xCC, 0x87, 0xE6, 0x03, 0x45, 0xCC, + 0x88, 0xE6, 0x03, 0x45, 0xCC, 0x89, 0xE6, 0x03, + 0x45, 0xCC, 0x8C, 0xE6, 0x03, 0x45, 0xCC, 0x8F, + 0xE6, 0x03, 0x45, 0xCC, 0x91, 0xE6, 0x03, 0x45, + // Bytes 3000 - 303f + 0xCC, 0xA8, 0xCA, 0x03, 0x45, 0xCC, 0xAD, 0xDC, + 0x03, 0x45, 0xCC, 0xB0, 0xDC, 0x03, 0x46, 0xCC, + 0x87, 0xE6, 0x03, 0x47, 0xCC, 0x81, 0xE6, 0x03, + 0x47, 0xCC, 0x82, 0xE6, 0x03, 0x47, 0xCC, 0x84, + 0xE6, 0x03, 0x47, 0xCC, 0x86, 0xE6, 0x03, 0x47, + 0xCC, 0x87, 0xE6, 0x03, 0x47, 0xCC, 0x8C, 0xE6, + 0x03, 0x47, 0xCC, 0xA7, 0xCA, 0x03, 0x48, 0xCC, + 0x82, 0xE6, 0x03, 0x48, 0xCC, 0x87, 0xE6, 0x03, + // Bytes 3040 - 307f + 0x48, 0xCC, 0x88, 0xE6, 0x03, 0x48, 0xCC, 0x8C, + 0xE6, 0x03, 0x48, 0xCC, 0xA3, 0xDC, 0x03, 0x48, + 0xCC, 0xA7, 0xCA, 0x03, 0x48, 0xCC, 0xAE, 0xDC, + 0x03, 0x49, 0xCC, 0x80, 0xE6, 0x03, 0x49, 0xCC, + 0x81, 0xE6, 0x03, 0x49, 0xCC, 0x82, 0xE6, 0x03, + 0x49, 0xCC, 0x83, 0xE6, 0x03, 0x49, 0xCC, 0x84, + 0xE6, 0x03, 0x49, 0xCC, 0x86, 0xE6, 0x03, 0x49, + 0xCC, 0x87, 0xE6, 0x03, 0x49, 0xCC, 0x89, 0xE6, + // Bytes 3080 - 30bf + 0x03, 0x49, 0xCC, 0x8C, 0xE6, 0x03, 0x49, 0xCC, + 0x8F, 0xE6, 0x03, 0x49, 0xCC, 0x91, 0xE6, 0x03, + 0x49, 0xCC, 0xA3, 0xDC, 0x03, 0x49, 0xCC, 0xA8, + 0xCA, 0x03, 0x49, 0xCC, 0xB0, 0xDC, 0x03, 0x4A, + 0xCC, 0x82, 0xE6, 0x03, 0x4B, 0xCC, 0x81, 0xE6, + 0x03, 0x4B, 0xCC, 0x8C, 0xE6, 0x03, 0x4B, 0xCC, + 0xA3, 0xDC, 0x03, 0x4B, 0xCC, 0xA7, 0xCA, 0x03, + 0x4B, 0xCC, 0xB1, 0xDC, 0x03, 0x4C, 0xCC, 0x81, + // Bytes 30c0 - 30ff + 0xE6, 0x03, 0x4C, 0xCC, 0x8C, 0xE6, 0x03, 0x4C, + 0xCC, 0xA7, 0xCA, 0x03, 0x4C, 0xCC, 0xAD, 0xDC, + 0x03, 0x4C, 0xCC, 0xB1, 0xDC, 0x03, 0x4D, 0xCC, + 0x81, 0xE6, 0x03, 0x4D, 0xCC, 0x87, 0xE6, 0x03, + 0x4D, 0xCC, 0xA3, 0xDC, 0x03, 0x4E, 0xCC, 0x80, + 0xE6, 0x03, 0x4E, 0xCC, 0x81, 0xE6, 0x03, 0x4E, + 0xCC, 0x83, 0xE6, 0x03, 0x4E, 0xCC, 0x87, 0xE6, + 0x03, 0x4E, 0xCC, 0x8C, 0xE6, 0x03, 0x4E, 0xCC, + // Bytes 3100 - 313f + 0xA3, 0xDC, 0x03, 0x4E, 0xCC, 0xA7, 0xCA, 0x03, + 0x4E, 0xCC, 0xAD, 0xDC, 0x03, 0x4E, 0xCC, 0xB1, + 0xDC, 0x03, 0x4F, 0xCC, 0x80, 0xE6, 0x03, 0x4F, + 0xCC, 0x81, 0xE6, 0x03, 0x4F, 0xCC, 0x86, 0xE6, + 0x03, 0x4F, 0xCC, 0x89, 0xE6, 0x03, 0x4F, 0xCC, + 0x8B, 0xE6, 0x03, 0x4F, 0xCC, 0x8C, 0xE6, 0x03, + 0x4F, 0xCC, 0x8F, 0xE6, 0x03, 0x4F, 0xCC, 0x91, + 0xE6, 0x03, 0x50, 0xCC, 0x81, 0xE6, 0x03, 0x50, + // Bytes 3140 - 317f + 0xCC, 0x87, 0xE6, 0x03, 0x52, 0xCC, 0x81, 0xE6, + 0x03, 0x52, 0xCC, 0x87, 0xE6, 0x03, 0x52, 0xCC, + 0x8C, 0xE6, 0x03, 0x52, 0xCC, 0x8F, 0xE6, 0x03, + 0x52, 0xCC, 0x91, 0xE6, 0x03, 0x52, 0xCC, 0xA7, + 0xCA, 0x03, 0x52, 0xCC, 0xB1, 0xDC, 0x03, 0x53, + 0xCC, 0x82, 0xE6, 0x03, 0x53, 0xCC, 0x87, 0xE6, + 0x03, 0x53, 0xCC, 0xA6, 0xDC, 0x03, 0x53, 0xCC, + 0xA7, 0xCA, 0x03, 0x54, 0xCC, 0x87, 0xE6, 0x03, + // Bytes 3180 - 31bf + 0x54, 0xCC, 0x8C, 0xE6, 0x03, 0x54, 0xCC, 0xA3, + 0xDC, 0x03, 0x54, 0xCC, 0xA6, 0xDC, 0x03, 0x54, + 0xCC, 0xA7, 0xCA, 0x03, 0x54, 0xCC, 0xAD, 0xDC, + 0x03, 0x54, 0xCC, 0xB1, 0xDC, 0x03, 0x55, 0xCC, + 0x80, 0xE6, 0x03, 0x55, 0xCC, 0x81, 0xE6, 0x03, + 0x55, 0xCC, 0x82, 0xE6, 0x03, 0x55, 0xCC, 0x86, + 0xE6, 0x03, 0x55, 0xCC, 0x89, 0xE6, 0x03, 0x55, + 0xCC, 0x8A, 0xE6, 0x03, 0x55, 0xCC, 0x8B, 0xE6, + // Bytes 31c0 - 31ff + 0x03, 0x55, 0xCC, 0x8C, 0xE6, 0x03, 0x55, 0xCC, + 0x8F, 0xE6, 0x03, 0x55, 0xCC, 0x91, 0xE6, 0x03, + 0x55, 0xCC, 0xA3, 0xDC, 0x03, 0x55, 0xCC, 0xA4, + 0xDC, 0x03, 0x55, 0xCC, 0xA8, 0xCA, 0x03, 0x55, + 0xCC, 0xAD, 0xDC, 0x03, 0x55, 0xCC, 0xB0, 0xDC, + 0x03, 0x56, 0xCC, 0x83, 0xE6, 0x03, 0x56, 0xCC, + 0xA3, 0xDC, 0x03, 0x57, 0xCC, 0x80, 0xE6, 0x03, + 0x57, 0xCC, 0x81, 0xE6, 0x03, 0x57, 0xCC, 0x82, + // Bytes 3200 - 323f + 0xE6, 0x03, 0x57, 0xCC, 0x87, 0xE6, 0x03, 0x57, + 0xCC, 0x88, 0xE6, 0x03, 0x57, 0xCC, 0xA3, 0xDC, + 0x03, 0x58, 0xCC, 0x87, 0xE6, 0x03, 0x58, 0xCC, + 0x88, 0xE6, 0x03, 0x59, 0xCC, 0x80, 0xE6, 0x03, + 0x59, 0xCC, 0x81, 0xE6, 0x03, 0x59, 0xCC, 0x82, + 0xE6, 0x03, 0x59, 0xCC, 0x83, 0xE6, 0x03, 0x59, + 0xCC, 0x84, 0xE6, 0x03, 0x59, 0xCC, 0x87, 0xE6, + 0x03, 0x59, 0xCC, 0x88, 0xE6, 0x03, 0x59, 0xCC, + // Bytes 3240 - 327f + 0x89, 0xE6, 0x03, 0x59, 0xCC, 0xA3, 0xDC, 0x03, + 0x5A, 0xCC, 0x81, 0xE6, 0x03, 0x5A, 0xCC, 0x82, + 0xE6, 0x03, 0x5A, 0xCC, 0x87, 0xE6, 0x03, 0x5A, + 0xCC, 0x8C, 0xE6, 0x03, 0x5A, 0xCC, 0xA3, 0xDC, + 0x03, 0x5A, 0xCC, 0xB1, 0xDC, 0x03, 0x61, 0xCC, + 0x80, 0xE6, 0x03, 0x61, 0xCC, 0x81, 0xE6, 0x03, + 0x61, 0xCC, 0x83, 0xE6, 0x03, 0x61, 0xCC, 0x84, + 0xE6, 0x03, 0x61, 0xCC, 0x89, 0xE6, 0x03, 0x61, + // Bytes 3280 - 32bf + 0xCC, 0x8C, 0xE6, 0x03, 0x61, 0xCC, 0x8F, 0xE6, + 0x03, 0x61, 0xCC, 0x91, 0xE6, 0x03, 0x61, 0xCC, + 0xA5, 0xDC, 0x03, 0x61, 0xCC, 0xA8, 0xCA, 0x03, + 0x62, 0xCC, 0x87, 0xE6, 0x03, 0x62, 0xCC, 0xA3, + 0xDC, 0x03, 0x62, 0xCC, 0xB1, 0xDC, 0x03, 0x63, + 0xCC, 0x81, 0xE6, 0x03, 0x63, 0xCC, 0x82, 0xE6, + 0x03, 0x63, 0xCC, 0x87, 0xE6, 0x03, 0x63, 0xCC, + 0x8C, 0xE6, 0x03, 0x64, 0xCC, 0x87, 0xE6, 0x03, + // Bytes 32c0 - 32ff + 0x64, 0xCC, 0x8C, 0xE6, 0x03, 0x64, 0xCC, 0xA3, + 0xDC, 0x03, 0x64, 0xCC, 0xA7, 0xCA, 0x03, 0x64, + 0xCC, 0xAD, 0xDC, 0x03, 0x64, 0xCC, 0xB1, 0xDC, + 0x03, 0x65, 0xCC, 0x80, 0xE6, 0x03, 0x65, 0xCC, + 0x81, 0xE6, 0x03, 0x65, 0xCC, 0x83, 0xE6, 0x03, + 0x65, 0xCC, 0x86, 0xE6, 0x03, 0x65, 0xCC, 0x87, + 0xE6, 0x03, 0x65, 0xCC, 0x88, 0xE6, 0x03, 0x65, + 0xCC, 0x89, 0xE6, 0x03, 0x65, 0xCC, 0x8C, 0xE6, + // Bytes 3300 - 333f + 0x03, 0x65, 0xCC, 0x8F, 0xE6, 0x03, 0x65, 0xCC, + 0x91, 0xE6, 0x03, 0x65, 0xCC, 0xA8, 0xCA, 0x03, + 0x65, 0xCC, 0xAD, 0xDC, 0x03, 0x65, 0xCC, 0xB0, + 0xDC, 0x03, 0x66, 0xCC, 0x87, 0xE6, 0x03, 0x67, + 0xCC, 0x81, 0xE6, 0x03, 0x67, 0xCC, 0x82, 0xE6, + 0x03, 0x67, 0xCC, 0x84, 0xE6, 0x03, 0x67, 0xCC, + 0x86, 0xE6, 0x03, 0x67, 0xCC, 0x87, 0xE6, 0x03, + 0x67, 0xCC, 0x8C, 0xE6, 0x03, 0x67, 0xCC, 0xA7, + // Bytes 3340 - 337f + 0xCA, 0x03, 0x68, 0xCC, 0x82, 0xE6, 0x03, 0x68, + 0xCC, 0x87, 0xE6, 0x03, 0x68, 0xCC, 0x88, 0xE6, + 0x03, 0x68, 0xCC, 0x8C, 0xE6, 0x03, 0x68, 0xCC, + 0xA3, 0xDC, 0x03, 0x68, 0xCC, 0xA7, 0xCA, 0x03, + 0x68, 0xCC, 0xAE, 0xDC, 0x03, 0x68, 0xCC, 0xB1, + 0xDC, 0x03, 0x69, 0xCC, 0x80, 0xE6, 0x03, 0x69, + 0xCC, 0x81, 0xE6, 0x03, 0x69, 0xCC, 0x82, 0xE6, + 0x03, 0x69, 0xCC, 0x83, 0xE6, 0x03, 0x69, 0xCC, + // Bytes 3380 - 33bf + 0x84, 0xE6, 0x03, 0x69, 0xCC, 0x86, 0xE6, 0x03, + 0x69, 0xCC, 0x89, 0xE6, 0x03, 0x69, 0xCC, 0x8C, + 0xE6, 0x03, 0x69, 0xCC, 0x8F, 0xE6, 0x03, 0x69, + 0xCC, 0x91, 0xE6, 0x03, 0x69, 0xCC, 0xA3, 0xDC, + 0x03, 0x69, 0xCC, 0xA8, 0xCA, 0x03, 0x69, 0xCC, + 0xB0, 0xDC, 0x03, 0x6A, 0xCC, 0x82, 0xE6, 0x03, + 0x6A, 0xCC, 0x8C, 0xE6, 0x03, 0x6B, 0xCC, 0x81, + 0xE6, 0x03, 0x6B, 0xCC, 0x8C, 0xE6, 0x03, 0x6B, + // Bytes 33c0 - 33ff + 0xCC, 0xA3, 0xDC, 0x03, 0x6B, 0xCC, 0xA7, 0xCA, + 0x03, 0x6B, 0xCC, 0xB1, 0xDC, 0x03, 0x6C, 0xCC, + 0x81, 0xE6, 0x03, 0x6C, 0xCC, 0x8C, 0xE6, 0x03, + 0x6C, 0xCC, 0xA7, 0xCA, 0x03, 0x6C, 0xCC, 0xAD, + 0xDC, 0x03, 0x6C, 0xCC, 0xB1, 0xDC, 0x03, 0x6D, + 0xCC, 0x81, 0xE6, 0x03, 0x6D, 0xCC, 0x87, 0xE6, + 0x03, 0x6D, 0xCC, 0xA3, 0xDC, 0x03, 0x6E, 0xCC, + 0x80, 0xE6, 0x03, 0x6E, 0xCC, 0x81, 0xE6, 0x03, + // Bytes 3400 - 343f + 0x6E, 0xCC, 0x83, 0xE6, 0x03, 0x6E, 0xCC, 0x87, + 0xE6, 0x03, 0x6E, 0xCC, 0x8C, 0xE6, 0x03, 0x6E, + 0xCC, 0xA3, 0xDC, 0x03, 0x6E, 0xCC, 0xA7, 0xCA, + 0x03, 0x6E, 0xCC, 0xAD, 0xDC, 0x03, 0x6E, 0xCC, + 0xB1, 0xDC, 0x03, 0x6F, 0xCC, 0x80, 0xE6, 0x03, + 0x6F, 0xCC, 0x81, 0xE6, 0x03, 0x6F, 0xCC, 0x86, + 0xE6, 0x03, 0x6F, 0xCC, 0x89, 0xE6, 0x03, 0x6F, + 0xCC, 0x8B, 0xE6, 0x03, 0x6F, 0xCC, 0x8C, 0xE6, + // Bytes 3440 - 347f + 0x03, 0x6F, 0xCC, 0x8F, 0xE6, 0x03, 0x6F, 0xCC, + 0x91, 0xE6, 0x03, 0x70, 0xCC, 0x81, 0xE6, 0x03, + 0x70, 0xCC, 0x87, 0xE6, 0x03, 0x72, 0xCC, 0x81, + 0xE6, 0x03, 0x72, 0xCC, 0x87, 0xE6, 0x03, 0x72, + 0xCC, 0x8C, 0xE6, 0x03, 0x72, 0xCC, 0x8F, 0xE6, + 0x03, 0x72, 0xCC, 0x91, 0xE6, 0x03, 0x72, 0xCC, + 0xA7, 0xCA, 0x03, 0x72, 0xCC, 0xB1, 0xDC, 0x03, + 0x73, 0xCC, 0x82, 0xE6, 0x03, 0x73, 0xCC, 0x87, + // Bytes 3480 - 34bf + 0xE6, 0x03, 0x73, 0xCC, 0xA6, 0xDC, 0x03, 0x73, + 0xCC, 0xA7, 0xCA, 0x03, 0x74, 0xCC, 0x87, 0xE6, + 0x03, 0x74, 0xCC, 0x88, 0xE6, 0x03, 0x74, 0xCC, + 0x8C, 0xE6, 0x03, 0x74, 0xCC, 0xA3, 0xDC, 0x03, + 0x74, 0xCC, 0xA6, 0xDC, 0x03, 0x74, 0xCC, 0xA7, + 0xCA, 0x03, 0x74, 0xCC, 0xAD, 0xDC, 0x03, 0x74, + 0xCC, 0xB1, 0xDC, 0x03, 0x75, 0xCC, 0x80, 0xE6, + 0x03, 0x75, 0xCC, 0x81, 0xE6, 0x03, 0x75, 0xCC, + // Bytes 34c0 - 34ff + 0x82, 0xE6, 0x03, 0x75, 0xCC, 0x86, 0xE6, 0x03, + 0x75, 0xCC, 0x89, 0xE6, 0x03, 0x75, 0xCC, 0x8A, + 0xE6, 0x03, 0x75, 0xCC, 0x8B, 0xE6, 0x03, 0x75, + 0xCC, 0x8C, 0xE6, 0x03, 0x75, 0xCC, 0x8F, 0xE6, + 0x03, 0x75, 0xCC, 0x91, 0xE6, 0x03, 0x75, 0xCC, + 0xA3, 0xDC, 0x03, 0x75, 0xCC, 0xA4, 0xDC, 0x03, + 0x75, 0xCC, 0xA8, 0xCA, 0x03, 0x75, 0xCC, 0xAD, + 0xDC, 0x03, 0x75, 0xCC, 0xB0, 0xDC, 0x03, 0x76, + // Bytes 3500 - 353f + 0xCC, 0x83, 0xE6, 0x03, 0x76, 0xCC, 0xA3, 0xDC, + 0x03, 0x77, 0xCC, 0x80, 0xE6, 0x03, 0x77, 0xCC, + 0x81, 0xE6, 0x03, 0x77, 0xCC, 0x82, 0xE6, 0x03, + 0x77, 0xCC, 0x87, 0xE6, 0x03, 0x77, 0xCC, 0x88, + 0xE6, 0x03, 0x77, 0xCC, 0x8A, 0xE6, 0x03, 0x77, + 0xCC, 0xA3, 0xDC, 0x03, 0x78, 0xCC, 0x87, 0xE6, + 0x03, 0x78, 0xCC, 0x88, 0xE6, 0x03, 0x79, 0xCC, + 0x80, 0xE6, 0x03, 0x79, 0xCC, 0x81, 0xE6, 0x03, + // Bytes 3540 - 357f + 0x79, 0xCC, 0x82, 0xE6, 0x03, 0x79, 0xCC, 0x83, + 0xE6, 0x03, 0x79, 0xCC, 0x84, 0xE6, 0x03, 0x79, + 0xCC, 0x87, 0xE6, 0x03, 0x79, 0xCC, 0x88, 0xE6, + 0x03, 0x79, 0xCC, 0x89, 0xE6, 0x03, 0x79, 0xCC, + 0x8A, 0xE6, 0x03, 0x79, 0xCC, 0xA3, 0xDC, 0x03, + 0x7A, 0xCC, 0x81, 0xE6, 0x03, 0x7A, 0xCC, 0x82, + 0xE6, 0x03, 0x7A, 0xCC, 0x87, 0xE6, 0x03, 0x7A, + 0xCC, 0x8C, 0xE6, 0x03, 0x7A, 0xCC, 0xA3, 0xDC, + // Bytes 3580 - 35bf + 0x03, 0x7A, 0xCC, 0xB1, 0xDC, 0x04, 0xC2, 0xA8, + 0xCC, 0x80, 0xE6, 0x04, 0xC2, 0xA8, 0xCC, 0x81, + 0xE6, 0x04, 0xC2, 0xA8, 0xCD, 0x82, 0xE6, 0x04, + 0xC3, 0x86, 0xCC, 0x81, 0xE6, 0x04, 0xC3, 0x86, + 0xCC, 0x84, 0xE6, 0x04, 0xC3, 0x98, 0xCC, 0x81, + 0xE6, 0x04, 0xC3, 0xA6, 0xCC, 0x81, 0xE6, 0x04, + 0xC3, 0xA6, 0xCC, 0x84, 0xE6, 0x04, 0xC3, 0xB8, + 0xCC, 0x81, 0xE6, 0x04, 0xC5, 0xBF, 0xCC, 0x87, + // Bytes 35c0 - 35ff + 0xE6, 0x04, 0xC6, 0xB7, 0xCC, 0x8C, 0xE6, 0x04, + 0xCA, 0x92, 0xCC, 0x8C, 0xE6, 0x04, 0xCE, 0x91, + 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0x91, 0xCC, 0x81, + 0xE6, 0x04, 0xCE, 0x91, 0xCC, 0x84, 0xE6, 0x04, + 0xCE, 0x91, 0xCC, 0x86, 0xE6, 0x04, 0xCE, 0x91, + 0xCD, 0x85, 0xF0, 0x04, 0xCE, 0x95, 0xCC, 0x80, + 0xE6, 0x04, 0xCE, 0x95, 0xCC, 0x81, 0xE6, 0x04, + 0xCE, 0x97, 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0x97, + // Bytes 3600 - 363f + 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0x97, 0xCD, 0x85, + 0xF0, 0x04, 0xCE, 0x99, 0xCC, 0x80, 0xE6, 0x04, + 0xCE, 0x99, 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0x99, + 0xCC, 0x84, 0xE6, 0x04, 0xCE, 0x99, 0xCC, 0x86, + 0xE6, 0x04, 0xCE, 0x99, 0xCC, 0x88, 0xE6, 0x04, + 0xCE, 0x9F, 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0x9F, + 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0xA1, 0xCC, 0x94, + 0xE6, 0x04, 0xCE, 0xA5, 0xCC, 0x80, 0xE6, 0x04, + // Bytes 3640 - 367f + 0xCE, 0xA5, 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0xA5, + 0xCC, 0x84, 0xE6, 0x04, 0xCE, 0xA5, 0xCC, 0x86, + 0xE6, 0x04, 0xCE, 0xA5, 0xCC, 0x88, 0xE6, 0x04, + 0xCE, 0xA9, 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0xA9, + 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0xA9, 0xCD, 0x85, + 0xF0, 0x04, 0xCE, 0xB1, 0xCC, 0x84, 0xE6, 0x04, + 0xCE, 0xB1, 0xCC, 0x86, 0xE6, 0x04, 0xCE, 0xB1, + 0xCD, 0x85, 0xF0, 0x04, 0xCE, 0xB5, 0xCC, 0x80, + // Bytes 3680 - 36bf + 0xE6, 0x04, 0xCE, 0xB5, 0xCC, 0x81, 0xE6, 0x04, + 0xCE, 0xB7, 0xCD, 0x85, 0xF0, 0x04, 0xCE, 0xB9, + 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0xB9, 0xCC, 0x81, + 0xE6, 0x04, 0xCE, 0xB9, 0xCC, 0x84, 0xE6, 0x04, + 0xCE, 0xB9, 0xCC, 0x86, 0xE6, 0x04, 0xCE, 0xB9, + 0xCD, 0x82, 0xE6, 0x04, 0xCE, 0xBF, 0xCC, 0x80, + 0xE6, 0x04, 0xCE, 0xBF, 0xCC, 0x81, 0xE6, 0x04, + 0xCF, 0x81, 0xCC, 0x93, 0xE6, 0x04, 0xCF, 0x81, + // Bytes 36c0 - 36ff + 0xCC, 0x94, 0xE6, 0x04, 0xCF, 0x85, 0xCC, 0x80, + 0xE6, 0x04, 0xCF, 0x85, 0xCC, 0x81, 0xE6, 0x04, + 0xCF, 0x85, 0xCC, 0x84, 0xE6, 0x04, 0xCF, 0x85, + 0xCC, 0x86, 0xE6, 0x04, 0xCF, 0x85, 0xCD, 0x82, + 0xE6, 0x04, 0xCF, 0x89, 0xCD, 0x85, 0xF0, 0x04, + 0xCF, 0x92, 0xCC, 0x81, 0xE6, 0x04, 0xCF, 0x92, + 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0x86, 0xCC, 0x88, + 0xE6, 0x04, 0xD0, 0x90, 0xCC, 0x86, 0xE6, 0x04, + // Bytes 3700 - 373f + 0xD0, 0x90, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0x93, + 0xCC, 0x81, 0xE6, 0x04, 0xD0, 0x95, 0xCC, 0x80, + 0xE6, 0x04, 0xD0, 0x95, 0xCC, 0x86, 0xE6, 0x04, + 0xD0, 0x95, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0x96, + 0xCC, 0x86, 0xE6, 0x04, 0xD0, 0x96, 0xCC, 0x88, + 0xE6, 0x04, 0xD0, 0x97, 0xCC, 0x88, 0xE6, 0x04, + 0xD0, 0x98, 0xCC, 0x80, 0xE6, 0x04, 0xD0, 0x98, + 0xCC, 0x84, 0xE6, 0x04, 0xD0, 0x98, 0xCC, 0x86, + // Bytes 3740 - 377f + 0xE6, 0x04, 0xD0, 0x98, 0xCC, 0x88, 0xE6, 0x04, + 0xD0, 0x9A, 0xCC, 0x81, 0xE6, 0x04, 0xD0, 0x9E, + 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xA3, 0xCC, 0x84, + 0xE6, 0x04, 0xD0, 0xA3, 0xCC, 0x86, 0xE6, 0x04, + 0xD0, 0xA3, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xA3, + 0xCC, 0x8B, 0xE6, 0x04, 0xD0, 0xA7, 0xCC, 0x88, + 0xE6, 0x04, 0xD0, 0xAB, 0xCC, 0x88, 0xE6, 0x04, + 0xD0, 0xAD, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xB0, + // Bytes 3780 - 37bf + 0xCC, 0x86, 0xE6, 0x04, 0xD0, 0xB0, 0xCC, 0x88, + 0xE6, 0x04, 0xD0, 0xB3, 0xCC, 0x81, 0xE6, 0x04, + 0xD0, 0xB5, 0xCC, 0x80, 0xE6, 0x04, 0xD0, 0xB5, + 0xCC, 0x86, 0xE6, 0x04, 0xD0, 0xB5, 0xCC, 0x88, + 0xE6, 0x04, 0xD0, 0xB6, 0xCC, 0x86, 0xE6, 0x04, + 0xD0, 0xB6, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xB7, + 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xB8, 0xCC, 0x80, + 0xE6, 0x04, 0xD0, 0xB8, 0xCC, 0x84, 0xE6, 0x04, + // Bytes 37c0 - 37ff + 0xD0, 0xB8, 0xCC, 0x86, 0xE6, 0x04, 0xD0, 0xB8, + 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xBA, 0xCC, 0x81, + 0xE6, 0x04, 0xD0, 0xBE, 0xCC, 0x88, 0xE6, 0x04, + 0xD1, 0x83, 0xCC, 0x84, 0xE6, 0x04, 0xD1, 0x83, + 0xCC, 0x86, 0xE6, 0x04, 0xD1, 0x83, 0xCC, 0x88, + 0xE6, 0x04, 0xD1, 0x83, 0xCC, 0x8B, 0xE6, 0x04, + 0xD1, 0x87, 0xCC, 0x88, 0xE6, 0x04, 0xD1, 0x8B, + 0xCC, 0x88, 0xE6, 0x04, 0xD1, 0x8D, 0xCC, 0x88, + // Bytes 3800 - 383f + 0xE6, 0x04, 0xD1, 0x96, 0xCC, 0x88, 0xE6, 0x04, + 0xD1, 0xB4, 0xCC, 0x8F, 0xE6, 0x04, 0xD1, 0xB5, + 0xCC, 0x8F, 0xE6, 0x04, 0xD3, 0x98, 0xCC, 0x88, + 0xE6, 0x04, 0xD3, 0x99, 0xCC, 0x88, 0xE6, 0x04, + 0xD3, 0xA8, 0xCC, 0x88, 0xE6, 0x04, 0xD3, 0xA9, + 0xCC, 0x88, 0xE6, 0x04, 0xD8, 0xA7, 0xD9, 0x93, + 0xE6, 0x04, 0xD8, 0xA7, 0xD9, 0x94, 0xE6, 0x04, + 0xD8, 0xA7, 0xD9, 0x95, 0xDC, 0x04, 0xD9, 0x88, + // Bytes 3840 - 387f + 0xD9, 0x94, 0xE6, 0x04, 0xD9, 0x8A, 0xD9, 0x94, + 0xE6, 0x04, 0xDB, 0x81, 0xD9, 0x94, 0xE6, 0x04, + 0xDB, 0x92, 0xD9, 0x94, 0xE6, 0x04, 0xDB, 0x95, + 0xD9, 0x94, 0xE6, 0x05, 0x41, 0xCC, 0x82, 0xCC, + 0x80, 0xE6, 0x05, 0x41, 0xCC, 0x82, 0xCC, 0x81, + 0xE6, 0x05, 0x41, 0xCC, 0x82, 0xCC, 0x83, 0xE6, + 0x05, 0x41, 0xCC, 0x82, 0xCC, 0x89, 0xE6, 0x05, + 0x41, 0xCC, 0x86, 0xCC, 0x80, 0xE6, 0x05, 0x41, + // Bytes 3880 - 38bf + 0xCC, 0x86, 0xCC, 0x81, 0xE6, 0x05, 0x41, 0xCC, + 0x86, 0xCC, 0x83, 0xE6, 0x05, 0x41, 0xCC, 0x86, + 0xCC, 0x89, 0xE6, 0x05, 0x41, 0xCC, 0x87, 0xCC, + 0x84, 0xE6, 0x05, 0x41, 0xCC, 0x88, 0xCC, 0x84, + 0xE6, 0x05, 0x41, 0xCC, 0x8A, 0xCC, 0x81, 0xE6, + 0x05, 0x41, 0xCC, 0xA3, 0xCC, 0x82, 0xE6, 0x05, + 0x41, 0xCC, 0xA3, 0xCC, 0x86, 0xE6, 0x05, 0x43, + 0xCC, 0xA7, 0xCC, 0x81, 0xE6, 0x05, 0x45, 0xCC, + // Bytes 38c0 - 38ff + 0x82, 0xCC, 0x80, 0xE6, 0x05, 0x45, 0xCC, 0x82, + 0xCC, 0x81, 0xE6, 0x05, 0x45, 0xCC, 0x82, 0xCC, + 0x83, 0xE6, 0x05, 0x45, 0xCC, 0x82, 0xCC, 0x89, + 0xE6, 0x05, 0x45, 0xCC, 0x84, 0xCC, 0x80, 0xE6, + 0x05, 0x45, 0xCC, 0x84, 0xCC, 0x81, 0xE6, 0x05, + 0x45, 0xCC, 0xA3, 0xCC, 0x82, 0xE6, 0x05, 0x45, + 0xCC, 0xA7, 0xCC, 0x86, 0xE6, 0x05, 0x49, 0xCC, + 0x88, 0xCC, 0x81, 0xE6, 0x05, 0x4C, 0xCC, 0xA3, + // Bytes 3900 - 393f + 0xCC, 0x84, 0xE6, 0x05, 0x4F, 0xCC, 0x82, 0xCC, + 0x80, 0xE6, 0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x81, + 0xE6, 0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x83, 0xE6, + 0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x89, 0xE6, 0x05, + 0x4F, 0xCC, 0x83, 0xCC, 0x81, 0xE6, 0x05, 0x4F, + 0xCC, 0x83, 0xCC, 0x84, 0xE6, 0x05, 0x4F, 0xCC, + 0x83, 0xCC, 0x88, 0xE6, 0x05, 0x4F, 0xCC, 0x84, + 0xCC, 0x80, 0xE6, 0x05, 0x4F, 0xCC, 0x84, 0xCC, + // Bytes 3940 - 397f + 0x81, 0xE6, 0x05, 0x4F, 0xCC, 0x87, 0xCC, 0x84, + 0xE6, 0x05, 0x4F, 0xCC, 0x88, 0xCC, 0x84, 0xE6, + 0x05, 0x4F, 0xCC, 0x9B, 0xCC, 0x80, 0xE6, 0x05, + 0x4F, 0xCC, 0x9B, 0xCC, 0x81, 0xE6, 0x05, 0x4F, + 0xCC, 0x9B, 0xCC, 0x83, 0xE6, 0x05, 0x4F, 0xCC, + 0x9B, 0xCC, 0x89, 0xE6, 0x05, 0x4F, 0xCC, 0x9B, + 0xCC, 0xA3, 0xDC, 0x05, 0x4F, 0xCC, 0xA3, 0xCC, + 0x82, 0xE6, 0x05, 0x4F, 0xCC, 0xA8, 0xCC, 0x84, + // Bytes 3980 - 39bf + 0xE6, 0x05, 0x52, 0xCC, 0xA3, 0xCC, 0x84, 0xE6, + 0x05, 0x53, 0xCC, 0x81, 0xCC, 0x87, 0xE6, 0x05, + 0x53, 0xCC, 0x8C, 0xCC, 0x87, 0xE6, 0x05, 0x53, + 0xCC, 0xA3, 0xCC, 0x87, 0xE6, 0x05, 0x55, 0xCC, + 0x83, 0xCC, 0x81, 0xE6, 0x05, 0x55, 0xCC, 0x84, + 0xCC, 0x88, 0xE6, 0x05, 0x55, 0xCC, 0x88, 0xCC, + 0x80, 0xE6, 0x05, 0x55, 0xCC, 0x88, 0xCC, 0x81, + 0xE6, 0x05, 0x55, 0xCC, 0x88, 0xCC, 0x84, 0xE6, + // Bytes 39c0 - 39ff + 0x05, 0x55, 0xCC, 0x88, 0xCC, 0x8C, 0xE6, 0x05, + 0x55, 0xCC, 0x9B, 0xCC, 0x80, 0xE6, 0x05, 0x55, + 0xCC, 0x9B, 0xCC, 0x81, 0xE6, 0x05, 0x55, 0xCC, + 0x9B, 0xCC, 0x83, 0xE6, 0x05, 0x55, 0xCC, 0x9B, + 0xCC, 0x89, 0xE6, 0x05, 0x55, 0xCC, 0x9B, 0xCC, + 0xA3, 0xDC, 0x05, 0x61, 0xCC, 0x82, 0xCC, 0x80, + 0xE6, 0x05, 0x61, 0xCC, 0x82, 0xCC, 0x81, 0xE6, + 0x05, 0x61, 0xCC, 0x82, 0xCC, 0x83, 0xE6, 0x05, + // Bytes 3a00 - 3a3f + 0x61, 0xCC, 0x82, 0xCC, 0x89, 0xE6, 0x05, 0x61, + 0xCC, 0x86, 0xCC, 0x80, 0xE6, 0x05, 0x61, 0xCC, + 0x86, 0xCC, 0x81, 0xE6, 0x05, 0x61, 0xCC, 0x86, + 0xCC, 0x83, 0xE6, 0x05, 0x61, 0xCC, 0x86, 0xCC, + 0x89, 0xE6, 0x05, 0x61, 0xCC, 0x87, 0xCC, 0x84, + 0xE6, 0x05, 0x61, 0xCC, 0x88, 0xCC, 0x84, 0xE6, + 0x05, 0x61, 0xCC, 0x8A, 0xCC, 0x81, 0xE6, 0x05, + 0x61, 0xCC, 0xA3, 0xCC, 0x82, 0xE6, 0x05, 0x61, + // Bytes 3a40 - 3a7f + 0xCC, 0xA3, 0xCC, 0x86, 0xE6, 0x05, 0x63, 0xCC, + 0xA7, 0xCC, 0x81, 0xE6, 0x05, 0x65, 0xCC, 0x82, + 0xCC, 0x80, 0xE6, 0x05, 0x65, 0xCC, 0x82, 0xCC, + 0x81, 0xE6, 0x05, 0x65, 0xCC, 0x82, 0xCC, 0x83, + 0xE6, 0x05, 0x65, 0xCC, 0x82, 0xCC, 0x89, 0xE6, + 0x05, 0x65, 0xCC, 0x84, 0xCC, 0x80, 0xE6, 0x05, + 0x65, 0xCC, 0x84, 0xCC, 0x81, 0xE6, 0x05, 0x65, + 0xCC, 0xA3, 0xCC, 0x82, 0xE6, 0x05, 0x65, 0xCC, + // Bytes 3a80 - 3abf + 0xA7, 0xCC, 0x86, 0xE6, 0x05, 0x69, 0xCC, 0x88, + 0xCC, 0x81, 0xE6, 0x05, 0x6C, 0xCC, 0xA3, 0xCC, + 0x84, 0xE6, 0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x80, + 0xE6, 0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x81, 0xE6, + 0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x83, 0xE6, 0x05, + 0x6F, 0xCC, 0x82, 0xCC, 0x89, 0xE6, 0x05, 0x6F, + 0xCC, 0x83, 0xCC, 0x81, 0xE6, 0x05, 0x6F, 0xCC, + 0x83, 0xCC, 0x84, 0xE6, 0x05, 0x6F, 0xCC, 0x83, + // Bytes 3ac0 - 3aff + 0xCC, 0x88, 0xE6, 0x05, 0x6F, 0xCC, 0x84, 0xCC, + 0x80, 0xE6, 0x05, 0x6F, 0xCC, 0x84, 0xCC, 0x81, + 0xE6, 0x05, 0x6F, 0xCC, 0x87, 0xCC, 0x84, 0xE6, + 0x05, 0x6F, 0xCC, 0x88, 0xCC, 0x84, 0xE6, 0x05, + 0x6F, 0xCC, 0x9B, 0xCC, 0x80, 0xE6, 0x05, 0x6F, + 0xCC, 0x9B, 0xCC, 0x81, 0xE6, 0x05, 0x6F, 0xCC, + 0x9B, 0xCC, 0x83, 0xE6, 0x05, 0x6F, 0xCC, 0x9B, + 0xCC, 0x89, 0xE6, 0x05, 0x6F, 0xCC, 0x9B, 0xCC, + // Bytes 3b00 - 3b3f + 0xA3, 0xDC, 0x05, 0x6F, 0xCC, 0xA3, 0xCC, 0x82, + 0xE6, 0x05, 0x6F, 0xCC, 0xA8, 0xCC, 0x84, 0xE6, + 0x05, 0x72, 0xCC, 0xA3, 0xCC, 0x84, 0xE6, 0x05, + 0x73, 0xCC, 0x81, 0xCC, 0x87, 0xE6, 0x05, 0x73, + 0xCC, 0x8C, 0xCC, 0x87, 0xE6, 0x05, 0x73, 0xCC, + 0xA3, 0xCC, 0x87, 0xE6, 0x05, 0x75, 0xCC, 0x83, + 0xCC, 0x81, 0xE6, 0x05, 0x75, 0xCC, 0x84, 0xCC, + 0x88, 0xE6, 0x05, 0x75, 0xCC, 0x88, 0xCC, 0x80, + // Bytes 3b40 - 3b7f + 0xE6, 0x05, 0x75, 0xCC, 0x88, 0xCC, 0x81, 0xE6, + 0x05, 0x75, 0xCC, 0x88, 0xCC, 0x84, 0xE6, 0x05, + 0x75, 0xCC, 0x88, 0xCC, 0x8C, 0xE6, 0x05, 0x75, + 0xCC, 0x9B, 0xCC, 0x80, 0xE6, 0x05, 0x75, 0xCC, + 0x9B, 0xCC, 0x81, 0xE6, 0x05, 0x75, 0xCC, 0x9B, + 0xCC, 0x83, 0xE6, 0x05, 0x75, 0xCC, 0x9B, 0xCC, + 0x89, 0xE6, 0x05, 0x75, 0xCC, 0x9B, 0xCC, 0xA3, + 0xDC, 0x05, 0xE1, 0xBE, 0xBF, 0xCC, 0x80, 0xE6, + // Bytes 3b80 - 3bbf + 0x05, 0xE1, 0xBE, 0xBF, 0xCC, 0x81, 0xE6, 0x05, + 0xE1, 0xBE, 0xBF, 0xCD, 0x82, 0xE6, 0x05, 0xE1, + 0xBF, 0xBE, 0xCC, 0x80, 0xE6, 0x05, 0xE1, 0xBF, + 0xBE, 0xCC, 0x81, 0xE6, 0x05, 0xE1, 0xBF, 0xBE, + 0xCD, 0x82, 0xE6, 0x05, 0xE2, 0x86, 0x90, 0xCC, + 0xB8, 0x01, 0x05, 0xE2, 0x86, 0x92, 0xCC, 0xB8, + 0x01, 0x05, 0xE2, 0x86, 0x94, 0xCC, 0xB8, 0x01, + 0x05, 0xE2, 0x87, 0x90, 0xCC, 0xB8, 0x01, 0x05, + // Bytes 3bc0 - 3bff + 0xE2, 0x87, 0x92, 0xCC, 0xB8, 0x01, 0x05, 0xE2, + 0x87, 0x94, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x88, + 0x83, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x88, 0x88, + 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x88, 0x8B, 0xCC, + 0xB8, 0x01, 0x05, 0xE2, 0x88, 0xA3, 0xCC, 0xB8, + 0x01, 0x05, 0xE2, 0x88, 0xA5, 0xCC, 0xB8, 0x01, + 0x05, 0xE2, 0x88, 0xBC, 0xCC, 0xB8, 0x01, 0x05, + 0xE2, 0x89, 0x83, 0xCC, 0xB8, 0x01, 0x05, 0xE2, + // Bytes 3c00 - 3c3f + 0x89, 0x85, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, + 0x88, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, 0x8D, + 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xA1, 0xCC, + 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xA4, 0xCC, 0xB8, + 0x01, 0x05, 0xE2, 0x89, 0xA5, 0xCC, 0xB8, 0x01, + 0x05, 0xE2, 0x89, 0xB2, 0xCC, 0xB8, 0x01, 0x05, + 0xE2, 0x89, 0xB3, 0xCC, 0xB8, 0x01, 0x05, 0xE2, + 0x89, 0xB6, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, + // Bytes 3c40 - 3c7f + 0xB7, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xBA, + 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xBB, 0xCC, + 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xBC, 0xCC, 0xB8, + 0x01, 0x05, 0xE2, 0x89, 0xBD, 0xCC, 0xB8, 0x01, + 0x05, 0xE2, 0x8A, 0x82, 0xCC, 0xB8, 0x01, 0x05, + 0xE2, 0x8A, 0x83, 0xCC, 0xB8, 0x01, 0x05, 0xE2, + 0x8A, 0x86, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, + 0x87, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0x91, + // Bytes 3c80 - 3cbf + 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0x92, 0xCC, + 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0xA2, 0xCC, 0xB8, + 0x01, 0x05, 0xE2, 0x8A, 0xA8, 0xCC, 0xB8, 0x01, + 0x05, 0xE2, 0x8A, 0xA9, 0xCC, 0xB8, 0x01, 0x05, + 0xE2, 0x8A, 0xAB, 0xCC, 0xB8, 0x01, 0x05, 0xE2, + 0x8A, 0xB2, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, + 0xB3, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0xB4, + 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0xB5, 0xCC, + // Bytes 3cc0 - 3cff + 0xB8, 0x01, 0x06, 0xCE, 0x91, 0xCC, 0x93, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0x91, 0xCC, 0x94, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0x95, 0xCC, 0x93, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0x95, 0xCC, 0x93, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0x95, 0xCC, 0x94, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0x95, 0xCC, 0x94, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0x97, 0xCC, 0x93, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0x97, 0xCC, 0x94, 0xCD, + // Bytes 3d00 - 3d3f + 0x85, 0xF0, 0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCD, + 0x82, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCD, + 0x82, 0xE6, 0x06, 0xCE, 0x9F, 0xCC, 0x93, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0x9F, 0xCC, 0x93, 0xCC, + // Bytes 3d40 - 3d7f + 0x81, 0xE6, 0x06, 0xCE, 0x9F, 0xCC, 0x94, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0x9F, 0xCC, 0x94, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCD, + 0x82, 0xE6, 0x06, 0xCE, 0xA9, 0xCC, 0x93, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xA9, 0xCC, 0x94, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCC, 0x80, 0xCD, + // Bytes 3d80 - 3dbf + 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCC, 0x81, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCC, 0x93, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCC, 0x94, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCD, 0x82, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB5, 0xCC, 0x93, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0xB5, 0xCC, 0x93, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0xB5, 0xCC, 0x94, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0xB5, 0xCC, 0x94, 0xCC, + // Bytes 3dc0 - 3dff + 0x81, 0xE6, 0x06, 0xCE, 0xB7, 0xCC, 0x80, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB7, 0xCC, 0x81, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB7, 0xCC, 0x93, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB7, 0xCC, 0x94, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB7, 0xCD, 0x82, 0xCD, + 0x85, 0xF0, 0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCD, + // Bytes 3e00 - 3e3f + 0x82, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCD, + 0x82, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCC, + 0x81, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCD, + 0x82, 0xE6, 0x06, 0xCE, 0xBF, 0xCC, 0x93, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0xBF, 0xCC, 0x93, 0xCC, + // Bytes 3e40 - 3e7f + 0x81, 0xE6, 0x06, 0xCE, 0xBF, 0xCC, 0x94, 0xCC, + 0x80, 0xE6, 0x06, 0xCE, 0xBF, 0xCC, 0x94, 0xCC, + 0x81, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCC, + 0x80, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCC, + 0x81, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCD, + 0x82, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCC, + 0x80, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCC, + 0x81, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCD, + // Bytes 3e80 - 3ebf + 0x82, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCC, + 0x80, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCC, + 0x81, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCD, + 0x82, 0xE6, 0x06, 0xCF, 0x89, 0xCC, 0x80, 0xCD, + 0x85, 0xF0, 0x06, 0xCF, 0x89, 0xCC, 0x81, 0xCD, + 0x85, 0xF0, 0x06, 0xCF, 0x89, 0xCC, 0x93, 0xCD, + 0x85, 0xF0, 0x06, 0xCF, 0x89, 0xCC, 0x94, 0xCD, + 0x85, 0xF0, 0x06, 0xCF, 0x89, 0xCD, 0x82, 0xCD, + // Bytes 3ec0 - 3eff + 0x85, 0xF0, 0x06, 0xE0, 0xA4, 0xA8, 0xE0, 0xA4, + 0xBC, 0x07, 0x06, 0xE0, 0xA4, 0xB0, 0xE0, 0xA4, + 0xBC, 0x07, 0x06, 0xE0, 0xA4, 0xB3, 0xE0, 0xA4, + 0xBC, 0x07, 0x06, 0xE0, 0xB1, 0x86, 0xE0, 0xB1, + 0x96, 0x5B, 0x06, 0xE0, 0xB7, 0x99, 0xE0, 0xB7, + 0x8A, 0x09, 0x06, 0xE3, 0x81, 0x86, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x8B, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x8D, 0xE3, 0x82, + // Bytes 3f00 - 3f3f + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x8F, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x91, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x93, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x95, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x97, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x99, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x9B, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x9D, 0xE3, 0x82, + // Bytes 3f40 - 3f7f + 0x99, 0x08, 0x06, 0xE3, 0x81, 0x9F, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xA1, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xA4, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xA6, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xA8, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xAF, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xAF, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x81, 0xB2, 0xE3, 0x82, + // Bytes 3f80 - 3fbf + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xB2, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x81, 0xB5, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xB5, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x81, 0xB8, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xB8, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x81, 0xBB, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x81, 0xBB, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x82, 0x9D, 0xE3, 0x82, + // Bytes 3fc0 - 3fff + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xA6, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xAB, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xAD, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xAF, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB1, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB3, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB5, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB7, 0xE3, 0x82, + // Bytes 4000 - 403f + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB9, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xBB, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xBD, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x82, 0xBF, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x81, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x84, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x86, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x88, 0xE3, 0x82, + // Bytes 4040 - 407f + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x8F, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x8F, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x83, 0x92, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x92, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x83, 0x95, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x95, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x83, 0x98, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x98, 0xE3, 0x82, + // Bytes 4080 - 40bf + 0x9A, 0x08, 0x06, 0xE3, 0x83, 0x9B, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0x9B, 0xE3, 0x82, + 0x9A, 0x08, 0x06, 0xE3, 0x83, 0xAF, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0xB0, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0xB1, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0xB2, 0xE3, 0x82, + 0x99, 0x08, 0x06, 0xE3, 0x83, 0xBD, 0xE3, 0x82, + 0x99, 0x08, 0x08, 0xCE, 0x91, 0xCC, 0x93, 0xCC, + // Bytes 40c0 - 40ff + 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0x91, 0xCC, + 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, + 0x91, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF0, + 0x08, 0xCE, 0x91, 0xCC, 0x94, 0xCC, 0x80, 0xCD, + 0x85, 0xF0, 0x08, 0xCE, 0x91, 0xCC, 0x94, 0xCC, + 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0x91, 0xCC, + 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCE, + 0x97, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF0, + // Bytes 4100 - 413f + 0x08, 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x81, 0xCD, + 0x85, 0xF0, 0x08, 0xCE, 0x97, 0xCC, 0x93, 0xCD, + 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0x97, 0xCC, + 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE, + 0x97, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF0, + 0x08, 0xCE, 0x97, 0xCC, 0x94, 0xCD, 0x82, 0xCD, + 0x85, 0xF0, 0x08, 0xCE, 0xA9, 0xCC, 0x93, 0xCC, + 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xA9, 0xCC, + // Bytes 4140 - 417f + 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, + 0xA9, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF0, + 0x08, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, 0x80, 0xCD, + 0x85, 0xF0, 0x08, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, + 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xA9, 0xCC, + 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCE, + 0xB1, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF0, + 0x08, 0xCE, 0xB1, 0xCC, 0x93, 0xCC, 0x81, 0xCD, + // Bytes 4180 - 41bf + 0x85, 0xF0, 0x08, 0xCE, 0xB1, 0xCC, 0x93, 0xCD, + 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xB1, 0xCC, + 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE, + 0xB1, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF0, + 0x08, 0xCE, 0xB1, 0xCC, 0x94, 0xCD, 0x82, 0xCD, + 0x85, 0xF0, 0x08, 0xCE, 0xB7, 0xCC, 0x93, 0xCC, + 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xB7, 0xCC, + 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, + // Bytes 41c0 - 41ff + 0xB7, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF0, + 0x08, 0xCE, 0xB7, 0xCC, 0x94, 0xCC, 0x80, 0xCD, + 0x85, 0xF0, 0x08, 0xCE, 0xB7, 0xCC, 0x94, 0xCC, + 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xB7, 0xCC, + 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCF, + 0x89, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF0, + 0x08, 0xCF, 0x89, 0xCC, 0x93, 0xCC, 0x81, 0xCD, + 0x85, 0xF0, 0x08, 0xCF, 0x89, 0xCC, 0x93, 0xCD, + // Bytes 4200 - 423f + 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCF, 0x89, 0xCC, + 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCF, + 0x89, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF0, + 0x08, 0xCF, 0x89, 0xCC, 0x94, 0xCD, 0x82, 0xCD, + 0x85, 0xF0, 0x08, 0xF0, 0x91, 0x82, 0x99, 0xF0, + 0x91, 0x82, 0xBA, 0x07, 0x08, 0xF0, 0x91, 0x82, + 0x9B, 0xF0, 0x91, 0x82, 0xBA, 0x07, 0x08, 0xF0, + 0x91, 0x82, 0xA5, 0xF0, 0x91, 0x82, 0xBA, 0x07, + // Bytes 4240 - 427f + 0x43, 0x20, 0xCC, 0x81, 0xE6, 0x43, 0x20, 0xCC, + 0x83, 0xE6, 0x43, 0x20, 0xCC, 0x84, 0xE6, 0x43, + 0x20, 0xCC, 0x85, 0xE6, 0x43, 0x20, 0xCC, 0x86, + 0xE6, 0x43, 0x20, 0xCC, 0x87, 0xE6, 0x43, 0x20, + 0xCC, 0x88, 0xE6, 0x43, 0x20, 0xCC, 0x8A, 0xE6, + 0x43, 0x20, 0xCC, 0x8B, 0xE6, 0x43, 0x20, 0xCC, + 0x93, 0xE6, 0x43, 0x20, 0xCC, 0x94, 0xE6, 0x43, + 0x20, 0xCC, 0xA7, 0xCA, 0x43, 0x20, 0xCC, 0xA8, + // Bytes 4280 - 42bf + 0xCA, 0x43, 0x20, 0xCC, 0xB3, 0xDC, 0x43, 0x20, + 0xCD, 0x82, 0xE6, 0x43, 0x20, 0xCD, 0x85, 0xF0, + 0x43, 0x20, 0xD9, 0x8B, 0x1B, 0x43, 0x20, 0xD9, + 0x8C, 0x1C, 0x43, 0x20, 0xD9, 0x8D, 0x1D, 0x43, + 0x20, 0xD9, 0x8E, 0x1E, 0x43, 0x20, 0xD9, 0x8F, + 0x1F, 0x43, 0x20, 0xD9, 0x90, 0x20, 0x43, 0x20, + 0xD9, 0x91, 0x21, 0x43, 0x20, 0xD9, 0x92, 0x22, + 0x43, 0x41, 0xCC, 0x8A, 0xE6, 0x43, 0x73, 0xCC, + // Bytes 42c0 - 42ff + 0x87, 0xE6, 0x44, 0x20, 0xE3, 0x82, 0x99, 0x08, + 0x44, 0x20, 0xE3, 0x82, 0x9A, 0x08, 0x44, 0xC2, + 0xA8, 0xCC, 0x81, 0xE6, 0x44, 0xCE, 0x91, 0xCC, + 0x81, 0xE6, 0x44, 0xCE, 0x95, 0xCC, 0x81, 0xE6, + 0x44, 0xCE, 0x97, 0xCC, 0x81, 0xE6, 0x44, 0xCE, + 0x99, 0xCC, 0x81, 0xE6, 0x44, 0xCE, 0x9F, 0xCC, + 0x81, 0xE6, 0x44, 0xCE, 0xA5, 0xCC, 0x81, 0xE6, + 0x44, 0xCE, 0xA5, 0xCC, 0x88, 0xE6, 0x44, 0xCE, + // Bytes 4300 - 433f + 0xA9, 0xCC, 0x81, 0xE6, 0x44, 0xCE, 0xB1, 0xCC, + 0x81, 0xE6, 0x44, 0xCE, 0xB5, 0xCC, 0x81, 0xE6, + 0x44, 0xCE, 0xB7, 0xCC, 0x81, 0xE6, 0x44, 0xCE, + 0xB9, 0xCC, 0x81, 0xE6, 0x44, 0xCE, 0xBF, 0xCC, + 0x81, 0xE6, 0x44, 0xCF, 0x85, 0xCC, 0x81, 0xE6, + 0x44, 0xCF, 0x89, 0xCC, 0x81, 0xE6, 0x44, 0xD7, + 0x90, 0xD6, 0xB7, 0x11, 0x44, 0xD7, 0x90, 0xD6, + 0xB8, 0x12, 0x44, 0xD7, 0x90, 0xD6, 0xBC, 0x15, + // Bytes 4340 - 437f + 0x44, 0xD7, 0x91, 0xD6, 0xBC, 0x15, 0x44, 0xD7, + 0x91, 0xD6, 0xBF, 0x17, 0x44, 0xD7, 0x92, 0xD6, + 0xBC, 0x15, 0x44, 0xD7, 0x93, 0xD6, 0xBC, 0x15, + 0x44, 0xD7, 0x94, 0xD6, 0xBC, 0x15, 0x44, 0xD7, + 0x95, 0xD6, 0xB9, 0x13, 0x44, 0xD7, 0x95, 0xD6, + 0xBC, 0x15, 0x44, 0xD7, 0x96, 0xD6, 0xBC, 0x15, + 0x44, 0xD7, 0x98, 0xD6, 0xBC, 0x15, 0x44, 0xD7, + 0x99, 0xD6, 0xB4, 0x0E, 0x44, 0xD7, 0x99, 0xD6, + // Bytes 4380 - 43bf + 0xBC, 0x15, 0x44, 0xD7, 0x9A, 0xD6, 0xBC, 0x15, + 0x44, 0xD7, 0x9B, 0xD6, 0xBC, 0x15, 0x44, 0xD7, + 0x9B, 0xD6, 0xBF, 0x17, 0x44, 0xD7, 0x9C, 0xD6, + 0xBC, 0x15, 0x44, 0xD7, 0x9E, 0xD6, 0xBC, 0x15, + 0x44, 0xD7, 0xA0, 0xD6, 0xBC, 0x15, 0x44, 0xD7, + 0xA1, 0xD6, 0xBC, 0x15, 0x44, 0xD7, 0xA3, 0xD6, + 0xBC, 0x15, 0x44, 0xD7, 0xA4, 0xD6, 0xBC, 0x15, + 0x44, 0xD7, 0xA4, 0xD6, 0xBF, 0x17, 0x44, 0xD7, + // Bytes 43c0 - 43ff + 0xA6, 0xD6, 0xBC, 0x15, 0x44, 0xD7, 0xA7, 0xD6, + 0xBC, 0x15, 0x44, 0xD7, 0xA8, 0xD6, 0xBC, 0x15, + 0x44, 0xD7, 0xA9, 0xD6, 0xBC, 0x15, 0x44, 0xD7, + 0xA9, 0xD7, 0x81, 0x18, 0x44, 0xD7, 0xA9, 0xD7, + 0x82, 0x19, 0x44, 0xD7, 0xAA, 0xD6, 0xBC, 0x15, + 0x44, 0xD7, 0xB2, 0xD6, 0xB7, 0x11, 0x44, 0xD8, + 0xA7, 0xD9, 0x8B, 0x1B, 0x44, 0xD8, 0xA7, 0xD9, + 0x93, 0xE6, 0x44, 0xD8, 0xA7, 0xD9, 0x94, 0xE6, + // Bytes 4400 - 443f + 0x44, 0xD8, 0xA7, 0xD9, 0x95, 0xDC, 0x44, 0xD8, + 0xB0, 0xD9, 0xB0, 0x23, 0x44, 0xD8, 0xB1, 0xD9, + 0xB0, 0x23, 0x44, 0xD9, 0x80, 0xD9, 0x8B, 0x1B, + 0x44, 0xD9, 0x80, 0xD9, 0x8E, 0x1E, 0x44, 0xD9, + 0x80, 0xD9, 0x8F, 0x1F, 0x44, 0xD9, 0x80, 0xD9, + 0x90, 0x20, 0x44, 0xD9, 0x80, 0xD9, 0x91, 0x21, + 0x44, 0xD9, 0x80, 0xD9, 0x92, 0x22, 0x44, 0xD9, + 0x87, 0xD9, 0xB0, 0x23, 0x44, 0xD9, 0x88, 0xD9, + // Bytes 4440 - 447f + 0x94, 0xE6, 0x44, 0xD9, 0x89, 0xD9, 0xB0, 0x23, + 0x44, 0xD9, 0x8A, 0xD9, 0x94, 0xE6, 0x44, 0xDB, + 0x92, 0xD9, 0x94, 0xE6, 0x44, 0xDB, 0x95, 0xD9, + 0x94, 0xE6, 0x45, 0x20, 0xCC, 0x88, 0xCC, 0x80, + 0xE6, 0x45, 0x20, 0xCC, 0x88, 0xCC, 0x81, 0xE6, + 0x45, 0x20, 0xCC, 0x88, 0xCD, 0x82, 0xE6, 0x45, + 0x20, 0xCC, 0x93, 0xCC, 0x80, 0xE6, 0x45, 0x20, + 0xCC, 0x93, 0xCC, 0x81, 0xE6, 0x45, 0x20, 0xCC, + // Bytes 4480 - 44bf + 0x93, 0xCD, 0x82, 0xE6, 0x45, 0x20, 0xCC, 0x94, + 0xCC, 0x80, 0xE6, 0x45, 0x20, 0xCC, 0x94, 0xCC, + 0x81, 0xE6, 0x45, 0x20, 0xCC, 0x94, 0xCD, 0x82, + 0xE6, 0x45, 0x20, 0xD9, 0x8C, 0xD9, 0x91, 0x21, + 0x45, 0x20, 0xD9, 0x8D, 0xD9, 0x91, 0x21, 0x45, + 0x20, 0xD9, 0x8E, 0xD9, 0x91, 0x21, 0x45, 0x20, + 0xD9, 0x8F, 0xD9, 0x91, 0x21, 0x45, 0x20, 0xD9, + 0x90, 0xD9, 0x91, 0x21, 0x45, 0x20, 0xD9, 0x91, + // Bytes 44c0 - 44ff + 0xD9, 0xB0, 0x23, 0x45, 0xE2, 0xAB, 0x9D, 0xCC, + 0xB8, 0x01, 0x46, 0xCE, 0xB9, 0xCC, 0x88, 0xCC, + 0x81, 0xE6, 0x46, 0xCF, 0x85, 0xCC, 0x88, 0xCC, + 0x81, 0xE6, 0x46, 0xD7, 0xA9, 0xD6, 0xBC, 0xD7, + 0x81, 0x18, 0x46, 0xD7, 0xA9, 0xD6, 0xBC, 0xD7, + 0x82, 0x19, 0x46, 0xD9, 0x80, 0xD9, 0x8E, 0xD9, + 0x91, 0x21, 0x46, 0xD9, 0x80, 0xD9, 0x8F, 0xD9, + 0x91, 0x21, 0x46, 0xD9, 0x80, 0xD9, 0x90, 0xD9, + // Bytes 4500 - 453f + 0x91, 0x21, 0x46, 0xE0, 0xA4, 0x95, 0xE0, 0xA4, + 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0x96, 0xE0, 0xA4, + 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0x97, 0xE0, 0xA4, + 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0x9C, 0xE0, 0xA4, + 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0xA1, 0xE0, 0xA4, + 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0xA2, 0xE0, 0xA4, + 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0xAB, 0xE0, 0xA4, + 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0xAF, 0xE0, 0xA4, + // Bytes 4540 - 457f + 0xBC, 0x07, 0x46, 0xE0, 0xA6, 0xA1, 0xE0, 0xA6, + 0xBC, 0x07, 0x46, 0xE0, 0xA6, 0xA2, 0xE0, 0xA6, + 0xBC, 0x07, 0x46, 0xE0, 0xA6, 0xAF, 0xE0, 0xA6, + 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0x96, 0xE0, 0xA8, + 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0x97, 0xE0, 0xA8, + 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0x9C, 0xE0, 0xA8, + 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0xAB, 0xE0, 0xA8, + 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0xB2, 0xE0, 0xA8, + // Bytes 4580 - 45bf + 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0xB8, 0xE0, 0xA8, + 0xBC, 0x07, 0x46, 0xE0, 0xAC, 0xA1, 0xE0, 0xAC, + 0xBC, 0x07, 0x46, 0xE0, 0xAC, 0xA2, 0xE0, 0xAC, + 0xBC, 0x07, 0x46, 0xE0, 0xBE, 0xB2, 0xE0, 0xBE, + 0x80, 0x82, 0x46, 0xE0, 0xBE, 0xB3, 0xE0, 0xBE, + 0x80, 0x82, 0x46, 0xE3, 0x83, 0x86, 0xE3, 0x82, + 0x99, 0x08, 0x48, 0xF0, 0x9D, 0x85, 0x97, 0xF0, + 0x9D, 0x85, 0xA5, 0xD8, 0x48, 0xF0, 0x9D, 0x85, + // Bytes 45c0 - 45ff + 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xD8, 0x48, 0xF0, + 0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xD8, + 0x48, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85, + 0xA5, 0xD8, 0x49, 0xE0, 0xBE, 0xB2, 0xE0, 0xBD, + 0xB1, 0xE0, 0xBE, 0x80, 0x82, 0x49, 0xE0, 0xBE, + 0xB3, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0x82, + 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, + 0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xD8, 0x4C, 0xF0, + // Bytes 4600 - 463f + 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, + 0x9D, 0x85, 0xAF, 0xD8, 0x4C, 0xF0, 0x9D, 0x85, + 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, + 0xB0, 0xD8, 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, + 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xB1, 0xD8, + 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, + 0xA5, 0xF0, 0x9D, 0x85, 0xB2, 0xD8, 0x4C, 0xF0, + 0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, + // Bytes 4640 - 467f + 0x9D, 0x85, 0xAE, 0xD8, 0x4C, 0xF0, 0x9D, 0x86, + 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, + 0xAF, 0xD8, 0x4C, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, + 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xD8, + 0x4C, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85, + 0xA5, 0xF0, 0x9D, 0x85, 0xAF, 0xD8, 0x83, 0x41, + 0xCC, 0x82, 0xE6, 0x83, 0x41, 0xCC, 0x86, 0xE6, + 0x83, 0x41, 0xCC, 0x87, 0xE6, 0x83, 0x41, 0xCC, + // Bytes 4680 - 46bf + 0x88, 0xE6, 0x83, 0x41, 0xCC, 0x8A, 0xE6, 0x83, + 0x41, 0xCC, 0xA3, 0xDC, 0x83, 0x43, 0xCC, 0xA7, + 0xCA, 0x83, 0x45, 0xCC, 0x82, 0xE6, 0x83, 0x45, + 0xCC, 0x84, 0xE6, 0x83, 0x45, 0xCC, 0xA3, 0xDC, + 0x83, 0x45, 0xCC, 0xA7, 0xCA, 0x83, 0x49, 0xCC, + 0x88, 0xE6, 0x83, 0x4C, 0xCC, 0xA3, 0xDC, 0x83, + 0x4F, 0xCC, 0x82, 0xE6, 0x83, 0x4F, 0xCC, 0x83, + 0xE6, 0x83, 0x4F, 0xCC, 0x84, 0xE6, 0x83, 0x4F, + // Bytes 46c0 - 46ff + 0xCC, 0x87, 0xE6, 0x83, 0x4F, 0xCC, 0x88, 0xE6, + 0x83, 0x4F, 0xCC, 0x9B, 0xD8, 0x83, 0x4F, 0xCC, + 0xA3, 0xDC, 0x83, 0x4F, 0xCC, 0xA8, 0xCA, 0x83, + 0x52, 0xCC, 0xA3, 0xDC, 0x83, 0x53, 0xCC, 0x81, + 0xE6, 0x83, 0x53, 0xCC, 0x8C, 0xE6, 0x83, 0x53, + 0xCC, 0xA3, 0xDC, 0x83, 0x55, 0xCC, 0x83, 0xE6, + 0x83, 0x55, 0xCC, 0x84, 0xE6, 0x83, 0x55, 0xCC, + 0x88, 0xE6, 0x83, 0x55, 0xCC, 0x9B, 0xD8, 0x83, + // Bytes 4700 - 473f + 0x61, 0xCC, 0x82, 0xE6, 0x83, 0x61, 0xCC, 0x86, + 0xE6, 0x83, 0x61, 0xCC, 0x87, 0xE6, 0x83, 0x61, + 0xCC, 0x88, 0xE6, 0x83, 0x61, 0xCC, 0x8A, 0xE6, + 0x83, 0x61, 0xCC, 0xA3, 0xDC, 0x83, 0x63, 0xCC, + 0xA7, 0xCA, 0x83, 0x65, 0xCC, 0x82, 0xE6, 0x83, + 0x65, 0xCC, 0x84, 0xE6, 0x83, 0x65, 0xCC, 0xA3, + 0xDC, 0x83, 0x65, 0xCC, 0xA7, 0xCA, 0x83, 0x69, + 0xCC, 0x88, 0xE6, 0x83, 0x6C, 0xCC, 0xA3, 0xDC, + // Bytes 4740 - 477f + 0x83, 0x6F, 0xCC, 0x82, 0xE6, 0x83, 0x6F, 0xCC, + 0x83, 0xE6, 0x83, 0x6F, 0xCC, 0x84, 0xE6, 0x83, + 0x6F, 0xCC, 0x87, 0xE6, 0x83, 0x6F, 0xCC, 0x88, + 0xE6, 0x83, 0x6F, 0xCC, 0x9B, 0xD8, 0x83, 0x6F, + 0xCC, 0xA3, 0xDC, 0x83, 0x6F, 0xCC, 0xA8, 0xCA, + 0x83, 0x72, 0xCC, 0xA3, 0xDC, 0x83, 0x73, 0xCC, + 0x81, 0xE6, 0x83, 0x73, 0xCC, 0x8C, 0xE6, 0x83, + 0x73, 0xCC, 0xA3, 0xDC, 0x83, 0x75, 0xCC, 0x83, + // Bytes 4780 - 47bf + 0xE6, 0x83, 0x75, 0xCC, 0x84, 0xE6, 0x83, 0x75, + 0xCC, 0x88, 0xE6, 0x83, 0x75, 0xCC, 0x9B, 0xD8, + 0x84, 0xCE, 0x91, 0xCC, 0x93, 0xE6, 0x84, 0xCE, + 0x91, 0xCC, 0x94, 0xE6, 0x84, 0xCE, 0x95, 0xCC, + 0x93, 0xE6, 0x84, 0xCE, 0x95, 0xCC, 0x94, 0xE6, + 0x84, 0xCE, 0x97, 0xCC, 0x93, 0xE6, 0x84, 0xCE, + 0x97, 0xCC, 0x94, 0xE6, 0x84, 0xCE, 0x99, 0xCC, + 0x93, 0xE6, 0x84, 0xCE, 0x99, 0xCC, 0x94, 0xE6, + // Bytes 47c0 - 47ff + 0x84, 0xCE, 0x9F, 0xCC, 0x93, 0xE6, 0x84, 0xCE, + 0x9F, 0xCC, 0x94, 0xE6, 0x84, 0xCE, 0xA5, 0xCC, + 0x94, 0xE6, 0x84, 0xCE, 0xA9, 0xCC, 0x93, 0xE6, + 0x84, 0xCE, 0xA9, 0xCC, 0x94, 0xE6, 0x84, 0xCE, + 0xB1, 0xCC, 0x80, 0xE6, 0x84, 0xCE, 0xB1, 0xCC, + 0x81, 0xE6, 0x84, 0xCE, 0xB1, 0xCC, 0x93, 0xE6, + 0x84, 0xCE, 0xB1, 0xCC, 0x94, 0xE6, 0x84, 0xCE, + 0xB1, 0xCD, 0x82, 0xE6, 0x84, 0xCE, 0xB5, 0xCC, + // Bytes 4800 - 483f + 0x93, 0xE6, 0x84, 0xCE, 0xB5, 0xCC, 0x94, 0xE6, + 0x84, 0xCE, 0xB7, 0xCC, 0x80, 0xE6, 0x84, 0xCE, + 0xB7, 0xCC, 0x81, 0xE6, 0x84, 0xCE, 0xB7, 0xCC, + 0x93, 0xE6, 0x84, 0xCE, 0xB7, 0xCC, 0x94, 0xE6, + 0x84, 0xCE, 0xB7, 0xCD, 0x82, 0xE6, 0x84, 0xCE, + 0xB9, 0xCC, 0x88, 0xE6, 0x84, 0xCE, 0xB9, 0xCC, + 0x93, 0xE6, 0x84, 0xCE, 0xB9, 0xCC, 0x94, 0xE6, + 0x84, 0xCE, 0xBF, 0xCC, 0x93, 0xE6, 0x84, 0xCE, + // Bytes 4840 - 487f + 0xBF, 0xCC, 0x94, 0xE6, 0x84, 0xCF, 0x85, 0xCC, + 0x88, 0xE6, 0x84, 0xCF, 0x85, 0xCC, 0x93, 0xE6, + 0x84, 0xCF, 0x85, 0xCC, 0x94, 0xE6, 0x84, 0xCF, + 0x89, 0xCC, 0x80, 0xE6, 0x84, 0xCF, 0x89, 0xCC, + 0x81, 0xE6, 0x84, 0xCF, 0x89, 0xCC, 0x93, 0xE6, + 0x84, 0xCF, 0x89, 0xCC, 0x94, 0xE6, 0x84, 0xCF, + 0x89, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0x91, 0xCC, + 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0x91, 0xCC, + // Bytes 4880 - 48bf + 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0x91, 0xCC, + 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0x91, 0xCC, + 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0x91, 0xCC, + 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0x91, 0xCC, + 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0x97, 0xCC, + 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0x97, 0xCC, + 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0x97, 0xCC, + 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0x97, 0xCC, + // Bytes 48c0 - 48ff + 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0x97, 0xCC, + 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0x97, 0xCC, + 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xA9, 0xCC, + 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xA9, 0xCC, + 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xA9, 0xCC, + 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xA9, 0xCC, + 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xA9, 0xCC, + 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xA9, 0xCC, + // Bytes 4900 - 493f + 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xB1, 0xCC, + 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xB1, 0xCC, + 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xB1, 0xCC, + 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xB1, 0xCC, + 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xB1, 0xCC, + 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xB1, 0xCC, + 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xB7, 0xCC, + 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xB7, 0xCC, + // Bytes 4940 - 497f + 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xB7, 0xCC, + 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xB7, 0xCC, + 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xB7, 0xCC, + 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xB7, 0xCC, + 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCF, 0x89, 0xCC, + 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCF, 0x89, 0xCC, + 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCF, 0x89, 0xCC, + 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCF, 0x89, 0xCC, + // Bytes 4980 - 49bf + 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCF, 0x89, 0xCC, + 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCF, 0x89, 0xCC, + 0x94, 0xCD, 0x82, 0xE6, 0x42, 0xCC, 0x80, 0xE6, + 0xE6, 0x42, 0xCC, 0x81, 0xE6, 0xE6, 0x42, 0xCC, + 0x93, 0xE6, 0xE6, 0x44, 0xCC, 0x88, 0xCC, 0x81, + 0xE6, 0xE6, 0x43, 0xE3, 0x82, 0x99, 0x08, 0x08, + 0x43, 0xE3, 0x82, 0x9A, 0x08, 0x08, 0x46, 0xE0, + 0xBD, 0xB1, 0xE0, 0xBD, 0xB2, 0x82, 0x81, 0x46, + // Bytes 49c0 - 49ff + 0xE0, 0xBD, 0xB1, 0xE0, 0xBD, 0xB4, 0x84, 0x81, + 0x46, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0x82, + 0x81, +} + +// nfcValues: 2944 entries, 5888 bytes +// Block 2 is the null block. +var nfcValues = [2944]uint16{ + // Block 0x0, offset 0x0 + 0x003c: 0x8800, 0x003d: 0x8800, 0x003e: 0x8800, + // Block 0x1, offset 0x40 + 0x0041: 0x8800, 0x0042: 0x8800, 0x0043: 0x8800, 0x0044: 0x8800, 0x0045: 0x8800, + 0x0046: 0x8800, 0x0047: 0x8800, 0x0048: 0x8800, 0x0049: 0x8800, 0x004a: 0x8800, 0x004b: 0x8800, + 0x004c: 0x8800, 0x004d: 0x8800, 0x004e: 0x8800, 0x004f: 0x8800, 0x0050: 0x8800, + 0x0052: 0x8800, 0x0053: 0x8800, 0x0054: 0x8800, 0x0055: 0x8800, 0x0056: 0x8800, 0x0057: 0x8800, + 0x0058: 0x8800, 0x0059: 0x8800, 0x005a: 0x8800, + 0x0061: 0x8800, 0x0062: 0x8800, 0x0063: 0x8800, + 0x0064: 0x8800, 0x0065: 0x8800, 0x0066: 0x8800, 0x0067: 0x8800, 0x0068: 0x8800, 0x0069: 0x8800, + 0x006a: 0x8800, 0x006b: 0x8800, 0x006c: 0x8800, 0x006d: 0x8800, 0x006e: 0x8800, 0x006f: 0x8800, + 0x0070: 0x8800, 0x0072: 0x8800, 0x0073: 0x8800, 0x0074: 0x8800, 0x0075: 0x8800, + 0x0076: 0x8800, 0x0077: 0x8800, 0x0078: 0x8800, 0x0079: 0x8800, 0x007a: 0x8800, + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0x00c0: 0x2f59, 0x00c1: 0x2f5e, 0x00c2: 0x466e, 0x00c3: 0x2f63, 0x00c4: 0x467d, 0x00c5: 0x4682, + 0x00c6: 0x8800, 0x00c7: 0x468c, 0x00c8: 0x2fcc, 0x00c9: 0x2fd1, 0x00ca: 0x4691, 0x00cb: 0x2fe5, + 0x00cc: 0x3058, 0x00cd: 0x305d, 0x00ce: 0x3062, 0x00cf: 0x46a5, 0x00d1: 0x30ee, + 0x00d2: 0x3111, 0x00d3: 0x3116, 0x00d4: 0x46af, 0x00d5: 0x46b4, 0x00d6: 0x46c3, + 0x00d8: 0x8800, 0x00d9: 0x319d, 0x00da: 0x31a2, 0x00db: 0x31a7, 0x00dc: 0x46f5, 0x00dd: 0x321f, + 0x00e0: 0x3265, 0x00e1: 0x326a, 0x00e2: 0x46ff, 0x00e3: 0x326f, + 0x00e4: 0x470e, 0x00e5: 0x4713, 0x00e6: 0x8800, 0x00e7: 0x471d, 0x00e8: 0x32d8, 0x00e9: 0x32dd, + 0x00ea: 0x4722, 0x00eb: 0x32f1, 0x00ec: 0x3369, 0x00ed: 0x336e, 0x00ee: 0x3373, 0x00ef: 0x4736, + 0x00f1: 0x33ff, 0x00f2: 0x3422, 0x00f3: 0x3427, 0x00f4: 0x4740, 0x00f5: 0x4745, + 0x00f6: 0x4754, 0x00f8: 0x8800, 0x00f9: 0x34b3, 0x00fa: 0x34b8, 0x00fb: 0x34bd, + 0x00fc: 0x4786, 0x00fd: 0x353a, 0x00ff: 0x3553, + // Block 0x4, offset 0x100 + 0x0100: 0x2f68, 0x0101: 0x3274, 0x0102: 0x4673, 0x0103: 0x4704, 0x0104: 0x2f86, 0x0105: 0x3292, + 0x0106: 0x2f9a, 0x0107: 0x32a6, 0x0108: 0x2f9f, 0x0109: 0x32ab, 0x010a: 0x2fa4, 0x010b: 0x32b0, + 0x010c: 0x2fa9, 0x010d: 0x32b5, 0x010e: 0x2fb3, 0x010f: 0x32bf, + 0x0112: 0x4696, 0x0113: 0x4727, 0x0114: 0x2fdb, 0x0115: 0x32e7, 0x0116: 0x2fe0, 0x0117: 0x32ec, + 0x0118: 0x2ffe, 0x0119: 0x330a, 0x011a: 0x2fef, 0x011b: 0x32fb, 0x011c: 0x3017, 0x011d: 0x3323, + 0x011e: 0x3021, 0x011f: 0x332d, 0x0120: 0x3026, 0x0121: 0x3332, 0x0122: 0x3030, 0x0123: 0x333c, + 0x0124: 0x3035, 0x0125: 0x3341, 0x0128: 0x3067, 0x0129: 0x3378, + 0x012a: 0x306c, 0x012b: 0x337d, 0x012c: 0x3071, 0x012d: 0x3382, 0x012e: 0x3094, 0x012f: 0x33a0, + 0x0130: 0x3076, 0x0134: 0x309e, 0x0135: 0x33aa, + 0x0136: 0x30b2, 0x0137: 0x33c3, 0x0139: 0x30bc, 0x013a: 0x33cd, 0x013b: 0x30c6, + 0x013c: 0x33d7, 0x013d: 0x30c1, 0x013e: 0x33d2, + // Block 0x5, offset 0x140 + 0x0143: 0x30e9, 0x0144: 0x33fa, 0x0145: 0x3102, + 0x0146: 0x3413, 0x0147: 0x30f8, 0x0148: 0x3409, + 0x014c: 0x46b9, 0x014d: 0x474a, 0x014e: 0x311b, 0x014f: 0x342c, 0x0150: 0x3125, 0x0151: 0x3436, + 0x0154: 0x3143, 0x0155: 0x3454, 0x0156: 0x315c, 0x0157: 0x346d, + 0x0158: 0x314d, 0x0159: 0x345e, 0x015a: 0x46dc, 0x015b: 0x476d, 0x015c: 0x3166, 0x015d: 0x3477, + 0x015e: 0x3175, 0x015f: 0x3486, 0x0160: 0x46e1, 0x0161: 0x4772, 0x0162: 0x318e, 0x0163: 0x34a4, + 0x0164: 0x317f, 0x0165: 0x3495, 0x0168: 0x46eb, 0x0169: 0x477c, + 0x016a: 0x46f0, 0x016b: 0x4781, 0x016c: 0x31ac, 0x016d: 0x34c2, 0x016e: 0x31b6, 0x016f: 0x34cc, + 0x0170: 0x31bb, 0x0171: 0x34d1, 0x0172: 0x31d9, 0x0173: 0x34ef, 0x0174: 0x31fc, 0x0175: 0x3512, + 0x0176: 0x3224, 0x0177: 0x353f, 0x0178: 0x3238, 0x0179: 0x3247, 0x017a: 0x3567, 0x017b: 0x3251, + 0x017c: 0x3571, 0x017d: 0x3256, 0x017e: 0x3576, 0x017f: 0x8800, + // Block 0x6, offset 0x180 + 0x018d: 0x2f72, 0x018e: 0x327e, 0x018f: 0x3080, 0x0190: 0x338c, 0x0191: 0x312a, + 0x0192: 0x343b, 0x0193: 0x31c0, 0x0194: 0x34d6, 0x0195: 0x39b9, 0x0196: 0x3b48, 0x0197: 0x39b2, + 0x0198: 0x3b41, 0x0199: 0x39c0, 0x019a: 0x3b4f, 0x019b: 0x39ab, 0x019c: 0x3b3a, + 0x019e: 0x389a, 0x019f: 0x3a29, 0x01a0: 0x3893, 0x01a1: 0x3a22, 0x01a2: 0x359d, 0x01a3: 0x35af, + 0x01a6: 0x302b, 0x01a7: 0x3337, 0x01a8: 0x30a8, 0x01a9: 0x33b9, + 0x01aa: 0x46d2, 0x01ab: 0x4763, 0x01ac: 0x397a, 0x01ad: 0x3b09, 0x01ae: 0x35c1, 0x01af: 0x35c7, + 0x01b0: 0x33af, 0x01b4: 0x3012, 0x01b5: 0x331e, + 0x01b8: 0x30e4, 0x01b9: 0x33f5, 0x01ba: 0x38a1, 0x01bb: 0x3a30, + 0x01bc: 0x3597, 0x01bd: 0x35a9, 0x01be: 0x35a3, 0x01bf: 0x35b5, + // Block 0x7, offset 0x1c0 + 0x01c0: 0x2f77, 0x01c1: 0x3283, 0x01c2: 0x2f7c, 0x01c3: 0x3288, 0x01c4: 0x2ff4, 0x01c5: 0x3300, + 0x01c6: 0x2ff9, 0x01c7: 0x3305, 0x01c8: 0x3085, 0x01c9: 0x3391, 0x01ca: 0x308a, 0x01cb: 0x3396, + 0x01cc: 0x312f, 0x01cd: 0x3440, 0x01ce: 0x3134, 0x01cf: 0x3445, 0x01d0: 0x3152, 0x01d1: 0x3463, + 0x01d2: 0x3157, 0x01d3: 0x3468, 0x01d4: 0x31c5, 0x01d5: 0x34db, 0x01d6: 0x31ca, 0x01d7: 0x34e0, + 0x01d8: 0x3170, 0x01d9: 0x3481, 0x01da: 0x3189, 0x01db: 0x349f, + 0x01de: 0x3044, 0x01df: 0x3350, + 0x01e6: 0x4678, 0x01e7: 0x4709, 0x01e8: 0x46a0, 0x01e9: 0x4731, + 0x01ea: 0x3949, 0x01eb: 0x3ad8, 0x01ec: 0x3926, 0x01ed: 0x3ab5, 0x01ee: 0x46be, 0x01ef: 0x474f, + 0x01f0: 0x3942, 0x01f1: 0x3ad1, 0x01f2: 0x322e, 0x01f3: 0x3549, + // Block 0x8, offset 0x200 + 0x0200: 0x86e6, 0x0201: 0x86e6, 0x0202: 0x86e6, 0x0203: 0x86e6, 0x0204: 0x86e6, 0x0205: 0x80e6, + 0x0206: 0x86e6, 0x0207: 0x86e6, 0x0208: 0x86e6, 0x0209: 0x86e6, 0x020a: 0x86e6, 0x020b: 0x86e6, + 0x020c: 0x86e6, 0x020d: 0x80e6, 0x020e: 0x80e6, 0x020f: 0x86e6, 0x0210: 0x80e6, 0x0211: 0x86e6, + 0x0212: 0x80e6, 0x0213: 0x86e6, 0x0214: 0x86e6, 0x0215: 0x80e8, 0x0216: 0x80dc, 0x0217: 0x80dc, + 0x0218: 0x80dc, 0x0219: 0x80dc, 0x021a: 0x80e8, 0x021b: 0x86d8, 0x021c: 0x80dc, 0x021d: 0x80dc, + 0x021e: 0x80dc, 0x021f: 0x80dc, 0x0220: 0x80dc, 0x0221: 0x80ca, 0x0222: 0x80ca, 0x0223: 0x86dc, + 0x0224: 0x86dc, 0x0225: 0x86dc, 0x0226: 0x86dc, 0x0227: 0x86ca, 0x0228: 0x86ca, 0x0229: 0x80dc, + 0x022a: 0x80dc, 0x022b: 0x80dc, 0x022c: 0x80dc, 0x022d: 0x86dc, 0x022e: 0x86dc, 0x022f: 0x80dc, + 0x0230: 0x86dc, 0x0231: 0x86dc, 0x0232: 0x80dc, 0x0233: 0x80dc, 0x0234: 0x8001, 0x0235: 0x8001, + 0x0236: 0x8001, 0x0237: 0x8001, 0x0238: 0x8601, 0x0239: 0x80dc, 0x023a: 0x80dc, 0x023b: 0x80dc, + 0x023c: 0x80dc, 0x023d: 0x80e6, 0x023e: 0x80e6, 0x023f: 0x80e6, + // Block 0x9, offset 0x240 + 0x0240: 0x4994, 0x0241: 0x4999, 0x0242: 0x86e6, 0x0243: 0x499e, 0x0244: 0x49a3, 0x0245: 0x86f0, + 0x0246: 0x80e6, 0x0247: 0x80dc, 0x0248: 0x80dc, 0x0249: 0x80dc, 0x024a: 0x80e6, 0x024b: 0x80e6, + 0x024c: 0x80e6, 0x024d: 0x80dc, 0x024e: 0x80dc, 0x0250: 0x80e6, 0x0251: 0x80e6, + 0x0252: 0x80e6, 0x0253: 0x80dc, 0x0254: 0x80dc, 0x0255: 0x80dc, 0x0256: 0x80dc, 0x0257: 0x80e6, + 0x0258: 0x80e8, 0x0259: 0x80dc, 0x025a: 0x80dc, 0x025b: 0x80e6, 0x025c: 0x80e9, 0x025d: 0x80ea, + 0x025e: 0x80ea, 0x025f: 0x80e9, 0x0260: 0x80ea, 0x0261: 0x80ea, 0x0262: 0x80e9, 0x0263: 0x80e6, + 0x0264: 0x80e6, 0x0265: 0x80e6, 0x0266: 0x80e6, 0x0267: 0x80e6, 0x0268: 0x80e6, 0x0269: 0x80e6, + 0x026a: 0x80e6, 0x026b: 0x80e6, 0x026c: 0x80e6, 0x026d: 0x80e6, 0x026e: 0x80e6, 0x026f: 0x80e6, + 0x0274: 0x0170, + 0x027e: 0x0037, + // Block 0xa, offset 0x280 + 0x0285: 0x358b, + 0x0286: 0x35d3, 0x0287: 0x00d1, 0x0288: 0x35f1, 0x0289: 0x35fd, 0x028a: 0x360f, + 0x028c: 0x362d, 0x028e: 0x363f, 0x028f: 0x365d, 0x0290: 0x3df2, 0x0291: 0x8800, + 0x0295: 0x8800, 0x0297: 0x8800, + 0x0299: 0x8800, + 0x029f: 0x8800, 0x02a1: 0x8800, + 0x02a5: 0x8800, 0x02a9: 0x8800, + 0x02aa: 0x3621, 0x02ab: 0x3651, 0x02ac: 0x47e4, 0x02ad: 0x3681, 0x02ae: 0x480e, 0x02af: 0x3693, + 0x02b0: 0x3e5a, 0x02b1: 0x8800, 0x02b5: 0x8800, + 0x02b7: 0x8800, 0x02b9: 0x8800, + 0x02bf: 0x8800, + // Block 0xb, offset 0x2c0 + 0x02c0: 0x370b, 0x02c1: 0x3717, 0x02c3: 0x3705, + 0x02c6: 0x8800, 0x02c7: 0x36f3, + 0x02cc: 0x3747, 0x02cd: 0x372f, 0x02ce: 0x3759, 0x02d0: 0x8800, + 0x02d3: 0x8800, 0x02d5: 0x8800, 0x02d6: 0x8800, 0x02d7: 0x8800, + 0x02d8: 0x8800, 0x02d9: 0x373b, 0x02da: 0x8800, + 0x02de: 0x8800, 0x02e3: 0x8800, + 0x02e7: 0x8800, + 0x02eb: 0x8800, 0x02ed: 0x8800, + 0x02f0: 0x8800, 0x02f3: 0x8800, 0x02f5: 0x8800, + 0x02f6: 0x8800, 0x02f7: 0x8800, 0x02f8: 0x8800, 0x02f9: 0x37bf, 0x02fa: 0x8800, + 0x02fe: 0x8800, + // Block 0xc, offset 0x300 + 0x0301: 0x371d, 0x0302: 0x37a1, + 0x0310: 0x36f9, 0x0311: 0x377d, + 0x0312: 0x36ff, 0x0313: 0x3783, 0x0316: 0x3711, 0x0317: 0x3795, + 0x0318: 0x8800, 0x0319: 0x8800, 0x031a: 0x3813, 0x031b: 0x3819, 0x031c: 0x3723, 0x031d: 0x37a7, + 0x031e: 0x3729, 0x031f: 0x37ad, 0x0322: 0x3735, 0x0323: 0x37b9, + 0x0324: 0x3741, 0x0325: 0x37c5, 0x0326: 0x374d, 0x0327: 0x37d1, 0x0328: 0x8800, 0x0329: 0x8800, + 0x032a: 0x381f, 0x032b: 0x3825, 0x032c: 0x3777, 0x032d: 0x37fb, 0x032e: 0x3753, 0x032f: 0x37d7, + 0x0330: 0x375f, 0x0331: 0x37e3, 0x0332: 0x3765, 0x0333: 0x37e9, 0x0334: 0x376b, 0x0335: 0x37ef, + 0x0338: 0x3771, 0x0339: 0x37f5, + // Block 0xd, offset 0x340 + 0x0351: 0x80dc, + 0x0352: 0x80e6, 0x0353: 0x80e6, 0x0354: 0x80e6, 0x0355: 0x80e6, 0x0356: 0x80dc, 0x0357: 0x80e6, + 0x0358: 0x80e6, 0x0359: 0x80e6, 0x035a: 0x80de, 0x035b: 0x80dc, 0x035c: 0x80e6, 0x035d: 0x80e6, + 0x035e: 0x80e6, 0x035f: 0x80e6, 0x0360: 0x80e6, 0x0361: 0x80e6, 0x0362: 0x80dc, 0x0363: 0x80dc, + 0x0364: 0x80dc, 0x0365: 0x80dc, 0x0366: 0x80dc, 0x0367: 0x80dc, 0x0368: 0x80e6, 0x0369: 0x80e6, + 0x036a: 0x80dc, 0x036b: 0x80e6, 0x036c: 0x80e6, 0x036d: 0x80de, 0x036e: 0x80e4, 0x036f: 0x80e6, + 0x0370: 0x800a, 0x0371: 0x800b, 0x0372: 0x800c, 0x0373: 0x800d, 0x0374: 0x800e, 0x0375: 0x800f, + 0x0376: 0x8010, 0x0377: 0x8011, 0x0378: 0x8012, 0x0379: 0x8013, 0x037a: 0x8013, 0x037b: 0x8014, + 0x037c: 0x8015, 0x037d: 0x8016, 0x037f: 0x8017, + // Block 0xe, offset 0x380 + 0x0388: 0x8800, 0x038a: 0x8800, 0x038b: 0x801b, + 0x038c: 0x801c, 0x038d: 0x801d, 0x038e: 0x801e, 0x038f: 0x801f, 0x0390: 0x8020, 0x0391: 0x8021, + 0x0392: 0x8022, 0x0393: 0x86e6, 0x0394: 0x86e6, 0x0395: 0x86dc, 0x0396: 0x80dc, 0x0397: 0x80e6, + 0x0398: 0x80e6, 0x0399: 0x80e6, 0x039a: 0x80e6, 0x039b: 0x80e6, 0x039c: 0x80dc, 0x039d: 0x80e6, + 0x039e: 0x80e6, 0x039f: 0x80dc, + 0x03b0: 0x8023, + // Block 0xf, offset 0x3c0 + 0x03c5: 0x8800, + 0x03c6: 0x1946, 0x03c7: 0x8800, 0x03c8: 0x194d, 0x03c9: 0x8800, 0x03ca: 0x1954, 0x03cb: 0x8800, + 0x03cc: 0x195b, 0x03cd: 0x8800, 0x03ce: 0x1962, 0x03d1: 0x8800, + 0x03d2: 0x1969, + 0x03f4: 0x8007, 0x03f5: 0x8600, + 0x03fa: 0x8800, 0x03fb: 0x1970, + 0x03fc: 0x8800, 0x03fd: 0x1977, 0x03fe: 0x8800, 0x03ff: 0x8800, + // Block 0x10, offset 0x400 + 0x0400: 0x2f81, 0x0401: 0x328d, 0x0402: 0x2f8b, 0x0403: 0x3297, 0x0404: 0x2f90, 0x0405: 0x329c, + 0x0406: 0x2f95, 0x0407: 0x32a1, 0x0408: 0x38b6, 0x0409: 0x3a45, 0x040a: 0x2fae, 0x040b: 0x32ba, + 0x040c: 0x2fb8, 0x040d: 0x32c4, 0x040e: 0x2fc7, 0x040f: 0x32d3, 0x0410: 0x2fbd, 0x0411: 0x32c9, + 0x0412: 0x2fc2, 0x0413: 0x32ce, 0x0414: 0x38d9, 0x0415: 0x3a68, 0x0416: 0x38e0, 0x0417: 0x3a6f, + 0x0418: 0x3003, 0x0419: 0x330f, 0x041a: 0x3008, 0x041b: 0x3314, 0x041c: 0x38ee, 0x041d: 0x3a7d, + 0x041e: 0x300d, 0x041f: 0x3319, 0x0420: 0x301c, 0x0421: 0x3328, 0x0422: 0x303a, 0x0423: 0x3346, + 0x0424: 0x3049, 0x0425: 0x3355, 0x0426: 0x303f, 0x0427: 0x334b, 0x0428: 0x304e, 0x0429: 0x335a, + 0x042a: 0x3053, 0x042b: 0x335f, 0x042c: 0x3099, 0x042d: 0x33a5, 0x042e: 0x38f5, 0x042f: 0x3a84, + 0x0430: 0x30a3, 0x0431: 0x33b4, 0x0432: 0x30ad, 0x0433: 0x33be, 0x0434: 0x30b7, 0x0435: 0x33c8, + 0x0436: 0x46aa, 0x0437: 0x473b, 0x0438: 0x38fc, 0x0439: 0x3a8b, 0x043a: 0x30d0, 0x043b: 0x33e1, + 0x043c: 0x30cb, 0x043d: 0x33dc, 0x043e: 0x30d5, 0x043f: 0x33e6, + // Block 0x11, offset 0x440 + 0x0440: 0x30da, 0x0441: 0x33eb, 0x0442: 0x30df, 0x0443: 0x33f0, 0x0444: 0x30f3, 0x0445: 0x3404, + 0x0446: 0x30fd, 0x0447: 0x340e, 0x0448: 0x310c, 0x0449: 0x341d, 0x044a: 0x3107, 0x044b: 0x3418, + 0x044c: 0x391f, 0x044d: 0x3aae, 0x044e: 0x392d, 0x044f: 0x3abc, 0x0450: 0x3934, 0x0451: 0x3ac3, + 0x0452: 0x393b, 0x0453: 0x3aca, 0x0454: 0x3139, 0x0455: 0x344a, 0x0456: 0x313e, 0x0457: 0x344f, + 0x0458: 0x3148, 0x0459: 0x3459, 0x045a: 0x46d7, 0x045b: 0x4768, 0x045c: 0x3981, 0x045d: 0x3b10, + 0x045e: 0x3161, 0x045f: 0x3472, 0x0460: 0x316b, 0x0461: 0x347c, 0x0462: 0x46e6, 0x0463: 0x4777, + 0x0464: 0x3988, 0x0465: 0x3b17, 0x0466: 0x398f, 0x0467: 0x3b1e, 0x0468: 0x3996, 0x0469: 0x3b25, + 0x046a: 0x317a, 0x046b: 0x348b, 0x046c: 0x3184, 0x046d: 0x349a, 0x046e: 0x3198, 0x046f: 0x34ae, + 0x0470: 0x3193, 0x0471: 0x34a9, 0x0472: 0x31d4, 0x0473: 0x34ea, 0x0474: 0x31e3, 0x0475: 0x34f9, + 0x0476: 0x31de, 0x0477: 0x34f4, 0x0478: 0x399d, 0x0479: 0x3b2c, 0x047a: 0x39a4, 0x047b: 0x3b33, + 0x047c: 0x31e8, 0x047d: 0x34fe, 0x047e: 0x31ed, 0x047f: 0x3503, + // Block 0x12, offset 0x480 + 0x0480: 0x31f2, 0x0481: 0x3508, 0x0482: 0x31f7, 0x0483: 0x350d, 0x0484: 0x3206, 0x0485: 0x351c, + 0x0486: 0x3201, 0x0487: 0x3517, 0x0488: 0x320b, 0x0489: 0x3526, 0x048a: 0x3210, 0x048b: 0x352b, + 0x048c: 0x3215, 0x048d: 0x3530, 0x048e: 0x3233, 0x048f: 0x354e, 0x0490: 0x324c, 0x0491: 0x356c, + 0x0492: 0x325b, 0x0493: 0x357b, 0x0494: 0x3260, 0x0495: 0x3580, 0x0496: 0x3364, 0x0497: 0x3490, + 0x0498: 0x3521, 0x0499: 0x355d, 0x049b: 0x35bb, + 0x04a0: 0x4687, 0x04a1: 0x4718, 0x04a2: 0x2f6d, 0x04a3: 0x3279, + 0x04a4: 0x3862, 0x04a5: 0x39f1, 0x04a6: 0x385b, 0x04a7: 0x39ea, 0x04a8: 0x3870, 0x04a9: 0x39ff, + 0x04aa: 0x3869, 0x04ab: 0x39f8, 0x04ac: 0x38a8, 0x04ad: 0x3a37, 0x04ae: 0x387e, 0x04af: 0x3a0d, + 0x04b0: 0x3877, 0x04b1: 0x3a06, 0x04b2: 0x388c, 0x04b3: 0x3a1b, 0x04b4: 0x3885, 0x04b5: 0x3a14, + 0x04b6: 0x38af, 0x04b7: 0x3a3e, 0x04b8: 0x469b, 0x04b9: 0x472c, 0x04ba: 0x2fea, 0x04bb: 0x32f6, + 0x04bc: 0x2fd6, 0x04bd: 0x32e2, 0x04be: 0x38c4, 0x04bf: 0x3a53, + // Block 0x13, offset 0x4c0 + 0x04c0: 0x38bd, 0x04c1: 0x3a4c, 0x04c2: 0x38d2, 0x04c3: 0x3a61, 0x04c4: 0x38cb, 0x04c5: 0x3a5a, + 0x04c6: 0x38e7, 0x04c7: 0x3a76, 0x04c8: 0x307b, 0x04c9: 0x3387, 0x04ca: 0x308f, 0x04cb: 0x339b, + 0x04cc: 0x46cd, 0x04cd: 0x475e, 0x04ce: 0x3120, 0x04cf: 0x3431, 0x04d0: 0x390a, 0x04d1: 0x3a99, + 0x04d2: 0x3903, 0x04d3: 0x3a92, 0x04d4: 0x3918, 0x04d5: 0x3aa7, 0x04d6: 0x3911, 0x04d7: 0x3aa0, + 0x04d8: 0x3973, 0x04d9: 0x3b02, 0x04da: 0x3957, 0x04db: 0x3ae6, 0x04dc: 0x3950, 0x04dd: 0x3adf, + 0x04de: 0x3965, 0x04df: 0x3af4, 0x04e0: 0x395e, 0x04e1: 0x3aed, 0x04e2: 0x396c, 0x04e3: 0x3afb, + 0x04e4: 0x31cf, 0x04e5: 0x34e5, 0x04e6: 0x31b1, 0x04e7: 0x34c7, 0x04e8: 0x39ce, 0x04e9: 0x3b5d, + 0x04ea: 0x39c7, 0x04eb: 0x3b56, 0x04ec: 0x39dc, 0x04ed: 0x3b6b, 0x04ee: 0x39d5, 0x04ef: 0x3b64, + 0x04f0: 0x39e3, 0x04f1: 0x3b72, 0x04f2: 0x321a, 0x04f3: 0x3535, 0x04f4: 0x3242, 0x04f5: 0x3562, + 0x04f6: 0x323d, 0x04f7: 0x3558, 0x04f8: 0x3229, 0x04f9: 0x3544, + // Block 0x14, offset 0x500 + 0x0500: 0x47ea, 0x0501: 0x47f0, 0x0502: 0x4904, 0x0503: 0x491c, 0x0504: 0x490c, 0x0505: 0x4924, + 0x0506: 0x4914, 0x0507: 0x492c, 0x0508: 0x4790, 0x0509: 0x4796, 0x050a: 0x4874, 0x050b: 0x488c, + 0x050c: 0x487c, 0x050d: 0x4894, 0x050e: 0x4884, 0x050f: 0x489c, 0x0510: 0x47fc, 0x0511: 0x4802, + 0x0512: 0x3da2, 0x0513: 0x3db2, 0x0514: 0x3daa, 0x0515: 0x3dba, + 0x0518: 0x479c, 0x0519: 0x47a2, 0x051a: 0x3cd2, 0x051b: 0x3ce2, 0x051c: 0x3cda, 0x051d: 0x3cea, + 0x0520: 0x4814, 0x0521: 0x481a, 0x0522: 0x4934, 0x0523: 0x494c, + 0x0524: 0x493c, 0x0525: 0x4954, 0x0526: 0x4944, 0x0527: 0x495c, 0x0528: 0x47a8, 0x0529: 0x47ae, + 0x052a: 0x48a4, 0x052b: 0x48bc, 0x052c: 0x48ac, 0x052d: 0x48c4, 0x052e: 0x48b4, 0x052f: 0x48cc, + 0x0530: 0x482c, 0x0531: 0x4832, 0x0532: 0x3e02, 0x0533: 0x3e1a, 0x0534: 0x3e0a, 0x0535: 0x3e22, + 0x0536: 0x3e12, 0x0537: 0x3e2a, 0x0538: 0x47b4, 0x0539: 0x47ba, 0x053a: 0x3d02, 0x053b: 0x3d1a, + 0x053c: 0x3d0a, 0x053d: 0x3d22, 0x053e: 0x3d12, 0x053f: 0x3d2a, + // Block 0x15, offset 0x540 + 0x0540: 0x4838, 0x0541: 0x483e, 0x0542: 0x3e32, 0x0543: 0x3e42, 0x0544: 0x3e3a, 0x0545: 0x3e4a, + 0x0548: 0x47c0, 0x0549: 0x47c6, 0x054a: 0x3d32, 0x054b: 0x3d42, + 0x054c: 0x3d3a, 0x054d: 0x3d4a, 0x0550: 0x484a, 0x0551: 0x4850, + 0x0552: 0x3e6a, 0x0553: 0x3e82, 0x0554: 0x3e72, 0x0555: 0x3e8a, 0x0556: 0x3e7a, 0x0557: 0x3e92, + 0x0559: 0x47cc, 0x055b: 0x3d52, 0x055d: 0x3d5a, + 0x055f: 0x3d62, 0x0560: 0x4862, 0x0561: 0x4868, 0x0562: 0x4964, 0x0563: 0x497c, + 0x0564: 0x496c, 0x0565: 0x4984, 0x0566: 0x4974, 0x0567: 0x498c, 0x0568: 0x47d2, 0x0569: 0x47d8, + 0x056a: 0x48d4, 0x056b: 0x48ec, 0x056c: 0x48dc, 0x056d: 0x48f4, 0x056e: 0x48e4, 0x056f: 0x48fc, + 0x0570: 0x47de, 0x0571: 0x4304, 0x0572: 0x367b, 0x0573: 0x430a, 0x0574: 0x4808, 0x0575: 0x4310, + 0x0576: 0x368d, 0x0577: 0x4316, 0x0578: 0x36ab, 0x0579: 0x431c, 0x057a: 0x36c3, 0x057b: 0x4322, + 0x057c: 0x4856, 0x057d: 0x4328, + // Block 0x16, offset 0x580 + 0x0580: 0x3d8a, 0x0581: 0x3d92, 0x0582: 0x416e, 0x0583: 0x418c, 0x0584: 0x4178, 0x0585: 0x4196, + 0x0586: 0x4182, 0x0587: 0x41a0, 0x0588: 0x3cc2, 0x0589: 0x3cca, 0x058a: 0x40ba, 0x058b: 0x40d8, + 0x058c: 0x40c4, 0x058d: 0x40e2, 0x058e: 0x40ce, 0x058f: 0x40ec, 0x0590: 0x3dd2, 0x0591: 0x3dda, + 0x0592: 0x41aa, 0x0593: 0x41c8, 0x0594: 0x41b4, 0x0595: 0x41d2, 0x0596: 0x41be, 0x0597: 0x41dc, + 0x0598: 0x3cf2, 0x0599: 0x3cfa, 0x059a: 0x40f6, 0x059b: 0x4114, 0x059c: 0x4100, 0x059d: 0x411e, + 0x059e: 0x410a, 0x059f: 0x4128, 0x05a0: 0x3eaa, 0x05a1: 0x3eb2, 0x05a2: 0x41e6, 0x05a3: 0x4204, + 0x05a4: 0x41f0, 0x05a5: 0x420e, 0x05a6: 0x41fa, 0x05a7: 0x4218, 0x05a8: 0x3d6a, 0x05a9: 0x3d72, + 0x05aa: 0x4132, 0x05ab: 0x4150, 0x05ac: 0x413c, 0x05ad: 0x415a, 0x05ae: 0x4146, 0x05af: 0x4164, + 0x05b0: 0x366f, 0x05b1: 0x3669, 0x05b2: 0x3d7a, 0x05b3: 0x3675, 0x05b4: 0x3d82, + 0x05b6: 0x47f6, 0x05b7: 0x3d9a, 0x05b8: 0x35df, 0x05b9: 0x35d9, 0x05ba: 0x35cd, 0x05bb: 0x42d4, + 0x05bc: 0x35e5, 0x05be: 0x01d3, 0x05bf: 0x8800, + // Block 0x17, offset 0x5c0 + 0x05c1: 0x3591, 0x05c2: 0x3dc2, 0x05c3: 0x3687, 0x05c4: 0x3dca, + 0x05c6: 0x4820, 0x05c7: 0x3de2, 0x05c8: 0x35eb, 0x05c9: 0x42da, 0x05ca: 0x35f7, 0x05cb: 0x42e0, + 0x05cc: 0x3603, 0x05cd: 0x3b79, 0x05ce: 0x3b80, 0x05cf: 0x3b87, 0x05d0: 0x369f, 0x05d1: 0x3699, + 0x05d2: 0x3dea, 0x05d3: 0x44ca, 0x05d6: 0x36a5, 0x05d7: 0x3dfa, + 0x05d8: 0x361b, 0x05d9: 0x3615, 0x05da: 0x3609, 0x05db: 0x42e6, 0x05dd: 0x3b8e, + 0x05de: 0x3b95, 0x05df: 0x3b9c, 0x05e0: 0x36d5, 0x05e1: 0x36cf, 0x05e2: 0x3e52, 0x05e3: 0x44d2, + 0x05e4: 0x36b7, 0x05e5: 0x36bd, 0x05e6: 0x36db, 0x05e7: 0x3e62, 0x05e8: 0x364b, 0x05e9: 0x3645, + 0x05ea: 0x3639, 0x05eb: 0x42f2, 0x05ec: 0x3633, 0x05ed: 0x3585, 0x05ee: 0x42ce, 0x05ef: 0x0081, + 0x05f2: 0x3e9a, 0x05f3: 0x36e1, 0x05f4: 0x3ea2, + 0x05f6: 0x486e, 0x05f7: 0x3eba, 0x05f8: 0x3627, 0x05f9: 0x42ec, 0x05fa: 0x3657, 0x05fb: 0x42fe, + 0x05fc: 0x3663, 0x05fd: 0x00ce, 0x05fe: 0x8800, + // Block 0x18, offset 0x600 + 0x0601: 0x3bf0, 0x0603: 0x8800, 0x0604: 0x3bf7, 0x0605: 0x8800, + 0x0607: 0x3bfe, 0x0608: 0x8800, 0x0609: 0x3c05, + 0x060d: 0x8800, + 0x0620: 0x2f4f, 0x0621: 0x8800, 0x0622: 0x3c13, + 0x0624: 0x8800, 0x0625: 0x8800, + 0x062d: 0x3c0c, 0x062e: 0x2f4a, 0x062f: 0x2f54, + 0x0630: 0x3c1a, 0x0631: 0x3c21, 0x0632: 0x8800, 0x0633: 0x8800, 0x0634: 0x3c28, 0x0635: 0x3c2f, + 0x0636: 0x8800, 0x0637: 0x8800, 0x0638: 0x3c36, 0x0639: 0x3c3d, 0x063a: 0x8800, 0x063b: 0x8800, + 0x063c: 0x8800, 0x063d: 0x8800, + // Block 0x19, offset 0x640 + 0x0640: 0x3c44, 0x0641: 0x3c4b, 0x0642: 0x8800, 0x0643: 0x8800, 0x0644: 0x3c60, 0x0645: 0x3c67, + 0x0646: 0x8800, 0x0647: 0x8800, 0x0648: 0x3c6e, 0x0649: 0x3c75, + 0x0651: 0x8800, + 0x0652: 0x8800, + 0x0662: 0x8800, + 0x0668: 0x8800, 0x0669: 0x8800, + 0x066b: 0x8800, 0x066c: 0x3c8a, 0x066d: 0x3c91, 0x066e: 0x3c98, 0x066f: 0x3c9f, + 0x0672: 0x8800, 0x0673: 0x8800, 0x0674: 0x8800, 0x0675: 0x8800, + // Block 0x1a, offset 0x680 + 0x0686: 0x8800, 0x068b: 0x8800, + 0x068c: 0x3ef2, 0x068d: 0x8800, 0x068e: 0x3efa, 0x068f: 0x8800, 0x0690: 0x3f02, 0x0691: 0x8800, + 0x0692: 0x3f0a, 0x0693: 0x8800, 0x0694: 0x3f12, 0x0695: 0x8800, 0x0696: 0x3f1a, 0x0697: 0x8800, + 0x0698: 0x3f22, 0x0699: 0x8800, 0x069a: 0x3f2a, 0x069b: 0x8800, 0x069c: 0x3f32, 0x069d: 0x8800, + 0x069e: 0x3f3a, 0x069f: 0x8800, 0x06a0: 0x3f42, 0x06a1: 0x8800, 0x06a2: 0x3f4a, + 0x06a4: 0x8800, 0x06a5: 0x3f52, 0x06a6: 0x8800, 0x06a7: 0x3f5a, 0x06a8: 0x8800, 0x06a9: 0x3f62, + 0x06af: 0x8800, + 0x06b0: 0x3f6a, 0x06b1: 0x3f72, 0x06b2: 0x8800, 0x06b3: 0x3f7a, 0x06b4: 0x3f82, 0x06b5: 0x8800, + 0x06b6: 0x3f8a, 0x06b7: 0x3f92, 0x06b8: 0x8800, 0x06b9: 0x3f9a, 0x06ba: 0x3fa2, 0x06bb: 0x8800, + 0x06bc: 0x3faa, 0x06bd: 0x3fb2, + // Block 0x1b, offset 0x6c0 + 0x06d4: 0x3eea, + 0x06d9: 0x8608, 0x06da: 0x8608, 0x06dd: 0x8800, + 0x06de: 0x3fba, + 0x06e6: 0x8800, + 0x06eb: 0x8800, 0x06ec: 0x3fca, 0x06ed: 0x8800, 0x06ee: 0x3fd2, 0x06ef: 0x8800, + 0x06f0: 0x3fda, 0x06f1: 0x8800, 0x06f2: 0x3fe2, 0x06f3: 0x8800, 0x06f4: 0x3fea, 0x06f5: 0x8800, + 0x06f6: 0x3ff2, 0x06f7: 0x8800, 0x06f8: 0x3ffa, 0x06f9: 0x8800, 0x06fa: 0x4002, 0x06fb: 0x8800, + 0x06fc: 0x400a, 0x06fd: 0x8800, 0x06fe: 0x4012, 0x06ff: 0x8800, + // Block 0x1c, offset 0x700 + 0x0700: 0x401a, 0x0701: 0x8800, 0x0702: 0x4022, 0x0704: 0x8800, 0x0705: 0x402a, + 0x0706: 0x8800, 0x0707: 0x4032, 0x0708: 0x8800, 0x0709: 0x403a, + 0x070f: 0x8800, 0x0710: 0x4042, 0x0711: 0x404a, + 0x0712: 0x8800, 0x0713: 0x4052, 0x0714: 0x405a, 0x0715: 0x8800, 0x0716: 0x4062, 0x0717: 0x406a, + 0x0718: 0x8800, 0x0719: 0x4072, 0x071a: 0x407a, 0x071b: 0x8800, 0x071c: 0x4082, 0x071d: 0x408a, + 0x072f: 0x8800, + 0x0730: 0x8800, 0x0731: 0x8800, 0x0732: 0x8800, 0x0734: 0x3fc2, + 0x0737: 0x4092, 0x0738: 0x409a, 0x0739: 0x40a2, 0x073a: 0x40aa, + 0x073d: 0x8800, 0x073e: 0x40b2, + // Block 0x1d, offset 0x740 + 0x0740: 0x13e9, 0x0741: 0x0d6d, 0x0742: 0x1445, 0x0743: 0x1411, 0x0744: 0x0ec9, 0x0745: 0x075d, + 0x0746: 0x0951, 0x0747: 0x1699, 0x0748: 0x1699, 0x0749: 0x0a7d, 0x074a: 0x14cd, 0x074b: 0x09b5, + 0x074c: 0x0a79, 0x074d: 0x0c61, 0x074e: 0x1041, 0x074f: 0x11d1, 0x0750: 0x1309, 0x0751: 0x1345, + 0x0752: 0x1379, 0x0753: 0x148d, 0x0754: 0x0de5, 0x0755: 0x0e71, 0x0756: 0x0f1d, 0x0757: 0x0fb5, + 0x0758: 0x12d1, 0x0759: 0x14b5, 0x075a: 0x15e1, 0x075b: 0x0781, 0x075c: 0x0925, 0x075d: 0x0df9, + 0x075e: 0x0f41, 0x075f: 0x1305, 0x0760: 0x1631, 0x0761: 0x0b25, 0x0762: 0x0ee9, 0x0763: 0x12f5, + 0x0764: 0x1389, 0x0765: 0x0c95, 0x0766: 0x122d, 0x0767: 0x1351, 0x0768: 0x0b91, 0x0769: 0x0d81, + 0x076a: 0x0e89, 0x076b: 0x0f8d, 0x076c: 0x1499, 0x076d: 0x07c1, 0x076e: 0x0859, 0x076f: 0x08c5, + 0x0770: 0x0cfd, 0x0771: 0x0df1, 0x0772: 0x0f3d, 0x0773: 0x1061, 0x0774: 0x11e9, 0x0775: 0x12fd, + 0x0776: 0x1315, 0x0777: 0x1439, 0x0778: 0x155d, 0x0779: 0x1611, 0x077a: 0x162d, 0x077b: 0x109d, + 0x077c: 0x10dd, 0x077d: 0x1195, 0x077e: 0x12b5, 0x077f: 0x14e9, + // Block 0x1e, offset 0x780 + 0x0780: 0x1639, 0x0781: 0x13bd, 0x0782: 0x0a39, 0x0783: 0x0bad, 0x0784: 0x114d, 0x0785: 0x120d, + 0x0786: 0x0f71, 0x0787: 0x10a5, 0x0788: 0x1409, 0x0789: 0x1555, 0x078a: 0x0a35, 0x078b: 0x0b01, + 0x078c: 0x0de9, 0x078d: 0x0e9d, 0x078e: 0x0ed1, 0x078f: 0x1185, 0x0790: 0x11ad, 0x0791: 0x1515, + 0x0792: 0x08c1, 0x0793: 0x1219, 0x0794: 0x0865, 0x0795: 0x0861, 0x0796: 0x1109, 0x0797: 0x1199, + 0x0798: 0x12cd, 0x0799: 0x151d, 0x079a: 0x13d9, 0x079b: 0x0c99, 0x079c: 0x0de5, 0x079d: 0x13c9, + 0x079e: 0x0769, 0x079f: 0x0ad5, 0x07a0: 0x0c05, 0x07a1: 0x0fa1, 0x07a2: 0x1021, 0x07a3: 0x08e5, + 0x07a4: 0x10ad, 0x07a5: 0x07d1, 0x07a6: 0x0be9, 0x07a7: 0x0749, 0x07a8: 0x0e5d, 0x07a9: 0x0d15, + 0x07aa: 0x1181, 0x07ab: 0x0939, 0x07ac: 0x0a25, 0x07ad: 0x106d, 0x07ae: 0x12d5, 0x07af: 0x13ad, + 0x07b0: 0x0e29, 0x07b1: 0x1469, 0x07b2: 0x0e55, 0x07b3: 0x0ca9, 0x07b4: 0x128d, 0x07b5: 0x0cc9, + 0x07b6: 0x101d, 0x07b7: 0x079d, 0x07b8: 0x0819, 0x07b9: 0x085d, 0x07ba: 0x0dc5, 0x07bb: 0x116d, + 0x07bc: 0x1265, 0x07bd: 0x13b9, 0x07be: 0x14c9, 0x07bf: 0x08cd, + // Block 0x1f, offset 0x7c0 + 0x07c0: 0x0981, 0x07c1: 0x0a89, 0x07c2: 0x0ba1, 0x07c3: 0x0d31, 0x07c4: 0x0eed, 0x07c5: 0x10b1, + 0x07c6: 0x1505, 0x07c7: 0x15e9, 0x07c8: 0x163d, 0x07c9: 0x1655, 0x07ca: 0x08a9, 0x07cb: 0x0d65, + 0x07cc: 0x0e15, 0x07cd: 0x145d, 0x07ce: 0x0b6d, 0x07cf: 0x0c49, 0x07d0: 0x0c65, 0x07d1: 0x0cf5, + 0x07d2: 0x0edd, 0x07d3: 0x0f29, 0x07d4: 0x0fd9, 0x07d5: 0x10fd, 0x07d6: 0x11a1, 0x07d7: 0x1205, + 0x07d8: 0x144d, 0x07d9: 0x12dd, 0x07da: 0x1475, 0x07db: 0x14ed, 0x07dc: 0x0881, 0x07dd: 0x08ad, + 0x07de: 0x0995, 0x07df: 0x0f19, 0x07e0: 0x1365, 0x07e1: 0x13ad, 0x07e2: 0x0b8d, 0x07e3: 0x0bfd, + 0x07e4: 0x0cc1, 0x07e5: 0x0e21, 0x07e6: 0x1149, 0x07e7: 0x0f95, 0x07e8: 0x07ad, 0x07e9: 0x09f1, + 0x07ea: 0x0ad5, 0x07eb: 0x0b39, 0x07ec: 0x0c09, 0x07ed: 0x0fb1, 0x07ee: 0x0fcd, 0x07ef: 0x11dd, + 0x07f0: 0x11fd, 0x07f1: 0x14d1, 0x07f2: 0x1551, 0x07f3: 0x1561, 0x07f4: 0x159d, 0x07f5: 0x07c5, + 0x07f6: 0x10f1, 0x07f7: 0x14bd, 0x07f8: 0x1539, 0x07f9: 0x0c21, 0x07fa: 0x0789, 0x07fb: 0x07e9, + 0x07fc: 0x0ad9, 0x07fd: 0x0af9, 0x07fe: 0x0d21, 0x07ff: 0x0de5, + // Block 0x20, offset 0x800 + 0x0800: 0x0f35, 0x0801: 0x103d, 0x0802: 0x12e9, 0x0803: 0x1489, 0x0804: 0x1691, 0x0805: 0x0d55, + 0x0806: 0x1511, 0x0807: 0x08a5, 0x0808: 0x0da1, 0x0809: 0x0dad, 0x080a: 0x0e81, 0x080b: 0x0eb9, + 0x080c: 0x0fbd, 0x080d: 0x1019, 0x080e: 0x1099, 0x080f: 0x117d, 0x0810: 0x15a9, 0x0811: 0x0821, + 0x0812: 0x0c75, 0x0813: 0x1521, 0x0814: 0x07d9, 0x0815: 0x0b1d, 0x0816: 0x0ea1, 0x0817: 0x1451, + 0x0818: 0x0bd9, 0x0819: 0x0c29, 0x081a: 0x0db5, 0x081b: 0x0fa1, 0x081c: 0x1529, 0x081d: 0x0889, + 0x081e: 0x0971, 0x081f: 0x0b09, 0x0820: 0x0d45, 0x0821: 0x0d91, 0x0822: 0x0dd1, 0x0823: 0x0e65, + 0x0824: 0x0fb9, 0x0825: 0x102d, 0x0826: 0x11c9, 0x0827: 0x1369, 0x0828: 0x1375, 0x0829: 0x14c5, + 0x082a: 0x1545, 0x082b: 0x08f5, 0x082c: 0x0ebd, 0x082d: 0x0975, 0x082e: 0x0f39, 0x082f: 0x0fdd, + 0x0830: 0x12f9, 0x0831: 0x152d, 0x0832: 0x1619, 0x0833: 0x1641, 0x0834: 0x0da9, 0x0835: 0x0e99, + 0x0836: 0x1235, 0x0837: 0x1129, 0x0838: 0x1135, 0x0839: 0x1159, 0x083a: 0x0f89, 0x083b: 0x0f11, + 0x083c: 0x13d5, 0x083d: 0x07a5, 0x083e: 0x129d, 0x083f: 0x088d, + // Block 0x21, offset 0x840 + 0x0840: 0x087d, 0x0841: 0x0b7d, 0x0842: 0x0c9d, 0x0843: 0x1165, 0x0844: 0x0ac5, 0x0845: 0x0e75, + 0x0846: 0x0d61, 0x0847: 0x1459, 0x0848: 0x1359, 0x0849: 0x1519, 0x084a: 0x1395, 0x084b: 0x0b99, + 0x084c: 0x07f9, 0x084d: 0x09cd, 0x0850: 0x0a21, + 0x0852: 0x0d51, 0x0855: 0x0869, 0x0856: 0x0f91, 0x0857: 0x1055, + 0x0858: 0x10b9, 0x0859: 0x10d5, 0x085a: 0x10d9, 0x085b: 0x10ed, 0x085c: 0x1569, 0x085d: 0x115d, + 0x085e: 0x11e1, 0x0860: 0x1301, 0x0862: 0x13c5, + 0x0865: 0x1479, 0x0866: 0x14a5, + 0x086a: 0x15bd, 0x086b: 0x15c1, 0x086c: 0x15c5, 0x086d: 0x1629, 0x086e: 0x149d, 0x086f: 0x1535, + 0x0870: 0x07c9, 0x0871: 0x07ed, 0x0872: 0x0801, 0x0873: 0x08bd, 0x0874: 0x08c9, 0x0875: 0x0909, + 0x0876: 0x09bd, 0x0877: 0x09d9, 0x0878: 0x09e1, 0x0879: 0x0a1d, 0x087a: 0x0a29, 0x087b: 0x0b05, + 0x087c: 0x0b0d, 0x087d: 0x0c15, 0x087e: 0x0c3d, 0x087f: 0x0c45, + // Block 0x22, offset 0x880 + 0x0880: 0x0c5d, 0x0881: 0x0d09, 0x0882: 0x0d39, 0x0883: 0x0d59, 0x0884: 0x0dc9, 0x0885: 0x0e8d, + 0x0886: 0x0ea9, 0x0887: 0x0ed9, 0x0888: 0x0f2d, 0x0889: 0x0f4d, 0x088a: 0x0fc1, 0x088b: 0x10a1, + 0x088c: 0x10bd, 0x088d: 0x10c5, 0x088e: 0x10c1, 0x088f: 0x10c9, 0x0890: 0x10cd, 0x0891: 0x10d1, + 0x0892: 0x10e5, 0x0893: 0x10e9, 0x0894: 0x110d, 0x0895: 0x1121, 0x0896: 0x113d, 0x0897: 0x11a1, + 0x0898: 0x11a9, 0x0899: 0x11b1, 0x089a: 0x11c5, 0x089b: 0x11ed, 0x089c: 0x123d, 0x089d: 0x1271, + 0x089e: 0x1271, 0x089f: 0x12d9, 0x08a0: 0x1381, 0x08a1: 0x1399, 0x08a2: 0x13cd, 0x08a3: 0x13d1, + 0x08a4: 0x1415, 0x08a5: 0x1419, 0x08a6: 0x1471, 0x08a7: 0x1479, 0x08a8: 0x1549, 0x08a9: 0x158d, + 0x08aa: 0x15a5, 0x08ab: 0x0c0d, 0x08ac: 0x1780, 0x08ad: 0x1255, + 0x08b0: 0x0751, 0x08b1: 0x0855, 0x08b2: 0x0815, 0x08b3: 0x07bd, 0x08b4: 0x07fd, 0x08b5: 0x0829, + 0x08b6: 0x08b9, 0x08b7: 0x08d5, 0x08b8: 0x09bd, 0x08b9: 0x09a9, 0x08ba: 0x09b9, 0x08bb: 0x09d5, + 0x08bc: 0x0a21, 0x08bd: 0x0a31, 0x08be: 0x0a75, 0x08bf: 0x0a81, + // Block 0x23, offset 0x8c0 + 0x08c0: 0x0a9d, 0x08c1: 0x0aad, 0x08c2: 0x0b95, 0x08c3: 0x0b9d, 0x08c4: 0x0bcd, 0x08c5: 0x0bed, + 0x08c6: 0x0c1d, 0x08c7: 0x0c35, 0x08c8: 0x0c25, 0x08c9: 0x0c45, 0x08ca: 0x0c39, 0x08cb: 0x0c5d, + 0x08cc: 0x0c79, 0x08cd: 0x0cd1, 0x08ce: 0x0cdd, 0x08cf: 0x0ce5, 0x08d0: 0x0d0d, 0x08d1: 0x0d51, + 0x08d2: 0x0d81, 0x08d3: 0x0d85, 0x08d4: 0x0d99, 0x08d5: 0x0e19, 0x08d6: 0x0e29, 0x08d7: 0x0e81, + 0x08d8: 0x0ecd, 0x08d9: 0x0ec5, 0x08da: 0x0ed9, 0x08db: 0x0ef5, 0x08dc: 0x0f2d, 0x08dd: 0x1085, + 0x08de: 0x0f51, 0x08df: 0x0f85, 0x08e0: 0x0f91, 0x08e1: 0x0fd1, 0x08e2: 0x0fed, 0x08e3: 0x1011, + 0x08e4: 0x1035, 0x08e5: 0x1039, 0x08e6: 0x1055, 0x08e7: 0x1059, 0x08e8: 0x1069, 0x08e9: 0x107d, + 0x08ea: 0x1079, 0x08eb: 0x10a9, 0x08ec: 0x1125, 0x08ed: 0x113d, 0x08ee: 0x1155, 0x08ef: 0x118d, + 0x08f0: 0x11a1, 0x08f1: 0x11bd, 0x08f2: 0x11ed, 0x08f3: 0x12a1, 0x08f4: 0x12c9, 0x08f5: 0x133d, + 0x08f6: 0x1385, 0x08f7: 0x1391, 0x08f8: 0x1399, 0x08f9: 0x13b1, 0x08fa: 0x13c5, 0x08fb: 0x13b5, + 0x08fc: 0x13cd, 0x08fd: 0x13c9, 0x08fe: 0x13c1, 0x08ff: 0x13d1, + // Block 0x24, offset 0x900 + 0x0900: 0x13dd, 0x0901: 0x1419, 0x0902: 0x1455, 0x0903: 0x1485, 0x0904: 0x14b9, 0x0905: 0x14d9, + 0x0906: 0x1525, 0x0907: 0x1549, 0x0908: 0x1569, 0x0909: 0x157d, 0x090a: 0x158d, 0x090b: 0x1599, + 0x090c: 0x15a5, 0x090d: 0x15f9, 0x090e: 0x1699, 0x090f: 0x1717, 0x0910: 0x1712, 0x0911: 0x1744, + 0x0912: 0x0679, 0x0913: 0x06a1, 0x0914: 0x06a5, 0x0915: 0x17c6, 0x0916: 0x17f3, 0x0917: 0x186b, + 0x0918: 0x1685, 0x0919: 0x1695, + // Block 0x25, offset 0x940 + 0x0940: 0x076d, 0x0941: 0x0765, 0x0942: 0x0775, 0x0943: 0x16a9, 0x0944: 0x07b9, 0x0945: 0x07c9, + 0x0946: 0x07cd, 0x0947: 0x07d5, 0x0948: 0x07dd, 0x0949: 0x07e1, 0x094a: 0x07ed, 0x094b: 0x07e5, + 0x094c: 0x0625, 0x094d: 0x16bd, 0x094e: 0x0801, 0x094f: 0x0805, 0x0950: 0x0809, 0x0951: 0x0825, + 0x0952: 0x16ae, 0x0953: 0x0629, 0x0954: 0x0811, 0x0955: 0x0831, 0x0956: 0x16b8, 0x0957: 0x0841, + 0x0958: 0x0849, 0x0959: 0x07a9, 0x095a: 0x0851, 0x095b: 0x0855, 0x095c: 0x1893, 0x095d: 0x0871, + 0x095e: 0x0879, 0x095f: 0x0631, 0x0960: 0x0891, 0x0961: 0x0895, 0x0962: 0x089d, 0x0963: 0x08a1, + 0x0964: 0x0635, 0x0965: 0x08b9, 0x0966: 0x08bd, 0x0967: 0x08c9, 0x0968: 0x08d5, 0x0969: 0x08d9, + 0x096a: 0x08dd, 0x096b: 0x08e5, 0x096c: 0x0905, 0x096d: 0x0909, 0x096e: 0x0911, 0x096f: 0x0921, + 0x0970: 0x0929, 0x0971: 0x092d, 0x0972: 0x092d, 0x0973: 0x092d, 0x0974: 0x16cc, 0x0975: 0x0f05, + 0x0976: 0x0941, 0x0977: 0x0949, 0x0978: 0x16d1, 0x0979: 0x0955, 0x097a: 0x095d, 0x097b: 0x0965, + 0x097c: 0x098d, 0x097d: 0x0979, 0x097e: 0x0985, 0x097f: 0x0989, + // Block 0x26, offset 0x980 + 0x0980: 0x0991, 0x0981: 0x0999, 0x0982: 0x099d, 0x0983: 0x09a5, 0x0984: 0x09ad, 0x0985: 0x09b1, + 0x0986: 0x09b1, 0x0987: 0x09b9, 0x0988: 0x09c1, 0x0989: 0x09c5, 0x098a: 0x09d1, 0x098b: 0x09f5, + 0x098c: 0x09d9, 0x098d: 0x09f9, 0x098e: 0x09dd, 0x098f: 0x09e5, 0x0990: 0x087d, 0x0991: 0x0a41, + 0x0992: 0x0a09, 0x0993: 0x0a0d, 0x0994: 0x0a11, 0x0995: 0x0a05, 0x0996: 0x0a19, 0x0997: 0x0a15, + 0x0998: 0x0a2d, 0x0999: 0x16d6, 0x099a: 0x0a49, 0x099b: 0x0a4d, 0x099c: 0x0a55, 0x099d: 0x0a61, + 0x099e: 0x0a69, 0x099f: 0x0a85, 0x09a0: 0x16db, 0x09a1: 0x16e0, 0x09a2: 0x0a91, 0x09a3: 0x0a95, + 0x09a4: 0x0a99, 0x09a5: 0x0a8d, 0x09a6: 0x0aa1, 0x09a7: 0x0639, 0x09a8: 0x063d, 0x09a9: 0x0aa9, + 0x09aa: 0x0ab1, 0x09ab: 0x0ab1, 0x09ac: 0x16e5, 0x09ad: 0x0acd, 0x09ae: 0x0ad1, 0x09af: 0x0ad5, + 0x09b0: 0x0add, 0x09b1: 0x16ea, 0x09b2: 0x0ae5, 0x09b3: 0x0ae9, 0x09b4: 0x0bc1, 0x09b5: 0x0af1, + 0x09b6: 0x0641, 0x09b7: 0x0afd, 0x09b8: 0x0b0d, 0x09b9: 0x0b19, 0x09ba: 0x0b15, 0x09bb: 0x16f4, + 0x09bc: 0x0b21, 0x09bd: 0x16f9, 0x09be: 0x0b2d, 0x09bf: 0x0b29, + // Block 0x27, offset 0x9c0 + 0x09c0: 0x0b31, 0x09c1: 0x0b41, 0x09c2: 0x0b45, 0x09c3: 0x0645, 0x09c4: 0x0b55, 0x09c5: 0x0b5d, + 0x09c6: 0x0b61, 0x09c7: 0x0b65, 0x09c8: 0x0649, 0x09c9: 0x16fe, 0x09ca: 0x064d, 0x09cb: 0x0b81, + 0x09cc: 0x0b85, 0x09cd: 0x0b89, 0x09ce: 0x0b91, 0x09cf: 0x18c5, 0x09d0: 0x0ba9, 0x09d1: 0x1708, + 0x09d2: 0x1708, 0x09d3: 0x1249, 0x09d4: 0x0bb9, 0x09d5: 0x0bb9, 0x09d6: 0x0651, 0x09d7: 0x172b, + 0x09d8: 0x17fd, 0x09d9: 0x0bc9, 0x09da: 0x0bd1, 0x09db: 0x0655, 0x09dc: 0x0be5, 0x09dd: 0x0bf5, + 0x09de: 0x0bf9, 0x09df: 0x0c01, 0x09e0: 0x0c11, 0x09e1: 0x065d, 0x09e2: 0x0659, 0x09e3: 0x0c15, + 0x09e4: 0x170d, 0x09e5: 0x0c19, 0x09e6: 0x0c2d, 0x09e7: 0x0c31, 0x09e8: 0x0c35, 0x09e9: 0x0c31, + 0x09ea: 0x0c41, 0x09eb: 0x0c45, 0x09ec: 0x0c55, 0x09ed: 0x0c4d, 0x09ee: 0x0c51, 0x09ef: 0x0c59, + 0x09f0: 0x0c5d, 0x09f1: 0x0c61, 0x09f2: 0x0c6d, 0x09f3: 0x0c71, 0x09f4: 0x0c89, 0x09f5: 0x0c91, + 0x09f6: 0x0ca1, 0x09f7: 0x0cb5, 0x09f8: 0x171c, 0x09f9: 0x0cb1, 0x09fa: 0x0ca5, 0x09fb: 0x0cbd, + 0x09fc: 0x0cc5, 0x09fd: 0x0cd9, 0x09fe: 0x1721, 0x09ff: 0x0ce1, + // Block 0x28, offset 0xa00 + 0x0a00: 0x0cd5, 0x0a01: 0x0ccd, 0x0a02: 0x0661, 0x0a03: 0x0ce9, 0x0a04: 0x0cf1, 0x0a05: 0x0cf9, + 0x0a06: 0x0ced, 0x0a07: 0x0665, 0x0a08: 0x0d09, 0x0a09: 0x0d11, 0x0a0a: 0x1726, 0x0a0b: 0x0d3d, + 0x0a0c: 0x0d71, 0x0a0d: 0x0d4d, 0x0a0e: 0x0671, 0x0a0f: 0x0d59, 0x0a10: 0x066d, 0x0a11: 0x0669, + 0x0a12: 0x0835, 0x0a13: 0x0839, 0x0a14: 0x0d75, 0x0a15: 0x0d5d, 0x0a16: 0x121d, 0x0a17: 0x06d5, + 0x0a18: 0x0d81, 0x0a19: 0x0d85, 0x0a1a: 0x0d89, 0x0a1b: 0x0d9d, 0x0a1c: 0x0d95, 0x0a1d: 0x173f, + 0x0a1e: 0x0675, 0x0a1f: 0x0db1, 0x0a20: 0x0da5, 0x0a21: 0x0dc1, 0x0a22: 0x0dc9, 0x0a23: 0x1749, + 0x0a24: 0x0dcd, 0x0a25: 0x0db9, 0x0a26: 0x0dd5, 0x0a27: 0x0679, 0x0a28: 0x0dd9, 0x0a29: 0x0ddd, + 0x0a2a: 0x0de1, 0x0a2b: 0x0ded, 0x0a2c: 0x174e, 0x0a2d: 0x0df5, 0x0a2e: 0x067d, 0x0a2f: 0x0e01, + 0x0a30: 0x1753, 0x0a31: 0x0e05, 0x0a32: 0x0681, 0x0a33: 0x0e11, 0x0a34: 0x0e1d, 0x0a35: 0x0e29, + 0x0a36: 0x0e2d, 0x0a37: 0x1758, 0x0a38: 0x16ef, 0x0a39: 0x175d, 0x0a3a: 0x0e4d, 0x0a3b: 0x1762, + 0x0a3c: 0x0e59, 0x0a3d: 0x0e61, 0x0a3e: 0x0e51, 0x0a3f: 0x0e6d, + // Block 0x29, offset 0xa40 + 0x0a40: 0x0e7d, 0x0a41: 0x0e8d, 0x0a42: 0x0e81, 0x0a43: 0x0e85, 0x0a44: 0x0e91, 0x0a45: 0x0e95, + 0x0a46: 0x1767, 0x0a47: 0x0e79, 0x0a48: 0x0ead, 0x0a49: 0x0eb1, 0x0a4a: 0x0685, 0x0a4b: 0x0ec5, + 0x0a4c: 0x0ec1, 0x0a4d: 0x176c, 0x0a4e: 0x0ea5, 0x0a4f: 0x0ee1, 0x0a50: 0x1771, 0x0a51: 0x1776, + 0x0a52: 0x0ee5, 0x0a53: 0x0ef9, 0x0a54: 0x0ef5, 0x0a55: 0x0ef1, 0x0a56: 0x0689, 0x0a57: 0x0efd, + 0x0a58: 0x0f0d, 0x0a59: 0x0f09, 0x0a5a: 0x0f15, 0x0a5b: 0x16b3, 0x0a5c: 0x0f25, 0x0a5d: 0x177b, + 0x0a5e: 0x0f31, 0x0a5f: 0x1785, 0x0a60: 0x0f45, 0x0a61: 0x0f51, 0x0a62: 0x0f65, 0x0a63: 0x178a, + 0x0a64: 0x0f79, 0x0a65: 0x0f7d, 0x0a66: 0x178f, 0x0a67: 0x1794, 0x0a68: 0x0f99, 0x0a69: 0x0fa9, + 0x0a6a: 0x068d, 0x0a6b: 0x0fad, 0x0a6c: 0x0691, 0x0a6d: 0x0691, 0x0a6e: 0x0fc5, 0x0a6f: 0x0fc9, + 0x0a70: 0x0fd1, 0x0a71: 0x0fd5, 0x0a72: 0x0fe1, 0x0a73: 0x0695, 0x0a74: 0x0ff9, 0x0a75: 0x1799, + 0x0a76: 0x1015, 0x0a77: 0x179e, 0x0a78: 0x1021, 0x0a79: 0x1703, 0x0a7a: 0x1031, 0x0a7b: 0x17a3, + 0x0a7c: 0x17a8, 0x0a7d: 0x17ad, 0x0a7e: 0x0699, 0x0a7f: 0x069d, + // Block 0x2a, offset 0xa80 + 0x0a80: 0x1069, 0x0a81: 0x17b7, 0x0a82: 0x17b2, 0x0a83: 0x17bc, 0x0a84: 0x17c1, 0x0a85: 0x1071, + 0x0a86: 0x1075, 0x0a87: 0x1075, 0x0a88: 0x107d, 0x0a89: 0x06a5, 0x0a8a: 0x1081, 0x0a8b: 0x06a9, + 0x0a8c: 0x06ad, 0x0a8d: 0x17cb, 0x0a8e: 0x1095, 0x0a8f: 0x109d, 0x0a90: 0x10a9, 0x0a91: 0x06b1, + 0x0a92: 0x17d0, 0x0a93: 0x10cd, 0x0a94: 0x17d5, 0x0a95: 0x17da, 0x0a96: 0x10ed, 0x0a97: 0x1105, + 0x0a98: 0x06b5, 0x0a99: 0x110d, 0x0a9a: 0x1111, 0x0a9b: 0x1115, 0x0a9c: 0x17df, 0x0a9d: 0x17e4, + 0x0a9e: 0x17e4, 0x0a9f: 0x112d, 0x0aa0: 0x06b9, 0x0aa1: 0x17e9, 0x0aa2: 0x1141, 0x0aa3: 0x1145, + 0x0aa4: 0x06bd, 0x0aa5: 0x17ee, 0x0aa6: 0x1161, 0x0aa7: 0x06c1, 0x0aa8: 0x1171, 0x0aa9: 0x1169, + 0x0aaa: 0x1179, 0x0aab: 0x17f8, 0x0aac: 0x1191, 0x0aad: 0x06c5, 0x0aae: 0x119d, 0x0aaf: 0x11a5, + 0x0ab0: 0x11b5, 0x0ab1: 0x06c9, 0x0ab2: 0x1802, 0x0ab3: 0x1807, 0x0ab4: 0x06cd, 0x0ab5: 0x180c, + 0x0ab6: 0x11cd, 0x0ab7: 0x1811, 0x0ab8: 0x11d9, 0x0ab9: 0x11e5, 0x0aba: 0x11ed, 0x0abb: 0x1816, + 0x0abc: 0x181b, 0x0abd: 0x1201, 0x0abe: 0x1820, 0x0abf: 0x1209, + // Block 0x2b, offset 0xac0 + 0x0ac0: 0x1730, 0x0ac1: 0x06d1, 0x0ac2: 0x1221, 0x0ac3: 0x1225, 0x0ac4: 0x06d9, 0x0ac5: 0x1229, + 0x0ac6: 0x0aa5, 0x0ac7: 0x1825, 0x0ac8: 0x182a, 0x0ac9: 0x1735, 0x0aca: 0x173a, 0x0acb: 0x1249, + 0x0acc: 0x124d, 0x0acd: 0x1465, 0x0ace: 0x06dd, 0x0acf: 0x1279, 0x0ad0: 0x1275, 0x0ad1: 0x127d, + 0x0ad2: 0x08b1, 0x0ad3: 0x1281, 0x0ad4: 0x1285, 0x0ad5: 0x1289, 0x0ad6: 0x1291, 0x0ad7: 0x182f, + 0x0ad8: 0x128d, 0x0ad9: 0x1295, 0x0ada: 0x12a9, 0x0adb: 0x12ad, 0x0adc: 0x1299, 0x0add: 0x12b1, + 0x0ade: 0x12c5, 0x0adf: 0x12d9, 0x0ae0: 0x12a5, 0x0ae1: 0x12b9, 0x0ae2: 0x12bd, 0x0ae3: 0x12c1, + 0x0ae4: 0x1834, 0x0ae5: 0x183e, 0x0ae6: 0x1839, 0x0ae7: 0x06e1, 0x0ae8: 0x12e1, 0x0ae9: 0x12e5, + 0x0aea: 0x12ed, 0x0aeb: 0x1852, 0x0aec: 0x12f1, 0x0aed: 0x1843, 0x0aee: 0x06e5, 0x0aef: 0x06e9, + 0x0af0: 0x1848, 0x0af1: 0x184d, 0x0af2: 0x06ed, 0x0af3: 0x1311, 0x0af4: 0x1315, 0x0af5: 0x1319, + 0x0af6: 0x131d, 0x0af7: 0x1329, 0x0af8: 0x1325, 0x0af9: 0x1331, 0x0afa: 0x132d, 0x0afb: 0x133d, + 0x0afc: 0x1335, 0x0afd: 0x1339, 0x0afe: 0x1341, 0x0aff: 0x06f1, + // Block 0x2c, offset 0xb00 + 0x0b00: 0x1349, 0x0b01: 0x134d, 0x0b02: 0x06f5, 0x0b03: 0x135d, 0x0b04: 0x1361, 0x0b05: 0x1857, + 0x0b06: 0x136d, 0x0b07: 0x1371, 0x0b08: 0x06f9, 0x0b09: 0x137d, 0x0b0a: 0x062d, 0x0b0b: 0x185c, + 0x0b0c: 0x1861, 0x0b0d: 0x06fd, 0x0b0e: 0x0701, 0x0b0f: 0x13a9, 0x0b10: 0x13c1, 0x0b11: 0x13dd, + 0x0b12: 0x13ed, 0x0b13: 0x1866, 0x0b14: 0x1401, 0x0b15: 0x1405, 0x0b16: 0x141d, 0x0b17: 0x1429, + 0x0b18: 0x1870, 0x0b19: 0x16c2, 0x0b1a: 0x1435, 0x0b1b: 0x1431, 0x0b1c: 0x143d, 0x0b1d: 0x16c7, + 0x0b1e: 0x1449, 0x0b1f: 0x1455, 0x0b20: 0x1875, 0x0b21: 0x187a, 0x0b22: 0x1495, 0x0b23: 0x14a1, + 0x0b24: 0x14a9, 0x0b25: 0x187f, 0x0b26: 0x14ad, 0x0b27: 0x14d5, 0x0b28: 0x14e1, 0x0b29: 0x14e5, + 0x0b2a: 0x14dd, 0x0b2b: 0x14f1, 0x0b2c: 0x14f5, 0x0b2d: 0x1884, 0x0b2e: 0x1501, 0x0b2f: 0x0705, + 0x0b30: 0x1509, 0x0b31: 0x1889, 0x0b32: 0x0709, 0x0b33: 0x1541, 0x0b34: 0x0b35, 0x0b35: 0x1559, + 0x0b36: 0x188e, 0x0b37: 0x1898, 0x0b38: 0x070d, 0x0b39: 0x0711, 0x0b3a: 0x1581, 0x0b3b: 0x189d, + 0x0b3c: 0x0715, 0x0b3d: 0x18a2, 0x0b3e: 0x1599, 0x0b3f: 0x1599, + // Block 0x2d, offset 0xb40 + 0x0b40: 0x15a1, 0x0b41: 0x18a7, 0x0b42: 0x15b9, 0x0b43: 0x0719, 0x0b44: 0x15c9, 0x0b45: 0x15d5, + 0x0b46: 0x15dd, 0x0b47: 0x15e5, 0x0b48: 0x071d, 0x0b49: 0x18ac, 0x0b4a: 0x15f9, 0x0b4b: 0x1615, + 0x0b4c: 0x1621, 0x0b4d: 0x0721, 0x0b4e: 0x0725, 0x0b4f: 0x1625, 0x0b50: 0x18b1, 0x0b51: 0x0729, + 0x0b52: 0x18b6, 0x0b53: 0x18bb, 0x0b54: 0x18c0, 0x0b55: 0x1649, 0x0b56: 0x072d, 0x0b57: 0x165d, + 0x0b58: 0x1665, 0x0b59: 0x1669, 0x0b5a: 0x1671, 0x0b5b: 0x1679, 0x0b5c: 0x1681, 0x0b5d: 0x18ca, +} + +// nfcSparseOffset: 98 entries, 196 bytes +var nfcSparseOffset = []uint16{0x0, 0x2, 0x6, 0x8, 0x13, 0x23, 0x25, 0x2a, 0x35, 0x44, 0x51, 0x59, 0x5d, 0x62, 0x64, 0x73, 0x7b, 0x82, 0x85, 0x8d, 0x91, 0x95, 0x97, 0x99, 0xa2, 0xa6, 0xad, 0xb2, 0xb5, 0xbf, 0xc1, 0xc8, 0xd0, 0xd3, 0xd5, 0xd7, 0xd9, 0xde, 0xed, 0xf9, 0xfb, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x110, 0x113, 0x115, 0x118, 0x11b, 0x11f, 0x128, 0x12a, 0x12d, 0x12f, 0x139, 0x148, 0x14a, 0x158, 0x15b, 0x161, 0x167, 0x172, 0x176, 0x178, 0x17a, 0x17c, 0x17e, 0x180, 0x186, 0x189, 0x18b, 0x18d, 0x18f, 0x192, 0x194, 0x196, 0x198, 0x19a, 0x1a0, 0x1a3, 0x1a5, 0x1a7, 0x1a9, 0x1b7, 0x1c0, 0x1c2, 0x1c4, 0x1ca, 0x1d2, 0x1d9, 0x1dc, 0x1e9, 0x1f3, 0x1f5} + +// nfcSparseValues: 503 entries, 2012 bytes +var nfcSparseValues = [503]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0000, lo: 0x01}, + {value: 0x8800, lo: 0xa8, hi: 0xa8}, + // Block 0x1, offset 0x2 + {value: 0x0091, lo: 0x03}, + {value: 0x46c8, lo: 0xa0, hi: 0xa1}, + {value: 0x46fa, lo: 0xaf, hi: 0xb0}, + {value: 0x8800, lo: 0xb7, hi: 0xb7}, + // Block 0x2, offset 0x3 + {value: 0x0000, lo: 0x01}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + // Block 0x3, offset 0x4 + {value: 0x0006, lo: 0x0a}, + {value: 0x8800, lo: 0x81, hi: 0x81}, + {value: 0x8800, lo: 0x85, hi: 0x85}, + {value: 0x8800, lo: 0x89, hi: 0x89}, + {value: 0x4826, lo: 0x8a, hi: 0x8a}, + {value: 0x4844, lo: 0x8b, hi: 0x8b}, + {value: 0x36b1, lo: 0x8c, hi: 0x8c}, + {value: 0x36c9, lo: 0x8d, hi: 0x8d}, + {value: 0x485c, lo: 0x8e, hi: 0x8e}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x36e7, lo: 0x93, hi: 0x94}, + // Block 0x4, offset 0x5 + {value: 0x0000, lo: 0x0f}, + {value: 0x8800, lo: 0x83, hi: 0x83}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x8800, lo: 0x8b, hi: 0x8b}, + {value: 0x8800, lo: 0x8d, hi: 0x8d}, + {value: 0x378f, lo: 0x90, hi: 0x90}, + {value: 0x379b, lo: 0x91, hi: 0x91}, + {value: 0x3789, lo: 0x93, hi: 0x93}, + {value: 0x8800, lo: 0x96, hi: 0x96}, + {value: 0x3801, lo: 0x97, hi: 0x97}, + {value: 0x37cb, lo: 0x9c, hi: 0x9c}, + {value: 0x37b3, lo: 0x9d, hi: 0x9d}, + {value: 0x37dd, lo: 0x9e, hi: 0x9e}, + {value: 0x8800, lo: 0xb4, hi: 0xb5}, + {value: 0x3807, lo: 0xb6, hi: 0xb6}, + {value: 0x380d, lo: 0xb7, hi: 0xb7}, + // Block 0x5, offset 0x6 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0x83, hi: 0x87}, + // Block 0x6, offset 0x7 + {value: 0x0001, lo: 0x04}, + {value: 0x8018, lo: 0x81, hi: 0x82}, + {value: 0x80e6, lo: 0x84, hi: 0x84}, + {value: 0x80dc, lo: 0x85, hi: 0x85}, + {value: 0x8012, lo: 0x87, hi: 0x87}, + // Block 0x7, offset 0x8 + {value: 0x0000, lo: 0x0a}, + {value: 0x80e6, lo: 0x90, hi: 0x97}, + {value: 0x801e, lo: 0x98, hi: 0x98}, + {value: 0x801f, lo: 0x99, hi: 0x99}, + {value: 0x8020, lo: 0x9a, hi: 0x9a}, + {value: 0x382b, lo: 0xa2, hi: 0xa2}, + {value: 0x3831, lo: 0xa3, hi: 0xa3}, + {value: 0x383d, lo: 0xa4, hi: 0xa4}, + {value: 0x3837, lo: 0xa5, hi: 0xa5}, + {value: 0x3843, lo: 0xa6, hi: 0xa6}, + {value: 0x8800, lo: 0xa7, hi: 0xa7}, + // Block 0x8, offset 0x9 + {value: 0x0000, lo: 0x0e}, + {value: 0x3855, lo: 0x80, hi: 0x80}, + {value: 0x8800, lo: 0x81, hi: 0x81}, + {value: 0x3849, lo: 0x82, hi: 0x82}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x384f, lo: 0x93, hi: 0x93}, + {value: 0x8800, lo: 0x95, hi: 0x95}, + {value: 0x80e6, lo: 0x96, hi: 0x9c}, + {value: 0x80e6, lo: 0x9f, hi: 0xa2}, + {value: 0x80dc, lo: 0xa3, hi: 0xa3}, + {value: 0x80e6, lo: 0xa4, hi: 0xa4}, + {value: 0x80e6, lo: 0xa7, hi: 0xa8}, + {value: 0x80dc, lo: 0xaa, hi: 0xaa}, + {value: 0x80e6, lo: 0xab, hi: 0xac}, + {value: 0x80dc, lo: 0xad, hi: 0xad}, + // Block 0x9, offset 0xa + {value: 0x0000, lo: 0x0c}, + {value: 0x8024, lo: 0x91, hi: 0x91}, + {value: 0x80e6, lo: 0xb0, hi: 0xb0}, + {value: 0x80dc, lo: 0xb1, hi: 0xb1}, + {value: 0x80e6, lo: 0xb2, hi: 0xb3}, + {value: 0x80dc, lo: 0xb4, hi: 0xb4}, + {value: 0x80e6, lo: 0xb5, hi: 0xb6}, + {value: 0x80dc, lo: 0xb7, hi: 0xb9}, + {value: 0x80e6, lo: 0xba, hi: 0xba}, + {value: 0x80dc, lo: 0xbb, hi: 0xbc}, + {value: 0x80e6, lo: 0xbd, hi: 0xbd}, + {value: 0x80dc, lo: 0xbe, hi: 0xbe}, + {value: 0x80e6, lo: 0xbf, hi: 0xbf}, + // Block 0xa, offset 0xb + {value: 0x000a, lo: 0x07}, + {value: 0x80e6, lo: 0x80, hi: 0x80}, + {value: 0x80e6, lo: 0x81, hi: 0x81}, + {value: 0x80dc, lo: 0x82, hi: 0x83}, + {value: 0x80dc, lo: 0x84, hi: 0x85}, + {value: 0x80dc, lo: 0x86, hi: 0x87}, + {value: 0x80dc, lo: 0x88, hi: 0x89}, + {value: 0x80e6, lo: 0x8a, hi: 0x8a}, + // Block 0xb, offset 0xc + {value: 0x0000, lo: 0x03}, + {value: 0x80e6, lo: 0xab, hi: 0xb1}, + {value: 0x80dc, lo: 0xb2, hi: 0xb2}, + {value: 0x80e6, lo: 0xb3, hi: 0xb3}, + // Block 0xc, offset 0xd + {value: 0x0000, lo: 0x04}, + {value: 0x80e6, lo: 0x96, hi: 0x99}, + {value: 0x80e6, lo: 0x9b, hi: 0xa3}, + {value: 0x80e6, lo: 0xa5, hi: 0xa7}, + {value: 0x80e6, lo: 0xa9, hi: 0xad}, + // Block 0xd, offset 0xe + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0x99, hi: 0x9b}, + // Block 0xe, offset 0xf + {value: 0x0000, lo: 0x0e}, + {value: 0x80e6, lo: 0xa4, hi: 0xa5}, + {value: 0x80dc, lo: 0xa6, hi: 0xa6}, + {value: 0x80e6, lo: 0xa7, hi: 0xa8}, + {value: 0x80dc, lo: 0xa9, hi: 0xa9}, + {value: 0x80e6, lo: 0xaa, hi: 0xac}, + {value: 0x80dc, lo: 0xad, hi: 0xaf}, + {value: 0x801b, lo: 0xb0, hi: 0xb0}, + {value: 0x801c, lo: 0xb1, hi: 0xb1}, + {value: 0x801d, lo: 0xb2, hi: 0xb2}, + {value: 0x80e6, lo: 0xb3, hi: 0xb5}, + {value: 0x80dc, lo: 0xb6, hi: 0xb6}, + {value: 0x80e6, lo: 0xb7, hi: 0xb8}, + {value: 0x80dc, lo: 0xb9, hi: 0xba}, + {value: 0x80e6, lo: 0xbb, hi: 0xbe}, + // Block 0xf, offset 0x10 + {value: 0x0000, lo: 0x07}, + {value: 0x8800, lo: 0xa8, hi: 0xa8}, + {value: 0x3ec2, lo: 0xa9, hi: 0xa9}, + {value: 0x8800, lo: 0xb0, hi: 0xb0}, + {value: 0x3eca, lo: 0xb1, hi: 0xb1}, + {value: 0x8800, lo: 0xb3, hi: 0xb3}, + {value: 0x3ed2, lo: 0xb4, hi: 0xb4}, + {value: 0x8607, lo: 0xbc, hi: 0xbc}, + // Block 0x10, offset 0x11 + {value: 0x0008, lo: 0x06}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x80e6, lo: 0x91, hi: 0x91}, + {value: 0x80dc, lo: 0x92, hi: 0x92}, + {value: 0x80e6, lo: 0x93, hi: 0x93}, + {value: 0x80e6, lo: 0x94, hi: 0x94}, + {value: 0x4502, lo: 0x98, hi: 0x9f}, + // Block 0x11, offset 0x12 + {value: 0x0000, lo: 0x02}, + {value: 0x8007, lo: 0xbc, hi: 0xbc}, + {value: 0x8600, lo: 0xbe, hi: 0xbe}, + // Block 0x12, offset 0x13 + {value: 0x0007, lo: 0x07}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x18cf, lo: 0x8b, hi: 0x8c}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x97, hi: 0x97}, + {value: 0x4542, lo: 0x9c, hi: 0x9c}, + {value: 0x454a, lo: 0x9d, hi: 0x9d}, + {value: 0x4552, lo: 0x9f, hi: 0x9f}, + // Block 0x13, offset 0x14 + {value: 0x0000, lo: 0x03}, + {value: 0x457a, lo: 0xb3, hi: 0xb3}, + {value: 0x4582, lo: 0xb6, hi: 0xb6}, + {value: 0x8007, lo: 0xbc, hi: 0xbc}, + // Block 0x14, offset 0x15 + {value: 0x0008, lo: 0x03}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x455a, lo: 0x99, hi: 0x9b}, + {value: 0x4572, lo: 0x9e, hi: 0x9e}, + // Block 0x15, offset 0x16 + {value: 0x0000, lo: 0x01}, + {value: 0x8007, lo: 0xbc, hi: 0xbc}, + // Block 0x16, offset 0x17 + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + // Block 0x17, offset 0x18 + {value: 0x0000, lo: 0x08}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x18e4, lo: 0x88, hi: 0x88}, + {value: 0x18dd, lo: 0x8b, hi: 0x8b}, + {value: 0x18eb, lo: 0x8c, hi: 0x8c}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x96, hi: 0x97}, + {value: 0x458a, lo: 0x9c, hi: 0x9c}, + {value: 0x4592, lo: 0x9d, hi: 0x9d}, + // Block 0x18, offset 0x19 + {value: 0x0000, lo: 0x03}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x18f2, lo: 0x94, hi: 0x94}, + {value: 0x8600, lo: 0xbe, hi: 0xbe}, + // Block 0x19, offset 0x1a + {value: 0x0000, lo: 0x06}, + {value: 0x8800, lo: 0x86, hi: 0x87}, + {value: 0x18f9, lo: 0x8a, hi: 0x8a}, + {value: 0x1907, lo: 0x8b, hi: 0x8b}, + {value: 0x1900, lo: 0x8c, hi: 0x8c}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x97, hi: 0x97}, + // Block 0x1a, offset 0x1b + {value: 0x0607, lo: 0x04}, + {value: 0x8800, lo: 0x86, hi: 0x86}, + {value: 0x3eda, lo: 0x88, hi: 0x88}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8054, lo: 0x95, hi: 0x96}, + // Block 0x1b, offset 0x1c + {value: 0x0000, lo: 0x02}, + {value: 0x8007, lo: 0xbc, hi: 0xbc}, + {value: 0x8800, lo: 0xbf, hi: 0xbf}, + // Block 0x1c, offset 0x1d + {value: 0x0000, lo: 0x09}, + {value: 0x190e, lo: 0x80, hi: 0x80}, + {value: 0x8600, lo: 0x82, hi: 0x82}, + {value: 0x8800, lo: 0x86, hi: 0x86}, + {value: 0x1915, lo: 0x87, hi: 0x87}, + {value: 0x191c, lo: 0x88, hi: 0x88}, + {value: 0x2e66, lo: 0x8a, hi: 0x8a}, + {value: 0x19a5, lo: 0x8b, hi: 0x8b}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x95, hi: 0x96}, + // Block 0x1d, offset 0x1e + {value: 0x0000, lo: 0x01}, + {value: 0x8600, lo: 0xbe, hi: 0xbe}, + // Block 0x1e, offset 0x1f + {value: 0x0000, lo: 0x06}, + {value: 0x8800, lo: 0x86, hi: 0x87}, + {value: 0x1923, lo: 0x8a, hi: 0x8a}, + {value: 0x1931, lo: 0x8b, hi: 0x8b}, + {value: 0x192a, lo: 0x8c, hi: 0x8c}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x97, hi: 0x97}, + // Block 0x1f, offset 0x20 + {value: 0x0007, lo: 0x07}, + {value: 0x8609, lo: 0x8a, hi: 0x8a}, + {value: 0x8600, lo: 0x8f, hi: 0x8f}, + {value: 0x8800, lo: 0x99, hi: 0x99}, + {value: 0x3ee2, lo: 0x9a, hi: 0x9a}, + {value: 0x2e6d, lo: 0x9c, hi: 0x9d}, + {value: 0x1938, lo: 0x9e, hi: 0x9e}, + {value: 0x8600, lo: 0x9f, hi: 0x9f}, + // Block 0x20, offset 0x21 + {value: 0x0000, lo: 0x02}, + {value: 0x8067, lo: 0xb8, hi: 0xb9}, + {value: 0x8009, lo: 0xba, hi: 0xba}, + // Block 0x21, offset 0x22 + {value: 0x0000, lo: 0x01}, + {value: 0x806b, lo: 0x88, hi: 0x8b}, + // Block 0x22, offset 0x23 + {value: 0x0000, lo: 0x01}, + {value: 0x8076, lo: 0xb8, hi: 0xb9}, + // Block 0x23, offset 0x24 + {value: 0x0000, lo: 0x01}, + {value: 0x807a, lo: 0x88, hi: 0x8b}, + // Block 0x24, offset 0x25 + {value: 0x0000, lo: 0x04}, + {value: 0x80dc, lo: 0x98, hi: 0x99}, + {value: 0x80dc, lo: 0xb5, hi: 0xb5}, + {value: 0x80dc, lo: 0xb7, hi: 0xb7}, + {value: 0x80d8, lo: 0xb9, hi: 0xb9}, + // Block 0x25, offset 0x26 + {value: 0x0000, lo: 0x0e}, + {value: 0x2786, lo: 0x83, hi: 0x83}, + {value: 0x278d, lo: 0x8d, hi: 0x8d}, + {value: 0x2794, lo: 0x92, hi: 0x92}, + {value: 0x279b, lo: 0x97, hi: 0x97}, + {value: 0x27a2, lo: 0x9c, hi: 0x9c}, + {value: 0x277f, lo: 0xa9, hi: 0xa9}, + {value: 0x8081, lo: 0xb1, hi: 0xb1}, + {value: 0x8082, lo: 0xb2, hi: 0xb2}, + {value: 0x49b6, lo: 0xb3, hi: 0xb3}, + {value: 0x8084, lo: 0xb4, hi: 0xb4}, + {value: 0x49bf, lo: 0xb5, hi: 0xb5}, + {value: 0x459a, lo: 0xb6, hi: 0xb6}, + {value: 0x45a2, lo: 0xb8, hi: 0xb8}, + {value: 0x8082, lo: 0xba, hi: 0xbd}, + // Block 0x26, offset 0x27 + {value: 0x0000, lo: 0x0b}, + {value: 0x8082, lo: 0x80, hi: 0x80}, + {value: 0x49c8, lo: 0x81, hi: 0x81}, + {value: 0x80e6, lo: 0x82, hi: 0x83}, + {value: 0x8009, lo: 0x84, hi: 0x84}, + {value: 0x80e6, lo: 0x86, hi: 0x87}, + {value: 0x27b0, lo: 0x93, hi: 0x93}, + {value: 0x27b7, lo: 0x9d, hi: 0x9d}, + {value: 0x27be, lo: 0xa2, hi: 0xa2}, + {value: 0x27c5, lo: 0xa7, hi: 0xa7}, + {value: 0x27cc, lo: 0xac, hi: 0xac}, + {value: 0x27a9, lo: 0xb9, hi: 0xb9}, + // Block 0x27, offset 0x28 + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0x86, hi: 0x86}, + // Block 0x28, offset 0x29 + {value: 0x0000, lo: 0x05}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x193f, lo: 0xa6, hi: 0xa6}, + {value: 0x8600, lo: 0xae, hi: 0xae}, + {value: 0x8007, lo: 0xb7, hi: 0xb7}, + {value: 0x8009, lo: 0xb9, hi: 0xba}, + // Block 0x29, offset 0x2a + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0x8d, hi: 0x8d}, + // Block 0x2a, offset 0x2b + {value: 0x0000, lo: 0x01}, + {value: 0x8800, lo: 0x80, hi: 0x92}, + // Block 0x2b, offset 0x2c + {value: 0x0000, lo: 0x01}, + {value: 0x8e00, lo: 0xa1, hi: 0xb5}, + // Block 0x2c, offset 0x2d + {value: 0x0000, lo: 0x01}, + {value: 0x8600, lo: 0xa8, hi: 0xbf}, + // Block 0x2d, offset 0x2e + {value: 0x0000, lo: 0x01}, + {value: 0x8600, lo: 0x80, hi: 0x82}, + // Block 0x2e, offset 0x2f + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0x9d, hi: 0x9f}, + // Block 0x2f, offset 0x30 + {value: 0x0000, lo: 0x02}, + {value: 0x8009, lo: 0x94, hi: 0x94}, + {value: 0x8009, lo: 0xb4, hi: 0xb4}, + // Block 0x30, offset 0x31 + {value: 0x0000, lo: 0x02}, + {value: 0x8009, lo: 0x92, hi: 0x92}, + {value: 0x80e6, lo: 0x9d, hi: 0x9d}, + // Block 0x31, offset 0x32 + {value: 0x0000, lo: 0x01}, + {value: 0x80e4, lo: 0xa9, hi: 0xa9}, + // Block 0x32, offset 0x33 + {value: 0x0008, lo: 0x02}, + {value: 0x80de, lo: 0xb9, hi: 0xba}, + {value: 0x80dc, lo: 0xbb, hi: 0xbb}, + // Block 0x33, offset 0x34 + {value: 0x0000, lo: 0x02}, + {value: 0x80e6, lo: 0x97, hi: 0x97}, + {value: 0x80dc, lo: 0x98, hi: 0x98}, + // Block 0x34, offset 0x35 + {value: 0x0000, lo: 0x03}, + {value: 0x8009, lo: 0xa0, hi: 0xa0}, + {value: 0x80e6, lo: 0xb5, hi: 0xbc}, + {value: 0x80dc, lo: 0xbf, hi: 0xbf}, + // Block 0x35, offset 0x36 + {value: 0x0000, lo: 0x08}, + {value: 0x197e, lo: 0x80, hi: 0x80}, + {value: 0x1985, lo: 0x81, hi: 0x81}, + {value: 0x8800, lo: 0x82, hi: 0x82}, + {value: 0x198c, lo: 0x83, hi: 0x83}, + {value: 0x8009, lo: 0x84, hi: 0x84}, + {value: 0x80e6, lo: 0xab, hi: 0xab}, + {value: 0x80dc, lo: 0xac, hi: 0xac}, + {value: 0x80e6, lo: 0xad, hi: 0xb3}, + // Block 0x36, offset 0x37 + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0xaa, hi: 0xab}, + // Block 0x37, offset 0x38 + {value: 0x0000, lo: 0x02}, + {value: 0x8007, lo: 0xa6, hi: 0xa6}, + {value: 0x8009, lo: 0xb2, hi: 0xb3}, + // Block 0x38, offset 0x39 + {value: 0x0000, lo: 0x01}, + {value: 0x8007, lo: 0xb7, hi: 0xb7}, + // Block 0x39, offset 0x3a + {value: 0x0000, lo: 0x09}, + {value: 0x80e6, lo: 0x90, hi: 0x92}, + {value: 0x8001, lo: 0x94, hi: 0x94}, + {value: 0x80dc, lo: 0x95, hi: 0x99}, + {value: 0x80e6, lo: 0x9a, hi: 0x9b}, + {value: 0x80dc, lo: 0x9c, hi: 0x9f}, + {value: 0x80e6, lo: 0xa0, hi: 0xa0}, + {value: 0x8001, lo: 0xa2, hi: 0xa8}, + {value: 0x80dc, lo: 0xad, hi: 0xad}, + {value: 0x80e6, lo: 0xb4, hi: 0xb4}, + // Block 0x3a, offset 0x3b + {value: 0x0000, lo: 0x0e}, + {value: 0x80e6, lo: 0x80, hi: 0x81}, + {value: 0x80dc, lo: 0x82, hi: 0x82}, + {value: 0x80e6, lo: 0x83, hi: 0x89}, + {value: 0x80dc, lo: 0x8a, hi: 0x8a}, + {value: 0x80e6, lo: 0x8b, hi: 0x8c}, + {value: 0x80ea, lo: 0x8d, hi: 0x8d}, + {value: 0x80d6, lo: 0x8e, hi: 0x8e}, + {value: 0x80dc, lo: 0x8f, hi: 0x8f}, + {value: 0x80ca, lo: 0x90, hi: 0x90}, + {value: 0x80e6, lo: 0x91, hi: 0xa6}, + {value: 0x80e9, lo: 0xbc, hi: 0xbc}, + {value: 0x80dc, lo: 0xbd, hi: 0xbd}, + {value: 0x80e6, lo: 0xbe, hi: 0xbe}, + {value: 0x80dc, lo: 0xbf, hi: 0xbf}, + // Block 0x3b, offset 0x3c + {value: 0x0004, lo: 0x01}, + {value: 0x04a5, lo: 0x80, hi: 0x81}, + // Block 0x3c, offset 0x3d + {value: 0x0000, lo: 0x0d}, + {value: 0x80e6, lo: 0x90, hi: 0x91}, + {value: 0x8001, lo: 0x92, hi: 0x93}, + {value: 0x80e6, lo: 0x94, hi: 0x97}, + {value: 0x8001, lo: 0x98, hi: 0x9a}, + {value: 0x80e6, lo: 0x9b, hi: 0x9c}, + {value: 0x80e6, lo: 0xa1, hi: 0xa1}, + {value: 0x8001, lo: 0xa5, hi: 0xa6}, + {value: 0x80e6, lo: 0xa7, hi: 0xa7}, + {value: 0x80dc, lo: 0xa8, hi: 0xa8}, + {value: 0x80e6, lo: 0xa9, hi: 0xa9}, + {value: 0x8001, lo: 0xaa, hi: 0xab}, + {value: 0x80dc, lo: 0xac, hi: 0xaf}, + {value: 0x80e6, lo: 0xb0, hi: 0xb0}, + // Block 0x3d, offset 0x3e + {value: 0x4261, lo: 0x02}, + {value: 0x01b8, lo: 0xa6, hi: 0xa6}, + {value: 0x0057, lo: 0xaa, hi: 0xab}, + // Block 0x3e, offset 0x3f + {value: 0x0007, lo: 0x05}, + {value: 0x8800, lo: 0x90, hi: 0x90}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x8800, lo: 0x94, hi: 0x94}, + {value: 0x3ba3, lo: 0x9a, hi: 0x9b}, + {value: 0x3bb1, lo: 0xae, hi: 0xae}, + // Block 0x3f, offset 0x40 + {value: 0x000e, lo: 0x05}, + {value: 0x3bb8, lo: 0x8d, hi: 0x8e}, + {value: 0x3bbf, lo: 0x8f, hi: 0x8f}, + {value: 0x8800, lo: 0x90, hi: 0x90}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x8800, lo: 0x94, hi: 0x94}, + // Block 0x40, offset 0x41 + {value: 0x4c1e, lo: 0x0a}, + {value: 0x8800, lo: 0x83, hi: 0x83}, + {value: 0x3bcd, lo: 0x84, hi: 0x84}, + {value: 0x8800, lo: 0x88, hi: 0x88}, + {value: 0x3bd4, lo: 0x89, hi: 0x89}, + {value: 0x8800, lo: 0x8b, hi: 0x8b}, + {value: 0x3bdb, lo: 0x8c, hi: 0x8c}, + {value: 0x8800, lo: 0xa3, hi: 0xa3}, + {value: 0x3be2, lo: 0xa4, hi: 0xa5}, + {value: 0x3be9, lo: 0xa6, hi: 0xa6}, + {value: 0x8800, lo: 0xbc, hi: 0xbc}, + // Block 0x41, offset 0x42 + {value: 0x0007, lo: 0x03}, + {value: 0x3c52, lo: 0xa0, hi: 0xa1}, + {value: 0x3c7c, lo: 0xa2, hi: 0xa3}, + {value: 0x3ca6, lo: 0xaa, hi: 0xad}, + // Block 0x42, offset 0x43 + {value: 0x0004, lo: 0x01}, + {value: 0x04fd, lo: 0xa9, hi: 0xaa}, + // Block 0x43, offset 0x44 + {value: 0x0000, lo: 0x01}, + {value: 0x44c3, lo: 0x9c, hi: 0x9c}, + // Block 0x44, offset 0x45 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0xaf, hi: 0xb1}, + // Block 0x45, offset 0x46 + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0xbf, hi: 0xbf}, + // Block 0x46, offset 0x47 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0xa0, hi: 0xbf}, + // Block 0x47, offset 0x48 + {value: 0x0000, lo: 0x05}, + {value: 0x80da, lo: 0xaa, hi: 0xaa}, + {value: 0x80e4, lo: 0xab, hi: 0xab}, + {value: 0x80e8, lo: 0xac, hi: 0xac}, + {value: 0x80de, lo: 0xad, hi: 0xad}, + {value: 0x80e0, lo: 0xae, hi: 0xaf}, + // Block 0x48, offset 0x49 + {value: 0x0000, lo: 0x02}, + {value: 0x80e6, lo: 0xaf, hi: 0xaf}, + {value: 0x80e6, lo: 0xb4, hi: 0xbd}, + // Block 0x49, offset 0x4a + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0x9f, hi: 0x9f}, + // Block 0x4a, offset 0x4b + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0xb0, hi: 0xb1}, + // Block 0x4b, offset 0x4c + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0x86, hi: 0x86}, + // Block 0x4c, offset 0x4d + {value: 0x0000, lo: 0x02}, + {value: 0x8009, lo: 0x84, hi: 0x84}, + {value: 0x80e6, lo: 0xa0, hi: 0xb1}, + // Block 0x4d, offset 0x4e + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0xab, hi: 0xad}, + // Block 0x4e, offset 0x4f + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0x93, hi: 0x93}, + // Block 0x4f, offset 0x50 + {value: 0x0000, lo: 0x01}, + {value: 0x8007, lo: 0xb3, hi: 0xb3}, + // Block 0x50, offset 0x51 + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0x80, hi: 0x80}, + // Block 0x51, offset 0x52 + {value: 0x0000, lo: 0x05}, + {value: 0x80e6, lo: 0xb0, hi: 0xb0}, + {value: 0x80e6, lo: 0xb2, hi: 0xb3}, + {value: 0x80dc, lo: 0xb4, hi: 0xb4}, + {value: 0x80e6, lo: 0xb7, hi: 0xb8}, + {value: 0x80e6, lo: 0xbe, hi: 0xbf}, + // Block 0x52, offset 0x53 + {value: 0x0000, lo: 0x02}, + {value: 0x80e6, lo: 0x81, hi: 0x81}, + {value: 0x8009, lo: 0xb6, hi: 0xb6}, + // Block 0x53, offset 0x54 + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0xad, hi: 0xad}, + // Block 0x54, offset 0x55 + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0x80, hi: 0xbf}, + // Block 0x55, offset 0x56 + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0x80, hi: 0xa3}, + // Block 0x56, offset 0x57 + {value: 0x0006, lo: 0x0d}, + {value: 0x4376, lo: 0x9d, hi: 0x9d}, + {value: 0x801a, lo: 0x9e, hi: 0x9e}, + {value: 0x43e8, lo: 0x9f, hi: 0x9f}, + {value: 0x43d6, lo: 0xaa, hi: 0xab}, + {value: 0x44da, lo: 0xac, hi: 0xac}, + {value: 0x44e2, lo: 0xad, hi: 0xad}, + {value: 0x432e, lo: 0xae, hi: 0xb1}, + {value: 0x434c, lo: 0xb2, hi: 0xb4}, + {value: 0x4364, lo: 0xb5, hi: 0xb6}, + {value: 0x4370, lo: 0xb8, hi: 0xb8}, + {value: 0x437c, lo: 0xb9, hi: 0xbb}, + {value: 0x4394, lo: 0xbc, hi: 0xbc}, + {value: 0x439a, lo: 0xbe, hi: 0xbe}, + // Block 0x57, offset 0x58 + {value: 0x0006, lo: 0x08}, + {value: 0x43a0, lo: 0x80, hi: 0x81}, + {value: 0x43ac, lo: 0x83, hi: 0x84}, + {value: 0x43be, lo: 0x86, hi: 0x89}, + {value: 0x43e2, lo: 0x8a, hi: 0x8a}, + {value: 0x435e, lo: 0x8b, hi: 0x8b}, + {value: 0x4346, lo: 0x8c, hi: 0x8c}, + {value: 0x438e, lo: 0x8d, hi: 0x8d}, + {value: 0x43b8, lo: 0x8e, hi: 0x8e}, + // Block 0x58, offset 0x59 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0xa0, hi: 0xa6}, + // Block 0x59, offset 0x5a + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0xbd, hi: 0xbd}, + // Block 0x5a, offset 0x5b + {value: 0x00db, lo: 0x05}, + {value: 0x80dc, lo: 0x8d, hi: 0x8d}, + {value: 0x80e6, lo: 0x8f, hi: 0x8f}, + {value: 0x80e6, lo: 0xb8, hi: 0xb8}, + {value: 0x8001, lo: 0xb9, hi: 0xba}, + {value: 0x8009, lo: 0xbf, hi: 0xbf}, + // Block 0x5b, offset 0x5c + {value: 0x05fe, lo: 0x07}, + {value: 0x8800, lo: 0x99, hi: 0x99}, + {value: 0x4222, lo: 0x9a, hi: 0x9a}, + {value: 0x8800, lo: 0x9b, hi: 0x9b}, + {value: 0x422c, lo: 0x9c, hi: 0x9c}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x4236, lo: 0xab, hi: 0xab}, + {value: 0x8009, lo: 0xb9, hi: 0xba}, + // Block 0x5c, offset 0x5d + {value: 0x0000, lo: 0x06}, + {value: 0x80e6, lo: 0x80, hi: 0x82}, + {value: 0x8600, lo: 0xa7, hi: 0xa7}, + {value: 0x1993, lo: 0xae, hi: 0xae}, + {value: 0x199c, lo: 0xaf, hi: 0xaf}, + {value: 0x8800, lo: 0xb1, hi: 0xb2}, + {value: 0x8009, lo: 0xb3, hi: 0xb4}, + // Block 0x5d, offset 0x5e + {value: 0x0000, lo: 0x02}, + {value: 0x8009, lo: 0xb6, hi: 0xb6}, + {value: 0x8007, lo: 0xb7, hi: 0xb7}, + // Block 0x5e, offset 0x5f + {value: 0x0000, lo: 0x0c}, + {value: 0x45b2, lo: 0x9e, hi: 0x9e}, + {value: 0x45bc, lo: 0x9f, hi: 0x9f}, + {value: 0x45f0, lo: 0xa0, hi: 0xa0}, + {value: 0x45fe, lo: 0xa1, hi: 0xa1}, + {value: 0x460c, lo: 0xa2, hi: 0xa2}, + {value: 0x461a, lo: 0xa3, hi: 0xa3}, + {value: 0x4628, lo: 0xa4, hi: 0xa4}, + {value: 0x80d8, lo: 0xa5, hi: 0xa6}, + {value: 0x8001, lo: 0xa7, hi: 0xa9}, + {value: 0x80e2, lo: 0xad, hi: 0xad}, + {value: 0x80d8, lo: 0xae, hi: 0xb2}, + {value: 0x80dc, lo: 0xbb, hi: 0xbf}, + // Block 0x5f, offset 0x60 + {value: 0x0000, lo: 0x09}, + {value: 0x80dc, lo: 0x80, hi: 0x82}, + {value: 0x80e6, lo: 0x85, hi: 0x89}, + {value: 0x80dc, lo: 0x8a, hi: 0x8b}, + {value: 0x80e6, lo: 0xaa, hi: 0xad}, + {value: 0x45c6, lo: 0xbb, hi: 0xbb}, + {value: 0x45d0, lo: 0xbc, hi: 0xbc}, + {value: 0x4636, lo: 0xbd, hi: 0xbd}, + {value: 0x4652, lo: 0xbe, hi: 0xbe}, + {value: 0x4644, lo: 0xbf, hi: 0xbf}, + // Block 0x60, offset 0x61 + {value: 0x0000, lo: 0x01}, + {value: 0x4660, lo: 0x80, hi: 0x80}, + // Block 0x61, offset 0x62 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0x82, hi: 0x84}, +} + +// nfcLookup: 1088 bytes +// Block 0 is the null block. +var nfcLookup = [1088]uint8{ + // Block 0x0, offset 0x0 + // Block 0x1, offset 0x40 + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0x0c2: 0x2c, 0x0c3: 0x01, 0x0c4: 0x02, 0x0c5: 0x03, 0x0c6: 0x2d, 0x0c7: 0x04, + 0x0c8: 0x05, 0x0ca: 0x2e, 0x0cc: 0x06, 0x0cd: 0x07, 0x0ce: 0x08, 0x0cf: 0x2f, + 0x0d0: 0x09, 0x0d1: 0x30, 0x0d2: 0x31, 0x0d3: 0x0a, 0x0d6: 0x0b, 0x0d7: 0x32, + 0x0d8: 0x33, 0x0d9: 0x0c, 0x0db: 0x34, 0x0dc: 0x35, 0x0dd: 0x36, 0x0df: 0x37, + 0x0e0: 0x02, 0x0e1: 0x03, 0x0e2: 0x04, 0x0e3: 0x05, + 0x0ea: 0x06, 0x0eb: 0x07, 0x0ec: 0x07, 0x0ed: 0x08, 0x0ef: 0x09, + 0x0f0: 0x0e, + // Block 0x4, offset 0x100 + 0x120: 0x38, 0x121: 0x39, 0x123: 0x3a, 0x124: 0x3b, 0x125: 0x3c, 0x126: 0x3d, 0x127: 0x3e, + 0x128: 0x3f, 0x129: 0x40, 0x12a: 0x41, 0x12b: 0x42, 0x12c: 0x3d, 0x12d: 0x43, 0x12e: 0x44, 0x12f: 0x45, + 0x131: 0x46, 0x132: 0x47, 0x133: 0x48, 0x134: 0x49, 0x135: 0x4a, 0x137: 0x4b, + 0x138: 0x4c, 0x139: 0x4d, 0x13a: 0x4e, 0x13b: 0x4f, 0x13c: 0x50, 0x13d: 0x51, 0x13e: 0x52, 0x13f: 0x53, + // Block 0x5, offset 0x140 + 0x140: 0x54, 0x142: 0x55, 0x144: 0x56, 0x145: 0x57, 0x146: 0x58, 0x147: 0x59, + 0x14d: 0x5a, + 0x15c: 0x5b, 0x15f: 0x5c, + 0x162: 0x5d, 0x164: 0x5e, + 0x168: 0x5f, 0x169: 0x60, 0x16c: 0x0d, 0x16d: 0x61, 0x16e: 0x62, 0x16f: 0x63, + 0x170: 0x64, 0x173: 0x65, 0x177: 0x66, + 0x178: 0x0e, 0x179: 0x0f, 0x17a: 0x10, 0x17b: 0x11, 0x17c: 0x12, 0x17d: 0x13, 0x17e: 0x14, 0x17f: 0x15, + // Block 0x6, offset 0x180 + 0x180: 0x67, 0x183: 0x68, 0x184: 0x69, 0x186: 0x6a, 0x187: 0x6b, + 0x188: 0x6c, 0x189: 0x16, 0x18a: 0x17, 0x18b: 0x6d, 0x18c: 0x6e, + 0x1ab: 0x6f, + 0x1b3: 0x70, 0x1b5: 0x71, 0x1b7: 0x72, + // Block 0x7, offset 0x1c0 + 0x1c0: 0x73, 0x1c1: 0x18, 0x1c2: 0x19, 0x1c3: 0x1a, + // Block 0x8, offset 0x200 + 0x219: 0x74, 0x21a: 0x75, 0x21b: 0x76, + 0x220: 0x77, 0x223: 0x78, 0x224: 0x79, 0x225: 0x7a, 0x226: 0x7b, 0x227: 0x7c, + 0x22a: 0x7d, 0x22b: 0x7e, 0x22f: 0x7f, + 0x230: 0x80, 0x231: 0x80, 0x232: 0x80, 0x233: 0x80, 0x234: 0x80, 0x235: 0x80, 0x236: 0x80, 0x237: 0x80, + 0x238: 0x80, 0x239: 0x80, 0x23a: 0x80, 0x23b: 0x80, 0x23c: 0x80, 0x23d: 0x80, 0x23e: 0x80, 0x23f: 0x80, + // Block 0x9, offset 0x240 + 0x240: 0x80, 0x241: 0x80, 0x242: 0x80, 0x243: 0x80, 0x244: 0x80, 0x245: 0x80, 0x246: 0x80, 0x247: 0x80, + 0x248: 0x80, 0x249: 0x80, 0x24a: 0x80, 0x24b: 0x80, 0x24c: 0x80, 0x24d: 0x80, 0x24e: 0x80, 0x24f: 0x80, + 0x250: 0x80, 0x251: 0x80, 0x252: 0x80, 0x253: 0x80, 0x254: 0x80, 0x255: 0x80, 0x256: 0x80, 0x257: 0x80, + 0x258: 0x80, 0x259: 0x80, 0x25a: 0x80, 0x25b: 0x80, 0x25c: 0x80, 0x25d: 0x80, 0x25e: 0x80, 0x25f: 0x80, + 0x260: 0x80, 0x261: 0x80, 0x262: 0x80, 0x263: 0x80, 0x264: 0x80, 0x265: 0x80, 0x266: 0x80, 0x267: 0x80, + 0x268: 0x80, 0x269: 0x80, 0x26a: 0x80, 0x26b: 0x80, 0x26c: 0x80, 0x26d: 0x80, 0x26e: 0x80, 0x26f: 0x80, + 0x270: 0x80, 0x271: 0x80, 0x272: 0x80, 0x273: 0x80, 0x274: 0x80, 0x275: 0x80, 0x276: 0x80, 0x277: 0x80, + 0x278: 0x80, 0x279: 0x80, 0x27a: 0x80, 0x27b: 0x80, 0x27c: 0x80, 0x27d: 0x80, 0x27e: 0x80, 0x27f: 0x80, + // Block 0xa, offset 0x280 + 0x280: 0x80, 0x281: 0x80, 0x282: 0x80, 0x283: 0x80, 0x284: 0x80, 0x285: 0x80, 0x286: 0x80, 0x287: 0x80, + 0x288: 0x80, 0x289: 0x80, 0x28a: 0x80, 0x28b: 0x80, 0x28c: 0x80, 0x28d: 0x80, 0x28e: 0x80, 0x28f: 0x80, + 0x290: 0x80, 0x291: 0x80, 0x292: 0x80, 0x293: 0x80, 0x294: 0x80, 0x295: 0x80, 0x296: 0x80, 0x297: 0x80, + 0x298: 0x80, 0x299: 0x80, 0x29a: 0x80, 0x29b: 0x80, 0x29c: 0x80, 0x29d: 0x80, 0x29e: 0x81, + // Block 0xb, offset 0x2c0 + 0x2e4: 0x1b, 0x2e5: 0x1c, 0x2e6: 0x1d, 0x2e7: 0x1e, + 0x2e8: 0x1f, 0x2e9: 0x20, 0x2ea: 0x21, 0x2eb: 0x22, 0x2ec: 0x82, 0x2ed: 0x83, + 0x2f8: 0x84, + // Block 0xc, offset 0x300 + 0x307: 0x85, + 0x328: 0x86, + // Block 0xd, offset 0x340 + 0x341: 0x77, 0x342: 0x87, 0x344: 0x88, 0x347: 0x7c, + 0x35a: 0x89, + // Block 0xe, offset 0x380 + 0x385: 0x8a, 0x386: 0x8b, 0x387: 0x8c, + 0x389: 0x8d, + // Block 0xf, offset 0x3c0 + 0x3e0: 0x23, 0x3e1: 0x24, 0x3e2: 0x25, 0x3e3: 0x26, 0x3e4: 0x27, 0x3e5: 0x28, 0x3e6: 0x29, 0x3e7: 0x2a, + 0x3e8: 0x2b, + // Block 0x10, offset 0x400 + 0x410: 0x0a, 0x411: 0x0b, + 0x41d: 0x0c, + 0x42f: 0x0d, +} + +var nfcTrie = trie{nfcLookup[:], nfcValues[:], nfcSparseValues[:], nfcSparseOffset[:], 44} + +// nfkcValues: 5760 entries, 11520 bytes +// Block 2 is the null block. +var nfkcValues = [5760]uint16{ + // Block 0x0, offset 0x0 + 0x003c: 0x8800, 0x003d: 0x8800, 0x003e: 0x8800, + // Block 0x1, offset 0x40 + 0x0041: 0x8800, 0x0042: 0x8800, 0x0043: 0x8800, 0x0044: 0x8800, 0x0045: 0x8800, + 0x0046: 0x8800, 0x0047: 0x8800, 0x0048: 0x8800, 0x0049: 0x8800, 0x004a: 0x8800, 0x004b: 0x8800, + 0x004c: 0x8800, 0x004d: 0x8800, 0x004e: 0x8800, 0x004f: 0x8800, 0x0050: 0x8800, + 0x0052: 0x8800, 0x0053: 0x8800, 0x0054: 0x8800, 0x0055: 0x8800, 0x0056: 0x8800, 0x0057: 0x8800, + 0x0058: 0x8800, 0x0059: 0x8800, 0x005a: 0x8800, + 0x0061: 0x8800, 0x0062: 0x8800, 0x0063: 0x8800, + 0x0064: 0x8800, 0x0065: 0x8800, 0x0066: 0x8800, 0x0067: 0x8800, 0x0068: 0x8800, 0x0069: 0x8800, + 0x006a: 0x8800, 0x006b: 0x8800, 0x006c: 0x8800, 0x006d: 0x8800, 0x006e: 0x8800, 0x006f: 0x8800, + 0x0070: 0x8800, 0x0072: 0x8800, 0x0073: 0x8800, 0x0074: 0x8800, 0x0075: 0x8800, + 0x0076: 0x8800, 0x0077: 0x8800, 0x0078: 0x8800, 0x0079: 0x8800, 0x007a: 0x8800, + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0x00c0: 0x2f59, 0x00c1: 0x2f5e, 0x00c2: 0x466e, 0x00c3: 0x2f63, 0x00c4: 0x467d, 0x00c5: 0x4682, + 0x00c6: 0x8800, 0x00c7: 0x468c, 0x00c8: 0x2fcc, 0x00c9: 0x2fd1, 0x00ca: 0x4691, 0x00cb: 0x2fe5, + 0x00cc: 0x3058, 0x00cd: 0x305d, 0x00ce: 0x3062, 0x00cf: 0x46a5, 0x00d1: 0x30ee, + 0x00d2: 0x3111, 0x00d3: 0x3116, 0x00d4: 0x46af, 0x00d5: 0x46b4, 0x00d6: 0x46c3, + 0x00d8: 0x8800, 0x00d9: 0x319d, 0x00da: 0x31a2, 0x00db: 0x31a7, 0x00dc: 0x46f5, 0x00dd: 0x321f, + 0x00e0: 0x3265, 0x00e1: 0x326a, 0x00e2: 0x46ff, 0x00e3: 0x326f, + 0x00e4: 0x470e, 0x00e5: 0x4713, 0x00e6: 0x8800, 0x00e7: 0x471d, 0x00e8: 0x32d8, 0x00e9: 0x32dd, + 0x00ea: 0x4722, 0x00eb: 0x32f1, 0x00ec: 0x3369, 0x00ed: 0x336e, 0x00ee: 0x3373, 0x00ef: 0x4736, + 0x00f1: 0x33ff, 0x00f2: 0x3422, 0x00f3: 0x3427, 0x00f4: 0x4740, 0x00f5: 0x4745, + 0x00f6: 0x4754, 0x00f8: 0x8800, 0x00f9: 0x34b3, 0x00fa: 0x34b8, 0x00fb: 0x34bd, + 0x00fc: 0x4786, 0x00fd: 0x353a, 0x00ff: 0x3553, + // Block 0x4, offset 0x100 + 0x0100: 0x2f68, 0x0101: 0x3274, 0x0102: 0x4673, 0x0103: 0x4704, 0x0104: 0x2f86, 0x0105: 0x3292, + 0x0106: 0x2f9a, 0x0107: 0x32a6, 0x0108: 0x2f9f, 0x0109: 0x32ab, 0x010a: 0x2fa4, 0x010b: 0x32b0, + 0x010c: 0x2fa9, 0x010d: 0x32b5, 0x010e: 0x2fb3, 0x010f: 0x32bf, + 0x0112: 0x4696, 0x0113: 0x4727, 0x0114: 0x2fdb, 0x0115: 0x32e7, 0x0116: 0x2fe0, 0x0117: 0x32ec, + 0x0118: 0x2ffe, 0x0119: 0x330a, 0x011a: 0x2fef, 0x011b: 0x32fb, 0x011c: 0x3017, 0x011d: 0x3323, + 0x011e: 0x3021, 0x011f: 0x332d, 0x0120: 0x3026, 0x0121: 0x3332, 0x0122: 0x3030, 0x0123: 0x333c, + 0x0124: 0x3035, 0x0125: 0x3341, 0x0128: 0x3067, 0x0129: 0x3378, + 0x012a: 0x306c, 0x012b: 0x337d, 0x012c: 0x3071, 0x012d: 0x3382, 0x012e: 0x3094, 0x012f: 0x33a0, + 0x0130: 0x3076, 0x0132: 0x1a9f, 0x0133: 0x1b29, 0x0134: 0x309e, 0x0135: 0x33aa, + 0x0136: 0x30b2, 0x0137: 0x33c3, 0x0139: 0x30bc, 0x013a: 0x33cd, 0x013b: 0x30c6, + 0x013c: 0x33d7, 0x013d: 0x30c1, 0x013e: 0x33d2, 0x013f: 0x1cee, + // Block 0x5, offset 0x140 + 0x0140: 0x1d76, 0x0143: 0x30e9, 0x0144: 0x33fa, 0x0145: 0x3102, + 0x0146: 0x3413, 0x0147: 0x30f8, 0x0148: 0x3409, 0x0149: 0x1d9e, + 0x014c: 0x46b9, 0x014d: 0x474a, 0x014e: 0x311b, 0x014f: 0x342c, 0x0150: 0x3125, 0x0151: 0x3436, + 0x0154: 0x3143, 0x0155: 0x3454, 0x0156: 0x315c, 0x0157: 0x346d, + 0x0158: 0x314d, 0x0159: 0x345e, 0x015a: 0x46dc, 0x015b: 0x476d, 0x015c: 0x3166, 0x015d: 0x3477, + 0x015e: 0x3175, 0x015f: 0x3486, 0x0160: 0x46e1, 0x0161: 0x4772, 0x0162: 0x318e, 0x0163: 0x34a4, + 0x0164: 0x317f, 0x0165: 0x3495, 0x0168: 0x46eb, 0x0169: 0x477c, + 0x016a: 0x46f0, 0x016b: 0x4781, 0x016c: 0x31ac, 0x016d: 0x34c2, 0x016e: 0x31b6, 0x016f: 0x34cc, + 0x0170: 0x31bb, 0x0171: 0x34d1, 0x0172: 0x31d9, 0x0173: 0x34ef, 0x0174: 0x31fc, 0x0175: 0x3512, + 0x0176: 0x3224, 0x0177: 0x353f, 0x0178: 0x3238, 0x0179: 0x3247, 0x017a: 0x3567, 0x017b: 0x3251, + 0x017c: 0x3571, 0x017d: 0x3256, 0x017e: 0x3576, 0x017f: 0x00a7, + // Block 0x6, offset 0x180 + 0x0184: 0x2e7f, 0x0185: 0x2e85, + 0x0186: 0x2e8b, 0x0187: 0x1ab4, 0x0188: 0x1ab7, 0x0189: 0x1b4a, 0x018a: 0x1ac9, 0x018b: 0x1acc, + 0x018c: 0x1b80, 0x018d: 0x2f72, 0x018e: 0x327e, 0x018f: 0x3080, 0x0190: 0x338c, 0x0191: 0x312a, + 0x0192: 0x343b, 0x0193: 0x31c0, 0x0194: 0x34d6, 0x0195: 0x39b9, 0x0196: 0x3b48, 0x0197: 0x39b2, + 0x0198: 0x3b41, 0x0199: 0x39c0, 0x019a: 0x3b4f, 0x019b: 0x39ab, 0x019c: 0x3b3a, + 0x019e: 0x389a, 0x019f: 0x3a29, 0x01a0: 0x3893, 0x01a1: 0x3a22, 0x01a2: 0x359d, 0x01a3: 0x35af, + 0x01a6: 0x302b, 0x01a7: 0x3337, 0x01a8: 0x30a8, 0x01a9: 0x33b9, + 0x01aa: 0x46d2, 0x01ab: 0x4763, 0x01ac: 0x397a, 0x01ad: 0x3b09, 0x01ae: 0x35c1, 0x01af: 0x35c7, + 0x01b0: 0x33af, 0x01b1: 0x1a84, 0x01b2: 0x1a87, 0x01b3: 0x1b11, 0x01b4: 0x3012, 0x01b5: 0x331e, + 0x01b8: 0x30e4, 0x01b9: 0x33f5, 0x01ba: 0x38a1, 0x01bb: 0x3a30, + 0x01bc: 0x3597, 0x01bd: 0x35a9, 0x01be: 0x35a3, 0x01bf: 0x35b5, + // Block 0x7, offset 0x1c0 + 0x01c0: 0x2f77, 0x01c1: 0x3283, 0x01c2: 0x2f7c, 0x01c3: 0x3288, 0x01c4: 0x2ff4, 0x01c5: 0x3300, + 0x01c6: 0x2ff9, 0x01c7: 0x3305, 0x01c8: 0x3085, 0x01c9: 0x3391, 0x01ca: 0x308a, 0x01cb: 0x3396, + 0x01cc: 0x312f, 0x01cd: 0x3440, 0x01ce: 0x3134, 0x01cf: 0x3445, 0x01d0: 0x3152, 0x01d1: 0x3463, + 0x01d2: 0x3157, 0x01d3: 0x3468, 0x01d4: 0x31c5, 0x01d5: 0x34db, 0x01d6: 0x31ca, 0x01d7: 0x34e0, + 0x01d8: 0x3170, 0x01d9: 0x3481, 0x01da: 0x3189, 0x01db: 0x349f, + 0x01de: 0x3044, 0x01df: 0x3350, + 0x01e6: 0x4678, 0x01e7: 0x4709, 0x01e8: 0x46a0, 0x01e9: 0x4731, + 0x01ea: 0x3949, 0x01eb: 0x3ad8, 0x01ec: 0x3926, 0x01ed: 0x3ab5, 0x01ee: 0x46be, 0x01ef: 0x474f, + 0x01f0: 0x3942, 0x01f1: 0x3ad1, 0x01f2: 0x322e, 0x01f3: 0x3549, + // Block 0x8, offset 0x200 + 0x0200: 0x86e6, 0x0201: 0x86e6, 0x0202: 0x86e6, 0x0203: 0x86e6, 0x0204: 0x86e6, 0x0205: 0x80e6, + 0x0206: 0x86e6, 0x0207: 0x86e6, 0x0208: 0x86e6, 0x0209: 0x86e6, 0x020a: 0x86e6, 0x020b: 0x86e6, + 0x020c: 0x86e6, 0x020d: 0x80e6, 0x020e: 0x80e6, 0x020f: 0x86e6, 0x0210: 0x80e6, 0x0211: 0x86e6, + 0x0212: 0x80e6, 0x0213: 0x86e6, 0x0214: 0x86e6, 0x0215: 0x80e8, 0x0216: 0x80dc, 0x0217: 0x80dc, + 0x0218: 0x80dc, 0x0219: 0x80dc, 0x021a: 0x80e8, 0x021b: 0x86d8, 0x021c: 0x80dc, 0x021d: 0x80dc, + 0x021e: 0x80dc, 0x021f: 0x80dc, 0x0220: 0x80dc, 0x0221: 0x80ca, 0x0222: 0x80ca, 0x0223: 0x86dc, + 0x0224: 0x86dc, 0x0225: 0x86dc, 0x0226: 0x86dc, 0x0227: 0x86ca, 0x0228: 0x86ca, 0x0229: 0x80dc, + 0x022a: 0x80dc, 0x022b: 0x80dc, 0x022c: 0x80dc, 0x022d: 0x86dc, 0x022e: 0x86dc, 0x022f: 0x80dc, + 0x0230: 0x86dc, 0x0231: 0x86dc, 0x0232: 0x80dc, 0x0233: 0x80dc, 0x0234: 0x8001, 0x0235: 0x8001, + 0x0236: 0x8001, 0x0237: 0x8001, 0x0238: 0x8601, 0x0239: 0x80dc, 0x023a: 0x80dc, 0x023b: 0x80dc, + 0x023c: 0x80dc, 0x023d: 0x80e6, 0x023e: 0x80e6, 0x023f: 0x80e6, + // Block 0x9, offset 0x240 + 0x0240: 0x4994, 0x0241: 0x4999, 0x0242: 0x86e6, 0x0243: 0x499e, 0x0244: 0x49a3, 0x0245: 0x86f0, + 0x0246: 0x80e6, 0x0247: 0x80dc, 0x0248: 0x80dc, 0x0249: 0x80dc, 0x024a: 0x80e6, 0x024b: 0x80e6, + 0x024c: 0x80e6, 0x024d: 0x80dc, 0x024e: 0x80dc, 0x0250: 0x80e6, 0x0251: 0x80e6, + 0x0252: 0x80e6, 0x0253: 0x80dc, 0x0254: 0x80dc, 0x0255: 0x80dc, 0x0256: 0x80dc, 0x0257: 0x80e6, + 0x0258: 0x80e8, 0x0259: 0x80dc, 0x025a: 0x80dc, 0x025b: 0x80e6, 0x025c: 0x80e9, 0x025d: 0x80ea, + 0x025e: 0x80ea, 0x025f: 0x80e9, 0x0260: 0x80ea, 0x0261: 0x80ea, 0x0262: 0x80e9, 0x0263: 0x80e6, + 0x0264: 0x80e6, 0x0265: 0x80e6, 0x0266: 0x80e6, 0x0267: 0x80e6, 0x0268: 0x80e6, 0x0269: 0x80e6, + 0x026a: 0x80e6, 0x026b: 0x80e6, 0x026c: 0x80e6, 0x026d: 0x80e6, 0x026e: 0x80e6, 0x026f: 0x80e6, + 0x0274: 0x0170, + 0x027a: 0x428b, + 0x027e: 0x0037, + // Block 0xa, offset 0x280 + 0x0284: 0x4240, 0x0285: 0x4461, + 0x0286: 0x35d3, 0x0287: 0x00d1, 0x0288: 0x35f1, 0x0289: 0x35fd, 0x028a: 0x360f, + 0x028c: 0x362d, 0x028e: 0x363f, 0x028f: 0x365d, 0x0290: 0x3df2, 0x0291: 0x8800, + 0x0295: 0x8800, 0x0297: 0x8800, + 0x0299: 0x8800, + 0x029f: 0x8800, 0x02a1: 0x8800, + 0x02a5: 0x8800, 0x02a9: 0x8800, + 0x02aa: 0x3621, 0x02ab: 0x3651, 0x02ac: 0x47e4, 0x02ad: 0x3681, 0x02ae: 0x480e, 0x02af: 0x3693, + 0x02b0: 0x3e5a, 0x02b1: 0x8800, 0x02b5: 0x8800, + 0x02b7: 0x8800, 0x02b9: 0x8800, + 0x02bf: 0x8800, + // Block 0xb, offset 0x2c0 + 0x02c1: 0x8800, 0x02c5: 0x8800, + 0x02c9: 0x8800, 0x02ca: 0x4826, 0x02cb: 0x4844, + 0x02cc: 0x36b1, 0x02cd: 0x36c9, 0x02ce: 0x485c, 0x02d0: 0x01be, 0x02d1: 0x01d0, + 0x02d2: 0x01ac, 0x02d3: 0x42f2, 0x02d4: 0x42f8, 0x02d5: 0x01fa, 0x02d6: 0x01e8, + 0x02f0: 0x01d6, 0x02f1: 0x01eb, 0x02f2: 0x01ee, 0x02f4: 0x0188, 0x02f5: 0x01c7, + 0x02f9: 0x01a6, + // Block 0xc, offset 0x300 + 0x0300: 0x370b, 0x0301: 0x3717, 0x0303: 0x3705, + 0x0306: 0x8800, 0x0307: 0x36f3, + 0x030c: 0x3747, 0x030d: 0x372f, 0x030e: 0x3759, 0x0310: 0x8800, + 0x0313: 0x8800, 0x0315: 0x8800, 0x0316: 0x8800, 0x0317: 0x8800, + 0x0318: 0x8800, 0x0319: 0x373b, 0x031a: 0x8800, + 0x031e: 0x8800, 0x0323: 0x8800, + 0x0327: 0x8800, + 0x032b: 0x8800, 0x032d: 0x8800, + 0x0330: 0x8800, 0x0333: 0x8800, 0x0335: 0x8800, + 0x0336: 0x8800, 0x0337: 0x8800, 0x0338: 0x8800, 0x0339: 0x37bf, 0x033a: 0x8800, + 0x033e: 0x8800, + // Block 0xd, offset 0x340 + 0x0341: 0x371d, 0x0342: 0x37a1, + 0x0350: 0x36f9, 0x0351: 0x377d, + 0x0352: 0x36ff, 0x0353: 0x3783, 0x0356: 0x3711, 0x0357: 0x3795, + 0x0358: 0x8800, 0x0359: 0x8800, 0x035a: 0x3813, 0x035b: 0x3819, 0x035c: 0x3723, 0x035d: 0x37a7, + 0x035e: 0x3729, 0x035f: 0x37ad, 0x0362: 0x3735, 0x0363: 0x37b9, + 0x0364: 0x3741, 0x0365: 0x37c5, 0x0366: 0x374d, 0x0367: 0x37d1, 0x0368: 0x8800, 0x0369: 0x8800, + 0x036a: 0x381f, 0x036b: 0x3825, 0x036c: 0x3777, 0x036d: 0x37fb, 0x036e: 0x3753, 0x036f: 0x37d7, + 0x0370: 0x375f, 0x0371: 0x37e3, 0x0372: 0x3765, 0x0373: 0x37e9, 0x0374: 0x376b, 0x0375: 0x37ef, + 0x0378: 0x3771, 0x0379: 0x37f5, + // Block 0xe, offset 0x380 + 0x0387: 0x1ea3, + 0x0391: 0x80dc, + 0x0392: 0x80e6, 0x0393: 0x80e6, 0x0394: 0x80e6, 0x0395: 0x80e6, 0x0396: 0x80dc, 0x0397: 0x80e6, + 0x0398: 0x80e6, 0x0399: 0x80e6, 0x039a: 0x80de, 0x039b: 0x80dc, 0x039c: 0x80e6, 0x039d: 0x80e6, + 0x039e: 0x80e6, 0x039f: 0x80e6, 0x03a0: 0x80e6, 0x03a1: 0x80e6, 0x03a2: 0x80dc, 0x03a3: 0x80dc, + 0x03a4: 0x80dc, 0x03a5: 0x80dc, 0x03a6: 0x80dc, 0x03a7: 0x80dc, 0x03a8: 0x80e6, 0x03a9: 0x80e6, + 0x03aa: 0x80dc, 0x03ab: 0x80e6, 0x03ac: 0x80e6, 0x03ad: 0x80de, 0x03ae: 0x80e4, 0x03af: 0x80e6, + 0x03b0: 0x800a, 0x03b1: 0x800b, 0x03b2: 0x800c, 0x03b3: 0x800d, 0x03b4: 0x800e, 0x03b5: 0x800f, + 0x03b6: 0x8010, 0x03b7: 0x8011, 0x03b8: 0x8012, 0x03b9: 0x8013, 0x03ba: 0x8013, 0x03bb: 0x8014, + 0x03bc: 0x8015, 0x03bd: 0x8016, 0x03bf: 0x8017, + // Block 0xf, offset 0x3c0 + 0x03c8: 0x8800, 0x03ca: 0x8800, 0x03cb: 0x801b, + 0x03cc: 0x801c, 0x03cd: 0x801d, 0x03ce: 0x801e, 0x03cf: 0x801f, 0x03d0: 0x8020, 0x03d1: 0x8021, + 0x03d2: 0x8022, 0x03d3: 0x86e6, 0x03d4: 0x86e6, 0x03d5: 0x86dc, 0x03d6: 0x80dc, 0x03d7: 0x80e6, + 0x03d8: 0x80e6, 0x03d9: 0x80e6, 0x03da: 0x80e6, 0x03db: 0x80e6, 0x03dc: 0x80dc, 0x03dd: 0x80e6, + 0x03de: 0x80e6, 0x03df: 0x80dc, + 0x03f0: 0x8023, 0x03f5: 0x1ec6, + 0x03f6: 0x2155, 0x03f7: 0x2191, 0x03f8: 0x218c, + // Block 0x10, offset 0x400 + 0x0405: 0x8800, + 0x0406: 0x1946, 0x0407: 0x8800, 0x0408: 0x194d, 0x0409: 0x8800, 0x040a: 0x1954, 0x040b: 0x8800, + 0x040c: 0x195b, 0x040d: 0x8800, 0x040e: 0x1962, 0x0411: 0x8800, + 0x0412: 0x1969, + 0x0434: 0x8007, 0x0435: 0x8600, + 0x043a: 0x8800, 0x043b: 0x1970, + 0x043c: 0x8800, 0x043d: 0x1977, 0x043e: 0x8800, 0x043f: 0x8800, + // Block 0x11, offset 0x440 + 0x0440: 0x0069, 0x0441: 0x006b, 0x0442: 0x006f, 0x0443: 0x0083, 0x0444: 0x00f8, 0x0445: 0x00fb, + 0x0446: 0x0485, 0x0447: 0x0085, 0x0448: 0x0089, 0x0449: 0x008b, 0x044a: 0x0107, 0x044b: 0x010a, + 0x044c: 0x010d, 0x044d: 0x008f, 0x044f: 0x0097, 0x0450: 0x009b, 0x0451: 0x00e3, + 0x0452: 0x009f, 0x0453: 0x0101, 0x0454: 0x0489, 0x0455: 0x048d, 0x0456: 0x00a1, 0x0457: 0x00a9, + 0x0458: 0x00ab, 0x0459: 0x0495, 0x045a: 0x012b, 0x045b: 0x00ad, 0x045c: 0x0499, 0x045d: 0x01be, + 0x045e: 0x01c1, 0x045f: 0x01c4, 0x0460: 0x01fa, 0x0461: 0x01fd, 0x0462: 0x0093, 0x0463: 0x00a5, + 0x0464: 0x00ab, 0x0465: 0x00ad, 0x0466: 0x01be, 0x0467: 0x01c1, 0x0468: 0x01eb, 0x0469: 0x01fa, + 0x046a: 0x01fd, + 0x0478: 0x020c, + // Block 0x12, offset 0x480 + 0x049b: 0x00fe, 0x049c: 0x0087, 0x049d: 0x0104, + 0x049e: 0x00d7, 0x049f: 0x010d, 0x04a0: 0x008d, 0x04a1: 0x0110, 0x04a2: 0x0113, 0x04a3: 0x0119, + 0x04a4: 0x011f, 0x04a5: 0x0122, 0x04a6: 0x0125, 0x04a7: 0x049d, 0x04a8: 0x016a, 0x04a9: 0x0128, + 0x04aa: 0x04a1, 0x04ab: 0x016d, 0x04ac: 0x0131, 0x04ad: 0x012e, 0x04ae: 0x0134, 0x04af: 0x0137, + 0x04b0: 0x013a, 0x04b1: 0x013d, 0x04b2: 0x0140, 0x04b3: 0x014c, 0x04b4: 0x014f, 0x04b5: 0x00ef, + 0x04b6: 0x0152, 0x04b7: 0x0155, 0x04b8: 0x0491, 0x04b9: 0x0158, 0x04ba: 0x015b, 0x04bb: 0x00b5, + 0x04bc: 0x015e, 0x04bd: 0x0161, 0x04be: 0x0164, 0x04bf: 0x01d0, + // Block 0x13, offset 0x4c0 + 0x04c0: 0x2f81, 0x04c1: 0x328d, 0x04c2: 0x2f8b, 0x04c3: 0x3297, 0x04c4: 0x2f90, 0x04c5: 0x329c, + 0x04c6: 0x2f95, 0x04c7: 0x32a1, 0x04c8: 0x38b6, 0x04c9: 0x3a45, 0x04ca: 0x2fae, 0x04cb: 0x32ba, + 0x04cc: 0x2fb8, 0x04cd: 0x32c4, 0x04ce: 0x2fc7, 0x04cf: 0x32d3, 0x04d0: 0x2fbd, 0x04d1: 0x32c9, + 0x04d2: 0x2fc2, 0x04d3: 0x32ce, 0x04d4: 0x38d9, 0x04d5: 0x3a68, 0x04d6: 0x38e0, 0x04d7: 0x3a6f, + 0x04d8: 0x3003, 0x04d9: 0x330f, 0x04da: 0x3008, 0x04db: 0x3314, 0x04dc: 0x38ee, 0x04dd: 0x3a7d, + 0x04de: 0x300d, 0x04df: 0x3319, 0x04e0: 0x301c, 0x04e1: 0x3328, 0x04e2: 0x303a, 0x04e3: 0x3346, + 0x04e4: 0x3049, 0x04e5: 0x3355, 0x04e6: 0x303f, 0x04e7: 0x334b, 0x04e8: 0x304e, 0x04e9: 0x335a, + 0x04ea: 0x3053, 0x04eb: 0x335f, 0x04ec: 0x3099, 0x04ed: 0x33a5, 0x04ee: 0x38f5, 0x04ef: 0x3a84, + 0x04f0: 0x30a3, 0x04f1: 0x33b4, 0x04f2: 0x30ad, 0x04f3: 0x33be, 0x04f4: 0x30b7, 0x04f5: 0x33c8, + 0x04f6: 0x46aa, 0x04f7: 0x473b, 0x04f8: 0x38fc, 0x04f9: 0x3a8b, 0x04fa: 0x30d0, 0x04fb: 0x33e1, + 0x04fc: 0x30cb, 0x04fd: 0x33dc, 0x04fe: 0x30d5, 0x04ff: 0x33e6, + // Block 0x14, offset 0x500 + 0x0500: 0x30da, 0x0501: 0x33eb, 0x0502: 0x30df, 0x0503: 0x33f0, 0x0504: 0x30f3, 0x0505: 0x3404, + 0x0506: 0x30fd, 0x0507: 0x340e, 0x0508: 0x310c, 0x0509: 0x341d, 0x050a: 0x3107, 0x050b: 0x3418, + 0x050c: 0x391f, 0x050d: 0x3aae, 0x050e: 0x392d, 0x050f: 0x3abc, 0x0510: 0x3934, 0x0511: 0x3ac3, + 0x0512: 0x393b, 0x0513: 0x3aca, 0x0514: 0x3139, 0x0515: 0x344a, 0x0516: 0x313e, 0x0517: 0x344f, + 0x0518: 0x3148, 0x0519: 0x3459, 0x051a: 0x46d7, 0x051b: 0x4768, 0x051c: 0x3981, 0x051d: 0x3b10, + 0x051e: 0x3161, 0x051f: 0x3472, 0x0520: 0x316b, 0x0521: 0x347c, 0x0522: 0x46e6, 0x0523: 0x4777, + 0x0524: 0x3988, 0x0525: 0x3b17, 0x0526: 0x398f, 0x0527: 0x3b1e, 0x0528: 0x3996, 0x0529: 0x3b25, + 0x052a: 0x317a, 0x052b: 0x348b, 0x052c: 0x3184, 0x052d: 0x349a, 0x052e: 0x3198, 0x052f: 0x34ae, + 0x0530: 0x3193, 0x0531: 0x34a9, 0x0532: 0x31d4, 0x0533: 0x34ea, 0x0534: 0x31e3, 0x0535: 0x34f9, + 0x0536: 0x31de, 0x0537: 0x34f4, 0x0538: 0x399d, 0x0539: 0x3b2c, 0x053a: 0x39a4, 0x053b: 0x3b33, + 0x053c: 0x31e8, 0x053d: 0x34fe, 0x053e: 0x31ed, 0x053f: 0x3503, + // Block 0x15, offset 0x540 + 0x0540: 0x31f2, 0x0541: 0x3508, 0x0542: 0x31f7, 0x0543: 0x350d, 0x0544: 0x3206, 0x0545: 0x351c, + 0x0546: 0x3201, 0x0547: 0x3517, 0x0548: 0x320b, 0x0549: 0x3526, 0x054a: 0x3210, 0x054b: 0x352b, + 0x054c: 0x3215, 0x054d: 0x3530, 0x054e: 0x3233, 0x054f: 0x354e, 0x0550: 0x324c, 0x0551: 0x356c, + 0x0552: 0x325b, 0x0553: 0x357b, 0x0554: 0x3260, 0x0555: 0x3580, 0x0556: 0x3364, 0x0557: 0x3490, + 0x0558: 0x3521, 0x0559: 0x355d, 0x055a: 0x1d22, 0x055b: 0x42bd, + 0x0560: 0x4687, 0x0561: 0x4718, 0x0562: 0x2f6d, 0x0563: 0x3279, + 0x0564: 0x3862, 0x0565: 0x39f1, 0x0566: 0x385b, 0x0567: 0x39ea, 0x0568: 0x3870, 0x0569: 0x39ff, + 0x056a: 0x3869, 0x056b: 0x39f8, 0x056c: 0x38a8, 0x056d: 0x3a37, 0x056e: 0x387e, 0x056f: 0x3a0d, + 0x0570: 0x3877, 0x0571: 0x3a06, 0x0572: 0x388c, 0x0573: 0x3a1b, 0x0574: 0x3885, 0x0575: 0x3a14, + 0x0576: 0x38af, 0x0577: 0x3a3e, 0x0578: 0x469b, 0x0579: 0x472c, 0x057a: 0x2fea, 0x057b: 0x32f6, + 0x057c: 0x2fd6, 0x057d: 0x32e2, 0x057e: 0x38c4, 0x057f: 0x3a53, + // Block 0x16, offset 0x580 + 0x0580: 0x38bd, 0x0581: 0x3a4c, 0x0582: 0x38d2, 0x0583: 0x3a61, 0x0584: 0x38cb, 0x0585: 0x3a5a, + 0x0586: 0x38e7, 0x0587: 0x3a76, 0x0588: 0x307b, 0x0589: 0x3387, 0x058a: 0x308f, 0x058b: 0x339b, + 0x058c: 0x46cd, 0x058d: 0x475e, 0x058e: 0x3120, 0x058f: 0x3431, 0x0590: 0x390a, 0x0591: 0x3a99, + 0x0592: 0x3903, 0x0593: 0x3a92, 0x0594: 0x3918, 0x0595: 0x3aa7, 0x0596: 0x3911, 0x0597: 0x3aa0, + 0x0598: 0x3973, 0x0599: 0x3b02, 0x059a: 0x3957, 0x059b: 0x3ae6, 0x059c: 0x3950, 0x059d: 0x3adf, + 0x059e: 0x3965, 0x059f: 0x3af4, 0x05a0: 0x395e, 0x05a1: 0x3aed, 0x05a2: 0x396c, 0x05a3: 0x3afb, + 0x05a4: 0x31cf, 0x05a5: 0x34e5, 0x05a6: 0x31b1, 0x05a7: 0x34c7, 0x05a8: 0x39ce, 0x05a9: 0x3b5d, + 0x05aa: 0x39c7, 0x05ab: 0x3b56, 0x05ac: 0x39dc, 0x05ad: 0x3b6b, 0x05ae: 0x39d5, 0x05af: 0x3b64, + 0x05b0: 0x39e3, 0x05b1: 0x3b72, 0x05b2: 0x321a, 0x05b3: 0x3535, 0x05b4: 0x3242, 0x05b5: 0x3562, + 0x05b6: 0x323d, 0x05b7: 0x3558, 0x05b8: 0x3229, 0x05b9: 0x3544, + // Block 0x17, offset 0x5c0 + 0x05c0: 0x47ea, 0x05c1: 0x47f0, 0x05c2: 0x4904, 0x05c3: 0x491c, 0x05c4: 0x490c, 0x05c5: 0x4924, + 0x05c6: 0x4914, 0x05c7: 0x492c, 0x05c8: 0x4790, 0x05c9: 0x4796, 0x05ca: 0x4874, 0x05cb: 0x488c, + 0x05cc: 0x487c, 0x05cd: 0x4894, 0x05ce: 0x4884, 0x05cf: 0x489c, 0x05d0: 0x47fc, 0x05d1: 0x4802, + 0x05d2: 0x3da2, 0x05d3: 0x3db2, 0x05d4: 0x3daa, 0x05d5: 0x3dba, + 0x05d8: 0x479c, 0x05d9: 0x47a2, 0x05da: 0x3cd2, 0x05db: 0x3ce2, 0x05dc: 0x3cda, 0x05dd: 0x3cea, + 0x05e0: 0x4814, 0x05e1: 0x481a, 0x05e2: 0x4934, 0x05e3: 0x494c, + 0x05e4: 0x493c, 0x05e5: 0x4954, 0x05e6: 0x4944, 0x05e7: 0x495c, 0x05e8: 0x47a8, 0x05e9: 0x47ae, + 0x05ea: 0x48a4, 0x05eb: 0x48bc, 0x05ec: 0x48ac, 0x05ed: 0x48c4, 0x05ee: 0x48b4, 0x05ef: 0x48cc, + 0x05f0: 0x482c, 0x05f1: 0x4832, 0x05f2: 0x3e02, 0x05f3: 0x3e1a, 0x05f4: 0x3e0a, 0x05f5: 0x3e22, + 0x05f6: 0x3e12, 0x05f7: 0x3e2a, 0x05f8: 0x47b4, 0x05f9: 0x47ba, 0x05fa: 0x3d02, 0x05fb: 0x3d1a, + 0x05fc: 0x3d0a, 0x05fd: 0x3d22, 0x05fe: 0x3d12, 0x05ff: 0x3d2a, + // Block 0x18, offset 0x600 + 0x0600: 0x4838, 0x0601: 0x483e, 0x0602: 0x3e32, 0x0603: 0x3e42, 0x0604: 0x3e3a, 0x0605: 0x3e4a, + 0x0608: 0x47c0, 0x0609: 0x47c6, 0x060a: 0x3d32, 0x060b: 0x3d42, + 0x060c: 0x3d3a, 0x060d: 0x3d4a, 0x0610: 0x484a, 0x0611: 0x4850, + 0x0612: 0x3e6a, 0x0613: 0x3e82, 0x0614: 0x3e72, 0x0615: 0x3e8a, 0x0616: 0x3e7a, 0x0617: 0x3e92, + 0x0619: 0x47cc, 0x061b: 0x3d52, 0x061d: 0x3d5a, + 0x061f: 0x3d62, 0x0620: 0x4862, 0x0621: 0x4868, 0x0622: 0x4964, 0x0623: 0x497c, + 0x0624: 0x496c, 0x0625: 0x4984, 0x0626: 0x4974, 0x0627: 0x498c, 0x0628: 0x47d2, 0x0629: 0x47d8, + 0x062a: 0x48d4, 0x062b: 0x48ec, 0x062c: 0x48dc, 0x062d: 0x48f4, 0x062e: 0x48e4, 0x062f: 0x48fc, + 0x0630: 0x47de, 0x0631: 0x4304, 0x0632: 0x367b, 0x0633: 0x430a, 0x0634: 0x4808, 0x0635: 0x4310, + 0x0636: 0x368d, 0x0637: 0x4316, 0x0638: 0x36ab, 0x0639: 0x431c, 0x063a: 0x36c3, 0x063b: 0x4322, + 0x063c: 0x4856, 0x063d: 0x4328, + // Block 0x19, offset 0x640 + 0x0640: 0x3d8a, 0x0641: 0x3d92, 0x0642: 0x416e, 0x0643: 0x418c, 0x0644: 0x4178, 0x0645: 0x4196, + 0x0646: 0x4182, 0x0647: 0x41a0, 0x0648: 0x3cc2, 0x0649: 0x3cca, 0x064a: 0x40ba, 0x064b: 0x40d8, + 0x064c: 0x40c4, 0x064d: 0x40e2, 0x064e: 0x40ce, 0x064f: 0x40ec, 0x0650: 0x3dd2, 0x0651: 0x3dda, + 0x0652: 0x41aa, 0x0653: 0x41c8, 0x0654: 0x41b4, 0x0655: 0x41d2, 0x0656: 0x41be, 0x0657: 0x41dc, + 0x0658: 0x3cf2, 0x0659: 0x3cfa, 0x065a: 0x40f6, 0x065b: 0x4114, 0x065c: 0x4100, 0x065d: 0x411e, + 0x065e: 0x410a, 0x065f: 0x4128, 0x0660: 0x3eaa, 0x0661: 0x3eb2, 0x0662: 0x41e6, 0x0663: 0x4204, + 0x0664: 0x41f0, 0x0665: 0x420e, 0x0666: 0x41fa, 0x0667: 0x4218, 0x0668: 0x3d6a, 0x0669: 0x3d72, + 0x066a: 0x4132, 0x066b: 0x4150, 0x066c: 0x413c, 0x066d: 0x415a, 0x066e: 0x4146, 0x066f: 0x4164, + 0x0670: 0x366f, 0x0671: 0x3669, 0x0672: 0x3d7a, 0x0673: 0x3675, 0x0674: 0x3d82, + 0x0676: 0x47f6, 0x0677: 0x3d9a, 0x0678: 0x35df, 0x0679: 0x35d9, 0x067a: 0x35cd, 0x067b: 0x42d4, + 0x067c: 0x35e5, 0x067d: 0x426d, 0x067e: 0x01d3, 0x067f: 0x426d, + // Block 0x1a, offset 0x680 + 0x0680: 0x4286, 0x0681: 0x4468, 0x0682: 0x3dc2, 0x0683: 0x3687, 0x0684: 0x3dca, + 0x0686: 0x4820, 0x0687: 0x3de2, 0x0688: 0x35eb, 0x0689: 0x42da, 0x068a: 0x35f7, 0x068b: 0x42e0, + 0x068c: 0x3603, 0x068d: 0x446f, 0x068e: 0x4476, 0x068f: 0x447d, 0x0690: 0x369f, 0x0691: 0x3699, + 0x0692: 0x3dea, 0x0693: 0x44ca, 0x0696: 0x36a5, 0x0697: 0x3dfa, + 0x0698: 0x361b, 0x0699: 0x3615, 0x069a: 0x3609, 0x069b: 0x42e6, 0x069d: 0x4484, + 0x069e: 0x448b, 0x069f: 0x4492, 0x06a0: 0x36d5, 0x06a1: 0x36cf, 0x06a2: 0x3e52, 0x06a3: 0x44d2, + 0x06a4: 0x36b7, 0x06a5: 0x36bd, 0x06a6: 0x36db, 0x06a7: 0x3e62, 0x06a8: 0x364b, 0x06a9: 0x3645, + 0x06aa: 0x3639, 0x06ab: 0x42f2, 0x06ac: 0x3633, 0x06ad: 0x445a, 0x06ae: 0x4461, 0x06af: 0x0081, + 0x06b2: 0x3e9a, 0x06b3: 0x36e1, 0x06b4: 0x3ea2, + 0x06b6: 0x486e, 0x06b7: 0x3eba, 0x06b8: 0x3627, 0x06b9: 0x42ec, 0x06ba: 0x3657, 0x06bb: 0x42fe, + 0x06bc: 0x3663, 0x06bd: 0x4240, 0x06be: 0x4272, + // Block 0x1b, offset 0x6c0 + 0x06c0: 0x1d1a, 0x06c1: 0x1d1e, 0x06c2: 0x0047, 0x06c3: 0x1d96, 0x06c5: 0x1d2a, + 0x06c6: 0x1d2e, 0x06c7: 0x00ec, 0x06c9: 0x1d9a, 0x06ca: 0x008f, 0x06cb: 0x0051, + 0x06cc: 0x0051, 0x06cd: 0x0051, 0x06ce: 0x0091, 0x06cf: 0x00dd, 0x06d0: 0x0053, 0x06d1: 0x0053, + 0x06d2: 0x0059, 0x06d3: 0x0099, 0x06d5: 0x005d, 0x06d6: 0x1acf, + 0x06d9: 0x0061, 0x06da: 0x0063, 0x06db: 0x0065, 0x06dc: 0x0065, 0x06dd: 0x0065, + 0x06e0: 0x1ae1, 0x06e1: 0x1d0a, 0x06e2: 0x1aea, + 0x06e4: 0x0075, 0x06e6: 0x01b8, 0x06e8: 0x0075, + 0x06ea: 0x0057, 0x06eb: 0x42b8, 0x06ec: 0x0045, 0x06ed: 0x0047, 0x06ef: 0x008b, + 0x06f0: 0x004b, 0x06f1: 0x004d, 0x06f3: 0x005b, 0x06f4: 0x009f, 0x06f5: 0x020f, + 0x06f6: 0x0212, 0x06f7: 0x0215, 0x06f8: 0x0218, 0x06f9: 0x0093, 0x06fb: 0x1cda, + 0x06fc: 0x01e8, 0x06fd: 0x01c1, 0x06fe: 0x0179, 0x06ff: 0x01a0, + // Block 0x1c, offset 0x700 + 0x0700: 0x04d5, 0x0705: 0x0049, + 0x0706: 0x0089, 0x0707: 0x008b, 0x0708: 0x0093, 0x0709: 0x0095, + 0x0710: 0x2370, 0x0711: 0x237c, + 0x0712: 0x2430, 0x0713: 0x2358, 0x0714: 0x23dc, 0x0715: 0x2364, 0x0716: 0x23e2, 0x0717: 0x23fa, + 0x0718: 0x2406, 0x0719: 0x236a, 0x071a: 0x240c, 0x071b: 0x2376, 0x071c: 0x2400, 0x071d: 0x2412, + 0x071e: 0x2418, 0x071f: 0x1dfe, 0x0720: 0x0053, 0x0721: 0x1a9c, 0x0722: 0x1ce6, 0x0723: 0x1aa5, + 0x0724: 0x006d, 0x0725: 0x1aed, 0x0726: 0x1d12, 0x0727: 0x1e8a, 0x0728: 0x1aa8, 0x0729: 0x0071, + 0x072a: 0x1af9, 0x072b: 0x1d16, 0x072c: 0x0059, 0x072d: 0x0047, 0x072e: 0x0049, 0x072f: 0x005b, + 0x0730: 0x0093, 0x0731: 0x1b26, 0x0732: 0x1d5a, 0x0733: 0x1b2f, 0x0734: 0x00ad, 0x0735: 0x1ba4, + 0x0736: 0x1d8e, 0x0737: 0x1e9e, 0x0738: 0x1b32, 0x0739: 0x00b1, 0x073a: 0x1ba7, 0x073b: 0x1d92, + 0x073c: 0x0099, 0x073d: 0x0087, 0x073e: 0x0089, 0x073f: 0x009b, + // Block 0x1d, offset 0x740 + 0x0741: 0x3bf0, 0x0743: 0x8800, 0x0744: 0x3bf7, 0x0745: 0x8800, + 0x0747: 0x3bfe, 0x0748: 0x8800, 0x0749: 0x3c05, + 0x074d: 0x8800, + 0x0760: 0x2f4f, 0x0761: 0x8800, 0x0762: 0x3c13, + 0x0764: 0x8800, 0x0765: 0x8800, + 0x076d: 0x3c0c, 0x076e: 0x2f4a, 0x076f: 0x2f54, + 0x0770: 0x3c1a, 0x0771: 0x3c21, 0x0772: 0x8800, 0x0773: 0x8800, 0x0774: 0x3c28, 0x0775: 0x3c2f, + 0x0776: 0x8800, 0x0777: 0x8800, 0x0778: 0x3c36, 0x0779: 0x3c3d, 0x077a: 0x8800, 0x077b: 0x8800, + 0x077c: 0x8800, 0x077d: 0x8800, + // Block 0x1e, offset 0x780 + 0x0780: 0x3c44, 0x0781: 0x3c4b, 0x0782: 0x8800, 0x0783: 0x8800, 0x0784: 0x3c60, 0x0785: 0x3c67, + 0x0786: 0x8800, 0x0787: 0x8800, 0x0788: 0x3c6e, 0x0789: 0x3c75, + 0x0791: 0x8800, + 0x0792: 0x8800, + 0x07a2: 0x8800, + 0x07a8: 0x8800, 0x07a9: 0x8800, + 0x07ab: 0x8800, 0x07ac: 0x3c8a, 0x07ad: 0x3c91, 0x07ae: 0x3c98, 0x07af: 0x3c9f, + 0x07b2: 0x8800, 0x07b3: 0x8800, 0x07b4: 0x8800, 0x07b5: 0x8800, + // Block 0x1f, offset 0x7c0 + 0x07e0: 0x0023, 0x07e1: 0x0025, 0x07e2: 0x0027, 0x07e3: 0x0029, + 0x07e4: 0x002b, 0x07e5: 0x002d, 0x07e6: 0x002f, 0x07e7: 0x0031, 0x07e8: 0x0033, 0x07e9: 0x19c4, + 0x07ea: 0x19c7, 0x07eb: 0x19ca, 0x07ec: 0x19cd, 0x07ed: 0x19d0, 0x07ee: 0x19d3, 0x07ef: 0x19d6, + 0x07f0: 0x19d9, 0x07f1: 0x19dc, 0x07f2: 0x19df, 0x07f3: 0x19e8, 0x07f4: 0x1baa, 0x07f5: 0x1bae, + 0x07f6: 0x1bb2, 0x07f7: 0x1bb6, 0x07f8: 0x1bba, 0x07f9: 0x1bbe, 0x07fa: 0x1bc2, 0x07fb: 0x1bc6, + 0x07fc: 0x1bca, 0x07fd: 0x1dc2, 0x07fe: 0x1dc7, 0x07ff: 0x1dcc, + // Block 0x20, offset 0x800 + 0x0800: 0x1dd1, 0x0801: 0x1dd6, 0x0802: 0x1ddb, 0x0803: 0x1de0, 0x0804: 0x1de5, 0x0805: 0x1dea, + 0x0806: 0x1def, 0x0807: 0x1df4, 0x0808: 0x19c1, 0x0809: 0x19e5, 0x080a: 0x1a09, 0x080b: 0x1a2d, + 0x080c: 0x1a51, 0x080d: 0x1a5a, 0x080e: 0x1a60, 0x080f: 0x1a66, 0x0810: 0x1a6c, 0x0811: 0x1ca2, + 0x0812: 0x1ca6, 0x0813: 0x1caa, 0x0814: 0x1cae, 0x0815: 0x1cb2, 0x0816: 0x1cb6, 0x0817: 0x1cba, + 0x0818: 0x1cbe, 0x0819: 0x1cc2, 0x081a: 0x1cc6, 0x081b: 0x1cca, 0x081c: 0x1c36, 0x081d: 0x1c3a, + 0x081e: 0x1c3e, 0x081f: 0x1c42, 0x0820: 0x1c46, 0x0821: 0x1c4a, 0x0822: 0x1c4e, 0x0823: 0x1c52, + 0x0824: 0x1c56, 0x0825: 0x1c5a, 0x0826: 0x1c5e, 0x0827: 0x1c62, 0x0828: 0x1c66, 0x0829: 0x1c6a, + 0x082a: 0x1c6e, 0x082b: 0x1c72, 0x082c: 0x1c76, 0x082d: 0x1c7a, 0x082e: 0x1c7e, 0x082f: 0x1c82, + 0x0830: 0x1c86, 0x0831: 0x1c8a, 0x0832: 0x1c8e, 0x0833: 0x1c92, 0x0834: 0x1c96, 0x0835: 0x1c9a, + 0x0836: 0x0043, 0x0837: 0x0045, 0x0838: 0x0047, 0x0839: 0x0049, 0x083a: 0x004b, 0x083b: 0x004d, + 0x083c: 0x004f, 0x083d: 0x0051, 0x083e: 0x0053, 0x083f: 0x0055, + // Block 0x21, offset 0x840 + 0x0840: 0x0731, 0x0841: 0x0755, 0x0842: 0x0761, 0x0843: 0x0771, 0x0844: 0x0779, 0x0845: 0x0785, + 0x0846: 0x078d, 0x0847: 0x0795, 0x0848: 0x07a1, 0x0849: 0x07f5, 0x084a: 0x080d, 0x084b: 0x081d, + 0x084c: 0x082d, 0x084d: 0x083d, 0x084e: 0x084d, 0x084f: 0x086d, 0x0850: 0x0871, 0x0851: 0x0875, + 0x0852: 0x08a9, 0x0853: 0x08d1, 0x0854: 0x08e1, 0x0855: 0x08e9, 0x0856: 0x08ed, 0x0857: 0x08f9, + 0x0858: 0x0915, 0x0859: 0x0919, 0x085a: 0x0931, 0x085b: 0x0935, 0x085c: 0x093d, 0x085d: 0x094d, + 0x085e: 0x09e9, 0x085f: 0x09fd, 0x0860: 0x0a3d, 0x0861: 0x0a51, 0x0862: 0x0a59, 0x0863: 0x0a5d, + 0x0864: 0x0a6d, 0x0865: 0x0a89, 0x0866: 0x0ab5, 0x0867: 0x0ac1, 0x0868: 0x0ae1, 0x0869: 0x0aed, + 0x086a: 0x0af1, 0x086b: 0x0af5, 0x086c: 0x0b0d, 0x086d: 0x0b11, 0x086e: 0x0b3d, 0x086f: 0x0b49, + 0x0870: 0x0b51, 0x0871: 0x0b59, 0x0872: 0x0b69, 0x0873: 0x0b71, 0x0874: 0x0b79, 0x0875: 0x0ba5, + 0x0876: 0x0ba9, 0x0877: 0x0bb1, 0x0878: 0x0bb5, 0x0879: 0x0bbd, 0x087a: 0x0bc5, 0x087b: 0x0bd5, + 0x087c: 0x0bf1, 0x087d: 0x0c69, 0x087e: 0x0c7d, 0x087f: 0x0c81, + // Block 0x22, offset 0x880 + 0x0880: 0x0d01, 0x0881: 0x0d05, 0x0882: 0x0d19, 0x0883: 0x0d1d, 0x0884: 0x0d25, 0x0885: 0x0d2d, + 0x0886: 0x0d35, 0x0887: 0x0d41, 0x0888: 0x0d69, 0x0889: 0x0d79, 0x088a: 0x0d8d, 0x088b: 0x0dfd, + 0x088c: 0x0e09, 0x088d: 0x0e19, 0x088e: 0x0e25, 0x088f: 0x0e31, 0x0890: 0x0e39, 0x0891: 0x0e3d, + 0x0892: 0x0e41, 0x0893: 0x0e45, 0x0894: 0x0e49, 0x0895: 0x0f01, 0x0896: 0x0f49, 0x0897: 0x0f55, + 0x0898: 0x0f59, 0x0899: 0x0f5d, 0x089a: 0x0f61, 0x089b: 0x0f69, 0x089c: 0x0f6d, 0x089d: 0x0f81, + 0x089e: 0x0f9d, 0x089f: 0x0fa5, 0x08a0: 0x0fe5, 0x08a1: 0x0fe9, 0x08a2: 0x0ff1, 0x08a3: 0x0ff5, + 0x08a4: 0x0ffd, 0x08a5: 0x1001, 0x08a6: 0x1025, 0x08a7: 0x1029, 0x08a8: 0x1045, 0x08a9: 0x1049, + 0x08aa: 0x104d, 0x08ab: 0x1051, 0x08ac: 0x1065, 0x08ad: 0x1089, 0x08ae: 0x108d, 0x08af: 0x1091, + 0x08b0: 0x10b5, 0x08b1: 0x10f5, 0x08b2: 0x10f9, 0x08b3: 0x1119, 0x08b4: 0x1129, 0x08b5: 0x1131, + 0x08b6: 0x1151, 0x08b7: 0x1175, 0x08b8: 0x11b9, 0x08b9: 0x11c1, 0x08ba: 0x11d5, 0x08bb: 0x11e1, + 0x08bc: 0x11e9, 0x08bd: 0x11f1, 0x08be: 0x11f5, 0x08bf: 0x11f9, + // Block 0x23, offset 0x8c0 + 0x08c0: 0x1211, 0x08c1: 0x1215, 0x08c2: 0x1231, 0x08c3: 0x1239, 0x08c4: 0x1241, 0x08c5: 0x1245, + 0x08c6: 0x1251, 0x08c7: 0x1259, 0x08c8: 0x125d, 0x08c9: 0x1261, 0x08ca: 0x1269, 0x08cb: 0x126d, + 0x08cc: 0x130d, 0x08cd: 0x1321, 0x08ce: 0x1355, 0x08cf: 0x1359, 0x08d0: 0x1361, 0x08d1: 0x138d, + 0x08d2: 0x1395, 0x08d3: 0x139d, 0x08d4: 0x13a5, 0x08d5: 0x13e1, 0x08d6: 0x13e5, 0x08d7: 0x13ed, + 0x08d8: 0x13f1, 0x08d9: 0x13f5, 0x08da: 0x1421, 0x08db: 0x1425, 0x08dc: 0x142d, 0x08dd: 0x1441, + 0x08de: 0x1445, 0x08df: 0x1461, 0x08e0: 0x1469, 0x08e1: 0x146d, 0x08e2: 0x1491, 0x08e3: 0x14b1, + 0x08e4: 0x14c1, 0x08e5: 0x14c5, 0x08e6: 0x14cd, 0x08e7: 0x14f9, 0x08e8: 0x14fd, 0x08e9: 0x150d, + 0x08ea: 0x1531, 0x08eb: 0x153d, 0x08ec: 0x154d, 0x08ed: 0x1565, 0x08ee: 0x156d, 0x08ef: 0x1571, + 0x08f0: 0x1575, 0x08f1: 0x1579, 0x08f2: 0x1585, 0x08f3: 0x1589, 0x08f4: 0x1591, 0x08f5: 0x15ad, + 0x08f6: 0x15b1, 0x08f7: 0x15b5, 0x08f8: 0x15cd, 0x08f9: 0x15d1, 0x08fa: 0x15d9, 0x08fb: 0x15ed, + 0x08fc: 0x15f1, 0x08fd: 0x15f5, 0x08fe: 0x15fd, 0x08ff: 0x1601, + // Block 0x24, offset 0x900 + 0x0906: 0x8800, 0x090b: 0x8800, + 0x090c: 0x3ef2, 0x090d: 0x8800, 0x090e: 0x3efa, 0x090f: 0x8800, 0x0910: 0x3f02, 0x0911: 0x8800, + 0x0912: 0x3f0a, 0x0913: 0x8800, 0x0914: 0x3f12, 0x0915: 0x8800, 0x0916: 0x3f1a, 0x0917: 0x8800, + 0x0918: 0x3f22, 0x0919: 0x8800, 0x091a: 0x3f2a, 0x091b: 0x8800, 0x091c: 0x3f32, 0x091d: 0x8800, + 0x091e: 0x3f3a, 0x091f: 0x8800, 0x0920: 0x3f42, 0x0921: 0x8800, 0x0922: 0x3f4a, + 0x0924: 0x8800, 0x0925: 0x3f52, 0x0926: 0x8800, 0x0927: 0x3f5a, 0x0928: 0x8800, 0x0929: 0x3f62, + 0x092f: 0x8800, + 0x0930: 0x3f6a, 0x0931: 0x3f72, 0x0932: 0x8800, 0x0933: 0x3f7a, 0x0934: 0x3f82, 0x0935: 0x8800, + 0x0936: 0x3f8a, 0x0937: 0x3f92, 0x0938: 0x8800, 0x0939: 0x3f9a, 0x093a: 0x3fa2, 0x093b: 0x8800, + 0x093c: 0x3faa, 0x093d: 0x3fb2, + // Block 0x25, offset 0x940 + 0x0954: 0x3eea, + 0x0959: 0x8608, 0x095a: 0x8608, 0x095b: 0x42c2, 0x095c: 0x42c8, 0x095d: 0x8800, + 0x095e: 0x3fba, 0x095f: 0x285f, + 0x0966: 0x8800, + 0x096b: 0x8800, 0x096c: 0x3fca, 0x096d: 0x8800, 0x096e: 0x3fd2, 0x096f: 0x8800, + 0x0970: 0x3fda, 0x0971: 0x8800, 0x0972: 0x3fe2, 0x0973: 0x8800, 0x0974: 0x3fea, 0x0975: 0x8800, + 0x0976: 0x3ff2, 0x0977: 0x8800, 0x0978: 0x3ffa, 0x0979: 0x8800, 0x097a: 0x4002, 0x097b: 0x8800, + 0x097c: 0x400a, 0x097d: 0x8800, 0x097e: 0x4012, 0x097f: 0x8800, + // Block 0x26, offset 0x980 + 0x0980: 0x401a, 0x0981: 0x8800, 0x0982: 0x4022, 0x0984: 0x8800, 0x0985: 0x402a, + 0x0986: 0x8800, 0x0987: 0x4032, 0x0988: 0x8800, 0x0989: 0x403a, + 0x098f: 0x8800, 0x0990: 0x4042, 0x0991: 0x404a, + 0x0992: 0x8800, 0x0993: 0x4052, 0x0994: 0x405a, 0x0995: 0x8800, 0x0996: 0x4062, 0x0997: 0x406a, + 0x0998: 0x8800, 0x0999: 0x4072, 0x099a: 0x407a, 0x099b: 0x8800, 0x099c: 0x4082, 0x099d: 0x408a, + 0x09af: 0x8800, + 0x09b0: 0x8800, 0x09b1: 0x8800, 0x09b2: 0x8800, 0x09b4: 0x3fc2, + 0x09b7: 0x4092, 0x09b8: 0x409a, 0x09b9: 0x40a2, 0x09ba: 0x40aa, + 0x09bd: 0x8800, 0x09be: 0x40b2, 0x09bf: 0x2874, + // Block 0x27, offset 0x9c0 + 0x09c0: 0x03a9, 0x09c1: 0x03ad, 0x09c2: 0x047d, 0x09c3: 0x0481, 0x09c4: 0x03b1, 0x09c5: 0x03b5, + 0x09c6: 0x03b9, 0x09c7: 0x0415, 0x09c8: 0x0419, 0x09c9: 0x041d, 0x09ca: 0x0421, 0x09cb: 0x0425, + 0x09cc: 0x0429, 0x09cd: 0x042d, 0x09ce: 0x0431, + 0x09d2: 0x0731, 0x09d3: 0x078d, 0x09d4: 0x073d, 0x09d5: 0x09ed, 0x09d6: 0x0741, 0x09d7: 0x0759, + 0x09d8: 0x0745, 0x09d9: 0x1005, 0x09da: 0x0779, 0x09db: 0x074d, 0x09dc: 0x0735, 0x09dd: 0x0a71, + 0x09de: 0x0a01, 0x09df: 0x07a1, + // Block 0x28, offset 0xa00 + 0x0a00: 0x2196, 0x0a01: 0x219c, 0x0a02: 0x21a2, 0x0a03: 0x21a8, 0x0a04: 0x21ae, 0x0a05: 0x21b4, + 0x0a06: 0x21ba, 0x0a07: 0x21c0, 0x0a08: 0x21c6, 0x0a09: 0x21cc, 0x0a0a: 0x21d2, 0x0a0b: 0x21d8, + 0x0a0c: 0x21de, 0x0a0d: 0x21e4, 0x0a0e: 0x28d1, 0x0a0f: 0x28da, 0x0a10: 0x28e3, 0x0a11: 0x28ec, + 0x0a12: 0x28f5, 0x0a13: 0x28fe, 0x0a14: 0x2907, 0x0a15: 0x2910, 0x0a16: 0x2919, 0x0a17: 0x292b, + 0x0a18: 0x2934, 0x0a19: 0x293d, 0x0a1a: 0x2946, 0x0a1b: 0x294f, 0x0a1c: 0x2922, 0x0a1d: 0x2d74, + 0x0a1e: 0x2ca5, 0x0a20: 0x21ea, 0x0a21: 0x2202, 0x0a22: 0x21f6, 0x0a23: 0x224a, + 0x0a24: 0x2208, 0x0a25: 0x2226, 0x0a26: 0x21f0, 0x0a27: 0x2220, 0x0a28: 0x21fc, 0x0a29: 0x2232, + 0x0a2a: 0x2262, 0x0a2b: 0x2280, 0x0a2c: 0x227a, 0x0a2d: 0x226e, 0x0a2e: 0x22bc, 0x0a2f: 0x2250, + 0x0a30: 0x225c, 0x0a31: 0x2274, 0x0a32: 0x2268, 0x0a33: 0x2292, 0x0a34: 0x223e, 0x0a35: 0x2286, + 0x0a36: 0x22b0, 0x0a37: 0x2298, 0x0a38: 0x222c, 0x0a39: 0x220e, 0x0a3a: 0x2244, 0x0a3b: 0x2256, + 0x0a3c: 0x228c, 0x0a3d: 0x2214, 0x0a3e: 0x22b6, 0x0a3f: 0x2238, + // Block 0x29, offset 0xa40 + 0x0a40: 0x229e, 0x0a41: 0x221a, 0x0a42: 0x22a4, 0x0a43: 0x22aa, 0x0a44: 0x09a1, 0x0a45: 0x0b75, + 0x0a46: 0x0d19, 0x0a47: 0x1139, + 0x0a50: 0x1d06, 0x0a51: 0x19eb, + 0x0a52: 0x19ee, 0x0a53: 0x19f1, 0x0a54: 0x19f4, 0x0a55: 0x19f7, 0x0a56: 0x19fa, 0x0a57: 0x19fd, + 0x0a58: 0x1a00, 0x0a59: 0x1a03, 0x0a5a: 0x1a0c, 0x0a5b: 0x1a0f, 0x0a5c: 0x1a12, 0x0a5d: 0x1a15, + 0x0a5e: 0x1a18, 0x0a5f: 0x1a1b, 0x0a60: 0x030d, 0x0a61: 0x0315, 0x0a62: 0x0319, 0x0a63: 0x0321, + 0x0a64: 0x0325, 0x0a65: 0x0329, 0x0a66: 0x0331, 0x0a67: 0x0339, 0x0a68: 0x033d, 0x0a69: 0x0345, + 0x0a6a: 0x0349, 0x0a6b: 0x034d, 0x0a6c: 0x0351, 0x0a6d: 0x0355, 0x0a6e: 0x27d3, 0x0a6f: 0x27da, + 0x0a70: 0x27e1, 0x0a71: 0x27e8, 0x0a72: 0x27ef, 0x0a73: 0x27f6, 0x0a74: 0x27fd, 0x0a75: 0x2804, + 0x0a76: 0x2812, 0x0a77: 0x2819, 0x0a78: 0x2820, 0x0a79: 0x2827, 0x0a7a: 0x282e, 0x0a7b: 0x2835, + 0x0a7c: 0x2cc4, 0x0a7d: 0x2b39, 0x0a7e: 0x280b, + // Block 0x2a, offset 0xa80 + 0x0a80: 0x0731, 0x0a81: 0x078d, 0x0a82: 0x073d, 0x0a83: 0x09ed, 0x0a84: 0x0791, 0x0a85: 0x0821, + 0x0a86: 0x0739, 0x0a87: 0x081d, 0x0a88: 0x077d, 0x0a89: 0x08f9, 0x0a8a: 0x0d79, 0x0a8b: 0x0f01, + 0x0a8c: 0x0e49, 0x0a8d: 0x0d8d, 0x0a8e: 0x14cd, 0x0a8f: 0x09fd, 0x0a90: 0x0d41, 0x0a91: 0x0dbd, + 0x0a92: 0x0d7d, 0x0a93: 0x10bd, 0x0a94: 0x096d, 0x0a95: 0x0f75, 0x0a96: 0x13f9, 0x0a97: 0x10d1, + 0x0a98: 0x08b5, 0x0a99: 0x1101, 0x0a9a: 0x100d, 0x0a9b: 0x0a89, 0x0a9c: 0x1481, 0x0a9d: 0x07f1, + 0x0a9e: 0x091d, 0x0a9f: 0x0e69, 0x0aa0: 0x1595, 0x0aa1: 0x07b5, 0x0aa2: 0x0845, 0x0aa3: 0x0e0d, + 0x0aa4: 0x0741, 0x0aa5: 0x0759, 0x0aa6: 0x0745, 0x0aa7: 0x0b4d, 0x0aa8: 0x0961, 0x0aa9: 0x08f1, + 0x0aaa: 0x0ac9, 0x0aab: 0x0abd, 0x0aac: 0x105d, 0x0aad: 0x07b1, 0x0aae: 0x140d, 0x0aaf: 0x090d, + 0x0ab0: 0x0a65, 0x0ab1: 0x1a1e, 0x0ab2: 0x1a21, 0x0ab3: 0x1a24, 0x0ab4: 0x1a27, 0x0ab5: 0x1a30, + 0x0ab6: 0x1a33, 0x0ab7: 0x1a36, 0x0ab8: 0x1a39, 0x0ab9: 0x1a3c, 0x0aba: 0x1a3f, 0x0abb: 0x1a42, + 0x0abc: 0x1a45, 0x0abd: 0x1a48, 0x0abe: 0x1a4b, 0x0abf: 0x1a54, + // Block 0x2b, offset 0xac0 + 0x0ac0: 0x1e08, 0x0ac1: 0x1e17, 0x0ac2: 0x1e26, 0x0ac3: 0x1e35, 0x0ac4: 0x1e44, 0x0ac5: 0x1e53, + 0x0ac6: 0x1e62, 0x0ac7: 0x1e71, 0x0ac8: 0x1e80, 0x0ac9: 0x22ce, 0x0aca: 0x22e0, 0x0acb: 0x22f2, + 0x0acc: 0x1a96, 0x0acd: 0x1d46, 0x0ace: 0x1b14, 0x0acf: 0x1cea, 0x0ad0: 0x053d, 0x0ad1: 0x0545, + 0x0ad2: 0x054d, 0x0ad3: 0x0555, 0x0ad4: 0x055d, 0x0ad5: 0x0561, 0x0ad6: 0x0565, 0x0ad7: 0x0569, + 0x0ad8: 0x056d, 0x0ad9: 0x0571, 0x0ada: 0x0575, 0x0adb: 0x0579, 0x0adc: 0x057d, 0x0add: 0x0581, + 0x0ade: 0x0585, 0x0adf: 0x0589, 0x0ae0: 0x058d, 0x0ae1: 0x0595, 0x0ae2: 0x0599, 0x0ae3: 0x059d, + 0x0ae4: 0x05a1, 0x0ae5: 0x05a5, 0x0ae6: 0x05a9, 0x0ae7: 0x05ad, 0x0ae8: 0x05b1, 0x0ae9: 0x05b5, + 0x0aea: 0x05b9, 0x0aeb: 0x05bd, 0x0aec: 0x05c1, 0x0aed: 0x05c5, 0x0aee: 0x05c9, 0x0aef: 0x05cd, + 0x0af0: 0x05d1, 0x0af1: 0x05d5, 0x0af2: 0x05d9, 0x0af3: 0x05e1, 0x0af4: 0x05e9, 0x0af5: 0x05f1, + 0x0af6: 0x05f5, 0x0af7: 0x05f9, 0x0af8: 0x05fd, 0x0af9: 0x0601, 0x0afa: 0x0605, 0x0afb: 0x0609, + 0x0afc: 0x060d, 0x0afd: 0x0611, 0x0afe: 0x0615, + // Block 0x2c, offset 0xb00 + 0x0b00: 0x2cd4, 0x0b01: 0x2b60, 0x0b02: 0x2ce4, 0x0b03: 0x2a2b, 0x0b04: 0x2ede, 0x0b05: 0x2a35, + 0x0b06: 0x2a3f, 0x0b07: 0x2f22, 0x0b08: 0x2b6d, 0x0b09: 0x2a49, 0x0b0a: 0x2a53, 0x0b0b: 0x2a5d, + 0x0b0c: 0x2b94, 0x0b0d: 0x2ba1, 0x0b0e: 0x2b7a, 0x0b0f: 0x2b87, 0x0b10: 0x2eb4, 0x0b11: 0x2bae, + 0x0b12: 0x2bbb, 0x0b13: 0x2d86, 0x0b14: 0x2866, 0x0b15: 0x2d99, 0x0b16: 0x2dac, 0x0b17: 0x2cf4, + 0x0b18: 0x2bc8, 0x0b19: 0x2dbf, 0x0b1a: 0x2dd2, 0x0b1b: 0x2bd5, 0x0b1c: 0x2a67, 0x0b1d: 0x2a71, + 0x0b1e: 0x2ec2, 0x0b1f: 0x2be2, 0x0b20: 0x2d04, 0x0b21: 0x2eef, 0x0b22: 0x2a7b, 0x0b23: 0x2a85, + 0x0b24: 0x2bef, 0x0b25: 0x2a8f, 0x0b26: 0x2a99, 0x0b27: 0x287b, 0x0b28: 0x2882, 0x0b29: 0x2aa3, + 0x0b2a: 0x2aad, 0x0b2b: 0x2de5, 0x0b2c: 0x2bfc, 0x0b2d: 0x2d14, 0x0b2e: 0x2df8, 0x0b2f: 0x2c09, + 0x0b30: 0x2ac1, 0x0b31: 0x2ab7, 0x0b32: 0x2f36, 0x0b33: 0x2c16, 0x0b34: 0x2e0b, 0x0b35: 0x2acb, + 0x0b36: 0x2d24, 0x0b37: 0x2ad5, 0x0b38: 0x2c30, 0x0b39: 0x2adf, 0x0b3a: 0x2c3d, 0x0b3b: 0x2f00, + 0x0b3c: 0x2c23, 0x0b3d: 0x2d34, 0x0b3e: 0x2c4a, 0x0b3f: 0x2889, + // Block 0x2d, offset 0xb40 + 0x0b40: 0x2f11, 0x0b41: 0x2ae9, 0x0b42: 0x2af3, 0x0b43: 0x2c57, 0x0b44: 0x2afd, 0x0b45: 0x2b07, + 0x0b46: 0x2b11, 0x0b47: 0x2d44, 0x0b48: 0x2c64, 0x0b49: 0x2890, 0x0b4a: 0x2e1e, 0x0b4b: 0x2ea9, + 0x0b4c: 0x2d54, 0x0b4d: 0x2c71, 0x0b4e: 0x2ed0, 0x0b4f: 0x2b1b, 0x0b50: 0x2b25, 0x0b51: 0x2c7e, + 0x0b52: 0x2897, 0x0b53: 0x2c8b, 0x0b54: 0x2d64, 0x0b55: 0x289e, 0x0b56: 0x2e31, 0x0b57: 0x2b2f, + 0x0b58: 0x1df9, 0x0b59: 0x1e0d, 0x0b5a: 0x1e1c, 0x0b5b: 0x1e2b, 0x0b5c: 0x1e3a, 0x0b5d: 0x1e49, + 0x0b5e: 0x1e58, 0x0b5f: 0x1e67, 0x0b60: 0x1e76, 0x0b61: 0x1e85, 0x0b62: 0x22d4, 0x0b63: 0x22e6, + 0x0b64: 0x22f8, 0x0b65: 0x2304, 0x0b66: 0x2310, 0x0b67: 0x231c, 0x0b68: 0x2328, 0x0b69: 0x2334, + 0x0b6a: 0x2340, 0x0b6b: 0x234c, 0x0b6c: 0x2388, 0x0b6d: 0x2394, 0x0b6e: 0x23a0, 0x0b6f: 0x23ac, + 0x0b70: 0x23b8, 0x0b71: 0x1d56, 0x0b72: 0x1b08, 0x0b73: 0x1a78, 0x0b74: 0x1d26, 0x0b75: 0x1b89, + 0x0b76: 0x1b98, 0x0b77: 0x1b0e, 0x0b78: 0x1d3e, 0x0b79: 0x1d42, 0x0b7a: 0x1aa2, 0x0b7b: 0x28ac, + 0x0b7c: 0x28ba, 0x0b7d: 0x28a5, 0x0b7e: 0x28b3, 0x0b7f: 0x2c98, + // Block 0x2e, offset 0xb80 + 0x0b80: 0x1b8c, 0x0b81: 0x1b74, 0x0b82: 0x1da2, 0x0b83: 0x1b5c, 0x0b84: 0x1b35, 0x0b85: 0x1aab, + 0x0b86: 0x1aba, 0x0b87: 0x1a8a, 0x0b88: 0x1d32, 0x0b89: 0x1e94, 0x0b8a: 0x1b8f, 0x0b8b: 0x1b77, + 0x0b8c: 0x1da6, 0x0b8d: 0x1db2, 0x0b8e: 0x1b68, 0x0b8f: 0x1b3e, 0x0b90: 0x1a99, 0x0b91: 0x1d5e, + 0x0b92: 0x1cf2, 0x0b93: 0x1cde, 0x0b94: 0x1d0e, 0x0b95: 0x1db6, 0x0b96: 0x1b6b, 0x0b97: 0x1b0b, + 0x0b98: 0x1b41, 0x0b99: 0x1b20, 0x0b9a: 0x1b83, 0x0b9b: 0x1dba, 0x0b9c: 0x1b6e, 0x0b9d: 0x1b02, + 0x0b9e: 0x1b44, 0x0b9f: 0x1d7e, 0x0ba0: 0x1d36, 0x0ba1: 0x1b56, 0x0ba2: 0x1d66, 0x0ba3: 0x1d82, + 0x0ba4: 0x1d3a, 0x0ba5: 0x1b59, 0x0ba6: 0x1d6a, 0x0ba7: 0x242a, 0x0ba8: 0x243e, 0x0ba9: 0x1ad8, + 0x0baa: 0x1d62, 0x0bab: 0x1cf6, 0x0bac: 0x1ce2, 0x0bad: 0x1d8a, 0x0bae: 0x28c1, 0x0baf: 0x2958, + 0x0bb0: 0x1b9b, 0x0bb1: 0x1b86, 0x0bb2: 0x1dbe, 0x0bb3: 0x1b71, 0x0bb4: 0x1b92, 0x0bb5: 0x1b7a, + 0x0bb6: 0x1daa, 0x0bb7: 0x1b5f, 0x0bb8: 0x1b38, 0x0bb9: 0x1ac3, 0x0bba: 0x1b95, 0x0bbb: 0x1b7d, + 0x0bbc: 0x1dae, 0x0bbd: 0x1b62, 0x0bbe: 0x1b3b, 0x0bbf: 0x1ac6, + // Block 0x2f, offset 0xbc0 + 0x0bc0: 0x1d6e, 0x0bc1: 0x1cfa, 0x0bc2: 0x1e8f, 0x0bc3: 0x1a7b, 0x0bc4: 0x1afc, 0x0bc5: 0x1aff, + 0x0bc6: 0x2437, 0x0bc7: 0x1cd6, 0x0bc8: 0x1b05, 0x0bc9: 0x1a8d, 0x0bca: 0x1b23, 0x0bcb: 0x1a90, + 0x0bcc: 0x1b2c, 0x0bcd: 0x1aae, 0x0bce: 0x1ab1, 0x0bcf: 0x1b47, 0x0bd0: 0x1b4d, 0x0bd1: 0x1b50, + 0x0bd2: 0x1d72, 0x0bd3: 0x1b53, 0x0bd4: 0x1b65, 0x0bd5: 0x1d7a, 0x0bd6: 0x1d86, 0x0bd7: 0x1ad2, + 0x0bd8: 0x1e99, 0x0bd9: 0x1cfe, 0x0bda: 0x1ad5, 0x0bdb: 0x1b9e, 0x0bdc: 0x1ae7, 0x0bdd: 0x1af6, + 0x0bde: 0x2424, 0x0bdf: 0x241e, 0x0be0: 0x1e03, 0x0be1: 0x1e12, 0x0be2: 0x1e21, 0x0be3: 0x1e30, + 0x0be4: 0x1e3f, 0x0be5: 0x1e4e, 0x0be6: 0x1e5d, 0x0be7: 0x1e6c, 0x0be8: 0x1e7b, 0x0be9: 0x22c8, + 0x0bea: 0x22da, 0x0beb: 0x22ec, 0x0bec: 0x22fe, 0x0bed: 0x230a, 0x0bee: 0x2316, 0x0bef: 0x2322, + 0x0bf0: 0x232e, 0x0bf1: 0x233a, 0x0bf2: 0x2346, 0x0bf3: 0x2382, 0x0bf4: 0x238e, 0x0bf5: 0x239a, + 0x0bf6: 0x23a6, 0x0bf7: 0x23b2, 0x0bf8: 0x23be, 0x0bf9: 0x23c4, 0x0bfa: 0x23ca, 0x0bfb: 0x23d0, + 0x0bfc: 0x23d6, 0x0bfd: 0x23e8, 0x0bfe: 0x23ee, 0x0bff: 0x1d52, + // Block 0x30, offset 0xc00 + 0x0c00: 0x13e9, 0x0c01: 0x0d6d, 0x0c02: 0x1445, 0x0c03: 0x1411, 0x0c04: 0x0ec9, 0x0c05: 0x075d, + 0x0c06: 0x0951, 0x0c07: 0x1699, 0x0c08: 0x1699, 0x0c09: 0x0a7d, 0x0c0a: 0x14cd, 0x0c0b: 0x09b5, + 0x0c0c: 0x0a79, 0x0c0d: 0x0c61, 0x0c0e: 0x1041, 0x0c0f: 0x11d1, 0x0c10: 0x1309, 0x0c11: 0x1345, + 0x0c12: 0x1379, 0x0c13: 0x148d, 0x0c14: 0x0de5, 0x0c15: 0x0e71, 0x0c16: 0x0f1d, 0x0c17: 0x0fb5, + 0x0c18: 0x12d1, 0x0c19: 0x14b5, 0x0c1a: 0x15e1, 0x0c1b: 0x0781, 0x0c1c: 0x0925, 0x0c1d: 0x0df9, + 0x0c1e: 0x0f41, 0x0c1f: 0x1305, 0x0c20: 0x1631, 0x0c21: 0x0b25, 0x0c22: 0x0ee9, 0x0c23: 0x12f5, + 0x0c24: 0x1389, 0x0c25: 0x0c95, 0x0c26: 0x122d, 0x0c27: 0x1351, 0x0c28: 0x0b91, 0x0c29: 0x0d81, + 0x0c2a: 0x0e89, 0x0c2b: 0x0f8d, 0x0c2c: 0x1499, 0x0c2d: 0x07c1, 0x0c2e: 0x0859, 0x0c2f: 0x08c5, + 0x0c30: 0x0cfd, 0x0c31: 0x0df1, 0x0c32: 0x0f3d, 0x0c33: 0x1061, 0x0c34: 0x11e9, 0x0c35: 0x12fd, + 0x0c36: 0x1315, 0x0c37: 0x1439, 0x0c38: 0x155d, 0x0c39: 0x1611, 0x0c3a: 0x162d, 0x0c3b: 0x109d, + 0x0c3c: 0x10dd, 0x0c3d: 0x1195, 0x0c3e: 0x12b5, 0x0c3f: 0x14e9, + // Block 0x31, offset 0xc40 + 0x0c40: 0x1639, 0x0c41: 0x13bd, 0x0c42: 0x0a39, 0x0c43: 0x0bad, 0x0c44: 0x114d, 0x0c45: 0x120d, + 0x0c46: 0x0f71, 0x0c47: 0x10a5, 0x0c48: 0x1409, 0x0c49: 0x1555, 0x0c4a: 0x0a35, 0x0c4b: 0x0b01, + 0x0c4c: 0x0de9, 0x0c4d: 0x0e9d, 0x0c4e: 0x0ed1, 0x0c4f: 0x1185, 0x0c50: 0x11ad, 0x0c51: 0x1515, + 0x0c52: 0x08c1, 0x0c53: 0x1219, 0x0c54: 0x0865, 0x0c55: 0x0861, 0x0c56: 0x1109, 0x0c57: 0x1199, + 0x0c58: 0x12cd, 0x0c59: 0x151d, 0x0c5a: 0x13d9, 0x0c5b: 0x0c99, 0x0c5c: 0x0de5, 0x0c5d: 0x13c9, + 0x0c5e: 0x0769, 0x0c5f: 0x0ad5, 0x0c60: 0x0c05, 0x0c61: 0x0fa1, 0x0c62: 0x1021, 0x0c63: 0x08e5, + 0x0c64: 0x10ad, 0x0c65: 0x07d1, 0x0c66: 0x0be9, 0x0c67: 0x0749, 0x0c68: 0x0e5d, 0x0c69: 0x0d15, + 0x0c6a: 0x1181, 0x0c6b: 0x0939, 0x0c6c: 0x0a25, 0x0c6d: 0x106d, 0x0c6e: 0x12d5, 0x0c6f: 0x13ad, + 0x0c70: 0x0e29, 0x0c71: 0x1469, 0x0c72: 0x0e55, 0x0c73: 0x0ca9, 0x0c74: 0x128d, 0x0c75: 0x0cc9, + 0x0c76: 0x101d, 0x0c77: 0x079d, 0x0c78: 0x0819, 0x0c79: 0x085d, 0x0c7a: 0x0dc5, 0x0c7b: 0x116d, + 0x0c7c: 0x1265, 0x0c7d: 0x13b9, 0x0c7e: 0x14c9, 0x0c7f: 0x08cd, + // Block 0x32, offset 0xc80 + 0x0c80: 0x0981, 0x0c81: 0x0a89, 0x0c82: 0x0ba1, 0x0c83: 0x0d31, 0x0c84: 0x0eed, 0x0c85: 0x10b1, + 0x0c86: 0x1505, 0x0c87: 0x15e9, 0x0c88: 0x163d, 0x0c89: 0x1655, 0x0c8a: 0x08a9, 0x0c8b: 0x0d65, + 0x0c8c: 0x0e15, 0x0c8d: 0x145d, 0x0c8e: 0x0b6d, 0x0c8f: 0x0c49, 0x0c90: 0x0c65, 0x0c91: 0x0cf5, + 0x0c92: 0x0edd, 0x0c93: 0x0f29, 0x0c94: 0x0fd9, 0x0c95: 0x10fd, 0x0c96: 0x11a1, 0x0c97: 0x1205, + 0x0c98: 0x144d, 0x0c99: 0x12dd, 0x0c9a: 0x1475, 0x0c9b: 0x14ed, 0x0c9c: 0x0881, 0x0c9d: 0x08ad, + 0x0c9e: 0x0995, 0x0c9f: 0x0f19, 0x0ca0: 0x1365, 0x0ca1: 0x13ad, 0x0ca2: 0x0b8d, 0x0ca3: 0x0bfd, + 0x0ca4: 0x0cc1, 0x0ca5: 0x0e21, 0x0ca6: 0x1149, 0x0ca7: 0x0f95, 0x0ca8: 0x07ad, 0x0ca9: 0x09f1, + 0x0caa: 0x0ad5, 0x0cab: 0x0b39, 0x0cac: 0x0c09, 0x0cad: 0x0fb1, 0x0cae: 0x0fcd, 0x0caf: 0x11dd, + 0x0cb0: 0x11fd, 0x0cb1: 0x14d1, 0x0cb2: 0x1551, 0x0cb3: 0x1561, 0x0cb4: 0x159d, 0x0cb5: 0x07c5, + 0x0cb6: 0x10f1, 0x0cb7: 0x14bd, 0x0cb8: 0x1539, 0x0cb9: 0x0c21, 0x0cba: 0x0789, 0x0cbb: 0x07e9, + 0x0cbc: 0x0ad9, 0x0cbd: 0x0af9, 0x0cbe: 0x0d21, 0x0cbf: 0x0de5, + // Block 0x33, offset 0xcc0 + 0x0cc0: 0x0f35, 0x0cc1: 0x103d, 0x0cc2: 0x12e9, 0x0cc3: 0x1489, 0x0cc4: 0x1691, 0x0cc5: 0x0d55, + 0x0cc6: 0x1511, 0x0cc7: 0x08a5, 0x0cc8: 0x0da1, 0x0cc9: 0x0dad, 0x0cca: 0x0e81, 0x0ccb: 0x0eb9, + 0x0ccc: 0x0fbd, 0x0ccd: 0x1019, 0x0cce: 0x1099, 0x0ccf: 0x117d, 0x0cd0: 0x15a9, 0x0cd1: 0x0821, + 0x0cd2: 0x0c75, 0x0cd3: 0x1521, 0x0cd4: 0x07d9, 0x0cd5: 0x0b1d, 0x0cd6: 0x0ea1, 0x0cd7: 0x1451, + 0x0cd8: 0x0bd9, 0x0cd9: 0x0c29, 0x0cda: 0x0db5, 0x0cdb: 0x0fa1, 0x0cdc: 0x1529, 0x0cdd: 0x0889, + 0x0cde: 0x0971, 0x0cdf: 0x0b09, 0x0ce0: 0x0d45, 0x0ce1: 0x0d91, 0x0ce2: 0x0dd1, 0x0ce3: 0x0e65, + 0x0ce4: 0x0fb9, 0x0ce5: 0x102d, 0x0ce6: 0x11c9, 0x0ce7: 0x1369, 0x0ce8: 0x1375, 0x0ce9: 0x14c5, + 0x0cea: 0x1545, 0x0ceb: 0x08f5, 0x0cec: 0x0ebd, 0x0ced: 0x0975, 0x0cee: 0x0f39, 0x0cef: 0x0fdd, + 0x0cf0: 0x12f9, 0x0cf1: 0x152d, 0x0cf2: 0x1619, 0x0cf3: 0x1641, 0x0cf4: 0x0da9, 0x0cf5: 0x0e99, + 0x0cf6: 0x1235, 0x0cf7: 0x1129, 0x0cf8: 0x1135, 0x0cf9: 0x1159, 0x0cfa: 0x0f89, 0x0cfb: 0x0f11, + 0x0cfc: 0x13d5, 0x0cfd: 0x07a5, 0x0cfe: 0x129d, 0x0cff: 0x088d, + // Block 0x34, offset 0xd00 + 0x0d00: 0x087d, 0x0d01: 0x0b7d, 0x0d02: 0x0c9d, 0x0d03: 0x1165, 0x0d04: 0x0ac5, 0x0d05: 0x0e75, + 0x0d06: 0x0d61, 0x0d07: 0x1459, 0x0d08: 0x1359, 0x0d09: 0x1519, 0x0d0a: 0x1395, 0x0d0b: 0x0b99, + 0x0d0c: 0x07f9, 0x0d0d: 0x09cd, 0x0d10: 0x0a21, + 0x0d12: 0x0d51, 0x0d15: 0x0869, 0x0d16: 0x0f91, 0x0d17: 0x1055, + 0x0d18: 0x10b9, 0x0d19: 0x10d5, 0x0d1a: 0x10d9, 0x0d1b: 0x10ed, 0x0d1c: 0x1569, 0x0d1d: 0x115d, + 0x0d1e: 0x11e1, 0x0d20: 0x1301, 0x0d22: 0x13c5, + 0x0d25: 0x1479, 0x0d26: 0x14a5, + 0x0d2a: 0x15bd, 0x0d2b: 0x15c1, 0x0d2c: 0x15c5, 0x0d2d: 0x1629, 0x0d2e: 0x149d, 0x0d2f: 0x1535, + 0x0d30: 0x07c9, 0x0d31: 0x07ed, 0x0d32: 0x0801, 0x0d33: 0x08bd, 0x0d34: 0x08c9, 0x0d35: 0x0909, + 0x0d36: 0x09bd, 0x0d37: 0x09d9, 0x0d38: 0x09e1, 0x0d39: 0x0a1d, 0x0d3a: 0x0a29, 0x0d3b: 0x0b05, + 0x0d3c: 0x0b0d, 0x0d3d: 0x0c15, 0x0d3e: 0x0c3d, 0x0d3f: 0x0c45, + // Block 0x35, offset 0xd40 + 0x0d40: 0x0c5d, 0x0d41: 0x0d09, 0x0d42: 0x0d39, 0x0d43: 0x0d59, 0x0d44: 0x0dc9, 0x0d45: 0x0e8d, + 0x0d46: 0x0ea9, 0x0d47: 0x0ed9, 0x0d48: 0x0f2d, 0x0d49: 0x0f4d, 0x0d4a: 0x0fc1, 0x0d4b: 0x10a1, + 0x0d4c: 0x10bd, 0x0d4d: 0x10c5, 0x0d4e: 0x10c1, 0x0d4f: 0x10c9, 0x0d50: 0x10cd, 0x0d51: 0x10d1, + 0x0d52: 0x10e5, 0x0d53: 0x10e9, 0x0d54: 0x110d, 0x0d55: 0x1121, 0x0d56: 0x113d, 0x0d57: 0x11a1, + 0x0d58: 0x11a9, 0x0d59: 0x11b1, 0x0d5a: 0x11c5, 0x0d5b: 0x11ed, 0x0d5c: 0x123d, 0x0d5d: 0x1271, + 0x0d5e: 0x1271, 0x0d5f: 0x12d9, 0x0d60: 0x1381, 0x0d61: 0x1399, 0x0d62: 0x13cd, 0x0d63: 0x13d1, + 0x0d64: 0x1415, 0x0d65: 0x1419, 0x0d66: 0x1471, 0x0d67: 0x1479, 0x0d68: 0x1549, 0x0d69: 0x158d, + 0x0d6a: 0x15a5, 0x0d6b: 0x0c0d, 0x0d6c: 0x1780, 0x0d6d: 0x1255, + 0x0d70: 0x0751, 0x0d71: 0x0855, 0x0d72: 0x0815, 0x0d73: 0x07bd, 0x0d74: 0x07fd, 0x0d75: 0x0829, + 0x0d76: 0x08b9, 0x0d77: 0x08d5, 0x0d78: 0x09bd, 0x0d79: 0x09a9, 0x0d7a: 0x09b9, 0x0d7b: 0x09d5, + 0x0d7c: 0x0a21, 0x0d7d: 0x0a31, 0x0d7e: 0x0a75, 0x0d7f: 0x0a81, + // Block 0x36, offset 0xd80 + 0x0d80: 0x0a9d, 0x0d81: 0x0aad, 0x0d82: 0x0b95, 0x0d83: 0x0b9d, 0x0d84: 0x0bcd, 0x0d85: 0x0bed, + 0x0d86: 0x0c1d, 0x0d87: 0x0c35, 0x0d88: 0x0c25, 0x0d89: 0x0c45, 0x0d8a: 0x0c39, 0x0d8b: 0x0c5d, + 0x0d8c: 0x0c79, 0x0d8d: 0x0cd1, 0x0d8e: 0x0cdd, 0x0d8f: 0x0ce5, 0x0d90: 0x0d0d, 0x0d91: 0x0d51, + 0x0d92: 0x0d81, 0x0d93: 0x0d85, 0x0d94: 0x0d99, 0x0d95: 0x0e19, 0x0d96: 0x0e29, 0x0d97: 0x0e81, + 0x0d98: 0x0ecd, 0x0d99: 0x0ec5, 0x0d9a: 0x0ed9, 0x0d9b: 0x0ef5, 0x0d9c: 0x0f2d, 0x0d9d: 0x1085, + 0x0d9e: 0x0f51, 0x0d9f: 0x0f85, 0x0da0: 0x0f91, 0x0da1: 0x0fd1, 0x0da2: 0x0fed, 0x0da3: 0x1011, + 0x0da4: 0x1035, 0x0da5: 0x1039, 0x0da6: 0x1055, 0x0da7: 0x1059, 0x0da8: 0x1069, 0x0da9: 0x107d, + 0x0daa: 0x1079, 0x0dab: 0x10a9, 0x0dac: 0x1125, 0x0dad: 0x113d, 0x0dae: 0x1155, 0x0daf: 0x118d, + 0x0db0: 0x11a1, 0x0db1: 0x11bd, 0x0db2: 0x11ed, 0x0db3: 0x12a1, 0x0db4: 0x12c9, 0x0db5: 0x133d, + 0x0db6: 0x1385, 0x0db7: 0x1391, 0x0db8: 0x1399, 0x0db9: 0x13b1, 0x0dba: 0x13c5, 0x0dbb: 0x13b5, + 0x0dbc: 0x13cd, 0x0dbd: 0x13c9, 0x0dbe: 0x13c1, 0x0dbf: 0x13d1, + // Block 0x37, offset 0xdc0 + 0x0dc0: 0x13dd, 0x0dc1: 0x1419, 0x0dc2: 0x1455, 0x0dc3: 0x1485, 0x0dc4: 0x14b9, 0x0dc5: 0x14d9, + 0x0dc6: 0x1525, 0x0dc7: 0x1549, 0x0dc8: 0x1569, 0x0dc9: 0x157d, 0x0dca: 0x158d, 0x0dcb: 0x1599, + 0x0dcc: 0x15a5, 0x0dcd: 0x15f9, 0x0dce: 0x1699, 0x0dcf: 0x1717, 0x0dd0: 0x1712, 0x0dd1: 0x1744, + 0x0dd2: 0x0679, 0x0dd3: 0x06a1, 0x0dd4: 0x06a5, 0x0dd5: 0x17c6, 0x0dd6: 0x17f3, 0x0dd7: 0x186b, + 0x0dd8: 0x1685, 0x0dd9: 0x1695, + // Block 0x38, offset 0xe00 + 0x0e00: 0x1b17, 0x0e01: 0x1b1a, 0x0e02: 0x1b1d, 0x0e03: 0x1d4a, 0x0e04: 0x1d4e, 0x0e05: 0x1ba1, + 0x0e06: 0x1ba1, + 0x0e13: 0x1eb7, 0x0e14: 0x1ea8, 0x0e15: 0x1ead, 0x0e16: 0x1ebc, 0x0e17: 0x1eb2, + 0x0e1d: 0x4376, + 0x0e1e: 0x801a, 0x0e1f: 0x43e8, 0x0e20: 0x0227, 0x0e21: 0x020f, 0x0e22: 0x0218, 0x0e23: 0x021b, + 0x0e24: 0x021e, 0x0e25: 0x0221, 0x0e26: 0x0224, 0x0e27: 0x022a, 0x0e28: 0x022d, 0x0e29: 0x0017, + 0x0e2a: 0x43d6, 0x0e2b: 0x43dc, 0x0e2c: 0x44da, 0x0e2d: 0x44e2, 0x0e2e: 0x432e, 0x0e2f: 0x4334, + 0x0e30: 0x433a, 0x0e31: 0x4340, 0x0e32: 0x434c, 0x0e33: 0x4352, 0x0e34: 0x4358, 0x0e35: 0x4364, + 0x0e36: 0x436a, 0x0e38: 0x4370, 0x0e39: 0x437c, 0x0e3a: 0x4382, 0x0e3b: 0x4388, + 0x0e3c: 0x4394, 0x0e3e: 0x439a, + // Block 0x39, offset 0xe40 + 0x0e40: 0x43a0, 0x0e41: 0x43a6, 0x0e43: 0x43ac, 0x0e44: 0x43b2, + 0x0e46: 0x43be, 0x0e47: 0x43c4, 0x0e48: 0x43ca, 0x0e49: 0x43d0, 0x0e4a: 0x43e2, 0x0e4b: 0x435e, + 0x0e4c: 0x4346, 0x0e4d: 0x438e, 0x0e4e: 0x43b8, 0x0e4f: 0x1ec1, 0x0e50: 0x0293, 0x0e51: 0x0293, + 0x0e52: 0x029c, 0x0e53: 0x029c, 0x0e54: 0x029c, 0x0e55: 0x029c, 0x0e56: 0x029f, 0x0e57: 0x029f, + 0x0e58: 0x029f, 0x0e59: 0x029f, 0x0e5a: 0x02a5, 0x0e5b: 0x02a5, 0x0e5c: 0x02a5, 0x0e5d: 0x02a5, + 0x0e5e: 0x0299, 0x0e5f: 0x0299, 0x0e60: 0x0299, 0x0e61: 0x0299, 0x0e62: 0x02a2, 0x0e63: 0x02a2, + 0x0e64: 0x02a2, 0x0e65: 0x02a2, 0x0e66: 0x0296, 0x0e67: 0x0296, 0x0e68: 0x0296, 0x0e69: 0x0296, + 0x0e6a: 0x02c9, 0x0e6b: 0x02c9, 0x0e6c: 0x02c9, 0x0e6d: 0x02c9, 0x0e6e: 0x02cc, 0x0e6f: 0x02cc, + 0x0e70: 0x02cc, 0x0e71: 0x02cc, 0x0e72: 0x02ab, 0x0e73: 0x02ab, 0x0e74: 0x02ab, 0x0e75: 0x02ab, + 0x0e76: 0x02a8, 0x0e77: 0x02a8, 0x0e78: 0x02a8, 0x0e79: 0x02a8, 0x0e7a: 0x02ae, 0x0e7b: 0x02ae, + 0x0e7c: 0x02ae, 0x0e7d: 0x02ae, 0x0e7e: 0x02b1, 0x0e7f: 0x02b1, + // Block 0x3a, offset 0xe80 + 0x0e80: 0x02b1, 0x0e81: 0x02b1, 0x0e82: 0x02ba, 0x0e83: 0x02ba, 0x0e84: 0x02b7, 0x0e85: 0x02b7, + 0x0e86: 0x02bd, 0x0e87: 0x02bd, 0x0e88: 0x02b4, 0x0e89: 0x02b4, 0x0e8a: 0x02c3, 0x0e8b: 0x02c3, + 0x0e8c: 0x02c0, 0x0e8d: 0x02c0, 0x0e8e: 0x02cf, 0x0e8f: 0x02cf, 0x0e90: 0x02cf, 0x0e91: 0x02cf, + 0x0e92: 0x02d5, 0x0e93: 0x02d5, 0x0e94: 0x02d5, 0x0e95: 0x02d5, 0x0e96: 0x02db, 0x0e97: 0x02db, + 0x0e98: 0x02db, 0x0e99: 0x02db, 0x0e9a: 0x02d8, 0x0e9b: 0x02d8, 0x0e9c: 0x02d8, 0x0e9d: 0x02d8, + 0x0e9e: 0x02de, 0x0e9f: 0x02de, 0x0ea0: 0x02e1, 0x0ea1: 0x02e1, 0x0ea2: 0x02e1, 0x0ea3: 0x02e1, + 0x0ea4: 0x4454, 0x0ea5: 0x4454, 0x0ea6: 0x02e7, 0x0ea7: 0x02e7, 0x0ea8: 0x02e7, 0x0ea9: 0x02e7, + 0x0eaa: 0x02e4, 0x0eab: 0x02e4, 0x0eac: 0x02e4, 0x0ead: 0x02e4, 0x0eae: 0x0302, 0x0eaf: 0x0302, + 0x0eb0: 0x444e, 0x0eb1: 0x444e, + // Block 0x3b, offset 0xec0 + 0x0ed3: 0x02d2, 0x0ed4: 0x02d2, 0x0ed5: 0x02d2, 0x0ed6: 0x02d2, 0x0ed7: 0x02f0, + 0x0ed8: 0x02f0, 0x0ed9: 0x02ed, 0x0eda: 0x02ed, 0x0edb: 0x02f3, 0x0edc: 0x02f3, 0x0edd: 0x2191, + 0x0ede: 0x02f9, 0x0edf: 0x02f9, 0x0ee0: 0x02ea, 0x0ee1: 0x02ea, 0x0ee2: 0x02f6, 0x0ee3: 0x02f6, + 0x0ee4: 0x02ff, 0x0ee5: 0x02ff, 0x0ee6: 0x02ff, 0x0ee7: 0x02ff, 0x0ee8: 0x0287, 0x0ee9: 0x0287, + 0x0eea: 0x26ec, 0x0eeb: 0x26ec, 0x0eec: 0x275c, 0x0eed: 0x275c, 0x0eee: 0x272b, 0x0eef: 0x272b, + 0x0ef0: 0x2747, 0x0ef1: 0x2747, 0x0ef2: 0x2740, 0x0ef3: 0x2740, 0x0ef4: 0x274e, 0x0ef5: 0x274e, + 0x0ef6: 0x2755, 0x0ef7: 0x2755, 0x0ef8: 0x2755, 0x0ef9: 0x2732, 0x0efa: 0x2732, 0x0efb: 0x2732, + 0x0efc: 0x02fc, 0x0efd: 0x02fc, 0x0efe: 0x02fc, 0x0eff: 0x02fc, + // Block 0x3c, offset 0xf00 + 0x0f00: 0x26f3, 0x0f01: 0x26fa, 0x0f02: 0x2716, 0x0f03: 0x2732, 0x0f04: 0x2739, 0x0f05: 0x1ecb, + 0x0f06: 0x1ed0, 0x0f07: 0x1ed5, 0x0f08: 0x1ee4, 0x0f09: 0x1ef3, 0x0f0a: 0x1ef8, 0x0f0b: 0x1efd, + 0x0f0c: 0x1f02, 0x0f0d: 0x1f07, 0x0f0e: 0x1f16, 0x0f0f: 0x1f25, 0x0f10: 0x1f2a, 0x0f11: 0x1f2f, + 0x0f12: 0x1f3e, 0x0f13: 0x1f4d, 0x0f14: 0x1f52, 0x0f15: 0x1f57, 0x0f16: 0x1f5c, 0x0f17: 0x1f6b, + 0x0f18: 0x1f70, 0x0f19: 0x1f7f, 0x0f1a: 0x1f84, 0x0f1b: 0x1f89, 0x0f1c: 0x1f98, 0x0f1d: 0x1f9d, + 0x0f1e: 0x1fa2, 0x0f1f: 0x1fac, 0x0f20: 0x1fe8, 0x0f21: 0x1ff7, 0x0f22: 0x2006, 0x0f23: 0x200b, + 0x0f24: 0x2010, 0x0f25: 0x201a, 0x0f26: 0x2029, 0x0f27: 0x202e, 0x0f28: 0x203d, 0x0f29: 0x2042, + 0x0f2a: 0x2047, 0x0f2b: 0x2056, 0x0f2c: 0x205b, 0x0f2d: 0x206a, 0x0f2e: 0x206f, 0x0f2f: 0x2074, + 0x0f30: 0x2079, 0x0f31: 0x207e, 0x0f32: 0x2083, 0x0f33: 0x2088, 0x0f34: 0x208d, 0x0f35: 0x2092, + 0x0f36: 0x2097, 0x0f37: 0x209c, 0x0f38: 0x20a1, 0x0f39: 0x20a6, 0x0f3a: 0x20ab, 0x0f3b: 0x20b0, + 0x0f3c: 0x20b5, 0x0f3d: 0x20ba, 0x0f3e: 0x20bf, 0x0f3f: 0x20c9, + // Block 0x3d, offset 0xf40 + 0x0f40: 0x20ce, 0x0f41: 0x20d3, 0x0f42: 0x20d8, 0x0f43: 0x20e2, 0x0f44: 0x20e7, 0x0f45: 0x20f1, + 0x0f46: 0x20f6, 0x0f47: 0x20fb, 0x0f48: 0x2100, 0x0f49: 0x2105, 0x0f4a: 0x210a, 0x0f4b: 0x210f, + 0x0f4c: 0x2114, 0x0f4d: 0x2119, 0x0f4e: 0x2128, 0x0f4f: 0x2137, 0x0f50: 0x213c, 0x0f51: 0x2141, + 0x0f52: 0x2146, 0x0f53: 0x214b, 0x0f54: 0x2150, 0x0f55: 0x215a, 0x0f56: 0x215f, 0x0f57: 0x2164, + 0x0f58: 0x2173, 0x0f59: 0x2182, 0x0f5a: 0x2187, 0x0f5b: 0x4406, 0x0f5c: 0x440c, 0x0f5d: 0x4442, + 0x0f5e: 0x4499, 0x0f5f: 0x44a0, 0x0f60: 0x44a7, 0x0f61: 0x44ae, 0x0f62: 0x44b5, 0x0f63: 0x44bc, + 0x0f64: 0x2708, 0x0f65: 0x270f, 0x0f66: 0x2716, 0x0f67: 0x271d, 0x0f68: 0x2732, 0x0f69: 0x2739, + 0x0f6a: 0x1eda, 0x0f6b: 0x1edf, 0x0f6c: 0x1ee4, 0x0f6d: 0x1ee9, 0x0f6e: 0x1ef3, 0x0f6f: 0x1ef8, + 0x0f70: 0x1f0c, 0x0f71: 0x1f11, 0x0f72: 0x1f16, 0x0f73: 0x1f1b, 0x0f74: 0x1f25, 0x0f75: 0x1f2a, + 0x0f76: 0x1f34, 0x0f77: 0x1f39, 0x0f78: 0x1f3e, 0x0f79: 0x1f43, 0x0f7a: 0x1f4d, 0x0f7b: 0x1f52, + 0x0f7c: 0x207e, 0x0f7d: 0x2083, 0x0f7e: 0x2092, 0x0f7f: 0x2097, + // Block 0x3e, offset 0xf80 + 0x0f80: 0x209c, 0x0f81: 0x20b0, 0x0f82: 0x20b5, 0x0f83: 0x20ba, 0x0f84: 0x20bf, 0x0f85: 0x20d8, + 0x0f86: 0x20e2, 0x0f87: 0x20e7, 0x0f88: 0x20ec, 0x0f89: 0x2100, 0x0f8a: 0x211e, 0x0f8b: 0x2123, + 0x0f8c: 0x2128, 0x0f8d: 0x212d, 0x0f8e: 0x2137, 0x0f8f: 0x213c, 0x0f90: 0x4442, 0x0f91: 0x2169, + 0x0f92: 0x216e, 0x0f93: 0x2173, 0x0f94: 0x2178, 0x0f95: 0x2182, 0x0f96: 0x2187, 0x0f97: 0x26f3, + 0x0f98: 0x26fa, 0x0f99: 0x2701, 0x0f9a: 0x2716, 0x0f9b: 0x2724, 0x0f9c: 0x1ecb, 0x0f9d: 0x1ed0, + 0x0f9e: 0x1ed5, 0x0f9f: 0x1ee4, 0x0fa0: 0x1eee, 0x0fa1: 0x1efd, 0x0fa2: 0x1f02, 0x0fa3: 0x1f07, + 0x0fa4: 0x1f16, 0x0fa5: 0x1f20, 0x0fa6: 0x1f3e, 0x0fa7: 0x1f57, 0x0fa8: 0x1f5c, 0x0fa9: 0x1f6b, + 0x0faa: 0x1f70, 0x0fab: 0x1f7f, 0x0fac: 0x1f89, 0x0fad: 0x1f98, 0x0fae: 0x1f9d, 0x0faf: 0x1fa2, + 0x0fb0: 0x1fac, 0x0fb1: 0x1fe8, 0x0fb2: 0x1fed, 0x0fb3: 0x1ff7, 0x0fb4: 0x2006, 0x0fb5: 0x200b, + 0x0fb6: 0x2010, 0x0fb7: 0x201a, 0x0fb8: 0x2029, 0x0fb9: 0x203d, 0x0fba: 0x2042, 0x0fbb: 0x2047, + 0x0fbc: 0x2056, 0x0fbd: 0x205b, 0x0fbe: 0x206a, 0x0fbf: 0x206f, + // Block 0x3f, offset 0xfc0 + 0x0fc0: 0x2074, 0x0fc1: 0x2079, 0x0fc2: 0x2088, 0x0fc3: 0x208d, 0x0fc4: 0x20a1, 0x0fc5: 0x20a6, + 0x0fc6: 0x20ab, 0x0fc7: 0x20b0, 0x0fc8: 0x20b5, 0x0fc9: 0x20c9, 0x0fca: 0x20ce, 0x0fcb: 0x20d3, + 0x0fcc: 0x20d8, 0x0fcd: 0x20dd, 0x0fce: 0x20f1, 0x0fcf: 0x20f6, 0x0fd0: 0x20fb, 0x0fd1: 0x2100, + 0x0fd2: 0x210f, 0x0fd3: 0x2114, 0x0fd4: 0x2119, 0x0fd5: 0x2128, 0x0fd6: 0x2132, 0x0fd7: 0x2141, + 0x0fd8: 0x2146, 0x0fd9: 0x4436, 0x0fda: 0x215a, 0x0fdb: 0x215f, 0x0fdc: 0x2164, 0x0fdd: 0x2173, + 0x0fde: 0x217d, 0x0fdf: 0x2716, 0x0fe0: 0x2724, 0x0fe1: 0x1ee4, 0x0fe2: 0x1eee, 0x0fe3: 0x1f16, + 0x0fe4: 0x1f20, 0x0fe5: 0x1f3e, 0x0fe6: 0x1f48, 0x0fe7: 0x1fac, 0x0fe8: 0x1fb1, 0x0fe9: 0x1fd4, + 0x0fea: 0x1fd9, 0x0feb: 0x20b0, 0x0fec: 0x20b5, 0x0fed: 0x20d8, 0x0fee: 0x2128, 0x0fef: 0x2132, + 0x0ff0: 0x2173, 0x0ff1: 0x217d, 0x0ff2: 0x44ea, 0x0ff3: 0x44f2, 0x0ff4: 0x44fa, 0x0ff5: 0x2033, + 0x0ff6: 0x2038, 0x0ff7: 0x204c, 0x0ff8: 0x2051, 0x0ff9: 0x2060, 0x0ffa: 0x2065, 0x0ffb: 0x1fb6, + 0x0ffc: 0x1fbb, 0x0ffd: 0x1fde, 0x0ffe: 0x1fe3, 0x0fff: 0x1f75, + // Block 0x40, offset 0x1000 + 0x1000: 0x1f7a, 0x1001: 0x1f61, 0x1002: 0x1f66, 0x1003: 0x1f8e, 0x1004: 0x1f93, 0x1005: 0x1ffc, + 0x1006: 0x2001, 0x1007: 0x201f, 0x1008: 0x2024, 0x1009: 0x1fc0, 0x100a: 0x1fc5, 0x100b: 0x1fca, + 0x100c: 0x1fd4, 0x100d: 0x1fcf, 0x100e: 0x1fa7, 0x100f: 0x1ff2, 0x1010: 0x2015, 0x1011: 0x2033, + 0x1012: 0x2038, 0x1013: 0x204c, 0x1014: 0x2051, 0x1015: 0x2060, 0x1016: 0x2065, 0x1017: 0x1fb6, + 0x1018: 0x1fbb, 0x1019: 0x1fde, 0x101a: 0x1fe3, 0x101b: 0x1f75, 0x101c: 0x1f7a, 0x101d: 0x1f61, + 0x101e: 0x1f66, 0x101f: 0x1f8e, 0x1020: 0x1f93, 0x1021: 0x1ffc, 0x1022: 0x2001, 0x1023: 0x201f, + 0x1024: 0x2024, 0x1025: 0x1fc0, 0x1026: 0x1fc5, 0x1027: 0x1fca, 0x1028: 0x1fd4, 0x1029: 0x1fcf, + 0x102a: 0x1fa7, 0x102b: 0x1ff2, 0x102c: 0x2015, 0x102d: 0x1fc0, 0x102e: 0x1fc5, 0x102f: 0x1fca, + 0x1030: 0x1fd4, 0x1031: 0x1fb1, 0x1032: 0x1fd9, 0x1033: 0x202e, 0x1034: 0x1f98, 0x1035: 0x1f9d, + 0x1036: 0x1fa2, 0x1037: 0x1fc0, 0x1038: 0x1fc5, 0x1039: 0x1fca, 0x103a: 0x202e, 0x103b: 0x203d, + 0x103c: 0x43ee, 0x103d: 0x43ee, + // Block 0x41, offset 0x1040 + 0x1050: 0x2453, 0x1051: 0x2468, + 0x1052: 0x2468, 0x1053: 0x246f, 0x1054: 0x2476, 0x1055: 0x248b, 0x1056: 0x2492, 0x1057: 0x2499, + 0x1058: 0x24bc, 0x1059: 0x24bc, 0x105a: 0x24df, 0x105b: 0x24d8, 0x105c: 0x24f4, 0x105d: 0x24e6, + 0x105e: 0x24ed, 0x105f: 0x2510, 0x1060: 0x2510, 0x1061: 0x2509, 0x1062: 0x2517, 0x1063: 0x2517, + 0x1064: 0x2541, 0x1065: 0x2541, 0x1066: 0x255d, 0x1067: 0x2525, 0x1068: 0x2525, 0x1069: 0x251e, + 0x106a: 0x2533, 0x106b: 0x2533, 0x106c: 0x253a, 0x106d: 0x253a, 0x106e: 0x2564, 0x106f: 0x2572, + 0x1070: 0x2572, 0x1071: 0x2579, 0x1072: 0x2579, 0x1073: 0x2580, 0x1074: 0x2587, 0x1075: 0x258e, + 0x1076: 0x2595, 0x1077: 0x2595, 0x1078: 0x259c, 0x1079: 0x25aa, 0x107a: 0x25b8, 0x107b: 0x25b1, + 0x107c: 0x25bf, 0x107d: 0x25bf, 0x107e: 0x25d4, 0x107f: 0x25db, + // Block 0x42, offset 0x1080 + 0x1080: 0x260c, 0x1081: 0x261a, 0x1082: 0x2613, 0x1083: 0x25f7, 0x1084: 0x25f7, 0x1085: 0x2621, + 0x1086: 0x2621, 0x1087: 0x2628, 0x1088: 0x2628, 0x1089: 0x2652, 0x108a: 0x2659, 0x108b: 0x2660, + 0x108c: 0x2636, 0x108d: 0x2644, 0x108e: 0x2667, 0x108f: 0x266e, + 0x1092: 0x263d, 0x1093: 0x26c2, 0x1094: 0x26c9, 0x1095: 0x269f, 0x1096: 0x26a6, 0x1097: 0x268a, + 0x1098: 0x268a, 0x1099: 0x2691, 0x109a: 0x26bb, 0x109b: 0x26b4, 0x109c: 0x26de, 0x109d: 0x26de, + 0x109e: 0x244c, 0x109f: 0x2461, 0x10a0: 0x245a, 0x10a1: 0x2484, 0x10a2: 0x247d, 0x10a3: 0x24a7, + 0x10a4: 0x24a0, 0x10a5: 0x24ca, 0x10a6: 0x24ae, 0x10a7: 0x24c3, 0x10a8: 0x24fb, 0x10a9: 0x2548, + 0x10aa: 0x252c, 0x10ab: 0x256b, 0x10ac: 0x2605, 0x10ad: 0x262f, 0x10ae: 0x26d7, 0x10af: 0x26d0, + 0x10b0: 0x26e5, 0x10b1: 0x267c, 0x10b2: 0x25e2, 0x10b3: 0x26ad, 0x10b4: 0x25d4, 0x10b5: 0x260c, + 0x10b6: 0x25a3, 0x10b7: 0x25f0, 0x10b8: 0x2683, 0x10b9: 0x2675, 0x10ba: 0x25fe, 0x10bb: 0x25e9, + 0x10bc: 0x25fe, 0x10bd: 0x2683, 0x10be: 0x24b5, 0x10bf: 0x24d1, + // Block 0x43, offset 0x10c0 + 0x10c0: 0x264b, 0x10c1: 0x25c6, 0x10c2: 0x2445, 0x10c3: 0x25e9, 0x10c4: 0x258e, 0x10c5: 0x255d, + 0x10c6: 0x2502, 0x10c7: 0x2698, + 0x10f0: 0x2556, 0x10f1: 0x25cd, 0x10f2: 0x296a, 0x10f3: 0x2961, 0x10f4: 0x2997, 0x10f5: 0x2985, + 0x10f6: 0x2973, 0x10f7: 0x298e, 0x10f8: 0x29a0, 0x10f9: 0x254f, 0x10fa: 0x2e44, 0x10fb: 0x2cb4, + 0x10fc: 0x297c, + // Block 0x44, offset 0x1100 + 0x1110: 0x0019, 0x1111: 0x04f5, + 0x1112: 0x04f9, 0x1113: 0x0035, 0x1114: 0x0037, 0x1115: 0x0003, 0x1116: 0x003f, 0x1117: 0x0531, + 0x1118: 0x0535, 0x1119: 0x1c9e, + 0x1120: 0x80e6, 0x1121: 0x80e6, 0x1122: 0x80e6, 0x1123: 0x80e6, + 0x1124: 0x80e6, 0x1125: 0x80e6, 0x1126: 0x80e6, + 0x1130: 0x19b5, 0x1131: 0x04b5, 0x1132: 0x04b1, 0x1133: 0x007f, 0x1134: 0x007f, 0x1135: 0x0011, + 0x1136: 0x0013, 0x1137: 0x00b7, 0x1138: 0x00bb, 0x1139: 0x0529, 0x113a: 0x052d, 0x113b: 0x051d, + 0x113c: 0x0521, 0x113d: 0x0505, 0x113e: 0x0509, 0x113f: 0x04fd, + // Block 0x45, offset 0x1140 + 0x1140: 0x0501, 0x1141: 0x050d, 0x1142: 0x0511, 0x1143: 0x0515, 0x1144: 0x0519, + 0x1147: 0x0077, 0x1148: 0x007b, 0x1149: 0x424f, 0x114a: 0x424f, 0x114b: 0x424f, + 0x114c: 0x424f, 0x114d: 0x007f, 0x114e: 0x007f, 0x114f: 0x007f, 0x1150: 0x0019, 0x1151: 0x04f5, + 0x1152: 0x001d, 0x1154: 0x0037, 0x1155: 0x0035, 0x1156: 0x003f, 0x1157: 0x0003, + 0x1158: 0x04b5, 0x1159: 0x0011, 0x115a: 0x0013, 0x115b: 0x00b7, 0x115c: 0x00bb, 0x115d: 0x0529, + 0x115e: 0x052d, 0x115f: 0x0007, 0x1160: 0x000d, 0x1161: 0x0015, 0x1162: 0x0017, 0x1163: 0x001b, + 0x1164: 0x0039, 0x1165: 0x003d, 0x1166: 0x003b, 0x1168: 0x0079, 0x1169: 0x0009, + 0x116a: 0x000b, 0x116b: 0x0041, + 0x1170: 0x4290, 0x1171: 0x4412, 0x1172: 0x4295, 0x1174: 0x429a, + 0x1176: 0x429f, 0x1177: 0x4418, 0x1178: 0x42a4, 0x1179: 0x441e, 0x117a: 0x42a9, 0x117b: 0x4424, + 0x117c: 0x42ae, 0x117d: 0x442a, 0x117e: 0x42b3, 0x117f: 0x4430, + // Block 0x46, offset 0x1180 + 0x1180: 0x0230, 0x1181: 0x43f4, 0x1182: 0x43f4, 0x1183: 0x43fa, 0x1184: 0x43fa, 0x1185: 0x443c, + 0x1186: 0x443c, 0x1187: 0x4400, 0x1188: 0x4400, 0x1189: 0x4448, 0x118a: 0x4448, 0x118b: 0x4448, + 0x118c: 0x4448, 0x118d: 0x0233, 0x118e: 0x0233, 0x118f: 0x0236, 0x1190: 0x0236, 0x1191: 0x0236, + 0x1192: 0x0236, 0x1193: 0x0239, 0x1194: 0x0239, 0x1195: 0x023c, 0x1196: 0x023c, 0x1197: 0x023c, + 0x1198: 0x023c, 0x1199: 0x023f, 0x119a: 0x023f, 0x119b: 0x023f, 0x119c: 0x023f, 0x119d: 0x0242, + 0x119e: 0x0242, 0x119f: 0x0242, 0x11a0: 0x0242, 0x11a1: 0x0245, 0x11a2: 0x0245, 0x11a3: 0x0245, + 0x11a4: 0x0245, 0x11a5: 0x0248, 0x11a6: 0x0248, 0x11a7: 0x0248, 0x11a8: 0x0248, 0x11a9: 0x024b, + 0x11aa: 0x024b, 0x11ab: 0x024e, 0x11ac: 0x024e, 0x11ad: 0x0251, 0x11ae: 0x0251, 0x11af: 0x0254, + 0x11b0: 0x0254, 0x11b1: 0x0257, 0x11b2: 0x0257, 0x11b3: 0x0257, 0x11b4: 0x0257, 0x11b5: 0x025a, + 0x11b6: 0x025a, 0x11b7: 0x025a, 0x11b8: 0x025a, 0x11b9: 0x025d, 0x11ba: 0x025d, 0x11bb: 0x025d, + 0x11bc: 0x025d, 0x11bd: 0x0260, 0x11be: 0x0260, 0x11bf: 0x0260, + // Block 0x47, offset 0x11c0 + 0x11c0: 0x0260, 0x11c1: 0x0263, 0x11c2: 0x0263, 0x11c3: 0x0263, 0x11c4: 0x0263, 0x11c5: 0x0266, + 0x11c6: 0x0266, 0x11c7: 0x0266, 0x11c8: 0x0266, 0x11c9: 0x0269, 0x11ca: 0x0269, 0x11cb: 0x0269, + 0x11cc: 0x0269, 0x11cd: 0x026c, 0x11ce: 0x026c, 0x11cf: 0x026c, 0x11d0: 0x026c, 0x11d1: 0x026f, + 0x11d2: 0x026f, 0x11d3: 0x026f, 0x11d4: 0x026f, 0x11d5: 0x0272, 0x11d6: 0x0272, 0x11d7: 0x0272, + 0x11d8: 0x0272, 0x11d9: 0x0275, 0x11da: 0x0275, 0x11db: 0x0275, 0x11dc: 0x0275, 0x11dd: 0x0278, + 0x11de: 0x0278, 0x11df: 0x0278, 0x11e0: 0x0278, 0x11e1: 0x027b, 0x11e2: 0x027b, 0x11e3: 0x027b, + 0x11e4: 0x027b, 0x11e5: 0x027e, 0x11e6: 0x027e, 0x11e7: 0x027e, 0x11e8: 0x027e, 0x11e9: 0x0281, + 0x11ea: 0x0281, 0x11eb: 0x0281, 0x11ec: 0x0281, 0x11ed: 0x0284, 0x11ee: 0x0284, 0x11ef: 0x0287, + 0x11f0: 0x0287, 0x11f1: 0x028a, 0x11f2: 0x028a, 0x11f3: 0x028a, 0x11f4: 0x028a, 0x11f5: 0x2e91, + 0x11f6: 0x2e91, 0x11f7: 0x2e99, 0x11f8: 0x2e99, 0x11f9: 0x2ea1, 0x11fa: 0x2ea1, 0x11fb: 0x20c4, + 0x11fc: 0x20c4, + // Block 0x48, offset 0x1200 + 0x1200: 0x0081, 0x1201: 0x0083, 0x1202: 0x0085, 0x1203: 0x0087, 0x1204: 0x0089, 0x1205: 0x008b, + 0x1206: 0x008d, 0x1207: 0x008f, 0x1208: 0x0091, 0x1209: 0x0093, 0x120a: 0x0095, 0x120b: 0x0097, + 0x120c: 0x0099, 0x120d: 0x009b, 0x120e: 0x009d, 0x120f: 0x009f, 0x1210: 0x00a1, 0x1211: 0x00a3, + 0x1212: 0x00a5, 0x1213: 0x00a7, 0x1214: 0x00a9, 0x1215: 0x00ab, 0x1216: 0x00ad, 0x1217: 0x00af, + 0x1218: 0x00b1, 0x1219: 0x00b3, 0x121a: 0x00b5, 0x121b: 0x00b7, 0x121c: 0x00b9, 0x121d: 0x00bb, + 0x121e: 0x00bd, 0x121f: 0x04e9, 0x1220: 0x04ed, 0x1221: 0x04f9, 0x1222: 0x050d, 0x1223: 0x0511, + 0x1224: 0x04f5, 0x1225: 0x061d, 0x1226: 0x0615, 0x1227: 0x0539, 0x1228: 0x0541, 0x1229: 0x0549, + 0x122a: 0x0551, 0x122b: 0x0559, 0x122c: 0x05dd, 0x122d: 0x05e5, 0x122e: 0x05ed, 0x122f: 0x0591, + 0x1230: 0x0621, 0x1231: 0x053d, 0x1232: 0x0545, 0x1233: 0x054d, 0x1234: 0x0555, 0x1235: 0x055d, + 0x1236: 0x0561, 0x1237: 0x0565, 0x1238: 0x0569, 0x1239: 0x056d, 0x123a: 0x0571, 0x123b: 0x0575, + 0x123c: 0x0579, 0x123d: 0x057d, 0x123e: 0x0581, 0x123f: 0x0585, + // Block 0x49, offset 0x1240 + 0x1240: 0x0589, 0x1241: 0x058d, 0x1242: 0x0595, 0x1243: 0x0599, 0x1244: 0x059d, 0x1245: 0x05a1, + 0x1246: 0x05a5, 0x1247: 0x05a9, 0x1248: 0x05ad, 0x1249: 0x05b1, 0x124a: 0x05b5, 0x124b: 0x05b9, + 0x124c: 0x05bd, 0x124d: 0x05c1, 0x124e: 0x05c5, 0x124f: 0x05c9, 0x1250: 0x05cd, 0x1251: 0x05d1, + 0x1252: 0x05d5, 0x1253: 0x05d9, 0x1254: 0x05e1, 0x1255: 0x05e9, 0x1256: 0x05f1, 0x1257: 0x05f5, + 0x1258: 0x05f9, 0x1259: 0x05fd, 0x125a: 0x0601, 0x125b: 0x0605, 0x125c: 0x0609, 0x125d: 0x0619, + 0x125e: 0x49aa, 0x125f: 0x49b0, 0x1260: 0x03bd, 0x1261: 0x030d, 0x1262: 0x0311, 0x1263: 0x0435, + 0x1264: 0x0315, 0x1265: 0x0439, 0x1266: 0x043d, 0x1267: 0x0319, 0x1268: 0x031d, 0x1269: 0x0321, + 0x126a: 0x0441, 0x126b: 0x0445, 0x126c: 0x0449, 0x126d: 0x044d, 0x126e: 0x0451, 0x126f: 0x0455, + 0x1270: 0x0361, 0x1271: 0x0325, 0x1272: 0x0329, 0x1273: 0x032d, 0x1274: 0x0375, 0x1275: 0x0331, + 0x1276: 0x0335, 0x1277: 0x0339, 0x1278: 0x033d, 0x1279: 0x0341, 0x127a: 0x0345, 0x127b: 0x0349, + 0x127c: 0x034d, 0x127d: 0x0351, 0x127e: 0x0355, + // Block 0x4a, offset 0x1280 + 0x1280: 0x0063, 0x1281: 0x0065, 0x1282: 0x0067, 0x1283: 0x0069, 0x1284: 0x006b, 0x1285: 0x006d, + 0x1286: 0x006f, 0x1287: 0x0071, 0x1288: 0x0073, 0x1289: 0x0075, 0x128a: 0x0083, 0x128b: 0x0085, + 0x128c: 0x0087, 0x128d: 0x0089, 0x128e: 0x008b, 0x128f: 0x008d, 0x1290: 0x008f, 0x1291: 0x0091, + 0x1292: 0x0093, 0x1293: 0x0095, 0x1294: 0x0097, 0x1295: 0x0099, 0x1296: 0x009b, 0x1297: 0x009d, + 0x1298: 0x009f, 0x1299: 0x00a1, 0x129a: 0x00a3, 0x129b: 0x00a5, 0x129c: 0x00a7, 0x129d: 0x00a9, + 0x129e: 0x00ab, 0x129f: 0x00ad, 0x12a0: 0x00af, 0x12a1: 0x00b1, 0x12a2: 0x00b3, 0x12a3: 0x00b5, + 0x12a4: 0x00e0, 0x12a5: 0x00f5, 0x12a8: 0x0173, 0x12a9: 0x0176, + 0x12aa: 0x0179, 0x12ab: 0x017c, 0x12ac: 0x017f, 0x12ad: 0x0182, 0x12ae: 0x0185, 0x12af: 0x0188, + 0x12b0: 0x018b, 0x12b1: 0x018e, 0x12b2: 0x0191, 0x12b3: 0x0194, 0x12b4: 0x0197, 0x12b5: 0x019a, + 0x12b6: 0x019d, 0x12b7: 0x01a0, 0x12b8: 0x01a3, 0x12b9: 0x0188, 0x12ba: 0x01a6, 0x12bb: 0x01a9, + 0x12bc: 0x01ac, 0x12bd: 0x01af, 0x12be: 0x01b2, 0x12bf: 0x01b5, + // Block 0x4b, offset 0x12c0 + 0x12c0: 0x01fd, 0x12c1: 0x0200, 0x12c2: 0x0203, 0x12c3: 0x04cd, 0x12c4: 0x01c7, 0x12c5: 0x01d0, + 0x12c6: 0x01d6, 0x12c7: 0x01fa, 0x12c8: 0x01eb, 0x12c9: 0x01e8, 0x12ca: 0x0206, 0x12cb: 0x0209, + 0x12ce: 0x0021, 0x12cf: 0x0023, 0x12d0: 0x0025, 0x12d1: 0x0027, + 0x12d2: 0x0029, 0x12d3: 0x002b, 0x12d4: 0x002d, 0x12d5: 0x002f, 0x12d6: 0x0031, 0x12d7: 0x0033, + 0x12d8: 0x0021, 0x12d9: 0x0023, 0x12da: 0x0025, 0x12db: 0x0027, 0x12dc: 0x0029, 0x12dd: 0x002b, + 0x12de: 0x002d, 0x12df: 0x002f, 0x12e0: 0x0031, 0x12e1: 0x0033, 0x12e2: 0x0021, 0x12e3: 0x0023, + 0x12e4: 0x0025, 0x12e5: 0x0027, 0x12e6: 0x0029, 0x12e7: 0x002b, 0x12e8: 0x002d, 0x12e9: 0x002f, + 0x12ea: 0x0031, 0x12eb: 0x0033, 0x12ec: 0x0021, 0x12ed: 0x0023, 0x12ee: 0x0025, 0x12ef: 0x0027, + 0x12f0: 0x0029, 0x12f1: 0x002b, 0x12f2: 0x002d, 0x12f3: 0x002f, 0x12f4: 0x0031, 0x12f5: 0x0033, + 0x12f6: 0x0021, 0x12f7: 0x0023, 0x12f8: 0x0025, 0x12f9: 0x0027, 0x12fa: 0x0029, 0x12fb: 0x002b, + 0x12fc: 0x002d, 0x12fd: 0x002f, 0x12fe: 0x0031, 0x12ff: 0x0033, + // Block 0x4c, offset 0x1300 + 0x1300: 0x0233, 0x1301: 0x0236, 0x1302: 0x0242, 0x1303: 0x024b, 0x1305: 0x0284, + 0x1306: 0x0254, 0x1307: 0x0245, 0x1308: 0x0263, 0x1309: 0x028a, 0x130a: 0x0275, 0x130b: 0x0278, + 0x130c: 0x027b, 0x130d: 0x027e, 0x130e: 0x0257, 0x130f: 0x0269, 0x1310: 0x026f, 0x1311: 0x025d, + 0x1312: 0x0272, 0x1313: 0x0251, 0x1314: 0x025a, 0x1315: 0x023c, 0x1316: 0x023f, 0x1317: 0x0248, + 0x1318: 0x024e, 0x1319: 0x0260, 0x131a: 0x0266, 0x131b: 0x026c, 0x131c: 0x028d, 0x131d: 0x02de, + 0x131e: 0x02c6, 0x131f: 0x0290, 0x1321: 0x0236, 0x1322: 0x0242, + 0x1324: 0x0281, 0x1327: 0x0245, 0x1329: 0x028a, + 0x132a: 0x0275, 0x132b: 0x0278, 0x132c: 0x027b, 0x132d: 0x027e, 0x132e: 0x0257, 0x132f: 0x0269, + 0x1330: 0x026f, 0x1331: 0x025d, 0x1332: 0x0272, 0x1334: 0x025a, 0x1335: 0x023c, + 0x1336: 0x023f, 0x1337: 0x0248, 0x1339: 0x0260, 0x133b: 0x026c, + // Block 0x4d, offset 0x1340 + 0x1342: 0x0242, + 0x1347: 0x0245, 0x1349: 0x028a, 0x134b: 0x0278, + 0x134d: 0x027e, 0x134e: 0x0257, 0x134f: 0x0269, 0x1351: 0x025d, + 0x1352: 0x0272, 0x1354: 0x025a, 0x1357: 0x0248, + 0x1359: 0x0260, 0x135b: 0x026c, 0x135d: 0x02de, + 0x135f: 0x0290, 0x1361: 0x0236, 0x1362: 0x0242, + 0x1364: 0x0281, 0x1367: 0x0245, 0x1368: 0x0263, 0x1369: 0x028a, + 0x136a: 0x0275, 0x136c: 0x027b, 0x136d: 0x027e, 0x136e: 0x0257, 0x136f: 0x0269, + 0x1370: 0x026f, 0x1371: 0x025d, 0x1372: 0x0272, 0x1374: 0x025a, 0x1375: 0x023c, + 0x1376: 0x023f, 0x1377: 0x0248, 0x1379: 0x0260, 0x137a: 0x0266, 0x137b: 0x026c, + 0x137c: 0x028d, 0x137e: 0x02c6, + // Block 0x4e, offset 0x1380 + 0x1380: 0x0233, 0x1381: 0x0236, 0x1382: 0x0242, 0x1383: 0x024b, 0x1384: 0x0281, 0x1385: 0x0284, + 0x1386: 0x0254, 0x1387: 0x0245, 0x1388: 0x0263, 0x1389: 0x028a, 0x138b: 0x0278, + 0x138c: 0x027b, 0x138d: 0x027e, 0x138e: 0x0257, 0x138f: 0x0269, 0x1390: 0x026f, 0x1391: 0x025d, + 0x1392: 0x0272, 0x1393: 0x0251, 0x1394: 0x025a, 0x1395: 0x023c, 0x1396: 0x023f, 0x1397: 0x0248, + 0x1398: 0x024e, 0x1399: 0x0260, 0x139a: 0x0266, 0x139b: 0x026c, + 0x13a1: 0x0236, 0x13a2: 0x0242, 0x13a3: 0x024b, + 0x13a5: 0x0284, 0x13a6: 0x0254, 0x13a7: 0x0245, 0x13a8: 0x0263, 0x13a9: 0x028a, + 0x13ab: 0x0278, 0x13ac: 0x027b, 0x13ad: 0x027e, 0x13ae: 0x0257, 0x13af: 0x0269, + 0x13b0: 0x026f, 0x13b1: 0x025d, 0x13b2: 0x0272, 0x13b3: 0x0251, 0x13b4: 0x025a, 0x13b5: 0x023c, + 0x13b6: 0x023f, 0x13b7: 0x0248, 0x13b8: 0x024e, 0x13b9: 0x0260, 0x13ba: 0x0266, 0x13bb: 0x026c, + // Block 0x4f, offset 0x13c0 + 0x13c0: 0x19bb, 0x13c1: 0x19b8, 0x13c2: 0x19be, 0x13c3: 0x19e2, 0x13c4: 0x1a06, 0x13c5: 0x1a2a, + 0x13c6: 0x1a4e, 0x13c7: 0x1a57, 0x13c8: 0x1a5d, 0x13c9: 0x1a63, 0x13ca: 0x1a69, + 0x13d0: 0x1bce, 0x13d1: 0x1bd2, + 0x13d2: 0x1bd6, 0x13d3: 0x1bda, 0x13d4: 0x1bde, 0x13d5: 0x1be2, 0x13d6: 0x1be6, 0x13d7: 0x1bea, + 0x13d8: 0x1bee, 0x13d9: 0x1bf2, 0x13da: 0x1bf6, 0x13db: 0x1bfa, 0x13dc: 0x1bfe, 0x13dd: 0x1c02, + 0x13de: 0x1c06, 0x13df: 0x1c0a, 0x13e0: 0x1c0e, 0x13e1: 0x1c12, 0x13e2: 0x1c16, 0x13e3: 0x1c1a, + 0x13e4: 0x1c1e, 0x13e5: 0x1c22, 0x13e6: 0x1c26, 0x13e7: 0x1c2a, 0x13e8: 0x1c2e, 0x13e9: 0x1c32, + 0x13ea: 0x28c9, 0x13eb: 0x0047, 0x13ec: 0x0065, 0x13ed: 0x1a7e, 0x13ee: 0x1af3, + 0x13f0: 0x0043, 0x13f1: 0x0045, 0x13f2: 0x0047, 0x13f3: 0x0049, 0x13f4: 0x004b, 0x13f5: 0x004d, + 0x13f6: 0x004f, 0x13f7: 0x0051, 0x13f8: 0x0053, 0x13f9: 0x0055, 0x13fa: 0x0057, 0x13fb: 0x0059, + 0x13fc: 0x005b, 0x13fd: 0x005d, 0x13fe: 0x005f, 0x13ff: 0x0061, + // Block 0x50, offset 0x1400 + 0x1400: 0x2858, 0x1401: 0x286d, 0x1402: 0x0575, + 0x1410: 0x0c81, 0x1411: 0x0ab9, + 0x1412: 0x0945, 0x1413: 0x45aa, 0x1414: 0x078d, 0x1415: 0x0a61, 0x1416: 0x13a1, 0x1417: 0x0a71, + 0x1418: 0x0799, 0x1419: 0x0d49, 0x141a: 0x0f21, 0x141b: 0x0d21, 0x141c: 0x0899, 0x141d: 0x0bdd, + 0x141e: 0x0831, 0x141f: 0x0d29, 0x1420: 0x0885, 0x1421: 0x1189, 0x1422: 0x0ff5, 0x1423: 0x13fd, + 0x1424: 0x0a45, 0x1425: 0x097d, 0x1426: 0x0ed5, 0x1427: 0x0c8d, 0x1428: 0x0cb9, 0x1429: 0x0731, + 0x142a: 0x073d, 0x142b: 0x147d, 0x142c: 0x0b4d, 0x142d: 0x0759, 0x142e: 0x0961, 0x142f: 0x0cad, + 0x1430: 0x1425, 0x1431: 0x0c85, 0x1432: 0x10e1, 0x1433: 0x111d, 0x1434: 0x0969, 0x1435: 0x0eb5, + 0x1436: 0x0d7d, 0x1437: 0x0d79, 0x1438: 0x1009, 0x1439: 0x089d, 0x143a: 0x09c9, + // Block 0x51, offset 0x1440 + 0x1440: 0x076d, 0x1441: 0x0765, 0x1442: 0x0775, 0x1443: 0x16a9, 0x1444: 0x07b9, 0x1445: 0x07c9, + 0x1446: 0x07cd, 0x1447: 0x07d5, 0x1448: 0x07dd, 0x1449: 0x07e1, 0x144a: 0x07ed, 0x144b: 0x07e5, + 0x144c: 0x0625, 0x144d: 0x16bd, 0x144e: 0x0801, 0x144f: 0x0805, 0x1450: 0x0809, 0x1451: 0x0825, + 0x1452: 0x16ae, 0x1453: 0x0629, 0x1454: 0x0811, 0x1455: 0x0831, 0x1456: 0x16b8, 0x1457: 0x0841, + 0x1458: 0x0849, 0x1459: 0x07a9, 0x145a: 0x0851, 0x145b: 0x0855, 0x145c: 0x1893, 0x145d: 0x0871, + 0x145e: 0x0879, 0x145f: 0x0631, 0x1460: 0x0891, 0x1461: 0x0895, 0x1462: 0x089d, 0x1463: 0x08a1, + 0x1464: 0x0635, 0x1465: 0x08b9, 0x1466: 0x08bd, 0x1467: 0x08c9, 0x1468: 0x08d5, 0x1469: 0x08d9, + 0x146a: 0x08dd, 0x146b: 0x08e5, 0x146c: 0x0905, 0x146d: 0x0909, 0x146e: 0x0911, 0x146f: 0x0921, + 0x1470: 0x0929, 0x1471: 0x092d, 0x1472: 0x092d, 0x1473: 0x092d, 0x1474: 0x16cc, 0x1475: 0x0f05, + 0x1476: 0x0941, 0x1477: 0x0949, 0x1478: 0x16d1, 0x1479: 0x0955, 0x147a: 0x095d, 0x147b: 0x0965, + 0x147c: 0x098d, 0x147d: 0x0979, 0x147e: 0x0985, 0x147f: 0x0989, + // Block 0x52, offset 0x1480 + 0x1480: 0x0991, 0x1481: 0x0999, 0x1482: 0x099d, 0x1483: 0x09a5, 0x1484: 0x09ad, 0x1485: 0x09b1, + 0x1486: 0x09b1, 0x1487: 0x09b9, 0x1488: 0x09c1, 0x1489: 0x09c5, 0x148a: 0x09d1, 0x148b: 0x09f5, + 0x148c: 0x09d9, 0x148d: 0x09f9, 0x148e: 0x09dd, 0x148f: 0x09e5, 0x1490: 0x087d, 0x1491: 0x0a41, + 0x1492: 0x0a09, 0x1493: 0x0a0d, 0x1494: 0x0a11, 0x1495: 0x0a05, 0x1496: 0x0a19, 0x1497: 0x0a15, + 0x1498: 0x0a2d, 0x1499: 0x16d6, 0x149a: 0x0a49, 0x149b: 0x0a4d, 0x149c: 0x0a55, 0x149d: 0x0a61, + 0x149e: 0x0a69, 0x149f: 0x0a85, 0x14a0: 0x16db, 0x14a1: 0x16e0, 0x14a2: 0x0a91, 0x14a3: 0x0a95, + 0x14a4: 0x0a99, 0x14a5: 0x0a8d, 0x14a6: 0x0aa1, 0x14a7: 0x0639, 0x14a8: 0x063d, 0x14a9: 0x0aa9, + 0x14aa: 0x0ab1, 0x14ab: 0x0ab1, 0x14ac: 0x16e5, 0x14ad: 0x0acd, 0x14ae: 0x0ad1, 0x14af: 0x0ad5, + 0x14b0: 0x0add, 0x14b1: 0x16ea, 0x14b2: 0x0ae5, 0x14b3: 0x0ae9, 0x14b4: 0x0bc1, 0x14b5: 0x0af1, + 0x14b6: 0x0641, 0x14b7: 0x0afd, 0x14b8: 0x0b0d, 0x14b9: 0x0b19, 0x14ba: 0x0b15, 0x14bb: 0x16f4, + 0x14bc: 0x0b21, 0x14bd: 0x16f9, 0x14be: 0x0b2d, 0x14bf: 0x0b29, + // Block 0x53, offset 0x14c0 + 0x14c0: 0x0b31, 0x14c1: 0x0b41, 0x14c2: 0x0b45, 0x14c3: 0x0645, 0x14c4: 0x0b55, 0x14c5: 0x0b5d, + 0x14c6: 0x0b61, 0x14c7: 0x0b65, 0x14c8: 0x0649, 0x14c9: 0x16fe, 0x14ca: 0x064d, 0x14cb: 0x0b81, + 0x14cc: 0x0b85, 0x14cd: 0x0b89, 0x14ce: 0x0b91, 0x14cf: 0x18c5, 0x14d0: 0x0ba9, 0x14d1: 0x1708, + 0x14d2: 0x1708, 0x14d3: 0x1249, 0x14d4: 0x0bb9, 0x14d5: 0x0bb9, 0x14d6: 0x0651, 0x14d7: 0x172b, + 0x14d8: 0x17fd, 0x14d9: 0x0bc9, 0x14da: 0x0bd1, 0x14db: 0x0655, 0x14dc: 0x0be5, 0x14dd: 0x0bf5, + 0x14de: 0x0bf9, 0x14df: 0x0c01, 0x14e0: 0x0c11, 0x14e1: 0x065d, 0x14e2: 0x0659, 0x14e3: 0x0c15, + 0x14e4: 0x170d, 0x14e5: 0x0c19, 0x14e6: 0x0c2d, 0x14e7: 0x0c31, 0x14e8: 0x0c35, 0x14e9: 0x0c31, + 0x14ea: 0x0c41, 0x14eb: 0x0c45, 0x14ec: 0x0c55, 0x14ed: 0x0c4d, 0x14ee: 0x0c51, 0x14ef: 0x0c59, + 0x14f0: 0x0c5d, 0x14f1: 0x0c61, 0x14f2: 0x0c6d, 0x14f3: 0x0c71, 0x14f4: 0x0c89, 0x14f5: 0x0c91, + 0x14f6: 0x0ca1, 0x14f7: 0x0cb5, 0x14f8: 0x171c, 0x14f9: 0x0cb1, 0x14fa: 0x0ca5, 0x14fb: 0x0cbd, + 0x14fc: 0x0cc5, 0x14fd: 0x0cd9, 0x14fe: 0x1721, 0x14ff: 0x0ce1, + // Block 0x54, offset 0x1500 + 0x1500: 0x0cd5, 0x1501: 0x0ccd, 0x1502: 0x0661, 0x1503: 0x0ce9, 0x1504: 0x0cf1, 0x1505: 0x0cf9, + 0x1506: 0x0ced, 0x1507: 0x0665, 0x1508: 0x0d09, 0x1509: 0x0d11, 0x150a: 0x1726, 0x150b: 0x0d3d, + 0x150c: 0x0d71, 0x150d: 0x0d4d, 0x150e: 0x0671, 0x150f: 0x0d59, 0x1510: 0x066d, 0x1511: 0x0669, + 0x1512: 0x0835, 0x1513: 0x0839, 0x1514: 0x0d75, 0x1515: 0x0d5d, 0x1516: 0x121d, 0x1517: 0x06d5, + 0x1518: 0x0d81, 0x1519: 0x0d85, 0x151a: 0x0d89, 0x151b: 0x0d9d, 0x151c: 0x0d95, 0x151d: 0x173f, + 0x151e: 0x0675, 0x151f: 0x0db1, 0x1520: 0x0da5, 0x1521: 0x0dc1, 0x1522: 0x0dc9, 0x1523: 0x1749, + 0x1524: 0x0dcd, 0x1525: 0x0db9, 0x1526: 0x0dd5, 0x1527: 0x0679, 0x1528: 0x0dd9, 0x1529: 0x0ddd, + 0x152a: 0x0de1, 0x152b: 0x0ded, 0x152c: 0x174e, 0x152d: 0x0df5, 0x152e: 0x067d, 0x152f: 0x0e01, + 0x1530: 0x1753, 0x1531: 0x0e05, 0x1532: 0x0681, 0x1533: 0x0e11, 0x1534: 0x0e1d, 0x1535: 0x0e29, + 0x1536: 0x0e2d, 0x1537: 0x1758, 0x1538: 0x16ef, 0x1539: 0x175d, 0x153a: 0x0e4d, 0x153b: 0x1762, + 0x153c: 0x0e59, 0x153d: 0x0e61, 0x153e: 0x0e51, 0x153f: 0x0e6d, + // Block 0x55, offset 0x1540 + 0x1540: 0x0e7d, 0x1541: 0x0e8d, 0x1542: 0x0e81, 0x1543: 0x0e85, 0x1544: 0x0e91, 0x1545: 0x0e95, + 0x1546: 0x1767, 0x1547: 0x0e79, 0x1548: 0x0ead, 0x1549: 0x0eb1, 0x154a: 0x0685, 0x154b: 0x0ec5, + 0x154c: 0x0ec1, 0x154d: 0x176c, 0x154e: 0x0ea5, 0x154f: 0x0ee1, 0x1550: 0x1771, 0x1551: 0x1776, + 0x1552: 0x0ee5, 0x1553: 0x0ef9, 0x1554: 0x0ef5, 0x1555: 0x0ef1, 0x1556: 0x0689, 0x1557: 0x0efd, + 0x1558: 0x0f0d, 0x1559: 0x0f09, 0x155a: 0x0f15, 0x155b: 0x16b3, 0x155c: 0x0f25, 0x155d: 0x177b, + 0x155e: 0x0f31, 0x155f: 0x1785, 0x1560: 0x0f45, 0x1561: 0x0f51, 0x1562: 0x0f65, 0x1563: 0x178a, + 0x1564: 0x0f79, 0x1565: 0x0f7d, 0x1566: 0x178f, 0x1567: 0x1794, 0x1568: 0x0f99, 0x1569: 0x0fa9, + 0x156a: 0x068d, 0x156b: 0x0fad, 0x156c: 0x0691, 0x156d: 0x0691, 0x156e: 0x0fc5, 0x156f: 0x0fc9, + 0x1570: 0x0fd1, 0x1571: 0x0fd5, 0x1572: 0x0fe1, 0x1573: 0x0695, 0x1574: 0x0ff9, 0x1575: 0x1799, + 0x1576: 0x1015, 0x1577: 0x179e, 0x1578: 0x1021, 0x1579: 0x1703, 0x157a: 0x1031, 0x157b: 0x17a3, + 0x157c: 0x17a8, 0x157d: 0x17ad, 0x157e: 0x0699, 0x157f: 0x069d, + // Block 0x56, offset 0x1580 + 0x1580: 0x1069, 0x1581: 0x17b7, 0x1582: 0x17b2, 0x1583: 0x17bc, 0x1584: 0x17c1, 0x1585: 0x1071, + 0x1586: 0x1075, 0x1587: 0x1075, 0x1588: 0x107d, 0x1589: 0x06a5, 0x158a: 0x1081, 0x158b: 0x06a9, + 0x158c: 0x06ad, 0x158d: 0x17cb, 0x158e: 0x1095, 0x158f: 0x109d, 0x1590: 0x10a9, 0x1591: 0x06b1, + 0x1592: 0x17d0, 0x1593: 0x10cd, 0x1594: 0x17d5, 0x1595: 0x17da, 0x1596: 0x10ed, 0x1597: 0x1105, + 0x1598: 0x06b5, 0x1599: 0x110d, 0x159a: 0x1111, 0x159b: 0x1115, 0x159c: 0x17df, 0x159d: 0x17e4, + 0x159e: 0x17e4, 0x159f: 0x112d, 0x15a0: 0x06b9, 0x15a1: 0x17e9, 0x15a2: 0x1141, 0x15a3: 0x1145, + 0x15a4: 0x06bd, 0x15a5: 0x17ee, 0x15a6: 0x1161, 0x15a7: 0x06c1, 0x15a8: 0x1171, 0x15a9: 0x1169, + 0x15aa: 0x1179, 0x15ab: 0x17f8, 0x15ac: 0x1191, 0x15ad: 0x06c5, 0x15ae: 0x119d, 0x15af: 0x11a5, + 0x15b0: 0x11b5, 0x15b1: 0x06c9, 0x15b2: 0x1802, 0x15b3: 0x1807, 0x15b4: 0x06cd, 0x15b5: 0x180c, + 0x15b6: 0x11cd, 0x15b7: 0x1811, 0x15b8: 0x11d9, 0x15b9: 0x11e5, 0x15ba: 0x11ed, 0x15bb: 0x1816, + 0x15bc: 0x181b, 0x15bd: 0x1201, 0x15be: 0x1820, 0x15bf: 0x1209, + // Block 0x57, offset 0x15c0 + 0x15c0: 0x1730, 0x15c1: 0x06d1, 0x15c2: 0x1221, 0x15c3: 0x1225, 0x15c4: 0x06d9, 0x15c5: 0x1229, + 0x15c6: 0x0aa5, 0x15c7: 0x1825, 0x15c8: 0x182a, 0x15c9: 0x1735, 0x15ca: 0x173a, 0x15cb: 0x1249, + 0x15cc: 0x124d, 0x15cd: 0x1465, 0x15ce: 0x06dd, 0x15cf: 0x1279, 0x15d0: 0x1275, 0x15d1: 0x127d, + 0x15d2: 0x08b1, 0x15d3: 0x1281, 0x15d4: 0x1285, 0x15d5: 0x1289, 0x15d6: 0x1291, 0x15d7: 0x182f, + 0x15d8: 0x128d, 0x15d9: 0x1295, 0x15da: 0x12a9, 0x15db: 0x12ad, 0x15dc: 0x1299, 0x15dd: 0x12b1, + 0x15de: 0x12c5, 0x15df: 0x12d9, 0x15e0: 0x12a5, 0x15e1: 0x12b9, 0x15e2: 0x12bd, 0x15e3: 0x12c1, + 0x15e4: 0x1834, 0x15e5: 0x183e, 0x15e6: 0x1839, 0x15e7: 0x06e1, 0x15e8: 0x12e1, 0x15e9: 0x12e5, + 0x15ea: 0x12ed, 0x15eb: 0x1852, 0x15ec: 0x12f1, 0x15ed: 0x1843, 0x15ee: 0x06e5, 0x15ef: 0x06e9, + 0x15f0: 0x1848, 0x15f1: 0x184d, 0x15f2: 0x06ed, 0x15f3: 0x1311, 0x15f4: 0x1315, 0x15f5: 0x1319, + 0x15f6: 0x131d, 0x15f7: 0x1329, 0x15f8: 0x1325, 0x15f9: 0x1331, 0x15fa: 0x132d, 0x15fb: 0x133d, + 0x15fc: 0x1335, 0x15fd: 0x1339, 0x15fe: 0x1341, 0x15ff: 0x06f1, + // Block 0x58, offset 0x1600 + 0x1600: 0x1349, 0x1601: 0x134d, 0x1602: 0x06f5, 0x1603: 0x135d, 0x1604: 0x1361, 0x1605: 0x1857, + 0x1606: 0x136d, 0x1607: 0x1371, 0x1608: 0x06f9, 0x1609: 0x137d, 0x160a: 0x062d, 0x160b: 0x185c, + 0x160c: 0x1861, 0x160d: 0x06fd, 0x160e: 0x0701, 0x160f: 0x13a9, 0x1610: 0x13c1, 0x1611: 0x13dd, + 0x1612: 0x13ed, 0x1613: 0x1866, 0x1614: 0x1401, 0x1615: 0x1405, 0x1616: 0x141d, 0x1617: 0x1429, + 0x1618: 0x1870, 0x1619: 0x16c2, 0x161a: 0x1435, 0x161b: 0x1431, 0x161c: 0x143d, 0x161d: 0x16c7, + 0x161e: 0x1449, 0x161f: 0x1455, 0x1620: 0x1875, 0x1621: 0x187a, 0x1622: 0x1495, 0x1623: 0x14a1, + 0x1624: 0x14a9, 0x1625: 0x187f, 0x1626: 0x14ad, 0x1627: 0x14d5, 0x1628: 0x14e1, 0x1629: 0x14e5, + 0x162a: 0x14dd, 0x162b: 0x14f1, 0x162c: 0x14f5, 0x162d: 0x1884, 0x162e: 0x1501, 0x162f: 0x0705, + 0x1630: 0x1509, 0x1631: 0x1889, 0x1632: 0x0709, 0x1633: 0x1541, 0x1634: 0x0b35, 0x1635: 0x1559, + 0x1636: 0x188e, 0x1637: 0x1898, 0x1638: 0x070d, 0x1639: 0x0711, 0x163a: 0x1581, 0x163b: 0x189d, + 0x163c: 0x0715, 0x163d: 0x18a2, 0x163e: 0x1599, 0x163f: 0x1599, + // Block 0x59, offset 0x1640 + 0x1640: 0x15a1, 0x1641: 0x18a7, 0x1642: 0x15b9, 0x1643: 0x0719, 0x1644: 0x15c9, 0x1645: 0x15d5, + 0x1646: 0x15dd, 0x1647: 0x15e5, 0x1648: 0x071d, 0x1649: 0x18ac, 0x164a: 0x15f9, 0x164b: 0x1615, + 0x164c: 0x1621, 0x164d: 0x0721, 0x164e: 0x0725, 0x164f: 0x1625, 0x1650: 0x18b1, 0x1651: 0x0729, + 0x1652: 0x18b6, 0x1653: 0x18bb, 0x1654: 0x18c0, 0x1655: 0x1649, 0x1656: 0x072d, 0x1657: 0x165d, + 0x1658: 0x1665, 0x1659: 0x1669, 0x165a: 0x1671, 0x165b: 0x1679, 0x165c: 0x1681, 0x165d: 0x18ca, +} + +// nfkcSparseOffset: 128 entries, 256 bytes +var nfkcSparseOffset = []uint16{0x0, 0xe, 0x12, 0x1b, 0x25, 0x35, 0x37, 0x3c, 0x47, 0x56, 0x63, 0x6b, 0x6f, 0x74, 0x76, 0x85, 0x8d, 0x94, 0x97, 0x9f, 0xa3, 0xa7, 0xa9, 0xab, 0xb4, 0xb8, 0xbf, 0xc4, 0xc7, 0xd1, 0xd3, 0xda, 0xe2, 0xe6, 0xe8, 0xeb, 0xef, 0xf5, 0x106, 0x112, 0x114, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12b, 0x12e, 0x130, 0x133, 0x136, 0x13a, 0x143, 0x145, 0x148, 0x14a, 0x154, 0x15f, 0x16e, 0x17c, 0x18a, 0x19a, 0x1a8, 0x1af, 0x1b5, 0x1c4, 0x1c8, 0x1ca, 0x1ce, 0x1d0, 0x1d3, 0x1d5, 0x1d8, 0x1da, 0x1dd, 0x1df, 0x1e1, 0x1e3, 0x1ef, 0x1f8, 0x1ff, 0x20c, 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21c, 0x21e, 0x220, 0x222, 0x224, 0x22a, 0x22d, 0x22f, 0x231, 0x233, 0x235, 0x244, 0x246, 0x24c, 0x254, 0x25b, 0x25e, 0x26b, 0x275, 0x277, 0x279, 0x27d, 0x282, 0x28e, 0x293, 0x29c, 0x2a2, 0x2a7, 0x2ab, 0x2b0, 0x2b4, 0x2c4, 0x2d2, 0x2e0, 0x2ee, 0x2f8, 0x2fa} + +// nfkcSparseValues: 772 entries, 3088 bytes +var nfkcSparseValues = [772]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0002, lo: 0x0d}, + {value: 0x0001, lo: 0xa0, hi: 0xa0}, + {value: 0x425e, lo: 0xa8, hi: 0xa8}, + {value: 0x0083, lo: 0xaa, hi: 0xaa}, + {value: 0x424a, lo: 0xaf, hi: 0xaf}, + {value: 0x0025, lo: 0xb2, hi: 0xb3}, + {value: 0x4240, lo: 0xb4, hi: 0xb4}, + {value: 0x01dc, lo: 0xb5, hi: 0xb5}, + {value: 0x4277, lo: 0xb8, hi: 0xb8}, + {value: 0x0023, lo: 0xb9, hi: 0xb9}, + {value: 0x009f, lo: 0xba, hi: 0xba}, + {value: 0x235e, lo: 0xbc, hi: 0xbc}, + {value: 0x2352, lo: 0xbd, hi: 0xbd}, + {value: 0x23f4, lo: 0xbe, hi: 0xbe}, + // Block 0x1, offset 0x2 + {value: 0x0091, lo: 0x03}, + {value: 0x46c8, lo: 0xa0, hi: 0xa1}, + {value: 0x46fa, lo: 0xaf, hi: 0xb0}, + {value: 0x8800, lo: 0xb7, hi: 0xb7}, + // Block 0x2, offset 0x3 + {value: 0x0003, lo: 0x08}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x0091, lo: 0xb0, hi: 0xb0}, + {value: 0x011c, lo: 0xb1, hi: 0xb1}, + {value: 0x0095, lo: 0xb2, hi: 0xb2}, + {value: 0x00a5, lo: 0xb3, hi: 0xb3}, + {value: 0x0143, lo: 0xb4, hi: 0xb6}, + {value: 0x00af, lo: 0xb7, hi: 0xb7}, + {value: 0x00b3, lo: 0xb8, hi: 0xb8}, + // Block 0x3, offset 0x4 + {value: 0x000a, lo: 0x09}, + {value: 0x4254, lo: 0x98, hi: 0x98}, + {value: 0x4259, lo: 0x99, hi: 0x9a}, + {value: 0x427c, lo: 0x9b, hi: 0x9b}, + {value: 0x4245, lo: 0x9c, hi: 0x9c}, + {value: 0x4268, lo: 0x9d, hi: 0x9d}, + {value: 0x0116, lo: 0xa0, hi: 0xa0}, + {value: 0x0099, lo: 0xa1, hi: 0xa1}, + {value: 0x00a7, lo: 0xa2, hi: 0xa3}, + {value: 0x0167, lo: 0xa4, hi: 0xa4}, + // Block 0x4, offset 0x5 + {value: 0x0000, lo: 0x0f}, + {value: 0x8800, lo: 0x83, hi: 0x83}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x8800, lo: 0x8b, hi: 0x8b}, + {value: 0x8800, lo: 0x8d, hi: 0x8d}, + {value: 0x378f, lo: 0x90, hi: 0x90}, + {value: 0x379b, lo: 0x91, hi: 0x91}, + {value: 0x3789, lo: 0x93, hi: 0x93}, + {value: 0x8800, lo: 0x96, hi: 0x96}, + {value: 0x3801, lo: 0x97, hi: 0x97}, + {value: 0x37cb, lo: 0x9c, hi: 0x9c}, + {value: 0x37b3, lo: 0x9d, hi: 0x9d}, + {value: 0x37dd, lo: 0x9e, hi: 0x9e}, + {value: 0x8800, lo: 0xb4, hi: 0xb5}, + {value: 0x3807, lo: 0xb6, hi: 0xb6}, + {value: 0x380d, lo: 0xb7, hi: 0xb7}, + // Block 0x5, offset 0x6 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0x83, hi: 0x87}, + // Block 0x6, offset 0x7 + {value: 0x0001, lo: 0x04}, + {value: 0x8018, lo: 0x81, hi: 0x82}, + {value: 0x80e6, lo: 0x84, hi: 0x84}, + {value: 0x80dc, lo: 0x85, hi: 0x85}, + {value: 0x8012, lo: 0x87, hi: 0x87}, + // Block 0x7, offset 0x8 + {value: 0x0000, lo: 0x0a}, + {value: 0x80e6, lo: 0x90, hi: 0x97}, + {value: 0x801e, lo: 0x98, hi: 0x98}, + {value: 0x801f, lo: 0x99, hi: 0x99}, + {value: 0x8020, lo: 0x9a, hi: 0x9a}, + {value: 0x382b, lo: 0xa2, hi: 0xa2}, + {value: 0x3831, lo: 0xa3, hi: 0xa3}, + {value: 0x383d, lo: 0xa4, hi: 0xa4}, + {value: 0x3837, lo: 0xa5, hi: 0xa5}, + {value: 0x3843, lo: 0xa6, hi: 0xa6}, + {value: 0x8800, lo: 0xa7, hi: 0xa7}, + // Block 0x8, offset 0x9 + {value: 0x0000, lo: 0x0e}, + {value: 0x3855, lo: 0x80, hi: 0x80}, + {value: 0x8800, lo: 0x81, hi: 0x81}, + {value: 0x3849, lo: 0x82, hi: 0x82}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x384f, lo: 0x93, hi: 0x93}, + {value: 0x8800, lo: 0x95, hi: 0x95}, + {value: 0x80e6, lo: 0x96, hi: 0x9c}, + {value: 0x80e6, lo: 0x9f, hi: 0xa2}, + {value: 0x80dc, lo: 0xa3, hi: 0xa3}, + {value: 0x80e6, lo: 0xa4, hi: 0xa4}, + {value: 0x80e6, lo: 0xa7, hi: 0xa8}, + {value: 0x80dc, lo: 0xaa, hi: 0xaa}, + {value: 0x80e6, lo: 0xab, hi: 0xac}, + {value: 0x80dc, lo: 0xad, hi: 0xad}, + // Block 0x9, offset 0xa + {value: 0x0000, lo: 0x0c}, + {value: 0x8024, lo: 0x91, hi: 0x91}, + {value: 0x80e6, lo: 0xb0, hi: 0xb0}, + {value: 0x80dc, lo: 0xb1, hi: 0xb1}, + {value: 0x80e6, lo: 0xb2, hi: 0xb3}, + {value: 0x80dc, lo: 0xb4, hi: 0xb4}, + {value: 0x80e6, lo: 0xb5, hi: 0xb6}, + {value: 0x80dc, lo: 0xb7, hi: 0xb9}, + {value: 0x80e6, lo: 0xba, hi: 0xba}, + {value: 0x80dc, lo: 0xbb, hi: 0xbc}, + {value: 0x80e6, lo: 0xbd, hi: 0xbd}, + {value: 0x80dc, lo: 0xbe, hi: 0xbe}, + {value: 0x80e6, lo: 0xbf, hi: 0xbf}, + // Block 0xa, offset 0xb + {value: 0x000a, lo: 0x07}, + {value: 0x80e6, lo: 0x80, hi: 0x80}, + {value: 0x80e6, lo: 0x81, hi: 0x81}, + {value: 0x80dc, lo: 0x82, hi: 0x83}, + {value: 0x80dc, lo: 0x84, hi: 0x85}, + {value: 0x80dc, lo: 0x86, hi: 0x87}, + {value: 0x80dc, lo: 0x88, hi: 0x89}, + {value: 0x80e6, lo: 0x8a, hi: 0x8a}, + // Block 0xb, offset 0xc + {value: 0x0000, lo: 0x03}, + {value: 0x80e6, lo: 0xab, hi: 0xb1}, + {value: 0x80dc, lo: 0xb2, hi: 0xb2}, + {value: 0x80e6, lo: 0xb3, hi: 0xb3}, + // Block 0xc, offset 0xd + {value: 0x0000, lo: 0x04}, + {value: 0x80e6, lo: 0x96, hi: 0x99}, + {value: 0x80e6, lo: 0x9b, hi: 0xa3}, + {value: 0x80e6, lo: 0xa5, hi: 0xa7}, + {value: 0x80e6, lo: 0xa9, hi: 0xad}, + // Block 0xd, offset 0xe + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0x99, hi: 0x9b}, + // Block 0xe, offset 0xf + {value: 0x0000, lo: 0x0e}, + {value: 0x80e6, lo: 0xa4, hi: 0xa5}, + {value: 0x80dc, lo: 0xa6, hi: 0xa6}, + {value: 0x80e6, lo: 0xa7, hi: 0xa8}, + {value: 0x80dc, lo: 0xa9, hi: 0xa9}, + {value: 0x80e6, lo: 0xaa, hi: 0xac}, + {value: 0x80dc, lo: 0xad, hi: 0xaf}, + {value: 0x801b, lo: 0xb0, hi: 0xb0}, + {value: 0x801c, lo: 0xb1, hi: 0xb1}, + {value: 0x801d, lo: 0xb2, hi: 0xb2}, + {value: 0x80e6, lo: 0xb3, hi: 0xb5}, + {value: 0x80dc, lo: 0xb6, hi: 0xb6}, + {value: 0x80e6, lo: 0xb7, hi: 0xb8}, + {value: 0x80dc, lo: 0xb9, hi: 0xba}, + {value: 0x80e6, lo: 0xbb, hi: 0xbe}, + // Block 0xf, offset 0x10 + {value: 0x0000, lo: 0x07}, + {value: 0x8800, lo: 0xa8, hi: 0xa8}, + {value: 0x3ec2, lo: 0xa9, hi: 0xa9}, + {value: 0x8800, lo: 0xb0, hi: 0xb0}, + {value: 0x3eca, lo: 0xb1, hi: 0xb1}, + {value: 0x8800, lo: 0xb3, hi: 0xb3}, + {value: 0x3ed2, lo: 0xb4, hi: 0xb4}, + {value: 0x8607, lo: 0xbc, hi: 0xbc}, + // Block 0x10, offset 0x11 + {value: 0x0008, lo: 0x06}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x80e6, lo: 0x91, hi: 0x91}, + {value: 0x80dc, lo: 0x92, hi: 0x92}, + {value: 0x80e6, lo: 0x93, hi: 0x93}, + {value: 0x80e6, lo: 0x94, hi: 0x94}, + {value: 0x4502, lo: 0x98, hi: 0x9f}, + // Block 0x11, offset 0x12 + {value: 0x0000, lo: 0x02}, + {value: 0x8007, lo: 0xbc, hi: 0xbc}, + {value: 0x8600, lo: 0xbe, hi: 0xbe}, + // Block 0x12, offset 0x13 + {value: 0x0007, lo: 0x07}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x18cf, lo: 0x8b, hi: 0x8c}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x97, hi: 0x97}, + {value: 0x4542, lo: 0x9c, hi: 0x9c}, + {value: 0x454a, lo: 0x9d, hi: 0x9d}, + {value: 0x4552, lo: 0x9f, hi: 0x9f}, + // Block 0x13, offset 0x14 + {value: 0x0000, lo: 0x03}, + {value: 0x457a, lo: 0xb3, hi: 0xb3}, + {value: 0x4582, lo: 0xb6, hi: 0xb6}, + {value: 0x8007, lo: 0xbc, hi: 0xbc}, + // Block 0x14, offset 0x15 + {value: 0x0008, lo: 0x03}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x455a, lo: 0x99, hi: 0x9b}, + {value: 0x4572, lo: 0x9e, hi: 0x9e}, + // Block 0x15, offset 0x16 + {value: 0x0000, lo: 0x01}, + {value: 0x8007, lo: 0xbc, hi: 0xbc}, + // Block 0x16, offset 0x17 + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + // Block 0x17, offset 0x18 + {value: 0x0000, lo: 0x08}, + {value: 0x8800, lo: 0x87, hi: 0x87}, + {value: 0x18e4, lo: 0x88, hi: 0x88}, + {value: 0x18dd, lo: 0x8b, hi: 0x8b}, + {value: 0x18eb, lo: 0x8c, hi: 0x8c}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x96, hi: 0x97}, + {value: 0x458a, lo: 0x9c, hi: 0x9c}, + {value: 0x4592, lo: 0x9d, hi: 0x9d}, + // Block 0x18, offset 0x19 + {value: 0x0000, lo: 0x03}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x18f2, lo: 0x94, hi: 0x94}, + {value: 0x8600, lo: 0xbe, hi: 0xbe}, + // Block 0x19, offset 0x1a + {value: 0x0000, lo: 0x06}, + {value: 0x8800, lo: 0x86, hi: 0x87}, + {value: 0x18f9, lo: 0x8a, hi: 0x8a}, + {value: 0x1907, lo: 0x8b, hi: 0x8b}, + {value: 0x1900, lo: 0x8c, hi: 0x8c}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x97, hi: 0x97}, + // Block 0x1a, offset 0x1b + {value: 0x0607, lo: 0x04}, + {value: 0x8800, lo: 0x86, hi: 0x86}, + {value: 0x3eda, lo: 0x88, hi: 0x88}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8054, lo: 0x95, hi: 0x96}, + // Block 0x1b, offset 0x1c + {value: 0x0000, lo: 0x02}, + {value: 0x8007, lo: 0xbc, hi: 0xbc}, + {value: 0x8800, lo: 0xbf, hi: 0xbf}, + // Block 0x1c, offset 0x1d + {value: 0x0000, lo: 0x09}, + {value: 0x190e, lo: 0x80, hi: 0x80}, + {value: 0x8600, lo: 0x82, hi: 0x82}, + {value: 0x8800, lo: 0x86, hi: 0x86}, + {value: 0x1915, lo: 0x87, hi: 0x87}, + {value: 0x191c, lo: 0x88, hi: 0x88}, + {value: 0x2e66, lo: 0x8a, hi: 0x8a}, + {value: 0x19a5, lo: 0x8b, hi: 0x8b}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x95, hi: 0x96}, + // Block 0x1d, offset 0x1e + {value: 0x0000, lo: 0x01}, + {value: 0x8600, lo: 0xbe, hi: 0xbe}, + // Block 0x1e, offset 0x1f + {value: 0x0000, lo: 0x06}, + {value: 0x8800, lo: 0x86, hi: 0x87}, + {value: 0x1923, lo: 0x8a, hi: 0x8a}, + {value: 0x1931, lo: 0x8b, hi: 0x8b}, + {value: 0x192a, lo: 0x8c, hi: 0x8c}, + {value: 0x8009, lo: 0x8d, hi: 0x8d}, + {value: 0x8600, lo: 0x97, hi: 0x97}, + // Block 0x1f, offset 0x20 + {value: 0x0007, lo: 0x07}, + {value: 0x8609, lo: 0x8a, hi: 0x8a}, + {value: 0x8600, lo: 0x8f, hi: 0x8f}, + {value: 0x8800, lo: 0x99, hi: 0x99}, + {value: 0x3ee2, lo: 0x9a, hi: 0x9a}, + {value: 0x2e6d, lo: 0x9c, hi: 0x9d}, + {value: 0x1938, lo: 0x9e, hi: 0x9e}, + {value: 0x8600, lo: 0x9f, hi: 0x9f}, + // Block 0x20, offset 0x21 + {value: 0x0000, lo: 0x03}, + {value: 0x2763, lo: 0xb3, hi: 0xb3}, + {value: 0x8067, lo: 0xb8, hi: 0xb9}, + {value: 0x8009, lo: 0xba, hi: 0xba}, + // Block 0x21, offset 0x22 + {value: 0x0000, lo: 0x01}, + {value: 0x806b, lo: 0x88, hi: 0x8b}, + // Block 0x22, offset 0x23 + {value: 0x0000, lo: 0x02}, + {value: 0x2778, lo: 0xb3, hi: 0xb3}, + {value: 0x8076, lo: 0xb8, hi: 0xb9}, + // Block 0x23, offset 0x24 + {value: 0x0000, lo: 0x03}, + {value: 0x807a, lo: 0x88, hi: 0x8b}, + {value: 0x276a, lo: 0x9c, hi: 0x9c}, + {value: 0x2771, lo: 0x9d, hi: 0x9d}, + // Block 0x24, offset 0x25 + {value: 0x0000, lo: 0x05}, + {value: 0x0305, lo: 0x8c, hi: 0x8c}, + {value: 0x80dc, lo: 0x98, hi: 0x99}, + {value: 0x80dc, lo: 0xb5, hi: 0xb5}, + {value: 0x80dc, lo: 0xb7, hi: 0xb7}, + {value: 0x80d8, lo: 0xb9, hi: 0xb9}, + // Block 0x25, offset 0x26 + {value: 0x0000, lo: 0x10}, + {value: 0x2786, lo: 0x83, hi: 0x83}, + {value: 0x278d, lo: 0x8d, hi: 0x8d}, + {value: 0x2794, lo: 0x92, hi: 0x92}, + {value: 0x279b, lo: 0x97, hi: 0x97}, + {value: 0x27a2, lo: 0x9c, hi: 0x9c}, + {value: 0x277f, lo: 0xa9, hi: 0xa9}, + {value: 0x8081, lo: 0xb1, hi: 0xb1}, + {value: 0x8082, lo: 0xb2, hi: 0xb2}, + {value: 0x49b6, lo: 0xb3, hi: 0xb3}, + {value: 0x8084, lo: 0xb4, hi: 0xb4}, + {value: 0x49bf, lo: 0xb5, hi: 0xb5}, + {value: 0x459a, lo: 0xb6, hi: 0xb6}, + {value: 0x45da, lo: 0xb7, hi: 0xb7}, + {value: 0x45a2, lo: 0xb8, hi: 0xb8}, + {value: 0x45e5, lo: 0xb9, hi: 0xb9}, + {value: 0x8082, lo: 0xba, hi: 0xbd}, + // Block 0x26, offset 0x27 + {value: 0x0000, lo: 0x0b}, + {value: 0x8082, lo: 0x80, hi: 0x80}, + {value: 0x49c8, lo: 0x81, hi: 0x81}, + {value: 0x80e6, lo: 0x82, hi: 0x83}, + {value: 0x8009, lo: 0x84, hi: 0x84}, + {value: 0x80e6, lo: 0x86, hi: 0x87}, + {value: 0x27b0, lo: 0x93, hi: 0x93}, + {value: 0x27b7, lo: 0x9d, hi: 0x9d}, + {value: 0x27be, lo: 0xa2, hi: 0xa2}, + {value: 0x27c5, lo: 0xa7, hi: 0xa7}, + {value: 0x27cc, lo: 0xac, hi: 0xac}, + {value: 0x27a9, lo: 0xb9, hi: 0xb9}, + // Block 0x27, offset 0x28 + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0x86, hi: 0x86}, + // Block 0x28, offset 0x29 + {value: 0x0000, lo: 0x05}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x193f, lo: 0xa6, hi: 0xa6}, + {value: 0x8600, lo: 0xae, hi: 0xae}, + {value: 0x8007, lo: 0xb7, hi: 0xb7}, + {value: 0x8009, lo: 0xb9, hi: 0xba}, + // Block 0x29, offset 0x2a + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0x8d, hi: 0x8d}, + // Block 0x2a, offset 0x2b + {value: 0x0000, lo: 0x01}, + {value: 0x0309, lo: 0xbc, hi: 0xbc}, + // Block 0x2b, offset 0x2c + {value: 0x0000, lo: 0x01}, + {value: 0x8800, lo: 0x80, hi: 0x92}, + // Block 0x2c, offset 0x2d + {value: 0x0000, lo: 0x01}, + {value: 0x8e00, lo: 0xa1, hi: 0xb5}, + // Block 0x2d, offset 0x2e + {value: 0x0000, lo: 0x01}, + {value: 0x8600, lo: 0xa8, hi: 0xbf}, + // Block 0x2e, offset 0x2f + {value: 0x0000, lo: 0x01}, + {value: 0x8600, lo: 0x80, hi: 0x82}, + // Block 0x2f, offset 0x30 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0x9d, hi: 0x9f}, + // Block 0x30, offset 0x31 + {value: 0x0000, lo: 0x02}, + {value: 0x8009, lo: 0x94, hi: 0x94}, + {value: 0x8009, lo: 0xb4, hi: 0xb4}, + // Block 0x31, offset 0x32 + {value: 0x0000, lo: 0x02}, + {value: 0x8009, lo: 0x92, hi: 0x92}, + {value: 0x80e6, lo: 0x9d, hi: 0x9d}, + // Block 0x32, offset 0x33 + {value: 0x0000, lo: 0x01}, + {value: 0x80e4, lo: 0xa9, hi: 0xa9}, + // Block 0x33, offset 0x34 + {value: 0x0008, lo: 0x02}, + {value: 0x80de, lo: 0xb9, hi: 0xba}, + {value: 0x80dc, lo: 0xbb, hi: 0xbb}, + // Block 0x34, offset 0x35 + {value: 0x0000, lo: 0x02}, + {value: 0x80e6, lo: 0x97, hi: 0x97}, + {value: 0x80dc, lo: 0x98, hi: 0x98}, + // Block 0x35, offset 0x36 + {value: 0x0000, lo: 0x03}, + {value: 0x8009, lo: 0xa0, hi: 0xa0}, + {value: 0x80e6, lo: 0xb5, hi: 0xbc}, + {value: 0x80dc, lo: 0xbf, hi: 0xbf}, + // Block 0x36, offset 0x37 + {value: 0x0000, lo: 0x08}, + {value: 0x197e, lo: 0x80, hi: 0x80}, + {value: 0x1985, lo: 0x81, hi: 0x81}, + {value: 0x8800, lo: 0x82, hi: 0x82}, + {value: 0x198c, lo: 0x83, hi: 0x83}, + {value: 0x8009, lo: 0x84, hi: 0x84}, + {value: 0x80e6, lo: 0xab, hi: 0xab}, + {value: 0x80dc, lo: 0xac, hi: 0xac}, + {value: 0x80e6, lo: 0xad, hi: 0xb3}, + // Block 0x37, offset 0x38 + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0xaa, hi: 0xab}, + // Block 0x38, offset 0x39 + {value: 0x0000, lo: 0x02}, + {value: 0x8007, lo: 0xa6, hi: 0xa6}, + {value: 0x8009, lo: 0xb2, hi: 0xb3}, + // Block 0x39, offset 0x3a + {value: 0x0000, lo: 0x01}, + {value: 0x8007, lo: 0xb7, hi: 0xb7}, + // Block 0x3a, offset 0x3b + {value: 0x0000, lo: 0x09}, + {value: 0x80e6, lo: 0x90, hi: 0x92}, + {value: 0x8001, lo: 0x94, hi: 0x94}, + {value: 0x80dc, lo: 0x95, hi: 0x99}, + {value: 0x80e6, lo: 0x9a, hi: 0x9b}, + {value: 0x80dc, lo: 0x9c, hi: 0x9f}, + {value: 0x80e6, lo: 0xa0, hi: 0xa0}, + {value: 0x8001, lo: 0xa2, hi: 0xa8}, + {value: 0x80dc, lo: 0xad, hi: 0xad}, + {value: 0x80e6, lo: 0xb4, hi: 0xb4}, + // Block 0x3b, offset 0x3c + {value: 0x0002, lo: 0x0a}, + {value: 0x0043, lo: 0xac, hi: 0xac}, + {value: 0x00d4, lo: 0xad, hi: 0xad}, + {value: 0x0045, lo: 0xae, hi: 0xae}, + {value: 0x0049, lo: 0xb0, hi: 0xb1}, + {value: 0x00e9, lo: 0xb2, hi: 0xb2}, + {value: 0x004f, lo: 0xb3, hi: 0xba}, + {value: 0x005f, lo: 0xbc, hi: 0xbc}, + {value: 0x00f2, lo: 0xbd, hi: 0xbd}, + {value: 0x0061, lo: 0xbe, hi: 0xbe}, + {value: 0x0065, lo: 0xbf, hi: 0xbf}, + // Block 0x3c, offset 0x3d + {value: 0x0000, lo: 0x0e}, + {value: 0x80e6, lo: 0x80, hi: 0x81}, + {value: 0x80dc, lo: 0x82, hi: 0x82}, + {value: 0x80e6, lo: 0x83, hi: 0x89}, + {value: 0x80dc, lo: 0x8a, hi: 0x8a}, + {value: 0x80e6, lo: 0x8b, hi: 0x8c}, + {value: 0x80ea, lo: 0x8d, hi: 0x8d}, + {value: 0x80d6, lo: 0x8e, hi: 0x8e}, + {value: 0x80dc, lo: 0x8f, hi: 0x8f}, + {value: 0x80ca, lo: 0x90, hi: 0x90}, + {value: 0x80e6, lo: 0x91, hi: 0xa6}, + {value: 0x80e9, lo: 0xbc, hi: 0xbc}, + {value: 0x80dc, lo: 0xbd, hi: 0xbd}, + {value: 0x80e6, lo: 0xbe, hi: 0xbe}, + {value: 0x80dc, lo: 0xbf, hi: 0xbf}, + // Block 0x3d, offset 0x3e + {value: 0x0000, lo: 0x0d}, + {value: 0x0001, lo: 0x80, hi: 0x8a}, + {value: 0x04ad, lo: 0x91, hi: 0x91}, + {value: 0x4281, lo: 0x97, hi: 0x97}, + {value: 0x001d, lo: 0xa4, hi: 0xa4}, + {value: 0x19b5, lo: 0xa5, hi: 0xa5}, + {value: 0x1c9e, lo: 0xa6, hi: 0xa6}, + {value: 0x0001, lo: 0xaf, hi: 0xaf}, + {value: 0x283c, lo: 0xb3, hi: 0xb3}, + {value: 0x29a9, lo: 0xb4, hi: 0xb4}, + {value: 0x2843, lo: 0xb6, hi: 0xb6}, + {value: 0x29b3, lo: 0xb7, hi: 0xb7}, + {value: 0x19af, lo: 0xbc, hi: 0xbc}, + {value: 0x424f, lo: 0xbe, hi: 0xbe}, + // Block 0x3e, offset 0x3f + {value: 0x0002, lo: 0x0d}, + {value: 0x1a75, lo: 0x87, hi: 0x87}, + {value: 0x1a72, lo: 0x88, hi: 0x88}, + {value: 0x19b2, lo: 0x89, hi: 0x89}, + {value: 0x2b46, lo: 0x97, hi: 0x97}, + {value: 0x0001, lo: 0x9f, hi: 0x9f}, + {value: 0x0021, lo: 0xb0, hi: 0xb0}, + {value: 0x0093, lo: 0xb1, hi: 0xb1}, + {value: 0x0029, lo: 0xb4, hi: 0xb9}, + {value: 0x0017, lo: 0xba, hi: 0xba}, + {value: 0x04d9, lo: 0xbb, hi: 0xbb}, + {value: 0x003b, lo: 0xbc, hi: 0xbc}, + {value: 0x0011, lo: 0xbd, hi: 0xbe}, + {value: 0x009d, lo: 0xbf, hi: 0xbf}, + // Block 0x3f, offset 0x40 + {value: 0x0002, lo: 0x0f}, + {value: 0x0021, lo: 0x80, hi: 0x89}, + {value: 0x0017, lo: 0x8a, hi: 0x8a}, + {value: 0x04d9, lo: 0x8b, hi: 0x8b}, + {value: 0x003b, lo: 0x8c, hi: 0x8c}, + {value: 0x0011, lo: 0x8d, hi: 0x8e}, + {value: 0x0083, lo: 0x90, hi: 0x90}, + {value: 0x008b, lo: 0x91, hi: 0x91}, + {value: 0x009f, lo: 0x92, hi: 0x92}, + {value: 0x00b1, lo: 0x93, hi: 0x93}, + {value: 0x0107, lo: 0x94, hi: 0x94}, + {value: 0x0091, lo: 0x95, hi: 0x95}, + {value: 0x0097, lo: 0x96, hi: 0x99}, + {value: 0x00a1, lo: 0x9a, hi: 0x9a}, + {value: 0x00a7, lo: 0x9b, hi: 0x9c}, + {value: 0x1adb, lo: 0xa8, hi: 0xa8}, + // Block 0x40, offset 0x41 + {value: 0x0000, lo: 0x0d}, + {value: 0x80e6, lo: 0x90, hi: 0x91}, + {value: 0x8001, lo: 0x92, hi: 0x93}, + {value: 0x80e6, lo: 0x94, hi: 0x97}, + {value: 0x8001, lo: 0x98, hi: 0x9a}, + {value: 0x80e6, lo: 0x9b, hi: 0x9c}, + {value: 0x80e6, lo: 0xa1, hi: 0xa1}, + {value: 0x8001, lo: 0xa5, hi: 0xa6}, + {value: 0x80e6, lo: 0xa7, hi: 0xa7}, + {value: 0x80dc, lo: 0xa8, hi: 0xa8}, + {value: 0x80e6, lo: 0xa9, hi: 0xa9}, + {value: 0x8001, lo: 0xaa, hi: 0xab}, + {value: 0x80dc, lo: 0xac, hi: 0xaf}, + {value: 0x80e6, lo: 0xb0, hi: 0xb0}, + // Block 0x41, offset 0x42 + {value: 0x0007, lo: 0x06}, + {value: 0x22c2, lo: 0x89, hi: 0x89}, + {value: 0x8800, lo: 0x90, hi: 0x90}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x8800, lo: 0x94, hi: 0x94}, + {value: 0x3ba3, lo: 0x9a, hi: 0x9b}, + {value: 0x3bb1, lo: 0xae, hi: 0xae}, + // Block 0x42, offset 0x43 + {value: 0x000e, lo: 0x05}, + {value: 0x3bb8, lo: 0x8d, hi: 0x8e}, + {value: 0x3bbf, lo: 0x8f, hi: 0x8f}, + {value: 0x8800, lo: 0x90, hi: 0x90}, + {value: 0x8800, lo: 0x92, hi: 0x92}, + {value: 0x8800, lo: 0x94, hi: 0x94}, + // Block 0x43, offset 0x44 + {value: 0x0173, lo: 0x0e}, + {value: 0x8800, lo: 0x83, hi: 0x83}, + {value: 0x3bcd, lo: 0x84, hi: 0x84}, + {value: 0x8800, lo: 0x88, hi: 0x88}, + {value: 0x3bd4, lo: 0x89, hi: 0x89}, + {value: 0x8800, lo: 0x8b, hi: 0x8b}, + {value: 0x3bdb, lo: 0x8c, hi: 0x8c}, + {value: 0x8800, lo: 0xa3, hi: 0xa3}, + {value: 0x3be2, lo: 0xa4, hi: 0xa4}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x3be9, lo: 0xa6, hi: 0xa6}, + {value: 0x284a, lo: 0xac, hi: 0xad}, + {value: 0x2851, lo: 0xaf, hi: 0xaf}, + {value: 0x29c7, lo: 0xb0, hi: 0xb0}, + {value: 0x8800, lo: 0xbc, hi: 0xbc}, + // Block 0x44, offset 0x45 + {value: 0x0007, lo: 0x03}, + {value: 0x3c52, lo: 0xa0, hi: 0xa1}, + {value: 0x3c7c, lo: 0xa2, hi: 0xa3}, + {value: 0x3ca6, lo: 0xaa, hi: 0xad}, + // Block 0x45, offset 0x46 + {value: 0x0004, lo: 0x01}, + {value: 0x04fd, lo: 0xa9, hi: 0xaa}, + // Block 0x46, offset 0x47 + {value: 0x0002, lo: 0x03}, + {value: 0x0057, lo: 0x80, hi: 0x8f}, + {value: 0x0083, lo: 0x90, hi: 0xa9}, + {value: 0x0021, lo: 0xaa, hi: 0xaa}, + // Block 0x47, offset 0x48 + {value: 0x0000, lo: 0x01}, + {value: 0x2b53, lo: 0x8c, hi: 0x8c}, + // Block 0x48, offset 0x49 + {value: 0x0263, lo: 0x02}, + {value: 0x1cce, lo: 0xb4, hi: 0xb4}, + {value: 0x1a6f, lo: 0xb5, hi: 0xb6}, + // Block 0x49, offset 0x4a + {value: 0x0000, lo: 0x01}, + {value: 0x44c3, lo: 0x9c, hi: 0x9c}, + // Block 0x4a, offset 0x4b + {value: 0x0000, lo: 0x02}, + {value: 0x0095, lo: 0xbc, hi: 0xbc}, + {value: 0x006d, lo: 0xbd, hi: 0xbd}, + // Block 0x4b, offset 0x4c + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0xaf, hi: 0xb1}, + // Block 0x4c, offset 0x4d + {value: 0x0000, lo: 0x02}, + {value: 0x04f1, lo: 0xaf, hi: 0xaf}, + {value: 0x8009, lo: 0xbf, hi: 0xbf}, + // Block 0x4d, offset 0x4e + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0xa0, hi: 0xbf}, + // Block 0x4e, offset 0x4f + {value: 0x0000, lo: 0x01}, + {value: 0x0e35, lo: 0x9f, hi: 0x9f}, + // Block 0x4f, offset 0x50 + {value: 0x0000, lo: 0x01}, + {value: 0x169d, lo: 0xb3, hi: 0xb3}, + // Block 0x50, offset 0x51 + {value: 0x0004, lo: 0x0b}, + {value: 0x1605, lo: 0x80, hi: 0x82}, + {value: 0x161d, lo: 0x83, hi: 0x83}, + {value: 0x1635, lo: 0x84, hi: 0x85}, + {value: 0x1645, lo: 0x86, hi: 0x89}, + {value: 0x1659, lo: 0x8a, hi: 0x8c}, + {value: 0x166d, lo: 0x8d, hi: 0x8d}, + {value: 0x1675, lo: 0x8e, hi: 0x8e}, + {value: 0x167d, lo: 0x8f, hi: 0x90}, + {value: 0x1689, lo: 0x91, hi: 0x93}, + {value: 0x1699, lo: 0x94, hi: 0x94}, + {value: 0x16a1, lo: 0x95, hi: 0x95}, + // Block 0x51, offset 0x52 + {value: 0x0004, lo: 0x08}, + {value: 0x0001, lo: 0x80, hi: 0x80}, + {value: 0x80da, lo: 0xaa, hi: 0xaa}, + {value: 0x80e4, lo: 0xab, hi: 0xac}, + {value: 0x80de, lo: 0xad, hi: 0xad}, + {value: 0x80e0, lo: 0xae, hi: 0xae}, + {value: 0x80e0, lo: 0xaf, hi: 0xaf}, + {value: 0x0525, lo: 0xb6, hi: 0xb6}, + {value: 0x08f9, lo: 0xb8, hi: 0xba}, + // Block 0x52, offset 0x53 + {value: 0x0004, lo: 0x06}, + {value: 0x030d, lo: 0xb1, hi: 0xb2}, + {value: 0x0435, lo: 0xb3, hi: 0xb3}, + {value: 0x0315, lo: 0xb4, hi: 0xb4}, + {value: 0x0439, lo: 0xb5, hi: 0xb6}, + {value: 0x0319, lo: 0xb7, hi: 0xb9}, + {value: 0x0441, lo: 0xba, hi: 0xbf}, + // Block 0x53, offset 0x54 + {value: 0x0004, lo: 0x0c}, + {value: 0x0361, lo: 0x80, hi: 0x80}, + {value: 0x0325, lo: 0x81, hi: 0x83}, + {value: 0x0375, lo: 0x84, hi: 0x84}, + {value: 0x0331, lo: 0x85, hi: 0x8e}, + {value: 0x03c1, lo: 0x8f, hi: 0xa3}, + {value: 0x03bd, lo: 0xa4, hi: 0xa4}, + {value: 0x0359, lo: 0xa5, hi: 0xa6}, + {value: 0x0459, lo: 0xa7, hi: 0xad}, + {value: 0x0365, lo: 0xae, hi: 0xae}, + {value: 0x0475, lo: 0xaf, hi: 0xb0}, + {value: 0x0369, lo: 0xb1, hi: 0xb3}, + {value: 0x0379, lo: 0xb4, hi: 0xbf}, + // Block 0x54, offset 0x55 + {value: 0x0000, lo: 0x02}, + {value: 0x80e6, lo: 0xaf, hi: 0xaf}, + {value: 0x80e6, lo: 0xb4, hi: 0xbd}, + // Block 0x55, offset 0x56 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0x9f, hi: 0x9f}, + // Block 0x56, offset 0x57 + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0xb0, hi: 0xb1}, + // Block 0x57, offset 0x58 + {value: 0x0000, lo: 0x01}, + {value: 0x16a5, lo: 0xb0, hi: 0xb0}, + // Block 0x58, offset 0x59 + {value: 0x000c, lo: 0x01}, + {value: 0x00da, lo: 0xb8, hi: 0xb9}, + // Block 0x59, offset 0x5a + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0x86, hi: 0x86}, + // Block 0x5a, offset 0x5b + {value: 0x0000, lo: 0x02}, + {value: 0x8009, lo: 0x84, hi: 0x84}, + {value: 0x80e6, lo: 0xa0, hi: 0xb1}, + // Block 0x5b, offset 0x5c + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0xab, hi: 0xad}, + // Block 0x5c, offset 0x5d + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0x93, hi: 0x93}, + // Block 0x5d, offset 0x5e + {value: 0x0000, lo: 0x01}, + {value: 0x8007, lo: 0xb3, hi: 0xb3}, + // Block 0x5e, offset 0x5f + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0x80, hi: 0x80}, + // Block 0x5f, offset 0x60 + {value: 0x0000, lo: 0x05}, + {value: 0x80e6, lo: 0xb0, hi: 0xb0}, + {value: 0x80e6, lo: 0xb2, hi: 0xb3}, + {value: 0x80dc, lo: 0xb4, hi: 0xb4}, + {value: 0x80e6, lo: 0xb7, hi: 0xb8}, + {value: 0x80e6, lo: 0xbe, hi: 0xbf}, + // Block 0x60, offset 0x61 + {value: 0x0000, lo: 0x02}, + {value: 0x80e6, lo: 0x81, hi: 0x81}, + {value: 0x8009, lo: 0xb6, hi: 0xb6}, + // Block 0x61, offset 0x62 + {value: 0x0000, lo: 0x01}, + {value: 0x8009, lo: 0xad, hi: 0xad}, + // Block 0x62, offset 0x63 + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0x80, hi: 0xbf}, + // Block 0x63, offset 0x64 + {value: 0x0000, lo: 0x01}, + {value: 0x8100, lo: 0x80, hi: 0xa3}, + // Block 0x64, offset 0x65 + {value: 0x0002, lo: 0x01}, + {value: 0x0003, lo: 0x81, hi: 0xbf}, + // Block 0x65, offset 0x66 + {value: 0x0004, lo: 0x0e}, + {value: 0x03c1, lo: 0x82, hi: 0x87}, + {value: 0x03d9, lo: 0x8a, hi: 0x8f}, + {value: 0x03f1, lo: 0x92, hi: 0x97}, + {value: 0x0409, lo: 0x9a, hi: 0x9c}, + {value: 0x00bf, lo: 0xa0, hi: 0xa0}, + {value: 0x00c2, lo: 0xa1, hi: 0xa1}, + {value: 0x00cb, lo: 0xa2, hi: 0xa2}, + {value: 0x424a, lo: 0xa3, hi: 0xa3}, + {value: 0x00c8, lo: 0xa4, hi: 0xa4}, + {value: 0x00c5, lo: 0xa5, hi: 0xa5}, + {value: 0x04b9, lo: 0xa6, hi: 0xa6}, + {value: 0x04dd, lo: 0xa8, hi: 0xa8}, + {value: 0x04bd, lo: 0xa9, hi: 0xac}, + {value: 0x04e1, lo: 0xad, hi: 0xae}, + // Block 0x66, offset 0x67 + {value: 0x0000, lo: 0x01}, + {value: 0x80dc, lo: 0xbd, hi: 0xbd}, + // Block 0x67, offset 0x68 + {value: 0x00db, lo: 0x05}, + {value: 0x80dc, lo: 0x8d, hi: 0x8d}, + {value: 0x80e6, lo: 0x8f, hi: 0x8f}, + {value: 0x80e6, lo: 0xb8, hi: 0xb8}, + {value: 0x8001, lo: 0xb9, hi: 0xba}, + {value: 0x8009, lo: 0xbf, hi: 0xbf}, + // Block 0x68, offset 0x69 + {value: 0x05fe, lo: 0x07}, + {value: 0x8800, lo: 0x99, hi: 0x99}, + {value: 0x4222, lo: 0x9a, hi: 0x9a}, + {value: 0x8800, lo: 0x9b, hi: 0x9b}, + {value: 0x422c, lo: 0x9c, hi: 0x9c}, + {value: 0x8800, lo: 0xa5, hi: 0xa5}, + {value: 0x4236, lo: 0xab, hi: 0xab}, + {value: 0x8009, lo: 0xb9, hi: 0xba}, + // Block 0x69, offset 0x6a + {value: 0x0000, lo: 0x06}, + {value: 0x80e6, lo: 0x80, hi: 0x82}, + {value: 0x8600, lo: 0xa7, hi: 0xa7}, + {value: 0x1993, lo: 0xae, hi: 0xae}, + {value: 0x199c, lo: 0xaf, hi: 0xaf}, + {value: 0x8800, lo: 0xb1, hi: 0xb2}, + {value: 0x8009, lo: 0xb3, hi: 0xb4}, + // Block 0x6a, offset 0x6b + {value: 0x0000, lo: 0x02}, + {value: 0x8009, lo: 0xb6, hi: 0xb6}, + {value: 0x8007, lo: 0xb7, hi: 0xb7}, + // Block 0x6b, offset 0x6c + {value: 0x0000, lo: 0x0c}, + {value: 0x45b2, lo: 0x9e, hi: 0x9e}, + {value: 0x45bc, lo: 0x9f, hi: 0x9f}, + {value: 0x45f0, lo: 0xa0, hi: 0xa0}, + {value: 0x45fe, lo: 0xa1, hi: 0xa1}, + {value: 0x460c, lo: 0xa2, hi: 0xa2}, + {value: 0x461a, lo: 0xa3, hi: 0xa3}, + {value: 0x4628, lo: 0xa4, hi: 0xa4}, + {value: 0x80d8, lo: 0xa5, hi: 0xa6}, + {value: 0x8001, lo: 0xa7, hi: 0xa9}, + {value: 0x80e2, lo: 0xad, hi: 0xad}, + {value: 0x80d8, lo: 0xae, hi: 0xb2}, + {value: 0x80dc, lo: 0xbb, hi: 0xbf}, + // Block 0x6c, offset 0x6d + {value: 0x0000, lo: 0x09}, + {value: 0x80dc, lo: 0x80, hi: 0x82}, + {value: 0x80e6, lo: 0x85, hi: 0x89}, + {value: 0x80dc, lo: 0x8a, hi: 0x8b}, + {value: 0x80e6, lo: 0xaa, hi: 0xad}, + {value: 0x45c6, lo: 0xbb, hi: 0xbb}, + {value: 0x45d0, lo: 0xbc, hi: 0xbc}, + {value: 0x4636, lo: 0xbd, hi: 0xbd}, + {value: 0x4652, lo: 0xbe, hi: 0xbe}, + {value: 0x4644, lo: 0xbf, hi: 0xbf}, + // Block 0x6d, offset 0x6e + {value: 0x0000, lo: 0x01}, + {value: 0x4660, lo: 0x80, hi: 0x80}, + // Block 0x6e, offset 0x6f + {value: 0x0000, lo: 0x01}, + {value: 0x80e6, lo: 0x82, hi: 0x84}, + // Block 0x6f, offset 0x70 + {value: 0x0002, lo: 0x03}, + {value: 0x0043, lo: 0x80, hi: 0x99}, + {value: 0x0083, lo: 0x9a, hi: 0xb3}, + {value: 0x0043, lo: 0xb4, hi: 0xbf}, + // Block 0x70, offset 0x71 + {value: 0x0002, lo: 0x04}, + {value: 0x005b, lo: 0x80, hi: 0x8d}, + {value: 0x0083, lo: 0x8e, hi: 0x94}, + {value: 0x0093, lo: 0x96, hi: 0xa7}, + {value: 0x0043, lo: 0xa8, hi: 0xbf}, + // Block 0x71, offset 0x72 + {value: 0x0002, lo: 0x0b}, + {value: 0x0073, lo: 0x80, hi: 0x81}, + {value: 0x0083, lo: 0x82, hi: 0x9b}, + {value: 0x0043, lo: 0x9c, hi: 0x9c}, + {value: 0x0047, lo: 0x9e, hi: 0x9f}, + {value: 0x004f, lo: 0xa2, hi: 0xa2}, + {value: 0x0055, lo: 0xa5, hi: 0xa6}, + {value: 0x005d, lo: 0xa9, hi: 0xac}, + {value: 0x0067, lo: 0xae, hi: 0xb5}, + {value: 0x0083, lo: 0xb6, hi: 0xb9}, + {value: 0x008d, lo: 0xbb, hi: 0xbb}, + {value: 0x0091, lo: 0xbd, hi: 0xbf}, + // Block 0x72, offset 0x73 + {value: 0x0002, lo: 0x04}, + {value: 0x0097, lo: 0x80, hi: 0x83}, + {value: 0x00a1, lo: 0x85, hi: 0x8f}, + {value: 0x0043, lo: 0x90, hi: 0xa9}, + {value: 0x0083, lo: 0xaa, hi: 0xbf}, + // Block 0x73, offset 0x74 + {value: 0x0002, lo: 0x08}, + {value: 0x00af, lo: 0x80, hi: 0x83}, + {value: 0x0043, lo: 0x84, hi: 0x85}, + {value: 0x0049, lo: 0x87, hi: 0x8a}, + {value: 0x0055, lo: 0x8d, hi: 0x94}, + {value: 0x0067, lo: 0x96, hi: 0x9c}, + {value: 0x0083, lo: 0x9e, hi: 0xb7}, + {value: 0x0043, lo: 0xb8, hi: 0xb9}, + {value: 0x0049, lo: 0xbb, hi: 0xbe}, + // Block 0x74, offset 0x75 + {value: 0x0002, lo: 0x05}, + {value: 0x0053, lo: 0x80, hi: 0x84}, + {value: 0x005f, lo: 0x86, hi: 0x86}, + {value: 0x0067, lo: 0x8a, hi: 0x90}, + {value: 0x0083, lo: 0x92, hi: 0xab}, + {value: 0x0043, lo: 0xac, hi: 0xbf}, + // Block 0x75, offset 0x76 + {value: 0x0002, lo: 0x04}, + {value: 0x006b, lo: 0x80, hi: 0x85}, + {value: 0x0083, lo: 0x86, hi: 0x9f}, + {value: 0x0043, lo: 0xa0, hi: 0xb9}, + {value: 0x0083, lo: 0xba, hi: 0xbf}, + // Block 0x76, offset 0x77 + {value: 0x0002, lo: 0x03}, + {value: 0x008f, lo: 0x80, hi: 0x93}, + {value: 0x0043, lo: 0x94, hi: 0xad}, + {value: 0x0083, lo: 0xae, hi: 0xbf}, + // Block 0x77, offset 0x78 + {value: 0x0002, lo: 0x04}, + {value: 0x00a7, lo: 0x80, hi: 0x87}, + {value: 0x0043, lo: 0x88, hi: 0xa1}, + {value: 0x0083, lo: 0xa2, hi: 0xbb}, + {value: 0x0043, lo: 0xbc, hi: 0xbf}, + // Block 0x78, offset 0x79 + {value: 0x0002, lo: 0x03}, + {value: 0x004b, lo: 0x80, hi: 0x95}, + {value: 0x0083, lo: 0x96, hi: 0xaf}, + {value: 0x0043, lo: 0xb0, hi: 0xbf}, + // Block 0x79, offset 0x7a + {value: 0x0003, lo: 0x0f}, + {value: 0x01b8, lo: 0x80, hi: 0x80}, + {value: 0x04d1, lo: 0x81, hi: 0x81}, + {value: 0x01bb, lo: 0x82, hi: 0x9a}, + {value: 0x04cd, lo: 0x9b, hi: 0x9b}, + {value: 0x01c7, lo: 0x9c, hi: 0x9c}, + {value: 0x01d0, lo: 0x9d, hi: 0x9d}, + {value: 0x01d6, lo: 0x9e, hi: 0x9e}, + {value: 0x01fa, lo: 0x9f, hi: 0x9f}, + {value: 0x01eb, lo: 0xa0, hi: 0xa0}, + {value: 0x01e8, lo: 0xa1, hi: 0xa1}, + {value: 0x0173, lo: 0xa2, hi: 0xb2}, + {value: 0x0188, lo: 0xb3, hi: 0xb3}, + {value: 0x01a6, lo: 0xb4, hi: 0xba}, + {value: 0x04d1, lo: 0xbb, hi: 0xbb}, + {value: 0x01bb, lo: 0xbc, hi: 0xbf}, + // Block 0x7a, offset 0x7b + {value: 0x0003, lo: 0x0d}, + {value: 0x01c7, lo: 0x80, hi: 0x94}, + {value: 0x04cd, lo: 0x95, hi: 0x95}, + {value: 0x01c7, lo: 0x96, hi: 0x96}, + {value: 0x01d0, lo: 0x97, hi: 0x97}, + {value: 0x01d6, lo: 0x98, hi: 0x98}, + {value: 0x01fa, lo: 0x99, hi: 0x99}, + {value: 0x01eb, lo: 0x9a, hi: 0x9a}, + {value: 0x01e8, lo: 0x9b, hi: 0x9b}, + {value: 0x0173, lo: 0x9c, hi: 0xac}, + {value: 0x0188, lo: 0xad, hi: 0xad}, + {value: 0x01a6, lo: 0xae, hi: 0xb4}, + {value: 0x04d1, lo: 0xb5, hi: 0xb5}, + {value: 0x01bb, lo: 0xb6, hi: 0xbf}, + // Block 0x7b, offset 0x7c + {value: 0x0003, lo: 0x0d}, + {value: 0x01d9, lo: 0x80, hi: 0x8e}, + {value: 0x04cd, lo: 0x8f, hi: 0x8f}, + {value: 0x01c7, lo: 0x90, hi: 0x90}, + {value: 0x01d0, lo: 0x91, hi: 0x91}, + {value: 0x01d6, lo: 0x92, hi: 0x92}, + {value: 0x01fa, lo: 0x93, hi: 0x93}, + {value: 0x01eb, lo: 0x94, hi: 0x94}, + {value: 0x01e8, lo: 0x95, hi: 0x95}, + {value: 0x0173, lo: 0x96, hi: 0xa6}, + {value: 0x0188, lo: 0xa7, hi: 0xa7}, + {value: 0x01a6, lo: 0xa8, hi: 0xae}, + {value: 0x04d1, lo: 0xaf, hi: 0xaf}, + {value: 0x01bb, lo: 0xb0, hi: 0xbf}, + // Block 0x7c, offset 0x7d + {value: 0x0003, lo: 0x0d}, + {value: 0x01eb, lo: 0x80, hi: 0x88}, + {value: 0x04cd, lo: 0x89, hi: 0x89}, + {value: 0x01c7, lo: 0x8a, hi: 0x8a}, + {value: 0x01d0, lo: 0x8b, hi: 0x8b}, + {value: 0x01d6, lo: 0x8c, hi: 0x8c}, + {value: 0x01fa, lo: 0x8d, hi: 0x8d}, + {value: 0x01eb, lo: 0x8e, hi: 0x8e}, + {value: 0x01e8, lo: 0x8f, hi: 0x8f}, + {value: 0x0173, lo: 0x90, hi: 0xa0}, + {value: 0x0188, lo: 0xa1, hi: 0xa1}, + {value: 0x01a6, lo: 0xa2, hi: 0xa8}, + {value: 0x04d1, lo: 0xa9, hi: 0xa9}, + {value: 0x01bb, lo: 0xaa, hi: 0xbf}, + // Block 0x7d, offset 0x7e + {value: 0x0002, lo: 0x09}, + {value: 0x0063, lo: 0x80, hi: 0x89}, + {value: 0x1a93, lo: 0x8a, hi: 0x8a}, + {value: 0x1ac3, lo: 0x8b, hi: 0x8b}, + {value: 0x1ade, lo: 0x8c, hi: 0x8c}, + {value: 0x1ae4, lo: 0x8d, hi: 0x8d}, + {value: 0x1d02, lo: 0x8e, hi: 0x8e}, + {value: 0x1af0, lo: 0x8f, hi: 0x8f}, + {value: 0x1abd, lo: 0xaa, hi: 0xaa}, + {value: 0x1ac0, lo: 0xab, hi: 0xab}, + // Block 0x7e, offset 0x7f + {value: 0x0000, lo: 0x01}, + {value: 0x1a81, lo: 0x90, hi: 0x90}, + // Block 0x7f, offset 0x80 + {value: 0x0028, lo: 0x09}, + {value: 0x2a0d, lo: 0x80, hi: 0x80}, + {value: 0x29d1, lo: 0x81, hi: 0x81}, + {value: 0x29db, lo: 0x82, hi: 0x82}, + {value: 0x29ef, lo: 0x83, hi: 0x84}, + {value: 0x29f9, lo: 0x85, hi: 0x86}, + {value: 0x29e5, lo: 0x87, hi: 0x87}, + {value: 0x2a03, lo: 0x88, hi: 0x88}, + {value: 0x0be1, lo: 0x90, hi: 0x90}, + {value: 0x0959, lo: 0x91, hi: 0x91}, +} + +// nfkcLookup: 1216 bytes +// Block 0 is the null block. +var nfkcLookup = [1216]uint8{ + // Block 0x0, offset 0x0 + // Block 0x1, offset 0x40 + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0x0c2: 0x58, 0x0c3: 0x01, 0x0c4: 0x02, 0x0c5: 0x03, 0x0c6: 0x59, 0x0c7: 0x04, + 0x0c8: 0x05, 0x0ca: 0x5a, 0x0cb: 0x5b, 0x0cc: 0x06, 0x0cd: 0x07, 0x0ce: 0x08, 0x0cf: 0x09, + 0x0d0: 0x0a, 0x0d1: 0x5c, 0x0d2: 0x5d, 0x0d3: 0x0b, 0x0d6: 0x0c, 0x0d7: 0x5e, + 0x0d8: 0x5f, 0x0d9: 0x0d, 0x0db: 0x60, 0x0dc: 0x61, 0x0dd: 0x62, 0x0df: 0x63, + 0x0e0: 0x02, 0x0e1: 0x03, 0x0e2: 0x04, 0x0e3: 0x05, + 0x0ea: 0x06, 0x0eb: 0x07, 0x0ec: 0x07, 0x0ed: 0x08, 0x0ef: 0x09, + 0x0f0: 0x10, + // Block 0x4, offset 0x100 + 0x120: 0x64, 0x121: 0x65, 0x123: 0x66, 0x124: 0x67, 0x125: 0x68, 0x126: 0x69, 0x127: 0x6a, + 0x128: 0x6b, 0x129: 0x6c, 0x12a: 0x6d, 0x12b: 0x6e, 0x12c: 0x69, 0x12d: 0x6f, 0x12e: 0x70, 0x12f: 0x71, + 0x131: 0x72, 0x132: 0x73, 0x133: 0x74, 0x134: 0x75, 0x135: 0x76, 0x137: 0x77, + 0x138: 0x78, 0x139: 0x79, 0x13a: 0x7a, 0x13b: 0x7b, 0x13c: 0x7c, 0x13d: 0x7d, 0x13e: 0x7e, 0x13f: 0x7f, + // Block 0x5, offset 0x140 + 0x140: 0x80, 0x142: 0x81, 0x143: 0x82, 0x144: 0x83, 0x145: 0x84, 0x146: 0x85, 0x147: 0x86, + 0x14d: 0x87, + 0x15c: 0x88, 0x15f: 0x89, + 0x162: 0x8a, 0x164: 0x8b, + 0x168: 0x8c, 0x169: 0x8d, 0x16c: 0x0e, 0x16d: 0x8e, 0x16e: 0x8f, 0x16f: 0x90, + 0x170: 0x91, 0x173: 0x92, 0x174: 0x93, 0x175: 0x0f, 0x176: 0x10, 0x177: 0x94, + 0x178: 0x11, 0x179: 0x12, 0x17a: 0x13, 0x17b: 0x14, 0x17c: 0x15, 0x17d: 0x16, 0x17e: 0x17, 0x17f: 0x18, + // Block 0x6, offset 0x180 + 0x180: 0x95, 0x181: 0x96, 0x182: 0x97, 0x183: 0x98, 0x184: 0x19, 0x185: 0x1a, 0x186: 0x99, 0x187: 0x9a, + 0x188: 0x9b, 0x189: 0x1b, 0x18a: 0x1c, 0x18b: 0x9c, 0x18c: 0x9d, + 0x191: 0x1d, 0x192: 0x1e, 0x193: 0x9e, + 0x1a8: 0x9f, 0x1a9: 0xa0, 0x1ab: 0xa1, + 0x1b1: 0xa2, 0x1b3: 0xa3, 0x1b5: 0xa4, 0x1b7: 0xa5, + 0x1ba: 0xa6, 0x1bb: 0xa7, 0x1bc: 0x1f, 0x1bd: 0x20, 0x1be: 0x21, 0x1bf: 0xa8, + // Block 0x7, offset 0x1c0 + 0x1c0: 0xa9, 0x1c1: 0x22, 0x1c2: 0x23, 0x1c3: 0x24, 0x1c4: 0xaa, 0x1c5: 0xab, 0x1c6: 0x25, + 0x1c8: 0x26, 0x1c9: 0x27, 0x1ca: 0x28, 0x1cb: 0x29, 0x1cc: 0x2a, 0x1cd: 0x2b, 0x1ce: 0x2c, 0x1cf: 0x2d, + // Block 0x8, offset 0x200 + 0x219: 0xac, 0x21a: 0xad, 0x21b: 0xae, 0x21d: 0xaf, 0x21f: 0xb0, + 0x220: 0xb1, 0x223: 0xb2, 0x224: 0xb3, 0x225: 0xb4, 0x226: 0xb5, 0x227: 0xb6, + 0x22a: 0xb7, 0x22b: 0xb8, 0x22f: 0xb9, + 0x230: 0xba, 0x231: 0xba, 0x232: 0xba, 0x233: 0xba, 0x234: 0xba, 0x235: 0xba, 0x236: 0xba, 0x237: 0xba, + 0x238: 0xba, 0x239: 0xba, 0x23a: 0xba, 0x23b: 0xba, 0x23c: 0xba, 0x23d: 0xba, 0x23e: 0xba, 0x23f: 0xba, + // Block 0x9, offset 0x240 + 0x240: 0xba, 0x241: 0xba, 0x242: 0xba, 0x243: 0xba, 0x244: 0xba, 0x245: 0xba, 0x246: 0xba, 0x247: 0xba, + 0x248: 0xba, 0x249: 0xba, 0x24a: 0xba, 0x24b: 0xba, 0x24c: 0xba, 0x24d: 0xba, 0x24e: 0xba, 0x24f: 0xba, + 0x250: 0xba, 0x251: 0xba, 0x252: 0xba, 0x253: 0xba, 0x254: 0xba, 0x255: 0xba, 0x256: 0xba, 0x257: 0xba, + 0x258: 0xba, 0x259: 0xba, 0x25a: 0xba, 0x25b: 0xba, 0x25c: 0xba, 0x25d: 0xba, 0x25e: 0xba, 0x25f: 0xba, + 0x260: 0xba, 0x261: 0xba, 0x262: 0xba, 0x263: 0xba, 0x264: 0xba, 0x265: 0xba, 0x266: 0xba, 0x267: 0xba, + 0x268: 0xba, 0x269: 0xba, 0x26a: 0xba, 0x26b: 0xba, 0x26c: 0xba, 0x26d: 0xba, 0x26e: 0xba, 0x26f: 0xba, + 0x270: 0xba, 0x271: 0xba, 0x272: 0xba, 0x273: 0xba, 0x274: 0xba, 0x275: 0xba, 0x276: 0xba, 0x277: 0xba, + 0x278: 0xba, 0x279: 0xba, 0x27a: 0xba, 0x27b: 0xba, 0x27c: 0xba, 0x27d: 0xba, 0x27e: 0xba, 0x27f: 0xba, + // Block 0xa, offset 0x280 + 0x280: 0xba, 0x281: 0xba, 0x282: 0xba, 0x283: 0xba, 0x284: 0xba, 0x285: 0xba, 0x286: 0xba, 0x287: 0xba, + 0x288: 0xba, 0x289: 0xba, 0x28a: 0xba, 0x28b: 0xba, 0x28c: 0xba, 0x28d: 0xba, 0x28e: 0xba, 0x28f: 0xba, + 0x290: 0xba, 0x291: 0xba, 0x292: 0xba, 0x293: 0xba, 0x294: 0xba, 0x295: 0xba, 0x296: 0xba, 0x297: 0xba, + 0x298: 0xba, 0x299: 0xba, 0x29a: 0xba, 0x29b: 0xba, 0x29c: 0xba, 0x29d: 0xba, 0x29e: 0xbb, + // Block 0xb, offset 0x2c0 + 0x2e4: 0x2e, 0x2e5: 0x2f, 0x2e6: 0x30, 0x2e7: 0x31, + 0x2e8: 0x32, 0x2e9: 0x33, 0x2ea: 0x34, 0x2eb: 0x35, 0x2ec: 0x36, 0x2ed: 0x37, 0x2ee: 0x38, 0x2ef: 0x39, + 0x2f0: 0x3a, 0x2f1: 0x3b, 0x2f2: 0x3c, 0x2f3: 0x3d, 0x2f4: 0x3e, 0x2f5: 0x3f, 0x2f6: 0x40, 0x2f7: 0x41, + 0x2f8: 0x42, 0x2f9: 0x43, 0x2fa: 0x44, 0x2fb: 0x45, 0x2fc: 0xbc, 0x2fd: 0x46, 0x2fe: 0x47, 0x2ff: 0xbd, + // Block 0xc, offset 0x300 + 0x307: 0xbe, + 0x328: 0xbf, + // Block 0xd, offset 0x340 + 0x341: 0xb1, 0x342: 0xc0, 0x344: 0xc1, 0x347: 0xb6, + 0x35a: 0xc2, + // Block 0xe, offset 0x380 + 0x385: 0xc3, 0x386: 0xc4, 0x387: 0xc5, + 0x389: 0xc6, + 0x390: 0xc7, 0x391: 0xc8, 0x392: 0xc9, 0x393: 0xca, 0x394: 0xcb, 0x395: 0xcc, 0x396: 0xcd, 0x397: 0xce, + 0x398: 0xcf, 0x399: 0xd0, 0x39a: 0x48, 0x39b: 0xd1, 0x39c: 0xd2, 0x39d: 0xd3, 0x39e: 0xd4, 0x39f: 0x49, + // Block 0xf, offset 0x3c0 + 0x3f8: 0x4a, 0x3f9: 0x4b, 0x3fa: 0x4c, + // Block 0x10, offset 0x400 + 0x404: 0x4d, 0x405: 0xd5, 0x406: 0xd6, + 0x408: 0x4e, 0x409: 0xd7, + // Block 0x11, offset 0x440 + 0x460: 0x4f, 0x461: 0x50, 0x462: 0x51, 0x463: 0x52, 0x464: 0x53, 0x465: 0x54, 0x466: 0x55, 0x467: 0x56, + 0x468: 0x57, + // Block 0x12, offset 0x480 + 0x490: 0x0a, 0x491: 0x0b, + 0x49d: 0x0c, 0x49e: 0x0d, 0x49f: 0x0e, + 0x4af: 0x0f, +} + +var nfkcTrie = trie{nfkcLookup[:], nfkcValues[:], nfkcSparseValues[:], nfkcSparseOffset[:], 88} + +// recompMap: 7464 bytes (entries only) +var recompMap = map[uint32]rune{ + 0x00410300: 0x00C0, + 0x00410301: 0x00C1, + 0x00410302: 0x00C2, + 0x00410303: 0x00C3, + 0x00410308: 0x00C4, + 0x0041030A: 0x00C5, + 0x00430327: 0x00C7, + 0x00450300: 0x00C8, + 0x00450301: 0x00C9, + 0x00450302: 0x00CA, + 0x00450308: 0x00CB, + 0x00490300: 0x00CC, + 0x00490301: 0x00CD, + 0x00490302: 0x00CE, + 0x00490308: 0x00CF, + 0x004E0303: 0x00D1, + 0x004F0300: 0x00D2, + 0x004F0301: 0x00D3, + 0x004F0302: 0x00D4, + 0x004F0303: 0x00D5, + 0x004F0308: 0x00D6, + 0x00550300: 0x00D9, + 0x00550301: 0x00DA, + 0x00550302: 0x00DB, + 0x00550308: 0x00DC, + 0x00590301: 0x00DD, + 0x00610300: 0x00E0, + 0x00610301: 0x00E1, + 0x00610302: 0x00E2, + 0x00610303: 0x00E3, + 0x00610308: 0x00E4, + 0x0061030A: 0x00E5, + 0x00630327: 0x00E7, + 0x00650300: 0x00E8, + 0x00650301: 0x00E9, + 0x00650302: 0x00EA, + 0x00650308: 0x00EB, + 0x00690300: 0x00EC, + 0x00690301: 0x00ED, + 0x00690302: 0x00EE, + 0x00690308: 0x00EF, + 0x006E0303: 0x00F1, + 0x006F0300: 0x00F2, + 0x006F0301: 0x00F3, + 0x006F0302: 0x00F4, + 0x006F0303: 0x00F5, + 0x006F0308: 0x00F6, + 0x00750300: 0x00F9, + 0x00750301: 0x00FA, + 0x00750302: 0x00FB, + 0x00750308: 0x00FC, + 0x00790301: 0x00FD, + 0x00790308: 0x00FF, + 0x00410304: 0x0100, + 0x00610304: 0x0101, + 0x00410306: 0x0102, + 0x00610306: 0x0103, + 0x00410328: 0x0104, + 0x00610328: 0x0105, + 0x00430301: 0x0106, + 0x00630301: 0x0107, + 0x00430302: 0x0108, + 0x00630302: 0x0109, + 0x00430307: 0x010A, + 0x00630307: 0x010B, + 0x0043030C: 0x010C, + 0x0063030C: 0x010D, + 0x0044030C: 0x010E, + 0x0064030C: 0x010F, + 0x00450304: 0x0112, + 0x00650304: 0x0113, + 0x00450306: 0x0114, + 0x00650306: 0x0115, + 0x00450307: 0x0116, + 0x00650307: 0x0117, + 0x00450328: 0x0118, + 0x00650328: 0x0119, + 0x0045030C: 0x011A, + 0x0065030C: 0x011B, + 0x00470302: 0x011C, + 0x00670302: 0x011D, + 0x00470306: 0x011E, + 0x00670306: 0x011F, + 0x00470307: 0x0120, + 0x00670307: 0x0121, + 0x00470327: 0x0122, + 0x00670327: 0x0123, + 0x00480302: 0x0124, + 0x00680302: 0x0125, + 0x00490303: 0x0128, + 0x00690303: 0x0129, + 0x00490304: 0x012A, + 0x00690304: 0x012B, + 0x00490306: 0x012C, + 0x00690306: 0x012D, + 0x00490328: 0x012E, + 0x00690328: 0x012F, + 0x00490307: 0x0130, + 0x004A0302: 0x0134, + 0x006A0302: 0x0135, + 0x004B0327: 0x0136, + 0x006B0327: 0x0137, + 0x004C0301: 0x0139, + 0x006C0301: 0x013A, + 0x004C0327: 0x013B, + 0x006C0327: 0x013C, + 0x004C030C: 0x013D, + 0x006C030C: 0x013E, + 0x004E0301: 0x0143, + 0x006E0301: 0x0144, + 0x004E0327: 0x0145, + 0x006E0327: 0x0146, + 0x004E030C: 0x0147, + 0x006E030C: 0x0148, + 0x004F0304: 0x014C, + 0x006F0304: 0x014D, + 0x004F0306: 0x014E, + 0x006F0306: 0x014F, + 0x004F030B: 0x0150, + 0x006F030B: 0x0151, + 0x00520301: 0x0154, + 0x00720301: 0x0155, + 0x00520327: 0x0156, + 0x00720327: 0x0157, + 0x0052030C: 0x0158, + 0x0072030C: 0x0159, + 0x00530301: 0x015A, + 0x00730301: 0x015B, + 0x00530302: 0x015C, + 0x00730302: 0x015D, + 0x00530327: 0x015E, + 0x00730327: 0x015F, + 0x0053030C: 0x0160, + 0x0073030C: 0x0161, + 0x00540327: 0x0162, + 0x00740327: 0x0163, + 0x0054030C: 0x0164, + 0x0074030C: 0x0165, + 0x00550303: 0x0168, + 0x00750303: 0x0169, + 0x00550304: 0x016A, + 0x00750304: 0x016B, + 0x00550306: 0x016C, + 0x00750306: 0x016D, + 0x0055030A: 0x016E, + 0x0075030A: 0x016F, + 0x0055030B: 0x0170, + 0x0075030B: 0x0171, + 0x00550328: 0x0172, + 0x00750328: 0x0173, + 0x00570302: 0x0174, + 0x00770302: 0x0175, + 0x00590302: 0x0176, + 0x00790302: 0x0177, + 0x00590308: 0x0178, + 0x005A0301: 0x0179, + 0x007A0301: 0x017A, + 0x005A0307: 0x017B, + 0x007A0307: 0x017C, + 0x005A030C: 0x017D, + 0x007A030C: 0x017E, + 0x004F031B: 0x01A0, + 0x006F031B: 0x01A1, + 0x0055031B: 0x01AF, + 0x0075031B: 0x01B0, + 0x0041030C: 0x01CD, + 0x0061030C: 0x01CE, + 0x0049030C: 0x01CF, + 0x0069030C: 0x01D0, + 0x004F030C: 0x01D1, + 0x006F030C: 0x01D2, + 0x0055030C: 0x01D3, + 0x0075030C: 0x01D4, + 0x00DC0304: 0x01D5, + 0x00FC0304: 0x01D6, + 0x00DC0301: 0x01D7, + 0x00FC0301: 0x01D8, + 0x00DC030C: 0x01D9, + 0x00FC030C: 0x01DA, + 0x00DC0300: 0x01DB, + 0x00FC0300: 0x01DC, + 0x00C40304: 0x01DE, + 0x00E40304: 0x01DF, + 0x02260304: 0x01E0, + 0x02270304: 0x01E1, + 0x00C60304: 0x01E2, + 0x00E60304: 0x01E3, + 0x0047030C: 0x01E6, + 0x0067030C: 0x01E7, + 0x004B030C: 0x01E8, + 0x006B030C: 0x01E9, + 0x004F0328: 0x01EA, + 0x006F0328: 0x01EB, + 0x01EA0304: 0x01EC, + 0x01EB0304: 0x01ED, + 0x01B7030C: 0x01EE, + 0x0292030C: 0x01EF, + 0x006A030C: 0x01F0, + 0x00470301: 0x01F4, + 0x00670301: 0x01F5, + 0x004E0300: 0x01F8, + 0x006E0300: 0x01F9, + 0x00C50301: 0x01FA, + 0x00E50301: 0x01FB, + 0x00C60301: 0x01FC, + 0x00E60301: 0x01FD, + 0x00D80301: 0x01FE, + 0x00F80301: 0x01FF, + 0x0041030F: 0x0200, + 0x0061030F: 0x0201, + 0x00410311: 0x0202, + 0x00610311: 0x0203, + 0x0045030F: 0x0204, + 0x0065030F: 0x0205, + 0x00450311: 0x0206, + 0x00650311: 0x0207, + 0x0049030F: 0x0208, + 0x0069030F: 0x0209, + 0x00490311: 0x020A, + 0x00690311: 0x020B, + 0x004F030F: 0x020C, + 0x006F030F: 0x020D, + 0x004F0311: 0x020E, + 0x006F0311: 0x020F, + 0x0052030F: 0x0210, + 0x0072030F: 0x0211, + 0x00520311: 0x0212, + 0x00720311: 0x0213, + 0x0055030F: 0x0214, + 0x0075030F: 0x0215, + 0x00550311: 0x0216, + 0x00750311: 0x0217, + 0x00530326: 0x0218, + 0x00730326: 0x0219, + 0x00540326: 0x021A, + 0x00740326: 0x021B, + 0x0048030C: 0x021E, + 0x0068030C: 0x021F, + 0x00410307: 0x0226, + 0x00610307: 0x0227, + 0x00450327: 0x0228, + 0x00650327: 0x0229, + 0x00D60304: 0x022A, + 0x00F60304: 0x022B, + 0x00D50304: 0x022C, + 0x00F50304: 0x022D, + 0x004F0307: 0x022E, + 0x006F0307: 0x022F, + 0x022E0304: 0x0230, + 0x022F0304: 0x0231, + 0x00590304: 0x0232, + 0x00790304: 0x0233, + 0x00A80301: 0x0385, + 0x03910301: 0x0386, + 0x03950301: 0x0388, + 0x03970301: 0x0389, + 0x03990301: 0x038A, + 0x039F0301: 0x038C, + 0x03A50301: 0x038E, + 0x03A90301: 0x038F, + 0x03CA0301: 0x0390, + 0x03990308: 0x03AA, + 0x03A50308: 0x03AB, + 0x03B10301: 0x03AC, + 0x03B50301: 0x03AD, + 0x03B70301: 0x03AE, + 0x03B90301: 0x03AF, + 0x03CB0301: 0x03B0, + 0x03B90308: 0x03CA, + 0x03C50308: 0x03CB, + 0x03BF0301: 0x03CC, + 0x03C50301: 0x03CD, + 0x03C90301: 0x03CE, + 0x03D20301: 0x03D3, + 0x03D20308: 0x03D4, + 0x04150300: 0x0400, + 0x04150308: 0x0401, + 0x04130301: 0x0403, + 0x04060308: 0x0407, + 0x041A0301: 0x040C, + 0x04180300: 0x040D, + 0x04230306: 0x040E, + 0x04180306: 0x0419, + 0x04380306: 0x0439, + 0x04350300: 0x0450, + 0x04350308: 0x0451, + 0x04330301: 0x0453, + 0x04560308: 0x0457, + 0x043A0301: 0x045C, + 0x04380300: 0x045D, + 0x04430306: 0x045E, + 0x0474030F: 0x0476, + 0x0475030F: 0x0477, + 0x04160306: 0x04C1, + 0x04360306: 0x04C2, + 0x04100306: 0x04D0, + 0x04300306: 0x04D1, + 0x04100308: 0x04D2, + 0x04300308: 0x04D3, + 0x04150306: 0x04D6, + 0x04350306: 0x04D7, + 0x04D80308: 0x04DA, + 0x04D90308: 0x04DB, + 0x04160308: 0x04DC, + 0x04360308: 0x04DD, + 0x04170308: 0x04DE, + 0x04370308: 0x04DF, + 0x04180304: 0x04E2, + 0x04380304: 0x04E3, + 0x04180308: 0x04E4, + 0x04380308: 0x04E5, + 0x041E0308: 0x04E6, + 0x043E0308: 0x04E7, + 0x04E80308: 0x04EA, + 0x04E90308: 0x04EB, + 0x042D0308: 0x04EC, + 0x044D0308: 0x04ED, + 0x04230304: 0x04EE, + 0x04430304: 0x04EF, + 0x04230308: 0x04F0, + 0x04430308: 0x04F1, + 0x0423030B: 0x04F2, + 0x0443030B: 0x04F3, + 0x04270308: 0x04F4, + 0x04470308: 0x04F5, + 0x042B0308: 0x04F8, + 0x044B0308: 0x04F9, + 0x06270653: 0x0622, + 0x06270654: 0x0623, + 0x06480654: 0x0624, + 0x06270655: 0x0625, + 0x064A0654: 0x0626, + 0x06D50654: 0x06C0, + 0x06C10654: 0x06C2, + 0x06D20654: 0x06D3, + 0x0928093C: 0x0929, + 0x0930093C: 0x0931, + 0x0933093C: 0x0934, + 0x09C709BE: 0x09CB, + 0x09C709D7: 0x09CC, + 0x0B470B56: 0x0B48, + 0x0B470B3E: 0x0B4B, + 0x0B470B57: 0x0B4C, + 0x0B920BD7: 0x0B94, + 0x0BC60BBE: 0x0BCA, + 0x0BC70BBE: 0x0BCB, + 0x0BC60BD7: 0x0BCC, + 0x0C460C56: 0x0C48, + 0x0CBF0CD5: 0x0CC0, + 0x0CC60CD5: 0x0CC7, + 0x0CC60CD6: 0x0CC8, + 0x0CC60CC2: 0x0CCA, + 0x0CCA0CD5: 0x0CCB, + 0x0D460D3E: 0x0D4A, + 0x0D470D3E: 0x0D4B, + 0x0D460D57: 0x0D4C, + 0x0DD90DCA: 0x0DDA, + 0x0DD90DCF: 0x0DDC, + 0x0DDC0DCA: 0x0DDD, + 0x0DD90DDF: 0x0DDE, + 0x1025102E: 0x1026, + 0x1B051B35: 0x1B06, + 0x1B071B35: 0x1B08, + 0x1B091B35: 0x1B0A, + 0x1B0B1B35: 0x1B0C, + 0x1B0D1B35: 0x1B0E, + 0x1B111B35: 0x1B12, + 0x1B3A1B35: 0x1B3B, + 0x1B3C1B35: 0x1B3D, + 0x1B3E1B35: 0x1B40, + 0x1B3F1B35: 0x1B41, + 0x1B421B35: 0x1B43, + 0x00410325: 0x1E00, + 0x00610325: 0x1E01, + 0x00420307: 0x1E02, + 0x00620307: 0x1E03, + 0x00420323: 0x1E04, + 0x00620323: 0x1E05, + 0x00420331: 0x1E06, + 0x00620331: 0x1E07, + 0x00C70301: 0x1E08, + 0x00E70301: 0x1E09, + 0x00440307: 0x1E0A, + 0x00640307: 0x1E0B, + 0x00440323: 0x1E0C, + 0x00640323: 0x1E0D, + 0x00440331: 0x1E0E, + 0x00640331: 0x1E0F, + 0x00440327: 0x1E10, + 0x00640327: 0x1E11, + 0x0044032D: 0x1E12, + 0x0064032D: 0x1E13, + 0x01120300: 0x1E14, + 0x01130300: 0x1E15, + 0x01120301: 0x1E16, + 0x01130301: 0x1E17, + 0x0045032D: 0x1E18, + 0x0065032D: 0x1E19, + 0x00450330: 0x1E1A, + 0x00650330: 0x1E1B, + 0x02280306: 0x1E1C, + 0x02290306: 0x1E1D, + 0x00460307: 0x1E1E, + 0x00660307: 0x1E1F, + 0x00470304: 0x1E20, + 0x00670304: 0x1E21, + 0x00480307: 0x1E22, + 0x00680307: 0x1E23, + 0x00480323: 0x1E24, + 0x00680323: 0x1E25, + 0x00480308: 0x1E26, + 0x00680308: 0x1E27, + 0x00480327: 0x1E28, + 0x00680327: 0x1E29, + 0x0048032E: 0x1E2A, + 0x0068032E: 0x1E2B, + 0x00490330: 0x1E2C, + 0x00690330: 0x1E2D, + 0x00CF0301: 0x1E2E, + 0x00EF0301: 0x1E2F, + 0x004B0301: 0x1E30, + 0x006B0301: 0x1E31, + 0x004B0323: 0x1E32, + 0x006B0323: 0x1E33, + 0x004B0331: 0x1E34, + 0x006B0331: 0x1E35, + 0x004C0323: 0x1E36, + 0x006C0323: 0x1E37, + 0x1E360304: 0x1E38, + 0x1E370304: 0x1E39, + 0x004C0331: 0x1E3A, + 0x006C0331: 0x1E3B, + 0x004C032D: 0x1E3C, + 0x006C032D: 0x1E3D, + 0x004D0301: 0x1E3E, + 0x006D0301: 0x1E3F, + 0x004D0307: 0x1E40, + 0x006D0307: 0x1E41, + 0x004D0323: 0x1E42, + 0x006D0323: 0x1E43, + 0x004E0307: 0x1E44, + 0x006E0307: 0x1E45, + 0x004E0323: 0x1E46, + 0x006E0323: 0x1E47, + 0x004E0331: 0x1E48, + 0x006E0331: 0x1E49, + 0x004E032D: 0x1E4A, + 0x006E032D: 0x1E4B, + 0x00D50301: 0x1E4C, + 0x00F50301: 0x1E4D, + 0x00D50308: 0x1E4E, + 0x00F50308: 0x1E4F, + 0x014C0300: 0x1E50, + 0x014D0300: 0x1E51, + 0x014C0301: 0x1E52, + 0x014D0301: 0x1E53, + 0x00500301: 0x1E54, + 0x00700301: 0x1E55, + 0x00500307: 0x1E56, + 0x00700307: 0x1E57, + 0x00520307: 0x1E58, + 0x00720307: 0x1E59, + 0x00520323: 0x1E5A, + 0x00720323: 0x1E5B, + 0x1E5A0304: 0x1E5C, + 0x1E5B0304: 0x1E5D, + 0x00520331: 0x1E5E, + 0x00720331: 0x1E5F, + 0x00530307: 0x1E60, + 0x00730307: 0x1E61, + 0x00530323: 0x1E62, + 0x00730323: 0x1E63, + 0x015A0307: 0x1E64, + 0x015B0307: 0x1E65, + 0x01600307: 0x1E66, + 0x01610307: 0x1E67, + 0x1E620307: 0x1E68, + 0x1E630307: 0x1E69, + 0x00540307: 0x1E6A, + 0x00740307: 0x1E6B, + 0x00540323: 0x1E6C, + 0x00740323: 0x1E6D, + 0x00540331: 0x1E6E, + 0x00740331: 0x1E6F, + 0x0054032D: 0x1E70, + 0x0074032D: 0x1E71, + 0x00550324: 0x1E72, + 0x00750324: 0x1E73, + 0x00550330: 0x1E74, + 0x00750330: 0x1E75, + 0x0055032D: 0x1E76, + 0x0075032D: 0x1E77, + 0x01680301: 0x1E78, + 0x01690301: 0x1E79, + 0x016A0308: 0x1E7A, + 0x016B0308: 0x1E7B, + 0x00560303: 0x1E7C, + 0x00760303: 0x1E7D, + 0x00560323: 0x1E7E, + 0x00760323: 0x1E7F, + 0x00570300: 0x1E80, + 0x00770300: 0x1E81, + 0x00570301: 0x1E82, + 0x00770301: 0x1E83, + 0x00570308: 0x1E84, + 0x00770308: 0x1E85, + 0x00570307: 0x1E86, + 0x00770307: 0x1E87, + 0x00570323: 0x1E88, + 0x00770323: 0x1E89, + 0x00580307: 0x1E8A, + 0x00780307: 0x1E8B, + 0x00580308: 0x1E8C, + 0x00780308: 0x1E8D, + 0x00590307: 0x1E8E, + 0x00790307: 0x1E8F, + 0x005A0302: 0x1E90, + 0x007A0302: 0x1E91, + 0x005A0323: 0x1E92, + 0x007A0323: 0x1E93, + 0x005A0331: 0x1E94, + 0x007A0331: 0x1E95, + 0x00680331: 0x1E96, + 0x00740308: 0x1E97, + 0x0077030A: 0x1E98, + 0x0079030A: 0x1E99, + 0x017F0307: 0x1E9B, + 0x00410323: 0x1EA0, + 0x00610323: 0x1EA1, + 0x00410309: 0x1EA2, + 0x00610309: 0x1EA3, + 0x00C20301: 0x1EA4, + 0x00E20301: 0x1EA5, + 0x00C20300: 0x1EA6, + 0x00E20300: 0x1EA7, + 0x00C20309: 0x1EA8, + 0x00E20309: 0x1EA9, + 0x00C20303: 0x1EAA, + 0x00E20303: 0x1EAB, + 0x1EA00302: 0x1EAC, + 0x1EA10302: 0x1EAD, + 0x01020301: 0x1EAE, + 0x01030301: 0x1EAF, + 0x01020300: 0x1EB0, + 0x01030300: 0x1EB1, + 0x01020309: 0x1EB2, + 0x01030309: 0x1EB3, + 0x01020303: 0x1EB4, + 0x01030303: 0x1EB5, + 0x1EA00306: 0x1EB6, + 0x1EA10306: 0x1EB7, + 0x00450323: 0x1EB8, + 0x00650323: 0x1EB9, + 0x00450309: 0x1EBA, + 0x00650309: 0x1EBB, + 0x00450303: 0x1EBC, + 0x00650303: 0x1EBD, + 0x00CA0301: 0x1EBE, + 0x00EA0301: 0x1EBF, + 0x00CA0300: 0x1EC0, + 0x00EA0300: 0x1EC1, + 0x00CA0309: 0x1EC2, + 0x00EA0309: 0x1EC3, + 0x00CA0303: 0x1EC4, + 0x00EA0303: 0x1EC5, + 0x1EB80302: 0x1EC6, + 0x1EB90302: 0x1EC7, + 0x00490309: 0x1EC8, + 0x00690309: 0x1EC9, + 0x00490323: 0x1ECA, + 0x00690323: 0x1ECB, + 0x004F0323: 0x1ECC, + 0x006F0323: 0x1ECD, + 0x004F0309: 0x1ECE, + 0x006F0309: 0x1ECF, + 0x00D40301: 0x1ED0, + 0x00F40301: 0x1ED1, + 0x00D40300: 0x1ED2, + 0x00F40300: 0x1ED3, + 0x00D40309: 0x1ED4, + 0x00F40309: 0x1ED5, + 0x00D40303: 0x1ED6, + 0x00F40303: 0x1ED7, + 0x1ECC0302: 0x1ED8, + 0x1ECD0302: 0x1ED9, + 0x01A00301: 0x1EDA, + 0x01A10301: 0x1EDB, + 0x01A00300: 0x1EDC, + 0x01A10300: 0x1EDD, + 0x01A00309: 0x1EDE, + 0x01A10309: 0x1EDF, + 0x01A00303: 0x1EE0, + 0x01A10303: 0x1EE1, + 0x01A00323: 0x1EE2, + 0x01A10323: 0x1EE3, + 0x00550323: 0x1EE4, + 0x00750323: 0x1EE5, + 0x00550309: 0x1EE6, + 0x00750309: 0x1EE7, + 0x01AF0301: 0x1EE8, + 0x01B00301: 0x1EE9, + 0x01AF0300: 0x1EEA, + 0x01B00300: 0x1EEB, + 0x01AF0309: 0x1EEC, + 0x01B00309: 0x1EED, + 0x01AF0303: 0x1EEE, + 0x01B00303: 0x1EEF, + 0x01AF0323: 0x1EF0, + 0x01B00323: 0x1EF1, + 0x00590300: 0x1EF2, + 0x00790300: 0x1EF3, + 0x00590323: 0x1EF4, + 0x00790323: 0x1EF5, + 0x00590309: 0x1EF6, + 0x00790309: 0x1EF7, + 0x00590303: 0x1EF8, + 0x00790303: 0x1EF9, + 0x03B10313: 0x1F00, + 0x03B10314: 0x1F01, + 0x1F000300: 0x1F02, + 0x1F010300: 0x1F03, + 0x1F000301: 0x1F04, + 0x1F010301: 0x1F05, + 0x1F000342: 0x1F06, + 0x1F010342: 0x1F07, + 0x03910313: 0x1F08, + 0x03910314: 0x1F09, + 0x1F080300: 0x1F0A, + 0x1F090300: 0x1F0B, + 0x1F080301: 0x1F0C, + 0x1F090301: 0x1F0D, + 0x1F080342: 0x1F0E, + 0x1F090342: 0x1F0F, + 0x03B50313: 0x1F10, + 0x03B50314: 0x1F11, + 0x1F100300: 0x1F12, + 0x1F110300: 0x1F13, + 0x1F100301: 0x1F14, + 0x1F110301: 0x1F15, + 0x03950313: 0x1F18, + 0x03950314: 0x1F19, + 0x1F180300: 0x1F1A, + 0x1F190300: 0x1F1B, + 0x1F180301: 0x1F1C, + 0x1F190301: 0x1F1D, + 0x03B70313: 0x1F20, + 0x03B70314: 0x1F21, + 0x1F200300: 0x1F22, + 0x1F210300: 0x1F23, + 0x1F200301: 0x1F24, + 0x1F210301: 0x1F25, + 0x1F200342: 0x1F26, + 0x1F210342: 0x1F27, + 0x03970313: 0x1F28, + 0x03970314: 0x1F29, + 0x1F280300: 0x1F2A, + 0x1F290300: 0x1F2B, + 0x1F280301: 0x1F2C, + 0x1F290301: 0x1F2D, + 0x1F280342: 0x1F2E, + 0x1F290342: 0x1F2F, + 0x03B90313: 0x1F30, + 0x03B90314: 0x1F31, + 0x1F300300: 0x1F32, + 0x1F310300: 0x1F33, + 0x1F300301: 0x1F34, + 0x1F310301: 0x1F35, + 0x1F300342: 0x1F36, + 0x1F310342: 0x1F37, + 0x03990313: 0x1F38, + 0x03990314: 0x1F39, + 0x1F380300: 0x1F3A, + 0x1F390300: 0x1F3B, + 0x1F380301: 0x1F3C, + 0x1F390301: 0x1F3D, + 0x1F380342: 0x1F3E, + 0x1F390342: 0x1F3F, + 0x03BF0313: 0x1F40, + 0x03BF0314: 0x1F41, + 0x1F400300: 0x1F42, + 0x1F410300: 0x1F43, + 0x1F400301: 0x1F44, + 0x1F410301: 0x1F45, + 0x039F0313: 0x1F48, + 0x039F0314: 0x1F49, + 0x1F480300: 0x1F4A, + 0x1F490300: 0x1F4B, + 0x1F480301: 0x1F4C, + 0x1F490301: 0x1F4D, + 0x03C50313: 0x1F50, + 0x03C50314: 0x1F51, + 0x1F500300: 0x1F52, + 0x1F510300: 0x1F53, + 0x1F500301: 0x1F54, + 0x1F510301: 0x1F55, + 0x1F500342: 0x1F56, + 0x1F510342: 0x1F57, + 0x03A50314: 0x1F59, + 0x1F590300: 0x1F5B, + 0x1F590301: 0x1F5D, + 0x1F590342: 0x1F5F, + 0x03C90313: 0x1F60, + 0x03C90314: 0x1F61, + 0x1F600300: 0x1F62, + 0x1F610300: 0x1F63, + 0x1F600301: 0x1F64, + 0x1F610301: 0x1F65, + 0x1F600342: 0x1F66, + 0x1F610342: 0x1F67, + 0x03A90313: 0x1F68, + 0x03A90314: 0x1F69, + 0x1F680300: 0x1F6A, + 0x1F690300: 0x1F6B, + 0x1F680301: 0x1F6C, + 0x1F690301: 0x1F6D, + 0x1F680342: 0x1F6E, + 0x1F690342: 0x1F6F, + 0x03B10300: 0x1F70, + 0x03B50300: 0x1F72, + 0x03B70300: 0x1F74, + 0x03B90300: 0x1F76, + 0x03BF0300: 0x1F78, + 0x03C50300: 0x1F7A, + 0x03C90300: 0x1F7C, + 0x1F000345: 0x1F80, + 0x1F010345: 0x1F81, + 0x1F020345: 0x1F82, + 0x1F030345: 0x1F83, + 0x1F040345: 0x1F84, + 0x1F050345: 0x1F85, + 0x1F060345: 0x1F86, + 0x1F070345: 0x1F87, + 0x1F080345: 0x1F88, + 0x1F090345: 0x1F89, + 0x1F0A0345: 0x1F8A, + 0x1F0B0345: 0x1F8B, + 0x1F0C0345: 0x1F8C, + 0x1F0D0345: 0x1F8D, + 0x1F0E0345: 0x1F8E, + 0x1F0F0345: 0x1F8F, + 0x1F200345: 0x1F90, + 0x1F210345: 0x1F91, + 0x1F220345: 0x1F92, + 0x1F230345: 0x1F93, + 0x1F240345: 0x1F94, + 0x1F250345: 0x1F95, + 0x1F260345: 0x1F96, + 0x1F270345: 0x1F97, + 0x1F280345: 0x1F98, + 0x1F290345: 0x1F99, + 0x1F2A0345: 0x1F9A, + 0x1F2B0345: 0x1F9B, + 0x1F2C0345: 0x1F9C, + 0x1F2D0345: 0x1F9D, + 0x1F2E0345: 0x1F9E, + 0x1F2F0345: 0x1F9F, + 0x1F600345: 0x1FA0, + 0x1F610345: 0x1FA1, + 0x1F620345: 0x1FA2, + 0x1F630345: 0x1FA3, + 0x1F640345: 0x1FA4, + 0x1F650345: 0x1FA5, + 0x1F660345: 0x1FA6, + 0x1F670345: 0x1FA7, + 0x1F680345: 0x1FA8, + 0x1F690345: 0x1FA9, + 0x1F6A0345: 0x1FAA, + 0x1F6B0345: 0x1FAB, + 0x1F6C0345: 0x1FAC, + 0x1F6D0345: 0x1FAD, + 0x1F6E0345: 0x1FAE, + 0x1F6F0345: 0x1FAF, + 0x03B10306: 0x1FB0, + 0x03B10304: 0x1FB1, + 0x1F700345: 0x1FB2, + 0x03B10345: 0x1FB3, + 0x03AC0345: 0x1FB4, + 0x03B10342: 0x1FB6, + 0x1FB60345: 0x1FB7, + 0x03910306: 0x1FB8, + 0x03910304: 0x1FB9, + 0x03910300: 0x1FBA, + 0x03910345: 0x1FBC, + 0x00A80342: 0x1FC1, + 0x1F740345: 0x1FC2, + 0x03B70345: 0x1FC3, + 0x03AE0345: 0x1FC4, + 0x03B70342: 0x1FC6, + 0x1FC60345: 0x1FC7, + 0x03950300: 0x1FC8, + 0x03970300: 0x1FCA, + 0x03970345: 0x1FCC, + 0x1FBF0300: 0x1FCD, + 0x1FBF0301: 0x1FCE, + 0x1FBF0342: 0x1FCF, + 0x03B90306: 0x1FD0, + 0x03B90304: 0x1FD1, + 0x03CA0300: 0x1FD2, + 0x03B90342: 0x1FD6, + 0x03CA0342: 0x1FD7, + 0x03990306: 0x1FD8, + 0x03990304: 0x1FD9, + 0x03990300: 0x1FDA, + 0x1FFE0300: 0x1FDD, + 0x1FFE0301: 0x1FDE, + 0x1FFE0342: 0x1FDF, + 0x03C50306: 0x1FE0, + 0x03C50304: 0x1FE1, + 0x03CB0300: 0x1FE2, + 0x03C10313: 0x1FE4, + 0x03C10314: 0x1FE5, + 0x03C50342: 0x1FE6, + 0x03CB0342: 0x1FE7, + 0x03A50306: 0x1FE8, + 0x03A50304: 0x1FE9, + 0x03A50300: 0x1FEA, + 0x03A10314: 0x1FEC, + 0x00A80300: 0x1FED, + 0x1F7C0345: 0x1FF2, + 0x03C90345: 0x1FF3, + 0x03CE0345: 0x1FF4, + 0x03C90342: 0x1FF6, + 0x1FF60345: 0x1FF7, + 0x039F0300: 0x1FF8, + 0x03A90300: 0x1FFA, + 0x03A90345: 0x1FFC, + 0x21900338: 0x219A, + 0x21920338: 0x219B, + 0x21940338: 0x21AE, + 0x21D00338: 0x21CD, + 0x21D40338: 0x21CE, + 0x21D20338: 0x21CF, + 0x22030338: 0x2204, + 0x22080338: 0x2209, + 0x220B0338: 0x220C, + 0x22230338: 0x2224, + 0x22250338: 0x2226, + 0x223C0338: 0x2241, + 0x22430338: 0x2244, + 0x22450338: 0x2247, + 0x22480338: 0x2249, + 0x003D0338: 0x2260, + 0x22610338: 0x2262, + 0x224D0338: 0x226D, + 0x003C0338: 0x226E, + 0x003E0338: 0x226F, + 0x22640338: 0x2270, + 0x22650338: 0x2271, + 0x22720338: 0x2274, + 0x22730338: 0x2275, + 0x22760338: 0x2278, + 0x22770338: 0x2279, + 0x227A0338: 0x2280, + 0x227B0338: 0x2281, + 0x22820338: 0x2284, + 0x22830338: 0x2285, + 0x22860338: 0x2288, + 0x22870338: 0x2289, + 0x22A20338: 0x22AC, + 0x22A80338: 0x22AD, + 0x22A90338: 0x22AE, + 0x22AB0338: 0x22AF, + 0x227C0338: 0x22E0, + 0x227D0338: 0x22E1, + 0x22910338: 0x22E2, + 0x22920338: 0x22E3, + 0x22B20338: 0x22EA, + 0x22B30338: 0x22EB, + 0x22B40338: 0x22EC, + 0x22B50338: 0x22ED, + 0x304B3099: 0x304C, + 0x304D3099: 0x304E, + 0x304F3099: 0x3050, + 0x30513099: 0x3052, + 0x30533099: 0x3054, + 0x30553099: 0x3056, + 0x30573099: 0x3058, + 0x30593099: 0x305A, + 0x305B3099: 0x305C, + 0x305D3099: 0x305E, + 0x305F3099: 0x3060, + 0x30613099: 0x3062, + 0x30643099: 0x3065, + 0x30663099: 0x3067, + 0x30683099: 0x3069, + 0x306F3099: 0x3070, + 0x306F309A: 0x3071, + 0x30723099: 0x3073, + 0x3072309A: 0x3074, + 0x30753099: 0x3076, + 0x3075309A: 0x3077, + 0x30783099: 0x3079, + 0x3078309A: 0x307A, + 0x307B3099: 0x307C, + 0x307B309A: 0x307D, + 0x30463099: 0x3094, + 0x309D3099: 0x309E, + 0x30AB3099: 0x30AC, + 0x30AD3099: 0x30AE, + 0x30AF3099: 0x30B0, + 0x30B13099: 0x30B2, + 0x30B33099: 0x30B4, + 0x30B53099: 0x30B6, + 0x30B73099: 0x30B8, + 0x30B93099: 0x30BA, + 0x30BB3099: 0x30BC, + 0x30BD3099: 0x30BE, + 0x30BF3099: 0x30C0, + 0x30C13099: 0x30C2, + 0x30C43099: 0x30C5, + 0x30C63099: 0x30C7, + 0x30C83099: 0x30C9, + 0x30CF3099: 0x30D0, + 0x30CF309A: 0x30D1, + 0x30D23099: 0x30D3, + 0x30D2309A: 0x30D4, + 0x30D53099: 0x30D6, + 0x30D5309A: 0x30D7, + 0x30D83099: 0x30D9, + 0x30D8309A: 0x30DA, + 0x30DB3099: 0x30DC, + 0x30DB309A: 0x30DD, + 0x30A63099: 0x30F4, + 0x30EF3099: 0x30F7, + 0x30F03099: 0x30F8, + 0x30F13099: 0x30F9, + 0x30F23099: 0x30FA, + 0x30FD3099: 0x30FE, + 0x109910BA: 0x1109A, + 0x109B10BA: 0x1109C, + 0x10A510BA: 0x110AB, + 0x11311127: 0x1112E, + 0x11321127: 0x1112F, +} + +// Total size of tables: 50KB (51625 bytes) diff --git a/src/pkg/exp/norm/trie.go b/src/pkg/exp/norm/trie.go new file mode 100644 index 000000000..82267a8d3 --- /dev/null +++ b/src/pkg/exp/norm/trie.go @@ -0,0 +1,232 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +type valueRange struct { + value uint16 // header: value:stride + lo, hi byte // header: lo:n +} + +type trie struct { + index []uint8 + values []uint16 + sparse []valueRange + sparseOffset []uint16 + cutoff uint8 // indices >= cutoff are sparse +} + +// lookupValue determines the type of block n and looks up the value for b. +// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block +// is a list of ranges with an accompanying value. Given a matching range r, +// the value for b is by r.value + (b - r.lo) * stride. +func (t *trie) lookupValue(n uint8, b byte) uint16 { + if n < t.cutoff { + return t.values[uint16(n)<<6+uint16(b)] + } + offset := t.sparseOffset[n-t.cutoff] + header := t.sparse[offset] + lo := offset + 1 + hi := lo + uint16(header.lo) + for lo < hi { + m := lo + (hi-lo)/2 + r := t.sparse[m] + if r.lo <= b && b <= r.hi { + return r.value + uint16(b-r.lo)*header.value + } + if b < r.lo { + hi = m + } else { + lo = m + 1 + } + } + return 0 +} + +const ( + t1 = 0x00 // 0000 0000 + tx = 0x80 // 1000 0000 + t2 = 0xC0 // 1100 0000 + t3 = 0xE0 // 1110 0000 + t4 = 0xF0 // 1111 0000 + t5 = 0xF8 // 1111 1000 + t6 = 0xFC // 1111 1100 + te = 0xFE // 1111 1110 +) + +// lookup returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *trie) lookup(s []byte) (v uint16, sz int) { + c0 := s[0] + switch { + case c0 < tx: + return t.values[c0], 1 + case c0 < t2: + return 0, 1 + case c0 < t3: + if len(s) < 2 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + return t.lookupValue(i, c1), 2 + case c0 < t4: + if len(s) < 3 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1) + i = t.index[o] + c2 := s[2] + if c2 < tx || t2 <= c2 { + return 0, 2 + } + return t.lookupValue(i, c2), 3 + case c0 < t5: + if len(s) < 4 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1) + i = t.index[o] + c2 := s[2] + if c2 < tx || t2 <= c2 { + return 0, 2 + } + o = uint16(i)<<6 + uint16(c2) + i = t.index[o] + c3 := s[3] + if c3 < tx || t2 <= c3 { + return 0, 3 + } + return t.lookupValue(i, c3), 4 + } + // Illegal rune + return 0, 1 +} + +// lookupString returns the trie value for the first UTF-8 encoding in s and +// the width in bytes of this encoding. The size will be 0 if s does not +// hold enough bytes to complete the encoding. len(s) must be greater than 0. +func (t *trie) lookupString(s string) (v uint16, sz int) { + c0 := s[0] + switch { + case c0 < tx: + return t.values[c0], 1 + case c0 < t2: + return 0, 1 + case c0 < t3: + if len(s) < 2 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + return t.lookupValue(i, c1), 2 + case c0 < t4: + if len(s) < 3 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1) + i = t.index[o] + c2 := s[2] + if c2 < tx || t2 <= c2 { + return 0, 2 + } + return t.lookupValue(i, c2), 3 + case c0 < t5: + if len(s) < 4 { + return 0, 0 + } + i := t.index[c0] + c1 := s[1] + if c1 < tx || t2 <= c1 { + return 0, 1 + } + o := uint16(i)<<6 + uint16(c1) + i = t.index[o] + c2 := s[2] + if c2 < tx || t2 <= c2 { + return 0, 2 + } + o = uint16(i)<<6 + uint16(c2) + i = t.index[o] + c3 := s[3] + if c3 < tx || t2 <= c3 { + return 0, 3 + } + return t.lookupValue(i, c3), 4 + } + // Illegal rune + return 0, 1 +} + +// lookupUnsafe returns the trie value for the first UTF-8 encoding in s. +// s must hold a full encoding. +func (t *trie) lookupUnsafe(s []byte) uint16 { + c0 := s[0] + if c0 < tx { + return t.values[c0] + } + if c0 < t2 { + return 0 + } + i := t.index[c0] + if c0 < t3 { + return t.lookupValue(i, s[1]) + } + i = t.index[uint16(i)<<6+uint16(s[1])] + if c0 < t4 { + return t.lookupValue(i, s[2]) + } + i = t.index[uint16(i)<<6+uint16(s[2])] + if c0 < t5 { + return t.lookupValue(i, s[3]) + } + return 0 +} + +// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s. +// s must hold a full encoding. +func (t *trie) lookupStringUnsafe(s string) uint16 { + c0 := s[0] + if c0 < tx { + return t.values[c0] + } + if c0 < t2 { + return 0 + } + i := t.index[c0] + if c0 < t3 { + return t.lookupValue(i, s[1]) + } + i = t.index[uint16(i)<<6+uint16(s[1])] + if c0 < t4 { + return t.lookupValue(i, s[2]) + } + i = t.index[uint16(i)<<6+uint16(s[2])] + if c0 < t5 { + return t.lookupValue(i, s[3]) + } + return 0 +} diff --git a/src/pkg/exp/norm/trie_test.go b/src/pkg/exp/norm/trie_test.go new file mode 100644 index 000000000..1a75cc705 --- /dev/null +++ b/src/pkg/exp/norm/trie_test.go @@ -0,0 +1,152 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package norm + +import ( + "testing" + "unicode/utf8" +) + +// Test data is located in triedata_test.go; generated by maketesttables. +var testdata = testdataTrie + +type rangeTest struct { + block uint8 + lookup byte + result uint16 + table []valueRange + offsets []uint16 +} + +var range1Off = []uint16{0, 2} +var range1 = []valueRange{ + {0, 1, 0}, + {1, 0x80, 0x80}, + {0, 2, 0}, + {1, 0x80, 0x80}, + {9, 0xff, 0xff}, +} + +var rangeTests = []rangeTest{ + {10, 0x80, 1, range1, range1Off}, + {10, 0x00, 0, range1, range1Off}, + {11, 0x80, 1, range1, range1Off}, + {11, 0xff, 9, range1, range1Off}, + {11, 0x00, 0, range1, range1Off}, +} + +func TestLookupSparse(t *testing.T) { + for i, test := range rangeTests { + n := trie{sparse: test.table, sparseOffset: test.offsets, cutoff: 10} + v := n.lookupValue(test.block, test.lookup) + if v != test.result { + t.Errorf("LookupSparse:%d: found %X; want %X", i, v, test.result) + } + } +} + +// Test cases for illegal runes. +type trietest struct { + size int + bytes []byte +} + +var tests = []trietest{ + // illegal runes + {1, []byte{0x80}}, + {1, []byte{0xFF}}, + {1, []byte{t2, tx - 1}}, + {1, []byte{t2, t2}}, + {2, []byte{t3, tx, tx - 1}}, + {2, []byte{t3, tx, t2}}, + {1, []byte{t3, tx - 1, tx}}, + {3, []byte{t4, tx, tx, tx - 1}}, + {3, []byte{t4, tx, tx, t2}}, + {1, []byte{t4, t2, tx, tx - 1}}, + {2, []byte{t4, tx, t2, tx - 1}}, + + // short runes + {0, []byte{t2}}, + {0, []byte{t3, tx}}, + {0, []byte{t4, tx, tx}}, + + // we only support UTF-8 up to utf8.UTFMax bytes (4 bytes) + {1, []byte{t5, tx, tx, tx, tx}}, + {1, []byte{t6, tx, tx, tx, tx, tx}}, +} + +func mkUTF8(r rune) ([]byte, int) { + var b [utf8.UTFMax]byte + sz := utf8.EncodeRune(b[:], r) + return b[:sz], sz +} + +func TestLookup(t *testing.T) { + for i, tt := range testRunes { + b, szg := mkUTF8(tt) + v, szt := testdata.lookup(b) + if int(v) != i { + t.Errorf("lookup(%U): found value %#x, expected %#x", tt, v, i) + } + if szt != szg { + t.Errorf("lookup(%U): found size %d, expected %d", tt, szt, szg) + } + } + for i, tt := range tests { + v, sz := testdata.lookup(tt.bytes) + if v != 0 { + t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v) + } + if sz != tt.size { + t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size) + } + } + // Verify defaults. + if v, _ := testdata.lookup([]byte{0xC1, 0x8C}); v != 0 { + t.Errorf("lookup of non-existing rune should be 0; found %X", v) + } +} + +func TestLookupUnsafe(t *testing.T) { + for i, tt := range testRunes { + b, _ := mkUTF8(tt) + v := testdata.lookupUnsafe(b) + if int(v) != i { + t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i) + } + } +} + +func TestLookupString(t *testing.T) { + for i, tt := range testRunes { + b, szg := mkUTF8(tt) + v, szt := testdata.lookupString(string(b)) + if int(v) != i { + t.Errorf("lookup(%U): found value %#x, expected %#x", i, v, i) + } + if szt != szg { + t.Errorf("lookup(%U): found size %d, expected %d", i, szt, szg) + } + } + for i, tt := range tests { + v, sz := testdata.lookupString(string(tt.bytes)) + if int(v) != 0 { + t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v) + } + if sz != tt.size { + t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size) + } + } +} + +func TestLookupStringUnsafe(t *testing.T) { + for i, tt := range testRunes { + b, _ := mkUTF8(tt) + v := testdata.lookupStringUnsafe(string(b)) + if int(v) != i { + t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i) + } + } +} diff --git a/src/pkg/exp/norm/triedata_test.go b/src/pkg/exp/norm/triedata_test.go new file mode 100644 index 000000000..d6c832d46 --- /dev/null +++ b/src/pkg/exp/norm/triedata_test.go @@ -0,0 +1,85 @@ +// Generated by running +// maketesttables +// DO NOT EDIT + +package norm + +var testRunes = []int32{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111, 512, 513, 514, 528, 533} + +// testdataValues: 192 entries, 384 bytes +// Block 2 is the null block. +var testdataValues = [192]uint16{ + // Block 0x0, offset 0x0 + 0x000c: 0x0001, + // Block 0x1, offset 0x40 + 0x007f: 0x0002, + // Block 0x2, offset 0x80 +} + +// testdataSparseOffset: 10 entries, 20 bytes +var testdataSparseOffset = []uint16{0x0, 0x2, 0x4, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14} + +// testdataSparseValues: 22 entries, 88 bytes +var testdataSparseValues = [22]valueRange{ + // Block 0x0, offset 0x1 + {value: 0x0000, lo: 0x01}, + {value: 0x0003, lo: 0x80, hi: 0x80}, + // Block 0x1, offset 0x2 + {value: 0x0000, lo: 0x01}, + {value: 0x0004, lo: 0x80, hi: 0x80}, + // Block 0x2, offset 0x3 + {value: 0x0001, lo: 0x03}, + {value: 0x000c, lo: 0x80, hi: 0x82}, + {value: 0x000f, lo: 0x90, hi: 0x90}, + {value: 0x0010, lo: 0x95, hi: 0x95}, + // Block 0x3, offset 0x4 + {value: 0x0000, lo: 0x01}, + {value: 0x0005, lo: 0xbf, hi: 0xbf}, + // Block 0x4, offset 0x5 + {value: 0x0000, lo: 0x01}, + {value: 0x0006, lo: 0x80, hi: 0x80}, + // Block 0x5, offset 0x6 + {value: 0x0000, lo: 0x01}, + {value: 0x0007, lo: 0x99, hi: 0x99}, + // Block 0x6, offset 0x7 + {value: 0x0000, lo: 0x01}, + {value: 0x0008, lo: 0xbf, hi: 0xbf}, + // Block 0x7, offset 0x8 + {value: 0x0000, lo: 0x01}, + {value: 0x0009, lo: 0x80, hi: 0x80}, + // Block 0x8, offset 0x9 + {value: 0x0000, lo: 0x01}, + {value: 0x000a, lo: 0x81, hi: 0x81}, + // Block 0x9, offset 0xa + {value: 0x0000, lo: 0x01}, + {value: 0x000b, lo: 0xbf, hi: 0xbf}, +} + +// testdataLookup: 640 bytes +// Block 0 is the null block. +var testdataLookup = [640]uint8{ + // Block 0x0, offset 0x0 + // Block 0x1, offset 0x40 + // Block 0x2, offset 0x80 + // Block 0x3, offset 0xc0 + 0x0c2: 0x01, 0x0c4: 0x02, + 0x0c8: 0x03, + 0x0df: 0x04, + 0x0e0: 0x02, + 0x0ef: 0x03, + 0x0f0: 0x05, 0x0f4: 0x07, + // Block 0x4, offset 0x100 + 0x120: 0x05, 0x126: 0x06, + // Block 0x5, offset 0x140 + 0x17f: 0x07, + // Block 0x6, offset 0x180 + 0x180: 0x08, 0x184: 0x09, + // Block 0x7, offset 0x1c0 + 0x1d0: 0x04, + // Block 0x8, offset 0x200 + 0x23f: 0x0a, + // Block 0x9, offset 0x240 + 0x24f: 0x06, +} + +var testdataTrie = trie{testdataLookup[:], testdataValues[:], testdataSparseValues[:], testdataSparseOffset[:], 1} diff --git a/src/pkg/exp/norm/triegen.go b/src/pkg/exp/norm/triegen.go new file mode 100644 index 000000000..52c88b039 --- /dev/null +++ b/src/pkg/exp/norm/triegen.go @@ -0,0 +1,317 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Trie table generator. +// Used by make*tables tools to generate a go file with trie data structures +// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte +// sequence are used to lookup offsets in the index table to be used for the +// next byte. The last byte is used to index into a table with 16-bit values. + +package main + +import ( + "fmt" + "hash/crc32" + "log" + "unicode/utf8" +) + +const ( + blockSize = 64 + blockOffset = 2 // Subtract two blocks to compensate for the 0x80 added to continuation bytes. + maxSparseEntries = 16 +) + +// Intermediate trie structure +type trieNode struct { + table [256]*trieNode + value int + b byte + leaf bool +} + +func newNode() *trieNode { + return new(trieNode) +} + +func (n trieNode) String() string { + s := fmt.Sprint("trieNode{table: { non-nil at index: ") + for i, v := range n.table { + if v != nil { + s += fmt.Sprintf("%d, ", i) + } + } + s += fmt.Sprintf("}, value:%#x, b:%#x leaf:%v}", n.value, n.b, n.leaf) + return s +} + +func (n trieNode) isInternal() bool { + internal := true + for i := 0; i < 256; i++ { + if nn := n.table[i]; nn != nil { + if !internal && !nn.leaf { + log.Fatalf("triegen: isInternal: node contains both leaf and non-leaf children (%v)", n) + } + internal = internal && !nn.leaf + } + } + return internal +} + +func (n trieNode) mostFrequentStride() int { + counts := make(map[int]int) + v := 0 + for _, t := range n.table[0x80 : 0x80+blockSize] { + if t != nil { + if stride := t.value - v; v != 0 && stride >= 0 { + counts[stride]++ + } + v = t.value + } else { + v = 0 + } + } + var maxs, maxc int + for stride, cnt := range counts { + if cnt > maxc || (cnt == maxc && stride < maxs) { + maxs, maxc = stride, cnt + } + } + return maxs +} + +func (n trieNode) countSparseEntries() int { + stride := n.mostFrequentStride() + var count, v int + for _, t := range n.table[0x80 : 0x80+blockSize] { + tv := 0 + if t != nil { + tv = t.value + } + if tv-v != stride { + if tv != 0 { + count++ + } + } + v = tv + } + return count +} + +func (n *trieNode) insert(r rune, value uint16) { + var p [utf8.UTFMax]byte + sz := utf8.EncodeRune(p[:], r) + + for i := 0; i < sz; i++ { + if n.leaf { + log.Fatalf("triegen: insert: node (%#v) should not be a leaf", n) + } + nn := n.table[p[i]] + if nn == nil { + nn = newNode() + nn.b = p[i] + n.table[p[i]] = nn + } + n = nn + } + n.value = int(value) + n.leaf = true +} + +type nodeIndex struct { + lookupBlocks []*trieNode + valueBlocks []*trieNode + sparseBlocks []*trieNode + sparseOffset []uint16 + sparseCount int + + lookupBlockIdx map[uint32]int + valueBlockIdx map[uint32]int +} + +func newIndex() *nodeIndex { + index := &nodeIndex{} + index.lookupBlocks = make([]*trieNode, 0) + index.valueBlocks = make([]*trieNode, 0) + index.sparseBlocks = make([]*trieNode, 0) + index.sparseOffset = make([]uint16, 1) + index.lookupBlockIdx = make(map[uint32]int) + index.valueBlockIdx = make(map[uint32]int) + return index +} + +func computeOffsets(index *nodeIndex, n *trieNode) int { + if n.leaf { + return n.value + } + hasher := crc32.New(crc32.MakeTable(crc32.IEEE)) + // We only index continuation bytes. + for i := 0; i < blockSize; i++ { + v := 0 + if nn := n.table[0x80+i]; nn != nil { + v = computeOffsets(index, nn) + } + hasher.Write([]byte{uint8(v >> 8), uint8(v)}) + } + h := hasher.Sum32() + if n.isInternal() { + v, ok := index.lookupBlockIdx[h] + if !ok { + v = len(index.lookupBlocks) - blockOffset + index.lookupBlocks = append(index.lookupBlocks, n) + index.lookupBlockIdx[h] = v + } + n.value = v + } else { + v, ok := index.valueBlockIdx[h] + if !ok { + if c := n.countSparseEntries(); c > maxSparseEntries { + v = len(index.valueBlocks) - blockOffset + index.valueBlocks = append(index.valueBlocks, n) + index.valueBlockIdx[h] = v + } else { + v = -len(index.sparseOffset) + index.sparseBlocks = append(index.sparseBlocks, n) + index.sparseOffset = append(index.sparseOffset, uint16(index.sparseCount)) + index.sparseCount += c + 1 + index.valueBlockIdx[h] = v + } + } + n.value = v + } + return n.value +} + +func printValueBlock(nr int, n *trieNode, offset int) { + boff := nr * blockSize + fmt.Printf("\n// Block %#x, offset %#x", nr, boff) + var printnewline bool + for i := 0; i < blockSize; i++ { + if i%6 == 0 { + printnewline = true + } + v := 0 + if nn := n.table[i+offset]; nn != nil { + v = nn.value + } + if v != 0 { + if printnewline { + fmt.Printf("\n") + printnewline = false + } + fmt.Printf("%#04x:%#04x, ", boff+i, v) + } + } +} + +func printSparseBlock(nr int, n *trieNode) { + boff := -n.value + fmt.Printf("\n// Block %#x, offset %#x", nr, boff) + v := 0 + //stride := f(n) + stride := n.mostFrequentStride() + c := n.countSparseEntries() + fmt.Printf("\n{value:%#04x,lo:%#02x},", stride, uint8(c)) + for i, nn := range n.table[0x80 : 0x80+blockSize] { + nv := 0 + if nn != nil { + nv = nn.value + } + if nv-v != stride { + if v != 0 { + fmt.Printf(",hi:%#02x},", 0x80+i-1) + } + if nv != 0 { + fmt.Printf("\n{value:%#04x,lo:%#02x", nv, nn.b) + } + } + v = nv + } + if v != 0 { + fmt.Printf(",hi:%#02x},", 0x80+blockSize-1) + } +} + +func printLookupBlock(nr int, n *trieNode, offset, cutoff int) { + boff := nr * blockSize + fmt.Printf("\n// Block %#x, offset %#x", nr, boff) + var printnewline bool + for i := 0; i < blockSize; i++ { + if i%8 == 0 { + printnewline = true + } + v := 0 + if nn := n.table[i+offset]; nn != nil { + v = nn.value + } + if v != 0 { + if v < 0 { + v = -v - 1 + cutoff + } + if printnewline { + fmt.Printf("\n") + printnewline = false + } + fmt.Printf("%#03x:%#02x, ", boff+i, v) + } + } +} + +// printTables returns the size in bytes of the generated tables. +func (t *trieNode) printTables(name string) int { + index := newIndex() + // Values for 7-bit ASCII are stored in first two block, followed by nil block. + index.valueBlocks = append(index.valueBlocks, nil, nil, nil) + // First byte of multi-byte UTF-8 codepoints are indexed in 4th block. + index.lookupBlocks = append(index.lookupBlocks, nil, nil, nil, nil) + // Index starter bytes of multi-byte UTF-8. + for i := 0xC0; i < 0x100; i++ { + if t.table[i] != nil { + computeOffsets(index, t.table[i]) + } + } + + nv := len(index.valueBlocks) * blockSize + fmt.Printf("// %sValues: %d entries, %d bytes\n", name, nv, nv*2) + fmt.Printf("// Block 2 is the null block.\n") + fmt.Printf("var %sValues = [%d]uint16 {", name, nv) + printValueBlock(0, t, 0) + printValueBlock(1, t, 64) + printValueBlock(2, newNode(), 0) + for i := 3; i < len(index.valueBlocks); i++ { + printValueBlock(i, index.valueBlocks[i], 0x80) + } + fmt.Print("\n}\n\n") + + ls := len(index.sparseBlocks) + fmt.Printf("// %sSparseOffset: %d entries, %d bytes\n", name, ls, ls*2) + fmt.Printf("var %sSparseOffset = %#v\n\n", name, index.sparseOffset[1:]) + + ns := index.sparseCount + fmt.Printf("// %sSparseValues: %d entries, %d bytes\n", name, ns, ns*4) + fmt.Printf("var %sSparseValues = [%d]valueRange {", name, ns) + for i, n := range index.sparseBlocks { + printSparseBlock(i, n) + } + fmt.Print("\n}\n\n") + + cutoff := len(index.valueBlocks) - blockOffset + ni := len(index.lookupBlocks) * blockSize + fmt.Printf("// %sLookup: %d bytes\n", name, ni) + fmt.Printf("// Block 0 is the null block.\n") + fmt.Printf("var %sLookup = [%d]uint8 {", name, ni) + printLookupBlock(0, newNode(), 0, cutoff) + printLookupBlock(1, newNode(), 0, cutoff) + printLookupBlock(2, newNode(), 0, cutoff) + printLookupBlock(3, t, 0xC0, cutoff) + for i := 4; i < len(index.lookupBlocks); i++ { + printLookupBlock(i, index.lookupBlocks[i], 0x80, cutoff) + } + fmt.Print("\n}\n\n") + fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:], %sSparseValues[:], %sSparseOffset[:], %d}\n\n", + name, name, name, name, name, cutoff) + return nv*2 + ns*4 + ni + ls*2 +} diff --git a/src/pkg/expvar/expvar_test.go b/src/pkg/expvar/expvar_test.go index bbd9dd8d6..572c62bee 100644 --- a/src/pkg/expvar/expvar_test.go +++ b/src/pkg/expvar/expvar_test.go @@ -18,6 +18,7 @@ func RemoveAll() { } func TestInt(t *testing.T) { + RemoveAll() reqs := NewInt("requests") if reqs.i != 0 { t.Errorf("reqs.i = %v, want 0", reqs.i) @@ -43,6 +44,7 @@ func TestInt(t *testing.T) { } func TestFloat(t *testing.T) { + RemoveAll() reqs := NewFloat("requests-float") if reqs.f != 0.0 { t.Errorf("reqs.f = %v, want 0", reqs.f) @@ -68,6 +70,7 @@ func TestFloat(t *testing.T) { } func TestString(t *testing.T) { + RemoveAll() name := NewString("my-name") if name.s != "" { t.Errorf("name.s = %q, want \"\"", name.s) @@ -84,6 +87,7 @@ func TestString(t *testing.T) { } func TestMapCounter(t *testing.T) { + RemoveAll() colors := NewMap("bike-shed-colors") colors.Add("red", 1) @@ -123,6 +127,7 @@ func TestMapCounter(t *testing.T) { } func TestFunc(t *testing.T) { + RemoveAll() var x interface{} = []string{"a", "b"} f := Func(func() interface{} { return x }) if s, exp := f.String(), `["a","b"]`; s != exp { diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index 5444ad141..85dd8c3b3 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -33,7 +33,7 @@ After parsing, the arguments after the flag are available as the slice flag.Args() or individually as flag.Arg(i). - The arguments are indexed from 0 up to flag.NArg(). + The arguments are indexed from 0 through flag.NArg()-1. Command line flag syntax: -flag @@ -91,6 +91,15 @@ func (b *boolValue) Set(s string) error { func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } +func (b *boolValue) IsBoolFlag() bool { return true } + +// optional interface to indicate boolean flags that can be +// supplied without "=value" text +type boolFlag interface { + Value + IsBoolFlag() bool +} + // -- int Value type intValue int @@ -204,6 +213,10 @@ func (d *durationValue) String() string { return (*time.Duration)(d).String() } // Value is the interface to the dynamic value stored in a flag. // (The default value is represented as a string.) +// +// If a Value has an IsBoolFlag() bool method returning true, +// the command-line parser makes -name equivalent to -name=true +// rather than using the next command-line argument. type Value interface { String() string Set(string) error @@ -704,10 +717,10 @@ func (f *FlagSet) parseOne() (bool, error) { } return false, f.failf("flag provided but not defined: -%s", name) } - if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg + if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg if has_value { if err := fv.Set(value); err != nil { - f.failf("invalid boolean value %q for -%s: %v", value, name, err) + return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err) } } else { fv.Set("true") diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go index a9561f269..ddd54b277 100644 --- a/src/pkg/flag/flag_test.go +++ b/src/pkg/flag/flag_test.go @@ -15,17 +15,6 @@ import ( "time" ) -var ( - test_bool = Bool("test_bool", false, "bool value") - test_int = Int("test_int", 0, "int value") - test_int64 = Int64("test_int64", 0, "int64 value") - test_uint = Uint("test_uint", 0, "uint value") - test_uint64 = Uint64("test_uint64", 0, "uint64 value") - test_string = String("test_string", "0", "string value") - test_float64 = Float64("test_float64", 0, "float64 value") - test_duration = Duration("test_duration", 0, "time.Duration value") -) - func boolString(s string) string { if s == "0" { return "false" @@ -34,6 +23,16 @@ func boolString(s string) string { } func TestEverything(t *testing.T) { + ResetForTesting(nil) + Bool("test_bool", false, "bool value") + Int("test_int", 0, "int value") + Int64("test_int64", 0, "int64 value") + Uint("test_uint", 0, "uint value") + Uint64("test_uint64", 0, "uint64 value") + String("test_string", "0", "string value") + Float64("test_float64", 0, "float64 value") + Duration("test_duration", 0, "time.Duration value") + m := make(map[string]*Flag) desired := "0" visitor := func(f *Flag) { @@ -208,6 +207,47 @@ func TestUserDefined(t *testing.T) { } } +// Declare a user-defined boolean flag type. +type boolFlagVar struct { + count int +} + +func (b *boolFlagVar) String() string { + return fmt.Sprintf("%d", b.count) +} + +func (b *boolFlagVar) Set(value string) error { + if value == "true" { + b.count++ + } + return nil +} + +func (b *boolFlagVar) IsBoolFlag() bool { + return b.count < 4 +} + +func TestUserDefinedBool(t *testing.T) { + var flags FlagSet + flags.Init("test", ContinueOnError) + var b boolFlagVar + var err error + flags.Var(&b, "b", "usage") + if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil { + if b.count < 4 { + t.Error(err) + } + } + + if b.count != 4 { + t.Errorf("want: %d; got: %d", 4, b.count) + } + + if err == nil { + t.Error("expected error; got none") + } +} + func TestSetOutput(t *testing.T) { var flags FlagSet var buf bytes.Buffer diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go index a9b9c9d0c..3cd02d7ed 100644 --- a/src/pkg/fmt/doc.go +++ b/src/pkg/fmt/doc.go @@ -31,8 +31,8 @@ %X base 16, with upper-case letters for A-F %U Unicode format: U+1234; same as "U+%04X" Floating-point and complex constituents: - %b decimalless scientific notation with exponent a power of two, - in the manner of strconv.FormatFloat with the 'b' format, + %b decimalless scientific notation with exponent a power of two, + in the manner of strconv.FormatFloat with the 'b' format, e.g. -123456p-78 %e scientific notation, e.g. -1234.456e+78 %E scientific notation, e.g. -1234.456E+78 @@ -56,13 +56,17 @@ character '*', causing their values to be obtained from the next operand, which must be of type int. - For numeric values, width sets the width of the field and precision - sets the number of places after the decimal, if appropriate. For - example, the format %6.2f prints 123.45. + For numeric values, width sets the minimum width of the field and + precision sets the number of places after the decimal, if appropriate, + except that for %g/%G it sets the total number of digits. For example, + given 123.45 the format %6.2f prints 123.45 while %.4g prints 123.5. + The default precision for %e and %f is 6; for %g it is the smallest + number of digits necessary to identify the value uniquely. - For strings, width is the minimum number of characters to output, - padding with spaces if necessary, and precision is the maximum - number of characters to output, truncating if necessary. + For most values, width is the minimum number of characters to output, + padding the formatted form with spaces if necessary. + For strings, precision is the maximum number of characters to output, + truncating if necessary. Other flags: + always print a sign for numeric values; @@ -70,11 +74,17 @@ - pad with spaces on the right rather than the left (left-justify the field) # alternate format: add leading 0 for octal (%#o), 0x for hex (%#x); 0X for hex (%#X); suppress 0x for %p (%#p); - print a raw (backquoted) string if possible for %q (%#q); + for %q, print a raw (backquoted) string if strconv.CanBackquote + returns true; write e.g. U+0078 'x' if the character is printable for %U (%#U). ' ' (space) leave a space for elided sign in numbers (% d); put spaces between bytes printing strings or slices in hex (% x, % X) - 0 pad with leading zeros rather than spaces + 0 pad with leading zeros rather than spaces; + for numbers, this moves the padding after the sign + + Flags are ignored by verbs that do not expect them. + For example there is no alternate decimal format, so %#d and %d + behave identically. For each Printf-like function, there is also a Print function that takes no format and is equivalent to saying %v for every @@ -152,6 +162,7 @@ %T is not implemented %e %E %f %F %g %G are all equivalent and scan any floating point or complex value %s and %v on strings scan a space-delimited token + Flags # and + are not implemented. The familiar base-setting prefixes 0 (octal) and 0x (hexadecimal) are accepted when scanning integers without a diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index de0342967..af4b5c8f8 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -9,7 +9,6 @@ import ( . "fmt" "io" "math" - "runtime" // for the malloc count test only "strings" "testing" "time" @@ -96,7 +95,7 @@ type SI struct { I interface{} } -// A type with a String method with pointer receiver for testing %p +// P is a type with a String method with pointer receiver for testing %p. type P int var pValue P @@ -105,6 +104,9 @@ func (p *P) String() string { return "String(p)" } +var barray = [5]renamedUint8{1, 2, 3, 4, 5} +var bslice = barray[:] + var b byte var fmttests = []struct { @@ -127,6 +129,10 @@ var fmttests = []struct { {"%s", []byte("abc"), "abc"}, {"%x", []byte("abc"), "616263"}, {"% x", []byte("abc\xff"), "61 62 63 ff"}, + {"%#x", []byte("abc\xff"), "0x610x620x630xff"}, + {"%#X", []byte("abc\xff"), "0X610X620X630XFF"}, + {"%# x", []byte("abc\xff"), "0x61 0x62 0x63 0xff"}, + {"%# X", []byte("abc\xff"), "0X61 0X62 0X63 0XFF"}, {"% X", []byte("abc\xff"), "61 62 63 FF"}, {"%x", []byte("xyz"), "78797a"}, {"%X", []byte("xyz"), "78797A"}, @@ -172,6 +178,8 @@ var fmttests = []struct { {"%.3q", "日本語日本語", `"日本語"`}, {"%.3q", []byte("日本語日本語"), `"日本語"`}, {"%10.1q", "日本語日本語", ` "日"`}, + {"%10v", nil, " "}, + {"%-10v", nil, " "}, // integers {"%d", 12345, "12345"}, @@ -328,14 +336,18 @@ var fmttests = []struct { // arrays {"%v", array, "[1 2 3 4 5]"}, {"%v", iarray, "[1 hello 2.5 ]"}, + {"%v", barray, "[1 2 3 4 5]"}, {"%v", &array, "&[1 2 3 4 5]"}, {"%v", &iarray, "&[1 hello 2.5 ]"}, + {"%v", &barray, "&[1 2 3 4 5]"}, // slices {"%v", slice, "[1 2 3 4 5]"}, {"%v", islice, "[1 hello 2.5 ]"}, + {"%v", bslice, "[1 2 3 4 5]"}, {"%v", &slice, "&[1 2 3 4 5]"}, {"%v", &islice, "&[1 hello 2.5 ]"}, + {"%v", &bslice, "&[1 2 3 4 5]"}, // complexes with %v {"%v", 1 + 2i, "(1+2i)"}, @@ -350,10 +362,12 @@ var fmttests = []struct { {"%+v", B{1, 2}, `{I:<1> j:2}`}, {"%+v", C{1, B{2, 3}}, `{i:1 B:{I:<2> j:3}}`}, - // q on Stringable items + // other formats on Stringable items {"%s", I(23), `<23>`}, {"%q", I(23), `"<23>"`}, {"%x", I(23), `3c32333e`}, + {"%#x", I(23), `0x3c0x320x330x3e`}, + {"%# x", I(23), `0x3c 0x32 0x33 0x3e`}, {"%d", I(23), `23`}, // Stringer applies only to string formats. // go syntax @@ -375,6 +389,9 @@ var fmttests = []struct { {"%#v", &iarray, `&[4]interface {}{1, "hello", 2.5, interface {}(nil)}`}, {"%#v", map[int]byte(nil), `map[int]uint8(nil)`}, {"%#v", map[int]byte{}, `map[int]uint8{}`}, + {"%#v", "foo", `"foo"`}, + {"%#v", barray, `[5]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`}, + {"%#v", bslice, `[]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`}, // slices with other formats {"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, @@ -400,6 +417,9 @@ var fmttests = []struct { {"%x", renamedString("thing"), "7468696e67"}, {"%d", renamedBytes([]byte{1, 2, 15}), `[1 2 15]`}, {"%q", renamedBytes([]byte("hello")), `"hello"`}, + {"%x", []renamedUint8{'a', 'b', 'c'}, "616263"}, + {"%s", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, "hello"}, + {"%q", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, `"hello"`}, {"%v", renamedFloat32(22), "22"}, {"%v", renamedFloat64(33), "33"}, {"%v", renamedComplex64(3 + 4i), "(3+4i)"}, @@ -419,6 +439,8 @@ var fmttests = []struct { {"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"}, {"%T", intVal, "int"}, {"%6T", &intVal, " *int"}, + {"%10T", nil, " "}, + {"%-10T", nil, " "}, // %p {"p0=%p", new(int), "p0=0xPTR"}, @@ -441,6 +463,11 @@ var fmttests = []struct { {"%v", (*int)(nil), ""}, {"%v", new(int), "0xPTR"}, + // %d etc. pointers use specified base. + {"%d", new(int), "PTR_d"}, + {"%o", new(int), "PTR_o"}, + {"%x", new(int), "PTR_x"}, + // %d on Stringer should give integer if possible {"%s", time.Time{}.Month(), "January"}, {"%d", time.Time{}.Month(), "1"}, @@ -464,20 +491,37 @@ var fmttests = []struct { // Used to crash because nByte didn't allow for a sign. {"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"}, + + // Complex fmt used to leave the plus flag set for future entries in the array + // causing +2+0i and +3+0i instead of 2+0i and 3+0i. + {"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"}, + {"%v", []complex128{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"}, } func TestSprintf(t *testing.T) { for _, tt := range fmttests { s := Sprintf(tt.fmt, tt.val) if i := strings.Index(tt.out, "PTR"); i >= 0 { + pattern := "PTR" + chars := "0123456789abcdefABCDEF" + switch { + case strings.HasPrefix(tt.out[i:], "PTR_d"): + pattern = "PTR_d" + chars = chars[:10] + case strings.HasPrefix(tt.out[i:], "PTR_o"): + pattern = "PTR_o" + chars = chars[:8] + case strings.HasPrefix(tt.out[i:], "PTR_x"): + pattern = "PTR_x" + } j := i for ; j < len(s); j++ { c := s[j] - if (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F') { + if !strings.ContainsRune(chars, rune(c)) { break } } - s = s[0:i] + "PTR" + s[j:] + s = s[0:i] + pattern + s[j:] } if s != tt.out { if _, ok := tt.val.(string); ok { @@ -527,6 +571,14 @@ func BenchmarkSprintfFloat(b *testing.B) { } } +func BenchmarkManyArgs(b *testing.B) { + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world") + } +} + var mallocBuf bytes.Buffer var mallocTest = []struct { @@ -550,17 +602,9 @@ var _ bytes.Buffer func TestCountMallocs(t *testing.T) { for _, mt := range mallocTest { - const N = 100 - memstats := new(runtime.MemStats) - runtime.ReadMemStats(memstats) - mallocs := 0 - memstats.Mallocs - for i := 0; i < N; i++ { - mt.fn() - } - runtime.ReadMemStats(memstats) - mallocs += memstats.Mallocs - if mallocs/N > uint64(mt.count) { - t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N) + mallocs := testing.AllocsPerRun(100, mt.fn) + if got, max := mallocs, float64(mt.count); got > max { + t.Errorf("%s: got %v allocs, want <=%v", mt.desc, got, max) } } } @@ -636,7 +680,8 @@ func TestStructPrinter(t *testing.T) { } } -// Check map printing using substrings so we don't depend on the print order. +// presentInMap checks map printing using substrings so we don't depend on the +// print order. func presentInMap(s string, a []string, t *testing.T) { for i := 0; i < len(a); i++ { loc := strings.Index(s, a[i]) @@ -677,8 +722,8 @@ func TestEmptyMap(t *testing.T) { } } -// Check that Sprint (and hence Print, Fprint) puts spaces in the right places, -// that is, between arg pairs in which neither is a string. +// TestBlank checks that Sprint (and hence Print, Fprint) puts spaces in the +// right places, that is, between arg pairs in which neither is a string. func TestBlank(t *testing.T) { got := Sprint("<", 1, ">:", 1, 2, 3, "!") expect := "<1>:1 2 3!" @@ -687,8 +732,8 @@ func TestBlank(t *testing.T) { } } -// Check that Sprintln (and hence Println, Fprintln) puts spaces in the right places, -// that is, between all arg pairs. +// TestBlankln checks that Sprintln (and hence Println, Fprintln) puts spaces in +// the right places, that is, between all arg pairs. func TestBlankln(t *testing.T) { got := Sprintln("<", 1, ">:", 1, 2, 3, "!") expect := "< 1 >: 1 2 3 !\n" @@ -697,7 +742,7 @@ func TestBlankln(t *testing.T) { } } -// Check Formatter with Sprint, Sprintln, Sprintf +// TestFormatterPrintln checks Formatter with Sprint, Sprintln, Sprintf. func TestFormatterPrintln(t *testing.T) { f := F(1) expect := "\n" @@ -746,7 +791,7 @@ func TestWidthAndPrecision(t *testing.T) { } } -// A type that panics in String. +// Panic is a type that panics in String. type Panic struct { message interface{} } @@ -761,7 +806,7 @@ func (p Panic) String() string { panic(p.message) } -// A type that panics in Format. +// PanicF is a type that panics in Format. type PanicF struct { message interface{} } @@ -799,7 +844,7 @@ func TestPanics(t *testing.T) { } } -// Test that erroneous String routine doesn't cause fatal recursion. +// recurCount tests that erroneous String routine doesn't cause fatal recursion. var recurCount = 0 type Recur struct { @@ -842,3 +887,15 @@ func TestIsSpace(t *testing.T) { } } } + +func TestNilDoesNotBecomeTyped(t *testing.T) { + type A struct{} + type B struct{} + var a *A = nil + var b B = B{} + got := Sprintf("%s %s %s %s %s", nil, a, nil, b, nil) + const expect = "%!s() %!s(*fmt_test.A=) %!s() {} %!s()" + if got != expect { + t.Errorf("expected:\n\t%q\ngot:\n\t%q", expect, got) + } +} diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go index caf900d5c..5665db12c 100644 --- a/src/pkg/fmt/format.go +++ b/src/pkg/fmt/format.go @@ -72,7 +72,7 @@ func (f *fmt) init(buf *buffer) { f.clearflags() } -// Compute left and right padding widths (only one will be non-zero). +// computePadding computes left and right padding widths (only one will be non-zero). func (f *fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth int) { left := !f.minus w := f.wid @@ -95,7 +95,7 @@ func (f *fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth i return } -// Generate n bytes of padding. +// writePadding generates n bytes of padding. func (f *fmt) writePadding(n int, padding []byte) { for n > 0 { m := n @@ -107,14 +107,13 @@ func (f *fmt) writePadding(n int, padding []byte) { } } -// Append b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus) -// clear flags afterwards. +// pad appends b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus). func (f *fmt) pad(b []byte) { - var padding []byte - var left, right int - if f.widPresent && f.wid != 0 { - padding, left, right = f.computePadding(len(b)) + if !f.widPresent || f.wid == 0 { + f.buf.Write(b) + return } + padding, left, right := f.computePadding(len(b)) if left > 0 { f.writePadding(left, padding) } @@ -124,14 +123,13 @@ func (f *fmt) pad(b []byte) { } } -// append s to buf, padded on left (w > 0) or right (w < 0 or f.minus). -// clear flags afterwards. +// padString appends s to buf, padded on left (w > 0) or right (w < 0 or f.minus). func (f *fmt) padString(s string) { - var padding []byte - var left, right int - if f.widPresent && f.wid != 0 { - padding, left, right = f.computePadding(utf8.RuneCountInString(s)) + if !f.widPresent || f.wid == 0 { + f.buf.WriteString(s) + return } + padding, left, right := f.computePadding(utf8.RuneCountInString(s)) if left > 0 { f.writePadding(left, padding) } @@ -141,17 +139,6 @@ func (f *fmt) padString(s string) { } } -func putint(buf []byte, base, val uint64, digits string) int { - i := len(buf) - 1 - for val >= base { - buf[i] = digits[val%base] - i-- - val /= base - } - buf[i] = digits[val] - return i - 1 -} - var ( trueBytes = []byte("true") falseBytes = []byte("false") @@ -285,18 +272,41 @@ func (f *fmt) fmt_s(s string) { f.padString(s) } -// fmt_sx formats a string as a hexadecimal encoding of its bytes. -func (f *fmt) fmt_sx(s, digits string) { +// fmt_sbx formats a string or byte slice as a hexadecimal encoding of its bytes. +func (f *fmt) fmt_sbx(s string, b []byte, digits string) { + n := len(b) + if b == nil { + n = len(s) + } + x := digits[10] - 'a' + 'x' // TODO: Avoid buffer by pre-padding. - var b []byte - for i := 0; i < len(s); i++ { + var buf []byte + for i := 0; i < n; i++ { if i > 0 && f.space { - b = append(b, ' ') + buf = append(buf, ' ') } - v := s[i] - b = append(b, digits[v>>4], digits[v&0xF]) + if f.sharp { + buf = append(buf, '0', x) + } + var c byte + if b == nil { + c = s[i] + } else { + c = b[i] + } + buf = append(buf, digits[c>>4], digits[c&0xF]) } - f.pad(b) + f.pad(buf) +} + +// fmt_sx formats a string as a hexadecimal encoding of its bytes. +func (f *fmt) fmt_sx(s, digits string) { + f.fmt_sbx(s, nil, digits) +} + +// fmt_bx formats a byte slice as a hexadecimal encoding of its bytes. +func (f *fmt) fmt_bx(b []byte, digits string) { + f.fmt_sbx("", b, digits) } // fmt_q formats a string as a double-quoted, escaped Go string constant. @@ -373,7 +383,7 @@ func (f *fmt) fmt_f64(v float64) { f.formatFloat(v, 'f', doPrec(f, 6), 64) } // fmt_g64 formats a float64 in the 'f' or 'e' form according to size. func (f *fmt) fmt_g64(v float64) { f.formatFloat(v, 'g', doPrec(f, -1), 64) } -// fmt_g64 formats a float64 in the 'f' or 'E' form according to size. +// fmt_G64 formats a float64 in the 'f' or 'E' form according to size. func (f *fmt) fmt_G64(v float64) { f.formatFloat(v, 'G', doPrec(f, -1), 64) } // fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2). @@ -405,6 +415,7 @@ func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) } func (f *fmt) fmt_c64(v complex64, verb rune) { f.buf.WriteByte('(') r := real(v) + oldPlus := f.plus for i := 0; ; i++ { switch verb { case 'e': @@ -424,6 +435,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) { f.plus = true r = imag(v) } + f.plus = oldPlus f.buf.Write(irparenBytes) } @@ -431,6 +443,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) { func (f *fmt) fmt_c128(v complex128, verb rune) { f.buf.WriteByte('(') r := real(v) + oldPlus := f.plus for i := 0; ; i++ { switch verb { case 'e': @@ -450,5 +463,6 @@ func (f *fmt) fmt_c128(v complex128, verb rune) { f.plus = true r = imag(v) } + f.plus = oldPlus f.buf.Write(irparenBytes) } diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 13438243c..7d7aa93b2 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -26,8 +26,8 @@ var ( extraBytes = []byte("%!(EXTRA ") irparenBytes = []byte("i)") bytesBytes = []byte("[]byte{") - widthBytes = []byte("%!(BADWIDTH)") - precBytes = []byte("%!(BADPREC)") + badWidthBytes = []byte("%!(BADWIDTH)") + badPrecBytes = []byte("%!(BADPREC)") noVerbBytes = []byte("%!(NOVERB)") ) @@ -153,7 +153,7 @@ func newCache(f func() interface{}) *cache { var ppFree = newCache(func() interface{} { return new(pp) }) -// Allocate a new pp struct or grab a cached one. +// newPrinter allocates a new pp struct or grab a cached one. func newPrinter() *pp { p := ppFree.get().(*pp) p.panicking = false @@ -162,7 +162,7 @@ func newPrinter() *pp { return p } -// Save used pp structs in ppFree; avoids an allocation per invocation. +// free saves used pp structs in ppFree; avoids an allocation per invocation. func (p *pp) free() { // Don't hold on to pp structs with large buffers. if cap(p.buf) > 1024 { @@ -231,7 +231,7 @@ func Sprintf(format string, a ...interface{}) string { return s } -// Errorf formats according to a format specifier and returns the string +// Errorf formats according to a format specifier and returns the string // as a value that satisfies error. func Errorf(format string, a ...interface{}) error { return errors.New(Sprintf(format, a...)) @@ -299,7 +299,7 @@ func Sprintln(a ...interface{}) string { return s } -// Get the i'th arg of the struct value. +// getField gets the i'th arg of the struct value. // If the arg itself is an interface, return a value for // the thing inside the interface, not the interface itself. func getField(v reflect.Value, i int) reflect.Value { @@ -310,7 +310,7 @@ func getField(v reflect.Value, i int) reflect.Value { return val } -// Convert ASCII to integer. n is 0 (and got is false) if no number present. +// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present. func parsenum(s string, start, end int) (num int, isnum bool, newi int) { if start >= end { return 0, false, end @@ -545,10 +545,15 @@ func (p *pp) fmtString(v string, verb rune, goSyntax bool) { } } -func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) { +func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, typ reflect.Type, depth int) { if verb == 'v' || verb == 'd' { if goSyntax { - p.buf.Write(bytesBytes) + if typ == nil { + p.buf.Write(bytesBytes) + } else { + p.buf.WriteString(typ.String()) + p.buf.WriteByte('{') + } } else { p.buf.WriteByte('[') } @@ -569,24 +574,27 @@ func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) { } return } - s := string(v) switch verb { case 's': - p.fmt.fmt_s(s) + p.fmt.fmt_s(string(v)) case 'x': - p.fmt.fmt_sx(s, ldigits) + p.fmt.fmt_bx(v, ldigits) case 'X': - p.fmt.fmt_sx(s, udigits) + p.fmt.fmt_bx(v, udigits) case 'q': - p.fmt.fmt_q(s) + p.fmt.fmt_q(string(v)) default: p.badVerb(verb) } } func (p *pp) fmtPointer(value reflect.Value, verb rune, goSyntax bool) { + use0x64 := true switch verb { - case 'p', 'v', 'b', 'd', 'o', 'x', 'X': + case 'p', 'v': + // ok + case 'b', 'd', 'o', 'x', 'X': + use0x64 = false // ok default: p.badVerb(verb) @@ -616,7 +624,11 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune, goSyntax bool) { } else if verb == 'v' && u == 0 { p.buf.Write(nilAngleBytes) } else { - p.fmt0x64(uint64(u), !p.fmt.sharp) + if use0x64 { + p.fmt0x64(uint64(u), !p.fmt.sharp) + } else { + p.fmtUint64(uint64(u), verb, false) + } } } @@ -712,17 +724,18 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString } func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth int) (wasString bool) { + p.field = field + p.value = reflect.Value{} + if field == nil { if verb == 'T' || verb == 'v' { - p.buf.Write(nilAngleBytes) + p.fmt.pad(nilAngleBytes) } else { p.badVerb(verb) } return false } - p.field = field - p.value = reflect.Value{} // Special processing considerations. // %T (the value's type) and %p (its address) are special; we always do them first. switch verb { @@ -734,8 +747,17 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth return false } - if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled { - return wasString + // Clear flags for base formatters. + // handleMethods needs them, so we must restore them later. + // We could call handleMethods here and avoid this work, but + // handleMethods is expensive enough to be worth delaying. + oldPlus := p.fmt.plus + oldSharp := p.fmt.sharp + if plus { + p.fmt.plus = false + } + if goSyntax { + p.fmt.sharp = false } // Some types can be done without reflection. @@ -776,9 +798,16 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth p.fmtString(f, verb, goSyntax) wasString = verb == 's' || verb == 'v' case []byte: - p.fmtBytes(f, verb, goSyntax, depth) + p.fmtBytes(f, verb, goSyntax, nil, depth) wasString = verb == 's' default: + // Restore flags in case handleMethods finds a Formatter. + p.fmt.plus = oldPlus + p.fmt.sharp = oldSharp + // If the type is not simple, it might have methods. + if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled { + return wasString + } // Need to use reflection return p.printReflectValue(reflect.ValueOf(field), verb, plus, goSyntax, depth) } @@ -915,19 +944,22 @@ BigSwitch: } case reflect.Array, reflect.Slice: // Byte slices are special. - if f.Type().Elem().Kind() == reflect.Uint8 { - // We know it's a slice of bytes, but we also know it does not have static type - // []byte, or it would have been caught above. Therefore we cannot convert - // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have - // that type, and we can't write an expression of the right type and do a - // conversion because we don't have a static way to write the right type. - // So we build a slice by hand. This is a rare case but it would be nice - // if reflection could help a little more. - bytes := make([]byte, f.Len()) - for i := range bytes { - bytes[i] = byte(f.Index(i).Uint()) + if typ := f.Type(); typ.Elem().Kind() == reflect.Uint8 { + var bytes []byte + if f.Kind() == reflect.Slice { + bytes = f.Bytes() + } else if f.CanAddr() { + bytes = f.Slice(0, f.Len()).Bytes() + } else { + // We have an array, but we cannot Slice() a non-addressable array, + // so we build a slice by hand. This is a rare case but it would be nice + // if reflection could help a little more. + bytes = make([]byte, f.Len()) + for i := range bytes { + bytes[i] = byte(f.Index(i).Uint()) + } } - p.fmtBytes(bytes, verb, goSyntax, depth) + p.fmtBytes(bytes, verb, goSyntax, typ, depth) wasString = verb == 's' break } @@ -1033,7 +1065,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { if i < end && format[i] == '*' { p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum) if !p.fmt.widPresent { - p.buf.Write(widthBytes) + p.buf.Write(badWidthBytes) } } else { p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) @@ -1043,7 +1075,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { if format[i+1] == '*' { p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum) if !p.fmt.precPresent { - p.buf.Write(precBytes) + p.buf.Write(badPrecBytes) } } else { p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) @@ -1099,7 +1131,7 @@ func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { prevString := false for fieldnum := 0; fieldnum < len(a); fieldnum++ { p.fmt.clearflags() - // always add spaces if we're doing println + // always add spaces if we're doing Println field := a[fieldnum] if fieldnum > 0 { isString := field != nil && reflect.TypeOf(field).Kind() == reflect.String diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index 0b3e04069..bf888c4d8 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -33,8 +33,8 @@ type ScanState interface { ReadRune() (r rune, size int, err error) // UnreadRune causes the next call to ReadRune to return the same rune. UnreadRune() error - // SkipSpace skips space in the input. Newlines are treated as space - // unless the scan operation is Scanln, Fscanln or Sscanln, in which case + // SkipSpace skips space in the input. Newlines are treated as space + // unless the scan operation is Scanln, Fscanln or Sscanln, in which case // a newline is treated as EOF. SkipSpace() // Token skips space in the input if skipSpace is true, then returns the @@ -312,8 +312,9 @@ func notSpace(r rune) bool { return !isSpace(r) } -// skipSpace provides Scan() methods the ability to skip space and newline characters -// in keeping with the current scanning mode set by format strings and Scan()/Scanln(). +// SkipSpace provides Scan methods the ability to skip space and newline +// characters in keeping with the current scanning mode set by format strings +// and Scan/Scanln. func (s *ss) SkipSpace() { s.skipSpace(false) } @@ -337,7 +338,10 @@ func (r *readRune) readByte() (b byte, err error) { r.pending-- return } - _, err = r.reader.Read(r.pendBuf[0:1]) + n, err := io.ReadFull(r.reader, r.pendBuf[0:1]) + if n != 1 { + return 0, err + } return r.pendBuf[0], err } @@ -378,7 +382,7 @@ func (r *readRune) ReadRune() (rr rune, size int, err error) { var ssFree = newCache(func() interface{} { return new(ss) }) -// Allocate a new ss struct or grab a cached one. +// newScanState allocates a new ss struct or grab a cached one. func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { // If the reader is a *ss, then we've got a recursive // call to Scan, so re-use the scan state. @@ -410,7 +414,7 @@ func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { return } -// Save used ss structs in ssFree; avoid an allocation per invocation. +// free saves used ss structs in ssFree; avoid an allocation per invocation. func (s *ss) free(old ssave) { // If it was used recursively, just restore the old state. if old.validSave { @@ -1090,7 +1094,8 @@ func (s *ss) advance(format string) (i int) { // There was space in the format, so there should be space (EOF) // in the input. inputc := s.getRune() - if inputc == eof { + if inputc == eof || inputc == '\n' { + // If we've reached a newline, stop now; don't read ahead. return } if !isSpace(inputc) { diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index 320857b73..4e2c0feb2 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -626,7 +626,7 @@ func TestScanlnWithMiddleNewline(t *testing.T) { } } -// Special Reader that counts reads at end of file. +// eofCounter is a special Reader that counts reads at end of file. type eofCounter struct { reader *strings.Reader eofCount int @@ -640,8 +640,8 @@ func (ec *eofCounter) Read(b []byte) (n int, err error) { return } -// Verify that when we scan, we see at most EOF once per call to a Scan function, -// and then only when it's really an EOF +// TestEOF verifies that when we scan, we see at most EOF once per call to a +// Scan function, and then only when it's really an EOF. func TestEOF(t *testing.T) { ec := &eofCounter{strings.NewReader("123\n"), 0} var a int @@ -668,7 +668,7 @@ func TestEOF(t *testing.T) { } } -// Verify that we see an EOF error if we run out of input. +// TestEOFAtEndOfInput verifies that we see an EOF error if we run out of input. // This was a buglet: we used to get "expected integer". func TestEOFAtEndOfInput(t *testing.T) { var i, j int @@ -730,7 +730,8 @@ func TestEOFAllTypes(t *testing.T) { } } -// Verify that, at least when using bufio, successive calls to Fscan do not lose runes. +// TestUnreadRuneWithBufio verifies that, at least when using bufio, successive +// calls to Fscan do not lose runes. func TestUnreadRuneWithBufio(t *testing.T) { r := bufio.NewReader(strings.NewReader("123αb")) var i int @@ -753,7 +754,7 @@ func TestUnreadRuneWithBufio(t *testing.T) { type TwoLines string -// Attempt to read two lines into the object. Scanln should prevent this +// Scan attempts to read two lines into the object. Scanln should prevent this // because it stops at newline; Scan and Scanf should be fine. func (t *TwoLines) Scan(state ScanState, verb rune) error { chars := make([]rune, 0, 100) @@ -810,6 +811,34 @@ func TestMultiLine(t *testing.T) { } } +// simpleReader is a strings.Reader that implements only Read, not ReadRune. +// Good for testing readahead. +type simpleReader struct { + sr *strings.Reader +} + +func (s *simpleReader) Read(b []byte) (n int, err error) { + return s.sr.Read(b) +} + +// TestLineByLineFscanf tests that Fscanf does not read past newline. Issue +// 3481. +func TestLineByLineFscanf(t *testing.T) { + r := &simpleReader{strings.NewReader("1\n2\n")} + var i, j int + n, err := Fscanf(r, "%v\n", &i) + if n != 1 || err != nil { + t.Fatalf("first read: %d %q", n, err) + } + n, err = Fscanf(r, "%v\n", &j) + if n != 1 || err != nil { + t.Fatalf("second read: %d %q", n, err) + } + if i != 1 || j != 2 { + t.Errorf("wrong values; wanted 1 2 got %d %d", i, j) + } +} + // RecursiveInt accepts a string matching %d.%d.%d.... // and parses it into a linked list. // It allows us to benchmark recursive descent style scanners. @@ -835,7 +864,7 @@ func (r *RecursiveInt) Scan(state ScanState, verb rune) (err error) { return } -// Perform the same scanning task as RecursiveInt.Scan +// scanInts performs the same scanning task as RecursiveInt.Scan // but without recurring through scanner, so we can compare // performance more directly. func scanInts(r *RecursiveInt, b *bytes.Buffer) (err error) { diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go index d2e75dc1c..bf533d1d2 100644 --- a/src/pkg/go/ast/ast.go +++ b/src/pkg/go/ast/ast.go @@ -407,6 +407,7 @@ type ( // A ChanType node represents a channel type. ChanType struct { Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first) + Arrow token.Pos // position of "<-" (noPos if there is no "<-") Dir ChanDir // channel direction Value Expr // value type } @@ -554,7 +555,7 @@ type ( // A DeclStmt node represents a declaration in a statement list. DeclStmt struct { - Decl Decl + Decl Decl // *GenDecl with CONST, TYPE, or VAR token } // An EmptyStmt node represents an empty statement. diff --git a/src/pkg/go/ast/commentmap.go b/src/pkg/go/ast/commentmap.go new file mode 100644 index 000000000..252d460af --- /dev/null +++ b/src/pkg/go/ast/commentmap.go @@ -0,0 +1,332 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "fmt" + "go/token" + "sort" +) + +type byPos []*CommentGroup + +func (a byPos) Len() int { return len(a) } +func (a byPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() } +func (a byPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// sortComments sorts the list of comment groups in source order. +// +func sortComments(list []*CommentGroup) { + // TODO(gri): Does it make sense to check for sorted-ness + // first (because we know that sorted-ness is + // very likely)? + if orderedList := byPos(list); !sort.IsSorted(orderedList) { + sort.Sort(orderedList) + } +} + +// A CommentMap maps an AST node to a list of comment groups +// associated with it. See NewCommentMap for a description of +// the association. +// +type CommentMap map[Node][]*CommentGroup + +func (cmap CommentMap) addComment(n Node, c *CommentGroup) { + list := cmap[n] + if len(list) == 0 { + list = []*CommentGroup{c} + } else { + list = append(list, c) + } + cmap[n] = list +} + +type byInterval []Node + +func (a byInterval) Len() int { return len(a) } +func (a byInterval) Less(i, j int) bool { + pi, pj := a[i].Pos(), a[j].Pos() + return pi < pj || pi == pj && a[i].End() > a[j].End() +} +func (a byInterval) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// nodeList returns the list of nodes of the AST n in source order. +// +func nodeList(n Node) []Node { + var list []Node + Inspect(n, func(n Node) bool { + // don't collect comments + switch n.(type) { + case nil, *CommentGroup, *Comment: + return false + } + list = append(list, n) + return true + }) + // Note: The current implementation assumes that Inspect traverses the + // AST in depth-first and thus _source_ order. If AST traversal + // does not follow source order, the sorting call below will be + // required. + // sort.Sort(byInterval(list)) + return list +} + +// A commentListReader helps iterating through a list of comment groups. +// +type commentListReader struct { + fset *token.FileSet + list []*CommentGroup + index int + comment *CommentGroup // comment group at current index + pos, end token.Position // source interval of comment group at current index +} + +func (r *commentListReader) eol() bool { + return r.index >= len(r.list) +} + +func (r *commentListReader) next() { + if !r.eol() { + r.comment = r.list[r.index] + r.pos = r.fset.Position(r.comment.Pos()) + r.end = r.fset.Position(r.comment.End()) + r.index++ + } +} + +// A nodeStack keeps track of nested nodes. +// A node lower on the stack lexically contains the nodes higher on the stack. +// +type nodeStack []Node + +// push pops all nodes that appear lexically before n +// and then pushes n on the stack. +// +func (s *nodeStack) push(n Node) { + s.pop(n.Pos()) + *s = append((*s), n) +} + +// pop pops all nodes that appear lexically before pos +// (i.e., whose lexical extent has ended before or at pos). +// It returns the last node popped. +// +func (s *nodeStack) pop(pos token.Pos) (top Node) { + i := len(*s) + for i > 0 && (*s)[i-1].End() <= pos { + top = (*s)[i-1] + i-- + } + *s = (*s)[0:i] + return top +} + +// NewCommentMap creates a new comment map by associating comment groups +// of the comments list with the nodes of the AST specified by node. +// +// A comment group g is associated with a node n if: +// +// - g starts on the same line as n ends +// - g starts on the line immediately following n, and there is +// at least one empty line after g and before the next node +// - g starts before n and is not associated to the node before n +// via the previous rules +// +// NewCommentMap tries to associate a comment group to the "largest" +// node possible: For instance, if the comment is a line comment +// trailing an assignment, the comment is associated with the entire +// assignment rather than just the last operand in the assignment. +// +func NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) CommentMap { + if len(comments) == 0 { + return nil // no comments to map + } + + cmap := make(CommentMap) + + // set up comment reader r + tmp := make([]*CommentGroup, len(comments)) + copy(tmp, comments) // don't change incomming comments + sortComments(tmp) + r := commentListReader{fset: fset, list: tmp} // !r.eol() because len(comments) > 0 + r.next() + + // create node list in lexical order + nodes := nodeList(node) + nodes = append(nodes, nil) // append sentinel + + // set up iteration variables + var ( + p Node // previous node + pend token.Position // end of p + pg Node // previous node group (enclosing nodes of "importance") + pgend token.Position // end of pg + stack nodeStack // stack of node groups + ) + + for _, q := range nodes { + var qpos token.Position + if q != nil { + qpos = fset.Position(q.Pos()) // current node position + } else { + // set fake sentinel position to infinity so that + // all comments get processed before the sentinel + const infinity = 1 << 30 + qpos.Offset = infinity + qpos.Line = infinity + } + + // process comments before current node + for r.end.Offset <= qpos.Offset { + // determine recent node group + if top := stack.pop(r.comment.Pos()); top != nil { + pg = top + pgend = fset.Position(pg.End()) + } + // Try to associate a comment first with a node group + // (i.e., a node of "importance" such as a declaration); + // if that fails, try to associate it with the most recent + // node. + // TODO(gri) try to simplify the logic below + var assoc Node + switch { + case pg != nil && + (pgend.Line == r.pos.Line || + pgend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line): + // 1) comment starts on same line as previous node group ends, or + // 2) comment starts on the line immediately after the + // previous node group and there is an empty line before + // the current node + // => associate comment with previous node group + assoc = pg + case p != nil && + (pend.Line == r.pos.Line || + pend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line || + q == nil): + // same rules apply as above for p rather than pg, + // but also associate with p if we are at the end (q == nil) + assoc = p + default: + // otherwise, associate comment with current node + if q == nil { + // we can only reach here if there was no p + // which would imply that there were no nodes + panic("internal error: no comments should be associated with sentinel") + } + assoc = q + } + cmap.addComment(assoc, r.comment) + if r.eol() { + return cmap + } + r.next() + } + + // update previous node + p = q + pend = fset.Position(p.End()) + + // update previous node group if we see an "important" node + switch q.(type) { + case *File, *Field, Decl, Spec, Stmt: + stack.push(q) + } + } + + return cmap +} + +// Update replaces an old node in the comment map with the new node +// and returns the new node. Comments that were associated with the +// old node are associated with the new node. +// +func (cmap CommentMap) Update(old, new Node) Node { + if list := cmap[old]; len(list) > 0 { + delete(cmap, old) + cmap[new] = append(cmap[new], list...) + } + return new +} + +// Filter returns a new comment map consisting of only those +// entries of cmap for which a corresponding node exists in +// the AST specified by node. +// +func (cmap CommentMap) Filter(node Node) CommentMap { + umap := make(CommentMap) + Inspect(node, func(n Node) bool { + if g := cmap[n]; len(g) > 0 { + umap[n] = g + } + return true + }) + return umap +} + +// Comments returns the list of comment groups in the comment map. +// The result is sorted is source order. +// +func (cmap CommentMap) Comments() []*CommentGroup { + list := make([]*CommentGroup, 0, len(cmap)) + for _, e := range cmap { + list = append(list, e...) + } + sortComments(list) + return list +} + +func summary(list []*CommentGroup) string { + const maxLen = 40 + var buf bytes.Buffer + + // collect comments text +loop: + for _, group := range list { + // Note: CommentGroup.Text() does too much work for what we + // need and would only replace this innermost loop. + // Just do it explicitly. + for _, comment := range group.List { + if buf.Len() >= maxLen { + break loop + } + buf.WriteString(comment.Text) + } + } + + // truncate if too long + if buf.Len() > maxLen { + buf.Truncate(maxLen - 3) + buf.WriteString("...") + } + + // replace any invisibles with blanks + bytes := buf.Bytes() + for i, b := range bytes { + switch b { + case '\t', '\n', '\r': + bytes[i] = ' ' + } + } + + return string(bytes) +} + +func (cmap CommentMap) String() string { + var buf bytes.Buffer + fmt.Fprintln(&buf, "CommentMap {") + for node, comment := range cmap { + // print name of identifiers; print node type for other nodes + var s string + if ident, ok := node.(*Ident); ok { + s = ident.Name + } else { + s = fmt.Sprintf("%T", node) + } + fmt.Fprintf(&buf, "\t%p %20s: %s\n", node, s, summary(comment)) + } + fmt.Fprintln(&buf, "}") + return buf.String() +} diff --git a/src/pkg/go/ast/commentmap_test.go b/src/pkg/go/ast/commentmap_test.go new file mode 100644 index 000000000..e372eab74 --- /dev/null +++ b/src/pkg/go/ast/commentmap_test.go @@ -0,0 +1,143 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// To avoid a cyclic dependency with go/parser, this file is in a separate package. + +package ast_test + +import ( + "bytes" + "fmt" + . "go/ast" + "go/parser" + "go/token" + "sort" + "testing" +) + +const src = ` +// the very first comment + +// package p +package p /* the name is p */ + +// imports +import ( + "bytes" // bytes + "fmt" // fmt + "go/ast" + "go/parser" +) + +// T +type T struct { + a, b, c int // associated with a, b, c + // associated with x, y + x, y float64 // float values + z complex128 // complex value +} +// also associated with T + +// x +var x = 0 // x = 0 +// also associated with x + +// f1 +func f1() { + /* associated with s1 */ + s1() + // also associated with s1 + + // associated with s2 + + // also associated with s2 + s2() // line comment for s2 +} +// associated with f1 +// also associated with f1 + +// associated with f2 + +// f2 +func f2() { +} + +func f3() { + i := 1 /* 1 */ + 2 // addition + _ = i +} + +// the very last comment +` + +// res maps a key of the form "line number: node type" +// to the associated comments' text. +// +var res = map[string]string{ + " 5: *ast.File": "the very first comment\npackage p\n", + " 5: *ast.Ident": " the name is p\n", + " 8: *ast.GenDecl": "imports\n", + " 9: *ast.ImportSpec": "bytes\n", + "10: *ast.ImportSpec": "fmt\n", + "16: *ast.GenDecl": "T\nalso associated with T\n", + "17: *ast.Field": "associated with a, b, c\n", + "19: *ast.Field": "associated with x, y\nfloat values\n", + "20: *ast.Field": "complex value\n", + "25: *ast.GenDecl": "x\nx = 0\nalso associated with x\n", + "29: *ast.FuncDecl": "f1\nassociated with f1\nalso associated with f1\n", + "31: *ast.ExprStmt": " associated with s1\nalso associated with s1\n", + "37: *ast.ExprStmt": "associated with s2\nalso associated with s2\nline comment for s2\n", + "45: *ast.FuncDecl": "associated with f2\nf2\n", + "49: *ast.AssignStmt": "addition\n", + "49: *ast.BasicLit": " 1\n", + "50: *ast.Ident": "the very last comment\n", +} + +func ctext(list []*CommentGroup) string { + var buf bytes.Buffer + for _, g := range list { + buf.WriteString(g.Text()) + } + return buf.String() +} + +func TestCommentMap(t *testing.T) { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + if err != nil { + t.Fatal(err) + } + cmap := NewCommentMap(fset, f, f.Comments) + + // very correct association of comments + for n, list := range cmap { + key := fmt.Sprintf("%2d: %T", fset.Position(n.Pos()).Line, n) + got := ctext(list) + want := res[key] + if got != want { + t.Errorf("%s: got %q; want %q", key, got, want) + } + } + + // verify that no comments got lost + if n := len(cmap.Comments()); n != len(f.Comments) { + t.Errorf("got %d comment groups in map; want %d", n, len(f.Comments)) + } + + // support code to update test: + // set genMap to true to generate res map + const genMap = false + if genMap { + out := make([]string, 0, len(cmap)) + for n, list := range cmap { + out = append(out, fmt.Sprintf("\t\"%2d: %T\":\t%q,", fset.Position(n.Pos()).Line, n, ctext(list))) + } + sort.Strings(out) + for _, s := range out { + fmt.Println(s) + } + } +} + +// TODO(gri): add tests for Filter. diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go index 4a89b8909..4db5814cb 100644 --- a/src/pkg/go/ast/filter.go +++ b/src/pkg/go/ast/filter.go @@ -414,7 +414,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File { if path := imp.Path.Value; !seen[path] { // TODO: consider handling cases where: // - 2 imports exist with the same import path but - // have different local names (one should probably + // have different local names (one should probably // keep both of them) // - 2 imports exist but only one has a comment // - 2 imports exist and they both have (possibly diff --git a/src/pkg/go/ast/import.go b/src/pkg/go/ast/import.go index 2d4f69aae..a68a4840f 100644 --- a/src/pkg/go/ast/import.go +++ b/src/pkg/go/ast/import.go @@ -20,7 +20,7 @@ func SortImports(fset *token.FileSet, f *File) { break } - if d.Lparen == token.NoPos { + if !d.Lparen.IsValid() { // Not a block: sorted by default. continue } diff --git a/src/pkg/go/ast/print.go b/src/pkg/go/ast/print.go index 02cf9e022..4a1ce480f 100644 --- a/src/pkg/go/ast/print.go +++ b/src/pkg/go/ast/print.go @@ -34,7 +34,8 @@ func NotNilFilter(_ string, v reflect.Value) bool { // // A non-nil FieldFilter f may be provided to control the output: // struct fields for which f(fieldname, fieldvalue) is true are -// are printed; all others are filtered from the output. +// are printed; all others are filtered from the output. Unexported +// struct fields are never printed. // func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) { // setup printer @@ -107,8 +108,10 @@ func (p *printer) Write(data []byte) (n int, err error) { } p.last = b } - m, err = p.output.Write(data[n:]) - n += m + if len(data) > n { + m, err = p.output.Write(data[n:]) + n += m + } return } @@ -145,15 +148,18 @@ func (p *printer) print(x reflect.Value) { p.print(x.Elem()) case reflect.Map: - p.printf("%s (len = %d) {\n", x.Type(), x.Len()) - p.indent++ - for _, key := range x.MapKeys() { - p.print(key) - p.printf(": ") - p.print(x.MapIndex(key)) + p.printf("%s (len = %d) {", x.Type(), x.Len()) + if x.Len() > 0 { + p.indent++ p.printf("\n") + for _, key := range x.MapKeys() { + p.print(key) + p.printf(": ") + p.print(x.MapIndex(key)) + p.printf("\n") + } + p.indent-- } - p.indent-- p.printf("}") case reflect.Ptr: @@ -169,32 +175,57 @@ func (p *printer) print(x reflect.Value) { p.print(x.Elem()) } + case reflect.Array: + p.printf("%s {", x.Type()) + if x.Len() > 0 { + p.indent++ + p.printf("\n") + for i, n := 0, x.Len(); i < n; i++ { + p.printf("%d: ", i) + p.print(x.Index(i)) + p.printf("\n") + } + p.indent-- + } + p.printf("}") + case reflect.Slice: if s, ok := x.Interface().([]byte); ok { p.printf("%#q", s) return } - p.printf("%s (len = %d) {\n", x.Type(), x.Len()) - p.indent++ - for i, n := 0, x.Len(); i < n; i++ { - p.printf("%d: ", i) - p.print(x.Index(i)) + p.printf("%s (len = %d) {", x.Type(), x.Len()) + if x.Len() > 0 { + p.indent++ p.printf("\n") + for i, n := 0, x.Len(); i < n; i++ { + p.printf("%d: ", i) + p.print(x.Index(i)) + p.printf("\n") + } + p.indent-- } - p.indent-- p.printf("}") case reflect.Struct: - p.printf("%s {\n", x.Type()) - p.indent++ t := x.Type() + p.printf("%s {", t) + p.indent++ + first := true for i, n := 0, t.NumField(); i < n; i++ { - name := t.Field(i).Name - value := x.Field(i) - if p.filter == nil || p.filter(name, value) { - p.printf("%s: ", name) - p.print(value) - p.printf("\n") + // exclude non-exported fields because their + // values cannot be accessed via reflection + if name := t.Field(i).Name; IsExported(name) { + value := x.Field(i) + if p.filter == nil || p.filter(name, value) { + if first { + p.printf("\n") + first = false + } + p.printf("%s: ", name) + p.print(value) + p.printf("\n") + } } } p.indent-- diff --git a/src/pkg/go/ast/print_test.go b/src/pkg/go/ast/print_test.go index 71c028e75..210f16430 100644 --- a/src/pkg/go/ast/print_test.go +++ b/src/pkg/go/ast/print_test.go @@ -23,6 +23,7 @@ var tests = []struct { {"foobar", "0 \"foobar\""}, // maps + {map[Expr]string{}, `0 map[ast.Expr]string (len = 0) {}`}, {map[string]int{"a": 1}, `0 map[string]int (len = 1) { 1 . "a": 1 @@ -31,7 +32,21 @@ var tests = []struct { // pointers {new(int), "0 *0"}, + // arrays + {[0]int{}, `0 [0]int {}`}, + {[3]int{1, 2, 3}, + `0 [3]int { + 1 . 0: 1 + 2 . 1: 2 + 3 . 2: 3 + 4 }`}, + {[...]int{42}, + `0 [1]int { + 1 . 0: 42 + 2 }`}, + // slices + {[]int{}, `0 []int (len = 0) {}`}, {[]int{1, 2, 3}, `0 []int (len = 3) { 1 . 0: 1 @@ -40,6 +55,12 @@ var tests = []struct { 4 }`}, // structs + {struct{}{}, `0 struct {} {}`}, + {struct{ x int }{007}, `0 struct { x int } {}`}, + {struct{ X, y int }{42, 991}, + `0 struct { X int; y int } { + 1 . X: 42 + 2 }`}, {struct{ X, Y int }{42, 991}, `0 struct { X int; Y int } { 1 . X: 42 diff --git a/src/pkg/go/ast/resolve.go b/src/pkg/go/ast/resolve.go index 908e61c5d..0406bfc58 100644 --- a/src/pkg/go/ast/resolve.go +++ b/src/pkg/go/ast/resolve.go @@ -57,7 +57,7 @@ func resolve(scope *Scope, ident *Ident) bool { // An Importer must determine the canonical import path and // check the map to see if it is already present in the imports map. // If so, the Importer can return the map entry. Otherwise, the -// Importer should load the package data for the given path into +// Importer should load the package data for the given path into // a new *Object (pkg), record pkg in the imports map, and then // return pkg. type Importer func(imports map[string]*Object, path string) (pkg *Object, err error) @@ -136,7 +136,7 @@ func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, for _, obj := range pkg.Data.(*Scope).Objects { p.declare(fileScope, pkgScope, obj) } - } else { + } else if name != "_" { // declare imported package object in file scope // (do not re-use pkg in the file scope but create // a new object instead; the Decl field is different diff --git a/src/pkg/go/ast/scope.go b/src/pkg/go/ast/scope.go index 11e6b13f1..8df5b2c65 100644 --- a/src/pkg/go/ast/scope.go +++ b/src/pkg/go/ast/scope.go @@ -64,18 +64,16 @@ func (s *Scope) String() string { // ---------------------------------------------------------------------------- // Objects -// TODO(gri) Consider replacing the Object struct with an interface -// and a corresponding set of object implementations. - // An Object describes a named language entity such as a package, // constant, type, variable, function (incl. methods), or label. // // The Data fields contains object-specific data: // -// Kind Data type Data value -// Pkg *Scope package scope -// Con int iota for the respective declaration -// Con != nil constant value +// Kind Data type Data value +// Pkg *types.Package package scope +// Con int iota for the respective declaration +// Con != nil constant value +// Typ *Scope (used as method scope during type checking - transient) // type Object struct { Kind ObjKind @@ -137,7 +135,7 @@ func (obj *Object) Pos() token.Pos { return token.NoPos } -// ObKind describes what an object represents. +// ObjKind describes what an object represents. type ObjKind int // The list of possible Object kinds. diff --git a/src/pkg/go/ast/walk.go b/src/pkg/go/ast/walk.go index 181cfd149..fef2503c3 100644 --- a/src/pkg/go/ast/walk.go +++ b/src/pkg/go/ast/walk.go @@ -158,7 +158,9 @@ func Walk(v Visitor, node Node) { Walk(v, n.Fields) case *FuncType: - Walk(v, n.Params) + if n.Params != nil { + Walk(v, n.Params) + } if n.Results != nil { Walk(v, n.Results) } @@ -344,9 +346,6 @@ func Walk(v Visitor, node Node) { } Walk(v, n.Name) walkDeclList(v, n.Decls) - for _, g := range n.Comments { - Walk(v, g) - } // don't walk n.Comments - they have been // visited already through the individual // nodes diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go index 7a81d5030..16c3da458 100644 --- a/src/pkg/go/build/build.go +++ b/src/pkg/go/build/build.go @@ -33,6 +33,7 @@ type Context struct { GOPATH string // Go path CgoEnabled bool // whether cgo can be used BuildTags []string // additional tags to recognize in +build lines + InstallTag string // package install directory suffix UseAllFiles bool // use files regardless of +build lines, file names Compiler string // compiler to assume when computing target paths @@ -116,12 +117,27 @@ func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) { return f(root, dir) } - if p, err := filepath.EvalSymlinks(root); err == nil { - root = p + // Try using paths we received. + if rel, ok = hasSubdir(root, dir); ok { + return } - if p, err := filepath.EvalSymlinks(dir); err == nil { - dir = p + + // Try expanding symlinks and comparing + // expanded against unexpanded and + // expanded against expanded. + rootSym, _ := filepath.EvalSymlinks(root) + dirSym, _ := filepath.EvalSymlinks(dir) + + if rel, ok = hasSubdir(rootSym, dir); ok { + return + } + if rel, ok = hasSubdir(root, dirSym); ok { + return } + return hasSubdir(rootSym, dirSym) +} + +func hasSubdir(root, dir string) (rel string, ok bool) { const sep = string(filepath.Separator) root = filepath.Clean(root) if !strings.HasSuffix(root, sep) { @@ -180,6 +196,21 @@ func (ctxt *Context) gopath() []string { // Do not get confused by this common mistake. continue } + if strings.Contains(p, "~") && runtime.GOOS != "windows" { + // Path segments containing ~ on Unix are almost always + // users who have incorrectly quoted ~ while setting GOPATH, + // preventing it from expanding to $HOME. + // The situation is made more confusing by the fact that + // bash allows quoted ~ in $PATH (most shells do not). + // Do not get confused by this, and do not try to use the path. + // It does not exist, and printing errors about it confuses + // those users even more, because they think "sure ~ exists!". + // The go command diagnoses this situation and prints a + // useful error. + // On Windows, ~ is used in short names, such as c:\progra~1 + // for c:\program files. + continue + } all = append(all, p) } return all @@ -213,10 +244,16 @@ var Default Context = defaultContext() var cgoEnabled = map[string]bool{ "darwin/386": true, "darwin/amd64": true, - "linux/386": true, - "linux/amd64": true, "freebsd/386": true, "freebsd/amd64": true, + "linux/386": true, + "linux/amd64": true, + "linux/arm": true, + "netbsd/386": true, + "netbsd/amd64": true, + "netbsd/arm": true, + "openbsd/386": true, + "openbsd/amd64": true, "windows/386": true, "windows/amd64": true, } @@ -278,12 +315,15 @@ type Package struct { PkgObj string // installed .a file // Source files - GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string // .go source files that import "C" - CFiles []string // .c source files - HFiles []string // .h source files - SFiles []string // .s source files - SysoFiles []string // .syso system object files to add to archive + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go source files that import "C" + IgnoredGoFiles []string // .go source files ignored for this build + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + SysoFiles []string // .syso system object files to add to archive + SwigFiles []string // .swig files + SwigCXXFiles []string // .swigcxx files // Cgo directives CgoPkgConfig []string // Cgo pkg-config directives @@ -346,6 +386,9 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa p := &Package{ ImportPath: path, } + if path == "" { + return p, fmt.Errorf("import %q: invalid import path", path) + } var pkga string var pkgerr error @@ -354,7 +397,11 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa dir, elem := pathpkg.Split(p.ImportPath) pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a" case "gc": - pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + p.ImportPath + ".a" + tag := "" + if ctxt.InstallTag != "" { + tag = "_" + ctxt.InstallTag + } + pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + tag + "/" + p.ImportPath + ".a" default: // Save error for end of function. pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler) @@ -410,6 +457,13 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa if strings.HasPrefix(path, "/") { return p, fmt.Errorf("import %q: cannot import absolute path", path) } + + // tried records the location of unsucsessful package lookups + var tried struct { + goroot string + gopath []string + } + // Determine directory from import path. if ctxt.GOROOT != "" { dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path) @@ -421,6 +475,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa p.Root = ctxt.GOROOT goto Found } + tried.goroot = dir } for _, root := range ctxt.gopath() { dir := ctxt.joinPath(root, "src", path) @@ -431,8 +486,28 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa p.Root = root goto Found } + tried.gopath = append(tried.gopath, dir) + } + + // package was not found + var paths []string + if tried.goroot != "" { + paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot)) + } else { + paths = append(paths, "\t($GOROOT not set)") } - return p, fmt.Errorf("import %q: cannot find package", path) + var i int + var format = "\t%s (from $GOPATH)" + for ; i < len(tried.gopath); i++ { + if i > 0 { + format = "\t%s" + } + paths = append(paths, fmt.Sprintf(format, tried.gopath[i])) + } + if i == 0 { + paths = append(paths, "\t($GOPATH not set)") + } + return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n")) } Found: @@ -476,17 +551,22 @@ Found: strings.HasPrefix(name, ".") { continue } - if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) { - continue - } i := strings.LastIndex(name, ".") if i < 0 { i = len(name) } ext := name[i:] + + if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) { + if ext == ".go" { + p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) + } + continue + } + switch ext { - case ".go", ".c", ".s", ".h", ".S": + case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx": // tentatively okay - read to make sure case ".syso": // binary objects to add to package archive @@ -504,7 +584,13 @@ Found: if err != nil { return p, err } - data, err := ioutil.ReadAll(f) + + var data []byte + if strings.HasSuffix(filename, ".go") { + data, err = readImports(f, false) + } else { + data, err = readComments(f) + } f.Close() if err != nil { return p, fmt.Errorf("read %s: %v", filename, err) @@ -512,6 +598,9 @@ Found: // Look for +build comments to accept or reject the file. if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) { + if ext == ".go" { + p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) + } continue } @@ -529,6 +618,12 @@ Found: case ".S": Sfiles = append(Sfiles, name) continue + case ".swig": + p.SwigFiles = append(p.SwigFiles, name) + continue + case ".swigcxx": + p.SwigCXXFiles = append(p.SwigCXXFiles, name) + continue } pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments) @@ -536,8 +631,9 @@ Found: return p, err } - pkg := string(pf.Name.Name) + pkg := pf.Name.Name if pkg == "documentation" { + p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) continue } @@ -570,7 +666,7 @@ Found: if !ok { continue } - quoted := string(spec.Path.Value) + quoted := spec.Path.Value path, err := strconv.Unquote(quoted) if err != nil { log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) @@ -678,7 +774,7 @@ func (ctxt *Context) shouldBuild(content []byte) bool { } line = bytes.TrimSpace(line) if len(line) == 0 { // Blank line - end = cap(content) - cap(line) // &line[0] - &content[0] + end = len(content) - len(p) continue } if !bytes.HasPrefix(line, slashslash) { // Not comment line @@ -872,6 +968,8 @@ func splitQuoted(s string) (r []string, err error) { // $GOARCH // cgo (if cgo is enabled) // !cgo (if cgo is disabled) +// ctxt.Compiler +// !ctxt.Compiler // tag (if tag is listed in ctxt.BuildTags) // !tag (if tag is not listed in ctxt.BuildTags) // a comma-separated list of any of these @@ -903,7 +1001,7 @@ func (ctxt *Context) match(name string) bool { if ctxt.CgoEnabled && name == "cgo" { return true } - if name == ctxt.GOOS || name == ctxt.GOARCH { + if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler { return true } diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go index 560ebad5c..d8cf98840 100644 --- a/src/pkg/go/build/build_test.go +++ b/src/pkg/go/build/build_test.go @@ -61,6 +61,19 @@ func TestDotSlashImport(t *testing.T) { } } +func TestEmptyImport(t *testing.T) { + p, err := Import("", Default.GOROOT, FindOnly) + if err == nil { + t.Fatal(`Import("") returned nil error.`) + } + if p == nil { + t.Fatal(`Import("") returned nil package.`) + } + if p.ImportPath != "" { + t.Fatalf("ImportPath=%q, want %q.", p.ImportPath, "") + } +} + func TestLocalDirectory(t *testing.T) { cwd, err := os.Getwd() if err != nil { @@ -75,3 +88,32 @@ func TestLocalDirectory(t *testing.T) { t.Fatalf("ImportPath=%q, want %q", p.ImportPath, "go/build") } } + +func TestShouldBuild(t *testing.T) { + const file1 = "// +build tag1\n\n" + + "package main\n" + + const file2 = "// +build cgo\n\n" + + "// This package implements parsing of tags like\n" + + "// +build tag1\n" + + "package build" + + const file3 = "// Copyright The Go Authors.\n\n" + + "package build\n\n" + + "// shouldBuild checks tags given by lines of the form\n" + + "// +build tag\n" + + "func shouldBuild(content []byte)\n" + + ctx := &Context{BuildTags: []string{"tag1"}} + if !ctx.shouldBuild([]byte(file1)) { + t.Errorf("should not build file1, expected the contrary") + } + if ctx.shouldBuild([]byte(file2)) { + t.Errorf("should build file2, expected the contrary") + } + + ctx = &Context{BuildTags: nil} + if !ctx.shouldBuild([]byte(file3)) { + t.Errorf("should not build file3, expected the contrary") + } +} diff --git a/src/pkg/go/build/deps_test.go b/src/pkg/go/build/deps_test.go index 4e9f32a03..9a715ba60 100644 --- a/src/pkg/go/build/deps_test.go +++ b/src/pkg/go/build/deps_test.go @@ -5,10 +5,9 @@ // This file exercises the import parser but also checks that // some low-level packages do not have new dependencies added. -package build_test +package build import ( - "go/build" "sort" "testing" ) @@ -24,13 +23,13 @@ import ( // be used as dependencies by other rules. // // DO NOT CHANGE THIS DATA TO FIX BUILDS. -// +// var pkgDeps = map[string][]string{ // L0 is the lowest level, core, nearly unavoidable packages. "errors": {}, "io": {"errors", "sync"}, "runtime": {"unsafe"}, - "sync": {"sync/atomic"}, + "sync": {"sync/atomic", "unsafe"}, "sync/atomic": {"unsafe"}, "unsafe": {}, @@ -142,7 +141,7 @@ var pkgDeps = map[string][]string{ // Packages used by testing must be low-level (L2+fmt). "regexp": {"L2", "regexp/syntax"}, "regexp/syntax": {"L2"}, - "runtime/debug": {"L2", "fmt", "io/ioutil", "os"}, + "runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"}, "runtime/pprof": {"L2", "fmt", "text/tabwriter"}, "text/tabwriter": {"L2"}, @@ -177,7 +176,7 @@ var pkgDeps = map[string][]string{ }, // One of a kind. - "archive/tar": {"L4", "OS"}, + "archive/tar": {"L4", "OS", "syscall"}, "archive/zip": {"L4", "OS", "compress/flate"}, "compress/bzip2": {"L4"}, "compress/flate": {"L4"}, @@ -249,18 +248,23 @@ var pkgDeps = map[string][]string{ "net/mail": {"L4", "NET", "OS"}, "net/textproto": {"L4", "OS", "net"}, + // Support libraries for crypto that aren't L2. + "CRYPTO-SUPPORT": { + "crypto/subtle", + }, + // Core crypto. "crypto/aes": {"L3"}, "crypto/des": {"L3"}, - "crypto/hmac": {"L3"}, + "crypto/hmac": {"L3", "CRYPTO-SUPPORT"}, "crypto/md5": {"L3"}, "crypto/rc4": {"L3"}, "crypto/sha1": {"L3"}, "crypto/sha256": {"L3"}, "crypto/sha512": {"L3"}, - "crypto/subtle": {"L3"}, "CRYPTO": { + "CRYPTO-SUPPORT", "crypto/aes", "crypto/des", "crypto/hmac", @@ -269,7 +273,6 @@ var pkgDeps = map[string][]string{ "crypto/sha1", "crypto/sha256", "crypto/sha512", - "crypto/subtle", }, // Random byte, number generation. @@ -300,7 +303,10 @@ var pkgDeps = map[string][]string{ "L4", "CRYPTO-MATH", "CGO", "OS", "crypto/x509", "encoding/pem", "net", "syscall", }, - "crypto/x509": {"L4", "CRYPTO-MATH", "OS", "CGO", "crypto/x509/pkix", "encoding/pem", "syscall"}, + "crypto/x509": { + "L4", "CRYPTO-MATH", "OS", "CGO", + "crypto/x509/pkix", "encoding/pem", "encoding/hex", "net", "syscall", + }, "crypto/x509/pkix": {"L4", "CRYPTO-MATH"}, // Simple net+crypto-aware packages. @@ -375,7 +381,7 @@ func TestDependencies(t *testing.T) { } sort.Strings(all) - ctxt := build.Default + ctxt := Default test := func(mustImport bool) { for _, pkg := range all { if isMacro(pkg) { diff --git a/src/pkg/go/build/doc.go b/src/pkg/go/build/doc.go index 67c26ac7f..c562d05d0 100644 --- a/src/pkg/go/build/doc.go +++ b/src/pkg/go/build/doc.go @@ -23,12 +23,12 @@ // As in the Go tree, each target operating system and // architecture pair has its own subdirectory of pkg // (pkg/GOOS_GOARCH). -// +// // If DIR is a directory listed in the Go path, a package with // source in DIR/src/foo/bar can be imported as "foo/bar" and // has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a" // (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a"). -// +// // The bin/ directory holds compiled commands. // Each command is named for its source directory, but only // using the final element, not the entire path. That is, the @@ -36,11 +36,11 @@ // DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped // so that you can add DIR/bin to your PATH to get at the // installed commands. -// +// // Here's an example directory layout: -// +// // GOPATH=/home/user/gocode -// +// // /home/user/gocode/ // src/ // foo/ @@ -60,9 +60,12 @@ // A build constraint is a line comment beginning with the directive +build // that lists the conditions under which a file should be included in the package. // Constraints may appear in any kind of source file (not just Go), but -// they must be appear near the top of the file, preceded +// they must appear near the top of the file, preceded // only by blank lines and other line comments. // +// To distinguish build constraints from package documentation, a series of +// build constraints must be followed by a blank line. +// // A build constraint is evaluated as the OR of space-separated options; // each option evaluates as the AND of its comma-separated terms; // and each term is an alphanumeric word or, preceded by !, its negation. @@ -74,10 +77,21 @@ // // (linux AND 386) OR (darwin AND (NOT cgo)) // +// A file may have multiple build constraints. The overall constraint is the AND +// of the individual constraints. That is, the build constraints: +// +// // +build linux darwin +// // +build 386 +// +// corresponds to the boolean formula: +// +// (linux OR darwin) AND 386 +// // During a particular build, the following words are satisfied: // // - the target operating system, as spelled by runtime.GOOS // - the target architecture, as spelled by runtime.GOARCH +// - the compiler being used, currently either "gc" or "gccgo" // - "cgo", if ctxt.CgoEnabled is true // - any additional words listed in ctxt.BuildTags // @@ -100,7 +114,7 @@ // default functionality for other systems, which in this case would // carry the constraint: // -// // +build !linux !darwin !cgo +// // +build !linux,!darwin !cgo // // Naming a file dns_windows.go will cause it to be included only when // building the package for Windows; similarly, math_386.s will be included diff --git a/src/pkg/go/build/read.go b/src/pkg/go/build/read.go new file mode 100644 index 000000000..c8079dfd1 --- /dev/null +++ b/src/pkg/go/build/read.go @@ -0,0 +1,238 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package build + +import ( + "bufio" + "errors" + "io" +) + +type importReader struct { + b *bufio.Reader + buf []byte + peek byte + err error + eof bool + nerr int +} + +func isIdent(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= 0x80 +} + +var ( + errSyntax = errors.New("syntax error") + errNUL = errors.New("unexpected NUL in input") +) + +// syntaxError records a syntax error, but only if an I/O error has not already been recorded. +func (r *importReader) syntaxError() { + if r.err == nil { + r.err = errSyntax + } +} + +// readByte reads the next byte from the input, saves it in buf, and returns it. +// If an error occurs, readByte records the error in r.err and returns 0. +func (r *importReader) readByte() byte { + c, err := r.b.ReadByte() + if err == nil { + r.buf = append(r.buf, c) + if c == 0 { + err = errNUL + } + } + if err != nil { + if err == io.EOF { + r.eof = true + } else if r.err == nil { + r.err = err + } + c = 0 + } + return c +} + +// peekByte returns the next byte from the input reader but does not advance beyond it. +// If skipSpace is set, peekByte skips leading spaces and comments. +func (r *importReader) peekByte(skipSpace bool) byte { + if r.err != nil { + if r.nerr++; r.nerr > 10000 { + panic("go/build: import reader looping") + } + return 0 + } + + // Use r.peek as first input byte. + // Don't just return r.peek here: it might have been left by peekByte(false) + // and this might be peekByte(true). + c := r.peek + if c == 0 { + c = r.readByte() + } + for r.err == nil && !r.eof { + if skipSpace { + // For the purposes of this reader, semicolons are never necessary to + // understand the input and are treated as spaces. + switch c { + case ' ', '\f', '\t', '\r', '\n', ';': + c = r.readByte() + continue + + case '/': + c = r.readByte() + if c == '/' { + for c != '\n' && r.err == nil && !r.eof { + c = r.readByte() + } + } else if c == '*' { + var c1 byte + for (c != '*' || c1 != '/') && r.err == nil { + if r.eof { + r.syntaxError() + } + c, c1 = c1, r.readByte() + } + } else { + r.syntaxError() + } + c = r.readByte() + continue + } + } + break + } + r.peek = c + return r.peek +} + +// nextByte is like peekByte but advances beyond the returned byte. +func (r *importReader) nextByte(skipSpace bool) byte { + c := r.peekByte(skipSpace) + r.peek = 0 + return c +} + +// readKeyword reads the given keyword from the input. +// If the keyword is not present, readKeyword records a syntax error. +func (r *importReader) readKeyword(kw string) { + r.peekByte(true) + for i := 0; i < len(kw); i++ { + if r.nextByte(false) != kw[i] { + r.syntaxError() + return + } + } + if isIdent(r.peekByte(false)) { + r.syntaxError() + } +} + +// readIdent reads an identifier from the input. +// If an identifier is not present, readIdent records a syntax error. +func (r *importReader) readIdent() { + c := r.peekByte(true) + if !isIdent(c) { + r.syntaxError() + return + } + for isIdent(r.peekByte(false)) { + r.peek = 0 + } +} + +// readString reads a quoted string literal from the input. +// If an identifier is not present, readString records a syntax error. +func (r *importReader) readString() { + switch r.nextByte(true) { + case '`': + for r.err == nil { + if r.nextByte(false) == '`' { + break + } + if r.eof { + r.syntaxError() + } + } + case '"': + for r.err == nil { + c := r.nextByte(false) + if c == '"' { + break + } + if r.eof || c == '\n' { + r.syntaxError() + } + if c == '\\' { + r.nextByte(false) + } + } + default: + r.syntaxError() + } +} + +// readImport reads an import clause - optional identifier followed by quoted string - +// from the input. +func (r *importReader) readImport() { + c := r.peekByte(true) + if c == '.' { + r.peek = 0 + } else if isIdent(c) { + r.readIdent() + } + r.readString() +} + +// readComments is like ioutil.ReadAll, except that it only reads the leading +// block of comments in the file. +func readComments(f io.Reader) ([]byte, error) { + r := &importReader{b: bufio.NewReader(f)} + r.peekByte(true) + if r.err == nil && !r.eof { + // Didn't reach EOF, so must have found a non-space byte. Remove it. + r.buf = r.buf[:len(r.buf)-1] + } + return r.buf, r.err +} + +// readImports is like ioutil.ReadAll, except that it expects a Go file as input +// and stops reading the input once the imports have completed. +func readImports(f io.Reader, reportSyntaxError bool) ([]byte, error) { + r := &importReader{b: bufio.NewReader(f)} + + r.readKeyword("package") + r.readIdent() + for r.peekByte(true) == 'i' { + r.readKeyword("import") + if r.peekByte(true) == '(' { + r.nextByte(false) + for r.peekByte(true) != ')' && r.err == nil { + r.readImport() + } + r.nextByte(false) + } else { + r.readImport() + } + } + + // If we stopped successfully before EOF, we read a byte that told us we were done. + // Return all but that last byte, which would cause a syntax error if we let it through. + if r.err == nil && !r.eof { + return r.buf[:len(r.buf)-1], nil + } + + // If we stopped for a syntax error, consume the whole file so that + // we are sure we don't change the errors that go/parser returns. + if r.err == errSyntax && !reportSyntaxError { + r.err = nil + for r.err == nil && !r.eof { + r.readByte() + } + } + + return r.buf, r.err +} diff --git a/src/pkg/go/build/read_test.go b/src/pkg/go/build/read_test.go new file mode 100644 index 000000000..2dcc1208f --- /dev/null +++ b/src/pkg/go/build/read_test.go @@ -0,0 +1,226 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package build + +import ( + "io" + "strings" + "testing" +) + +const quote = "`" + +type readTest struct { + // Test input contains ℙ where readImports should stop. + in string + err string +} + +var readImportsTests = []readTest{ + { + `package p`, + "", + }, + { + `package p; import "x"`, + "", + }, + { + `package p; import . "x"`, + "", + }, + { + `package p; import "x";ℙvar x = 1`, + "", + }, + { + `package p + + // comment + + import "x" + import _ "x" + import a "x" + + /* comment */ + + import ( + "x" /* comment */ + _ "x" + a "x" // comment + ` + quote + `x` + quote + ` + _ /*comment*/ ` + quote + `x` + quote + ` + a ` + quote + `x` + quote + ` + ) + import ( + ) + import () + import()import()import() + import();import();import() + + ℙvar x = 1 + `, + "", + }, +} + +var readCommentsTests = []readTest{ + { + `ℙpackage p`, + "", + }, + { + `ℙpackage p; import "x"`, + "", + }, + { + `ℙpackage p; import . "x"`, + "", + }, + { + `// foo + + /* bar */ + + /* quux */ // baz + + /*/ zot */ + + // asdf + ℙHello, world`, + "", + }, +} + +func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) { + for i, tt := range tests { + var in, testOut string + j := strings.Index(tt.in, "ℙ") + if j < 0 { + in = tt.in + testOut = tt.in + } else { + in = tt.in[:j] + tt.in[j+len("ℙ"):] + testOut = tt.in[:j] + } + r := strings.NewReader(in) + buf, err := read(r) + if err != nil { + if tt.err == "" { + t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf)) + continue + } + if !strings.Contains(err.Error(), tt.err) { + t.Errorf("#%d: err=%q, expected %q", i, err, tt.err) + continue + } + continue + } + if err == nil && tt.err != "" { + t.Errorf("#%d: success, expected %q", i, tt.err) + continue + } + + out := string(buf) + if out != testOut { + t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut) + } + } +} + +func TestReadImports(t *testing.T) { + testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return readImports(r, true) }) +} + +func TestReadComments(t *testing.T) { + testRead(t, readCommentsTests, readComments) +} + +var readFailuresTests = []readTest{ + { + `package`, + "syntax error", + }, + { + "package p\n\x00\nimport `math`\n", + "unexpected NUL in input", + }, + { + `package p; import`, + "syntax error", + }, + { + `package p; import "`, + "syntax error", + }, + { + "package p; import ` \n\n", + "syntax error", + }, + { + `package p; import "x`, + "syntax error", + }, + { + `package p; import _`, + "syntax error", + }, + { + `package p; import _ "`, + "syntax error", + }, + { + `package p; import _ "x`, + "syntax error", + }, + { + `package p; import .`, + "syntax error", + }, + { + `package p; import . "`, + "syntax error", + }, + { + `package p; import . "x`, + "syntax error", + }, + { + `package p; import (`, + "syntax error", + }, + { + `package p; import ("`, + "syntax error", + }, + { + `package p; import ("x`, + "syntax error", + }, + { + `package p; import ("x"`, + "syntax error", + }, +} + +func TestReadFailures(t *testing.T) { + // Errors should be reported (true arg to readImports). + testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return readImports(r, true) }) +} + +func TestReadFailuresIgnored(t *testing.T) { + // Syntax errors should not be reported (false arg to readImports). + // Instead, entire file should be the output and no error. + // Convert tests not to return syntax errors. + tests := make([]readTest, len(readFailuresTests)) + copy(tests, readFailuresTests) + for i := range tests { + tt := &tests[i] + if !strings.Contains(tt.err, "NUL") { + tt.err = "" + } + } + testRead(t, tests, func(r io.Reader) ([]byte, error) { return readImports(r, false) }) +} diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go index 6f0edd4ba..c4b7e6ae6 100644 --- a/src/pkg/go/doc/comment.go +++ b/src/pkg/go/doc/comment.go @@ -174,7 +174,7 @@ func unindent(block []string) { } // heading returns the trimmed line if it passes as a section heading; -// otherwise it returns the empty string. +// otherwise it returns the empty string. func heading(line string) string { line = strings.TrimSpace(line) if len(line) == 0 { @@ -229,7 +229,8 @@ type block struct { var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`) func anchorID(line string) string { - return nonAlphaNumRx.ReplaceAllString(line, "_") + // Add a "hdr-" prefix to avoid conflicting with IDs used for package symbols. + return "hdr-" + nonAlphaNumRx.ReplaceAllString(line, "_") } // ToHTML converts comment text to formatted HTML. diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go index 9c606315d..65b1b83eb 100644 --- a/src/pkg/go/doc/doc.go +++ b/src/pkg/go/doc/doc.go @@ -17,7 +17,16 @@ type Package struct { ImportPath string Imports []string Filenames []string - Bugs []string + // DEPRECATED. For backward compatibility Bugs is still populated, + // but all new code should use Notes instead. + Bugs []string + + // Notes such as TODO(userid): or SECURITY(userid): + // along the lines of BUG(userid). Any marker with 2 or more upper + // case [A-Z] letters is recognised. + // BUG is explicitly not included in these notes but will + // be in a subsequent change when the Bugs field above is removed. + Notes map[string][]string // declarations Consts []*Value @@ -89,6 +98,7 @@ func New(pkg *ast.Package, importPath string, mode Mode) *Package { Imports: sortedKeys(r.imports), Filenames: r.filenames, Bugs: r.bugs, + Notes: r.notes, Consts: sortedValues(r.values, token.CONST), Types: sortedTypes(r.types, mode&AllMethods != 0), Vars: sortedValues(r.values, token.VAR), diff --git a/src/pkg/go/doc/doc_test.go b/src/pkg/go/doc/doc_test.go index f957ede4a..8043038b4 100644 --- a/src/pkg/go/doc/doc_test.go +++ b/src/pkg/go/doc/doc_test.go @@ -123,7 +123,7 @@ func test(t *testing.T, mode Mode) { } // compare - if bytes.Compare(got, want) != 0 { + if !bytes.Equal(got, want) { t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want) } } diff --git a/src/pkg/go/doc/example.go b/src/pkg/go/doc/example.go index a7e0e250a..693ad5b94 100644 --- a/src/pkg/go/doc/example.go +++ b/src/pkg/go/doc/example.go @@ -9,21 +9,29 @@ package doc import ( "go/ast" "go/token" + "path" "regexp" "sort" + "strconv" "strings" "unicode" "unicode/utf8" ) +// An Example represents an example function found in a source files. type Example struct { - Name string // name of the item being exemplified - Doc string // example function doc string - Code ast.Node - Comments []*ast.CommentGroup - Output string // expected output + Name string // name of the item being exemplified + Doc string // example function doc string + Code ast.Node + Play *ast.File // a whole program version of the example + Comments []*ast.CommentGroup + Output string // expected output + EmptyOutput bool // expect empty output + Order int // original source code order } +// Examples returns the examples found in the files, sorted by Name field. +// The Order fields record the order in which the examples were encountered. func Examples(files ...*ast.File) []*Example { var list []*Example for _, file := range files { @@ -52,12 +60,16 @@ func Examples(files ...*ast.File) []*Example { if f.Doc != nil { doc = f.Doc.Text() } + output, hasOutput := exampleOutput(f.Body, file.Comments) flist = append(flist, &Example{ - Name: name[len("Example"):], - Doc: doc, - Code: f.Body, - Comments: file.Comments, - Output: exampleOutput(f, file.Comments), + Name: name[len("Example"):], + Doc: doc, + Code: f.Body, + Play: playExample(file, f.Body), + Comments: file.Comments, + Output: output, + EmptyOutput: output == "" && hasOutput, + Order: len(flist), }) } if !hasTests && numDecl > 1 && len(flist) == 1 { @@ -65,6 +77,7 @@ func Examples(files ...*ast.File) []*Example { // other top-level declarations, and no tests or // benchmarks, use the whole file as the example. flist[0].Code = file + flist[0].Play = playExampleFile(file) } list = append(list, flist...) } @@ -74,26 +87,22 @@ func Examples(files ...*ast.File) []*Example { var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`) -func exampleOutput(fun *ast.FuncDecl, comments []*ast.CommentGroup) string { - // find the last comment in the function - var last *ast.CommentGroup - for _, cg := range comments { - if cg.Pos() < fun.Pos() { - continue - } - if cg.End() > fun.End() { - break - } - last = cg - } - if last != nil { +// Extracts the expected output and whether there was a valid output comment +func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output string, ok bool) { + if _, last := lastComment(b, comments); last != nil { // test that it begins with the correct prefix text := last.Text() if loc := outputPrefix.FindStringIndex(text); loc != nil { - return strings.TrimSpace(text[loc[1]:]) + text = text[loc[1]:] + // Strip zero or more spaces followed by \n or a single space. + text = strings.TrimLeft(text, " ") + if len(text) > 0 && text[0] == '\n' { + text = text[1:] + } + return text, true } } - return "" // no suitable comment found + return "", false // no suitable comment found } // isTest tells whether name looks like a test, example, or benchmark. @@ -115,3 +124,214 @@ type exampleByName []*Example func (s exampleByName) Len() int { return len(s) } func (s exampleByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name } + +// playExample synthesizes a new *ast.File based on the provided +// file with the provided function body as the body of main. +func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { + if !strings.HasSuffix(file.Name.Name, "_test") { + // We don't support examples that are part of the + // greater package (yet). + return nil + } + + // Find top-level declarations in the file. + topDecls := make(map[*ast.Object]bool) + for _, decl := range file.Decls { + switch d := decl.(type) { + case *ast.FuncDecl: + topDecls[d.Name.Obj] = true + case *ast.GenDecl: + for _, spec := range d.Specs { + switch s := spec.(type) { + case *ast.TypeSpec: + topDecls[s.Name.Obj] = true + case *ast.ValueSpec: + for _, id := range s.Names { + topDecls[id.Obj] = true + } + } + } + } + } + + // Find unresolved identifiers and uses of top-level declarations. + unresolved := make(map[string]bool) + usesTopDecl := false + var inspectFunc func(ast.Node) bool + inspectFunc = func(n ast.Node) bool { + // For selector expressions, only inspect the left hand side. + // (For an expression like fmt.Println, only add "fmt" to the + // set of unresolved names, not "Println".) + if e, ok := n.(*ast.SelectorExpr); ok { + ast.Inspect(e.X, inspectFunc) + return false + } + if id, ok := n.(*ast.Ident); ok { + if id.Obj == nil { + unresolved[id.Name] = true + } else if topDecls[id.Obj] { + usesTopDecl = true + } + } + return true + } + ast.Inspect(body, inspectFunc) + if usesTopDecl { + // We don't support examples that are not self-contained (yet). + return nil + } + + // Remove predeclared identifiers from unresolved list. + for n := range unresolved { + if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] { + delete(unresolved, n) + } + } + + // Use unresolved identifiers to determine the imports used by this + // example. The heuristic assumes package names match base import + // paths for imports w/o renames (should be good enough most of the time). + namedImports := make(map[string]string) // [name]path + var blankImports []ast.Spec // _ imports + for _, s := range file.Imports { + p, err := strconv.Unquote(s.Path.Value) + if err != nil { + continue + } + n := path.Base(p) + if s.Name != nil { + n = s.Name.Name + switch n { + case "_": + blankImports = append(blankImports, s) + continue + case ".": + // We can't resolve dot imports (yet). + return nil + } + } + if unresolved[n] { + namedImports[n] = p + delete(unresolved, n) + } + } + + // If there are other unresolved identifiers, give up because this + // synthesized file is not going to build. + if len(unresolved) > 0 { + return nil + } + + // Include documentation belonging to blank imports. + var comments []*ast.CommentGroup + for _, s := range blankImports { + if c := s.(*ast.ImportSpec).Doc; c != nil { + comments = append(comments, c) + } + } + + // Include comments that are inside the function body. + for _, c := range file.Comments { + if body.Pos() <= c.Pos() && c.End() <= body.End() { + comments = append(comments, c) + } + } + + // Strip "Output:" commment and adjust body end position. + body, comments = stripOutputComment(body, comments) + + // Synthesize import declaration. + importDecl := &ast.GenDecl{ + Tok: token.IMPORT, + Lparen: 1, // Need non-zero Lparen and Rparen so that printer + Rparen: 1, // treats this as a factored import. + } + for n, p := range namedImports { + s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}} + if path.Base(p) != n { + s.Name = ast.NewIdent(n) + } + importDecl.Specs = append(importDecl.Specs, s) + } + importDecl.Specs = append(importDecl.Specs, blankImports...) + + // Synthesize main function. + funcDecl := &ast.FuncDecl{ + Name: ast.NewIdent("main"), + Type: &ast.FuncType{}, + Body: body, + } + + // Synthesize file. + return &ast.File{ + Name: ast.NewIdent("main"), + Decls: []ast.Decl{importDecl, funcDecl}, + Comments: comments, + } +} + +// playExampleFile takes a whole file example and synthesizes a new *ast.File +// such that the example is function main in package main. +func playExampleFile(file *ast.File) *ast.File { + // Strip copyright comment if present. + comments := file.Comments + if len(comments) > 0 && strings.HasPrefix(comments[0].Text(), "Copyright") { + comments = comments[1:] + } + + // Copy declaration slice, rewriting the ExampleX function to main. + var decls []ast.Decl + for _, d := range file.Decls { + if f, ok := d.(*ast.FuncDecl); ok && isTest(f.Name.Name, "Example") { + // Copy the FuncDecl, as it may be used elsewhere. + newF := *f + newF.Name = ast.NewIdent("main") + newF.Body, comments = stripOutputComment(f.Body, comments) + d = &newF + } + decls = append(decls, d) + } + + // Copy the File, as it may be used elsewhere. + f := *file + f.Name = ast.NewIdent("main") + f.Decls = decls + f.Comments = comments + return &f +} + +// stripOutputComment finds and removes an "Output:" commment from body +// and comments, and adjusts the body block's end position. +func stripOutputComment(body *ast.BlockStmt, comments []*ast.CommentGroup) (*ast.BlockStmt, []*ast.CommentGroup) { + // Do nothing if no "Output:" comment found. + i, last := lastComment(body, comments) + if last == nil || !outputPrefix.MatchString(last.Text()) { + return body, comments + } + + // Copy body and comments, as the originals may be used elsewhere. + newBody := &ast.BlockStmt{ + Lbrace: body.Lbrace, + List: body.List, + Rbrace: last.Pos(), + } + newComments := make([]*ast.CommentGroup, len(comments)-1) + copy(newComments, comments[:i]) + copy(newComments[i:], comments[i+1:]) + return newBody, newComments +} + +// lastComment returns the last comment inside the provided block. +func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.CommentGroup) { + pos, end := b.Pos(), b.End() + for j, cg := range c { + if cg.Pos() < pos { + continue + } + if cg.End() > end { + break + } + i, last = j, cg + } + return +} diff --git a/src/pkg/go/doc/example_test.go b/src/pkg/go/doc/example_test.go new file mode 100644 index 000000000..b70efd93d --- /dev/null +++ b/src/pkg/go/doc/example_test.go @@ -0,0 +1,111 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package doc_test + +import ( + "bytes" + "go/doc" + "go/format" + "go/parser" + "go/token" + "strings" + "testing" +) + +const exampleTestFile = ` +package foo_test + +import ( + "fmt" + "log" + "os/exec" +) + +func ExampleHello() { + fmt.Println("Hello, world!") + // Output: Hello, world! +} + +func ExampleImport() { + out, err := exec.Command("date").Output() + if err != nil { + log.Fatal(err) + } + fmt.Printf("The date is %s\n", out) +} +` + +var exampleTestCases = []struct { + Name, Play, Output string +}{ + { + Name: "Hello", + Play: exampleHelloPlay, + Output: "Hello, world!\n", + }, + { + Name: "Import", + Play: exampleImportPlay, + }, +} + +const exampleHelloPlay = `package main + +import ( + "fmt" +) + +func main() { + fmt.Println("Hello, world!") +} +` +const exampleImportPlay = `package main + +import ( + "fmt" + "log" + "os/exec" +) + +func main() { + out, err := exec.Command("date").Output() + if err != nil { + log.Fatal(err) + } + fmt.Printf("The date is %s\n", out) +} +` + +func TestExamples(t *testing.T) { + fs := token.NewFileSet() + file, err := parser.ParseFile(fs, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments) + if err != nil { + t.Fatal(err) + } + for i, e := range doc.Examples(file) { + c := exampleTestCases[i] + if e.Name != c.Name { + t.Errorf("got Name == %q, want %q", e.Name, c.Name) + } + if w := c.Play; w != "" { + var g string // hah + if e.Play == nil { + g = "" + } else { + b := new(bytes.Buffer) + if err := format.Node(b, fs, e.Play); err != nil { + t.Fatal(err) + } + g = b.String() + } + if g != w { + t.Errorf("%s: got Play == %q, want %q", c.Name, g, w) + } + } + if g, w := e.Output, c.Output; g != w { + t.Errorf("%s: got Output == %q, want %q", c.Name, g, w) + } + } +} diff --git a/src/pkg/go/doc/exports.go b/src/pkg/go/doc/exports.go index 146be5d87..ff01285d4 100644 --- a/src/pkg/go/doc/exports.go +++ b/src/pkg/go/doc/exports.go @@ -107,7 +107,7 @@ func (r *reader) filterParamList(fields *ast.FieldList) { // filterType strips any unexported struct fields or method types from typ // in place. If fields (or methods) have been removed, the corresponding -// struct or interface type has the Incomplete field set to true. +// struct or interface type has the Incomplete field set to true. // func (r *reader) filterType(parent *namedType, typ ast.Expr) { switch t := typ.(type) { diff --git a/src/pkg/go/doc/reader.go b/src/pkg/go/doc/reader.go index 5eaae37b7..dd6a57299 100644 --- a/src/pkg/go/doc/reader.go +++ b/src/pkg/go/doc/reader.go @@ -46,7 +46,7 @@ func (mset methodSet) set(f *ast.FuncDecl) { // since it has documentation, assume f is simply another // implementation and ignore it. This does not happen if the // caller is using go/build.ScanDir to determine the list of - // files implementing a package. + // files implementing a package. return } // function doesn't exist or has no documentation; use f @@ -149,6 +149,7 @@ type reader struct { doc string // package documentation, if any filenames []string bugs []string + notes map[string][]string // declarations imports map[string]int @@ -400,10 +401,23 @@ func (r *reader) readFunc(fun *ast.FuncDecl) { } var ( - bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid): - bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char + noteMarker = regexp.MustCompile(`^/[/*][ \t]*([A-Z][A-Z]+)\(.+\):[ \t]*(.*)`) // MARKER(uid) + noteContent = regexp.MustCompile(`[^ \n\r\t]+`) // at least one non-whitespace char ) +func readNote(c *ast.CommentGroup) (marker, annotation string) { + text := c.List[0].Text + if m := noteMarker.FindStringSubmatch(text); m != nil { + if btxt := m[2]; noteContent.MatchString(btxt) { + // non-empty MARKER comment; collect comment without the MARKER prefix + list := append([]*ast.Comment(nil), c.List...) // make a copy + list[0].Text = m[2] + return m[1], (&ast.CommentGroup{List: list}).Text() + } + } + return "", "" +} + // readFile adds the AST for a source file to the reader. // func (r *reader) readFile(src *ast.File) { @@ -469,16 +483,12 @@ func (r *reader) readFile(src *ast.File) { } } - // collect BUG(...) comments + // collect MARKER(...): annotations for _, c := range src.Comments { - text := c.List[0].Text - if m := bug_markers.FindStringIndex(text); m != nil { - // found a BUG comment; maybe empty - if btxt := text[m[1]:]; bug_content.MatchString(btxt) { - // non-empty BUG comment; collect comment without BUG prefix - list := append([]*ast.Comment(nil), c.List...) // make a copy - list[0].Text = text[m[1]:] - r.bugs = append(r.bugs, (&ast.CommentGroup{List: list}).Text()) + if marker, text := readNote(c); marker != "" { + r.notes[marker] = append(r.notes[marker], text) + if marker == "BUG" { + r.bugs = append(r.bugs, text) } } } @@ -492,9 +502,10 @@ func (r *reader) readPackage(pkg *ast.Package, mode Mode) { r.mode = mode r.types = make(map[string]*namedType) r.funcs = make(methodSet) + r.notes = make(map[string][]string) // sort package files before reading them so that the - // result result does not depend on map iteration order + // result does not depend on map iteration order i := 0 for filename := range pkg.Files { r.filenames[i] = filename @@ -515,29 +526,6 @@ func (r *reader) readPackage(pkg *ast.Package, mode Mode) { // ---------------------------------------------------------------------------- // Types -var predeclaredTypes = map[string]bool{ - "bool": true, - "byte": true, - "complex64": true, - "complex128": true, - "error": true, - "float32": true, - "float64": true, - "int": true, - "int8": true, - "int16": true, - "int32": true, - "int64": true, - "rune": true, - "string": true, - "uint": true, - "uint8": true, - "uint16": true, - "uint32": true, - "uint64": true, - "uintptr": true, -} - func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func { if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 { return f // shouldn't happen, but be safe @@ -620,7 +608,7 @@ func (r *reader) computeMethodSets() { // types that have no declaration. Instead, these functions and methods // are shown at the package level. It also removes types with missing // declarations or which are not visible. -// +// func (r *reader) cleanupTypes() { for _, t := range r.types { visible := r.isVisible(t.name) @@ -772,3 +760,54 @@ func sortedFuncs(m methodSet, allMethods bool) []*Func { ) return list } + +// ---------------------------------------------------------------------------- +// Predeclared identifiers + +var predeclaredTypes = map[string]bool{ + "bool": true, + "byte": true, + "complex64": true, + "complex128": true, + "error": true, + "float32": true, + "float64": true, + "int": true, + "int8": true, + "int16": true, + "int32": true, + "int64": true, + "rune": true, + "string": true, + "uint": true, + "uint8": true, + "uint16": true, + "uint32": true, + "uint64": true, + "uintptr": true, +} + +var predeclaredFuncs = map[string]bool{ + "append": true, + "cap": true, + "close": true, + "complex": true, + "copy": true, + "delete": true, + "imag": true, + "len": true, + "make": true, + "new": true, + "panic": true, + "print": true, + "println": true, + "real": true, + "recover": true, +} + +var predeclaredConstants = map[string]bool{ + "false": true, + "iota": true, + "nil": true, + "true": true, +} diff --git a/src/pkg/go/doc/synopsis.go b/src/pkg/go/doc/synopsis.go index 2192d78c0..2d1817439 100644 --- a/src/pkg/go/doc/synopsis.go +++ b/src/pkg/go/doc/synopsis.go @@ -4,7 +4,10 @@ package doc -import "unicode" +import ( + "strings" + "unicode" +) // firstSentenceLen returns the length of the first sentence in s. // The sentence ends after the first period followed by space and @@ -24,17 +27,12 @@ func firstSentenceLen(s string) int { return len(s) } -// Synopsis returns a cleaned version of the first sentence in s. -// That sentence ends after the first period followed by space and -// not preceded by exactly one uppercase letter. The result string -// has no \n, \r, or \t characters and uses only single spaces between -// words. -// -func Synopsis(s string) string { - n := firstSentenceLen(s) +// clean replaces each sequence of space, \n, \r, or \t characters +// with a single space and removes any trailing and leading spaces. +func clean(s string) string { var b []byte p := byte(' ') - for i := 0; i < n; i++ { + for i := 0; i < len(s); i++ { q := s[i] if q == '\n' || q == '\r' || q == '\t' { q = ' ' @@ -50,3 +48,26 @@ func Synopsis(s string) string { } return string(b) } + +// Synopsis returns a cleaned version of the first sentence in s. +// That sentence ends after the first period followed by space and +// not preceded by exactly one uppercase letter. The result string +// has no \n, \r, or \t characters and uses only single spaces between +// words. If s starts with any of the IllegalPrefixes, the result +// is the empty string. +// +func Synopsis(s string) string { + s = clean(s[0:firstSentenceLen(s)]) + for _, prefix := range IllegalPrefixes { + if strings.HasPrefix(strings.ToLower(s), prefix) { + return "" + } + } + return s +} + +var IllegalPrefixes = []string{ + "copyright", + "all rights", + "author", +} diff --git a/src/pkg/go/doc/synopsis_test.go b/src/pkg/go/doc/synopsis_test.go index dfc6598af..fd7081a07 100644 --- a/src/pkg/go/doc/synopsis_test.go +++ b/src/pkg/go/doc/synopsis_test.go @@ -28,6 +28,11 @@ var tests = []struct { {"P. Q. ", 8, "P. Q."}, {"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."}, {"Package こんにちは 世界\n", 31, "Package こんにちは 世界"}, + {"Package foo does bar.", 21, "Package foo does bar."}, + {"Copyright 2012 Google, Inc. Package foo does bar.", 27, ""}, + {"All Rights reserved. Package foo does bar.", 20, ""}, + {"All rights reserved. Package foo does bar.", 20, ""}, + {"Authors: foo@bar.com. Package foo does bar.", 21, ""}, } func TestSynopsis(t *testing.T) { diff --git a/src/pkg/go/doc/testdata/a.0.golden b/src/pkg/go/doc/testdata/a.0.golden index 24db02d34..ae3756c84 100644 --- a/src/pkg/go/doc/testdata/a.0.golden +++ b/src/pkg/go/doc/testdata/a.0.golden @@ -8,6 +8,17 @@ FILENAMES testdata/a0.go testdata/a1.go +BUGS .Bugs is now deprecated, please use .Notes instead + // bug0 + // bug1 + BUGS // bug0 // bug1 + +SECBUGS + // sec hole 0 need to fix asap + +TODOS + // todo0 + // todo1 diff --git a/src/pkg/go/doc/testdata/a.1.golden b/src/pkg/go/doc/testdata/a.1.golden index 24db02d34..ae3756c84 100644 --- a/src/pkg/go/doc/testdata/a.1.golden +++ b/src/pkg/go/doc/testdata/a.1.golden @@ -8,6 +8,17 @@ FILENAMES testdata/a0.go testdata/a1.go +BUGS .Bugs is now deprecated, please use .Notes instead + // bug0 + // bug1 + BUGS // bug0 // bug1 + +SECBUGS + // sec hole 0 need to fix asap + +TODOS + // todo0 + // todo1 diff --git a/src/pkg/go/doc/testdata/a.2.golden b/src/pkg/go/doc/testdata/a.2.golden index 24db02d34..ae3756c84 100644 --- a/src/pkg/go/doc/testdata/a.2.golden +++ b/src/pkg/go/doc/testdata/a.2.golden @@ -8,6 +8,17 @@ FILENAMES testdata/a0.go testdata/a1.go +BUGS .Bugs is now deprecated, please use .Notes instead + // bug0 + // bug1 + BUGS // bug0 // bug1 + +SECBUGS + // sec hole 0 need to fix asap + +TODOS + // todo0 + // todo1 diff --git a/src/pkg/go/doc/testdata/a0.go b/src/pkg/go/doc/testdata/a0.go index dc552989e..71af470ee 100644 --- a/src/pkg/go/doc/testdata/a0.go +++ b/src/pkg/go/doc/testdata/a0.go @@ -6,3 +6,12 @@ package a //BUG(uid): bug0 + +//TODO(uid): todo0 + +// A note with some spaces after it, should be ignored (watch out for +// emacs modes that remove trailing whitespace). +//NOTE(uid): + +// SECBUG(uid): sec hole 0 +// need to fix asap diff --git a/src/pkg/go/doc/testdata/a1.go b/src/pkg/go/doc/testdata/a1.go index 098776c1b..9fad1e09b 100644 --- a/src/pkg/go/doc/testdata/a1.go +++ b/src/pkg/go/doc/testdata/a1.go @@ -6,3 +6,7 @@ package a //BUG(uid): bug1 + +//TODO(uid): todo1 + +//TODO(): ignored diff --git a/src/pkg/go/doc/testdata/benchmark.go b/src/pkg/go/doc/testdata/benchmark.go index 0aded5bb4..905e49644 100644 --- a/src/pkg/go/doc/testdata/benchmark.go +++ b/src/pkg/go/doc/testdata/benchmark.go @@ -13,7 +13,7 @@ import ( ) var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run") -var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds") +var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark") // An internal type but exported because it is cross-package; part of the implementation // of go test. @@ -151,7 +151,7 @@ func (b *B) launch() { b.runN(n) // Run the benchmark for at least the specified amount of time. - d := time.Duration(*benchTime * float64(time.Second)) + d := *benchTime for !b.failed && b.duration < d && n < 1e9 { last := n // Predict iterations/sec. diff --git a/src/pkg/go/doc/testdata/e.go b/src/pkg/go/doc/testdata/e.go index 19dd138cf..ec432e3e5 100644 --- a/src/pkg/go/doc/testdata/e.go +++ b/src/pkg/go/doc/testdata/e.go @@ -106,7 +106,7 @@ type U4 struct { *u5 } -// U4.M should appear as method of U4. +// U4.M should appear as method of U4. func (*U4) M() {} type u5 struct { diff --git a/src/pkg/go/doc/testdata/error2.1.golden b/src/pkg/go/doc/testdata/error2.1.golden index 776bd1b3e..dbcc1b03e 100644 --- a/src/pkg/go/doc/testdata/error2.1.golden +++ b/src/pkg/go/doc/testdata/error2.1.golden @@ -10,7 +10,7 @@ FILENAMES TYPES // type I0 interface { - // When embedded, the the locally declared error interface + // When embedded, the locally-declared error interface // is only visible if all declarations are shown. error } diff --git a/src/pkg/go/doc/testdata/error2.go b/src/pkg/go/doc/testdata/error2.go index 6cc36feef..6ee96c245 100644 --- a/src/pkg/go/doc/testdata/error2.go +++ b/src/pkg/go/doc/testdata/error2.go @@ -5,7 +5,7 @@ package error2 type I0 interface { - // When embedded, the the locally declared error interface + // When embedded, the locally-declared error interface // is only visible if all declarations are shown. error } diff --git a/src/pkg/go/doc/testdata/template.txt b/src/pkg/go/doc/testdata/template.txt index 32e331cdd..d3882b6b9 100644 --- a/src/pkg/go/doc/testdata/template.txt +++ b/src/pkg/go/doc/testdata/template.txt @@ -60,6 +60,9 @@ TYPES {{end}}{{end}}{{end}}{{/* */}}{{with .Bugs}} -BUGS +BUGS .Bugs is now deprecated, please use .Notes instead {{range .}} {{synopsis .}} -{{end}}{{end}} \ No newline at end of file +{{end}}{{end}}{{with .Notes}}{{range $marker, $content := .}} +{{$marker}}S +{{range $content}} {{synopsis .}} +{{end}}{{end}}{{end}} \ No newline at end of file diff --git a/src/pkg/go/doc/testdata/testing.1.golden b/src/pkg/go/doc/testdata/testing.1.golden index d26a4685c..ffdb5c3b5 100644 --- a/src/pkg/go/doc/testdata/testing.1.golden +++ b/src/pkg/go/doc/testdata/testing.1.golden @@ -45,7 +45,7 @@ VARIABLES ) // - var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds") + var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark") // var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run") diff --git a/src/pkg/go/doc/testdata/testing.go b/src/pkg/go/doc/testdata/testing.go index 71c1d1eaf..c2499ad77 100644 --- a/src/pkg/go/doc/testdata/testing.go +++ b/src/pkg/go/doc/testdata/testing.go @@ -197,7 +197,7 @@ func (c *common) Fatalf(format string, args ...interface{}) { c.FailNow() } -// Parallel signals that this test is to be run in parallel with (and only with) +// Parallel signals that this test is to be run in parallel with (and only with) // other parallel tests in this CPU group. func (t *T) Parallel() { t.signal <- (*T)(nil) // Release main testing loop @@ -215,7 +215,7 @@ func tRunner(t *T, test *InternalTest) { t.start = time.Now() // When this goroutine is done, either because test.F(t) - // returned normally or because a test failure triggered + // returned normally or because a test failure triggered // a call to runtime.Goexit, record the duration and send // a signal saying that the test is done. defer func() { diff --git a/src/pkg/go/format/format.go b/src/pkg/go/format/format.go new file mode 100644 index 000000000..65b0e4e4b --- /dev/null +++ b/src/pkg/go/format/format.go @@ -0,0 +1,200 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package format implements standard formatting of Go source. +package format + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "io" + "strings" +) + +var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} + +// Node formats node in canonical gofmt style and writes the result to dst. +// +// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl, +// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, +// or ast.Stmt. Node does not modify node. Imports are not sorted for +// nodes representing partial source files (i.e., if the node is not an +// *ast.File or a *printer.CommentedNode not wrapping an *ast.File). +// +// The function may return early (before the entire result is written) +// and return a formatting error, for instance due to an incorrect AST. +// +func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { + // Determine if we have a complete source file (file != nil). + var file *ast.File + var cnode *printer.CommentedNode + switch n := node.(type) { + case *ast.File: + file = n + case *printer.CommentedNode: + if f, ok := n.Node.(*ast.File); ok { + file = f + cnode = n + } + } + + // Sort imports if necessary. + if file != nil && hasUnsortedImports(file) { + // Make a copy of the AST because ast.SortImports is destructive. + // TODO(gri) Do this more efficiently. + var buf bytes.Buffer + err := config.Fprint(&buf, fset, file) + if err != nil { + return err + } + file, err = parser.ParseFile(fset, "", buf.Bytes(), parser.ParseComments) + if err != nil { + // We should never get here. If we do, provide good diagnostic. + return fmt.Errorf("format.Node internal error (%s)", err) + } + ast.SortImports(fset, file) + + // Use new file with sorted imports. + node = file + if cnode != nil { + node = &printer.CommentedNode{Node: file, Comments: cnode.Comments} + } + } + + return config.Fprint(dst, fset, node) +} + +// Source formats src in canonical gofmt style and writes the result to dst +// or returns an I/O or syntax error. src is expected to be a syntactically +// correct Go source file, or a list of Go declarations or statements. +// +// If src is a partial source file, the leading and trailing space of src +// is applied to the result (such that it has the same leading and trailing +// space as src), and the formatted src is indented by the same amount as +// the first line of src containing code. Imports are not sorted for partial +// source files. +// +func Source(src []byte) ([]byte, error) { + fset := token.NewFileSet() + node, err := parse(fset, src) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + if file, ok := node.(*ast.File); ok { + // Complete source file. + ast.SortImports(fset, file) + err := config.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + + } else { + // Partial source file. + // Determine and prepend leading space. + i, j := 0, 0 + for j < len(src) && isSpace(src[j]) { + if src[j] == '\n' { + i = j + 1 // index of last line in leading space + } + j++ + } + buf.Write(src[:i]) + + // Determine indentation of first code line. + // Spaces are ignored unless there are no tabs, + // in which case spaces count as one tab. + indent := 0 + hasSpace := false + for _, b := range src[i:j] { + switch b { + case ' ': + hasSpace = true + case '\t': + indent++ + } + } + if indent == 0 && hasSpace { + indent = 1 + } + + // Format the source. + cfg := config + cfg.Indent = indent + err := cfg.Fprint(&buf, fset, node) + if err != nil { + return nil, err + } + + // Determine and append trailing space. + i = len(src) + for i > 0 && isSpace(src[i-1]) { + i-- + } + buf.Write(src[i:]) + } + + return buf.Bytes(), nil +} + +func hasUnsortedImports(file *ast.File) bool { + for _, d := range file.Decls { + d, ok := d.(*ast.GenDecl) + if !ok || d.Tok != token.IMPORT { + // Not an import declaration, so we're done. + // Imports are always first. + return false + } + if d.Lparen.IsValid() { + // For now assume all grouped imports are unsorted. + // TODO(gri) Should check if they are sorted already. + return true + } + // Ungrouped imports are sorted by default. + } + return false +} + +func isSpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' +} + +func parse(fset *token.FileSet, src []byte) (interface{}, error) { + // Try as a complete source file. + file, err := parser.ParseFile(fset, "", src, parser.ParseComments) + if err == nil { + return file, nil + } + // If the source is missing a package clause, try as a source fragment; otherwise fail. + if !strings.Contains(err.Error(), "expected 'package'") { + return nil, err + } + + // Try as a declaration list by prepending a package clause in front of src. + // Use ';' not '\n' to keep line numbers intact. + psrc := append([]byte("package p;"), src...) + file, err = parser.ParseFile(fset, "", psrc, parser.ParseComments) + if err == nil { + return file.Decls, nil + } + // If the source is missing a declaration, try as a statement list; otherwise fail. + if !strings.Contains(err.Error(), "expected declaration") { + return nil, err + } + + // Try as statement list by wrapping a function around src. + fsrc := append(append([]byte("package p; func _() {"), src...), '}') + file, err = parser.ParseFile(fset, "", fsrc, parser.ParseComments) + if err == nil { + return file.Decls[0].(*ast.FuncDecl).Body.List, nil + } + + // Failed, and out of options. + return nil, err +} diff --git a/src/pkg/go/format/format_test.go b/src/pkg/go/format/format_test.go new file mode 100644 index 000000000..7d7940bb5 --- /dev/null +++ b/src/pkg/go/format/format_test.go @@ -0,0 +1,125 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package format + +import ( + "bytes" + "go/parser" + "go/token" + "io/ioutil" + "strings" + "testing" +) + +const testfile = "format_test.go" + +func diff(t *testing.T, dst, src []byte) { + line := 1 + offs := 0 // line offset + for i := 0; i < len(dst) && i < len(src); i++ { + d := dst[i] + s := src[i] + if d != s { + t.Errorf("dst:%d: %s\n", line, dst[offs:i+1]) + t.Errorf("src:%d: %s\n", line, src[offs:i+1]) + return + } + if s == '\n' { + line++ + offs = i + 1 + } + } + if len(dst) != len(src) { + t.Errorf("len(dst) = %d, len(src) = %d\nsrc = %q", len(dst), len(src), src) + } +} + +func TestNode(t *testing.T) { + src, err := ioutil.ReadFile(testfile) + if err != nil { + t.Fatal(err) + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, testfile, src, parser.ParseComments) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + if err = Node(&buf, fset, file); err != nil { + t.Fatal("Node failed:", err) + } + + diff(t, buf.Bytes(), src) +} + +func TestSource(t *testing.T) { + src, err := ioutil.ReadFile(testfile) + if err != nil { + t.Fatal(err) + } + + res, err := Source(src) + if err != nil { + t.Fatal("Source failed:", err) + } + + diff(t, res, src) +} + +// Test cases that are expected to fail are marked by the prefix "ERROR". +var tests = []string{ + // declaration lists + `import "go/format"`, + "var x int", + "var x int\n\ntype T struct{}", + + // statement lists + "x := 0", + "f(a, b, c)\nvar x int = f(1, 2, 3)", + + // indentation, leading and trailing space + "\tx := 0\n\tgo f()", + "\tx := 0\n\tgo f()\n\n\n", + "\n\t\t\n\n\tx := 0\n\tgo f()\n\n\n", + "\n\t\t\n\n\t\t\tx := 0\n\t\t\tgo f()\n\n\n", + "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings + + // erroneous programs + "ERRORvar x", + "ERROR1 + 2 +", + "ERRORx := 0", +} + +func String(s string) (string, error) { + res, err := Source([]byte(s)) + if err != nil { + return "", err + } + return string(res), nil +} + +func TestPartial(t *testing.T) { + for _, src := range tests { + if strings.HasPrefix(src, "ERROR") { + // test expected to fail + src = src[5:] // remove ERROR prefix + res, err := String(src) + if err == nil && res == src { + t.Errorf("formatting succeeded but was expected to fail:\n%q", src) + } + } else { + // test expected to succeed + res, err := String(src) + if err != nil { + t.Errorf("formatting failed (%s):\n%q", err, src) + } else if res != src { + t.Errorf("formatting incorrect:\nsource: %q\nresult: %q", src, res) + } + } + } +} diff --git a/src/pkg/go/parser/error_test.go b/src/pkg/go/parser/error_test.go index 377c8b80c..b59fda11a 100644 --- a/src/pkg/go/parser/error_test.go +++ b/src/pkg/go/parser/error_test.go @@ -34,9 +34,11 @@ import ( const testdata = "testdata" +var fsetErrs *token.FileSet + // getFile assumes that each filename occurs at most once func getFile(filename string) (file *token.File) { - fset.Iterate(func(f *token.File) bool { + fsetErrs.Iterate(func(f *token.File) bool { if f.Name() == filename { if file != nil { panic(filename + " used multiple times") @@ -125,7 +127,7 @@ func compareErrors(t *testing.T, expected map[token.Pos]string, found scanner.Er if len(expected) > 0 { t.Errorf("%d errors not reported:", len(expected)) for pos, msg := range expected { - t.Errorf("%s: %s\n", fset.Position(pos), msg) + t.Errorf("%s: %s\n", fsetErrs.Position(pos), msg) } } } @@ -137,12 +139,13 @@ func checkErrors(t *testing.T, filename string, input interface{}) { return } - _, err = ParseFile(fset, filename, src, DeclarationErrors) + _, err = ParseFile(fsetErrs, filename, src, DeclarationErrors|AllErrors) found, ok := err.(scanner.ErrorList) if err != nil && !ok { t.Error(err) return } + found.RemoveMultiples() // we are expecting the following errors // (collect these after parsing a file so that it is found in the file set) @@ -153,6 +156,7 @@ func checkErrors(t *testing.T, filename string, input interface{}) { } func TestErrors(t *testing.T) { + fsetErrs = token.NewFileSet() list, err := ioutil.ReadDir(testdata) if err != nil { t.Fatal(err) diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go index 5c203a784..39affdd6b 100644 --- a/src/pkg/go/parser/interface.go +++ b/src/pkg/go/parser/interface.go @@ -52,12 +52,13 @@ func readSource(filename string, src interface{}) ([]byte, error) { type Mode uint const ( - PackageClauseOnly Mode = 1 << iota // parsing stops after package clause - ImportsOnly // parsing stops after import declarations - ParseComments // parse comments and add them to AST - Trace // print a trace of parsed productions - DeclarationErrors // report declaration errors - SpuriousErrors // report all (not just the first) errors per line + PackageClauseOnly Mode = 1 << iota // stop parsing after package clause + ImportsOnly // stop parsing after import declarations + ParseComments // parse comments and add them to AST + Trace // print a trace of parsed productions + DeclarationErrors // report declaration errors + SpuriousErrors // same as AllErrors, for backward-compatibility + AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines) ) // ParseFile parses the source code of a single Go source file and returns @@ -79,26 +80,39 @@ const ( // representing the fragments of erroneous source code). Multiple errors // are returned via a scanner.ErrorList which is sorted by file position. // -func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (*ast.File, error) { +func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) { // get source text, err := readSource(filename, src) if err != nil { return nil, err } - // parse source var p parser - p.init(fset, filename, text, mode) - f := p.parseFile() + defer func() { + if e := recover(); e != nil { + _ = e.(bailout) // re-panics if it's not a bailout + } + + // set result values + if f == nil { + // source is not a valid Go source file - satisfy + // ParseFile API and return a valid (but) empty + // *ast.File + f = &ast.File{ + Name: new(ast.Ident), + Scope: ast.NewScope(nil), + } + } - // sort errors - if p.mode&SpuriousErrors == 0 { - p.errors.RemoveMultiples() - } else { p.errors.Sort() - } + err = p.errors.Err() + }() + + // parse source + p.init(fset, filename, text, mode) + f = p.parseFile() - return f, p.errors.Err() + return } // ParseDir calls ParseFile for the files in the directory specified by path and @@ -149,7 +163,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m // ParseExpr is a convenience function for obtaining the AST of an expression x. // The position information recorded in the AST is undefined. -// +// func ParseExpr(x string) (ast.Expr, error) { // parse x within the context of a complete package for correct scopes; // use //line directive for correct positions in error messages and put diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go index 20e505d97..a021a5abe 100644 --- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -28,7 +28,7 @@ type parser struct { // Tracing/debugging mode Mode // parsing mode trace bool // == (mode & Trace != 0) - indent uint // indentation used for tracing output + indent int // indentation used for tracing output // Comments comments []*ast.CommentGroup @@ -56,7 +56,7 @@ type parser struct { unresolved []*ast.Ident // unresolved identifiers imports []*ast.ImportSpec // list of imports - // Label scope + // Label scopes // (maintained by open/close LabelScope) labelScope *ast.Scope // label scope for current function targetStack [][]*ast.Ident // stack of unresolved labels @@ -75,14 +75,6 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently) p.next() - - // set up the pkgScope here (as opposed to in parseFile) because - // there are other parser entry points (ParseExpr, etc.) - p.openScope() - p.pkgScope = p.topScope - - // for the same reason, set up a label scope - p.openLabelScope() } // ---------------------------------------------------------------------------- @@ -157,7 +149,7 @@ func (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr) { } } } else { - p.errorExpected(x.Pos(), "identifier") + p.errorExpected(x.Pos(), "identifier on left side of :=") } } if n == 0 && p.mode&DeclarationErrors != 0 { @@ -170,7 +162,12 @@ func (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr) { // internal consistency. var unresolved = new(ast.Object) -func (p *parser) resolve(x ast.Expr) { +// If x is an identifier, tryResolve attempts to resolve x by looking up +// the object it denotes. If no object is found and collectUnresolved is +// set, x is marked as unresolved and collected in the list of unresolved +// identifiers. +// +func (p *parser) tryResolve(x ast.Expr, collectUnresolved bool) { // nothing to do if x is not an identifier or the blank identifier ident, _ := x.(*ast.Ident) if ident == nil { @@ -191,23 +188,30 @@ func (p *parser) resolve(x ast.Expr) { // must be found either in the file scope, package scope // (perhaps in another file), or universe scope --- collect // them so that they can be resolved later - ident.Obj = unresolved - p.unresolved = append(p.unresolved, ident) + if collectUnresolved { + ident.Obj = unresolved + p.unresolved = append(p.unresolved, ident) + } +} + +func (p *parser) resolve(x ast.Expr) { + p.tryResolve(x, true) } // ---------------------------------------------------------------------------- // Parsing support func (p *parser) printTrace(a ...interface{}) { - const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + - ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " - const n = uint(len(dots)) + const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + const n = len(dots) pos := p.file.Position(p.pos) fmt.Printf("%5d:%3d: ", pos.Line, pos.Column) i := 2 * p.indent - for ; i > n; i -= n { + for i > n { fmt.Print(dots) + i -= n } + // i <= n fmt.Print(dots[0:i]) fmt.Println(a...) } @@ -218,7 +222,7 @@ func trace(p *parser, msg string) *parser { return p } -// Usage pattern: defer un(trace(p, "...")); +// Usage pattern: defer un(trace(p, "...")) func un(p *parser) { p.indent-- p.printTrace(")") @@ -304,14 +308,14 @@ func (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline func (p *parser) next() { p.leadComment = nil p.lineComment = nil - line := p.file.Line(p.pos) // current line + prev := p.pos p.next0() if p.tok == token.COMMENT { var comment *ast.CommentGroup var endline int - if p.file.Line(p.pos) == line { + if p.file.Line(p.pos) == p.file.Line(prev) { // The comment is on same line as the previous token; it // cannot be a lead comment but may be a line comment. comment, endline = p.consumeCommentGroup(0) @@ -336,8 +340,26 @@ func (p *parser) next() { } } +// A bailout panic is raised to indicate early termination. +type bailout struct{} + func (p *parser) error(pos token.Pos, msg string) { - p.errors.Add(p.file.Position(pos), msg) + epos := p.file.Position(pos) + + // If AllErrors is not set, discard errors reported on the same line + // as the last recorded error and stop parsing if there are more than + // 10 errors. + if p.mode&AllErrors == 0 { + n := len(p.errors) + if n > 0 && p.errors[n-1].Pos.Line == epos.Line { + return // discard - likely a spurious error + } + if n > 10 { + panic(bailout{}) + } + } + + p.errors.Add(epos, msg) } func (p *parser) errorExpected(pos token.Pos, msg string) { @@ -585,14 +607,15 @@ func (p *parser) parseTypeName() ast.Expr { return ident } -func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr { +func (p *parser) parseArrayType() ast.Expr { if p.trace { defer un(trace(p, "ArrayType")) } lbrack := p.expect(token.LBRACK) var len ast.Expr - if ellipsisOk && p.tok == token.ELLIPSIS { + // always permit ellipsis for more fault-tolerant parsing + if p.tok == token.ELLIPSIS { len = &ast.Ellipsis{Ellipsis: p.pos} p.next() } else if p.tok != token.RBRACK { @@ -704,7 +727,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr { if isParam && p.tok == token.ELLIPSIS { pos := p.pos p.next() - typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message + typ := p.tryIdentOrType() // don't use parseType so we can provide better error message if typ != nil { p.resolve(typ) } else { @@ -713,7 +736,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr { } return &ast.Ellipsis{Ellipsis: pos, Elt: typ} } - return p.tryIdentOrType(false) + return p.tryIdentOrType() } // If the result is an identifier, it is not resolved. @@ -931,29 +954,31 @@ func (p *parser) parseChanType() *ast.ChanType { pos := p.pos dir := ast.SEND | ast.RECV + var arrow token.Pos if p.tok == token.CHAN { p.next() if p.tok == token.ARROW { + arrow = p.pos p.next() dir = ast.SEND } } else { - p.expect(token.ARROW) + arrow = p.expect(token.ARROW) p.expect(token.CHAN) dir = ast.RECV } value := p.parseType() - return &ast.ChanType{Begin: pos, Dir: dir, Value: value} + return &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value} } // If the result is an identifier, it is not resolved. -func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr { +func (p *parser) tryIdentOrType() ast.Expr { switch p.tok { case token.IDENT: return p.parseTypeName() case token.LBRACK: - return p.parseArrayType(ellipsisOk) + return p.parseArrayType() case token.STRUCT: return p.parseStructType() case token.MUL: @@ -980,7 +1005,7 @@ func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr { } func (p *parser) tryType() ast.Expr { - typ := p.tryIdentOrType(false) + typ := p.tryIdentOrType() if typ != nil { p.resolve(typ) } @@ -1088,7 +1113,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { return p.parseFuncTypeOrLit() } - if typ := p.tryIdentOrType(true); typ != nil { + if typ := p.tryIdentOrType(); typ != nil { // could be type for composite literal or conversion _, isIdent := typ.(*ast.Ident) assert(!isIdent, "type cannot be identifier") @@ -1193,14 +1218,35 @@ func (p *parser) parseElement(keyOk bool) ast.Expr { return p.parseLiteralValue(nil) } - x := p.checkExpr(p.parseExpr(keyOk)) // don't resolve if map key + // Because the parser doesn't know the composite literal type, it cannot + // know if a key that's an identifier is a struct field name or a name + // denoting a value. The former is not resolved by the parser or the + // resolver. + // + // Instead, _try_ to resolve such a key if possible. If it resolves, + // it a) has correctly resolved, or b) incorrectly resolved because + // the key is a struct field with a name matching another identifier. + // In the former case we are done, and in the latter case we don't + // care because the type checker will do a separate field lookup. + // + // If the key does not resolve, it a) must be defined at the top + // level in another file of the same package, the universe scope, or be + // undeclared; or b) it is a struct field. In the former case, the type + // checker can do a top-level lookup, and in the latter case it will do + // a separate field lookup. + x := p.checkExpr(p.parseExpr(keyOk)) if keyOk { if p.tok == token.COLON { colon := p.pos p.next() + // Try to resolve the key but don't collect it + // as unresolved identifier if it fails so that + // we don't get (possibly false) errors about + // undeclared names. + p.tryResolve(x, false) return &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseElement(false)} } - p.resolve(x) // not a map key + p.resolve(x) // not a key } return x @@ -1404,16 +1450,49 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr { case token.ARROW: // channel type or receive expression - pos := p.pos + arrow := p.pos p.next() - if p.tok == token.CHAN { - p.next() - value := p.parseType() - return &ast.ChanType{Begin: pos, Dir: ast.RECV, Value: value} - } + + // If the next token is token.CHAN we still don't know if it + // is a channel type or a receive operation - we only know + // once we have found the end of the unary expression. There + // are two cases: + // + // <- type => (<-type) must be channel type + // <- expr => <-(expr) is a receive from an expression + // + // In the first case, the arrow must be re-associated with + // the channel type parsed already: + // + // <- (chan type) => (<-chan type) + // <- (chan<- type) => (<-chan (<-type)) x := p.parseUnaryExpr(false) - return &ast.UnaryExpr{OpPos: pos, Op: token.ARROW, X: p.checkExpr(x)} + + // determine which case we have + if typ, ok := x.(*ast.ChanType); ok { + // (<-type) + + // re-associate position info and <- + dir := ast.SEND + for ok && dir == ast.SEND { + if typ.Dir == ast.RECV { + // error: (<-type) is (<-(<-chan T)) + p.errorExpected(typ.Arrow, "'chan'") + } + arrow, typ.Begin, typ.Arrow = typ.Arrow, arrow, arrow + dir, typ.Dir = typ.Dir, ast.RECV + typ, ok = typ.Value.(*ast.ChanType) + } + if dir == ast.SEND { + p.errorExpected(arrow, "channel type") + } + + return x + } + + // <-(expr) + return &ast.UnaryExpr{OpPos: arrow, Op: token.ARROW, X: p.checkExpr(x)} case token.MUL: // pointer type or unary "*" expression @@ -1774,7 +1853,7 @@ func (p *parser) parseSwitchStmt() ast.Stmt { // // switch t := 0; t := x.(T) { ... } // - // (this code is not valid Go because the first t will + // (this code is not valid Go because the first t // cannot be accessed and thus is never used, the extra // scope is needed for the correct error message). // @@ -2012,7 +2091,7 @@ func (p *parser) parseStmt() (s ast.Stmt) { // ---------------------------------------------------------------------------- // Declarations -type parseSpecFunction func(p *parser, doc *ast.CommentGroup, iota int) ast.Spec +type parseSpecFunction func(p *parser, doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec func isValidImport(lit string) bool { const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" @@ -2025,7 +2104,7 @@ func isValidImport(lit string) bool { return s != "" } -func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { +func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec { if p.trace { defer un(trace(p, "ImportSpec")) } @@ -2063,15 +2142,15 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { return spec } -func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec { +func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec { if p.trace { - defer un(trace(p, "ConstSpec")) + defer un(trace(p, keyword.String()+"Spec")) } idents := p.parseIdentList() typ := p.tryType() var values []ast.Expr - if typ != nil || p.tok == token.ASSIGN || iota == 0 { + if p.tok == token.ASSIGN || keyword == token.CONST && (typ != nil || iota == 0) || keyword == token.VAR && typ == nil { p.expect(token.ASSIGN) values = p.parseRhsList() } @@ -2088,12 +2167,16 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec { Values: values, Comment: p.lineComment, } - p.declare(spec, iota, p.topScope, ast.Con, idents...) + kind := ast.Con + if keyword == token.VAR { + kind = ast.Var + } + p.declare(spec, iota, p.topScope, kind, idents...) return spec } -func parseTypeSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { +func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec { if p.trace { defer un(trace(p, "TypeSpec")) } @@ -2114,36 +2197,6 @@ func parseTypeSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { return spec } -func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec { - if p.trace { - defer un(trace(p, "VarSpec")) - } - - idents := p.parseIdentList() - typ := p.tryType() - var values []ast.Expr - if typ == nil || p.tok == token.ASSIGN { - p.expect(token.ASSIGN) - values = p.parseRhsList() - } - p.expectSemi() // call before accessing p.linecomment - - // Go spec: The scope of a constant or variable identifier declared inside - // a function begins at the end of the ConstSpec or VarSpec and ends at - // the end of the innermost containing block. - // (Global identifiers are resolved in a separate phase after parsing.) - spec := &ast.ValueSpec{ - Doc: doc, - Names: idents, - Type: typ, - Values: values, - Comment: p.lineComment, - } - p.declare(spec, nil, p.topScope, ast.Var, idents...) - - return spec -} - func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl { if p.trace { defer un(trace(p, "GenDecl("+keyword.String()+")")) @@ -2157,12 +2210,12 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen lparen = p.pos p.next() for iota := 0; p.tok != token.RPAREN && p.tok != token.EOF; iota++ { - list = append(list, f(p, p.leadComment, iota)) + list = append(list, f(p, p.leadComment, keyword, iota)) } rparen = p.expect(token.RPAREN) p.expectSemi() } else { - list = append(list, f(p, nil, 0)) + list = append(list, f(p, nil, keyword, 0)) } return &ast.GenDecl{ @@ -2262,14 +2315,11 @@ func (p *parser) parseDecl(sync func(*parser)) ast.Decl { var f parseSpecFunction switch p.tok { - case token.CONST: - f = parseConstSpec + case token.CONST, token.VAR: + f = (*parser).parseValueSpec case token.TYPE: - f = parseTypeSpec - - case token.VAR: - f = parseVarSpec + f = (*parser).parseTypeSpec case token.FUNC: return p.parseFuncDecl() @@ -2292,6 +2342,12 @@ func (p *parser) parseFile() *ast.File { defer un(trace(p, "File")) } + // Don't bother parsing the rest if we had errors scanning the first token. + // Likely not a Go source file at all. + if p.errors.Len() != 0 { + return nil + } + // package clause doc := p.leadComment pos := p.expect(token.PACKAGE) @@ -2303,15 +2359,19 @@ func (p *parser) parseFile() *ast.File { } p.expectSemi() - var decls []ast.Decl - - // Don't bother parsing the rest if we had errors already. + // Don't bother parsing the rest if we had errors parsing the package clause. // Likely not a Go source file at all. + if p.errors.Len() != 0 { + return nil + } - if p.errors.Len() == 0 && p.mode&PackageClauseOnly == 0 { + p.openScope() + p.pkgScope = p.topScope + var decls []ast.Decl + if p.mode&PackageClauseOnly == 0 { // import decls for p.tok == token.IMPORT { - decls = append(decls, p.parseGenDecl(token.IMPORT, parseImportSpec)) + decls = append(decls, p.parseGenDecl(token.IMPORT, (*parser).parseImportSpec)) } if p.mode&ImportsOnly == 0 { @@ -2321,8 +2381,9 @@ func (p *parser) parseFile() *ast.File { } } } - - assert(p.topScope == p.pkgScope, "imbalanced scopes") + p.closeScope() + assert(p.topScope == nil, "unbalanced scopes") + assert(p.labelScope == nil, "unbalanced label scopes") // resolve global identifiers within the same file i := 0 diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go index 1b7a41b1b..1960377b0 100644 --- a/src/pkg/go/parser/parser_test.go +++ b/src/pkg/go/parser/parser_test.go @@ -135,6 +135,53 @@ func TestVarScope(t *testing.T) { } } +func TestObjects(t *testing.T) { + const src = ` +package p +import fmt "fmt" +const pi = 3.14 +type T struct{} +var x int +func f() { L: } +` + + f, err := ParseFile(fset, "", src, 0) + if err != nil { + t.Fatal(err) + } + + objects := map[string]ast.ObjKind{ + "p": ast.Bad, // not in a scope + "fmt": ast.Bad, // not resolved yet + "pi": ast.Con, + "T": ast.Typ, + "x": ast.Var, + "int": ast.Bad, // not resolved yet + "f": ast.Fun, + "L": ast.Lbl, + } + + ast.Inspect(f, func(n ast.Node) bool { + if ident, ok := n.(*ast.Ident); ok { + obj := ident.Obj + if obj == nil { + if objects[ident.Name] != ast.Bad { + t.Errorf("no object for %s", ident.Name) + } + return true + } + if obj.Name != ident.Name { + t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name) + } + kind := objects[ident.Name] + if obj.Kind != kind { + t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind) + } + } + return true + }) +} + func TestUnresolved(t *testing.T) { f, err := ParseFile(fset, "", ` package p diff --git a/src/pkg/go/parser/performance_test.go b/src/pkg/go/parser/performance_test.go new file mode 100644 index 000000000..f2732c0e2 --- /dev/null +++ b/src/pkg/go/parser/performance_test.go @@ -0,0 +1,30 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package parser + +import ( + "go/token" + "io/ioutil" + "testing" +) + +var src = readFile("parser.go") + +func readFile(filename string) []byte { + data, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) + } + return data +} + +func BenchmarkParse(b *testing.B) { + b.SetBytes(int64(len(src))) + for i := 0; i < b.N; i++ { + if _, err := ParseFile(token.NewFileSet(), "", src, ParseComments); err != nil { + b.Fatalf("benchmark failed due to parse error: %s", err) + } + } +} diff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go index 238492bf3..c62f7e050 100644 --- a/src/pkg/go/parser/short_test.go +++ b/src/pkg/go/parser/short_test.go @@ -13,8 +13,10 @@ var valids = []string{ `package p;`, `package p; import "fmt"; func f() { fmt.Println("Hello, World!") };`, `package p; func f() { if f(T{}) {} };`, - `package p; func f() { _ = (<-chan int)(x) };`, - `package p; func f() { _ = (<-chan <-chan int)(x) };`, + `package p; func f() { _ = <-chan int(nil) };`, + `package p; func f() { _ = (<-chan int)(nil) };`, + `package p; func f() { _ = (<-chan <-chan int)(nil) };`, + `package p; func f() { _ = <-chan <-chan <-chan <-chan <-int(nil) };`, `package p; func f(func() func() func());`, `package p; func f(...T);`, `package p; func f(float, ...int);`, @@ -64,8 +66,11 @@ var invalids = []string{ `package p; var a = []int{[ /* ERROR "expected expression" */ ]int};`, `package p; var a = ( /* ERROR "expected expression" */ []int);`, `package p; var a = a[[ /* ERROR "expected expression" */ ]int:[]int];`, - `package p; var a = <- /* ERROR "expected expression" */ chan int;`, - `package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`, + `package p; var a = <- /* ERROR "expected expression" */ chan int;`, + `package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`, + `package p; func f() { _ = (<-<- /* ERROR "expected 'chan'" */ chan int)(nil) };`, + `package p; func f() { _ = (<-chan<-chan<-chan<-chan<-chan<- /* ERROR "expected channel type" */ int)(nil) };`, + `package p; func f() { var t []int; t /* ERROR "expected identifier on left side of :=" */ [0] := 0 };`, } func TestInvalid(t *testing.T) { diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go index f13f9a5a8..ee0bbf1ed 100644 --- a/src/pkg/go/printer/nodes.go +++ b/src/pkg/go/printer/nodes.go @@ -83,7 +83,7 @@ func (p *printer) setComment(g *ast.CommentGroup) { // don't overwrite any pending comment in the p.comment cache // (there may be a pending comment when a line comment is // immediately followed by a lead comment with no other - // tokens inbetween) + // tokens between) if p.commentOffset == infinity { p.nextComment() // get comment ready for use } @@ -203,7 +203,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp } else { const r = 4 // threshold ratio := float64(size) / float64(prevSize) - useFF = ratio <= 1/r || r <= ratio + useFF = ratio <= 1.0/r || r <= ratio } } @@ -307,7 +307,7 @@ func (p *printer) parameters(fields *ast.FieldList) { p.print(blank) } // parameter type - p.expr(par.Type) + p.expr(stripParensAlways(par.Type)) prevLine = parLineEnd } // if the closing ")" is on a separate line from the last parameter, @@ -325,13 +325,18 @@ func (p *printer) parameters(fields *ast.FieldList) { } func (p *printer) signature(params, result *ast.FieldList) { - p.parameters(params) + if params != nil { + p.parameters(params) + } else { + p.print(token.LPAREN, token.RPAREN) + } n := result.NumFields() if n > 0 { + // result != nil p.print(blank) if n == 1 && result.List[0].Names == nil { // single anonymous result; no ()'s - p.expr(result.List[0].Type) + p.expr(stripParensAlways(result.List[0].Type)) return } p.parameters(result) @@ -725,7 +730,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { case *ast.FuncLit: p.expr(x.Type) - p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true) + p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body) case *ast.ParenExpr: if _, hasParens := x.X.(*ast.ParenExpr); hasParens { @@ -786,7 +791,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { if len(x.Args) > 1 { depth++ } - p.expr1(x.Fun, token.HighestPrec, depth) + if _, ok := x.Fun.(*ast.FuncType); ok { + // conversions to literal function types require parentheses around the type + p.print(token.LPAREN) + p.expr1(x.Fun, token.HighestPrec, depth) + p.print(token.RPAREN) + } else { + p.expr1(x.Fun, token.HighestPrec, depth) + } p.print(x.Lparen, token.LPAREN) if x.Ellipsis.IsValid() { p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis) @@ -848,9 +860,9 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { case ast.SEND | ast.RECV: p.print(token.CHAN) case ast.RECV: - p.print(token.ARROW, token.CHAN) + p.print(token.ARROW, token.CHAN) // x.Arrow and x.Pos() are the same case ast.SEND: - p.print(token.CHAN, token.ARROW) + p.print(token.CHAN, x.Arrow, token.ARROW) } p.print(blank) p.expr(x.Value) @@ -877,30 +889,38 @@ func (p *printer) expr(x ast.Expr) { // Print the statement list indented, but without a newline after the last statement. // Extra line breaks between statements in the source are respected but at most one // empty line is printed between statements. -func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) { - // TODO(gri): fix _indent code - if _indent > 0 { +func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) { + if nindent > 0 { p.print(indent) } multiLine := false - for i, s := range list { - // _indent == 0 only for lists of switch/select case clauses; - // in those cases each clause is a new section - p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || _indent == 0 || multiLine) - p.stmt(s, nextIsRBrace && i == len(list)-1) - multiLine = p.isMultiLine(s) - } - if _indent > 0 { + i := 0 + for _, s := range list { + // ignore empty statements (was issue 3466) + if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty { + // _indent == 0 only for lists of switch/select case clauses; + // in those cases each clause is a new section + if len(p.output) > 0 { + // only print line break if we are not at the beginning of the output + // (i.e., we are not printing only a partial program) + p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine) + } + p.stmt(s, nextIsRBrace && i == len(list)-1) + multiLine = p.isMultiLine(s) + i++ + } + } + if nindent > 0 { p.print(unindent) } } // block prints an *ast.BlockStmt; it always spans at least two lines. -func (p *printer) block(s *ast.BlockStmt, indent int) { - p.print(s.Pos(), token.LBRACE) - p.stmtList(s.List, indent, true) - p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true) - p.print(s.Rbrace, token.RBRACE) +func (p *printer) block(b *ast.BlockStmt, nindent int) { + p.print(b.Lbrace, token.LBRACE) + p.stmtList(b.List, nindent, true) + p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true) + p.print(b.Rbrace, token.RBRACE) } func isTypeName(x ast.Expr) bool { @@ -939,6 +959,13 @@ func stripParens(x ast.Expr) ast.Expr { return x } +func stripParensAlways(x ast.Expr) ast.Expr { + if x, ok := x.(*ast.ParenExpr); ok { + return stripParensAlways(x.X) + } + return x +} + func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) { p.print(blank) needsBlank := false @@ -1405,19 +1432,19 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { return } -func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool { +// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's. +func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int { pos1 := b.Pos() pos2 := b.Rbrace if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) { // opening and closing brace are on different lines - don't make it a one-liner - return false + return maxSize + 1 } if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) { // too many statements or there is a comment inside - don't make it a one-liner - return false + return maxSize + 1 } // otherwise, estimate body size - const maxSize = 100 bodySize := 0 for i, s := range b.List { if i > 0 { @@ -1425,19 +1452,23 @@ func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool { } bodySize += p.nodeSize(s, maxSize) } - return headerSize+bodySize <= maxSize + return bodySize } -func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) { +// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following +// a header (e.g., a for-loop control clause or function signature) of given headerSize. +// If the header's and block's size are "small enough" and the block is "simple enough", +// the block is printed on the current line, without line breaks, spaced from the header +// by sep. Otherwise the block's opening "{" is printed on the current line, followed by +// lines for the block's statements and its closing "}". +// +func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) { if b == nil { return } - if p.isOneLineFunc(b, headerSize) { - sep := vtab - if isLit { - sep = blank - } + const maxSize = 100 + if headerSize+p.bodySize(b, maxSize) <= maxSize { p.print(sep, b.Lbrace, token.LBRACE) if len(b.List) > 0 { p.print(blank) @@ -1453,17 +1484,20 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) { return } - p.print(blank) + if sep != ignore { + p.print(blank) // always use blank + } p.block(b, 1) } -// distance returns the column difference between from and to if both -// are on the same line; if they are on different lines (or unknown) -// the result is infinity. -func (p *printer) distance(from0 token.Pos, to token.Position) int { - from := p.posFor(from0) - if from.IsValid() && to.IsValid() && from.Line == to.Line { - return to.Column - from.Column +// distanceFrom returns the column difference between from and p.pos (the current +// estimated position) if both are on the same line; if they are on different lines +// (or unknown) the result is infinity. +func (p *printer) distanceFrom(from token.Pos) int { + if from.IsValid() && p.pos.IsValid() { + if f := p.posFor(from); f.Line == p.pos.Line { + return p.pos.Column - f.Column + } } return infinity } @@ -1477,7 +1511,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) { } p.expr(d.Name) p.signature(d.Type.Params, d.Type.Results) - p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false) + p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body) } func (p *printer) decl(decl ast.Decl) { @@ -1507,31 +1541,35 @@ func declToken(decl ast.Decl) (tok token.Token) { return } -func (p *printer) file(src *ast.File) { - p.setComment(src.Doc) - p.print(src.Pos(), token.PACKAGE, blank) - p.expr(src.Name) - - if len(src.Decls) > 0 { - tok := token.ILLEGAL - for _, d := range src.Decls { - prev := tok - tok = declToken(d) - // if the declaration token changed (e.g., from CONST to TYPE) - // or the next declaration has documentation associated with it, - // print an empty line between top-level declarations - // (because p.linebreak is called with the position of d, which - // is past any documentation, the minimum requirement is satisfied - // even w/o the extra getDoc(d) nil-check - leave it in case the - // linebreak logic improves - there's already a TODO). +func (p *printer) declList(list []ast.Decl) { + tok := token.ILLEGAL + for _, d := range list { + prev := tok + tok = declToken(d) + // If the declaration token changed (e.g., from CONST to TYPE) + // or the next declaration has documentation associated with it, + // print an empty line between top-level declarations. + // (because p.linebreak is called with the position of d, which + // is past any documentation, the minimum requirement is satisfied + // even w/o the extra getDoc(d) nil-check - leave it in case the + // linebreak logic improves - there's already a TODO). + if len(p.output) > 0 { + // only print line break if we are not at the beginning of the output + // (i.e., we are not printing only a partial program) min := 1 if prev != tok || getDoc(d) != nil { min = 2 } p.linebreak(p.lineFor(d.Pos()), min, ignore, false) - p.decl(d) } + p.decl(d) } +} +func (p *printer) file(src *ast.File) { + p.setComment(src.Doc) + p.print(src.Pos(), token.PACKAGE, blank) + p.expr(src.Name) + p.declList(src.Decls) p.print(newline) } diff --git a/src/pkg/go/printer/performance_test.go b/src/pkg/go/printer/performance_test.go index 0c6a4e71f..5b29affcb 100644 --- a/src/pkg/go/printer/performance_test.go +++ b/src/pkg/go/printer/performance_test.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // This file implements a simple printer performance benchmark: -// go test -bench=BenchmarkPrint +// go test -bench=BenchmarkPrint package printer @@ -20,7 +20,7 @@ import ( var testfile *ast.File func testprint(out io.Writer, file *ast.File) { - if err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil { + if err := (&Config{TabIndent | UseSpaces, 8, 0}).Fprint(out, fset, file); err != nil { log.Fatalf("print error: %s", err) } } diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index a027d32da..3c8d23e65 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" "text/tabwriter" + "unicode" ) const ( @@ -164,15 +165,15 @@ func (p *printer) atLineBegin(pos token.Position) { // write indentation // use "hard" htabs - indentation columns // must not be discarded by the tabwriter - for i := 0; i < p.indent; i++ { + n := p.Config.Indent + p.indent // include base indentation + for i := 0; i < n; i++ { p.output = append(p.output, '\t') } // update positions - i := p.indent - p.pos.Offset += i - p.pos.Column += i - p.out.Column += i + p.pos.Offset += n + p.pos.Column += n + p.out.Column += n } // writeByte writes ch n times to p.output and updates p.pos. @@ -220,14 +221,6 @@ func (p *printer) writeString(pos token.Position, s string, isLit bool) { // atLineBegin updates p.pos if there's indentation, but p.pos // is the position of s. p.pos = pos - // reset state if the file changed - // (used when printing merged ASTs of different files - // e.g., the result of ast.MergePackageFiles) - if p.last.IsValid() && p.last.Filename != pos.Filename { - p.indent = 0 - p.mode = 0 - p.wsbuf = p.wsbuf[0:0] - } } if isLit { @@ -405,6 +398,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as // Split comment text into lines // (using strings.Split(text, "\n") is significantly slower for // this specific purpose, as measured with: go test -bench=Print) +// func split(text string) []string { // count lines (comment text never ends in a newline) n := 1 @@ -432,6 +426,7 @@ func split(text string) []string { // Returns true if s contains only white space // (only tabs and blanks can appear in the printer's context). +// func isBlank(s string) bool { for i := 0; i < len(s); i++ { if s[i] > ' ' { @@ -441,6 +436,7 @@ func isBlank(s string) bool { return true } +// commonPrefix returns the common prefix of a and b. func commonPrefix(a, b string) string { i := 0 for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') { @@ -449,11 +445,22 @@ func commonPrefix(a, b string) string { return a[0:i] } +// trimRight returns s with trailing whitespace removed. +func trimRight(s string) string { + return strings.TrimRightFunc(s, unicode.IsSpace) +} + +// stripCommonPrefix removes a common prefix from /*-style comment lines (unless no +// comment line is indented, all but the first line have some form of space prefix). +// The prefix is computed using heuristics such that is likely that the comment +// contents are nicely laid out after re-printing each line using the printer's +// current indentation. +// func stripCommonPrefix(lines []string) { - if len(lines) < 2 { + if len(lines) <= 1 { return // at most one line - nothing to do } - // len(lines) >= 2 + // len(lines) > 1 // The heuristic in this function tries to handle a few // common patterns of /*-style comments: Comments where @@ -479,7 +486,7 @@ func stripCommonPrefix(lines []string) { for i, line := range lines[1 : len(lines)-1] { switch { case isBlank(line): - lines[1+i] = "" // range starts at line 1 + lines[1+i] = "" // range starts with lines[1] case first: prefix = commonPrefix(line, line) first = false @@ -544,9 +551,7 @@ func stripCommonPrefix(lines []string) { } // Shorten the computed common prefix by the length of // suffix, if it is found as suffix of the prefix. - if strings.HasSuffix(prefix, string(suffix)) { - prefix = prefix[0 : len(prefix)-len(suffix)] - } + prefix = strings.TrimSuffix(prefix, string(suffix)) } } @@ -570,9 +575,9 @@ func stripCommonPrefix(lines []string) { } // Remove the common prefix from all but the first and empty lines. - for i, line := range lines[1:] { - if len(line) != 0 { - lines[1+i] = line[len(prefix):] // range starts at line 1 + for i, line := range lines { + if i > 0 && line != "" { + lines[i] = line[len(prefix):] } } } @@ -605,13 +610,26 @@ func (p *printer) writeComment(comment *ast.Comment) { // shortcut common case of //-style comments if text[1] == '/' { - p.writeString(pos, text, true) + p.writeString(pos, trimRight(text), true) return } // for /*-style comments, print line by line and let the // write function take care of the proper indentation lines := split(text) + + // The comment started in the first column but is going + // to be indented. For an idempotent result, add indentation + // to all lines such that they look like they were indented + // before - this will make sure the common prefix computation + // is the same independent of how many times formatting is + // applied (was issue 1835). + if pos.IsValid() && pos.Column == 1 && p.indent > 0 { + for i, line := range lines[1:] { + lines[1+i] = " " + line + } + } + stripCommonPrefix(lines) // write comment lines, separated by formfeed, @@ -622,7 +640,7 @@ func (p *printer) writeComment(comment *ast.Comment) { pos = p.pos } if len(line) > 0 { - p.writeString(pos, line, true) + p.writeString(pos, trimRight(line), true) } } } @@ -1012,9 +1030,9 @@ func (p *printer) printNode(node interface{}) error { case ast.Expr: p.expr(n) case ast.Stmt: - // A labeled statement will un-indent to position the - // label. Set indent to 1 so we don't get indent "underflow". - if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt { + // A labeled statement will un-indent to position the label. + // Set p.indent to 1 so we don't get indent "underflow". + if _, ok := n.(*ast.LabeledStmt); ok { p.indent = 1 } p.stmt(n, false) @@ -1022,6 +1040,17 @@ func (p *printer) printNode(node interface{}) error { p.decl(n) case ast.Spec: p.spec(n, 1, false) + case []ast.Stmt: + // A labeled statement will un-indent to position the label. + // Set p.indent to 1 so we don't get indent "underflow". + for _, s := range n { + if _, ok := s.(*ast.LabeledStmt); ok { + p.indent = 1 + } + } + p.stmtList(n, 0, false) + case []ast.Decl: + p.declList(n) case *ast.File: p.file(n) default: @@ -1140,7 +1169,7 @@ func (p *trimmer) Write(data []byte) (n int, err error) { // ---------------------------------------------------------------------------- // Public interface -// A Mode value is a set of flags (or 0). They coontrol printing. +// A Mode value is a set of flags (or 0). They control printing. type Mode uint const ( @@ -1154,6 +1183,7 @@ const ( type Config struct { Mode Mode // default: 0 Tabwidth int // default: 8 + Indent int // default: 0 (all code is indented at least by this much) } // fprint implements Fprint and takes a nodesSizes map for setting up the printer state. @@ -1198,7 +1228,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{ } // flush tabwriter, if any - if tw, _ := (output).(*tabwriter.Writer); tw != nil { + if tw, _ := output.(*tabwriter.Writer); tw != nil { err = tw.Flush() } @@ -1215,8 +1245,8 @@ type CommentedNode struct { // Fprint "pretty-prints" an AST node to output for a given configuration cfg. // Position information is interpreted relative to the file set fset. -// The node type must be *ast.File, *CommentedNode, or assignment-compatible -// to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt. +// The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt, +// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt. // func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error { return cfg.fprint(output, fset, node, make(map[ast.Node]int)) diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go index 497d671f2..8454ac12b 100644 --- a/src/pkg/go/printer/printer_test.go +++ b/src/pkg/go/printer/printer_test.go @@ -6,7 +6,9 @@ package printer import ( "bytes" + "errors" "flag" + "fmt" "go/ast" "go/parser" "go/token" @@ -25,33 +27,28 @@ var update = flag.Bool("update", false, "update golden files") var fset = token.NewFileSet() -func lineString(text []byte, i int) string { - i0 := i - for i < len(text) && text[i] != '\n' { - i++ - } - return string(text[i0:i]) -} - type checkMode uint const ( export checkMode = 1 << iota rawFormat + idempotent ) -func runcheck(t *testing.T, source, golden string, mode checkMode) { - // parse source - prog, err := parser.ParseFile(fset, source, nil, parser.ParseComments) +// format parses src, prints the corresponding AST, verifies the resulting +// src is syntactically correct, and returns the resulting src or an error +// if any. +func format(src []byte, mode checkMode) ([]byte, error) { + // parse src + f, err := parser.ParseFile(fset, "", src, parser.ParseComments) if err != nil { - t.Error(err) - return + return nil, fmt.Errorf("parse: %s\n%s", err, src) } // filter exports if necessary if mode&export != 0 { - ast.FileExports(prog) // ignore result - prog.Comments = nil // don't print comments that are not in AST + ast.FileExports(f) // ignore result + f.Comments = nil // don't print comments that are not in AST } // determine printer configuration @@ -60,17 +57,72 @@ func runcheck(t *testing.T, source, golden string, mode checkMode) { cfg.Mode |= RawFormat } - // format source + // print AST var buf bytes.Buffer - if err := cfg.Fprint(&buf, fset, prog); err != nil { - t.Error(err) + if err := cfg.Fprint(&buf, fset, f); err != nil { + return nil, fmt.Errorf("print: %s", err) } - res := buf.Bytes() - // formatted source must be valid + // make sure formated output is syntactically correct + res := buf.Bytes() if _, err := parser.ParseFile(fset, "", res, 0); err != nil { + return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes()) + } + + return res, nil +} + +// lineAt returns the line in text starting at offset offs. +func lineAt(text []byte, offs int) []byte { + i := offs + for i < len(text) && text[i] != '\n' { + i++ + } + return text[offs:i] +} + +// diff compares a and b. +func diff(aname, bname string, a, b []byte) error { + var buf bytes.Buffer // holding long error message + + // compare lengths + if len(a) != len(b) { + fmt.Fprintf(&buf, "\nlength changed: len(%s) = %d, len(%s) = %d", aname, len(a), bname, len(b)) + } + + // compare contents + line := 1 + offs := 1 + for i := 0; i < len(a) && i < len(b); i++ { + ch := a[i] + if ch != b[i] { + fmt.Fprintf(&buf, "\n%s:%d:%d: %s", aname, line, i-offs+1, lineAt(a, offs)) + fmt.Fprintf(&buf, "\n%s:%d:%d: %s", bname, line, i-offs+1, lineAt(b, offs)) + fmt.Fprintf(&buf, "\n\n") + break + } + if ch == '\n' { + line++ + offs = i + 1 + } + } + + if buf.Len() > 0 { + return errors.New(buf.String()) + } + return nil +} + +func runcheck(t *testing.T, source, golden string, mode checkMode) { + src, err := ioutil.ReadFile(source) + if err != nil { + t.Error(err) + return + } + + res, err := format(src, mode) + if err != nil { t.Error(err) - t.Logf("\n%s", res) return } @@ -89,23 +141,19 @@ func runcheck(t *testing.T, source, golden string, mode checkMode) { return } - // compare lengths - if len(res) != len(gld) { - t.Errorf("len = %d, expected %d (= len(%s))", len(res), len(gld), golden) + // formatted source and golden must be the same + if err := diff(source, golden, res, gld); err != nil { + t.Error(err) + return } - // compare contents - for i, line, offs := 0, 1, 0; i < len(res) && i < len(gld); i++ { - ch := res[i] - if ch != gld[i] { - t.Errorf("%s:%d:%d: %s", source, line, i-offs+1, lineString(res, offs)) - t.Errorf("%s:%d:%d: %s", golden, line, i-offs+1, lineString(gld, offs)) - t.Error() - return - } - if ch == '\n' { - line++ - offs = i + 1 + if mode&idempotent != 0 { + // formatting golden must be idempotent + // (This is very difficult to achieve in general and for now + // it is only checked for files explicitly marked as such.) + res, err = format(gld, mode) + if err := diff(golden, fmt.Sprintf("format(%s)", golden), gld, res); err != nil { + t.Errorf("golden is not idempotent: %s", err) } } } @@ -142,15 +190,16 @@ type entry struct { // Use go test -update to create/update the respective golden files. var data = []entry{ - {"empty.input", "empty.golden", 0}, + {"empty.input", "empty.golden", idempotent}, {"comments.input", "comments.golden", 0}, {"comments.input", "comments.x", export}, - {"linebreaks.input", "linebreaks.golden", 0}, - {"expressions.input", "expressions.golden", 0}, - {"expressions.input", "expressions.raw", rawFormat}, + {"comments2.input", "comments2.golden", idempotent}, + {"linebreaks.input", "linebreaks.golden", idempotent}, + {"expressions.input", "expressions.golden", idempotent}, + {"expressions.input", "expressions.raw", rawFormat | idempotent}, {"declarations.input", "declarations.golden", 0}, {"statements.input", "statements.golden", 0}, - {"slow.input", "slow.golden", 0}, + {"slow.input", "slow.golden", idempotent}, } func TestFiles(t *testing.T) { @@ -248,7 +297,7 @@ func testComment(t *testing.T, f *ast.File, srclen int, comment *ast.Comment) { } } -// Verify that the printer produces always produces a correct program +// Verify that the printer produces a correct program // even if the position information of comments introducing newlines // is incorrect. func TestBadComments(t *testing.T) { @@ -385,28 +434,136 @@ func (t *t) foo(a, b, c int) int { } } -// TextX is a skeleton test that can be filled in for debugging one-off cases. -// Do not remove. -func TestX(t *testing.T) { - const src = ` -package p -func _() {} -` - // parse original - f, err := parser.ParseFile(fset, "src", src, parser.ParseComments) +var decls = []string{ + `import "fmt"`, + "const pi = 3.1415\nconst e = 2.71828\n\nvar x = pi", + "func sum(x, y int) int\t{ return x + y }", +} + +func TestDeclLists(t *testing.T) { + for _, src := range decls { + file, err := parser.ParseFile(fset, "", "package p;"+src, parser.ParseComments) + if err != nil { + panic(err) // error in test + } + + var buf bytes.Buffer + err = Fprint(&buf, fset, file.Decls) // only print declarations + if err != nil { + panic(err) // error in test + } + + out := buf.String() + if out != src { + t.Errorf("\ngot : %q\nwant: %q\n", out, src) + } + } +} + +var stmts = []string{ + "i := 0", + "select {}\nvar a, b = 1, 2\nreturn a + b", + "go f()\ndefer func() {}()", +} + +func TestStmtLists(t *testing.T) { + for _, src := range stmts { + file, err := parser.ParseFile(fset, "", "package p; func _() {"+src+"}", parser.ParseComments) + if err != nil { + panic(err) // error in test + } + + var buf bytes.Buffer + err = Fprint(&buf, fset, file.Decls[0].(*ast.FuncDecl).Body.List) // only print statements + if err != nil { + panic(err) // error in test + } + + out := buf.String() + if out != src { + t.Errorf("\ngot : %q\nwant: %q\n", out, src) + } + } +} + +func TestBaseIndent(t *testing.T) { + // The testfile must not contain multi-line raw strings since those + // are not indented (because their values must not change) and make + // this test fail. + const filename = "printer.go" + src, err := ioutil.ReadFile(filename) if err != nil { - t.Fatal(err) + panic(err) // error in test + } + + file, err := parser.ParseFile(fset, filename, src, 0) + if err != nil { + panic(err) // error in test + } + + var buf bytes.Buffer + for indent := 0; indent < 4; indent++ { + buf.Reset() + (&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file) + // all code must be indented by at least 'indent' tabs + lines := bytes.Split(buf.Bytes(), []byte{'\n'}) + for i, line := range lines { + if len(line) == 0 { + continue // empty lines don't have indentation + } + n := 0 + for j, b := range line { + if b != '\t' { + // end of indentation + n = j + break + } + } + if n < indent { + t.Errorf("line %d: got only %d tabs; want at least %d: %q", i, n, indent, line) + } + } + } +} + +// TestFuncType tests that an ast.FuncType with a nil Params field +// can be printed (per go/ast specification). Test case for issue 3870. +func TestFuncType(t *testing.T) { + src := &ast.File{ + Name: &ast.Ident{Name: "p"}, + Decls: []ast.Decl{ + &ast.FuncDecl{ + Name: &ast.Ident{Name: "f"}, + Type: &ast.FuncType{}, + }, + }, } - // pretty-print original var buf bytes.Buffer - if err = (&Config{Mode: UseSpaces, Tabwidth: 8}).Fprint(&buf, fset, f); err != nil { + if err := Fprint(&buf, fset, src); err != nil { t.Fatal(err) } + got := buf.String() - // parse pretty printed original - if _, err := parser.ParseFile(fset, "", buf.Bytes(), 0); err != nil { - t.Fatalf("%s\n%s", err, buf.Bytes()) + const want = `package p + +func f() +` + + if got != want { + t.Fatalf("got:\n%s\nwant:\n%s\n", got, want) } +} +// TextX is a skeleton test that can be filled in for debugging one-off cases. +// Do not remove. +func TestX(t *testing.T) { + const src = ` +package p +func _() {} +` + _, err := format([]byte(src), 0) + if err != nil { + t.Error(err) + } } diff --git a/src/pkg/go/printer/testdata/comments.golden b/src/pkg/go/printer/testdata/comments.golden index d9aa2d82f..610a42a68 100644 --- a/src/pkg/go/printer/testdata/comments.golden +++ b/src/pkg/go/printer/testdata/comments.golden @@ -529,7 +529,7 @@ func _() { } func _() { - var a = []int{1, 2}// jasldf + var a = []int{1, 2}// jasldf _ = a } @@ -626,4 +626,13 @@ func _() { var lflag bool // -l - disable line directives } +// Trailing white space in comments should be trimmed +func _() { + // This comment has 4 blanks following that should be trimmed: + /* Each line of this comment has blanks or tabs following that should be trimmed: + line 2: + line 3: + */ +} + /* This comment is the last entry in this file. It must be printed and should be followed by a newline */ diff --git a/src/pkg/go/printer/testdata/comments.input b/src/pkg/go/printer/testdata/comments.input index 6084b3fe4..d121dd4be 100644 --- a/src/pkg/go/printer/testdata/comments.input +++ b/src/pkg/go/printer/testdata/comments.input @@ -534,7 +534,7 @@ func _() { } func _() { - var a = []int{1, 2, // jasldf + var a = []int{1, 2, // jasldf } _ = a } @@ -630,5 +630,13 @@ var vflag string // -v [y.output] - y.output file var lflag bool // -l - disable line directives } +// Trailing white space in comments should be trimmed +func _() { +// This comment has 4 blanks following that should be trimmed: +/* Each line of this comment has blanks or tabs following that should be trimmed: + line 2: + line 3: +*/ +} /* This comment is the last entry in this file. It must be printed and should be followed by a newline */ diff --git a/src/pkg/go/printer/testdata/comments2.golden b/src/pkg/go/printer/testdata/comments2.golden new file mode 100644 index 000000000..d3b50bf3e --- /dev/null +++ b/src/pkg/go/printer/testdata/comments2.golden @@ -0,0 +1,79 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is a package for testing comment placement by go/printer. +// +package main + +// Test cases for idempotent comment formatting (was issue 1835). +/* +c1a +*/ +/* + c1b +*/ +/* foo +c1c +*/ +/* foo + c1d +*/ +/* +c1e +foo */ +/* + c1f + foo */ + +func f() { + /* + c2a + */ + /* + c2b + */ + /* foo + c2c + */ + /* foo + c2d + */ + /* + c2e + foo */ + /* + c2f + foo */ +} + +func g() { + /* + c3a + */ + /* + c3b + */ + /* foo + c3c + */ + /* foo + c3d + */ + /* + c3e + foo */ + /* + c3f + foo */ +} + +// Test case taken literally from issue 1835. +func main() { + /* + prints test 5 times + */ + for i := 0; i < 5; i++ { + println("test") + } +} diff --git a/src/pkg/go/printer/testdata/comments2.input b/src/pkg/go/printer/testdata/comments2.input new file mode 100644 index 000000000..6f8c85c94 --- /dev/null +++ b/src/pkg/go/printer/testdata/comments2.input @@ -0,0 +1,79 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is a package for testing comment placement by go/printer. +// +package main + +// Test cases for idempotent comment formatting (was issue 1835). +/* +c1a +*/ +/* + c1b +*/ +/* foo +c1c +*/ +/* foo + c1d +*/ +/* +c1e +foo */ +/* + c1f + foo */ + +func f() { +/* +c2a +*/ +/* + c2b +*/ +/* foo +c2c +*/ +/* foo + c2d +*/ +/* +c2e +foo */ +/* + c2f + foo */ +} + +func g() { +/* +c3a +*/ +/* + c3b +*/ +/* foo +c3c +*/ +/* foo + c3d +*/ +/* +c3e +foo */ +/* + c3f + foo */ +} + +// Test case taken literally from issue 1835. +func main() { +/* +prints test 5 times +*/ + for i := 0; i < 5; i++ { + println("test") + } +} \ No newline at end of file diff --git a/src/pkg/go/printer/testdata/declarations.golden b/src/pkg/go/printer/testdata/declarations.golden index 71ed32ed1..0ad72d349 100644 --- a/src/pkg/go/printer/testdata/declarations.golden +++ b/src/pkg/go/printer/testdata/declarations.golden @@ -654,6 +654,35 @@ var _ = map[int]int{ abcde: a, // align with previous line } +// alignment of map composite entries: test cases from issue 3965 +// aligned +var _ = T1{ + a: x, + b: y, + cccccccccccccccccccc: z, +} + +// not aligned +var _ = T2{ + a: x, + b: y, + ccccccccccccccccccccc: z, +} + +// aligned +var _ = T3{ + aaaaaaaaaaaaaaaaaaaa: x, + b: y, + c: z, +} + +// not aligned +var _ = T4{ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: x, + b: y, + c: z, +} + func _() { var _ = T{ a, // must introduce trailing comma @@ -858,3 +887,28 @@ type _ interface { r string, x ...int) } + +// omit superfluous parentheses in parameter lists +func _(int) +func _(int) +func _(x int) +func _(x int) +func _(x, y int) +func _(x, y int) + +func _() int +func _() int +func _() int + +func _() (x int) +func _() (x int) +func _() (x int) + +// special cases: some channel types require parentheses +func _(x chan (<-chan int)) +func _(x chan (<-chan int)) +func _(x chan (<-chan int)) + +func _(x chan<- (chan int)) +func _(x chan<- (chan int)) +func _(x chan<- (chan int)) diff --git a/src/pkg/go/printer/testdata/declarations.input b/src/pkg/go/printer/testdata/declarations.input index d74cff25d..455c0c6c1 100644 --- a/src/pkg/go/printer/testdata/declarations.input +++ b/src/pkg/go/printer/testdata/declarations.input @@ -667,6 +667,35 @@ var _ = map[int]int{ abcde: a, // align with previous line } +// alignment of map composite entries: test cases from issue 3965 +// aligned +var _ = T1{ + a: x, + b: y, + cccccccccccccccccccc: z, +} + +// not aligned +var _ = T2{ + a: x, + b: y, + ccccccccccccccccccccc: z, +} + +// aligned +var _ = T3{ + aaaaaaaaaaaaaaaaaaaa: x, + b: y, + c: z, +} + +// not aligned +var _ = T4{ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: x, + b: y, + c: z, +} + func _() { var _ = T{ @@ -867,3 +896,28 @@ p, q, r string, x ...int) } + +// omit superfluous parentheses in parameter lists +func _((int)) +func _((((((int)))))) +func _(x (int)) +func _(x (((((int)))))) +func _(x, y (int)) +func _(x, y (((((int)))))) + +func _() (int) +func _() ((int)) +func _() ((((((int)))))) + +func _() (x int) +func _() (x (int)) +func _() (x (((((int)))))) + +// special cases: some channel types require parentheses +func _(x chan(<-chan int)) +func _(x (chan(<-chan int))) +func _(x ((((chan(<-chan int)))))) + +func _(x chan<-(chan int)) +func _(x (chan<-(chan int))) +func _(x ((((chan<-(chan int)))))) diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden index 45fa4d97a..4291c557c 100644 --- a/src/pkg/go/printer/testdata/expressions.golden +++ b/src/pkg/go/printer/testdata/expressions.golden @@ -647,3 +647,18 @@ func _() { a..., ) } + +// Literal function types in conversions must be parenthesized; +// for now go/parser accepts the unparenthesized form where it +// is non-ambiguous. +func _() { + // these conversions should be rewritten to look + // the same as the parenthesized conversions below + _ = (func())(nil) + _ = (func(x int) float)(nil) + _ = (func() func() func())(nil) + + _ = (func())(nil) + _ = (func(x int) float)(nil) + _ = (func() func() func())(nil) +} diff --git a/src/pkg/go/printer/testdata/expressions.input b/src/pkg/go/printer/testdata/expressions.input index f545c6605..1ec12a050 100644 --- a/src/pkg/go/printer/testdata/expressions.input +++ b/src/pkg/go/printer/testdata/expressions.input @@ -676,3 +676,18 @@ func _() { a..., ) } + +// Literal function types in conversions must be parenthesized; +// for now go/parser accepts the unparenthesized form where it +// is non-ambiguous. +func _() { + // these conversions should be rewritten to look + // the same as the parenthesized conversions below + _ = func()()(nil) + _ = func(x int)(float)(nil) + _ = func() func() func()()(nil) + + _ = (func()())(nil) + _ = (func(x int)(float))(nil) + _ = (func() func() func()())(nil) +} diff --git a/src/pkg/go/printer/testdata/expressions.raw b/src/pkg/go/printer/testdata/expressions.raw index 87a4b0083..062900e07 100644 --- a/src/pkg/go/printer/testdata/expressions.raw +++ b/src/pkg/go/printer/testdata/expressions.raw @@ -647,3 +647,18 @@ func _() { a..., ) } + +// Literal function types in conversions must be parenthesized; +// for now go/parser accepts the unparenthesized form where it +// is non-ambiguous. +func _() { + // these conversions should be rewritten to look + // the same as the parenthesized conversions below + _ = (func())(nil) + _ = (func(x int) float)(nil) + _ = (func() func() func())(nil) + + _ = (func())(nil) + _ = (func(x int) float)(nil) + _ = (func() func() func())(nil) +} diff --git a/src/pkg/go/printer/testdata/statements.golden b/src/pkg/go/printer/testdata/statements.golden index 4d70617bf..3b298f95e 100644 --- a/src/pkg/go/printer/testdata/statements.golden +++ b/src/pkg/go/printer/testdata/statements.golden @@ -241,7 +241,7 @@ func _() { } } -// Formatting of for-statement headers. +// Formatting of for-statement headers for single-line for-loops. func _() { for { } @@ -279,6 +279,86 @@ func _() { } // no parens printed } +// Formatting of for-statement headers for multi-line for-loops. +func _() { + for { + } + for expr { + } + for expr { + } // no parens printed + for { + } // no semicolons printed + for x := expr; ; { + use(x) + } + for expr { + } // no semicolons printed + for expr { + } // no semicolons and parens printed + for ; ; expr = false { + } + for x := expr; expr; { + use(x) + } + for x := expr; ; expr = false { + use(x) + } + for ; expr; expr = false { + } + for x := expr; expr; expr = false { + use(x) + } + for x := range []int{} { + use(x) + } + for x := range []int{} { + use(x) + } // no parens printed +} + +// Formatting of selected short single- and multi-line statements. +func _() { + if cond { + } + if cond { + } // multiple lines + if cond { + } else { + } // else clause always requires multiple lines + + for { + } + for i := 0; i < len(a); 1++ { + } + for i := 0; i < len(a); 1++ { + a[i] = i + } + for i := 0; i < len(a); 1++ { + a[i] = i + } // multiple lines + + for i := range a { + } + for i := range a { + a[i] = i + } + for i := range a { + a[i] = i + } // multiple lines + + go func() { + for { + a <- <-b + } + }() + defer func() { + if x := recover(); x != nil { + err = fmt.Sprintf("error: %s", x.msg) + } + }() +} + // Don't remove mandatory parentheses around composite literals in control clauses. func _() { // strip parentheses - no composite literals or composite literals don't start with a type name @@ -527,3 +607,29 @@ AVeryLongLabelThatShouldNotAffectFormatting: // There should be a single empty line before this comment. MoreCode() } + +// Formatting of empty statements. +func _() { + +} + +func _() { +} + +func _() { +} + +func _() { + f() +} + +func _() { +L: + ; +} + +func _() { +L: + ; + f() +} diff --git a/src/pkg/go/printer/testdata/statements.input b/src/pkg/go/printer/testdata/statements.input index bd03bc98b..e7fcc0e54 100644 --- a/src/pkg/go/printer/testdata/statements.input +++ b/src/pkg/go/printer/testdata/statements.input @@ -223,7 +223,7 @@ func _() { } -// Formatting of for-statement headers. +// Formatting of for-statement headers for single-line for-loops. func _() { for{} for expr {} @@ -235,14 +235,70 @@ func _() { for; ; expr = false {} for x :=expr; expr; {use(x)} for x := expr;; expr=false {use(x)} - for;expr;expr =false { - } + for;expr;expr =false {} for x := expr;expr;expr = false { use(x) } for x := range []int{} { use(x) } for x := range (([]int{})) { use(x) } // no parens printed } +// Formatting of for-statement headers for multi-line for-loops. +func _() { + for{ + } + for expr { + } + for (expr) { + } // no parens printed + for;;{ + } // no semicolons printed + for x :=expr;; {use( x) + } + for; expr;{ + } // no semicolons printed + for; ((expr));{ + } // no semicolons and parens printed + for; ; expr = false { + } + for x :=expr; expr; {use(x) + } + for x := expr;; expr=false {use(x) + } + for;expr;expr =false { + } + for x := expr;expr;expr = false { + use(x) + } + for x := range []int{} { + use(x) } + for x := range (([]int{})) { + use(x) } // no parens printed +} + + +// Formatting of selected short single- and multi-line statements. +func _() { + if cond {} + if cond { + } // multiple lines + if cond {} else {} // else clause always requires multiple lines + + for {} + for i := 0; i < len(a); 1++ {} + for i := 0; i < len(a); 1++ { a[i] = i } + for i := 0; i < len(a); 1++ { a[i] = i + } // multiple lines + + for i := range a {} + for i := range a { a[i] = i } + for i := range a { a[i] = i + } // multiple lines + + go func() { for { a <- <-b } }() + defer func() { if x := recover(); x != nil { err = fmt.Sprintf("error: %s", x.msg) } }() +} + + // Don't remove mandatory parentheses around composite literals in control clauses. func _() { // strip parentheses - no composite literals or composite literals don't start with a type name @@ -468,3 +524,27 @@ AVeryLongLabelThatShouldNotAffectFormatting: // There should be a single empty line before this comment. MoreCode() } + + +// Formatting of empty statements. +func _() { + ;;;;;;;;;;;;;;;;;;;;;;;;; +} + +func _() {;;;;;;;;;;;;;;;;;;;;;;;;; +} + +func _() {;;;;;;;;;;;;;;;;;;;;;;;;;} + +func _() { +f();;;;;;;;;;;;;;;;;;;;;;;;; +} + +func _() { +L:;;;;;;;;;;;; +} + +func _() { +L:;;;;;;;;;;;; + f() +} diff --git a/src/pkg/go/scanner/errors.go b/src/pkg/go/scanner/errors.go index 8a75a9650..22de69c3c 100644 --- a/src/pkg/go/scanner/errors.go +++ b/src/pkg/go/scanner/errors.go @@ -120,7 +120,7 @@ func PrintError(w io.Writer, err error) { for _, e := range list { fmt.Fprintf(w, "%s\n", e) } - } else { + } else if err != nil { fmt.Fprintf(w, "%s\n", err) } } diff --git a/src/pkg/go/scanner/scanner.go b/src/pkg/go/scanner/scanner.go index da508747a..3322c58b3 100644 --- a/src/pkg/go/scanner/scanner.go +++ b/src/pkg/go/scanner/scanner.go @@ -81,7 +81,7 @@ func (s *Scanner) next() { } } -// A mode value is set of flags (or 0). +// A mode value is a set of flags (or 0). // They control scanner behavior. // type Mode uint @@ -125,6 +125,9 @@ func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode s.ErrorCount = 0 s.next() + if s.ch == '\uFEFF' { + s.next() // ignore BOM + } } func (s *Scanner) error(offs int, msg string) { @@ -157,11 +160,15 @@ func (s *Scanner) interpretLineComment(text []byte) { func (s *Scanner) scanComment() string { // initial '/' already consumed; s.ch == '/' || s.ch == '*' offs := s.offset - 1 // position of initial '/' + hasCR := false if s.ch == '/' { //-style comment s.next() for s.ch != '\n' && s.ch >= 0 { + if s.ch == '\r' { + hasCR = true + } s.next() } if offs == s.lineOffset { @@ -175,6 +182,9 @@ func (s *Scanner) scanComment() string { s.next() for s.ch >= 0 { ch := s.ch + if ch == '\r' { + hasCR = true + } s.next() if ch == '*' && s.ch == '/' { s.next() @@ -185,7 +195,12 @@ func (s *Scanner) scanComment() string { s.error(offs, "comment not terminated") exit: - return string(s.src[offs:s.offset]) + lit := s.src[offs:s.offset] + if hasCR { + lit = stripCR(lit) + } + + return string(lit) } func (s *Scanner) findLineEnd() bool { @@ -378,7 +393,7 @@ func (s *Scanner) scanEscape(quote rune) { for ; i > 0 && s.ch != quote && s.ch >= 0; i-- { s.next() } - if x > max || 0xd800 <= x && x < 0xe000 { + if x > max || 0xD800 <= x && x < 0xE000 { s.error(offs, "escape sequence is invalid Unicode code point") } } @@ -527,6 +542,8 @@ func (s *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Tok // token.IMAG, token.CHAR, token.STRING) or token.COMMENT, the literal string // has the corresponding value. // +// If the returned token is a keyword, the literal string is the keyword. +// // If the returned token is token.SEMICOLON, the corresponding // literal string is ";" if the semicolon was present in the source, // and "\n" if the semicolon was inserted because of a newline or @@ -560,12 +577,18 @@ scanAgain: switch ch := s.ch; { case isLetter(ch): lit = s.scanIdentifier() - tok = token.Lookup(lit) - switch tok { - case token.IDENT, token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN: + if len(lit) > 1 { + // keywords are longer than one letter - avoid lookup otherwise + tok = token.Lookup(lit) + switch tok { + case token.IDENT, token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN: + insertSemi = true + } + } else { insertSemi = true + tok = token.IDENT } - case digitVal(ch) < 10: + case '0' <= ch && ch <= '9': insertSemi = true tok, lit = s.scanNumber(false) default: @@ -598,7 +621,7 @@ scanAgain: case ':': tok = s.switch2(token.COLON, token.DEFINE) case '.': - if digitVal(s.ch) < 10 { + if '0' <= s.ch && s.ch <= '9' { insertSemi = true tok, lit = s.scanNumber(true) } else if s.ch == '.' { diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go index 06223e23b..1c19053e6 100644 --- a/src/pkg/go/scanner/scanner_test.go +++ b/src/pkg/go/scanner/scanner_test.go @@ -6,6 +6,7 @@ package scanner import ( "go/token" + "io/ioutil" "os" "path/filepath" "runtime" @@ -43,12 +44,16 @@ var tokens = [...]elt{ // Special tokens {token.COMMENT, "/* a comment */", special}, {token.COMMENT, "// a comment \n", special}, + {token.COMMENT, "/*\r*/", special}, + {token.COMMENT, "//\r\n", special}, // Identifiers and basic type literals {token.IDENT, "foobar", literal}, {token.IDENT, "a۰۱۸", literal}, {token.IDENT, "foo६४", literal}, {token.IDENT, "bar9876", literal}, + {token.IDENT, "ŝ", literal}, // was bug (issue 4000) + {token.IDENT, "ŝfoo", literal}, // was bug (issue 4000) {token.INT, "0", literal}, {token.INT, "1", literal}, {token.INT, "123456789012345678890", literal}, @@ -214,8 +219,6 @@ func checkPos(t *testing.T, lit string, p token.Pos, expected token.Position) { // Verify that calling Scan() provides the correct results. func TestScan(t *testing.T) { - // make source - src_linecount := newlineCount(string(source)) whitespace_linecount := newlineCount(whitespace) // error handler @@ -226,59 +229,81 @@ func TestScan(t *testing.T) { // verify scan var s Scanner s.Init(fset.AddFile("", fset.Base(), len(source)), source, eh, ScanComments|dontInsertSemis) - index := 0 - // epos is the expected position + + // set up expected position epos := token.Position{ Filename: "", Offset: 0, Line: 1, Column: 1, } + + index := 0 for { pos, tok, lit := s.Scan() - if lit == "" { - // no literal value for non-literal tokens - lit = tok.String() + + // check position + if tok == token.EOF { + // correction for EOF + epos.Line = newlineCount(string(source)) + epos.Column = 2 } + checkPos(t, lit, pos, epos) + + // check token e := elt{token.EOF, "", special} if index < len(tokens) { e = tokens[index] + index++ } - if tok == token.EOF { - lit = "" - epos.Line = src_linecount - epos.Column = 2 - } - checkPos(t, lit, pos, epos) if tok != e.tok { t.Errorf("bad token for %q: got %s, expected %s", lit, tok, e.tok) } - if e.tok.IsLiteral() { - // no CRs in raw string literals - elit := e.lit - if elit[0] == '`' { - elit = string(stripCR([]byte(elit))) - epos.Offset += len(e.lit) - len(lit) // correct position - } - if lit != elit { - t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit) - } - } + + // check token class if tokenclass(tok) != e.class { t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class) } - epos.Offset += len(lit) + len(whitespace) - epos.Line += newlineCount(lit) + whitespace_linecount - if tok == token.COMMENT && lit[1] == '/' { - // correct for unaccounted '/n' in //-style comment - epos.Offset++ - epos.Line++ + + // check literal + elit := "" + switch e.tok { + case token.COMMENT: + // no CRs in comments + elit = string(stripCR([]byte(e.lit))) + //-style comment literal doesn't contain newline + if elit[1] == '/' { + elit = elit[0 : len(elit)-1] + } + case token.IDENT: + elit = e.lit + case token.SEMICOLON: + elit = ";" + default: + if e.tok.IsLiteral() { + // no CRs in raw string literals + elit = e.lit + if elit[0] == '`' { + elit = string(stripCR([]byte(elit))) + } + } else if e.tok.IsKeyword() { + elit = e.lit + } + } + if lit != elit { + t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit) } - index++ + if tok == token.EOF { break } + + // update position + epos.Offset += len(e.lit) + len(whitespace) + epos.Line += newlineCount(e.lit) + whitespace_linecount + } + if s.ErrorCount != 0 { t.Errorf("found %d errors", s.ErrorCount) } @@ -321,6 +346,7 @@ var lines = []string{ // # indicates a semicolon present in the source // $ indicates an automatically inserted semicolon "", + "\ufeff#;", // first BOM is ignored "#;", "foo$\n", "123$\n", @@ -521,7 +547,7 @@ func TestLineComments(t *testing.T) { } } -// Verify that initializing the same scanner more then once works correctly. +// Verify that initializing the same scanner more than once works correctly. func TestInit(t *testing.T) { var s Scanner @@ -669,6 +695,7 @@ var errors = []struct { {"0X", token.INT, 0, "illegal hexadecimal number"}, {"\"abc\x00def\"", token.STRING, 4, "illegal character NUL"}, {"\"abc\x80def\"", token.STRING, 4, "illegal UTF-8 encoding"}, + {"\ufeff\ufeff", token.ILLEGAL, 3, "illegal character U+FEFF"}, // only first BOM is ignored } func TestScanErrors(t *testing.T) { @@ -683,7 +710,7 @@ func BenchmarkScan(b *testing.B) { file := fset.AddFile("", fset.Base(), len(source)) var s Scanner b.StartTimer() - for i := b.N - 1; i >= 0; i-- { + for i := 0; i < b.N; i++ { s.Init(file, source, nil, ScanComments) for { _, tok, _ := s.Scan() @@ -693,3 +720,26 @@ func BenchmarkScan(b *testing.B) { } } } + +func BenchmarkScanFile(b *testing.B) { + b.StopTimer() + const filename = "scanner.go" + src, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) + } + fset := token.NewFileSet() + file := fset.AddFile(filename, fset.Base(), len(src)) + b.SetBytes(int64(len(src))) + var s Scanner + b.StartTimer() + for i := 0; i < b.N; i++ { + s.Init(file, src, nil, ScanComments) + for { + _, tok, _ := s.Scan() + if tok == token.EOF { + break + } + } + } +} diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go index 647d1b770..f5d999561 100644 --- a/src/pkg/go/token/position.go +++ b/src/pkg/go/token/position.go @@ -76,7 +76,7 @@ type Pos int // associated with it, and NoPos().IsValid() is false. NoPos is always // smaller than any other Pos value. The corresponding Position value // for NoPos is the zero value for Position. -// +// const NoPos Pos = 0 // IsValid returns true if the position is valid. @@ -295,9 +295,9 @@ type FileSet struct { // NewFileSet creates a new file set. func NewFileSet() *FileSet { - s := new(FileSet) - s.base = 1 // 0 == NoPos - return s + return &FileSet{ + base: 1, // 0 == NoPos + } } // Base returns the minimum base offset that must be provided to @@ -347,7 +347,7 @@ func (s *FileSet) AddFile(filename string, base, size int) *File { // Iterate calls f for the files in the file set in the order they were added // until f returns false. -// +// func (s *FileSet) Iterate(f func(*File) bool) { for i := 0; ; i++ { var file *File @@ -367,8 +367,10 @@ func searchFiles(a []*File, x int) int { } func (s *FileSet) file(p Pos) *File { + s.mutex.RLock() // common case: p is in last file if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size { + s.mutex.RUnlock() return f } // p is not in last file - search all files @@ -376,10 +378,14 @@ func (s *FileSet) file(p Pos) *File { f := s.files[i] // f.base <= int(p) by definition of searchFiles if int(p) <= f.base+f.size { - s.last = f + s.mutex.RUnlock() + s.mutex.Lock() + s.last = f // race is ok - s.last is only a cache + s.mutex.Unlock() return f } } + s.mutex.RUnlock() return nil } @@ -389,9 +395,7 @@ func (s *FileSet) file(p Pos) *File { // func (s *FileSet) File(p Pos) (f *File) { if p != NoPos { - s.mutex.RLock() f = s.file(p) - s.mutex.RUnlock() } return } @@ -399,11 +403,9 @@ func (s *FileSet) File(p Pos) (f *File) { // Position converts a Pos in the fileset into a general Position. func (s *FileSet) Position(p Pos) (pos Position) { if p != NoPos { - s.mutex.RLock() if f := s.file(p); f != nil { pos = f.position(p) } - s.mutex.RUnlock() } return } diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go index 160107df4..1d36c2226 100644 --- a/src/pkg/go/token/position_test.go +++ b/src/pkg/go/token/position_test.go @@ -6,6 +6,8 @@ package token import ( "fmt" + "math/rand" + "sync" "testing" ) @@ -179,3 +181,52 @@ func TestFiles(t *testing.T) { } } } + +// FileSet.File should return nil if Pos is past the end of the FileSet. +func TestFileSetPastEnd(t *testing.T) { + fset := NewFileSet() + for _, test := range tests { + fset.AddFile(test.filename, fset.Base(), test.size) + } + if f := fset.File(Pos(fset.Base())); f != nil { + t.Errorf("expected nil, got %v", f) + } +} + +func TestFileSetCacheUnlikely(t *testing.T) { + fset := NewFileSet() + offsets := make(map[string]int) + for _, test := range tests { + offsets[test.filename] = fset.Base() + fset.AddFile(test.filename, fset.Base(), test.size) + } + for file, pos := range offsets { + f := fset.File(Pos(pos)) + if f.Name() != file { + t.Errorf("expecting %q at position %d, got %q", file, pos, f.Name()) + } + } +} + +// issue 4345. Test concurrent use of FileSet.Pos does not trigger a +// race in the FileSet position cache. +func TestFileSetRace(t *testing.T) { + fset := NewFileSet() + for i := 0; i < 100; i++ { + fset.AddFile(fmt.Sprintf("file-%d", i), fset.Base(), 1031) + } + max := int32(fset.Base()) + var stop sync.WaitGroup + r := rand.New(rand.NewSource(7)) + for i := 0; i < 2; i++ { + r := rand.New(rand.NewSource(r.Int63())) + stop.Add(1) + go func() { + for i := 0; i < 1000; i++ { + fset.Position(Pos(r.Int31n(max))) + } + stop.Done() + }() + } + stop.Wait() +} diff --git a/src/pkg/go/types/api.go b/src/pkg/go/types/api.go new file mode 100644 index 000000000..13b453faa --- /dev/null +++ b/src/pkg/go/types/api.go @@ -0,0 +1,105 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package types declares the data structures for representing +// Go types and implements typechecking of package files. +// +// WARNING: THE TYPES API IS SUBJECT TO CHANGE. +// +package types + +import ( + "go/ast" + "go/token" +) + +// A Context specifies the supporting context for type checking. +// An empty Context is a ready-to-use default context. +type Context struct { + // If Error != nil, it is called with each error found + // during type checking. The error strings of errors with + // detailed position information are formatted as follows: + // filename:line:column: message + Error func(err error) + + // If Ident != nil, it is called for each identifier id + // denoting an Object in the files provided to Check, and + // obj is the denoted object. + // Ident is not called for fields and methods in struct or + // interface types or composite literals, or for blank (_) + // or dot (.) identifiers in dot-imports. + // TODO(gri) Consider making Fields and Methods ordinary + // Objects - than we could lift this restriction. + Ident func(id *ast.Ident, obj Object) + + // If Expr != nil, it is called exactly once for each expression x + // that is type-checked: typ is the expression type, and val is the + // value if x is constant, val is nil otherwise. + // + // If x is a literal value (constant, composite literal), typ is always + // the dynamic type of x (never an interface type). Otherwise, typ is x's + // static type (possibly an interface type). + // + // Constants are represented as follows: + // + // bool -> bool + // numeric -> int64, *big.Int, *big.Rat, Complex + // string -> string + // nil -> NilType + // + // Constant values are normalized, that is, they are represented + // using the "smallest" possible type that can represent the value. + // For instance, 1.0 is represented as an int64 because it can be + // represented accurately as an int64. + Expr func(x ast.Expr, typ Type, val interface{}) + + // If Import != nil, it is called for each imported package. + // Otherwise, GcImporter is called. + Import Importer + + // If Alignof != nil, it is called to determine the alignment + // of the given type. Otherwise DefaultAlignmentof is called. + // Alignof must implement the alignment guarantees required by + // the spec. + Alignof func(Type) int64 + + // If Offsetsof != nil, it is called to determine the offsets + // of the given struct fields, in bytes. Otherwise DefaultOffsetsof + // is called. Offsetsof must implement the offset guarantees + // required by the spec. + Offsetsof func(fields []*Field) []int64 + + // If Sizeof != nil, it is called to determine the size of the + // given type. Otherwise, DefaultSizeof is called. Sizeof must + // implement the size guarantees required by the spec. + Sizeof func(Type) int64 +} + +// An Importer resolves import paths to Package objects. +// The imports map records the packages already imported, +// indexed by package id (canonical import path). +// An Importer must determine the canonical import path and +// check the map to see if it is already present in the imports map. +// If so, the Importer can return the map entry. Otherwise, the +// Importer should load the package data for the given path into +// a new *Package, record pkg in the imports map, and then +// return pkg. +type Importer func(imports map[string]*Package, path string) (pkg *Package, err error) + +// Check resolves and typechecks a set of package files within the given +// context. It returns the package and the first error encountered, if +// any. If the context's Error handler is nil, Check terminates as soon +// as the first error is encountered; otherwise it continues until the +// entire package is checked. If there are errors, the package may be +// only partially type-checked, and the resulting package may be incomplete +// (missing objects, imports, etc.). +func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*Package, error) { + return check(ctxt, fset, files) +} + +// Check is shorthand for ctxt.Check where ctxt is a default (empty) context. +func Check(fset *token.FileSet, files []*ast.File) (*Package, error) { + var ctxt Context + return ctxt.Check(fset, files) +} diff --git a/src/pkg/go/types/builtins.go b/src/pkg/go/types/builtins.go new file mode 100644 index 000000000..ad9259118 --- /dev/null +++ b/src/pkg/go/types/builtins.go @@ -0,0 +1,455 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements typechecking of builtin function calls. + +package types + +import ( + "go/ast" + "go/token" +) + +// builtin typechecks a built-in call. The built-in type is bin, and iota is the current +// value of iota or -1 if iota doesn't have a value in the current context. The result +// of the call is returned via x. If the call has type errors, the returned x is marked +// as invalid (x.mode == invalid). +// +func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) { + args := call.Args + id := bin.id + + // declare before goto's + var arg0 ast.Expr // first argument, if present + + // check argument count + n := len(args) + msg := "" + if n < bin.nargs { + msg = "not enough" + } else if !bin.isVariadic && n > bin.nargs { + msg = "too many" + } + if msg != "" { + check.invalidOp(call.Pos(), msg+" arguments for %s (expected %d, found %d)", call, bin.nargs, n) + goto Error + } + + // common case: evaluate first argument if present; + // if it is an expression, x has the expression value + if n > 0 { + arg0 = args[0] + switch id { + case _Make, _New, _Print, _Println, _Offsetof, _Trace: + // respective cases below do the work + default: + // argument must be an expression + check.expr(x, arg0, nil, iota) + if x.mode == invalid { + goto Error + } + } + } + + switch id { + case _Append: + if _, ok := underlying(x.typ).(*Slice); !ok { + check.invalidArg(x.pos(), "%s is not a typed slice", x) + goto Error + } + resultTyp := x.typ + for _, arg := range args[1:] { + check.expr(x, arg, nil, iota) + if x.mode == invalid { + goto Error + } + // TODO(gri) check assignability + } + x.mode = value + x.typ = resultTyp + + case _Cap, _Len: + mode := invalid + var val interface{} + switch typ := implicitArrayDeref(underlying(x.typ)).(type) { + case *Basic: + if isString(typ) && id == _Len { + if x.mode == constant { + mode = constant + val = int64(len(x.val.(string))) + } else { + mode = value + } + } + + case *Array: + mode = value + // spec: "The expressions len(s) and cap(s) are constants + // if the type of s is an array or pointer to an array and + // the expression s does not contain channel receives or + // function calls; in this case s is not evaluated." + if !check.containsCallsOrReceives(arg0) { + mode = constant + val = typ.Len + } + + case *Slice, *Chan: + mode = value + + case *Map: + if id == _Len { + mode = value + } + } + + if mode == invalid { + check.invalidArg(x.pos(), "%s for %s", x, bin.name) + goto Error + } + x.mode = mode + x.typ = Typ[Int] + x.val = val + + case _Close: + ch, ok := underlying(x.typ).(*Chan) + if !ok { + check.invalidArg(x.pos(), "%s is not a channel", x) + goto Error + } + if ch.Dir&ast.SEND == 0 { + check.invalidArg(x.pos(), "%s must not be a receive-only channel", x) + goto Error + } + x.mode = novalue + + case _Complex: + if !check.complexArg(x) { + goto Error + } + + var y operand + check.expr(&y, args[1], nil, iota) + if y.mode == invalid { + goto Error + } + if !check.complexArg(&y) { + goto Error + } + + check.convertUntyped(x, y.typ) + if x.mode == invalid { + goto Error + } + check.convertUntyped(&y, x.typ) + if y.mode == invalid { + goto Error + } + + if !IsIdentical(x.typ, y.typ) { + check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) + goto Error + } + + typ := underlying(x.typ).(*Basic) + if x.mode == constant && y.mode == constant { + x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, typ) + } else { + x.mode = value + } + + switch typ.Kind { + case Float32: + x.typ = Typ[Complex64] + case Float64: + x.typ = Typ[Complex128] + case UntypedInt, UntypedRune, UntypedFloat: + x.typ = Typ[UntypedComplex] + default: + check.invalidArg(x.pos(), "float32 or float64 arguments expected") + goto Error + } + + case _Copy: + var y operand + check.expr(&y, args[1], nil, iota) + if y.mode == invalid { + goto Error + } + + var dst, src Type + if t, ok := underlying(x.typ).(*Slice); ok { + dst = t.Elt + } + switch t := underlying(y.typ).(type) { + case *Basic: + if isString(y.typ) { + src = Typ[Byte] + } + case *Slice: + src = t.Elt + } + + if dst == nil || src == nil { + check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y) + goto Error + } + + if !IsIdentical(dst, src) { + check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) + goto Error + } + + x.mode = value + x.typ = Typ[Int] + + case _Delete: + m, ok := underlying(x.typ).(*Map) + if !ok { + check.invalidArg(x.pos(), "%s is not a map", x) + goto Error + } + check.expr(x, args[1], nil, iota) + if x.mode == invalid { + goto Error + } + if !x.isAssignable(check.ctxt, m.Key) { + check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key) + goto Error + } + x.mode = novalue + + case _Imag, _Real: + if !isComplex(x.typ) { + check.invalidArg(x.pos(), "%s must be a complex number", x) + goto Error + } + if x.mode == constant { + // nothing to do for x.val == 0 + if !isZeroConst(x.val) { + c := x.val.(Complex) + if id == _Real { + x.val = c.Re + } else { + x.val = c.Im + } + } + } else { + x.mode = value + } + k := Invalid + switch underlying(x.typ).(*Basic).Kind { + case Complex64: + k = Float32 + case Complex128: + k = Float64 + case UntypedComplex: + k = UntypedFloat + default: + unreachable() + } + x.typ = Typ[k] + + case _Make: + resultTyp := check.typ(arg0, false) + if resultTyp == Typ[Invalid] { + goto Error + } + var min int // minimum number of arguments + switch underlying(resultTyp).(type) { + case *Slice: + min = 2 + case *Map, *Chan: + min = 1 + default: + check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0) + goto Error + } + if n := len(args); n < min || min+1 < n { + check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n) + goto Error + } + var sizes []interface{} // constant integer arguments, if any + for _, arg := range args[1:] { + check.expr(x, arg, nil, iota) + if x.isInteger(check.ctxt) { + if x.mode == constant { + if isNegConst(x.val) { + check.invalidArg(x.pos(), "%s must not be negative", x) + // safe to continue + } else { + sizes = append(sizes, x.val) // x.val >= 0 + } + } + } else { + check.invalidArg(x.pos(), "%s must be an integer", x) + // safe to continue + } + } + if len(sizes) == 2 && compareConst(sizes[0], sizes[1], token.GTR) { + check.invalidArg(args[1].Pos(), "length and capacity swapped") + // safe to continue + } + x.mode = variable + x.typ = resultTyp + + case _New: + resultTyp := check.typ(arg0, false) + if resultTyp == Typ[Invalid] { + goto Error + } + x.mode = variable + x.typ = &Pointer{Base: resultTyp} + + case _Panic: + x.mode = novalue + + case _Print, _Println: + for _, arg := range args { + check.expr(x, arg, nil, -1) + if x.mode == invalid { + goto Error + } + } + x.mode = novalue + + case _Recover: + x.mode = value + x.typ = new(Interface) + + case _Alignof: + x.mode = constant + x.val = check.ctxt.alignof(x.typ) + x.typ = Typ[Uintptr] + + case _Offsetof: + arg, ok := unparen(arg0).(*ast.SelectorExpr) + if !ok { + check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0) + goto Error + } + check.expr(x, arg.X, nil, -1) + if x.mode == invalid { + goto Error + } + sel := arg.Sel.Name + res := lookupField(x.typ, QualifiedName{check.pkg, arg.Sel.Name}) + if res.index == nil { + check.invalidArg(x.pos(), "%s has no single field %s", x, sel) + goto Error + } + offs := check.ctxt.offsetof(deref(x.typ), res.index) + if offs < 0 { + check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x) + goto Error + } + x.mode = constant + x.val = offs + x.typ = Typ[Uintptr] + + case _Sizeof: + x.mode = constant + x.val = check.ctxt.sizeof(x.typ) + x.typ = Typ[Uintptr] + + case _Assert: + // assert(pred) causes a typechecker error if pred is false. + // The result of assert is the value of pred if there is no error. + // Note: assert is only available in self-test mode. + if x.mode != constant || !isBoolean(x.typ) { + check.invalidArg(x.pos(), "%s is not a boolean constant", x) + goto Error + } + pred, ok := x.val.(bool) + if !ok { + check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x) + goto Error + } + if !pred { + check.errorf(call.Pos(), "%s failed", call) + // compile-time assertion failure - safe to continue + } + + case _Trace: + // trace(x, y, z, ...) dumps the positions, expressions, and + // values of its arguments. The result of trace is the value + // of the first argument. + // Note: trace is only available in self-test mode. + if len(args) == 0 { + check.dump("%s: trace() without arguments", call.Pos()) + x.mode = novalue + x.expr = call + return + } + var t operand + x1 := x + for _, arg := range args { + check.rawExpr(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T)) + check.dump("%s: %s", x1.pos(), x1) + x1 = &t // use incoming x only for first argument + } + + default: + check.invalidAST(call.Pos(), "unknown builtin id %d", id) + goto Error + } + + x.expr = call + return + +Error: + x.mode = invalid + x.expr = call +} + +// implicitArrayDeref returns A if typ is of the form *A and A is an array; +// otherwise it returns typ. +// +func implicitArrayDeref(typ Type) Type { + if p, ok := typ.(*Pointer); ok { + if a, ok := underlying(p.Base).(*Array); ok { + return a + } + } + return typ +} + +// containsCallsOrReceives reports if x contains function calls or channel receives. +// Expects that x was type-checked already. +// +func (check *checker) containsCallsOrReceives(x ast.Expr) (found bool) { + ast.Inspect(x, func(x ast.Node) bool { + switch x := x.(type) { + case *ast.CallExpr: + // calls and conversions look the same + if !check.conversions[x] { + found = true + } + case *ast.UnaryExpr: + if x.Op == token.ARROW { + found = true + } + } + return !found // no need to continue if found + }) + return +} + +// unparen removes any parentheses surrounding an expression and returns +// the naked expression. +// +func unparen(x ast.Expr) ast.Expr { + if p, ok := x.(*ast.ParenExpr); ok { + return unparen(p.X) + } + return x +} + +func (check *checker) complexArg(x *operand) bool { + t, _ := underlying(x.typ).(*Basic) + if t != nil && (t.Info&IsFloat != 0 || t.Kind == UntypedInt || t.Kind == UntypedRune) { + return true + } + check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x) + return false +} diff --git a/src/pkg/go/types/check.go b/src/pkg/go/types/check.go new file mode 100644 index 000000000..f7b87e30c --- /dev/null +++ b/src/pkg/go/types/check.go @@ -0,0 +1,507 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements the Check function, which typechecks a package. + +package types + +import ( + "fmt" + "go/ast" + "go/token" +) + +// debugging support +const ( + debug = true // leave on during development + trace = false // turn on for detailed type resolution traces +) + +type checker struct { + ctxt *Context + fset *token.FileSet + files []*ast.File + + // lazily initialized + pkg *Package // current package + firsterr error // first error encountered + idents map[*ast.Ident]Object // maps identifiers to their unique object + objects map[*ast.Object]Object // maps *ast.Objects to their unique object + initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations + methods map[*TypeName]*Scope // maps type names to associated methods + conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls) + + // untyped expressions + // TODO(gri): Consider merging the untyped and constants map. Should measure + // the ratio between untyped non-constant and untyped constant expressions + // to make an informed decision. + untyped map[ast.Expr]*Basic // map of expressions of untyped type + constants map[ast.Expr]interface{} // map of untyped constant expressions; each key also appears in untyped + shiftOps map[ast.Expr]bool // map of lhs shift operands with delayed type-checking + + // functions + funclist []function // list of functions/methods with correct signatures and non-empty bodies + funcsig *Signature // signature of currently typechecked function + pos []token.Pos // stack of expr positions; debugging support, used if trace is set +} + +func (check *checker) register(id *ast.Ident, obj Object) { + // When an expression is evaluated more than once (happens + // in rare cases, e.g. for statement expressions, see + // comment in stmt.go), the object has been registered + // before. Don't do anything in that case. + if alt := check.idents[id]; alt != nil { + assert(alt == obj) + return + } + check.idents[id] = obj + if f := check.ctxt.Ident; f != nil { + f(id, obj) + } +} + +// lookup returns the unique Object denoted by the identifier. +// For identifiers without assigned *ast.Object, it uses the +// checker.idents map; for identifiers with an *ast.Object it +// uses the checker.objects map. +// +// TODO(gri) Once identifier resolution is done entirely by +// the typechecker, only the idents map is needed. +// +func (check *checker) lookup(ident *ast.Ident) Object { + obj := check.idents[ident] + astObj := ident.Obj + + if obj != nil { + assert(astObj == nil || check.objects[astObj] == nil || check.objects[astObj] == obj) + return obj + } + + if astObj == nil { + return nil + } + + if obj = check.objects[astObj]; obj == nil { + obj = newObj(check.pkg, astObj) + check.objects[astObj] = obj + } + check.register(ident, obj) + + return obj +} + +type function struct { + obj *Func // for debugging/tracing only + sig *Signature + body *ast.BlockStmt +} + +// later adds a function with non-empty body to the list of functions +// that need to be processed after all package-level declarations +// are typechecked. +// +func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) { + // functions implemented elsewhere (say in assembly) have no body + if body != nil { + check.funclist = append(check.funclist, function{f, sig, body}) + } +} + +func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) { + assert(check.lookup(ident) == nil) // identifier already declared or resolved + check.register(ident, obj) + if ident.Name != "_" { + if alt := scope.Insert(obj); alt != nil { + prevDecl := "" + if pos := alt.GetPos(); pos.IsValid() { + prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos)) + } + check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl)) + } + } +} + +func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spec *ast.ValueSpec, iota int) { + if len(lhs) == 0 { + check.invalidAST(pos, "missing lhs in declaration") + return + } + + // determine type for all of lhs, if any + // (but only set it for the object we typecheck!) + var typ Type + if spec.Type != nil { + typ = check.typ(spec.Type, false) + } + + // len(lhs) > 0 + rhs := spec.Values + if len(lhs) == len(rhs) { + // check only lhs and rhs corresponding to obj + var l, r ast.Expr + for i, name := range lhs { + if check.lookup(name) == obj { + l = lhs[i] + r = rhs[i] + break + } + } + assert(l != nil) + switch obj := obj.(type) { + case *Const: + obj.Type = typ + case *Var: + obj.Type = typ + default: + unreachable() + } + check.assign1to1(l, r, nil, true, iota) + return + } + + // there must be a type or initialization expressions + if typ == nil && len(rhs) == 0 { + check.invalidAST(pos, "missing type or initialization expression") + typ = Typ[Invalid] + } + + // if we have a type, mark all of lhs + if typ != nil { + for _, name := range lhs { + switch obj := check.lookup(name).(type) { + case *Const: + obj.Type = typ + case *Var: + obj.Type = typ + default: + unreachable() + } + } + } + + // check initial values, if any + if len(rhs) > 0 { + // TODO(gri) should try to avoid this conversion + lhx := make([]ast.Expr, len(lhs)) + for i, e := range lhs { + lhx[i] = e + } + check.assignNtoM(lhx, rhs, true, iota) + } +} + +// object typechecks an object by assigning it a type. +// +func (check *checker) object(obj Object, cycleOk bool) { + switch obj := obj.(type) { + case *Package: + // nothing to do + case *Const: + if obj.Type != nil { + return // already checked + } + // The obj.Val field for constants is initialized to its respective + // iota value by the parser. + // The object's fields can be in one of the following states: + // Type != nil => the constant value is Val + // Type == nil => the constant is not typechecked yet, and Val can be: + // Val is int => Val is the value of iota for this declaration + // Val == nil => the object's expression is being evaluated + if obj.Val == nil { + check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name) + obj.Type = Typ[Invalid] + return + } + spec := obj.spec + iota := obj.Val.(int) + obj.Val = nil // mark obj as "visited" for cycle detection + // determine spec for type and initialization expressions + init := spec + if len(init.Values) == 0 { + init = check.initspecs[spec] + } + check.valueSpec(spec.Pos(), obj, spec.Names, init, iota) + + case *Var: + if obj.Type != nil { + return // already checked + } + if obj.visited { + check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name) + obj.Type = Typ[Invalid] + return + } + switch d := obj.decl.(type) { + case *ast.Field: + unreachable() // function parameters are always typed when collected + case *ast.ValueSpec: + obj.visited = true + check.valueSpec(d.Pos(), obj, d.Names, d, 0) + case *ast.AssignStmt: + // If we reach here, we have a short variable declaration + // where the rhs didn't typecheck and thus the lhs has no + // types. + obj.visited = true + obj.Type = Typ[Invalid] + default: + unreachable() // see also function newObj + } + + case *TypeName: + if obj.Type != nil { + return // already checked + } + typ := &NamedType{Obj: obj} + obj.Type = typ // "mark" object so recursion terminates + typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk)) + // typecheck associated method signatures + if scope := check.methods[obj]; scope != nil { + switch t := typ.Underlying.(type) { + case *Struct: + // struct fields must not conflict with methods + for _, f := range t.Fields { + if m := scope.Lookup(f.Name); m != nil { + check.errorf(m.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name) + // ok to continue + } + } + case *Interface: + // methods cannot be associated with an interface type + for _, m := range scope.Entries { + recv := m.(*Func).decl.Recv.List[0].Type + check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name) + // ok to continue + } + } + // typecheck method signatures + var methods []*Method + for _, obj := range scope.Entries { + m := obj.(*Func) + sig := check.typ(m.decl.Type, cycleOk).(*Signature) + params, _ := check.collectParams(m.decl.Recv, false) + sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter + m.Type = sig + methods = append(methods, &Method{QualifiedName{check.pkg, m.Name}, sig}) + check.later(m, sig, m.decl.Body) + } + typ.Methods = methods + delete(check.methods, obj) // we don't need this scope anymore + } + + case *Func: + if obj.Type != nil { + return // already checked + } + fdecl := obj.decl + // methods are typechecked when their receivers are typechecked + if fdecl.Recv == nil { + sig := check.typ(fdecl.Type, cycleOk).(*Signature) + if obj.Name == "init" && (len(sig.Params) != 0 || len(sig.Results) != 0) { + check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") + // ok to continue + } + obj.Type = sig + check.later(obj, sig, fdecl.Body) + } + + default: + unreachable() + } +} + +// assocInitvals associates "inherited" initialization expressions +// with the corresponding *ast.ValueSpec in the check.initspecs map +// for constant declarations without explicit initialization expressions. +// +func (check *checker) assocInitvals(decl *ast.GenDecl) { + var last *ast.ValueSpec + for _, s := range decl.Specs { + if s, ok := s.(*ast.ValueSpec); ok { + if len(s.Values) > 0 { + last = s + } else { + check.initspecs[s] = last + } + } + } + if last == nil { + check.invalidAST(decl.Pos(), "no initialization values provided") + } +} + +// assocMethod associates a method declaration with the respective +// receiver base type. meth.Recv must exist. +// +func (check *checker) assocMethod(meth *ast.FuncDecl) { + // The receiver type is one of the following (enforced by parser): + // - *ast.Ident + // - *ast.StarExpr{*ast.Ident} + // - *ast.BadExpr (parser error) + typ := meth.Recv.List[0].Type + if ptr, ok := typ.(*ast.StarExpr); ok { + typ = ptr.X + } + // determine receiver base type name + ident, ok := typ.(*ast.Ident) + if !ok { + // not an identifier - parser reported error already + return // ignore this method + } + // determine receiver base type object + var tname *TypeName + if obj := check.lookup(ident); obj != nil { + obj, ok := obj.(*TypeName) + if !ok { + check.errorf(ident.Pos(), "%s is not a type", ident.Name) + return // ignore this method + } + if obj.spec == nil { + check.errorf(ident.Pos(), "cannot define method on non-local type %s", ident.Name) + return // ignore this method + } + tname = obj + } else { + // identifier not declared/resolved - parser reported error already + return // ignore this method + } + // declare method in receiver base type scope + scope := check.methods[tname] + if scope == nil { + scope = new(Scope) + check.methods[tname] = scope + } + check.declareIdent(scope, meth.Name, &Func{Pkg: check.pkg, Name: meth.Name.Name, decl: meth}) +} + +func (check *checker) decl(decl ast.Decl) { + switch d := decl.(type) { + case *ast.BadDecl: + // ignore + case *ast.GenDecl: + for _, spec := range d.Specs { + switch s := spec.(type) { + case *ast.ImportSpec: + // nothing to do (handled by check.resolve) + case *ast.ValueSpec: + for _, name := range s.Names { + check.object(check.lookup(name), false) + } + case *ast.TypeSpec: + check.object(check.lookup(s.Name), false) + default: + check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) + } + } + case *ast.FuncDecl: + // methods are checked when their respective base types are checked + if d.Recv != nil { + return + } + obj := check.lookup(d.Name) + // Initialization functions don't have an object associated with them + // since they are not in any scope. Create a dummy object for them. + if d.Name.Name == "init" { + assert(obj == nil) // all other functions should have an object + obj = &Func{Pkg: check.pkg, Name: d.Name.Name, decl: d} + check.register(d.Name, obj) + } + check.object(obj, false) + default: + check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) + } +} + +// A bailout panic is raised to indicate early termination. +type bailout struct{} + +func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) { + // initialize checker + check := checker{ + ctxt: ctxt, + fset: fset, + files: files, + idents: make(map[*ast.Ident]Object), + objects: make(map[*ast.Object]Object), + initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec), + methods: make(map[*TypeName]*Scope), + conversions: make(map[*ast.CallExpr]bool), + untyped: make(map[ast.Expr]*Basic), + constants: make(map[ast.Expr]interface{}), + shiftOps: make(map[ast.Expr]bool), + } + + // set results and handle panics + defer func() { + pkg = check.pkg + switch p := recover().(type) { + case nil, bailout: + // normal return or early exit + err = check.firsterr + default: + // unexpected panic: don't crash clients + if debug { + check.dump("INTERNAL PANIC: %v", p) + panic(p) + } + // TODO(gri) add a test case for this scenario + err = fmt.Errorf("types internal error: %v", p) + } + }() + + // resolve identifiers + imp := ctxt.Import + if imp == nil { + imp = GcImport + } + methods := check.resolve(imp) + + // associate methods with types + for _, m := range methods { + check.assocMethod(m) + } + + // typecheck all declarations + for _, f := range check.files { + for _, d := range f.Decls { + check.decl(d) + } + } + + // typecheck all function/method bodies + // (funclist may grow when checking statements - do not use range clause!) + for i := 0; i < len(check.funclist); i++ { + f := check.funclist[i] + if trace { + s := "" + if f.obj != nil { + s = f.obj.Name + } + fmt.Println("---", s) + } + check.funcsig = f.sig + check.stmtList(f.body.List) + } + + // remaining untyped expressions must indeed be untyped + if debug { + for x, typ := range check.untyped { + if !isUntyped(typ) { + check.dump("%s: %s (type %s) is not untyped", x.Pos(), x, typ) + panic(0) + } + } + } + + // notify client of any untyped types left + // TODO(gri) Consider doing this before and + // after function body checking for smaller + // map size and more immediate feedback. + if ctxt.Expr != nil { + for x, typ := range check.untyped { + ctxt.Expr(x, typ, check.constants[x]) + } + } + + return +} diff --git a/src/pkg/go/types/check_test.go b/src/pkg/go/types/check_test.go new file mode 100644 index 000000000..470f3a1a9 --- /dev/null +++ b/src/pkg/go/types/check_test.go @@ -0,0 +1,259 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements a typechecker test harness. The packages specified +// in tests are typechecked. Error messages reported by the typechecker are +// compared against the error messages expected in the test files. +// +// Expected errors are indicated in the test files by putting a comment +// of the form /* ERROR "rx" */ immediately following an offending token. +// The harness will verify that an error matching the regular expression +// rx is reported at that source position. Consecutive comments may be +// used to indicate multiple errors for the same token position. +// +// For instance, the following test file indicates that a "not declared" +// error should be reported for the undeclared variable x: +// +// package p +// func f() { +// _ = x /* ERROR "not declared" */ + 1 +// } + +package types + +import ( + "flag" + "fmt" + "go/ast" + "go/parser" + "go/scanner" + "go/token" + "io/ioutil" + "os" + "regexp" + "testing" +) + +var listErrors = flag.Bool("list", false, "list errors") + +// The test filenames do not end in .go so that they are invisible +// to gofmt since they contain comments that must not change their +// positions relative to surrounding tokens. + +var tests = []struct { + name string + files []string +}{ + {"decls0", []string{"testdata/decls0.src"}}, + {"decls1", []string{"testdata/decls1.src"}}, + {"decls2", []string{"testdata/decls2a.src", "testdata/decls2b.src"}}, + {"decls3", []string{"testdata/decls3.src"}}, + {"const0", []string{"testdata/const0.src"}}, + {"expr0", []string{"testdata/expr0.src"}}, + {"expr1", []string{"testdata/expr1.src"}}, + {"expr2", []string{"testdata/expr2.src"}}, + {"expr3", []string{"testdata/expr3.src"}}, + {"builtins", []string{"testdata/builtins.src"}}, + {"conversions", []string{"testdata/conversions.src"}}, + {"stmt0", []string{"testdata/stmt0.src"}}, +} + +var fset = token.NewFileSet() + +func getFile(filename string) (file *token.File) { + fset.Iterate(func(f *token.File) bool { + if f.Name() == filename { + file = f + return false // end iteration + } + return true + }) + return file +} + +// Positioned errors are of the form filename:line:column: message . +var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`) + +// splitError splits an error's error message into a position string +// and the actual error message. If there's no position information, +// pos is the empty string, and msg is the entire error message. +// +func splitError(err error) (pos, msg string) { + msg = err.Error() + if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 { + pos = m[1] + msg = m[2] + } + return +} + +func parseFiles(t *testing.T, testname string, filenames []string) ([]*ast.File, []error) { + var files []*ast.File + var errlist []error + for _, filename := range filenames { + file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors) + if file == nil { + t.Fatalf("%s: could not parse file %s", testname, filename) + } + files = append(files, file) + if err != nil { + if list, _ := err.(scanner.ErrorList); len(list) > 0 { + for _, err := range list { + errlist = append(errlist, err) + } + } else { + errlist = append(errlist, err) + } + } + } + return files, errlist +} + +// ERROR comments must be of the form /* ERROR "rx" */ and rx is +// a regular expression that matches the expected error message. +// +var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`) + +// errMap collects the regular expressions of ERROR comments found +// in files and returns them as a map of error positions to error messages. +// +func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string { + errmap := make(map[string][]string) + + for _, file := range files { + filename := fset.Position(file.Package).Filename + src, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatalf("%s: could not read %s", testname, filename) + } + + var s scanner.Scanner + // file was parsed already - do not add it again to the file + // set otherwise the position information returned here will + // not match the position information collected by the parser + s.Init(getFile(filename), src, nil, scanner.ScanComments) + var prev string // position string of last non-comment, non-semicolon token + + scanFile: + for { + pos, tok, lit := s.Scan() + switch tok { + case token.EOF: + break scanFile + case token.COMMENT: + s := errRx.FindStringSubmatch(lit) + if len(s) == 2 { + errmap[prev] = append(errmap[prev], string(s[1])) + } + case token.SEMICOLON: + // ignore automatically inserted semicolon + if lit == "\n" { + continue scanFile + } + fallthrough + default: + prev = fset.Position(pos).String() + } + } + } + + return errmap +} + +func eliminate(t *testing.T, errmap map[string][]string, errlist []error) { + for _, err := range errlist { + pos, msg := splitError(err) + list := errmap[pos] + index := -1 // list index of matching message, if any + // we expect one of the messages in list to match the error at pos + for i, msg := range list { + rx, err := regexp.Compile(msg) + if err != nil { + t.Errorf("%s: %v", pos, err) + continue + } + if rx.MatchString(msg) { + index = i + break + } + } + if index >= 0 { + // eliminate from list + if n := len(list) - 1; n > 0 { + // not the last entry - swap in last element and shorten list by 1 + list[index] = list[n] + errmap[pos] = list[:n] + } else { + // last entry - remove list from map + delete(errmap, pos) + } + } else { + t.Errorf("%s: no error expected: %q", pos, msg) + } + + } +} + +func checkFiles(t *testing.T, testname string, testfiles []string) { + // parse files and collect parser errors + files, errlist := parseFiles(t, testname, testfiles) + + // typecheck and collect typechecker errors + var ctxt Context + ctxt.Error = func(err error) { errlist = append(errlist, err) } + ctxt.Check(fset, files) + + if *listErrors { + t.Errorf("--- %s: %d errors found:", testname, len(errlist)) + for _, err := range errlist { + t.Error(err) + } + return + } + + // match and eliminate errors + // we are expecting the following errors + // (collect these after parsing the files so that + // they are found in the file set) + errmap := errMap(t, testname, files) + eliminate(t, errmap, errlist) + + // there should be no expected errors left + if len(errmap) > 0 { + t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", testname, len(errmap)) + for pos, list := range errmap { + for _, rx := range list { + t.Errorf("%s: %q", pos, rx) + } + } + } +} + +var testBuiltinsDeclared = false + +func TestCheck(t *testing.T) { + // Declare builtins for testing. + // Not done in an init func to avoid an init race with + // the construction of the Universe var. + if !testBuiltinsDeclared { + testBuiltinsDeclared = true + // Pkg == nil for Universe objects + def(&Func{Name: "assert", Type: &builtin{_Assert, "assert", 1, false, true}}) + def(&Func{Name: "trace", Type: &builtin{_Trace, "trace", 0, true, true}}) + } + + // For easy debugging w/o changing the testing code, + // if there is a local test file, only test that file. + const testfile = "testdata/test.go" + if fi, err := os.Stat(testfile); err == nil && !fi.IsDir() { + fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile) + checkFiles(t, testfile, []string{testfile}) + return + } + + // Otherwise, run all the tests. + for _, test := range tests { + checkFiles(t, test.name, test.files) + } +} diff --git a/src/pkg/go/types/const.go b/src/pkg/go/types/const.go new file mode 100644 index 000000000..503652e75 --- /dev/null +++ b/src/pkg/go/types/const.go @@ -0,0 +1,718 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements operations on constant values. + +package types + +import ( + "fmt" + "go/token" + "math/big" + "strconv" +) + +// TODO(gri) At the moment, constants are different types +// passed around as interface{} values. Introduce a Const +// interface and use methods instead of xConst functions. + +// Representation of constant values. +// +// bool -> bool (true, false) +// numeric -> int64, *big.Int, *big.Rat, Complex (ordered by increasing data structure "size") +// string -> string +// nil -> NilType (nilConst) +// +// Numeric constants are normalized after each operation such +// that they are represented by the "smallest" data structure +// required to represent the constant, independent of actual +// type. Non-numeric constants are always normalized. + +// Representation of complex numbers. +type Complex struct { + Re, Im *big.Rat +} + +func (c Complex) String() string { + if c.Re.Sign() == 0 { + return fmt.Sprintf("%si", c.Im) + } + // normalized complex values always have an imaginary part + return fmt.Sprintf("(%s + %si)", c.Re, c.Im) +} + +// Representation of nil. +type NilType struct{} + +func (NilType) String() string { + return "nil" +} + +// Frequently used values. +var ( + nilConst = NilType{} + zeroConst = int64(0) +) + +// int64 bounds +var ( + minInt64 = big.NewInt(-1 << 63) + maxInt64 = big.NewInt(1<<63 - 1) +) + +// normalizeIntConst returns the smallest constant representation +// for the specific value of x; either an int64 or a *big.Int value. +// +func normalizeIntConst(x *big.Int) interface{} { + if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 { + return x.Int64() + } + return x +} + +// normalizeRatConst returns the smallest constant representation +// for the specific value of x; either an int64, *big.Int, +// or *big.Rat value. +// +func normalizeRatConst(x *big.Rat) interface{} { + if x.IsInt() { + return normalizeIntConst(x.Num()) + } + return x +} + +// newComplex returns the smallest constant representation +// for the specific value re + im*i; either an int64, *big.Int, +// *big.Rat, or complex value. +// +func newComplex(re, im *big.Rat) interface{} { + if im.Sign() == 0 { + return normalizeRatConst(re) + } + return Complex{re, im} +} + +// makeRuneConst returns the int64 code point for the rune literal +// lit. The result is nil if lit is not a correct rune literal. +// +func makeRuneConst(lit string) interface{} { + if n := len(lit); n >= 2 { + if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil { + return int64(code) + } + } + return nil +} + +// makeRuneConst returns the smallest integer constant representation +// (int64, *big.Int) for the integer literal lit. The result is nil if +// lit is not a correct integer literal. +// +func makeIntConst(lit string) interface{} { + if x, err := strconv.ParseInt(lit, 0, 64); err == nil { + return x + } + if x, ok := new(big.Int).SetString(lit, 0); ok { + return x + } + return nil +} + +// makeFloatConst returns the smallest floating-point constant representation +// (int64, *big.Int, *big.Rat) for the floating-point literal lit. The result +// is nil if lit is not a correct floating-point literal. +// +func makeFloatConst(lit string) interface{} { + if x, ok := new(big.Rat).SetString(lit); ok { + return normalizeRatConst(x) + } + return nil +} + +// makeComplexConst returns the complex constant representation (Complex) for +// the imaginary literal lit. The result is nil if lit is not a correct imaginary +// literal. +// +func makeComplexConst(lit string) interface{} { + n := len(lit) + if n > 0 && lit[n-1] == 'i' { + if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok { + return newComplex(big.NewRat(0, 1), im) + } + } + return nil +} + +// makeStringConst returns the string constant representation (string) for +// the string literal lit. The result is nil if lit is not a correct string +// literal. +// +func makeStringConst(lit string) interface{} { + if s, err := strconv.Unquote(lit); err == nil { + return s + } + return nil +} + +// toImagConst returns the constant Complex(0, x) for a non-complex x. +func toImagConst(x interface{}) interface{} { + var im *big.Rat + switch x := x.(type) { + case int64: + im = big.NewRat(x, 1) + case *big.Int: + im = new(big.Rat).SetFrac(x, int1) + case *big.Rat: + im = x + default: + unreachable() + } + return Complex{rat0, im} +} + +// isZeroConst reports whether the value of constant x is 0. +// x must be normalized. +// +func isZeroConst(x interface{}) bool { + i, ok := x.(int64) // good enough since constants are normalized + return ok && i == 0 +} + +// isNegConst reports whether the value of constant x is < 0. +// x must be a non-complex numeric value. +// +func isNegConst(x interface{}) bool { + switch x := x.(type) { + case int64: + return x < 0 + case *big.Int: + return x.Sign() < 0 + case *big.Rat: + return x.Sign() < 0 + } + unreachable() + return false +} + +// isRepresentableConst reports whether the value of constant x can +// be represented as a value of the basic type Typ[as] without loss +// of precision. +// +func isRepresentableConst(x interface{}, ctxt *Context, as BasicKind) bool { + switch x := x.(type) { + case bool: + return as == Bool || as == UntypedBool + + case int64: + switch as { + case Int: + var s = uint(ctxt.sizeof(Typ[as])) * 8 + return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1 + case Int8: + const s = 8 + return -1<<(s-1) <= x && x <= 1<<(s-1)-1 + case Int16: + const s = 16 + return -1<<(s-1) <= x && x <= 1<<(s-1)-1 + case Int32: + const s = 32 + return -1<<(s-1) <= x && x <= 1<<(s-1)-1 + case Int64: + return true + case Uint, Uintptr: + var s = uint(ctxt.sizeof(Typ[as])) * 8 + return 0 <= x && x <= int64(1)<<(s-1)-1 + case Uint8: + const s = 8 + return 0 <= x && x <= 1<= 0 && x.BitLen() <= int(s) + case Uint64: + return x.Sign() >= 0 && x.BitLen() <= 64 + case Float32: + return true // TODO(gri) fix this + case Float64: + return true // TODO(gri) fix this + case Complex64: + return true // TODO(gri) fix this + case Complex128: + return true // TODO(gri) fix this + case UntypedInt, UntypedFloat, UntypedComplex: + return true + } + + case *big.Rat: + switch as { + case Float32: + return true // TODO(gri) fix this + case Float64: + return true // TODO(gri) fix this + case Complex64: + return true // TODO(gri) fix this + case Complex128: + return true // TODO(gri) fix this + case UntypedFloat, UntypedComplex: + return true + } + + case Complex: + switch as { + case Complex64: + return true // TODO(gri) fix this + case Complex128: + return true // TODO(gri) fix this + case UntypedComplex: + return true + } + + case string: + return as == String || as == UntypedString + + case NilType: + return as == UntypedNil || as == UnsafePointer + + default: + unreachable() + } + + return false +} + +var ( + int1 = big.NewInt(1) + rat0 = big.NewRat(0, 1) +) + +// complexity returns a measure of representation complexity for constant x. +func complexity(x interface{}) int { + switch x.(type) { + case bool, string, NilType: + return 1 + case int64: + return 2 + case *big.Int: + return 3 + case *big.Rat: + return 4 + case Complex: + return 5 + } + unreachable() + return 0 +} + +// matchConst returns the matching representation (same type) with the +// smallest complexity for two constant values x and y. They must be +// of the same "kind" (boolean, numeric, string, or NilType). +// +func matchConst(x, y interface{}) (_, _ interface{}) { + if complexity(x) > complexity(y) { + y, x = matchConst(y, x) + return x, y + } + // complexity(x) <= complexity(y) + + switch x := x.(type) { + case bool, Complex, string, NilType: + return x, y + + case int64: + switch y := y.(type) { + case int64: + return x, y + case *big.Int: + return big.NewInt(x), y + case *big.Rat: + return big.NewRat(x, 1), y + case Complex: + return Complex{big.NewRat(x, 1), rat0}, y + } + + case *big.Int: + switch y := y.(type) { + case *big.Int: + return x, y + case *big.Rat: + return new(big.Rat).SetFrac(x, int1), y + case Complex: + return Complex{new(big.Rat).SetFrac(x, int1), rat0}, y + } + + case *big.Rat: + switch y := y.(type) { + case *big.Rat: + return x, y + case Complex: + return Complex{x, rat0}, y + } + } + + unreachable() + return nil, nil +} + +// is32bit reports whether x can be represented using 32 bits. +func is32bit(x int64) bool { + return -1<<31 <= x && x <= 1<<31-1 +} + +// is63bit reports whether x can be represented using 63 bits. +func is63bit(x int64) bool { + return -1<<62 <= x && x <= 1<<62-1 +} + +// unaryOpConst returns the result of the constant evaluation op x where x is of the given type. +func unaryOpConst(x interface{}, ctxt *Context, op token.Token, typ *Basic) interface{} { + switch op { + case token.ADD: + return x // nothing to do + case token.SUB: + switch x := x.(type) { + case int64: + if z := -x; z != x { + return z // no overflow + } + // overflow - need to convert to big.Int + return normalizeIntConst(new(big.Int).Neg(big.NewInt(x))) + case *big.Int: + return normalizeIntConst(new(big.Int).Neg(x)) + case *big.Rat: + return normalizeRatConst(new(big.Rat).Neg(x)) + case Complex: + return newComplex(new(big.Rat).Neg(x.Re), new(big.Rat).Neg(x.Im)) + } + case token.XOR: + var z big.Int + switch x := x.(type) { + case int64: + z.Not(big.NewInt(x)) + case *big.Int: + z.Not(x) + default: + unreachable() + } + // For unsigned types, the result will be negative and + // thus "too large": We must limit the result size to + // the type's size. + if typ.Info&IsUnsigned != 0 { + s := uint(ctxt.sizeof(typ)) * 8 + z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<>). x must be an +// integer constant. +// +func shiftConst(x interface{}, s uint, op token.Token) interface{} { + switch x := x.(type) { + case int64: + switch op { + case token.SHL: + z := big.NewInt(x) + return normalizeIntConst(z.Lsh(z, s)) + case token.SHR: + return x >> s + } + + case *big.Int: + var z big.Int + switch op { + case token.SHL: + return normalizeIntConst(z.Lsh(x, s)) + case token.SHR: + return normalizeIntConst(z.Rsh(x, s)) + } + } + + unreachable() + return nil +} + +// compareConst returns the result of the constant comparison x op y; +// both operands must be of the same "kind" (boolean, numeric, string, +// or NilType). +// +func compareConst(x, y interface{}, op token.Token) (z bool) { + x, y = matchConst(x, y) + + // x == y => x == y + // x != y => x != y + // x > y => y < x + // x >= y => u <= x + swap := false + switch op { + case token.GTR: + swap = true + op = token.LSS + case token.GEQ: + swap = true + op = token.LEQ + } + + // x == y => x == y + // x != y => !(x == y) + // x < y => x < y + // x <= y => !(y < x) + negate := false + switch op { + case token.NEQ: + negate = true + op = token.EQL + case token.LEQ: + swap = !swap + negate = true + op = token.LSS + } + + if negate { + defer func() { z = !z }() + } + + if swap { + x, y = y, x + } + + switch x := x.(type) { + case bool: + if op == token.EQL { + return x == y.(bool) + } + + case int64: + y := y.(int64) + switch op { + case token.EQL: + return x == y + case token.LSS: + return x < y + } + + case *big.Int: + s := x.Cmp(y.(*big.Int)) + switch op { + case token.EQL: + return s == 0 + case token.LSS: + return s < 0 + } + + case *big.Rat: + s := x.Cmp(y.(*big.Rat)) + switch op { + case token.EQL: + return s == 0 + case token.LSS: + return s < 0 + } + + case Complex: + y := y.(Complex) + if op == token.EQL { + return x.Re.Cmp(y.Re) == 0 && x.Im.Cmp(y.Im) == 0 + } + + case string: + y := y.(string) + switch op { + case token.EQL: + return x == y + case token.LSS: + return x < y + } + + case NilType: + if op == token.EQL { + return x == y.(NilType) + } + } + + fmt.Printf("x = %s (%T), y = %s (%T)\n", x, x, y, y) + unreachable() + return +} diff --git a/src/pkg/go/types/conversions.go b/src/pkg/go/types/conversions.go new file mode 100644 index 000000000..fcbaf7717 --- /dev/null +++ b/src/pkg/go/types/conversions.go @@ -0,0 +1,129 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements typechecking of conversions. + +package types + +import ( + "go/ast" +) + +// conversion typechecks the type conversion conv to type typ. iota is the current +// value of iota or -1 if iota doesn't have a value in the current context. The result +// of the conversion is returned via x. If the conversion has type errors, the returned +// x is marked as invalid (x.mode == invalid). +// +func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) { + // all conversions have one argument + if len(conv.Args) != 1 { + check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv) + goto Error + } + + // evaluate argument + check.expr(x, conv.Args[0], nil, iota) + if x.mode == invalid { + goto Error + } + + if x.mode == constant && isConstType(typ) { + // constant conversion + // TODO(gri) implement this + } else { + // non-constant conversion + if !x.isConvertible(check.ctxt, typ) { + check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ) + goto Error + } + x.mode = value + } + + check.conversions[conv] = true // for cap/len checking + x.expr = conv + x.typ = typ + return + +Error: + x.mode = invalid +} + +func (x *operand) isConvertible(ctxt *Context, T Type) bool { + // "x is assignable to T" + if x.isAssignable(ctxt, T) { + return true + } + + // "x's type and T have identical underlying types" + V := x.typ + Vu := underlying(V) + Tu := underlying(T) + if IsIdentical(Vu, Tu) { + return true + } + + // "x's type and T are unnamed pointer types and their pointer base types have identical underlying types" + if V, ok := V.(*Pointer); ok { + if T, ok := T.(*Pointer); ok { + if IsIdentical(underlying(V.Base), underlying(T.Base)) { + return true + } + } + } + + // "x's type and T are both integer or floating point types" + if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) { + return true + } + + // "x's type and T are both complex types" + if isComplex(V) && isComplex(T) { + return true + } + + // "x is an integer or a slice of bytes or runes and T is a string type" + if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) { + return true + } + + // "x is a string and T is a slice of bytes or runes" + if isString(V) && isBytesOrRunes(Tu) { + return true + } + + // package unsafe: + // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" + if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) { + return true + } + // "and vice versa" + if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) { + return true + } + + return false +} + +func isUintptr(typ Type) bool { + t, ok := typ.(*Basic) + return ok && t.Kind == Uintptr +} + +func isUnsafePointer(typ Type) bool { + t, ok := typ.(*Basic) + return ok && t.Kind == UnsafePointer +} + +func isPointer(typ Type) bool { + _, ok := typ.(*Pointer) + return ok +} + +func isBytesOrRunes(typ Type) bool { + if s, ok := typ.(*Slice); ok { + t, ok := underlying(s.Elt).(*Basic) + return ok && (t.Kind == Byte || t.Kind == Rune) + } + return false +} diff --git a/src/pkg/go/types/errors.go b/src/pkg/go/types/errors.go new file mode 100644 index 000000000..62ee54791 --- /dev/null +++ b/src/pkg/go/types/errors.go @@ -0,0 +1,335 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements various error reporters. + +package types + +import ( + "bytes" + "fmt" + "go/ast" + "go/token" +) + +// TODO(gri) eventually assert and unimplemented should disappear. +func assert(p bool) { + if !p { + panic("assertion failed") + } +} + +func unreachable() { + panic("unreachable") +} + +func (check *checker) printTrace(format string, args []interface{}) { + const dots = ". . . . . . . . . . . . . . . . . . . . " + n := len(check.pos) - 1 + i := 3 * n + for i > len(dots) { + fmt.Print(dots) + i -= len(dots) + } + // i <= len(dots) + fmt.Printf("%s:\t", check.fset.Position(check.pos[n])) + fmt.Print(dots[0:i]) + fmt.Println(check.formatMsg(format, args)) +} + +func (check *checker) trace(pos token.Pos, format string, args ...interface{}) { + check.pos = append(check.pos, pos) + check.printTrace(format, args) +} + +func (check *checker) untrace(format string, args ...interface{}) { + if len(format) > 0 { + check.printTrace(format, args) + } + check.pos = check.pos[:len(check.pos)-1] +} + +func (check *checker) formatMsg(format string, args []interface{}) string { + for i, arg := range args { + switch a := arg.(type) { + case token.Pos: + args[i] = check.fset.Position(a).String() + case ast.Expr: + args[i] = exprString(a) + case Type: + args[i] = typeString(a) + case operand: + panic("internal error: should always pass *operand") + } + } + return fmt.Sprintf(format, args...) +} + +// dump is only needed for debugging +func (check *checker) dump(format string, args ...interface{}) { + fmt.Println(check.formatMsg(format, args)) +} + +func (check *checker) err(err error) { + if check.firsterr == nil { + check.firsterr = err + } + f := check.ctxt.Error + if f == nil { + panic(bailout{}) // report only first error + } + f(err) +} + +func (check *checker) errorf(pos token.Pos, format string, args ...interface{}) { + check.err(fmt.Errorf("%s: %s", check.fset.Position(pos), check.formatMsg(format, args))) +} + +func (check *checker) invalidAST(pos token.Pos, format string, args ...interface{}) { + check.errorf(pos, "invalid AST: "+format, args...) +} + +func (check *checker) invalidArg(pos token.Pos, format string, args ...interface{}) { + check.errorf(pos, "invalid argument: "+format, args...) +} + +func (check *checker) invalidOp(pos token.Pos, format string, args ...interface{}) { + check.errorf(pos, "invalid operation: "+format, args...) +} + +// exprString returns a (simplified) string representation for an expression. +func exprString(expr ast.Expr) string { + var buf bytes.Buffer + writeExpr(&buf, expr) + return buf.String() +} + +// TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a)) +func writeExpr(buf *bytes.Buffer, expr ast.Expr) { + switch x := expr.(type) { + case *ast.Ident: + buf.WriteString(x.Name) + + case *ast.BasicLit: + buf.WriteString(x.Value) + + case *ast.FuncLit: + buf.WriteString("(func literal)") + + case *ast.CompositeLit: + buf.WriteString("(composite literal)") + + case *ast.ParenExpr: + buf.WriteByte('(') + writeExpr(buf, x.X) + buf.WriteByte(')') + + case *ast.SelectorExpr: + writeExpr(buf, x.X) + buf.WriteByte('.') + buf.WriteString(x.Sel.Name) + + case *ast.IndexExpr: + writeExpr(buf, x.X) + buf.WriteByte('[') + writeExpr(buf, x.Index) + buf.WriteByte(']') + + case *ast.SliceExpr: + writeExpr(buf, x.X) + buf.WriteByte('[') + if x.Low != nil { + writeExpr(buf, x.Low) + } + buf.WriteByte(':') + if x.High != nil { + writeExpr(buf, x.High) + } + buf.WriteByte(']') + + case *ast.TypeAssertExpr: + writeExpr(buf, x.X) + buf.WriteString(".(...)") + + case *ast.CallExpr: + writeExpr(buf, x.Fun) + buf.WriteByte('(') + for i, arg := range x.Args { + if i > 0 { + buf.WriteString(", ") + } + writeExpr(buf, arg) + } + buf.WriteByte(')') + + case *ast.StarExpr: + buf.WriteByte('*') + writeExpr(buf, x.X) + + case *ast.UnaryExpr: + buf.WriteString(x.Op.String()) + writeExpr(buf, x.X) + + case *ast.BinaryExpr: + // The AST preserves source-level parentheses so there is + // no need to introduce parentheses here for correctness. + writeExpr(buf, x.X) + buf.WriteByte(' ') + buf.WriteString(x.Op.String()) + buf.WriteByte(' ') + writeExpr(buf, x.Y) + + default: + fmt.Fprintf(buf, "", x) + } +} + +// typeString returns a string representation for typ. +func typeString(typ Type) string { + var buf bytes.Buffer + writeType(&buf, typ) + return buf.String() +} + +func writeParams(buf *bytes.Buffer, params []*Var, isVariadic bool) { + buf.WriteByte('(') + for i, par := range params { + if i > 0 { + buf.WriteString(", ") + } + if par.Name != "" { + buf.WriteString(par.Name) + buf.WriteByte(' ') + } + if isVariadic && i == len(params)-1 { + buf.WriteString("...") + } + writeType(buf, par.Type) + } + buf.WriteByte(')') +} + +func writeSignature(buf *bytes.Buffer, sig *Signature) { + writeParams(buf, sig.Params, sig.IsVariadic) + if len(sig.Results) == 0 { + // no result + return + } + + buf.WriteByte(' ') + if len(sig.Results) == 1 && sig.Results[0].Name == "" { + // single unnamed result + writeType(buf, sig.Results[0].Type.(Type)) + return + } + + // multiple or named result(s) + writeParams(buf, sig.Results, false) +} + +func writeType(buf *bytes.Buffer, typ Type) { + switch t := typ.(type) { + case nil: + buf.WriteString("") + + case *Basic: + buf.WriteString(t.Name) + + case *Array: + fmt.Fprintf(buf, "[%d]", t.Len) + writeType(buf, t.Elt) + + case *Slice: + buf.WriteString("[]") + writeType(buf, t.Elt) + + case *Struct: + buf.WriteString("struct{") + for i, f := range t.Fields { + if i > 0 { + buf.WriteString("; ") + } + if !f.IsAnonymous { + buf.WriteString(f.Name) + buf.WriteByte(' ') + } + writeType(buf, f.Type) + if f.Tag != "" { + fmt.Fprintf(buf, " %q", f.Tag) + } + } + buf.WriteByte('}') + + case *Pointer: + buf.WriteByte('*') + writeType(buf, t.Base) + + case *Result: + writeParams(buf, t.Values, false) + + case *Signature: + buf.WriteString("func") + writeSignature(buf, t) + + case *builtin: + fmt.Fprintf(buf, "", t.name) + + case *Interface: + buf.WriteString("interface{") + for i, m := range t.Methods { + if i > 0 { + buf.WriteString("; ") + } + buf.WriteString(m.Name) + writeSignature(buf, m.Type) + } + buf.WriteByte('}') + + case *Map: + buf.WriteString("map[") + writeType(buf, t.Key) + buf.WriteByte(']') + writeType(buf, t.Elt) + + case *Chan: + var s string + switch t.Dir { + case ast.SEND: + s = "chan<- " + case ast.RECV: + s = "<-chan " + default: + s = "chan " + } + buf.WriteString(s) + writeType(buf, t.Elt) + + case *NamedType: + s := "" + if obj := t.Obj; obj != nil { + if obj.Pkg != nil && obj.Pkg.Path != "" { + buf.WriteString(obj.Pkg.Path) + buf.WriteString(".") + } + s = t.Obj.GetName() + } + buf.WriteString(s) + + default: + fmt.Fprintf(buf, "", t) + } +} + +func (t *Array) String() string { return typeString(t) } +func (t *Basic) String() string { return typeString(t) } +func (t *Chan) String() string { return typeString(t) } +func (t *Interface) String() string { return typeString(t) } +func (t *Map) String() string { return typeString(t) } +func (t *NamedType) String() string { return typeString(t) } +func (t *Pointer) String() string { return typeString(t) } +func (t *Result) String() string { return typeString(t) } +func (t *Signature) String() string { return typeString(t) } +func (t *Slice) String() string { return typeString(t) } +func (t *Struct) String() string { return typeString(t) } +func (t *builtin) String() string { return typeString(t) } diff --git a/src/pkg/go/types/exportdata.go b/src/pkg/go/types/exportdata.go new file mode 100644 index 000000000..1f6a3c725 --- /dev/null +++ b/src/pkg/go/types/exportdata.go @@ -0,0 +1,111 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements FindGcExportData. + +package types + +import ( + "bufio" + "errors" + "fmt" + "io" + "strconv" + "strings" +) + +func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { + // See $GOROOT/include/ar.h. + hdr := make([]byte, 16+12+6+6+8+10+2) + _, err = io.ReadFull(r, hdr) + if err != nil { + return + } + // leave for debugging + if false { + fmt.Printf("header: %s", hdr) + } + s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) + size, err = strconv.Atoi(s) + if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { + err = errors.New("invalid archive header") + return + } + name = strings.TrimSpace(string(hdr[:16])) + return +} + +// FindGcExportData positions the reader r at the beginning of the +// export data section of an underlying GC-created object/archive +// file by reading from it. The reader must be positioned at the +// start of the file before calling this function. +// +func FindGcExportData(r *bufio.Reader) (err error) { + // Read first line to make sure this is an object file. + line, err := r.ReadSlice('\n') + if err != nil { + return + } + if string(line) == "!\n" { + // Archive file. Scan to __.PKGDEF, which should + // be second archive entry. + var name string + var size int + + // First entry should be __.GOSYMDEF. + // Older archives used __.SYMDEF, so allow that too. + // Read and discard. + if name, size, err = readGopackHeader(r); err != nil { + return + } + if name != "__.SYMDEF" && name != "__.GOSYMDEF" { + err = errors.New("go archive does not begin with __.SYMDEF or __.GOSYMDEF") + return + } + const block = 4096 + tmp := make([]byte, block) + for size > 0 { + n := size + if n > block { + n = block + } + if _, err = io.ReadFull(r, tmp[:n]); err != nil { + return + } + size -= n + } + + // Second entry should be __.PKGDEF. + if name, size, err = readGopackHeader(r); err != nil { + return + } + if name != "__.PKGDEF" { + err = errors.New("go archive is missing __.PKGDEF") + return + } + + // Read first line of __.PKGDEF data, so that line + // is once again the first line of the input. + if line, err = r.ReadSlice('\n'); err != nil { + return + } + } + + // Now at __.PKGDEF in archive or still at beginning of file. + // Either way, line should begin with "go object ". + if !strings.HasPrefix(string(line), "go object ") { + err = errors.New("not a go object file") + return + } + + // Skip over object header to export data. + // Begins after first line with $$. + for line[0] != '$' { + if line, err = r.ReadSlice('\n'); err != nil { + return + } + } + + return +} diff --git a/src/pkg/go/types/expr.go b/src/pkg/go/types/expr.go new file mode 100644 index 000000000..86d782d48 --- /dev/null +++ b/src/pkg/go/types/expr.go @@ -0,0 +1,1520 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements typechecking of expressions. + +package types + +import ( + "go/ast" + "go/token" + "strconv" +) + +// TODO(gri) Cleanups +// - don't print error messages referring to invalid types (they are likely spurious errors) +// - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values? +// - rethink error handling: should all callers check if x.mode == valid after making a call? +// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used +// - use "" or "_" consistently for anonymous identifiers? (e.g. reeceivers that have no name) +// - consider storing error messages in invalid operands for better error messages/debugging output + +// TODO(gri) API issues +// - clients need access to builtins type information +// - API tests are missing (e.g., identifiers should be handled as expressions in callbacks) + +func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) { + if list == nil { + return + } + var last *Var + for i, field := range list.List { + ftype := field.Type + if t, _ := ftype.(*ast.Ellipsis); t != nil { + ftype = t.Elt + if variadicOk && i == len(list.List)-1 { + isVariadic = true + } else { + check.invalidAST(field.Pos(), "... not permitted") + // ok to continue + } + } + // the parser ensures that f.Tag is nil and we don't + // care if a constructed AST contains a non-nil tag + typ := check.typ(ftype, true) + if len(field.Names) > 0 { + // named parameter + for _, name := range field.Names { + par := check.lookup(name).(*Var) + par.Type = typ + last = par + copy := *par + params = append(params, ©) + } + } else { + // anonymous parameter + par := &Var{Type: typ} + last = nil // not accessible inside function + params = append(params, par) + } + } + // For a variadic function, change the last parameter's object type + // from T to []T (this is the type used inside the function), but + // keep the params list unchanged (this is the externally visible type). + if isVariadic && last != nil { + last.Type = &Slice{Elt: last.Type} + } + return +} + +func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) { + if list == nil { + return + } + for _, f := range list.List { + typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces + // the parser ensures that f.Tag is nil and we don't + // care if a constructed AST contains a non-nil tag + if len(f.Names) > 0 { + // methods (the parser ensures that there's only one + // and we don't care if a constructed AST has more) + sig, ok := typ.(*Signature) + if !ok { + check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ) + continue + } + for _, name := range f.Names { + methods = append(methods, &Method{QualifiedName{check.pkg, name.Name}, sig}) + } + } else { + // embedded interface + utyp := underlying(typ) + if ityp, ok := utyp.(*Interface); ok { + methods = append(methods, ityp.Methods...) + } else if utyp != Typ[Invalid] { + // if utyp is invalid, don't complain (the root cause was reported before) + check.errorf(f.Type.Pos(), "%s is not an interface type", typ) + } + } + } + // Check for double declarations. + // The parser inserts methods into an interface-local scope, so local + // double declarations are reported by the parser already. We need to + // check again for conflicts due to embedded interfaces. This will lead + // to a 2nd error message if the double declaration was reported before + // by the parser. + // TODO(gri) clean this up a bit + seen := make(map[string]bool) + for _, m := range methods { + if seen[m.Name] { + check.errorf(list.Pos(), "multiple methods named %s", m.Name) + return // keep multiple entries, lookup will only return the first entry + } + seen[m.Name] = true + } + return +} + +func (check *checker) tag(t *ast.BasicLit) string { + if t != nil { + if t.Kind == token.STRING { + if val, err := strconv.Unquote(t.Value); err == nil { + return val + } + } + check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value) + } + return "" +} + +func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field) { + if list == nil { + return + } + + var typ Type // current field typ + var tag string // current field tag + add := func(name string, isAnonymous bool) { + fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, isAnonymous}) + } + + for _, f := range list.List { + typ = check.typ(f.Type, cycleOk) + tag = check.tag(f.Tag) + if len(f.Names) > 0 { + // named fields + for _, name := range f.Names { + add(name.Name, false) + } + } else { + // anonymous field + switch t := deref(typ).(type) { + case *Basic: + add(t.Name, true) + case *NamedType: + add(t.Obj.GetName(), true) + default: + if typ != Typ[Invalid] { + check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ) + } + } + } + } + + return +} + +type opPredicates map[token.Token]func(Type) bool + +var unaryOpPredicates = opPredicates{ + token.ADD: isNumeric, + token.SUB: isNumeric, + token.XOR: isInteger, + token.NOT: isBoolean, +} + +func (check *checker) op(m opPredicates, x *operand, op token.Token) bool { + if pred := m[op]; pred != nil { + if !pred(x.typ) { + check.invalidOp(x.pos(), "operator %s not defined for %s", op, x) + return false + } + } else { + check.invalidAST(x.pos(), "unknown operator %s", op) + return false + } + return true +} + +func (check *checker) unary(x *operand, op token.Token) { + switch op { + case token.AND: + // spec: "As an exception to the addressability + // requirement x may also be a composite literal." + if _, ok := unparen(x.expr).(*ast.CompositeLit); ok { + x.mode = variable + } + if x.mode != variable { + check.invalidOp(x.pos(), "cannot take address of %s", x) + goto Error + } + x.typ = &Pointer{Base: x.typ} + return + + case token.ARROW: + typ, ok := underlying(x.typ).(*Chan) + if !ok { + check.invalidOp(x.pos(), "cannot receive from non-channel %s", x) + goto Error + } + if typ.Dir&ast.RECV == 0 { + check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x) + goto Error + } + x.mode = valueok + x.typ = typ.Elt + return + } + + if !check.op(unaryOpPredicates, x, op) { + goto Error + } + + if x.mode == constant { + typ := underlying(x.typ).(*Basic) + x.val = unaryOpConst(x.val, check.ctxt, op, typ) + // Typed constants must be representable in + // their type after each constant operation. + check.isRepresentable(x, typ) + return + } + + x.mode = value + // x.typ remains unchanged + return + +Error: + x.mode = invalid +} + +func isShift(op token.Token) bool { + return op == token.SHL || op == token.SHR +} + +func isComparison(op token.Token) bool { + // Note: tokens are not ordered well to make this much easier + switch op { + case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: + return true + } + return false +} + +// isRepresentable checks that a constant operand is representable in the given type. +func (check *checker) isRepresentable(x *operand, typ *Basic) { + if x.mode != constant || isUntyped(typ) { + return + } + + if !isRepresentableConst(x.val, check.ctxt, typ.Kind) { + var msg string + if isNumeric(x.typ) && isNumeric(typ) { + msg = "%s overflows %s" + } else { + msg = "cannot convert %s to %s" + } + check.errorf(x.pos(), msg, x, typ) + x.mode = invalid + } +} + +// updateExprType updates the type of all untyped nodes in the +// expression tree of x to typ. If shiftOp is set, x is the lhs +// of a shift expression. In that case, and if x is in the set +// of shift operands with delayed type checking, and typ is not +// an untyped type, updateExprType will check if typ is an +// integer type. +// If Context.Expr != nil, it is called for all nodes that are +// now assigned their final (not untyped) type. +func (check *checker) updateExprType(x ast.Expr, typ Type, shiftOp bool) { + switch x := x.(type) { + case *ast.BadExpr, + *ast.FuncLit, + *ast.CompositeLit, + *ast.SelectorExpr, + *ast.IndexExpr, + *ast.SliceExpr, + *ast.TypeAssertExpr, + *ast.CallExpr, + *ast.StarExpr, + *ast.KeyValueExpr, + *ast.ArrayType, + *ast.StructType, + *ast.FuncType, + *ast.InterfaceType, + *ast.MapType, + *ast.ChanType: + // these expression are never untyped - nothing to do + return + + case *ast.Ident, *ast.BasicLit: + // update type + + case *ast.ParenExpr: + check.updateExprType(x.X, typ, false) + + case *ast.UnaryExpr: + check.updateExprType(x.X, typ, false) + + case *ast.BinaryExpr: + if isComparison(x.Op) { + // result type is independent of operand types + } else if isShift(x.Op) { + // result type depends only on lhs operand + check.updateExprType(x.X, typ, true) + } else { + // operand types match result type + check.updateExprType(x.X, typ, false) + check.updateExprType(x.Y, typ, false) + } + + case *ast.Ellipsis: + unreachable() + default: + unreachable() + } + + // TODO(gri) t should always exist, shouldn't it? + if t := check.untyped[x]; t != nil { + if isUntyped(typ) { + check.untyped[x] = typ.(*Basic) + } else { + // notify clients of final type for x + if f := check.ctxt.Expr; f != nil { + f(x, typ, check.constants[x]) + } + delete(check.untyped, x) + delete(check.constants, x) + // check delayed shift + // Note: Using shiftOp is an optimization: it prevents + // map lookups when we know x is not a shiftOp in the + // first place. + if shiftOp && check.shiftOps[x] { + if !isInteger(typ) { + check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ) + } + delete(check.shiftOps, x) + } + } + } +} + +// convertUntyped attempts to set the type of an untyped value to the target type. +func (check *checker) convertUntyped(x *operand, target Type) { + if x.mode == invalid || !isUntyped(x.typ) { + return + } + + // TODO(gri) Sloppy code - clean up. This function is central + // to assignment and expression checking. + + if isUntyped(target) { + // both x and target are untyped + xkind := x.typ.(*Basic).Kind + tkind := target.(*Basic).Kind + if isNumeric(x.typ) && isNumeric(target) { + if xkind < tkind { + x.typ = target + check.updateExprType(x.expr, target, false) + } + } else if xkind != tkind { + goto Error + } + return + } + + // typed target + switch t := underlying(target).(type) { + case nil: + // We may reach here due to previous type errors. + // Be conservative and don't crash. + x.mode = invalid + return + case *Basic: + check.isRepresentable(x, t) + if x.mode == invalid { + return // error already reported + } + case *Interface: + if !x.isNil() && len(t.Methods) > 0 /* empty interfaces are ok */ { + goto Error + } + // Update operand types to the default type rather then + // the target (interface) type: values must have concrete + // dynamic types. If the value is nil, keep it untyped + // (this is important for tools such as go vet which need + // the dynamic type for argument checking of say, print + // functions) + if x.isNil() { + target = Typ[UntypedNil] + } else { + // cannot assign untyped values to non-empty interfaces + if len(t.Methods) > 0 { + goto Error + } + target = defaultType(x.typ) + } + case *Pointer, *Signature, *Slice, *Map, *Chan: + if !x.isNil() { + goto Error + } + // keep nil untyped - see comment for interfaces, above + target = Typ[UntypedNil] + default: + if debug { + check.dump("convertUntyped(x = %v, target = %v)", x, target) + } + unreachable() + } + + x.typ = target + check.updateExprType(x.expr, target, false) + return + +Error: + check.errorf(x.pos(), "cannot convert %s to %s", x, target) + x.mode = invalid +} + +func (check *checker) comparison(x, y *operand, op token.Token) { + // TODO(gri) deal with interface vs non-interface comparison + + valid := false + if x.isAssignable(check.ctxt, y.typ) || y.isAssignable(check.ctxt, x.typ) { + switch op { + case token.EQL, token.NEQ: + valid = isComparable(x.typ) || + x.isNil() && hasNil(y.typ) || + y.isNil() && hasNil(x.typ) + case token.LSS, token.LEQ, token.GTR, token.GEQ: + valid = isOrdered(x.typ) + default: + unreachable() + } + } + + if !valid { + check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y) + x.mode = invalid + return + } + + if x.mode == constant && y.mode == constant { + x.val = compareConst(x.val, y.val, op) + } else { + x.mode = value + } + + x.typ = Typ[UntypedBool] +} + +func (check *checker) shift(x, y *operand, op token.Token) { + // spec: "The right operand in a shift expression must have unsigned + // integer type or be an untyped constant that can be converted to + // unsigned integer type." + switch { + case isInteger(y.typ) && isUnsigned(y.typ): + // nothing to do + case y.mode == constant && isUntyped(y.typ): + check.convertUntyped(x, Typ[UntypedInt]) + default: + check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) + x.mode = invalid + return + } + + if x.mode == constant { + if y.mode == constant { + // constant shift - lhs must be (representable as) an integer + if isUntyped(x.typ) { + if !isRepresentableConst(x.val, check.ctxt, UntypedInt) { + check.invalidOp(x.pos(), "shifted operand %s must be integer", x) + x.mode = invalid + return + } + x.typ = Typ[UntypedInt] + } + assert(x.isInteger(check.ctxt)) + + // rhs must be within reasonable bounds + const stupidShift = 1024 + s, ok := y.val.(int64) + if !ok || s < 0 || s >= stupidShift { + check.invalidOp(y.pos(), "%s: stupid shift", y) + x.mode = invalid + return + } + + // everything's ok + x.val = shiftConst(x.val, uint(s), op) + return + } + + // non-constant shift with constant lhs + if isUntyped(x.typ) { + // spec: "If the left operand of a non-constant shift expression is + // an untyped constant, the type of the constant is what it would be + // if the shift expression were replaced by its left operand alone; + // the type is int if it cannot be determined from the context (for + // instance, if the shift expression is an operand in a comparison + // against an untyped constant)". + + // delay operand checking until we know the type + check.shiftOps[x.expr] = true + x.mode = value + return + } + } + + // non-constant shift - lhs must be an integer + if !isInteger(x.typ) { + check.invalidOp(x.pos(), "shifted operand %s must be integer", x) + x.mode = invalid + return + } + + // non-constant shift + x.mode = value +} + +var binaryOpPredicates = opPredicates{ + token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) }, + token.SUB: isNumeric, + token.MUL: isNumeric, + token.QUO: isNumeric, + token.REM: isInteger, + + token.AND: isInteger, + token.OR: isInteger, + token.XOR: isInteger, + token.AND_NOT: isInteger, + + token.LAND: isBoolean, + token.LOR: isBoolean, +} + +func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota int) { + var y operand + + check.expr(x, lhs, nil, iota) + check.expr(&y, rhs, nil, iota) + + if x.mode == invalid { + return + } + if y.mode == invalid { + x.mode = invalid + x.expr = y.expr + return + } + + if isShift(op) { + check.shift(x, &y, op) + return + } + + check.convertUntyped(x, y.typ) + if x.mode == invalid { + return + } + check.convertUntyped(&y, x.typ) + if y.mode == invalid { + x.mode = invalid + return + } + + if isComparison(op) { + check.comparison(x, &y, op) + return + } + + if !IsIdentical(x.typ, y.typ) { + check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ) + x.mode = invalid + return + } + + if !check.op(binaryOpPredicates, x, op) { + x.mode = invalid + return + } + + if (op == token.QUO || op == token.REM) && y.mode == constant && isZeroConst(y.val) { + check.invalidOp(y.pos(), "division by zero") + x.mode = invalid + return + } + + if x.mode == constant && y.mode == constant { + typ := underlying(x.typ).(*Basic) + x.val = binaryOpConst(x.val, y.val, op, typ) + // Typed constants must be representable in + // their type after each constant operation. + check.isRepresentable(x, typ) + return + } + + x.mode = value + // x.typ is unchanged +} + +// index checks an index expression for validity. If length >= 0, it is the upper +// bound for the index. The result is a valid index >= 0, or a negative value. +// +func (check *checker) index(index ast.Expr, length int64, iota int) int64 { + var x operand + + check.expr(&x, index, nil, iota) + if !x.isInteger(check.ctxt) { + check.errorf(x.pos(), "index %s must be integer", &x) + return -1 + } + if x.mode != constant { + return -1 // we cannot check more + } + // The spec doesn't require int64 indices, but perhaps it should. + i, ok := x.val.(int64) + if !ok { + check.errorf(x.pos(), "stupid index %s", &x) + return -1 + } + if i < 0 { + check.errorf(x.pos(), "index %s must not be negative", &x) + return -1 + } + if length >= 0 && i >= length { + check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length) + return -1 + } + + return i +} + +// compositeLitKey resolves unresolved composite literal keys. +// For details, see comment in go/parser/parser.go, method parseElement. +func (check *checker) compositeLitKey(key ast.Expr) { + if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil { + if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil { + check.register(ident, obj) + } else if obj := Universe.Lookup(ident.Name); obj != nil { + check.register(ident, obj) + } else { + check.errorf(ident.Pos(), "undeclared name: %s", ident.Name) + } + } +} + +// indexElts checks the elements (elts) of an array or slice composite literal +// against the literal's element type (typ), and the element indices against +// the literal length if known (length >= 0). It returns the length of the +// literal (maximum index value + 1). +// +func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota int) int64 { + visited := make(map[int64]bool, len(elts)) + var index, max int64 + for _, e := range elts { + // determine and check index + validIndex := false + eval := e + if kv, _ := e.(*ast.KeyValueExpr); kv != nil { + check.compositeLitKey(kv.Key) + if i := check.index(kv.Key, length, iota); i >= 0 { + index = i + validIndex = true + } + eval = kv.Value + } else if length >= 0 && index >= length { + check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length) + } else { + validIndex = true + } + + // if we have a valid index, check for duplicate entries + if validIndex { + if visited[index] { + check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index) + } + visited[index] = true + } + index++ + if index > max { + max = index + } + + // check element against composite literal element type + var x operand + check.expr(&x, eval, typ, iota) + if !check.assignment(&x, typ) && x.mode != invalid { + check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ) + } + } + return max +} + +// argument typechecks passing an argument arg (if arg != nil) or +// x (if arg == nil) to the i'th parameter of the given signature. +// If passSlice is set, the argument is followed by ... in the call. +// +func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) { + // determine parameter + var par *Var + n := len(sig.Params) + if i < n { + par = sig.Params[i] + } else if sig.IsVariadic { + par = sig.Params[n-1] + } else { + check.errorf(arg.Pos(), "too many arguments") + return + } + + // determine argument + var z operand + z.mode = variable + z.expr = nil // TODO(gri) can we do better here? (for good error messages) + z.typ = par.Type + + if arg != nil { + check.expr(x, arg, z.typ, -1) + } + if x.mode == invalid { + return // ignore this argument + } + + // check last argument of the form x... + if passSlice { + if i+1 != n { + check.errorf(x.pos(), "can only use ... with matching parameter") + return // ignore this argument + } + // spec: "If the final argument is assignable to a slice type []T, + // it may be passed unchanged as the value for a ...T parameter if + // the argument is followed by ..." + z.typ = &Slice{Elt: z.typ} // change final parameter type to []T + } + + if !check.assignment(x, z.typ) && x.mode != invalid { + check.errorf(x.pos(), "cannot pass argument %s to %s", x, &z) + } +} + +var emptyResult Result + +func (check *checker) callExpr(x *operand) { + var typ Type + var val interface{} + switch x.mode { + case invalid: + return // nothing to do + case novalue: + typ = &emptyResult + case constant: + typ = x.typ + val = x.val + default: + typ = x.typ + } + + // if the operand is untyped, delay notification + // until it becomes typed or until the end of + // type checking + if isUntyped(typ) { + check.untyped[x.expr] = typ.(*Basic) + if val != nil { + check.constants[x.expr] = val + } + return + } + + // TODO(gri) ensure that literals always report + // their dynamic (never interface) type. + // This is not the case yet. + + if check.ctxt.Expr != nil { + check.ctxt.Expr(x.expr, typ, val) + } +} + +// rawExpr typechecks expression e and initializes x with the expression +// value or type. If an error occurred, x.mode is set to invalid. +// If hint != nil, it is the type of a composite literal element. +// iota >= 0 indicates that the expression is part of a constant declaration. +// cycleOk indicates whether it is ok for a type expression to refer to itself. +// +func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool) { + if trace { + c := "" + if cycleOk { + c = " ⨁" + } + check.trace(e.Pos(), "%s (%s, %d%s)", e, typeString(hint), iota, c) + defer check.untrace("=> %s", x) + } + + defer check.callExpr(x) + + switch e := e.(type) { + case *ast.BadExpr: + goto Error // error was reported before + + case *ast.Ident: + if e.Name == "_" { + check.invalidOp(e.Pos(), "cannot use _ as value or type") + goto Error + } + obj := check.lookup(e) + if obj == nil { + goto Error // error was reported before + } + check.object(obj, cycleOk) + switch obj := obj.(type) { + case *Package: + check.errorf(e.Pos(), "use of package %s not in selector", obj.Name) + goto Error + case *Const: + if obj.Val == nil { + goto Error // cycle detected + } + x.mode = constant + if obj == universeIota { + if iota < 0 { + check.invalidAST(e.Pos(), "cannot use iota outside constant declaration") + goto Error + } + x.val = int64(iota) + } else { + x.val = obj.Val + } + case *TypeName: + x.mode = typexpr + if !cycleOk && underlying(obj.Type) == nil { + check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name) + x.expr = e + x.typ = Typ[Invalid] + return // don't goto Error - need x.mode == typexpr + } + case *Var: + x.mode = variable + case *Func: + x.mode = value + default: + unreachable() + } + x.typ = obj.GetType() + + case *ast.Ellipsis: + // ellipses are handled explicitly where they are legal + // (array composite literals and parameter lists) + check.errorf(e.Pos(), "invalid use of '...'") + goto Error + + case *ast.BasicLit: + x.setConst(e.Kind, e.Value) + if x.mode == invalid { + check.invalidAST(e.Pos(), "invalid literal %v", e.Value) + goto Error + } + + case *ast.FuncLit: + if sig, ok := check.typ(e.Type, false).(*Signature); ok { + x.mode = value + x.typ = sig + check.later(nil, sig, e.Body) + } else { + check.invalidAST(e.Pos(), "invalid function literal %s", e) + goto Error + } + + case *ast.CompositeLit: + typ := hint + openArray := false + if e.Type != nil { + // [...]T array types may only appear with composite literals. + // Check for them here so we don't have to handle ... in general. + typ = nil + if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil { + if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil { + // We have an "open" [...]T array type. + // Create a new ArrayType with unknown length (-1) + // and finish setting it up after analyzing the literal. + typ = &Array{Len: -1, Elt: check.typ(atyp.Elt, cycleOk)} + openArray = true + } + } + if typ == nil { + typ = check.typ(e.Type, false) + } + } + if typ == nil { + check.errorf(e.Pos(), "missing type in composite literal") + goto Error + } + + switch utyp := underlying(deref(typ)).(type) { + case *Struct: + if len(e.Elts) == 0 { + break + } + fields := utyp.Fields + if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { + // all elements must have keys + visited := make([]bool, len(fields)) + for _, e := range e.Elts { + kv, _ := e.(*ast.KeyValueExpr) + if kv == nil { + check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal") + continue + } + key, _ := kv.Key.(*ast.Ident) + if key == nil { + check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) + continue + } + i := utyp.fieldIndex(QualifiedName{check.pkg, key.Name}) + if i < 0 { + check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) + continue + } + // 0 <= i < len(fields) + if visited[i] { + check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) + continue + } + visited[i] = true + check.expr(x, kv.Value, nil, iota) + etyp := fields[i].Type + if !check.assignment(x, etyp) { + if x.mode != invalid { + check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) + } + continue + } + } + } else { + // no element must have a key + for i, e := range e.Elts { + if kv, _ := e.(*ast.KeyValueExpr); kv != nil { + check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal") + continue + } + check.expr(x, e, nil, iota) + if i >= len(fields) { + check.errorf(x.pos(), "too many values in struct literal") + break // cannot continue + } + // i < len(fields) + etyp := fields[i].Type + if !check.assignment(x, etyp) { + if x.mode != invalid { + check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) + } + continue + } + } + if len(e.Elts) < len(fields) { + check.errorf(e.Rbrace, "too few values in struct literal") + // ok to continue + } + } + + case *Array: + n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota) + // if we have an "open" [...]T array, set the length now that we know it + if openArray { + utyp.Len = n + } + + case *Slice: + check.indexedElts(e.Elts, utyp.Elt, -1, iota) + + case *Map: + visited := make(map[interface{}]bool, len(e.Elts)) + for _, e := range e.Elts { + kv, _ := e.(*ast.KeyValueExpr) + if kv == nil { + check.errorf(e.Pos(), "missing key in map literal") + continue + } + check.compositeLitKey(kv.Key) + check.expr(x, kv.Key, nil, iota) + if !check.assignment(x, utyp.Key) { + if x.mode != invalid { + check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key) + } + continue + } + if x.mode == constant { + if visited[x.val] { + check.errorf(x.pos(), "duplicate key %s in map literal", x.val) + continue + } + visited[x.val] = true + } + check.expr(x, kv.Value, utyp.Elt, iota) + if !check.assignment(x, utyp.Elt) { + if x.mode != invalid { + check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt) + } + continue + } + } + + default: + check.errorf(e.Pos(), "%s is not a valid composite literal type", typ) + goto Error + } + + x.mode = value + x.typ = typ + + case *ast.ParenExpr: + check.rawExpr(x, e.X, nil, iota, cycleOk) + + case *ast.SelectorExpr: + sel := e.Sel.Name + // If the identifier refers to a package, handle everything here + // so we don't need a "package" mode for operands: package names + // can only appear in qualified identifiers which are mapped to + // selector expressions. + if ident, ok := e.X.(*ast.Ident); ok { + if pkg, ok := check.lookup(ident).(*Package); ok { + exp := pkg.Scope.Lookup(sel) + // gcimported package scopes contain non-exported + // objects such as types used in partially exported + // objects - do not accept them + if exp == nil || !ast.IsExported(exp.GetName()) { + check.errorf(e.Pos(), "cannot refer to unexported %s", e) + goto Error + } + check.register(e.Sel, exp) + // Simplified version of the code for *ast.Idents: + // - imported packages use types.Scope and types.Objects + // - imported objects are always fully initialized + switch exp := exp.(type) { + case *Const: + assert(exp.Val != nil) + x.mode = constant + x.typ = exp.Type + x.val = exp.Val + case *TypeName: + x.mode = typexpr + x.typ = exp.Type + case *Var: + x.mode = variable + x.typ = exp.Type + case *Func: + x.mode = value + x.typ = exp.Type + default: + unreachable() + } + x.expr = e + return + } + } + + check.exprOrType(x, e.X, iota, false) + if x.mode == invalid { + goto Error + } + res := lookupField(x.typ, QualifiedName{check.pkg, sel}) + if res.mode == invalid { + check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) + goto Error + } + if x.mode == typexpr { + // method expression + sig, ok := res.typ.(*Signature) + if !ok { + check.invalidOp(e.Pos(), "%s has no method %s", x, sel) + goto Error + } + // the receiver type becomes the type of the first function + // argument of the method expression's function type + // TODO(gri) at the moment, method sets don't correctly track + // pointer vs non-pointer receivers => typechecker is too lenient + x.mode = value + x.typ = &Signature{ + Params: append([]*Var{{Type: x.typ}}, sig.Params...), + Results: sig.Results, + IsVariadic: sig.IsVariadic, + } + } else { + // regular selector + x.mode = res.mode + x.typ = res.typ + } + + case *ast.IndexExpr: + check.expr(x, e.X, nil, iota) + if x.mode == invalid { + goto Error + } + + valid := false + length := int64(-1) // valid if >= 0 + switch typ := underlying(x.typ).(type) { + case *Basic: + if isString(typ) { + valid = true + if x.mode == constant { + length = int64(len(x.val.(string))) + } + // an indexed string always yields a byte value + // (not a constant) even if the string and the + // index are constant + x.mode = value + x.typ = Typ[Byte] + } + + case *Array: + valid = true + length = typ.Len + if x.mode != variable { + x.mode = value + } + x.typ = typ.Elt + + case *Pointer: + if typ, _ := underlying(typ.Base).(*Array); typ != nil { + valid = true + length = typ.Len + x.mode = variable + x.typ = typ.Elt + } + + case *Slice: + valid = true + x.mode = variable + x.typ = typ.Elt + + case *Map: + var key operand + check.expr(&key, e.Index, nil, iota) + if !check.assignment(&key, typ.Key) { + if key.mode != invalid { + check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.Key) + } + goto Error + } + x.mode = valueok + x.typ = typ.Elt + x.expr = e + return + } + + if !valid { + check.invalidOp(x.pos(), "cannot index %s", x) + goto Error + } + + if e.Index == nil { + check.invalidAST(e.Pos(), "missing index expression for %s", x) + return + } + + check.index(e.Index, length, iota) + // ok to continue + + case *ast.SliceExpr: + check.expr(x, e.X, nil, iota) + if x.mode == invalid { + goto Error + } + + valid := false + length := int64(-1) // valid if >= 0 + switch typ := underlying(x.typ).(type) { + case *Basic: + if isString(typ) { + valid = true + if x.mode == constant { + length = int64(len(x.val.(string))) + 1 // +1 for slice + } + // a sliced string always yields a string value + // of the same type as the original string (not + // a constant) even if the string and the indices + // are constant + x.mode = value + // x.typ doesn't change, but if it is an untyped + // string it becomes string (see also issue 4913). + if typ.Kind == UntypedString { + x.typ = Typ[String] + } + } + + case *Array: + valid = true + length = typ.Len + 1 // +1 for slice + if x.mode != variable { + check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x) + goto Error + } + x.typ = &Slice{Elt: typ.Elt} + + case *Pointer: + if typ, _ := underlying(typ.Base).(*Array); typ != nil { + valid = true + length = typ.Len + 1 // +1 for slice + x.mode = variable + x.typ = &Slice{Elt: typ.Elt} + } + + case *Slice: + valid = true + x.mode = variable + // x.typ doesn't change + } + + if !valid { + check.invalidOp(x.pos(), "cannot slice %s", x) + goto Error + } + + lo := int64(0) + if e.Low != nil { + lo = check.index(e.Low, length, iota) + } + + hi := int64(-1) + if e.High != nil { + hi = check.index(e.High, length, iota) + } else if length >= 0 { + hi = length + } + + if lo >= 0 && hi >= 0 && lo > hi { + check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi) + // ok to continue + } + + case *ast.TypeAssertExpr: + check.expr(x, e.X, nil, iota) + if x.mode == invalid { + goto Error + } + var T *Interface + if T, _ = underlying(x.typ).(*Interface); T == nil { + check.invalidOp(x.pos(), "%s is not an interface", x) + goto Error + } + // x.(type) expressions are handled explicitly in type switches + if e.Type == nil { + check.errorf(e.Pos(), "use of .(type) outside type switch") + goto Error + } + typ := check.typ(e.Type, false) + if typ == Typ[Invalid] { + goto Error + } + if method, wrongType := missingMethod(typ, T); method != nil { + var msg string + if wrongType { + msg = "%s cannot have dynamic type %s (wrong type for method %s)" + } else { + msg = "%s cannot have dynamic type %s (missing method %s)" + } + check.errorf(e.Type.Pos(), msg, x, typ, method.Name) + // ok to continue + } + x.mode = valueok + x.expr = e + x.typ = typ + + case *ast.CallExpr: + check.exprOrType(x, e.Fun, iota, false) + if x.mode == invalid { + goto Error + } else if x.mode == typexpr { + check.conversion(x, e, x.typ, iota) + } else if sig, ok := underlying(x.typ).(*Signature); ok { + // check parameters + + // If we have a trailing ... at the end of the parameter + // list, the last argument must match the parameter type + // []T of a variadic function parameter x ...T. + passSlice := false + if e.Ellipsis.IsValid() { + if sig.IsVariadic { + passSlice = true + } else { + check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun) + // ok to continue + } + } + + // If we have a single argument that is a function call + // we need to handle it separately. Determine if this + // is the case without checking the argument. + var call *ast.CallExpr + if len(e.Args) == 1 { + call, _ = unparen(e.Args[0]).(*ast.CallExpr) + } + + n := 0 // parameter count + if call != nil { + // We have a single argument that is a function call. + check.expr(x, call, nil, -1) + if x.mode == invalid { + goto Error // TODO(gri): we can do better + } + if t, _ := x.typ.(*Result); t != nil { + // multiple result values + n = len(t.Values) + for i, obj := range t.Values { + x.mode = value + x.expr = nil // TODO(gri) can we do better here? (for good error messages) + x.typ = obj.Type + check.argument(sig, i, nil, x, passSlice && i+1 == n) + } + } else { + // single result value + n = 1 + check.argument(sig, 0, nil, x, passSlice) + } + + } else { + // We don't have a single argument or it is not a function call. + n = len(e.Args) + for i, arg := range e.Args { + check.argument(sig, i, arg, x, passSlice && i+1 == n) + } + } + + // determine if we have enough arguments + if sig.IsVariadic { + // a variadic function accepts an "empty" + // last argument: count one extra + n++ + } + if n < len(sig.Params) { + check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun) + // ok to continue + } + + // determine result + switch len(sig.Results) { + case 0: + x.mode = novalue + case 1: + x.mode = value + x.typ = sig.Results[0].Type + default: + x.mode = value + x.typ = &Result{Values: sig.Results} + } + + } else if bin, ok := x.typ.(*builtin); ok { + check.builtin(x, e, bin, iota) + + } else { + check.invalidOp(x.pos(), "cannot call non-function %s", x) + goto Error + } + + case *ast.StarExpr: + check.exprOrType(x, e.X, iota, true) + switch x.mode { + case invalid: + goto Error + case typexpr: + x.typ = &Pointer{Base: x.typ} + default: + if typ, ok := underlying(x.typ).(*Pointer); ok { + x.mode = variable + x.typ = typ.Base + } else { + check.invalidOp(x.pos(), "cannot indirect %s", x) + goto Error + } + } + + case *ast.UnaryExpr: + check.expr(x, e.X, nil, iota) + if x.mode == invalid { + goto Error + } + check.unary(x, e.Op) + if x.mode == invalid { + goto Error + } + + case *ast.BinaryExpr: + check.binary(x, e.X, e.Y, e.Op, iota) + if x.mode == invalid { + goto Error + } + + case *ast.KeyValueExpr: + // key:value expressions are handled in composite literals + check.invalidAST(e.Pos(), "no key:value expected") + goto Error + + case *ast.ArrayType: + if e.Len != nil { + check.expr(x, e.Len, nil, iota) + if x.mode == invalid { + goto Error + } + if x.mode != constant { + if x.mode != invalid { + check.errorf(x.pos(), "array length %s must be constant", x) + } + goto Error + } + n, ok := x.val.(int64) + if !ok || n < 0 { + check.errorf(x.pos(), "invalid array length %s", x) + goto Error + } + x.typ = &Array{Len: n, Elt: check.typ(e.Elt, cycleOk)} + } else { + x.typ = &Slice{Elt: check.typ(e.Elt, true)} + } + x.mode = typexpr + + case *ast.StructType: + x.mode = typexpr + x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)} + + case *ast.FuncType: + params, isVariadic := check.collectParams(e.Params, true) + results, _ := check.collectParams(e.Results, false) + x.mode = typexpr + x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic} + + case *ast.InterfaceType: + x.mode = typexpr + x.typ = &Interface{Methods: check.collectMethods(e.Methods)} + + case *ast.MapType: + x.mode = typexpr + x.typ = &Map{Key: check.typ(e.Key, true), Elt: check.typ(e.Value, true)} + + case *ast.ChanType: + x.mode = typexpr + x.typ = &Chan{Dir: e.Dir, Elt: check.typ(e.Value, true)} + + default: + if debug { + check.dump("expr = %v (%T)", e, e) + } + unreachable() + } + + // everything went well + x.expr = e + return + +Error: + x.mode = invalid + x.expr = e +} + +// exprOrType is like rawExpr but reports an error if e doesn't represents a value or type. +func (check *checker) exprOrType(x *operand, e ast.Expr, iota int, cycleOk bool) { + check.rawExpr(x, e, nil, iota, cycleOk) + if x.mode == novalue { + check.errorf(x.pos(), "%s used as value or type", x) + x.mode = invalid + } +} + +// expr is like rawExpr but reports an error if e doesn't represents a value. +func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) { + check.rawExpr(x, e, hint, iota, false) + switch x.mode { + case novalue: + check.errorf(x.pos(), "%s used as value", x) + x.mode = invalid + case typexpr: + check.errorf(x.pos(), "%s is not an expression", x) + x.mode = invalid + } +} + +func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type { + var x operand + check.rawExpr(&x, e, nil, -1, cycleOk) + switch x.mode { + case invalid: + // ignore - error reported before + case novalue: + check.errorf(x.pos(), "%s used as type", &x) + case typexpr: + return x.typ + case constant: + if nilOk && x.isNil() { + return nil + } + fallthrough + default: + check.errorf(x.pos(), "%s is not a type", &x) + } + return Typ[Invalid] +} + +// typOrNil is like rawExpr but reports an error if e doesn't represents a type or the predeclared value nil. +// It returns e's type, nil, or Typ[Invalid] if an error occurred. +// +func (check *checker) typOrNil(e ast.Expr, cycleOk bool) Type { + return check.rawTyp(e, cycleOk, true) +} + +// typ is like rawExpr but reports an error if e doesn't represents a type. +// It returns e's type, or Typ[Invalid] if an error occurred. +// +func (check *checker) typ(e ast.Expr, cycleOk bool) Type { + return check.rawTyp(e, cycleOk, false) +} diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go new file mode 100644 index 000000000..7f968eb8d --- /dev/null +++ b/src/pkg/go/types/gcimporter.go @@ -0,0 +1,950 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements an Importer for gc-generated object files. + +package types + +import ( + "bufio" + "errors" + "fmt" + "go/ast" + "go/build" + "go/token" + "io" + "math/big" + "os" + "path/filepath" + "strconv" + "strings" + "text/scanner" +) + +var pkgExts = [...]string{".a", ".5", ".6", ".8"} + +// FindPkg returns the filename and unique package id for an import +// path based on package information provided by build.Import (using +// the build.Default build.Context). +// If no file was found, an empty filename is returned. +// +func FindPkg(path, srcDir string) (filename, id string) { + if len(path) == 0 { + return + } + + id = path + var noext string + switch { + default: + // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" + // Don't require the source files to be present. + bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) + if bp.PkgObj == "" { + return + } + noext = strings.TrimSuffix(bp.PkgObj, ".a") + + case build.IsLocalImport(path): + // "./x" -> "/this/directory/x.ext", "/this/directory/x" + noext = filepath.Join(srcDir, path) + id = noext + + case filepath.IsAbs(path): + // for completeness only - go/build.Import + // does not support absolute imports + // "/x" -> "/x.ext", "/x" + noext = path + } + + // try extensions + for _, ext := range pkgExts { + filename = noext + ext + if f, err := os.Stat(filename); err == nil && !f.IsDir() { + return + } + } + + filename = "" // not found + return +} + +// GcImportData imports a package by reading the gc-generated export data, +// adds the corresponding package object to the imports map indexed by id, +// and returns the object. +// +// The imports map must contains all packages already imported. The data +// reader position must be the beginning of the export data section. The +// filename is only used in error messages. +// +// If imports[id] contains the completely imported package, that package +// can be used directly, and there is no need to call this function (but +// there is also no harm but for extra time used). +// +func GcImportData(imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) { + // support for gcParser error handling + defer func() { + if r := recover(); r != nil { + err = r.(importError) // will re-panic if r is not an importError + } + }() + + var p gcParser + p.init(filename, id, data, imports) + pkg = p.parseExport() + + return +} + +// GcImport imports a gc-generated package given its import path, adds the +// corresponding package object to the imports map, and returns the object. +// Local import paths are interpreted relative to the current working directory. +// The imports map must contains all packages already imported. +// GcImport satisfies the ast.Importer signature. +// +func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) { + if path == "unsafe" { + return Unsafe, nil + } + + srcDir := "." + if build.IsLocalImport(path) { + srcDir, err = os.Getwd() + if err != nil { + return + } + } + + filename, id := FindPkg(path, srcDir) + if filename == "" { + err = errors.New("can't find import: " + id) + return + } + + // no need to re-import if the package was imported completely before + if pkg = imports[id]; pkg != nil && pkg.Complete { + return + } + + // open file + f, err := os.Open(filename) + if err != nil { + return + } + defer func() { + f.Close() + if err != nil { + // add file name to error + err = fmt.Errorf("reading export data: %s: %v", filename, err) + } + }() + + buf := bufio.NewReader(f) + if err = FindGcExportData(buf); err != nil { + return + } + + pkg, err = GcImportData(imports, filename, id, buf) + + return +} + +// ---------------------------------------------------------------------------- +// gcParser + +// gcParser parses the exports inside a gc compiler-produced +// object/archive file and populates its scope with the results. +type gcParser struct { + scanner scanner.Scanner + tok rune // current token + lit string // literal string; only valid for Ident, Int, String tokens + id string // package id of imported package + imports map[string]*Package // package id -> package object +} + +func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*Package) { + p.scanner.Init(src) + p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } + p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments + p.scanner.Whitespace = 1<<'\t' | 1<<' ' + p.scanner.Filename = filename // for good error messages + p.next() + p.id = id + p.imports = imports + // leave for debugging + if false { + // check consistency of imports map + for _, pkg := range imports { + if pkg.Name == "" { + fmt.Printf("no package name for %s\n", pkg.Path) + } + } + } +} + +func (p *gcParser) next() { + p.tok = p.scanner.Scan() + switch p.tok { + case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·': + p.lit = p.scanner.TokenText() + default: + p.lit = "" + } + // leave for debugging + if false { + fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) + } +} + +func declConst(pkg *Package, name string) *Const { + // the constant may have been imported before - if it exists + // already in the respective scope, return that constant + scope := pkg.Scope + if obj := scope.Lookup(name); obj != nil { + return obj.(*Const) + } + // otherwise create a new constant and insert it into the scope + obj := &Const{Pkg: pkg, Name: name} + scope.Insert(obj) + return obj +} + +func declTypeName(pkg *Package, name string) *TypeName { + scope := pkg.Scope + if obj := scope.Lookup(name); obj != nil { + return obj.(*TypeName) + } + obj := &TypeName{Pkg: pkg, Name: name} + // a named type may be referred to before the underlying type + // is known - set it up + obj.Type = &NamedType{Obj: obj} + scope.Insert(obj) + return obj +} + +func declVar(pkg *Package, name string) *Var { + scope := pkg.Scope + if obj := scope.Lookup(name); obj != nil { + return obj.(*Var) + } + obj := &Var{Pkg: pkg, Name: name} + scope.Insert(obj) + return obj +} + +func declFunc(pkg *Package, name string) *Func { + scope := pkg.Scope + if obj := scope.Lookup(name); obj != nil { + return obj.(*Func) + } + obj := &Func{Pkg: pkg, Name: name} + scope.Insert(obj) + return obj +} + +// ---------------------------------------------------------------------------- +// Error handling + +// Internal errors are boxed as importErrors. +type importError struct { + pos scanner.Position + err error +} + +func (e importError) Error() string { + return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) +} + +func (p *gcParser) error(err interface{}) { + if s, ok := err.(string); ok { + err = errors.New(s) + } + // panic with a runtime.Error if err is not an error + panic(importError{p.scanner.Pos(), err.(error)}) +} + +func (p *gcParser) errorf(format string, args ...interface{}) { + p.error(fmt.Sprintf(format, args...)) +} + +func (p *gcParser) expect(tok rune) string { + lit := p.lit + if p.tok != tok { + p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) + } + p.next() + return lit +} + +func (p *gcParser) expectSpecial(tok string) { + sep := 'x' // not white space + i := 0 + for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' { + sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token + p.next() + i++ + } + if i < len(tok) { + p.errorf("expected %q, got %q", tok, tok[0:i]) + } +} + +func (p *gcParser) expectKeyword(keyword string) { + lit := p.expect(scanner.Ident) + if lit != keyword { + p.errorf("expected keyword %s, got %q", keyword, lit) + } +} + +// ---------------------------------------------------------------------------- +// Qualified and unqualified names + +// PackageId = string_lit . +// +func (p *gcParser) parsePackageId() string { + id, err := strconv.Unquote(p.expect(scanner.String)) + if err != nil { + p.error(err) + } + // id == "" stands for the imported package id + // (only known at time of package installation) + if id == "" { + id = p.id + } + return id +} + +// PackageName = ident . +// +func (p *gcParser) parsePackageName() string { + return p.expect(scanner.Ident) +} + +// dotIdentifier = ( ident | '·' ) { ident | int | '·' } . +func (p *gcParser) parseDotIdent() string { + ident := "" + if p.tok != scanner.Int { + sep := 'x' // not white space + for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' { + ident += p.lit + sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token + p.next() + } + } + if ident == "" { + p.expect(scanner.Ident) // use expect() for error handling + } + return ident +} + +// QualifiedName = "@" PackageId "." dotIdentifier . +// +func (p *gcParser) parseQualifiedName() (id, name string) { + p.expect('@') + id = p.parsePackageId() + p.expect('.') + name = p.parseDotIdent() + return +} + +// getPkg returns the package for a given id. If the package is +// not found but we have a package name, create the package and +// add it to the p.imports map. +// +func (p *gcParser) getPkg(id, name string) *Package { + // package unsafe is not in the imports map - handle explicitly + if id == "unsafe" { + return Unsafe + } + pkg := p.imports[id] + if pkg == nil && name != "" { + pkg = &Package{Name: name, Path: id, Scope: new(Scope)} + p.imports[id] = pkg + } + return pkg +} + +// parseExportedName is like parseQualifiedName, but +// the package id is resolved to an imported *Package. +// +func (p *gcParser) parseExportedName() (pkg *Package, name string) { + id, name := p.parseQualifiedName() + pkg = p.getPkg(id, "") + if pkg == nil { + p.errorf("%s package not found", id) + } + return +} + +// ---------------------------------------------------------------------------- +// Types + +// BasicType = identifier . +// +func (p *gcParser) parseBasicType() Type { + id := p.expect(scanner.Ident) + obj := Universe.Lookup(id) + if obj, ok := obj.(*TypeName); ok { + return obj.Type + } + p.errorf("not a basic type: %s", id) + return nil +} + +// ArrayType = "[" int_lit "]" Type . +// +func (p *gcParser) parseArrayType() Type { + // "[" already consumed and lookahead known not to be "]" + lit := p.expect(scanner.Int) + p.expect(']') + elt := p.parseType() + n, err := strconv.ParseInt(lit, 10, 64) + if err != nil { + p.error(err) + } + return &Array{Len: n, Elt: elt} +} + +// MapType = "map" "[" Type "]" Type . +// +func (p *gcParser) parseMapType() Type { + p.expectKeyword("map") + p.expect('[') + key := p.parseType() + p.expect(']') + elt := p.parseType() + return &Map{Key: key, Elt: elt} +} + +// Name = identifier | "?" | QualifiedName . +// +// If materializePkg is set, a package is returned for fully qualified names. +// That package may be a fake package (without name, scope, and not in the +// p.imports map), created for the sole purpose of providing a package path +// for QualifiedNames. Fake packages are created when the package id is not +// found in the p.imports map; we cannot create a real package in that case +// because we don't have a package name. +// +// TODO(gri): consider changing QualifiedIdents to (path, name) pairs to +// simplify this code. +// +func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) { + switch p.tok { + case scanner.Ident: + name = p.lit + p.next() + case '?': + // anonymous + p.next() + case '@': + // exported name prefixed with package path + var id string + id, name = p.parseQualifiedName() + if materializePkg { + // we don't have a package name - if the package + // doesn't exist yet, create a fake package instead + pkg = p.getPkg(id, "") + if pkg == nil { + pkg = &Package{Path: id} + } + } + default: + p.error("name expected") + } + return +} + +// Field = Name Type [ string_lit ] . +// +func (p *gcParser) parseField() *Field { + var f Field + f.Pkg, f.Name = p.parseName(true) + f.Type = p.parseType() + if p.tok == scanner.String { + f.Tag = p.expect(scanner.String) + } + if f.Name == "" { + // anonymous field - typ must be T or *T and T must be a type name + if typ, ok := deref(f.Type).(*NamedType); ok && typ.Obj != nil { + f.Name = typ.Obj.GetName() + f.IsAnonymous = true + } else { + p.errorf("anonymous field expected") + } + } + return &f +} + +// StructType = "struct" "{" [ FieldList ] "}" . +// FieldList = Field { ";" Field } . +// +func (p *gcParser) parseStructType() Type { + var fields []*Field + + p.expectKeyword("struct") + p.expect('{') + for p.tok != '}' { + if len(fields) > 0 { + p.expect(';') + } + fields = append(fields, p.parseField()) + } + p.expect('}') + + return &Struct{Fields: fields} +} + +// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . +// +func (p *gcParser) parseParameter() (par *Var, isVariadic bool) { + _, name := p.parseName(false) + if name == "" { + name = "_" // cannot access unnamed identifiers + } + if p.tok == '.' { + p.expectSpecial("...") + isVariadic = true + } + typ := p.parseType() + // ignore argument tag (e.g. "noescape") + if p.tok == scanner.String { + p.next() + } + par = &Var{Name: name, Type: typ} // Pkg == nil + return +} + +// Parameters = "(" [ ParameterList ] ")" . +// ParameterList = { Parameter "," } Parameter . +// +func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) { + p.expect('(') + for p.tok != ')' { + if len(list) > 0 { + p.expect(',') + } + par, variadic := p.parseParameter() + list = append(list, par) + if variadic { + if isVariadic { + p.error("... not on final argument") + } + isVariadic = true + } + } + p.expect(')') + + return +} + +// Signature = Parameters [ Result ] . +// Result = Type | Parameters . +// +func (p *gcParser) parseSignature() *Signature { + params, isVariadic := p.parseParameters() + + // optional result type + var results []*Var + if p.tok == '(' { + var variadic bool + results, variadic = p.parseParameters() + if variadic { + p.error("... not permitted on result type") + } + } + + return &Signature{Params: params, Results: results, IsVariadic: isVariadic} +} + +// InterfaceType = "interface" "{" [ MethodList ] "}" . +// MethodList = Method { ";" Method } . +// Method = Name Signature . +// +// The methods of embedded interfaces are always "inlined" +// by the compiler and thus embedded interfaces are never +// visible in the export data. +// +func (p *gcParser) parseInterfaceType() Type { + var methods []*Method + + p.expectKeyword("interface") + p.expect('{') + for p.tok != '}' { + if len(methods) > 0 { + p.expect(';') + } + pkg, name := p.parseName(true) + typ := p.parseSignature() + methods = append(methods, &Method{QualifiedName{pkg, name}, typ}) + } + p.expect('}') + + return &Interface{Methods: methods} +} + +// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . +// +func (p *gcParser) parseChanType() Type { + dir := ast.SEND | ast.RECV + if p.tok == scanner.Ident { + p.expectKeyword("chan") + if p.tok == '<' { + p.expectSpecial("<-") + dir = ast.SEND + } + } else { + p.expectSpecial("<-") + p.expectKeyword("chan") + dir = ast.RECV + } + elt := p.parseType() + return &Chan{Dir: dir, Elt: elt} +} + +// Type = +// BasicType | TypeName | ArrayType | SliceType | StructType | +// PointerType | FuncType | InterfaceType | MapType | ChanType | +// "(" Type ")" . +// +// BasicType = ident . +// TypeName = ExportedName . +// SliceType = "[" "]" Type . +// PointerType = "*" Type . +// FuncType = "func" Signature . +// +func (p *gcParser) parseType() Type { + switch p.tok { + case scanner.Ident: + switch p.lit { + default: + return p.parseBasicType() + case "struct": + return p.parseStructType() + case "func": + // FuncType + p.next() + return p.parseSignature() + case "interface": + return p.parseInterfaceType() + case "map": + return p.parseMapType() + case "chan": + return p.parseChanType() + } + case '@': + // TypeName + pkg, name := p.parseExportedName() + return declTypeName(pkg, name).Type + case '[': + p.next() // look ahead + if p.tok == ']' { + // SliceType + p.next() + return &Slice{Elt: p.parseType()} + } + return p.parseArrayType() + case '*': + // PointerType + p.next() + return &Pointer{Base: p.parseType()} + case '<': + return p.parseChanType() + case '(': + // "(" Type ")" + p.next() + typ := p.parseType() + p.expect(')') + return typ + } + p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit) + return nil +} + +// ---------------------------------------------------------------------------- +// Declarations + +// ImportDecl = "import" PackageName PackageId . +// +func (p *gcParser) parseImportDecl() { + p.expectKeyword("import") + name := p.parsePackageName() + p.getPkg(p.parsePackageId(), name) +} + +// int_lit = [ "+" | "-" ] { "0" ... "9" } . +// +func (p *gcParser) parseInt() (neg bool, val string) { + switch p.tok { + case '-': + neg = true + fallthrough + case '+': + p.next() + } + val = p.expect(scanner.Int) + return +} + +// number = int_lit [ "p" int_lit ] . +// +func (p *gcParser) parseNumber() (x operand) { + x.mode = constant + + // mantissa + neg, val := p.parseInt() + mant, ok := new(big.Int).SetString(val, 0) + assert(ok) + if neg { + mant.Neg(mant) + } + + if p.lit == "p" { + // exponent (base 2) + p.next() + neg, val = p.parseInt() + exp64, err := strconv.ParseUint(val, 10, 0) + if err != nil { + p.error(err) + } + exp := uint(exp64) + if neg { + denom := big.NewInt(1) + denom.Lsh(denom, exp) + x.typ = Typ[UntypedFloat] + x.val = normalizeRatConst(new(big.Rat).SetFrac(mant, denom)) + return + } + if exp > 0 { + mant.Lsh(mant, exp) + } + x.typ = Typ[UntypedFloat] + x.val = normalizeIntConst(mant) + return + } + + x.typ = Typ[UntypedInt] + x.val = normalizeIntConst(mant) + return +} + +// ConstDecl = "const" ExportedName [ Type ] "=" Literal . +// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . +// bool_lit = "true" | "false" . +// complex_lit = "(" float_lit "+" float_lit "i" ")" . +// rune_lit = "(" int_lit "+" int_lit ")" . +// string_lit = `"` { unicode_char } `"` . +// +func (p *gcParser) parseConstDecl() { + p.expectKeyword("const") + pkg, name := p.parseExportedName() + obj := declConst(pkg, name) + var x operand + if p.tok != '=' { + obj.Type = p.parseType() + } + p.expect('=') + switch p.tok { + case scanner.Ident: + // bool_lit + if p.lit != "true" && p.lit != "false" { + p.error("expected true or false") + } + x.typ = Typ[UntypedBool] + x.val = p.lit == "true" + p.next() + + case '-', scanner.Int: + // int_lit + x = p.parseNumber() + + case '(': + // complex_lit or rune_lit + p.next() + if p.tok == scanner.Char { + p.next() + p.expect('+') + x = p.parseNumber() + x.typ = Typ[UntypedRune] + p.expect(')') + break + } + re := p.parseNumber() + p.expect('+') + im := p.parseNumber() + p.expectKeyword("i") + p.expect(')') + x.typ = Typ[UntypedComplex] + // TODO(gri) fix this + _, _ = re, im + x.val = zeroConst + + case scanner.Char: + // rune_lit + x.setConst(token.CHAR, p.lit) + p.next() + + case scanner.String: + // string_lit + x.setConst(token.STRING, p.lit) + p.next() + + default: + p.errorf("expected literal got %s", scanner.TokenString(p.tok)) + } + if obj.Type == nil { + obj.Type = x.typ + } + assert(x.val != nil) + obj.Val = x.val +} + +// TypeDecl = "type" ExportedName Type . +// +func (p *gcParser) parseTypeDecl() { + p.expectKeyword("type") + pkg, name := p.parseExportedName() + obj := declTypeName(pkg, name) + + // The type object may have been imported before and thus already + // have a type associated with it. We still need to parse the type + // structure, but throw it away if the object already has a type. + // This ensures that all imports refer to the same type object for + // a given type declaration. + typ := p.parseType() + + if name := obj.Type.(*NamedType); name.Underlying == nil { + name.Underlying = typ + } +} + +// VarDecl = "var" ExportedName Type . +// +func (p *gcParser) parseVarDecl() { + p.expectKeyword("var") + pkg, name := p.parseExportedName() + obj := declVar(pkg, name) + obj.Type = p.parseType() +} + +// Func = Signature [ Body ] . +// Body = "{" ... "}" . +// +func (p *gcParser) parseFunc() *Signature { + sig := p.parseSignature() + if p.tok == '{' { + p.next() + for i := 1; i > 0; p.next() { + switch p.tok { + case '{': + i++ + case '}': + i-- + } + } + } + return sig +} + +// MethodDecl = "func" Receiver Name Func . +// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . +// +func (p *gcParser) parseMethodDecl() { + // "func" already consumed + p.expect('(') + recv, _ := p.parseParameter() // receiver + p.expect(')') + + // determine receiver base type object + typ := recv.Type + if ptr, ok := typ.(*Pointer); ok { + typ = ptr.Base + } + base := typ.(*NamedType) + + // parse method name, signature, and possibly inlined body + pkg, name := p.parseName(true) // unexported method names in imports are qualified with their package. + sig := p.parseFunc() + sig.Recv = recv + + // add method to type unless type was imported before + // and method exists already + // TODO(gri) investigate if this can be avoided + for _, m := range base.Methods { + if m.Name == name { + return // method was added before + } + } + base.Methods = append(base.Methods, &Method{QualifiedName{pkg, name}, sig}) +} + +// FuncDecl = "func" ExportedName Func . +// +func (p *gcParser) parseFuncDecl() { + // "func" already consumed + pkg, name := p.parseExportedName() + typ := p.parseFunc() + declFunc(pkg, name).Type = typ +} + +// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . +// +func (p *gcParser) parseDecl() { + switch p.lit { + case "import": + p.parseImportDecl() + case "const": + p.parseConstDecl() + case "type": + p.parseTypeDecl() + case "var": + p.parseVarDecl() + case "func": + p.next() // look ahead + if p.tok == '(' { + p.parseMethodDecl() + } else { + p.parseFuncDecl() + } + } + p.expect('\n') +} + +// ---------------------------------------------------------------------------- +// Export + +// Export = "PackageClause { Decl } "$$" . +// PackageClause = "package" PackageName [ "safe" ] "\n" . +// +func (p *gcParser) parseExport() *Package { + p.expectKeyword("package") + name := p.parsePackageName() + if p.tok != '\n' { + // A package is safe if it was compiled with the -u flag, + // which disables the unsafe package. + // TODO(gri) remember "safe" package + p.expectKeyword("safe") + } + p.expect('\n') + + pkg := p.getPkg(p.id, name) + + for p.tok != '$' && p.tok != scanner.EOF { + p.parseDecl() + } + + if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' { + // don't call next()/expect() since reading past the + // export data may cause scanner errors (e.g. NUL chars) + p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch) + } + + if n := p.scanner.ErrorCount; n != 0 { + p.errorf("expected no scanner errors, got %d", n) + } + + // package was imported completely and without errors + pkg.Complete = true + + return pkg +} diff --git a/src/pkg/go/types/gcimporter_test.go b/src/pkg/go/types/gcimporter_test.go new file mode 100644 index 000000000..b793eb4cb --- /dev/null +++ b/src/pkg/go/types/gcimporter_test.go @@ -0,0 +1,180 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "go/ast" + "go/build" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + "time" +) + +var gcPath string // Go compiler path + +func init() { + // determine compiler + var gc string + switch runtime.GOARCH { + case "386": + gc = "8g" + case "amd64": + gc = "6g" + case "arm": + gc = "5g" + default: + gcPath = "unknown-GOARCH-compiler" + return + } + gcPath = filepath.Join(build.ToolDir, gc) +} + +func compile(t *testing.T, dirname, filename string) string { + cmd := exec.Command(gcPath, filename) + cmd.Dir = dirname + out, err := cmd.CombinedOutput() + if err != nil { + t.Logf("%s", out) + t.Fatalf("%s %s failed: %s", gcPath, filename, err) + } + archCh, _ := build.ArchChar(runtime.GOARCH) + // filename should end with ".go" + return filepath.Join(dirname, filename[:len(filename)-2]+archCh) +} + +// Use the same global imports map for all tests. The effect is +// as if all tested packages were imported into a single package. +var imports = make(map[string]*Package) + +func testPath(t *testing.T, path string) bool { + t0 := time.Now() + _, err := GcImport(imports, path) + if err != nil { + t.Errorf("testPath(%s): %s", path, err) + return false + } + t.Logf("testPath(%s): %v", path, time.Since(t0)) + return true +} + +const maxTime = 30 * time.Second + +func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { + dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) + list, err := ioutil.ReadDir(dirname) + if err != nil { + t.Fatalf("testDir(%s): %s", dirname, err) + } + for _, f := range list { + if time.Now().After(endTime) { + t.Log("testing time used up") + return + } + switch { + case !f.IsDir(): + // try extensions + for _, ext := range pkgExts { + if strings.HasSuffix(f.Name(), ext) { + name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension + if testPath(t, filepath.Join(dir, name)) { + nimports++ + } + } + } + case f.IsDir(): + nimports += testDir(t, filepath.Join(dir, f.Name()), endTime) + } + } + return +} + +func TestGcImport(t *testing.T) { + // On cross-compile builds, the path will not exist. + // Need to use GOHOSTOS, which is not available. + if _, err := os.Stat(gcPath); err != nil { + t.Skipf("skipping test: %v", err) + } + + if outFn := compile(t, "testdata", "exports.go"); outFn != "" { + defer os.Remove(outFn) + } + + nimports := 0 + if testPath(t, "./testdata/exports") { + nimports++ + } + nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages + t.Logf("tested %d imports", nimports) +} + +var importedObjectTests = []struct { + name string + kind ast.ObjKind + typ string +}{ + {"unsafe.Pointer", ast.Typ, "Pointer"}, + {"math.Pi", ast.Con, "untyped float"}, + {"io.Reader", ast.Typ, "interface{Read(p []byte) (n int, err error)}"}, + {"io.ReadWriter", ast.Typ, "interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"}, + {"math.Sin", ast.Fun, "func(x·2 float64) (_ float64)"}, + // TODO(gri) add more tests +} + +func TestGcImportedTypes(t *testing.T) { + // This package does not yet know how to read gccgo export data. + if runtime.Compiler == "gccgo" { + return + } + for _, test := range importedObjectTests { + s := strings.Split(test.name, ".") + if len(s) != 2 { + t.Fatal("inconsistent test data") + } + importPath := s[0] + objName := s[1] + + pkg, err := GcImport(imports, importPath) + if err != nil { + t.Error(err) + continue + } + + obj := pkg.Scope.Lookup(objName) + + // TODO(gri) should define an accessor on Object + var kind ast.ObjKind + var typ Type + switch obj := obj.(type) { + case *Const: + kind = ast.Con + typ = obj.Type + case *TypeName: + kind = ast.Typ + typ = obj.Type + case *Var: + kind = ast.Var + typ = obj.Type + case *Func: + kind = ast.Fun + typ = obj.Type + default: + unreachable() + } + + if kind != test.kind { + t.Errorf("%s: got kind = %q; want %q", test.name, kind, test.kind) + } + + str := typeString(underlying(typ)) + if str != test.typ { + t.Errorf("%s: got type = %q; want %q", test.name, typ, test.typ) + } + } +} diff --git a/src/pkg/go/types/objects.go b/src/pkg/go/types/objects.go new file mode 100644 index 000000000..02291d34c --- /dev/null +++ b/src/pkg/go/types/objects.go @@ -0,0 +1,186 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "go/ast" + "go/token" +) + +// An Object describes a named language entity such as a package, +// constant, type, variable, function (incl. methods), or label. +// All objects implement the Object interface. +// +type Object interface { + GetPkg() *Package + GetName() string + GetType() Type + GetPos() token.Pos + + anObject() +} + +// A Package represents the contents (objects) of a Go package. +type Package struct { + Name string + Path string // import path, "" for current (non-imported) package + Scope *Scope // package-level scope + Imports map[string]*Package // map of import paths to imported packages + Complete bool // if set, this package was imported completely + + spec *ast.ImportSpec +} + +// A Const represents a declared constant. +type Const struct { + Pkg *Package + Name string + Type Type + Val interface{} + + spec *ast.ValueSpec +} + +// A TypeName represents a declared type. +type TypeName struct { + Pkg *Package + Name string + Type Type // *NamedType or *Basic + + spec *ast.TypeSpec +} + +// A Variable represents a declared variable (including function parameters and results). +type Var struct { + Pkg *Package // nil for parameters + Name string + Type Type + + visited bool // for initialization cycle detection + decl interface{} +} + +// A Func represents a declared function. +type Func struct { + Pkg *Package + Name string + Type Type // *Signature or *Builtin + + decl *ast.FuncDecl +} + +func (obj *Package) GetPkg() *Package { return obj } +func (obj *Const) GetPkg() *Package { return obj.Pkg } +func (obj *TypeName) GetPkg() *Package { return obj.Pkg } +func (obj *Var) GetPkg() *Package { return obj.Pkg } +func (obj *Func) GetPkg() *Package { return obj.Pkg } + +func (obj *Package) GetName() string { return obj.Name } +func (obj *Const) GetName() string { return obj.Name } +func (obj *TypeName) GetName() string { return obj.Name } +func (obj *Var) GetName() string { return obj.Name } +func (obj *Func) GetName() string { return obj.Name } + +func (obj *Package) GetType() Type { return Typ[Invalid] } +func (obj *Const) GetType() Type { return obj.Type } +func (obj *TypeName) GetType() Type { return obj.Type } +func (obj *Var) GetType() Type { return obj.Type } +func (obj *Func) GetType() Type { return obj.Type } + +func (obj *Package) GetPos() token.Pos { + if obj.spec != nil { + return obj.spec.Pos() + } + return token.NoPos +} + +func (obj *Const) GetPos() token.Pos { + for _, n := range obj.spec.Names { + if n.Name == obj.Name { + return n.Pos() + } + } + return token.NoPos +} +func (obj *TypeName) GetPos() token.Pos { + if obj.spec != nil { + return obj.spec.Pos() + } + return token.NoPos +} + +func (obj *Var) GetPos() token.Pos { + switch d := obj.decl.(type) { + case *ast.Field: + for _, n := range d.Names { + if n.Name == obj.Name { + return n.Pos() + } + } + case *ast.ValueSpec: + for _, n := range d.Names { + if n.Name == obj.Name { + return n.Pos() + } + } + case *ast.AssignStmt: + for _, x := range d.Lhs { + if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.Name { + return ident.Pos() + } + } + } + return token.NoPos +} +func (obj *Func) GetPos() token.Pos { + if obj.decl != nil && obj.decl.Name != nil { + return obj.decl.Name.Pos() + } + return token.NoPos +} + +func (*Package) anObject() {} +func (*Const) anObject() {} +func (*TypeName) anObject() {} +func (*Var) anObject() {} +func (*Func) anObject() {} + +// newObj returns a new Object for a given *ast.Object. +// It does not canonicalize them (it always returns a new one). +// For canonicalization, see check.lookup. +// +// TODO(gri) Once we do identifier resolution completely in +// in the typechecker, this functionality can go. +// +func newObj(pkg *Package, astObj *ast.Object) Object { + assert(pkg != nil) + name := astObj.Name + typ, _ := astObj.Type.(Type) + switch astObj.Kind { + case ast.Bad: + // ignore + case ast.Pkg: + unreachable() + case ast.Con: + return &Const{Pkg: pkg, Name: name, Type: typ, Val: astObj.Data, spec: astObj.Decl.(*ast.ValueSpec)} + case ast.Typ: + return &TypeName{Pkg: pkg, Name: name, Type: typ, spec: astObj.Decl.(*ast.TypeSpec)} + case ast.Var: + switch astObj.Decl.(type) { + case *ast.Field: // function parameters + case *ast.ValueSpec: // proper variable declarations + case *ast.AssignStmt: // short variable declarations + default: + unreachable() // everything else is not ok + } + return &Var{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl} + case ast.Fun: + return &Func{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl.(*ast.FuncDecl)} + case ast.Lbl: + unreachable() // for now + } + unreachable() + return nil +} diff --git a/src/pkg/go/types/operand.go b/src/pkg/go/types/operand.go new file mode 100644 index 000000000..982ffef8d --- /dev/null +++ b/src/pkg/go/types/operand.go @@ -0,0 +1,411 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file defines operands and associated operations. + +package types + +import ( + "bytes" + "fmt" + "go/ast" + "go/token" +) + +// An operandMode specifies the (addressing) mode of an operand. +type operandMode int + +const ( + invalid operandMode = iota // operand is invalid (due to an earlier error) - ignore + novalue // operand represents no value (result of a function call w/o result) + typexpr // operand is a type + constant // operand is a constant; the operand's typ is a Basic type + variable // operand is an addressable variable + value // operand is a computed value + valueok // like mode == value, but operand may be used in a comma,ok expression +) + +var operandModeString = [...]string{ + invalid: "invalid", + novalue: "no value", + typexpr: "type", + constant: "constant", + variable: "variable", + value: "value", + valueok: "value,ok", +} + +// An operand represents an intermediate value during type checking. +// Operands have an (addressing) mode, the expression evaluating to +// the operand, the operand's type, and for constants a constant value. +// +type operand struct { + mode operandMode + expr ast.Expr + typ Type + val interface{} +} + +// pos returns the position of the expression corresponding to x. +// If x is invalid the position is token.NoPos. +// +func (x *operand) pos() token.Pos { + // x.expr may not be set if x is invalid + if x.expr == nil { + return token.NoPos + } + return x.expr.Pos() +} + +func (x *operand) String() string { + if x.mode == invalid { + return "invalid operand" + } + var buf bytes.Buffer + if x.expr != nil { + buf.WriteString(exprString(x.expr)) + buf.WriteString(" (") + } + buf.WriteString(operandModeString[x.mode]) + if x.mode == constant { + format := " %v" + if isString(x.typ) { + format = " %q" + } + fmt.Fprintf(&buf, format, x.val) + } + if x.mode != novalue && (x.mode != constant || !isUntyped(x.typ)) { + fmt.Fprintf(&buf, " of type %s", typeString(x.typ)) + } + if x.expr != nil { + buf.WriteByte(')') + } + return buf.String() +} + +// setConst sets x to the untyped constant for literal lit. +func (x *operand) setConst(tok token.Token, lit string) { + x.mode = invalid + + var kind BasicKind + var val interface{} + switch tok { + case token.INT: + kind = UntypedInt + val = makeIntConst(lit) + + case token.FLOAT: + kind = UntypedFloat + val = makeFloatConst(lit) + + case token.IMAG: + kind = UntypedComplex + val = makeComplexConst(lit) + + case token.CHAR: + kind = UntypedRune + val = makeRuneConst(lit) + + case token.STRING: + kind = UntypedString + val = makeStringConst(lit) + } + + if val != nil { + x.mode = constant + x.typ = Typ[kind] + x.val = val + } +} + +// isNil reports whether x is the predeclared nil constant. +func (x *operand) isNil() bool { + return x.mode == constant && x.val == nilConst +} + +// TODO(gri) The functions operand.isAssignable, checker.convertUntyped, +// checker.isRepresentable, and checker.assignOperand are +// overlapping in functionality. Need to simplify and clean up. + +// isAssignable reports whether x is assignable to a variable of type T. +func (x *operand) isAssignable(ctxt *Context, T Type) bool { + if x.mode == invalid || T == Typ[Invalid] { + return true // avoid spurious errors + } + + V := x.typ + + // x's type is identical to T + if IsIdentical(V, T) { + return true + } + + Vu := underlying(V) + Tu := underlying(T) + + // x's type V and T have identical underlying types + // and at least one of V or T is not a named type + if IsIdentical(Vu, Tu) { + return !isNamed(V) || !isNamed(T) + } + + // T is an interface type and x implements T + if Ti, ok := Tu.(*Interface); ok { + if m, _ := missingMethod(x.typ, Ti); m == nil { + return true + } + } + + // x is a bidirectional channel value, T is a channel + // type, x's type V and T have identical element types, + // and at least one of V or T is not a named type + if Vc, ok := Vu.(*Chan); ok && Vc.Dir == ast.SEND|ast.RECV { + if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.Elt, Tc.Elt) { + return !isNamed(V) || !isNamed(T) + } + } + + // x is the predeclared identifier nil and T is a pointer, + // function, slice, map, channel, or interface type + if x.isNil() { + switch t := Tu.(type) { + case *Basic: + if t.Kind == UnsafePointer { + return true + } + case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface: + return true + } + return false + } + + // x is an untyped constant representable by a value of type T + // TODO(gri) This is borrowing from checker.convertUntyped and + // checker.isRepresentable. Need to clean up. + if isUntyped(Vu) { + switch t := Tu.(type) { + case *Basic: + if x.mode == constant { + return isRepresentableConst(x.val, ctxt, t.Kind) + } + // The result of a comparison is an untyped boolean, + // but may not be a constant. + if Vb, _ := Vu.(*Basic); Vb != nil { + return Vb.Kind == UntypedBool && isBoolean(Tu) + } + case *Interface: + return x.isNil() || len(t.Methods) == 0 + case *Pointer, *Signature, *Slice, *Map, *Chan: + return x.isNil() + } + } + + return false +} + +// isInteger reports whether x is a (typed or untyped) integer value. +func (x *operand) isInteger(ctxt *Context) bool { + return x.mode == invalid || + isInteger(x.typ) || + x.mode == constant && isRepresentableConst(x.val, ctxt, UntypedInt) +} + +// lookupResult represents the result of a struct field/method lookup. +type lookupResult struct { + mode operandMode + typ Type + index []int // field index sequence; nil for methods +} + +type embeddedType struct { + typ *NamedType + index []int // field index sequence + multiples bool // if set, typ is embedded multiple times at the same level +} + +// lookupFieldBreadthFirst searches all types in list for a single entry (field +// or method) of the given name from the given package. If such a field is found, +// the result describes the field mode and type; otherwise the result mode is invalid. +// (This function is similar in structure to FieldByNameFunc in reflect/type.go) +// +func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res lookupResult) { + // visited records the types that have been searched already. + visited := make(map[*NamedType]bool) + + // embedded types of the next lower level + var next []embeddedType + + // potentialMatch is invoked every time a match is found. + potentialMatch := func(multiples bool, mode operandMode, typ Type) bool { + if multiples || res.mode != invalid { + // name appeared already at this level - annihilate + res.mode = invalid + return false + } + // first appearance of name + res.mode = mode + res.typ = typ + res.index = nil + return true + } + + // Search the current level if there is any work to do and collect + // embedded types of the next lower level in the next list. + for len(list) > 0 { + // The res.mode indicates whether we have found a match already + // on this level (mode != invalid), or not (mode == invalid). + assert(res.mode == invalid) + + // start with empty next list (don't waste underlying array) + next = next[:0] + + // look for name in all types at this level + for _, e := range list { + typ := e.typ + if visited[typ] { + continue + } + visited[typ] = true + + // look for a matching attached method + for _, m := range typ.Methods { + if name.IsSame(m.QualifiedName) { + assert(m.Type != nil) + if !potentialMatch(e.multiples, value, m.Type) { + return // name collision + } + } + } + + switch t := typ.Underlying.(type) { + case *Struct: + // look for a matching field and collect embedded types + for i, f := range t.Fields { + if name.IsSame(f.QualifiedName) { + assert(f.Type != nil) + if !potentialMatch(e.multiples, variable, f.Type) { + return // name collision + } + var index []int + index = append(index, e.index...) // copy e.index + index = append(index, i) + res.index = index + continue + } + // Collect embedded struct fields for searching the next + // lower level, but only if we have not seen a match yet + // (if we have a match it is either the desired field or + // we have a name collision on the same level; in either + // case we don't need to look further). + // Embedded fields are always of the form T or *T where + // T is a named type. If typ appeared multiple times at + // this level, f.Type appears multiple times at the next + // level. + if f.IsAnonymous && res.mode == invalid { + // Ignore embedded basic types - only user-defined + // named types can have methods or have struct fields. + if t, _ := deref(f.Type).(*NamedType); t != nil { + var index []int + index = append(index, e.index...) // copy e.index + index = append(index, i) + next = append(next, embeddedType{t, index, e.multiples}) + } + } + } + + case *Interface: + // look for a matching method + for _, m := range t.Methods { + if name.IsSame(m.QualifiedName) { + assert(m.Type != nil) + if !potentialMatch(e.multiples, value, m.Type) { + return // name collision + } + } + } + } + } + + if res.mode != invalid { + // we found a single match on this level + return + } + + // No match and no collision so far. + // Compute the list to search for the next level. + list = list[:0] // don't waste underlying array + for _, e := range next { + // Instead of adding the same type multiple times, look for + // it in the list and mark it as multiple if it was added + // before. + // We use a sequential search (instead of a map for next) + // because the lists tend to be small, can easily be reused, + // and explicit search appears to be faster in this case. + if alt := findType(list, e.typ); alt != nil { + alt.multiples = true + } else { + list = append(list, e) + } + } + + } + + return +} + +func findType(list []embeddedType, typ *NamedType) *embeddedType { + for i := range list { + if p := &list[i]; p.typ == typ { + return p + } + } + return nil +} + +func lookupField(typ Type, name QualifiedName) lookupResult { + typ = deref(typ) + + if t, ok := typ.(*NamedType); ok { + for _, m := range t.Methods { + if name.IsSame(m.QualifiedName) { + assert(m.Type != nil) + return lookupResult{value, m.Type, nil} + } + } + typ = t.Underlying + } + + switch t := typ.(type) { + case *Struct: + var next []embeddedType + for i, f := range t.Fields { + if name.IsSame(f.QualifiedName) { + return lookupResult{variable, f.Type, []int{i}} + } + if f.IsAnonymous { + // Possible optimization: If the embedded type + // is a pointer to the current type we could + // ignore it. + // Ignore embedded basic types - only user-defined + // named types can have methods or have struct fields. + if t, _ := deref(f.Type).(*NamedType); t != nil { + next = append(next, embeddedType{t, []int{i}, false}) + } + } + } + if len(next) > 0 { + return lookupFieldBreadthFirst(next, name) + } + + case *Interface: + for _, m := range t.Methods { + if name.IsSame(m.QualifiedName) { + return lookupResult{value, m.Type, nil} + } + } + } + + // not found + return lookupResult{mode: invalid} +} diff --git a/src/pkg/go/types/predicates.go b/src/pkg/go/types/predicates.go new file mode 100644 index 000000000..a99c91a4e --- /dev/null +++ b/src/pkg/go/types/predicates.go @@ -0,0 +1,303 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements commonly used type predicates. + +package types + +func isNamed(typ Type) bool { + if _, ok := typ.(*Basic); ok { + return ok + } + _, ok := typ.(*NamedType) + return ok +} + +func isBoolean(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsBoolean != 0 +} + +func isInteger(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsInteger != 0 +} + +func isUnsigned(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsUnsigned != 0 +} + +func isFloat(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsFloat != 0 +} + +func isComplex(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsComplex != 0 +} + +func isNumeric(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsNumeric != 0 +} + +func isString(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsString != 0 +} + +func isUntyped(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsUntyped != 0 +} + +func isOrdered(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsOrdered != 0 +} + +func isConstType(typ Type) bool { + t, ok := underlying(typ).(*Basic) + return ok && t.Info&IsConstType != 0 +} + +func isComparable(typ Type) bool { + switch t := underlying(typ).(type) { + case *Basic: + return t.Kind != Invalid && t.Kind != UntypedNil + case *Pointer, *Interface, *Chan: + // assumes types are equal for pointers and channels + return true + case *Struct: + for _, f := range t.Fields { + if !isComparable(f.Type) { + return false + } + } + return true + case *Array: + return isComparable(t.Elt) + } + return false +} + +func hasNil(typ Type) bool { + switch underlying(typ).(type) { + case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: + return true + } + return false +} + +// IsIdentical returns true if x and y are identical. +func IsIdentical(x, y Type) bool { + if x == y { + return true + } + + switch x := x.(type) { + case *Basic: + // Basic types are singletons except for the rune and byte + // aliases, thus we cannot solely rely on the x == y check + // above. + if y, ok := y.(*Basic); ok { + return x.Kind == y.Kind + } + + case *Array: + // Two array types are identical if they have identical element types + // and the same array length. + if y, ok := y.(*Array); ok { + return x.Len == y.Len && IsIdentical(x.Elt, y.Elt) + } + + case *Slice: + // Two slice types are identical if they have identical element types. + if y, ok := y.(*Slice); ok { + return IsIdentical(x.Elt, y.Elt) + } + + case *Struct: + // Two struct types are identical if they have the same sequence of fields, + // and if corresponding fields have the same names, and identical types, + // and identical tags. Two anonymous fields are considered to have the same + // name. Lower-case field names from different packages are always different. + if y, ok := y.(*Struct); ok { + if len(x.Fields) == len(y.Fields) { + for i, f := range x.Fields { + g := y.Fields[i] + if !f.QualifiedName.IsSame(g.QualifiedName) || + !IsIdentical(f.Type, g.Type) || + f.Tag != g.Tag || + f.IsAnonymous != g.IsAnonymous { + return false + } + } + return true + } + } + + case *Pointer: + // Two pointer types are identical if they have identical base types. + if y, ok := y.(*Pointer); ok { + return IsIdentical(x.Base, y.Base) + } + + case *Signature: + // Two function types are identical if they have the same number of parameters + // and result values, corresponding parameter and result types are identical, + // and either both functions are variadic or neither is. Parameter and result + // names are not required to match. + if y, ok := y.(*Signature); ok { + return identicalTypes(x.Params, y.Params) && + identicalTypes(x.Results, y.Results) && + x.IsVariadic == y.IsVariadic + } + + case *Interface: + // Two interface types are identical if they have the same set of methods with + // the same names and identical function types. Lower-case method names from + // different packages are always different. The order of the methods is irrelevant. + if y, ok := y.(*Interface); ok { + return identicalMethods(x.Methods, y.Methods) // methods are sorted + } + + case *Map: + // Two map types are identical if they have identical key and value types. + if y, ok := y.(*Map); ok { + return IsIdentical(x.Key, y.Key) && IsIdentical(x.Elt, y.Elt) + } + + case *Chan: + // Two channel types are identical if they have identical value types + // and the same direction. + if y, ok := y.(*Chan); ok { + return x.Dir == y.Dir && IsIdentical(x.Elt, y.Elt) + } + + case *NamedType: + // Two named types are identical if their type names originate + // in the same type declaration. + if y, ok := y.(*NamedType); ok { + return x.Obj == y.Obj + } + } + + return false +} + +// identicalTypes returns true if both lists a and b have the +// same length and corresponding objects have identical types. +func identicalTypes(a, b []*Var) bool { + if len(a) != len(b) { + return false + } + for i, x := range a { + y := b[i] + if !IsIdentical(x.Type, y.Type) { + return false + } + } + return true +} + +// identicalMethods returns true if both lists a and b have the +// same length and corresponding methods have identical types. +// TODO(gri) make this more efficient +func identicalMethods(a, b []*Method) bool { + if len(a) != len(b) { + return false + } + m := make(map[QualifiedName]*Method) + for _, x := range a { + assert(m[x.QualifiedName] == nil) // method list must not have duplicate entries + m[x.QualifiedName] = x + } + for _, y := range b { + if x := m[y.QualifiedName]; x == nil || !IsIdentical(x.Type, y.Type) { + return false + } + } + return true +} + +// underlying returns the underlying type of typ. +func underlying(typ Type) Type { + // Basic types are representing themselves directly even though they are named. + if typ, ok := typ.(*NamedType); ok { + return typ.Underlying // underlying types are never NamedTypes + } + return typ +} + +// deref returns a pointer's base type; otherwise it returns typ. +func deref(typ Type) Type { + if typ, ok := underlying(typ).(*Pointer); ok { + return typ.Base + } + return typ +} + +// defaultType returns the default "typed" type for an "untyped" type; +// it returns the incoming type for all other types. If there is no +// corresponding untyped type, the result is Typ[Invalid]. +// +func defaultType(typ Type) Type { + if t, ok := typ.(*Basic); ok { + k := Invalid + switch t.Kind { + // case UntypedNil: + // There is no default type for nil. For a good error message, + // catch this case before calling this function. + case UntypedBool: + k = Bool + case UntypedInt: + k = Int + case UntypedRune: + k = Rune + case UntypedFloat: + k = Float64 + case UntypedComplex: + k = Complex128 + case UntypedString: + k = String + } + typ = Typ[k] + } + return typ +} + +// missingMethod returns (nil, false) if typ implements T, otherwise +// it returns the first missing method required by T and whether it +// is missing or simply has the wrong type. +// +func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) { + // TODO(gri): this needs to correctly compare method names (taking package into account) + // TODO(gri): distinguish pointer and non-pointer receivers + // an interface type implements T if it has no methods with conflicting signatures + // Note: This is stronger than the current spec. Should the spec require this? + if ityp, _ := underlying(typ).(*Interface); ityp != nil { + for _, m := range T.Methods { + res := lookupField(ityp, m.QualifiedName) // TODO(gri) no need to go via lookupField + if res.mode != invalid && !IsIdentical(res.typ, m.Type) { + return m, true + } + } + return + } + + // a concrete type implements T if it implements all methods of T. + for _, m := range T.Methods { + res := lookupField(typ, m.QualifiedName) + if res.mode == invalid { + return m, false + } + if !IsIdentical(res.typ, m.Type) { + return m, true + } + } + return +} diff --git a/src/pkg/go/types/resolve.go b/src/pkg/go/types/resolve.go new file mode 100644 index 000000000..43db60708 --- /dev/null +++ b/src/pkg/go/types/resolve.go @@ -0,0 +1,197 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "fmt" + "go/ast" + "go/token" + "strconv" +) + +func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport token.Pos) { + alt := scope.Insert(obj) + if alt == nil && altScope != nil { + // see if there is a conflicting declaration in altScope + alt = altScope.Lookup(obj.GetName()) + } + if alt != nil { + prevDecl := "" + + // for dot-imports, local declarations are declared first - swap messages + if dotImport.IsValid() { + if pos := alt.GetPos(); pos.IsValid() { + check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s", + obj.GetName(), check.fset.Position(dotImport))) + return + } + + // get by w/o other position + check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.GetName())) + return + } + + if pos := alt.GetPos(); pos.IsValid() { + prevDecl = fmt.Sprintf("\n\tother declaration at %s", check.fset.Position(pos)) + } + check.errorf(obj.GetPos(), fmt.Sprintf("%s redeclared in this block%s", obj.GetName(), prevDecl)) + } +} + +func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool { + for ; scope != nil; scope = scope.Outer { + if obj := scope.Lookup(ident.Name); obj != nil { + check.register(ident, obj) + return true + } + } + return false +} + +func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { + pkg := &Package{Scope: &Scope{Outer: Universe}, Imports: make(map[string]*Package)} + check.pkg = pkg + + // complete package scope + i := 0 + for _, file := range check.files { + // package names must match + switch name := file.Name.Name; { + case pkg.Name == "": + pkg.Name = name + case name != pkg.Name: + check.errorf(file.Package, "package %s; expected %s", name, pkg.Name) + continue // ignore this file + } + + // keep this file + check.files[i] = file + i++ + + // the package identifier denotes the current package + check.register(file.Name, pkg) + + // insert top-level file objects in package scope + // (the parser took care of declaration errors) + for _, decl := range file.Decls { + switch d := decl.(type) { + case *ast.BadDecl: + // ignore + case *ast.GenDecl: + if d.Tok == token.CONST { + check.assocInitvals(d) + } + for _, spec := range d.Specs { + switch s := spec.(type) { + case *ast.ImportSpec: + // handled separately below + case *ast.ValueSpec: + for _, name := range s.Names { + if name.Name == "_" { + continue + } + pkg.Scope.Insert(check.lookup(name)) + } + case *ast.TypeSpec: + if s.Name.Name == "_" { + continue + } + pkg.Scope.Insert(check.lookup(s.Name)) + default: + check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) + } + } + case *ast.FuncDecl: + if d.Recv != nil { + // collect method + methods = append(methods, d) + continue + } + if d.Name.Name == "_" || d.Name.Name == "init" { + continue // blank (_) and init functions are inaccessible + } + pkg.Scope.Insert(check.lookup(d.Name)) + default: + check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) + } + } + } + check.files = check.files[0:i] + + // complete file scopes with imports and resolve identifiers + for _, file := range check.files { + // build file scope by processing all imports + importErrors := false + fileScope := &Scope{Outer: pkg.Scope} + for _, spec := range file.Imports { + if importer == nil { + importErrors = true + continue + } + path, _ := strconv.Unquote(spec.Path.Value) + imp, err := importer(pkg.Imports, path) + if err != nil { + check.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err) + importErrors = true + continue + } + // TODO(gri) If a local package name != "." is provided, + // global identifier resolution could proceed even if the + // import failed. Consider adjusting the logic here a bit. + + // local name overrides imported package name + name := imp.Name + if spec.Name != nil { + name = spec.Name.Name + } + + // add import to file scope + if name == "." { + // merge imported scope with file scope + for _, obj := range imp.Scope.Entries { + // gcimported package scopes contain non-exported + // objects such as types used in partially exported + // objects - do not accept them + if ast.IsExported(obj.GetName()) { + check.declareObj(fileScope, pkg.Scope, obj, spec.Pos()) + } + } + // TODO(gri) consider registering the "." identifier + // if we have Context.Ident callbacks for say blank + // (_) identifiers + // check.register(spec.Name, pkg) + } else if name != "_" { + // declare imported package object in file scope + // (do not re-use imp in the file scope but create + // a new object instead; the Decl field is different + // for different files) + obj := &Package{Name: name, Scope: imp.Scope, spec: spec} + check.declareObj(fileScope, pkg.Scope, obj, token.NoPos) + } + } + + // resolve identifiers + if importErrors { + // don't use the universe scope without correct imports + // (objects in the universe may be shadowed by imports; + // with missing imports, identifiers might get resolved + // incorrectly to universe objects) + pkg.Scope.Outer = nil + } + i := 0 + for _, ident := range file.Unresolved { + if !check.resolveIdent(fileScope, ident) { + check.errorf(ident.Pos(), "undeclared name: %s", ident.Name) + file.Unresolved[i] = ident + i++ + } + + } + file.Unresolved = file.Unresolved[0:i] + pkg.Scope.Outer = Universe // reset outer scope (is nil if there were importErrors) + } + + return +} diff --git a/src/pkg/go/types/resolver_test.go b/src/pkg/go/types/resolver_test.go new file mode 100644 index 000000000..d4e364451 --- /dev/null +++ b/src/pkg/go/types/resolver_test.go @@ -0,0 +1,167 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "go/ast" + "go/parser" + "go/token" + "testing" +) + +var sources = []string{ + ` + package p + import "fmt" + import "math" + const pi = math.Pi + func sin(x float64) float64 { + return math.Sin(x) + } + var Println = fmt.Println + `, + ` + package p + import "fmt" + func f() string { + _ = "foo" + return fmt.Sprintf("%d", g()) + } + func g() (x int) { return } + `, + ` + package p + import . "go/parser" + import "sync" + func g() Mode { return ImportsOnly } + var _, x int = 1, 2 + func init() {} + type T struct{ sync.Mutex; a, b, c int} + type I interface{ m() } + var _ = T{a: 1, b: 2, c: 3} + func (_ T) m() {} + `, +} + +var pkgnames = []string{ + "fmt", + "math", +} + +func TestResolveQualifiedIdents(t *testing.T) { + // parse package files + fset := token.NewFileSet() + var files []*ast.File + for _, src := range sources { + f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors) + if err != nil { + t.Fatal(err) + } + files = append(files, f) + } + + // resolve and type-check package AST + idents := make(map[*ast.Ident]Object) + var ctxt Context + ctxt.Ident = func(id *ast.Ident, obj Object) { idents[id] = obj } + pkg, err := ctxt.Check(fset, files) + if err != nil { + t.Fatal(err) + } + + // check that all packages were imported + for _, name := range pkgnames { + if pkg.Imports[name] == nil { + t.Errorf("package %s not imported", name) + } + } + + // check that there are no top-level unresolved identifiers + for _, f := range files { + for _, x := range f.Unresolved { + t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name) + } + } + + // check that qualified identifiers are resolved + for _, f := range files { + ast.Inspect(f, func(n ast.Node) bool { + if s, ok := n.(*ast.SelectorExpr); ok { + if x, ok := s.X.(*ast.Ident); ok { + obj := idents[x] + if obj == nil { + t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name) + return false + } + if _, ok := obj.(*Package); ok && idents[s.Sel] == nil { + t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name) + return false + } + return false + } + return false + } + return true + }) + } + + // Currently, the Check API doesn't call Ident for fields, methods, and composite literal keys. + // Introduce them artifically so that we can run the check below. + for _, f := range files { + ast.Inspect(f, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.StructType: + for _, list := range x.Fields.List { + for _, f := range list.Names { + assert(idents[f] == nil) + idents[f] = &Var{Pkg: pkg, Name: f.Name} + } + } + case *ast.InterfaceType: + for _, list := range x.Methods.List { + for _, f := range list.Names { + assert(idents[f] == nil) + idents[f] = &Func{Pkg: pkg, Name: f.Name} + } + } + case *ast.CompositeLit: + for _, e := range x.Elts { + if kv, ok := e.(*ast.KeyValueExpr); ok { + if k, ok := kv.Key.(*ast.Ident); ok { + assert(idents[k] == nil) + idents[k] = &Var{Pkg: pkg, Name: k.Name} + } + } + } + } + return true + }) + } + + // check that each identifier in the source is enumerated by the Context.Ident callback + for _, f := range files { + ast.Inspect(f, func(n ast.Node) bool { + if x, ok := n.(*ast.Ident); ok && x.Name != "_" && x.Name != "." { + obj := idents[x] + if obj == nil { + t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name) + } else { + delete(idents, x) + } + return false + } + return true + }) + } + + // TODO(gri) enable code below + // At the moment, the type checker introduces artifical identifiers which are not + // present in the source. Once it doesn't do that anymore, enable the checks below. + /* + for x := range idents { + t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name) + } + */ +} diff --git a/src/pkg/go/types/scope.go b/src/pkg/go/types/scope.go new file mode 100644 index 000000000..463ee40c5 --- /dev/null +++ b/src/pkg/go/types/scope.go @@ -0,0 +1,78 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "bytes" + "fmt" +) + +// A Scope maintains the set of named language entities declared +// in the scope and a link to the immediately surrounding (outer) +// scope. +// +type Scope struct { + Outer *Scope + Entries []Object // scope entries in insertion order + large map[string]Object // for fast lookup - only used for larger scopes +} + +// Lookup returns the object with the given name if it is +// found in scope s, otherwise it returns nil. Outer scopes +// are ignored. +// +func (s *Scope) Lookup(name string) Object { + if s.large != nil { + return s.large[name] + } + for _, obj := range s.Entries { + if obj.GetName() == name { + return obj + } + } + return nil +} + +// Insert attempts to insert an object obj into scope s. +// If s already contains an object with the same name, +// Insert leaves s unchanged and returns that object. +// Otherwise it inserts obj and returns nil. +// +func (s *Scope) Insert(obj Object) Object { + name := obj.GetName() + if alt := s.Lookup(name); alt != nil { + return alt + } + s.Entries = append(s.Entries, obj) + + // If the scope size reaches a threshold, use a map for faster lookups. + const threshold = 20 + if len(s.Entries) > threshold { + if s.large == nil { + m := make(map[string]Object, len(s.Entries)) + for _, obj := range s.Entries { + m[obj.GetName()] = obj + } + s.large = m + } + s.large[name] = obj + } + + return nil +} + +// Debugging support +func (s *Scope) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "scope %p {", s) + if s != nil && len(s.Entries) > 0 { + fmt.Fprintln(&buf) + for _, obj := range s.Entries { + fmt.Fprintf(&buf, "\t%s\t%T\n", obj.GetName(), obj) + } + } + fmt.Fprintf(&buf, "}\n") + return buf.String() +} diff --git a/src/pkg/go/types/sizes.go b/src/pkg/go/types/sizes.go new file mode 100644 index 000000000..ef6499ba4 --- /dev/null +++ b/src/pkg/go/types/sizes.go @@ -0,0 +1,162 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements support for (unsafe) Alignof, Offsetof, and Sizeof. + +package types + +func (ctxt *Context) alignof(typ Type) int64 { + if f := ctxt.Alignof; f != nil { + if a := f(typ); a >= 1 { + return a + } + panic("Context.Alignof returned an alignment < 1") + } + return DefaultAlignof(typ) +} + +func (ctxt *Context) offsetsof(s *Struct) []int64 { + offsets := s.offsets + if offsets == nil { + // compute offsets on demand + if f := ctxt.Offsetsof; f != nil { + offsets = f(s.Fields) + // sanity checks + if len(offsets) != len(s.Fields) { + panic("Context.Offsetsof returned the wrong number of offsets") + } + for _, o := range offsets { + if o < 0 { + panic("Context.Offsetsof returned an offset < 0") + } + } + } else { + offsets = DefaultOffsetsof(s.Fields) + } + s.offsets = offsets + } + return offsets +} + +// offsetof returns the offset of the field specified via +// the index sequence relative to typ. It returns a value +// < 0 if the field is in an embedded pointer type. +func (ctxt *Context) offsetof(typ Type, index []int) int64 { + var o int64 + for _, i := range index { + s, _ := underlying(typ).(*Struct) + if s == nil { + return -1 + } + o += ctxt.offsetsof(s)[i] + typ = s.Fields[i].Type + } + return o +} + +func (ctxt *Context) sizeof(typ Type) int64 { + if f := ctxt.Sizeof; f != nil { + if s := f(typ); s >= 0 { + return s + } + panic("Context.Sizeof returned a size < 0") + } + return DefaultSizeof(typ) +} + +// DefaultMaxAlign is the default maximum alignment, in bytes, +// used by DefaultAlignof. +const DefaultMaxAlign = 8 + +// DefaultAlignof implements the default alignment computation +// for unsafe.Alignof. It is used if Context.Alignof == nil. +func DefaultAlignof(typ Type) int64 { + // For arrays and structs, alignment is defined in terms + // of alignment of the elements and fields, respectively. + switch t := underlying(typ).(type) { + case *Array: + // spec: "For a variable x of array type: unsafe.Alignof(x) + // is the same as unsafe.Alignof(x[0]), but at least 1." + return DefaultAlignof(t.Elt) + case *Struct: + // spec: "For a variable x of struct type: unsafe.Alignof(x) + // is the largest of the values unsafe.Alignof(x.f) for each + // field f of x, but at least 1." + max := int64(1) + for _, f := range t.Fields { + if a := DefaultAlignof(f.Type); a > max { + max = a + } + } + return max + } + a := DefaultSizeof(typ) // may be 0 + // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." + if a < 1 { + return 1 + } + if a > DefaultMaxAlign { + return DefaultMaxAlign + } + return a +} + +// align returns the smallest y >= x such that y % a == 0. +func align(x, a int64) int64 { + y := x + a - 1 + return y - y%a +} + +// DefaultOffsetsof implements the default field offset computation +// for unsafe.Offsetof. It is used if Context.Offsetsof == nil. +func DefaultOffsetsof(fields []*Field) []int64 { + offsets := make([]int64, len(fields)) + var o int64 + for i, f := range fields { + a := DefaultAlignof(f.Type) + o = align(o, a) + offsets[i] = o + o += DefaultSizeof(f.Type) + } + return offsets +} + +// DefaultPtrSize is the default size of ints, uint, and pointers, in bytes, +// used by DefaultSizeof. +const DefaultPtrSize = 8 + +// DefaultSizeof implements the default size computation +// for unsafe.Sizeof. It is used if Context.Sizeof == nil. +func DefaultSizeof(typ Type) int64 { + switch t := underlying(typ).(type) { + case *Basic: + if s := t.size; s > 0 { + return s + } + if t.Kind == String { + return DefaultPtrSize * 2 + } + case *Array: + a := DefaultAlignof(t.Elt) + s := DefaultSizeof(t.Elt) + return align(s, a) * t.Len // may be 0 + case *Slice: + return DefaultPtrSize * 3 + case *Struct: + n := len(t.Fields) + if n == 0 { + return 0 + } + offsets := t.offsets + if t.offsets == nil { + // compute offsets on demand + offsets = DefaultOffsetsof(t.Fields) + t.offsets = offsets + } + return offsets[n-1] + DefaultSizeof(t.Fields[n-1].Type) + case *Signature: + return DefaultPtrSize * 2 + } + return DefaultPtrSize // catch-all +} diff --git a/src/pkg/go/types/stmt.go b/src/pkg/go/types/stmt.go new file mode 100644 index 000000000..53c46a167 --- /dev/null +++ b/src/pkg/go/types/stmt.go @@ -0,0 +1,743 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements typechecking of statements. + +package types + +import ( + "go/ast" + "go/token" +) + +// assigment reports whether x can be assigned to a variable of type 'to', +// if necessary by attempting to convert untyped values to the appropriate +// type. If x.mode == invalid upon return, then assignment has already +// issued an error message and the caller doesn't have to report another. +// TODO(gri) This latter behavior is for historic reasons and complicates +// callers. Needs to be cleaned up. +func (check *checker) assignment(x *operand, to Type) bool { + if x.mode == invalid { + return false + } + + if t, ok := x.typ.(*Result); ok { + // TODO(gri) elsewhere we use "assignment count mismatch" (consolidate) + check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x) + x.mode = invalid + return false + } + + check.convertUntyped(x, to) + + return x.mode != invalid && x.isAssignable(check.ctxt, to) +} + +// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil), +// or lhs = x (if rhs == nil). If decl is set, the lhs operand must be an identifier. +// If its type is not set, it is deduced from the type or value of x. If lhs has a +// type it is used as a hint when evaluating rhs, if present. +// +func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int) { + ident, _ := lhs.(*ast.Ident) + if x == nil { + assert(rhs != nil) + x = new(operand) + } + + if ident != nil && ident.Name == "_" { + // anything can be assigned to a blank identifier - check rhs only, if present + if rhs != nil { + check.expr(x, rhs, nil, iota) + } + return + } + + if !decl { + // regular assignment - start with lhs to obtain a type hint + // TODO(gri) clean this up - we don't need type hints anymore + var z operand + check.expr(&z, lhs, nil, -1) + if z.mode == invalid { + z.typ = nil // so we can proceed with rhs + } + + if rhs != nil { + check.expr(x, rhs, z.typ, -1) + if x.mode == invalid { + return + } + } + + if x.mode == invalid || z.mode == invalid { + return + } + + if !check.assignment(x, z.typ) { + if x.mode != invalid { + check.errorf(x.pos(), "cannot assign %s to %s", x, &z) + } + return + } + if z.mode == constant { + check.errorf(x.pos(), "cannot assign %s to %s", x, &z) + } + return + } + + // declaration - lhs must be an identifier + if ident == nil { + check.errorf(lhs.Pos(), "cannot declare %s", lhs) + return + } + + // lhs may or may not be typed yet + obj := check.lookup(ident) + var typ Type + if t := obj.GetType(); t != nil { + typ = t + } + + if rhs != nil { + check.expr(x, rhs, typ, iota) + // continue even if x.mode == invalid + } + + if typ == nil { + // determine lhs type from rhs expression; + // for variables, convert untyped types to + // default types + typ = Typ[Invalid] + if x.mode != invalid { + typ = x.typ + if _, ok := obj.(*Var); ok && isUntyped(typ) { + if x.isNil() { + check.errorf(x.pos(), "use of untyped nil") + x.mode = invalid + } else { + typ = defaultType(typ) + } + } + } + switch obj := obj.(type) { + case *Const: + obj.Type = typ + case *Var: + obj.Type = typ + default: + unreachable() + } + } + + if x.mode != invalid { + if !check.assignment(x, typ) { + if x.mode != invalid { + switch obj.(type) { + case *Const: + check.errorf(x.pos(), "cannot assign %s to variable of type %s", x, typ) + case *Var: + check.errorf(x.pos(), "cannot initialize constant of type %s with %s", typ, x) + default: + unreachable() + } + x.mode = invalid + } + } + } + + // for constants, set their value + if obj, ok := obj.(*Const); ok { + assert(obj.Val == nil) + if x.mode != invalid { + if x.mode == constant { + if isConstType(x.typ) { + obj.Val = x.val + } else { + check.errorf(x.pos(), "%s has invalid constant type", x) + } + } else { + check.errorf(x.pos(), "%s is not constant", x) + } + } + if obj.Val == nil { + // set the constant to its type's zero value to reduce spurious errors + switch typ := underlying(obj.Type); { + case typ == Typ[Invalid]: + // ignore + case isBoolean(typ): + obj.Val = false + case isNumeric(typ): + obj.Val = int64(0) + case isString(typ): + obj.Val = "" + case hasNil(typ): + obj.Val = nilConst + default: + // in all other cases just prevent use of the constant + // TODO(gri) re-evaluate this code + obj.Val = nilConst + } + } + } +} + +// assignNtoM typechecks a general assignment. If decl is set, the lhs operands +// must be identifiers. If their types are not set, they are deduced from the +// types of the corresponding rhs expressions. iota >= 0 indicates that the +// "assignment" is part of a constant/variable declaration. +// Precondition: len(lhs) > 0 . +// +func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) { + assert(len(lhs) > 0) + + if len(lhs) == len(rhs) { + for i, e := range rhs { + check.assign1to1(lhs[i], e, nil, decl, iota) + } + return + } + + if len(rhs) == 1 { + // len(lhs) > 1, therefore a correct rhs expression + // cannot be a shift and we don't need a type hint; + // ok to evaluate rhs first + var x operand + check.expr(&x, rhs[0], nil, iota) + if x.mode == invalid { + // If decl is set, this leaves the lhs identifiers + // untyped. We catch this when looking up the respective + // object. + return + } + + if t, ok := x.typ.(*Result); ok && len(lhs) == len(t.Values) { + // function result + x.mode = value + for i, obj := range t.Values { + x.expr = nil // TODO(gri) should do better here + x.typ = obj.Type + check.assign1to1(lhs[i], nil, &x, decl, iota) + } + return + } + + if x.mode == valueok && len(lhs) == 2 { + // comma-ok expression + x.mode = value + check.assign1to1(lhs[0], nil, &x, decl, iota) + + x.mode = value + x.typ = Typ[UntypedBool] + check.assign1to1(lhs[1], nil, &x, decl, iota) + return + } + } + + check.errorf(lhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs)) + + // avoid checking the same declaration over and over + // again for each lhs identifier that has no type yet + if iota >= 0 { + // declaration + for _, e := range lhs { + if name, ok := e.(*ast.Ident); ok { + switch obj := check.lookup(name).(type) { + case *Const: + obj.Type = Typ[Invalid] + case *Var: + obj.Type = Typ[Invalid] + default: + unreachable() + } + } + } + } +} + +func (check *checker) optionalStmt(s ast.Stmt) { + if s != nil { + check.stmt(s) + } +} + +func (check *checker) stmtList(list []ast.Stmt) { + for _, s := range list { + check.stmt(s) + } +} + +func (check *checker) call(call *ast.CallExpr) { + var x operand + check.rawExpr(&x, call, nil, -1, false) // don't check if value is used + // TODO(gri) If a builtin is called, the builtin must be valid in statement context. +} + +func (check *checker) multipleDefaults(list []ast.Stmt) { + var first ast.Stmt + for _, s := range list { + var d ast.Stmt + switch c := s.(type) { + case *ast.CaseClause: + if len(c.List) == 0 { + d = s + } + case *ast.CommClause: + if c.Comm == nil { + d = s + } + default: + check.invalidAST(s.Pos(), "case/communication clause expected") + } + if d != nil { + if first != nil { + check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos()) + } else { + first = d + } + } + } +} + +// stmt typechecks statement s. +func (check *checker) stmt(s ast.Stmt) { + switch s := s.(type) { + case *ast.BadStmt, *ast.EmptyStmt: + // ignore + + case *ast.DeclStmt: + d, _ := s.Decl.(*ast.GenDecl) + if d == nil || (d.Tok != token.CONST && d.Tok != token.TYPE && d.Tok != token.VAR) { + check.invalidAST(token.NoPos, "const, type, or var declaration expected") + return + } + if d.Tok == token.CONST { + check.assocInitvals(d) + } + check.decl(d) + + case *ast.LabeledStmt: + // TODO(gri) anything to do with label itself? + check.stmt(s.Stmt) + + case *ast.ExprStmt: + var x operand + used := false + switch e := unparen(s.X).(type) { + case *ast.CallExpr: + // function calls are permitted + used = true + // but some builtins are excluded + // (Caution: This evaluates e.Fun twice, once here and once + // below as part of s.X. This has consequences for + // check.register. Perhaps this can be avoided.) + check.expr(&x, e.Fun, nil, -1) + if x.mode != invalid { + if b, ok := x.typ.(*builtin); ok && !b.isStatement { + used = false + } + } + case *ast.UnaryExpr: + // receive operations are permitted + if e.Op == token.ARROW { + used = true + } + } + if !used { + check.errorf(s.Pos(), "%s not used", s.X) + // ok to continue + } + check.rawExpr(&x, s.X, nil, -1, false) + if x.mode == typexpr { + check.errorf(x.pos(), "%s is not an expression", &x) + } + + case *ast.SendStmt: + var ch, x operand + check.expr(&ch, s.Chan, nil, -1) + check.expr(&x, s.Value, nil, -1) + if ch.mode == invalid || x.mode == invalid { + return + } + if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !check.assignment(&x, tch.Elt) { + if x.mode != invalid { + check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch) + } + } + + case *ast.IncDecStmt: + var op token.Token + switch s.Tok { + case token.INC: + op = token.ADD + case token.DEC: + op = token.SUB + default: + check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok) + return + } + var x operand + Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position + check.binary(&x, s.X, Y, op, -1) + if x.mode == invalid { + return + } + check.assign1to1(s.X, nil, &x, false, -1) + + case *ast.AssignStmt: + switch s.Tok { + case token.ASSIGN, token.DEFINE: + if len(s.Lhs) == 0 { + check.invalidAST(s.Pos(), "missing lhs in assignment") + return + } + check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1) + default: + // assignment operations + if len(s.Lhs) != 1 || len(s.Rhs) != 1 { + check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok) + return + } + // TODO(gri) make this conversion more efficient + var op token.Token + switch s.Tok { + case token.ADD_ASSIGN: + op = token.ADD + case token.SUB_ASSIGN: + op = token.SUB + case token.MUL_ASSIGN: + op = token.MUL + case token.QUO_ASSIGN: + op = token.QUO + case token.REM_ASSIGN: + op = token.REM + case token.AND_ASSIGN: + op = token.AND + case token.OR_ASSIGN: + op = token.OR + case token.XOR_ASSIGN: + op = token.XOR + case token.SHL_ASSIGN: + op = token.SHL + case token.SHR_ASSIGN: + op = token.SHR + case token.AND_NOT_ASSIGN: + op = token.AND_NOT + default: + check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok) + return + } + var x operand + check.binary(&x, s.Lhs[0], s.Rhs[0], op, -1) + if x.mode == invalid { + return + } + check.assign1to1(s.Lhs[0], nil, &x, false, -1) + } + + case *ast.GoStmt: + check.call(s.Call) + + case *ast.DeferStmt: + check.call(s.Call) + + case *ast.ReturnStmt: + sig := check.funcsig + if n := len(sig.Results); n > 0 { + // TODO(gri) should not have to compute lhs, named every single time - clean this up + lhs := make([]ast.Expr, n) + named := false // if set, function has named results + for i, res := range sig.Results { + if len(res.Name) > 0 { + // a blank (_) result parameter is a named result + named = true + } + name := ast.NewIdent(res.Name) + name.NamePos = s.Pos() + check.register(name, &Var{Name: res.Name, Type: res.Type}) // Pkg == nil + lhs[i] = name + } + if len(s.Results) > 0 || !named { + // TODO(gri) assignNtoM should perhaps not require len(lhs) > 0 + check.assignNtoM(lhs, s.Results, false, -1) + } + } else if len(s.Results) > 0 { + check.errorf(s.Pos(), "no result values expected") + } + + case *ast.BranchStmt: + // TODO(gri) implement this + + case *ast.BlockStmt: + check.stmtList(s.List) + + case *ast.IfStmt: + check.optionalStmt(s.Init) + var x operand + check.expr(&x, s.Cond, nil, -1) + if x.mode != invalid && !isBoolean(x.typ) { + check.errorf(s.Cond.Pos(), "non-boolean condition in if statement") + } + check.stmt(s.Body) + check.optionalStmt(s.Else) + + case *ast.SwitchStmt: + check.optionalStmt(s.Init) + var x operand + tag := s.Tag + if tag == nil { + // use fake true tag value and position it at the opening { of the switch + ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"} + check.register(ident, Universe.Lookup("true")) + tag = ident + } + check.expr(&x, tag, nil, -1) + + check.multipleDefaults(s.Body.List) + seen := make(map[interface{}]token.Pos) + for _, s := range s.Body.List { + clause, _ := s.(*ast.CaseClause) + if clause == nil { + continue // error reported before + } + if x.mode != invalid { + for _, expr := range clause.List { + x := x // copy of x (don't modify original) + var y operand + check.expr(&y, expr, nil, -1) + if y.mode == invalid { + continue // error reported before + } + // If we have a constant case value, it must appear only + // once in the switch statement. Determine if there is a + // duplicate entry, but only report an error if there are + // no other errors. + var dupl token.Pos + var yy operand + if y.mode == constant { + // TODO(gri) This code doesn't work correctly for + // large integer, floating point, or + // complex values - the respective struct + // comparisons are shallow. Need to use a + // hash function to index the map. + dupl = seen[y.val] + seen[y.val] = y.pos() + yy = y // remember y + } + // TODO(gri) The convertUntyped call pair below appears in other places. Factor! + // Order matters: By comparing y against x, error positions are at the case values. + check.convertUntyped(&y, x.typ) + if y.mode == invalid { + continue // error reported before + } + check.convertUntyped(&x, y.typ) + if x.mode == invalid { + continue // error reported before + } + check.comparison(&y, &x, token.EQL) + if y.mode != invalid && dupl.IsValid() { + check.errorf(yy.pos(), "%s is duplicate case (previous at %s)", + &yy, check.fset.Position(dupl)) + } + } + } + check.stmtList(clause.Body) + } + + case *ast.TypeSwitchStmt: + check.optionalStmt(s.Init) + + // A type switch guard must be of the form: + // + // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" . + // + // The parser is checking syntactic correctness; + // remaining syntactic errors are considered AST errors here. + // TODO(gri) better factoring of error handling (invalid ASTs) + // + var lhs *Var // lhs variable or nil + var rhs ast.Expr + switch guard := s.Assign.(type) { + case *ast.ExprStmt: + rhs = guard.X + case *ast.AssignStmt: + if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 { + check.invalidAST(s.Pos(), "incorrect form of type switch guard") + return + } + ident, _ := guard.Lhs[0].(*ast.Ident) + if ident == nil { + check.invalidAST(s.Pos(), "incorrect form of type switch guard") + return + } + lhs = check.lookup(ident).(*Var) + rhs = guard.Rhs[0] + default: + check.invalidAST(s.Pos(), "incorrect form of type switch guard") + return + } + + // rhs must be of the form: expr.(type) and expr must be an interface + expr, _ := rhs.(*ast.TypeAssertExpr) + if expr == nil || expr.Type != nil { + check.invalidAST(s.Pos(), "incorrect form of type switch guard") + return + } + var x operand + check.expr(&x, expr.X, nil, -1) + if x.mode == invalid { + return + } + var T *Interface + if T, _ = underlying(x.typ).(*Interface); T == nil { + check.errorf(x.pos(), "%s is not an interface", &x) + return + } + + check.multipleDefaults(s.Body.List) + for _, s := range s.Body.List { + clause, _ := s.(*ast.CaseClause) + if clause == nil { + continue // error reported before + } + // Check each type in this type switch case. + var typ Type + for _, expr := range clause.List { + typ = check.typOrNil(expr, false) + if typ != nil && typ != Typ[Invalid] { + if method, wrongType := missingMethod(typ, T); method != nil { + var msg string + if wrongType { + msg = "%s cannot have dynamic type %s (wrong type for method %s)" + } else { + msg = "%s cannot have dynamic type %s (missing method %s)" + } + check.errorf(expr.Pos(), msg, &x, typ, method.Name) + // ok to continue + } + } + } + // If lhs exists, set its type for each clause. + if lhs != nil { + // In clauses with a case listing exactly one type, the variable has that type; + // otherwise, the variable has the type of the expression in the TypeSwitchGuard. + if len(clause.List) != 1 || typ == nil { + typ = x.typ + } + lhs.Type = typ + } + check.stmtList(clause.Body) + } + + // There is only one object (lhs) associated with a lhs identifier, but that object + // assumes different types for different clauses. Set it back to the type of the + // TypeSwitchGuard expression so that that variable always has a valid type. + if lhs != nil { + lhs.Type = x.typ + } + + case *ast.SelectStmt: + check.multipleDefaults(s.Body.List) + for _, s := range s.Body.List { + clause, _ := s.(*ast.CommClause) + if clause == nil { + continue // error reported before + } + check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt) + check.stmtList(clause.Body) + } + + case *ast.ForStmt: + check.optionalStmt(s.Init) + if s.Cond != nil { + var x operand + check.expr(&x, s.Cond, nil, -1) + if x.mode != invalid && !isBoolean(x.typ) { + check.errorf(s.Cond.Pos(), "non-boolean condition in for statement") + } + } + check.optionalStmt(s.Post) + check.stmt(s.Body) + + case *ast.RangeStmt: + // check expression to iterate over + decl := s.Tok == token.DEFINE + var x operand + check.expr(&x, s.X, nil, -1) + if x.mode == invalid { + // if we don't have a declaration, we can still check the loop's body + if !decl { + check.stmt(s.Body) + } + return + } + + // determine key/value types + var key, val Type + switch typ := underlying(x.typ).(type) { + case *Basic: + if isString(typ) { + key = Typ[UntypedInt] + val = Typ[UntypedRune] + } + case *Array: + key = Typ[UntypedInt] + val = typ.Elt + case *Slice: + key = Typ[UntypedInt] + val = typ.Elt + case *Pointer: + if typ, _ := underlying(typ.Base).(*Array); typ != nil { + key = Typ[UntypedInt] + val = typ.Elt + } + case *Map: + key = typ.Key + val = typ.Elt + case *Chan: + key = typ.Elt + if typ.Dir&ast.RECV == 0 { + check.errorf(x.pos(), "cannot range over send-only channel %s", &x) + // ok to continue + } + if s.Value != nil { + check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x) + // ok to continue + } + } + + if key == nil { + check.errorf(x.pos(), "cannot range over %s", &x) + // if we don't have a declaration, we can still check the loop's body + if !decl { + check.stmt(s.Body) + } + return + } + + // check assignment to/declaration of iteration variables + // TODO(gri) The error messages/positions are not great here, + // they refer to the expression in the range clause. + // Should give better messages w/o too much code + // duplication (assignment checking). + x.mode = value + if s.Key != nil { + x.typ = key + x.expr = s.Key + check.assign1to1(s.Key, nil, &x, decl, -1) + } else { + check.invalidAST(s.Pos(), "range clause requires index iteration variable") + // ok to continue + } + if s.Value != nil { + x.typ = val + x.expr = s.Value + check.assign1to1(s.Value, nil, &x, decl, -1) + } + + check.stmt(s.Body) + + default: + check.errorf(s.Pos(), "invalid statement") + } +} diff --git a/src/pkg/go/types/testdata/builtins.src b/src/pkg/go/types/testdata/builtins.src new file mode 100644 index 000000000..c08c442ce --- /dev/null +++ b/src/pkg/go/types/testdata/builtins.src @@ -0,0 +1,401 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// builtin calls + +package builtins + +import "unsafe" + +func _append() { + var x int + var s []byte + _0 := append /* ERROR "argument" */ () + _1 := append("foo" /* ERROR "not a typed slice" */) + _2 := append(nil /* ERROR "not a typed slice" */, s) + _3 := append(x /* ERROR "not a typed slice" */, s) + _4 := append(s) + append /* ERROR "not used" */ (s) +} + +func _cap() { + var a [10]bool + var p *[20]int + var s []int + var c chan string + _0 := cap /* ERROR "argument" */ () + _1 := cap /* ERROR "argument" */ (1, 2) + _2 := cap(42 /* ERROR "invalid" */) + const _3 = cap(a) + assert(_3 == 10) + const _4 = cap(p) + assert(_4 == 20) + _5 := cap(c) + cap /* ERROR "not used" */ (c) + + // issue 4744 + type T struct{ a [10]int } + const _ = cap(((*T)(nil)).a) +} + +func _close() { + var c chan int + var r <-chan int + close /* ERROR "argument" */ () + close /* ERROR "argument" */ (1, 2) + close(42 /* ERROR "not a channel" */) + close(r /* ERROR "receive-only channel" */) + close(c) +} + +func _complex() { + var i32 int32 + var f32 float32 + var f64 float64 + var c64 complex64 + _ = complex /* ERROR "argument" */ () + _ = complex /* ERROR "argument" */ (1) + _ = complex(true /* ERROR "invalid argument" */ , 0) + _ = complex(i32 /* ERROR "invalid argument" */ , 0) + _ = complex("foo" /* ERROR "invalid argument" */ , 0) + _ = complex(c64 /* ERROR "invalid argument" */ , 0) + _ = complex(0, true /* ERROR "invalid argument" */ ) + _ = complex(0, i32 /* ERROR "invalid argument" */ ) + _ = complex(0, "foo" /* ERROR "invalid argument" */ ) + _ = complex(0, c64 /* ERROR "invalid argument" */ ) + _ = complex(f32, f32) + _ = complex(f32, 1) + _ = complex(f32, 1.0) + _ = complex(f32, 'a') + _ = complex(f64, f64) + _ = complex(f64, 1) + _ = complex(f64, 1.0) + _ = complex(f64, 'a') + _ = complex(f32 /* ERROR "mismatched types" */, f64) + _ = complex(f64 /* ERROR "mismatched types" */, f32) + _ = complex(1, 1) + _ = complex(1, 1.1) + _ = complex(1, 'a') + complex /* ERROR "not used" */ (1, 2) +} + +func _copy() { + copy /* ERROR "not enough arguments" */ () + copy /* ERROR "not enough arguments" */ ("foo") + copy([ /* ERROR "copy expects slice arguments" */ ...]int{}, []int{}) + copy([ /* ERROR "copy expects slice arguments" */ ]int{}, [...]int{}) + copy([ /* ERROR "different element types" */ ]int8{}, "foo") + + // spec examples + var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7} + var s = make([]int, 6) + var b = make([]byte, 5) + n1 := copy(s, a[0:]) // n1 == 6, s == []int{0, 1, 2, 3, 4, 5} + n2 := copy(s, s[2:]) // n2 == 4, s == []int{2, 3, 4, 5, 4, 5} + n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello") +} + +func _delete() { + var m map[string]int + var s string + delete /* ERROR "argument" */ () + delete /* ERROR "argument" */ (1) + delete /* ERROR "argument" */ (1, 2, 3) + delete(m, 0 /* ERROR "not assignable" */) + delete(m, s) +} + +func _imag() { + var f32 float32 + var f64 float64 + var c64 complex64 + var c128 complex128 + _ = imag /* ERROR "argument" */ () + _ = imag /* ERROR "argument" */ (1, 2) + _ = imag(10 /* ERROR "must be a complex number" */) + _ = imag(2.7182818 /* ERROR "must be a complex number" */) + _ = imag("foo" /* ERROR "must be a complex number" */) + const _5 = imag(1 + 2i) + assert(_5 == 2) + f32 = _5 + f64 = _5 + const _6 = imag(0i) + assert(_6 == 0) + f32 = imag(c64) + f64 = imag(c128) + f32 = imag /* ERROR "cannot assign" */ (c128) + f64 = imag /* ERROR "cannot assign" */ (c64) + imag /* ERROR "not used" */ (c64) +} + +func _len() { + const c = "foobar" + var a [10]bool + var p *[20]int + var s []int + var m map[string]complex128 + _ = len /* ERROR "argument" */ () + _ = len /* ERROR "argument" */ (1, 2) + _ = len(42 /* ERROR "invalid" */) + const _3 = len(c) + assert(_3 == 6) + const _4 = len(a) + assert(_4 == 10) + const _5 = len(p) + assert(_5 == 20) + _ = len(m) + len /* ERROR "not used" */ (c) + + // esoteric case + var t string + var hash map[interface{}][]*[10]int + const n = len /* ERROR "not constant" */ (hash[recover()][len(t)]) + assert /* ERROR "failed" */ (n == 10) + var ch <-chan int + const nn = len /* ERROR "not constant" */ (hash[<-ch][len(t)]) + _ = nn // TODO(gri) remove this once unused constants get type-checked + + // issue 4744 + type T struct{ a [10]int } + const _ = len(((*T)(nil)).a) +} + +func _make() { + n := 0 + + _ = make /* ERROR "argument" */ () + _ = make(1 /* ERROR "not a type" */) + _ = make(int /* ERROR "cannot make" */) + + // slices + _ = make/* ERROR "arguments" */ ([]int) + _ = make/* ERROR "arguments" */ ([]int, 2, 3, 4) + _ = make([]int, int /* ERROR "not an expression" */) + _ = make([]int, 10, float32 /* ERROR "not an expression" */) + _ = make([]int, "foo" /* ERROR "must be an integer" */) + _ = make([]int, 10, 2.3 /* ERROR "must be an integer" */) + _ = make([]int, 5, 10.0) + _ = make([]int, 0i) + _ = make([]int, - /* ERROR "must not be negative" */ 1, 10) + _ = make([]int, 0, - /* ERROR "must not be negative" */ 1) + _ = make([]int, - /* ERROR "must not be negative" */ 1, - /* ERROR "must not be negative" */ 1) + _ = make([]int, 1<<100, 1<<100) // run-time panic + _ = make([]int, 1 /* ERROR "length and capacity swapped" */ <<100 + 1, 1<<100) + _ = make([]int, 1 /* ERROR "length and capacity swapped" */ <<100, 12345) + + // maps + _ = make /* ERROR "arguments" */ (map[int]string, 10, 20) + _ = make(map[int]float32, int /* ERROR "not an expression" */) + _ = make(map[int]float32, "foo" /* ERROR "must be an integer" */) + _ = make(map[int]float32, 10) + _ = make(map[int]float32, n) + _ = make(map[int]float32, int64(n)) + + // channels + _ = make /* ERROR "arguments" */ (chan int, 10, 20) + _ = make(chan int, int /* ERROR "not an expression" */) + _ = make(chan<- int, "foo" /* ERROR "must be an integer" */) + _ = make(<-chan float64, 10) + _ = make(chan chan int, n) + _ = make(chan string, int64(n)) + + make /* ERROR "not used" */ ([]int, 10) +} + +func _new() { + _ = new /* ERROR "argument" */ () + _ = new /* ERROR "argument" */ (1, 2) + _ = new("foo" /* ERROR "not a type" */) + p := new(float64) + _ = new(struct{ x, y int }) + q := new(*float64) + _ = *p == **q + new /* ERROR "not used" */ (int) +} + +func _panic() { + panic /* ERROR "arguments" */ () + panic /* ERROR "arguments" */ (1, 2) + panic(0) + panic("foo") + panic(false) +} + +func _print() { + print() + print(1) + print(1, 2) + print("foo") + print(2.718281828) + print(false) +} + +func _println() { + println() + println(1) + println(1, 2) + println("foo") + println(2.718281828) + println(false) +} + +func _real() { + var f32 float32 + var f64 float64 + var c64 complex64 + var c128 complex128 + _ = real /* ERROR "argument" */ () + _ = real /* ERROR "argument" */ (1, 2) + _ = real(10 /* ERROR "must be a complex number" */) + _ = real(2.7182818 /* ERROR "must be a complex number" */) + _ = real("foo" /* ERROR "must be a complex number" */) + const _5 = real(1 + 2i) + assert(_5 == 1) + f32 = _5 + f64 = _5 + const _6 = real(0i) + assert(_6 == 0) + f32 = real(c64) + f64 = real(c128) + f32 = real /* ERROR "cannot assign" */ (c128) + f64 = real /* ERROR "cannot assign" */ (c64) + real /* ERROR "not used" */ (c64) +} + +func _recover() { + _ = recover() + _ = recover /* ERROR "argument" */ (10) + recover() +} + +// assuming types.DefaultPtrSize == 8 +type S0 struct{ // offset + a bool // 0 + b rune // 4 + c *int // 8 + d bool // 16 + e complex128 // 24 +} // 40 + +type S1 struct{ // offset + x float32 // 0 + y string // 8 + z *S1 // 24 + S0 // 32 +} // 72 + +type S2 struct{ // offset + *S1 // 0 +} // 8 + +func _Alignof() { + var x int + _ = unsafe /* ERROR "argument" */ .Alignof() + _ = unsafe /* ERROR "argument" */ .Alignof(1, 2) + _ = unsafe.Alignof(int /* ERROR "not an expression" */) + _ = unsafe.Alignof(42) + _ = unsafe.Alignof(new(struct{})) + unsafe /* ERROR "not used" */ .Alignof(x) + + var y S0 + assert(unsafe.Alignof(y.a) == 1) + assert(unsafe.Alignof(y.b) == 4) + assert(unsafe.Alignof(y.c) == 8) + assert(unsafe.Alignof(y.d) == 1) + assert(unsafe.Alignof(y.e) == 8) +} + +func _Offsetof() { + var x struct{ f int } + _ = unsafe /* ERROR "argument" */ .Offsetof() + _ = unsafe /* ERROR "argument" */ .Offsetof(1, 2) + _ = unsafe.Offsetof(int /* ERROR "not an expression" */) + _ = unsafe.Offsetof(x /* ERROR "not a selector" */) + _ = unsafe.Offsetof(x.f) + _ = unsafe.Offsetof((x.f)) + _ = unsafe.Offsetof((((((((x))).f))))) + unsafe /* ERROR "not used" */ .Offsetof(x.f) + + var y0 S0 + assert(unsafe.Offsetof(y0.a) == 0) + assert(unsafe.Offsetof(y0.b) == 4) + assert(unsafe.Offsetof(y0.c) == 8) + assert(unsafe.Offsetof(y0.d) == 16) + assert(unsafe.Offsetof(y0.e) == 24) + + var y1 S1 + assert(unsafe.Offsetof(y1.x) == 0) + assert(unsafe.Offsetof(y1.y) == 8) + assert(unsafe.Offsetof(y1.z) == 24) + assert(unsafe.Offsetof(y1.S0) == 32) + + assert(unsafe.Offsetof(y1.S0.a) == 0) // relative to S0 + assert(unsafe.Offsetof(y1.a) == 32) // relative to S1 + assert(unsafe.Offsetof(y1.b) == 36) // relative to S1 + assert(unsafe.Offsetof(y1.c) == 40) // relative to S1 + assert(unsafe.Offsetof(y1.d) == 48) // relative to S1 + assert(unsafe.Offsetof(y1.e) == 56) // relative to S1 + + var y2 S2 + assert(unsafe.Offsetof(y2.S1) == 0) + _ = unsafe.Offsetof(y2 /* ERROR "embedded via pointer" */ .x) +} + +func _Sizeof() { + var x int + _ = unsafe /* ERROR "argument" */ .Sizeof() + _ = unsafe /* ERROR "argument" */ .Sizeof(1, 2) + _ = unsafe.Sizeof(int /* ERROR "not an expression" */) + _ = unsafe.Sizeof(42) + _ = unsafe.Sizeof(new(complex128)) + unsafe /* ERROR "not used" */ .Sizeof(x) + + // basic types have size guarantees + assert(unsafe.Sizeof(byte(0)) == 1) + assert(unsafe.Sizeof(uint8(0)) == 1) + assert(unsafe.Sizeof(int8(0)) == 1) + assert(unsafe.Sizeof(uint16(0)) == 2) + assert(unsafe.Sizeof(int16(0)) == 2) + assert(unsafe.Sizeof(uint32(0)) == 4) + assert(unsafe.Sizeof(int32(0)) == 4) + assert(unsafe.Sizeof(float32(0)) == 4) + assert(unsafe.Sizeof(uint64(0)) == 8) + assert(unsafe.Sizeof(int64(0)) == 8) + assert(unsafe.Sizeof(float64(0)) == 8) + assert(unsafe.Sizeof(complex64(0)) == 8) + assert(unsafe.Sizeof(complex128(0)) == 16) + + var y0 S0 + assert(unsafe.Sizeof(y0.a) == 1) + assert(unsafe.Sizeof(y0.b) == 4) + assert(unsafe.Sizeof(y0.c) == 8) + assert(unsafe.Sizeof(y0.d) == 1) + assert(unsafe.Sizeof(y0.e) == 16) + assert(unsafe.Sizeof(y0) == 40) + + var y1 S1 + assert(unsafe.Sizeof(y1) == 72) + + var y2 S2 + assert(unsafe.Sizeof(y2) == 8) +} + +// self-testing only +func _assert() { + var x int + assert /* ERROR "argument" */ () + assert /* ERROR "argument" */ (1, 2) + assert("foo" /* ERROR "boolean constant" */ ) + assert(x /* ERROR "boolean constant" */) + assert(true) + assert /* ERROR "failed" */ (false) +} + +// self-testing only +func _trace() { + // Uncomment the code below to test trace - will produce console output + // _ = trace /* ERROR "no value" */ () + // _ = trace(1) + // _ = trace(true, 1.2, '\'', "foo", 42i, "foo" <= "bar") +} diff --git a/src/pkg/go/types/testdata/const0.src b/src/pkg/go/types/testdata/const0.src new file mode 100644 index 000000000..a2ca344c7 --- /dev/null +++ b/src/pkg/go/types/testdata/const0.src @@ -0,0 +1,215 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// constant declarations + +package const0 + +// constants declarations must be initialized by constants +var x = 0 +const c0 = x /* ERROR "not constant" */ + +// untyped constants +const ( + // boolean values + ub0 = false + ub1 = true + ub2 = 2 < 1 + ub3 = ui1 == uf1 + ub4 = true /* ERROR "cannot convert" */ == 0 + + // integer values + ui0 = 0 + ui1 = 1 + ui2 = 42 + ui3 = 3141592653589793238462643383279502884197169399375105820974944592307816406286 + ui4 = -10 + + ui5 = ui0 + ui1 + ui6 = ui1 - ui1 + ui7 = ui2 * ui1 + ui8 = ui3 / ui3 + ui9 = ui3 % ui3 + + ui10 = 1 / 0 /* ERROR "division by zero" */ + ui11 = ui1 / 0 /* ERROR "division by zero" */ + ui12 = ui3 / ui0 /* ERROR "division by zero" */ + ui13 = 1 % 0 /* ERROR "division by zero" */ + ui14 = ui1 % 0 /* ERROR "division by zero" */ + ui15 = ui3 % ui0 /* ERROR "division by zero" */ + + ui16 = ui2 & ui3 + ui17 = ui2 | ui3 + ui18 = ui2 ^ ui3 + + // floating point values + uf0 = 0. + uf1 = 1. + uf2 = 4.2e1 + uf3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286 + uf4 = 1e-1 + + uf5 = uf0 + uf1 + uf6 = uf1 - uf1 + uf7 = uf2 * uf1 + uf8 = uf3 / uf3 + uf9 = uf3 /* ERROR "not defined" */ % uf3 + + uf10 = 1 / 0 /* ERROR "division by zero" */ + uf11 = uf1 / 0 /* ERROR "division by zero" */ + uf12 = uf3 / uf0 /* ERROR "division by zero" */ + + uf16 = uf2 /* ERROR "not defined" */ & uf3 + uf17 = uf2 /* ERROR "not defined" */ | uf3 + uf18 = uf2 /* ERROR "not defined" */ ^ uf3 + + // complex values + uc0 = 0.i + uc1 = 1.i + uc2 = 4.2e1i + uc3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286i + uc4 = 1e-1i + + uc5 = uc0 + uc1 + uc6 = uc1 - uc1 + uc7 = uc2 * uc1 + uc8 = uc3 / uc3 + uc9 = uc3 /* ERROR "not defined" */ % uc3 + + uc10 = 1 / 0 /* ERROR "division by zero" */ + uc11 = uc1 / 0 /* ERROR "division by zero" */ + uc12 = uc3 / uc0 /* ERROR "division by zero" */ + + uc16 = uc2 /* ERROR "not defined" */ & uc3 + uc17 = uc2 /* ERROR "not defined" */ | uc3 + uc18 = uc2 /* ERROR "not defined" */ ^ uc3 +) + +type ( + mybool bool + myint int + myfloat float64 + mycomplex complex128 +) + +// typed constants +const ( + // boolean values + tb0 bool = false + tb1 bool = true + tb2 mybool = 2 < 1 + tb3 mybool = ti1 /* ERROR "cannot compare" */ == tf1 + + // integer values + ti0 int8 = ui0 + ti1 int32 = ui1 + ti2 int64 = ui2 + ti3 myint = ui3 /* ERROR "overflows" */ + ti4 myint = ui4 + + ti5 = ti0 /* ERROR "mismatched types" */ + ti1 + ti6 = ti1 - ti1 + ti7 = ti2 /* ERROR "mismatched types" */ * ti1 + //ti8 = ti3 / ti3 // TODO(gri) enable this + //ti9 = ti3 % ti3 // TODO(gri) enable this + + ti10 = 1 / 0 /* ERROR "division by zero" */ + ti11 = ti1 / 0 /* ERROR "division by zero" */ + ti12 = ti3 /* ERROR "mismatched types" */ / ti0 + ti13 = 1 % 0 /* ERROR "division by zero" */ + ti14 = ti1 % 0 /* ERROR "division by zero" */ + ti15 = ti3 /* ERROR "mismatched types" */ % ti0 + + ti16 = ti2 /* ERROR "mismatched types" */ & ti3 + ti17 = ti2 /* ERROR "mismatched types" */ | ti4 + ti18 = ti2 ^ ti5 // no mismatched types error because the type of ti5 is unknown + + // floating point values + tf0 float32 = 0. + tf1 float32 = 1. + tf2 float64 = 4.2e1 + tf3 myfloat = 3.141592653589793238462643383279502884197169399375105820974944592307816406286 + tf4 myfloat = 1e-1 + + tf5 = tf0 + tf1 + tf6 = tf1 - tf1 + tf7 = tf2 /* ERROR "mismatched types" */ * tf1 + // tf8 = tf3 / tf3 // TODO(gri) enable this + tf9 = tf3 /* ERROR "not defined" */ % tf3 + + tf10 = 1 / 0 /* ERROR "division by zero" */ + tf11 = tf1 / 0 /* ERROR "division by zero" */ + tf12 = tf3 /* ERROR "mismatched types" */ / tf0 + + tf16 = tf2 /* ERROR "mismatched types" */ & tf3 + tf17 = tf2 /* ERROR "mismatched types" */ | tf3 + tf18 = tf2 /* ERROR "mismatched types" */ ^ tf3 + + // complex values + tc0 = 0.i + tc1 = 1.i + tc2 = 4.2e1i + tc3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286i + tc4 = 1e-1i + + tc5 = tc0 + tc1 + tc6 = tc1 - tc1 + tc7 = tc2 * tc1 + tc8 = tc3 / tc3 + tc9 = tc3 /* ERROR "not defined" */ % tc3 + + tc10 = 1 / 0 /* ERROR "division by zero" */ + tc11 = tc1 / 0 /* ERROR "division by zero" */ + tc12 = tc3 / tc0 /* ERROR "division by zero" */ + + tc16 = tc2 /* ERROR "not defined" */ & tc3 + tc17 = tc2 /* ERROR "not defined" */ | tc3 + tc18 = tc2 /* ERROR "not defined" */ ^ tc3 +) + +// initialization cycles +const ( + a /* ERROR "cycle" */ = a + b /* ERROR "cycle" */ , c /* ERROR "cycle" */, d, e = e, d, c, b // TODO(gri) should only have one cycle error + f float64 = d +) + +// multiple initialization +const ( + a1, a2, a3 = 7, 3.1415926, "foo" + b1, b2, b3 = b3, b1, 42 + _p0 = assert(a1 == 7) + _p1 = assert(a2 == 3.1415926) + _p2 = assert(a3 == "foo") + _p3 = assert(b1 == 42) + _p4 = assert(b2 == 42) + _p5 = assert(b3 == 42) +) + +// iota +const ( + iota0 = iota + iota1 = iota + iota2 = iota*2 + _a0 = assert(iota0 == 0) + _a1 = assert(iota1 == 1) + _a2 = assert(iota2 == 4) + iota6 = iota*3 + + iota7 + iota8 + _a3 = assert(iota7 == 21) + _a4 = assert(iota8 == 24) +) + +const ( + _b0 = iota + _b1 = assert(iota + iota2 == 5) +) + +// special cases +const ( + _n0 = nil /* ERROR "invalid constant type" */ + _n1 = [ /* ERROR "not constant" */ ]int{} +) \ No newline at end of file diff --git a/src/pkg/go/types/testdata/conversions.src b/src/pkg/go/types/testdata/conversions.src new file mode 100644 index 000000000..1b1518366 --- /dev/null +++ b/src/pkg/go/types/testdata/conversions.src @@ -0,0 +1,18 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// conversions + +package conversions + +// argument count +var ( + _v0 = int /* ERROR "one argument" */ () + _v1 = int /* ERROR "one argument" */ (1, 2) +) + +// +var ( + _v2 = int8(0) +) \ No newline at end of file diff --git a/src/pkg/go/types/testdata/decls0.src b/src/pkg/go/types/testdata/decls0.src new file mode 100644 index 000000000..f0115bd9d --- /dev/null +++ b/src/pkg/go/types/testdata/decls0.src @@ -0,0 +1,187 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// type declarations + +package decls0 + +import ( + "unsafe" + // we can have multiple blank imports (was bug) + _ "math" + _ "net/rpc" + // reflect defines a type "flag" which shows up in the gc export data + "reflect" + . "reflect" +) + +// reflect.flag must not be visible in this package +type flag int +type _ reflect /* ERROR "cannot refer to unexported" */ .flag + +// dot-imported exported objects may conflict with local objects +type Value /* ERROR "redeclared in this block by dot-import" */ struct{} + +const pi = 3.1415 + +type ( + N undeclared /* ERROR "undeclared" */ + B bool + I int32 + A [10]P + T struct { + x, y P + } + P *T + R (*R) + F func(A) I + Y interface { + f(A) I + } + S [](((P))) + M map[I]F + C chan<- I + + // blank types must be typechecked + _ pi /* ERROR "not a type" */ + _ struct{} + _ struct{ pi /* ERROR "not a type" */ } +) + + +// invalid array types +type ( + iA0 [... /* ERROR "invalid use of '...'" */ ]byte + iA1 [1 /* ERROR "invalid array length" */ <<100]int + iA2 [- /* ERROR "invalid array length" */ 1]complex128 + iA3 ["foo" /* ERROR "invalid array length" */ ]string +) + + +type ( + p1 pi /* ERROR "no single field or method foo" */ .foo + p2 unsafe.Pointer +) + + +type ( + Pi pi /* ERROR "not a type" */ + + a /* ERROR "illegal cycle" */ a + a /* ERROR "redeclared" */ int + + // where the cycle error appears depends on the + // order in which declarations are processed + // (which depends on the order in which a map + // is iterated through) + b /* ERROR "illegal cycle" */ c + c d + d e + e b + + t *t + + U V + V *W + W U + + P1 *S2 + P2 P1 + + S0 struct { + } + S1 struct { + a, b, c int + u, v, a /* ERROR "redeclared" */ float32 + } + S2 struct { + U // anonymous field + // TODO(gri) recognize double-declaration below + // U /* ERROR "redeclared" */ int + } + S3 struct { + x S2 + } + S4/* ERROR "illegal cycle" */ struct { + S4 + } + S5 /* ERROR "illegal cycle" */ struct { + S6 + } + S6 struct { + field S7 + } + S7 struct { + S5 + } + + L1 []L1 + L2 []int + + A1 [10.0]int + A2 /* ERROR "illegal cycle" */ [10]A2 + A3 /* ERROR "illegal cycle" */ [10]struct { + x A4 + } + A4 [10]A3 + + F1 func() + F2 func(x, y, z float32) + F3 func(x, y, x /* ERROR "redeclared" */ float32) + F4 func() (x, y, x /* ERROR "redeclared" */ float32) + F5 func(x int) (x /* ERROR "redeclared" */ float32) + F6 func(x ...int) + + I1 interface{} + I2 interface { + m1() + } + I3 interface { /* ERROR "multiple methods named m1" */ + m1() + m1 /* ERROR "redeclared" */ () + } + I4 interface { + m1(x, y, x /* ERROR "redeclared" */ float32) + m2() (x, y, x /* ERROR "redeclared" */ float32) + m3(x int) (x /* ERROR "redeclared" */ float32) + } + I5 interface { + m1(I5) + } + I6 interface { + S0 /* ERROR "not an interface" */ + } + I7 interface { + I1 + I1 + } + I8 /* ERROR "illegal cycle" */ interface { + I8 + } + // Use I09 (rather than I9) because it appears lexically before + // I10 so that we get the illegal cycle here rather then in the + // declaration of I10. If the implementation sorts by position + // rather than name, the error message will still be here. + I09 /* ERROR "illegal cycle" */ interface { + I10 + } + I10 interface { + I11 + } + I11 interface { + I09 + } + + C1 chan int + C2 <-chan int + C3 chan<- C3 + C4 chan C5 + C5 chan C6 + C6 chan C4 + + M1 map[Last]string + M2 map[string]M2 + + Last int +) diff --git a/src/pkg/go/types/testdata/decls1.src b/src/pkg/go/types/testdata/decls1.src new file mode 100644 index 000000000..2251f457f --- /dev/null +++ b/src/pkg/go/types/testdata/decls1.src @@ -0,0 +1,132 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// variable declarations + +package decls1 + +import ( + "math" +) + +// Global variables without initialization +var ( + a, b bool + c byte + d uint8 + r rune + i int + j, k, l int + x, y float32 + xx, yy float64 + u, v complex64 + uu, vv complex128 + s, t string + array []byte + iface interface{} + + blank _ /* ERROR "cannot use _" */ +) + +// Global variables with initialization +var ( + s1 = i + j + s2 = i /* ERROR "mismatched types" */ + x + s3 = c + d + s4 = s + t + s5 = s /* ERROR "invalid operation" */ / t + s6 = array[t1] + s7 = array[x /* ERROR "index" */] + s8 = &a + s10 = &42 /* ERROR "cannot take address" */ + s11 = &v + s12 = -(u + *t11) / *&v + s13 = a /* ERROR "shifted operand" */ << d + s14 = i << j /* ERROR "must be unsigned" */ + s18 = math.Pi * 10.0 + s19 = s1 /* ERROR "cannot call" */ () + s20 = f0 /* ERROR "no value" */ () + s21 = f6(1, s1, i) + s22 = f6(1, s1, uu /* ERROR "cannot assign" */ ) + + t1 int = i + j + t2 int = i /* ERROR "mismatched types" */ + x + t3 int = c /* ERROR "cannot assign" */ + d + t4 string = s + t + t5 string = s /* ERROR "invalid operation" */ / t + t6 byte = array[t1] + t7 byte = array[x /* ERROR "index" */] + t8 *int = & /* ERROR "cannot assign" */ a + t10 *int = &42 /* ERROR "cannot take address" */ + t11 *complex64 = &v + t12 complex64 = -(u + *t11) / *&v + t13 int = a /* ERROR "shifted operand" */ << d + t14 int = i << j /* ERROR "must be unsigned" */ + t15 math /* ERROR "not in selector" */ + t16 math /* ERROR "unexported" */ .xxx + t17 math /* ERROR "not a type" */ .Pi + t18 float64 = math.Pi * 10.0 + t19 int = t1 /* ERROR "cannot call" */ () + t20 int = f0 /* ERROR "no value" */ () +) + +// Various more complex expressions +var ( + u1 = x /* ERROR "not an interface" */ .(int) + u2 = iface.([]int) + u3 = iface.(a /* ERROR "not a type" */ ) + u4, ok = iface.(int) + u5 /* ERROR "assignment count mismatch" */ , ok2, ok3 = iface.(int) +) + +// Constant expression initializations +var ( + v1 = 1 /* ERROR "cannot convert" */ + "foo" + v2 = c + 255 + v3 = c + 256 /* ERROR "overflows" */ + v4 = r + 2147483647 + v5 = r + 2147483648 /* ERROR "overflows" */ + v6 = 42 + v7 = v6 + 9223372036854775807 + v8 = v6 + 9223372036854775808 /* ERROR "overflows" */ + v9 = i + 1 << 10 + v10 byte = 1024 /* ERROR "overflows" */ + v11 = xx/yy*yy - xx + v12 = true && false + v13 = nil /* ERROR "use of untyped nil" */ +) + +// Multiple assignment expressions +var ( + m1a, m1b = 1, 2 + m2a /* ERROR "assignment count mismatch" */ , m2b, m2c = 1, 2 + m3a /* ERROR "assignment count mismatch" */ , m3b = 1, 2, 3 +) + +// Declaration of parameters and results +func f0() {} +func f1(a /* ERROR "not a type" */) {} +func f2(a, b, c d /* ERROR "not a type" */) {} + +func f3() int {} +func f4() a /* ERROR "not a type" */ {} +func f5() (a, b, c d /* ERROR "not a type" */) {} + +func f6(a, b, c int) complex128 { return 0 } + +// Declaration of receivers +type T struct{} + +func (T) m0() {} +func (*T) m1() {} +func (x T) m2() {} +func (x *T) m3() {} + + +// Initialization functions +func init() {} +func /* ERROR "no arguments and no return values" */ init(int) {} +func /* ERROR "no arguments and no return values" */ init() int { return 0 } +func /* ERROR "no arguments and no return values" */ init(int) int {} +func (T) init(int) int { return 0 } diff --git a/src/pkg/go/types/testdata/decls2a.src b/src/pkg/go/types/testdata/decls2a.src new file mode 100644 index 000000000..3867be737 --- /dev/null +++ b/src/pkg/go/types/testdata/decls2a.src @@ -0,0 +1,67 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// method declarations + +package decls2 + +import "time" + +// T1 declared before its methods. +type T1 struct{ + f int +} + +func (T1) m() {} +func (T1) m /* ERROR "redeclared" */ () {} +func (x *T1) f /* ERROR "field and method" */ () {} + +// T2's method declared before the type. +func (*T2) f /* ERROR "field and method" */ () {} + +type T2 struct { + f int +} + +// Methods declared without a declared type. +func (undeclared /* ERROR "undeclared" */) m() {} +func (x *undeclared /* ERROR "undeclared" */) m() {} + +// TODO(gri) try to get rid of double error reporting here +func (pi /* ERROR "not a type" */) m1() {} +func (x pi /* ERROR "not a type" */) m2() {} +func (x *pi /* ERROR "not a type" */ ) m3() {} // TODO(gri) not closing the last /* comment crashes the system + +// Blank types. +type _ struct { m int } +type _ struct { m int } + +// TODO(gri) blank idents not fully checked - disabled for now +// func (_ /* ERROR "cannot use _" */) m() {} +// func (_ /* ERROR "cannot use _" */) m() {} + +// Methods with receiver base type declared in another file. +func (T3) m1() {} +func (*T3) m2() {} +func (x T3) m3() {} +func (x *T3) f /* ERROR "field and method" */ () {} + +// Methods of non-struct type. +type T4 func() + +func (self T4) m() func() { return self } + +// Methods associated with an interface. +type T5 interface { + m() int +} + +func (T5 /* ERROR "invalid receiver" */) m1() {} +func (T5 /* ERROR "invalid receiver" */) m2() {} + +// Methods associated with non-local or unnamed types. +func (int /* ERROR "non-local type" */ ) m() {} +func ([ /* ERROR "expected" */ ]int) m() {} +func (time /* ERROR "expected" */ .Time) m() {} +func (x interface /* ERROR "expected" */ {}) m() {} diff --git a/src/pkg/go/types/testdata/decls2b.src b/src/pkg/go/types/testdata/decls2b.src new file mode 100644 index 000000000..c7f9ddf01 --- /dev/null +++ b/src/pkg/go/types/testdata/decls2b.src @@ -0,0 +1,28 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// method declarations + +package decls2 + +const pi = 3.1415 + +func (T1) m /* ERROR "redeclared" */ () {} + +type T3 struct { + f *T3 +} + +type T6 struct { + x int +} + +func (t *T6) m1() int { + return t.x +} + +func f() { + var t *T6 + t.m1() +} \ No newline at end of file diff --git a/src/pkg/go/types/testdata/decls3.src b/src/pkg/go/types/testdata/decls3.src new file mode 100644 index 000000000..6aa9f90e9 --- /dev/null +++ b/src/pkg/go/types/testdata/decls3.src @@ -0,0 +1,253 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// embedded types + +package decls3 + +// fields with the same name at the same level cancel each other out + +func _() { + type ( + T1 struct { X int } + T2 struct { X int } + T3 struct { T1; T2 } // X is embedded twice at the same level via T1->X, T2->X + ) + + var t T3 + _ = t /* ERROR "no single field or method" */ .X +} + +func _() { + type ( + T1 struct { X int } + T2 struct { T1 } + T3 struct { T1 } + T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X + ) + + var t T4 + _ = t /* ERROR "no single field or method" */ .X +} + +func issue4355() { + type ( + T1 struct {X int} + T2 struct {T1} + T3 struct {T2} + T4 struct {T2} + T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X + ) + + var t T5 + _ = t /* ERROR "no single field or method" */ .X +} + +// Embedded fields can be predeclared types. + +func _() { + type T0 struct{ + int + float32 + f int + } + var x T0 + _ = x.int + _ = x.float32 + _ = x.f + + type T1 struct{ + T0 + } + var y T1 + _ = y.int + _ = y.float32 + _ = y.f +} + +// Borrowed from the FieldByName test cases in reflect/all_test.go. + +type D1 struct { + d int +} +type D2 struct { + d int +} + +type S0 struct { + A, B, C int + D1 + D2 +} + +type S1 struct { + B int + S0 +} + +type S2 struct { + A int + *S1 +} + +type S1x struct { + S1 +} + +type S1y struct { + S1 +} + +type S3 struct { + S1x + S2 + D, E int + *S1y +} + +type S4 struct { + *S4 + A int +} + +// The X in S6 and S7 annihilate, but they also block the X in S8.S9. +type S5 struct { + S6 + S7 + S8 +} + +type S6 struct { + X int +} + +type S7 S6 + +type S8 struct { + S9 +} + +type S9 struct { + X int + Y int +} + +// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. +type S10 struct { + S11 + S12 + S13 +} + +type S11 struct { + S6 +} + +type S12 struct { + S6 +} + +type S13 struct { + S8 +} + +func _() { + _ = struct /* ERROR "no single field or method" */ {}{}.Foo + _ = S0{}.A + _ = S0 /* ERROR "no single field or method" */ {}.D + _ = S1{}.A + _ = S1{}.B + _ = S1{}.S0 + _ = S1{}.C + _ = S2{}.A + _ = S2{}.S1 + _ = S2{}.B + _ = S2{}.C + _ = S2 /* ERROR "no single field or method" */ {}.D + _ = S3 /* ERROR "no single field or method" */ {}.S1 + _ = S3{}.A + _ = S3 /* ERROR "no single field or method" */ {}.B + _ = S3{}.D + _ = S3{}.E + _ = S4{}.A + _ = S4 /* ERROR "no single field or method" */ {}.B + _ = S5 /* ERROR "no single field or method" */ {}.X + _ = S5{}.Y + _ = S10 /* ERROR "no single field or method" */ {}.X + _ = S10{}.Y +} + +// Borrowed from the FieldByName benchmark in reflect/all_test.go. + +type R0 struct { + *R1 + *R2 + *R3 + *R4 +} + +type R1 struct { + *R5 + *R6 + *R7 + *R8 +} + +type R2 R1 +type R3 R1 +type R4 R1 + +type R5 struct { + *R9 + *R10 + *R11 + *R12 +} + +type R6 R5 +type R7 R5 +type R8 R5 + +type R9 struct { + *R13 + *R14 + *R15 + *R16 +} + +type R10 R9 +type R11 R9 +type R12 R9 + +type R13 struct { + *R17 + *R18 + *R19 + *R20 +} + +type R14 R13 +type R15 R13 +type R16 R13 + +type R17 struct { + *R21 + *R22 + *R23 + *R24 +} + +type R18 R17 +type R19 R17 +type R20 R17 + +type R21 struct { + X int +} + +type R22 R21 +type R23 R21 +type R24 R21 + +var _ = R0 /* ERROR "no single field or method" */ {}.X \ No newline at end of file diff --git a/src/pkg/go/types/testdata/exports.go b/src/pkg/go/types/testdata/exports.go new file mode 100644 index 000000000..8ee28b094 --- /dev/null +++ b/src/pkg/go/types/testdata/exports.go @@ -0,0 +1,89 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is used to generate an object file which +// serves as test file for gcimporter_test.go. + +package exports + +import ( + "go/ast" +) + +// Issue 3682: Correctly read dotted identifiers from export data. +const init1 = 0 + +func init() {} + +const ( + C0 int = 0 + C1 = 3.14159265 + C2 = 2.718281828i + C3 = -123.456e-789 + C4 = +123.456E+789 + C5 = 1234i + C6 = "foo\n" + C7 = `bar\n` +) + +type ( + T1 int + T2 [10]int + T3 []int + T4 *int + T5 chan int + T6a chan<- int + T6b chan (<-chan int) + T6c chan<- (chan int) + T7 <-chan *ast.File + T8 struct{} + T9 struct { + a int + b, c float32 + d []string `go:"tag"` + } + T10 struct { + T8 + T9 + _ *T10 + } + T11 map[int]string + T12 interface{} + T13 interface { + m1() + m2(int) float32 + } + T14 interface { + T12 + T13 + m3(x ...struct{}) []T9 + } + T15 func() + T16 func(int) + T17 func(x int) + T18 func() float32 + T19 func() (x float32) + T20 func(...interface{}) + T21 struct{ next *T21 } + T22 struct{ link *T23 } + T23 struct{ link *T22 } + T24 *T24 + T25 *T26 + T26 *T27 + T27 *T25 + T28 func(T28) T28 +) + +var ( + V0 int + V1 = -991.0 +) + +func F1() {} +func F2(x int) {} +func F3() int { return 0 } +func F4() float32 { return 0 } +func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10) + +func (p *T1) M1() diff --git a/src/pkg/go/types/testdata/expr0.src b/src/pkg/go/types/testdata/expr0.src new file mode 100644 index 000000000..8d057f63c --- /dev/null +++ b/src/pkg/go/types/testdata/expr0.src @@ -0,0 +1,161 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// unary expressions + +package expr0 + +var ( + // bool + b0 = true + b1 bool = b0 + b2 = !true + b3 = !b1 + b4 bool = !true + b5 bool = !b4 + b6 = +b0 /* ERROR "not defined" */ + b7 = -b0 /* ERROR "not defined" */ + b8 = ^b0 /* ERROR "not defined" */ + b9 = *b0 /* ERROR "cannot indirect" */ + b10 = &true /* ERROR "cannot take address" */ + b11 = &b0 + b12 = <-b0 /* ERROR "cannot receive" */ + + // int + i0 = 1 + i1 int = i0 + i2 = +1 + i3 = +i0 + i4 int = +1 + i5 int = +i4 + i6 = -1 + i7 = -i0 + i8 int = -1 + i9 int = -i4 + i10 = !i0 /* ERROR "not defined" */ + i11 = ^1 + i12 = ^i0 + i13 int = ^1 + i14 int = ^i4 + i15 = *i0 /* ERROR "cannot indirect" */ + i16 = &i0 + i17 = *i16 + i18 = <-i16 /* ERROR "cannot receive" */ + + // uint + u0 = uint(1) + u1 uint = u0 + u2 = +1 + u3 = +u0 + u4 uint = +1 + u5 uint = +u4 + u6 = -1 + u7 = -u0 + u8 uint = - /* ERROR "overflows" */ 1 + u9 uint = -u4 + u10 = !u0 /* ERROR "not defined" */ + u11 = ^1 + u12 = ^i0 + u13 uint = ^ /* ERROR "overflows" */ 1 + u14 uint = ^u4 + u15 = *u0 /* ERROR "cannot indirect" */ + u16 = &u0 + u17 = *u16 + u18 = <-u16 /* ERROR "cannot receive" */ + u19 = ^uint(0) + + // float64 + f0 = float64(1) + f1 float64 = f0 + f2 = +1 + f3 = +f0 + f4 float64 = +1 + f5 float64 = +f4 /* ERROR not defined */ + f6 = -1 + f7 = -f0 + f8 float64 = -1 + f9 float64 = -f4 + f10 = !f0 /* ERROR "not defined" */ + f11 = ^1 + f12 = ^i0 + f13 float64 = ^1 + f14 float64 = ^f4 /* ERROR "not defined" */ + f15 = *f0 /* ERROR "cannot indirect" */ + f16 = &f0 + f17 = *u16 + f18 = <-u16 /* ERROR "cannot receive" */ + + // complex128 + c0 = complex128(1) + c1 complex128 = c0 + c2 = +1 + c3 = +c0 + c4 complex128 = +1 + c5 complex128 = +c4 /* ERROR not defined */ + c6 = -1 + c7 = -c0 + c8 complex128 = -1 + c9 complex128 = -c4 + c10 = !c0 /* ERROR "not defined" */ + c11 = ^1 + c12 = ^i0 + c13 complex128 = ^1 + c14 complex128 = ^c4 /* ERROR "not defined" */ + c15 = *c0 /* ERROR "cannot indirect" */ + c16 = &c0 + c17 = *u16 + c18 = <-u16 /* ERROR "cannot receive" */ + + // string + s0 = "foo" + s1 = +"foo" /* ERROR "not defined" */ + s2 = -s0 /* ERROR "not defined" */ + s3 = !s0 /* ERROR "not defined" */ + s4 = ^s0 /* ERROR "not defined" */ + s5 = *s4 /* ERROR "cannot indirect" */ + s6 = &s4 + s7 = *s6 + s8 = <-s7 /* ERROR "cannot receive" */ + + // channel + ch chan int + rc <-chan float64 + sc chan <- string + ch0 = +ch /* ERROR "not defined" */ + ch1 = -ch /* ERROR "not defined" */ + ch2 = !ch /* ERROR "not defined" */ + ch3 = ^ch /* ERROR "not defined" */ + ch4 = *ch /* ERROR "cannot indirect" */ + ch5 = &ch + ch6 = *ch5 + ch7 = <-ch + ch8 = <-rc + ch9 = <-sc /* ERROR "cannot receive" */ +) + +// address of composite literals +type T struct{x, y int} + +func f() T { return T{} } + +var ( + _ = &T{1, 2} + _ = &[...]int{} + _ = &[]int{} + _ = &[]int{} + _ = &map[string]T{} + _ = &(T{1, 2}) + _ = &((((T{1, 2})))) + _ = &f /* ERROR "cannot take address" */ () +) + +// recursive pointer types +type P *P + +var ( + p1 P = new(P) + p2 P = *p1 + p3 P = &p2 +) + diff --git a/src/pkg/go/types/testdata/expr1.src b/src/pkg/go/types/testdata/expr1.src new file mode 100644 index 000000000..8ef0aed6d --- /dev/null +++ b/src/pkg/go/types/testdata/expr1.src @@ -0,0 +1,7 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// binary expressions + +package expr1 diff --git a/src/pkg/go/types/testdata/expr2.src b/src/pkg/go/types/testdata/expr2.src new file mode 100644 index 000000000..674be4005 --- /dev/null +++ b/src/pkg/go/types/testdata/expr2.src @@ -0,0 +1,23 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// comparisons + +package expr2 + +func _bool() { + const t = true == true + const f = true == false + _ = t /* ERROR "cannot compare" */ < f + _ = 0 /* ERROR "cannot convert" */ == t + var b bool + var x, y float32 + b = x < y + _ = struct{b bool}{x < y} +} + +// corner cases +var ( + v0 = nil /* ERROR "cannot compare" */ == nil +) \ No newline at end of file diff --git a/src/pkg/go/types/testdata/expr3.src b/src/pkg/go/types/testdata/expr3.src new file mode 100644 index 000000000..ff17f2eee --- /dev/null +++ b/src/pkg/go/types/testdata/expr3.src @@ -0,0 +1,463 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// various expressions + +package expr3 + +func shifts1() { + var ( + i0 int + u0 uint + ) + + var ( + v0 = 1<<0 + v1 = 1<> s) + // TODO(gri) add more tests (systematically) +} + +func shifts4() { + // from src/pkg/compress/lzw/reader.go:90 + { + var d struct { + bits uint32 + width uint + } + _ = uint16(d.bits & (1<= 0 { + return // nothing to do + } + // fix Obj link for named types + if typ, ok := obj.GetType().(*NamedType); ok { + typ.Obj = obj.(*TypeName) + } + // exported identifiers go into package unsafe + scope := Universe + if ast.IsExported(name) { + scope = Unsafe.Scope + // set Pkg field + switch obj := obj.(type) { + case *TypeName: + obj.Pkg = Unsafe + case *Func: + obj.Pkg = Unsafe + default: + unreachable() + } + } + if scope.Insert(obj) != nil { + panic("internal error: double declaration") + } +} diff --git a/src/pkg/hash/adler32/adler32.go b/src/pkg/hash/adler32/adler32.go index 64fe68c44..7c80796bf 100644 --- a/src/pkg/hash/adler32/adler32.go +++ b/src/pkg/hash/adler32/adler32.go @@ -3,7 +3,8 @@ // license that can be found in the LICENSE file. // Package adler32 implements the Adler-32 checksum. -// Defined in RFC 1950: +// +// It is defined in RFC 1950: // Adler-32 is composed of two sums accumulated per byte: s1 is // the sum of all bytes, s2 is the sum of all s1 values. Both sums // are done modulo 65521. s1 is initialized to 1, s2 to zero. The @@ -14,20 +15,22 @@ package adler32 import "hash" const ( + // mod is the largest prime that is less than 65536. mod = 65521 + // nmax is the largest n such that + // 255 * n * (n+1) / 2 + (n+1) * (mod-1) <= 2^32-1. + // It is mentioned in RFC 1950 (search for "5552"). + nmax = 5552 ) // The size of an Adler-32 checksum in bytes. const Size = 4 // digest represents the partial evaluation of a checksum. -type digest struct { - // invariant: (a < mod && b < mod) || a <= b - // invariant: a + b + 255 <= 0xffffffff - a, b uint32 -} +// The low 16 bits are s1, the high 16 bits are s2. +type digest uint32 -func (d *digest) Reset() { d.a, d.b = 1, 0 } +func (d *digest) Reset() { *d = 1 } // New returns a new hash.Hash32 computing the Adler-32 checksum. func New() hash.Hash32 { @@ -40,47 +43,36 @@ func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return 1 } -// Add p to the running checksum a, b. -func update(a, b uint32, p []byte) (aa, bb uint32) { - for _, pi := range p { - a += uint32(pi) - b += a - // invariant: a <= b - if b > (0xffffffff-255)/2 { - a %= mod - b %= mod - // invariant: a < mod && b < mod - } else { - // invariant: a + b + 255 <= 2 * b + 255 <= 0xffffffff +// Add p to the running checksum d. +func update(d digest, p []byte) digest { + s1, s2 := uint32(d&0xffff), uint32(d>>16) + for len(p) > 0 { + var q []byte + if len(p) > nmax { + p, q = p[:nmax], p[nmax:] } + for _, x := range p { + s1 += uint32(x) + s2 += s1 + } + s1 %= mod + s2 %= mod + p = q } - return a, b -} - -// Return the 32-bit checksum corresponding to a, b. -func finish(a, b uint32) uint32 { - if b >= mod { - a %= mod - b %= mod - } - return b<<16 | a + return digest(s2<<16 | s1) } func (d *digest) Write(p []byte) (nn int, err error) { - d.a, d.b = update(d.a, d.b, p) + *d = update(*d, p) return len(p), nil } -func (d *digest) Sum32() uint32 { return finish(d.a, d.b) } +func (d *digest) Sum32() uint32 { return uint32(*d) } func (d *digest) Sum(in []byte) []byte { - s := d.Sum32() - in = append(in, byte(s>>24)) - in = append(in, byte(s>>16)) - in = append(in, byte(s>>8)) - in = append(in, byte(s)) - return in + s := uint32(*d) + return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) } // Checksum returns the Adler-32 checksum of data. -func Checksum(data []byte) uint32 { return finish(update(1, 0, data)) } +func Checksum(data []byte) uint32 { return uint32(update(1, data)) } diff --git a/src/pkg/hash/adler32/adler32_test.go b/src/pkg/hash/adler32/adler32_test.go index 01f931c68..0e9c938d8 100644 --- a/src/pkg/hash/adler32/adler32_test.go +++ b/src/pkg/hash/adler32/adler32_test.go @@ -5,26 +5,23 @@ package adler32 import ( - "bytes" - "io" + "strings" "testing" ) -type _Adler32Test struct { +var golden = []struct { out uint32 in string -} - -var golden = []_Adler32Test{ - {0x1, ""}, - {0x620062, "a"}, - {0x12600c4, "ab"}, - {0x24d0127, "abc"}, - {0x3d8018b, "abcd"}, - {0x5c801f0, "abcde"}, - {0x81e0256, "abcdef"}, - {0xadb02bd, "abcdefg"}, - {0xe000325, "abcdefgh"}, +}{ + {0x00000001, ""}, + {0x00620062, "a"}, + {0x012600c4, "ab"}, + {0x024d0127, "abc"}, + {0x03d8018b, "abcd"}, + {0x05c801f0, "abcde"}, + {0x081e0256, "abcdef"}, + {0x0adb02bd, "abcdefg"}, + {0x0e000325, "abcdefgh"}, {0x118e038e, "abcdefghi"}, {0x158603f8, "abcdefghij"}, {0x3f090f02, "Discard medicine more than two years old."}, @@ -48,30 +45,61 @@ var golden = []_Adler32Test{ {0x91dd304f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, {0x2e5d1316, "How can you write a big system without C++? -Paul Glick"}, {0xd0201df6, "'Invariant assertions' is the most elegant programming technique! -Tom Szymanski"}, + {0x211297c8, strings.Repeat("\xff", 5548) + "8"}, + {0xbaa198c8, strings.Repeat("\xff", 5549) + "9"}, + {0x553499be, strings.Repeat("\xff", 5550) + "0"}, + {0xf0c19abe, strings.Repeat("\xff", 5551) + "1"}, + {0x8d5c9bbe, strings.Repeat("\xff", 5552) + "2"}, + {0x2af69cbe, strings.Repeat("\xff", 5553) + "3"}, + {0xc9809dbe, strings.Repeat("\xff", 5554) + "4"}, + {0x69189ebe, strings.Repeat("\xff", 5555) + "5"}, + {0x86af0001, strings.Repeat("\x00", 1e5)}, + {0x79660b4d, strings.Repeat("a", 1e5)}, + {0x110588ee, strings.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1e4)}, +} + +// checksum is a slow but simple implementation of the Adler-32 checksum. +// It is a straight port of the sample code in RFC 1950 section 9. +func checksum(p []byte) uint32 { + s1, s2 := uint32(1), uint32(0) + for _, x := range p { + s1 = (s1 + uint32(x)) % mod + s2 = (s2 + s1) % mod + } + return s2<<16 | s1 } func TestGolden(t *testing.T) { - for i := 0; i < len(golden); i++ { - g := golden[i] - c := New() - io.WriteString(c, g.in) - s := c.Sum32() - if s != g.out { - t.Errorf("adler32(%s) = 0x%x want 0x%x", g.in, s, g.out) - t.FailNow() + for _, g := range golden { + in := g.in + if len(in) > 220 { + in = in[:100] + "..." + in[len(in)-100:] + } + p := []byte(g.in) + if got := checksum(p); got != g.out { + t.Errorf("simple implementation: checksum(%q) = 0x%x want 0x%x", in, got, g.out) + continue + } + if got := Checksum(p); got != g.out { + t.Errorf("optimized implementation: Checksum(%q) = 0x%x want 0x%x", in, got, g.out) + continue } } } -func BenchmarkGolden(b *testing.B) { - b.StopTimer() - c := New() - var buf bytes.Buffer - for _, g := range golden { - buf.Write([]byte(g.in)) +func BenchmarkAdler32KB(b *testing.B) { + b.SetBytes(1024) + data := make([]byte, 1024) + for i := range data { + data[i] = byte(i) } - b.StartTimer() + h := New() + in := make([]byte, 0, h.Size()) + + b.ResetTimer() for i := 0; i < b.N; i++ { - c.Write(buf.Bytes()) + h.Reset() + h.Write(data) + h.Sum(in) } } diff --git a/src/pkg/hash/crc32/crc32.go b/src/pkg/hash/crc32/crc32.go index 236d77872..a2a21a06f 100644 --- a/src/pkg/hash/crc32/crc32.go +++ b/src/pkg/hash/crc32/crc32.go @@ -123,11 +123,7 @@ func (d *digest) Sum32() uint32 { return d.crc } func (d *digest) Sum(in []byte) []byte { s := d.Sum32() - in = append(in, byte(s>>24)) - in = append(in, byte(s>>16)) - in = append(in, byte(s>>8)) - in = append(in, byte(s)) - return in + return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) } // Checksum returns the CRC-32 checksum of data diff --git a/src/pkg/hash/crc32/crc32_amd64.go b/src/pkg/hash/crc32/crc32_amd64.go index 83349bc6c..b5bc6d3cf 100644 --- a/src/pkg/hash/crc32/crc32_amd64.go +++ b/src/pkg/hash/crc32/crc32_amd64.go @@ -13,7 +13,7 @@ func haveSSE42() bool // castagnoliSSE42 is defined in crc_amd64.s and uses the SSE4.2 CRC32 // instruction. -func castagnoliSSE42(uint32, []byte) uint32 +func castagnoliSSE42(crc uint32, p []byte) uint32 var sse42 = haveSSE42() diff --git a/src/pkg/hash/crc32/crc32_amd64.s b/src/pkg/hash/crc32/crc32_amd64.s index a9e5317e1..6e6a364ee 100644 --- a/src/pkg/hash/crc32/crc32_amd64.s +++ b/src/pkg/hash/crc32/crc32_amd64.s @@ -6,12 +6,12 @@ TEXT ·castagnoliSSE42(SB),7,$0 MOVL crc+0(FP), AX // CRC value MOVQ p+8(FP), SI // data pointer - MOVL p+16(FP), CX // len(p) + MOVQ p+16(FP), CX // len(p) NOTL AX /* If there's less than 8 bytes to process, we do it byte-by-byte. */ - CMPL CX, $8 + CMPQ CX, $8 JL cleanup /* Process individual bytes until the input is 8-byte aligned. */ @@ -21,13 +21,13 @@ startup: JZ aligned CRC32B (SI), AX - DECL CX + DECQ CX INCQ SI JMP startup aligned: /* The input is now 8-byte aligned and we can process 8-byte chunks. */ - CMPL CX, $8 + CMPQ CX, $8 JL cleanup CRC32Q (SI), AX @@ -37,7 +37,7 @@ aligned: cleanup: /* We may have some bytes left over that we process one at a time. */ - CMPL CX, $0 + CMPQ CX, $0 JE done CRC32B (SI), AX @@ -47,7 +47,7 @@ cleanup: done: NOTL AX - MOVL AX, ret+24(FP) + MOVL AX, r+32(FP) RET // func haveSSE42() bool @@ -57,6 +57,6 @@ TEXT ·haveSSE42(SB),7,$0 CPUID SHRQ $20, CX ANDQ $1, CX - MOVB CX, ret+0(FP) + MOVB CX, r+0(FP) RET diff --git a/src/pkg/hash/crc32/crc32_test.go b/src/pkg/hash/crc32/crc32_test.go index 7e82dd755..75dc26e7c 100644 --- a/src/pkg/hash/crc32/crc32_test.go +++ b/src/pkg/hash/crc32/crc32_test.go @@ -82,16 +82,18 @@ func TestGolden(t *testing.T) { } func BenchmarkCrc32KB(b *testing.B) { - b.StopTimer() - data := make([]uint8, 1024) - for i := 0; i < 1024; i++ { - data[i] = uint8(i) + b.SetBytes(1024) + data := make([]byte, 1024) + for i := range data { + data[i] = byte(i) } - c := NewIEEE() - b.StartTimer() - b.SetBytes(int64(len(data))) + h := NewIEEE() + in := make([]byte, 0, h.Size()) + b.ResetTimer() for i := 0; i < b.N; i++ { - c.Write(data) + h.Reset() + h.Write(data) + h.Sum(in) } } diff --git a/src/pkg/hash/crc64/crc64.go b/src/pkg/hash/crc64/crc64.go index 5b64390f3..692586798 100644 --- a/src/pkg/hash/crc64/crc64.go +++ b/src/pkg/hash/crc64/crc64.go @@ -79,15 +79,7 @@ func (d *digest) Sum64() uint64 { return d.crc } func (d *digest) Sum(in []byte) []byte { s := d.Sum64() - in = append(in, byte(s>>56)) - in = append(in, byte(s>>48)) - in = append(in, byte(s>>40)) - in = append(in, byte(s>>32)) - in = append(in, byte(s>>24)) - in = append(in, byte(s>>16)) - in = append(in, byte(s>>8)) - in = append(in, byte(s)) - return in + return append(in, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) } // Checksum returns the CRC-64 checksum of data diff --git a/src/pkg/hash/crc64/crc64_test.go b/src/pkg/hash/crc64/crc64_test.go index e932524e0..81a87b56e 100644 --- a/src/pkg/hash/crc64/crc64_test.go +++ b/src/pkg/hash/crc64/crc64_test.go @@ -64,15 +64,18 @@ func TestGolden(t *testing.T) { } func BenchmarkCrc64KB(b *testing.B) { - b.StopTimer() - data := make([]uint8, 1024) - for i := 0; i < 1024; i++ { - data[i] = uint8(i) + b.SetBytes(1024) + data := make([]byte, 1024) + for i := range data { + data[i] = byte(i) } - c := New(tab) - b.StartTimer() + h := New(tab) + in := make([]byte, 0, h.Size()) + b.ResetTimer() for i := 0; i < b.N; i++ { - c.Write(data) + h.Reset() + h.Write(data) + h.Sum(in) } } diff --git a/src/pkg/hash/fnv/fnv.go b/src/pkg/hash/fnv/fnv.go index ea5019818..b5ecd4a7c 100644 --- a/src/pkg/hash/fnv/fnv.go +++ b/src/pkg/hash/fnv/fnv.go @@ -111,44 +111,20 @@ func (s *sum64a) BlockSize() int { return 1 } func (s *sum32) Sum(in []byte) []byte { v := uint32(*s) - in = append(in, byte(v>>24)) - in = append(in, byte(v>>16)) - in = append(in, byte(v>>8)) - in = append(in, byte(v)) - return in + return append(in, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) } func (s *sum32a) Sum(in []byte) []byte { v := uint32(*s) - in = append(in, byte(v>>24)) - in = append(in, byte(v>>16)) - in = append(in, byte(v>>8)) - in = append(in, byte(v)) - return in + return append(in, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) } func (s *sum64) Sum(in []byte) []byte { v := uint64(*s) - in = append(in, byte(v>>56)) - in = append(in, byte(v>>48)) - in = append(in, byte(v>>40)) - in = append(in, byte(v>>32)) - in = append(in, byte(v>>24)) - in = append(in, byte(v>>16)) - in = append(in, byte(v>>8)) - in = append(in, byte(v)) - return in + return append(in, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) } func (s *sum64a) Sum(in []byte) []byte { v := uint64(*s) - in = append(in, byte(v>>56)) - in = append(in, byte(v>>48)) - in = append(in, byte(v>>40)) - in = append(in, byte(v>>32)) - in = append(in, byte(v>>24)) - in = append(in, byte(v>>16)) - in = append(in, byte(v>>8)) - in = append(in, byte(v)) - return in + return append(in, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) } diff --git a/src/pkg/hash/fnv/fnv_test.go b/src/pkg/hash/fnv/fnv_test.go index 17454deda..89d39b38a 100644 --- a/src/pkg/hash/fnv/fnv_test.go +++ b/src/pkg/hash/fnv/fnv_test.go @@ -11,8 +11,6 @@ import ( "testing" ) -const testDataSize = 40 - type golden struct { sum []byte text string @@ -134,34 +132,34 @@ func testIntegrity(t *testing.T, h hash.Hash) { } } -func Benchmark32(b *testing.B) { - benchmark(b, New32()) +func BenchmarkFnv32KB(b *testing.B) { + benchmarkKB(b, New32()) } -func Benchmark32a(b *testing.B) { - benchmark(b, New32a()) +func BenchmarkFnv32aKB(b *testing.B) { + benchmarkKB(b, New32a()) } -func Benchmark64(b *testing.B) { - benchmark(b, New64()) +func BenchmarkFnv64KB(b *testing.B) { + benchmarkKB(b, New64()) } -func Benchmark64a(b *testing.B) { - benchmark(b, New64a()) +func BenchmarkFnv64aKB(b *testing.B) { + benchmarkKB(b, New64a()) } -func benchmark(b *testing.B, h hash.Hash) { - b.ResetTimer() - b.SetBytes(testDataSize) - data := make([]byte, testDataSize) +func benchmarkKB(b *testing.B, h hash.Hash) { + b.SetBytes(1024) + data := make([]byte, 1024) for i := range data { - data[i] = byte(i + 'a') + data[i] = byte(i) } + in := make([]byte, 0, h.Size()) - b.StartTimer() - for todo := b.N; todo != 0; todo-- { + b.ResetTimer() + for i := 0; i < b.N; i++ { h.Reset() h.Write(data) - h.Sum(nil) + h.Sum(in) } } diff --git a/src/pkg/html/entity.go b/src/pkg/html/entity.go index bd8307523..af8a007ed 100644 --- a/src/pkg/html/entity.go +++ b/src/pkg/html/entity.go @@ -75,2083 +75,2083 @@ var entity = map[string]rune{ "Copf;": '\U00002102', "Coproduct;": '\U00002210', "CounterClockwiseContourIntegral;": '\U00002233', - "Cross;": '\U00002A2F', - "Cscr;": '\U0001D49E', - "Cup;": '\U000022D3', - "CupCap;": '\U0000224D', - "DD;": '\U00002145', - "DDotrahd;": '\U00002911', - "DJcy;": '\U00000402', - "DScy;": '\U00000405', - "DZcy;": '\U0000040F', - "Dagger;": '\U00002021', - "Darr;": '\U000021A1', - "Dashv;": '\U00002AE4', - "Dcaron;": '\U0000010E', - "Dcy;": '\U00000414', - "Del;": '\U00002207', - "Delta;": '\U00000394', - "Dfr;": '\U0001D507', - "DiacriticalAcute;": '\U000000B4', - "DiacriticalDot;": '\U000002D9', - "DiacriticalDoubleAcute;": '\U000002DD', - "DiacriticalGrave;": '\U00000060', - "DiacriticalTilde;": '\U000002DC', - "Diamond;": '\U000022C4', - "DifferentialD;": '\U00002146', - "Dopf;": '\U0001D53B', - "Dot;": '\U000000A8', - "DotDot;": '\U000020DC', - "DotEqual;": '\U00002250', - "DoubleContourIntegral;": '\U0000222F', - "DoubleDot;": '\U000000A8', - "DoubleDownArrow;": '\U000021D3', - "DoubleLeftArrow;": '\U000021D0', - "DoubleLeftRightArrow;": '\U000021D4', - "DoubleLeftTee;": '\U00002AE4', - "DoubleLongLeftArrow;": '\U000027F8', - "DoubleLongLeftRightArrow;": '\U000027FA', - "DoubleLongRightArrow;": '\U000027F9', - "DoubleRightArrow;": '\U000021D2', - "DoubleRightTee;": '\U000022A8', - "DoubleUpArrow;": '\U000021D1', - "DoubleUpDownArrow;": '\U000021D5', - "DoubleVerticalBar;": '\U00002225', - "DownArrow;": '\U00002193', - "DownArrowBar;": '\U00002913', - "DownArrowUpArrow;": '\U000021F5', - "DownBreve;": '\U00000311', - "DownLeftRightVector;": '\U00002950', - "DownLeftTeeVector;": '\U0000295E', - "DownLeftVector;": '\U000021BD', - "DownLeftVectorBar;": '\U00002956', - "DownRightTeeVector;": '\U0000295F', - "DownRightVector;": '\U000021C1', - "DownRightVectorBar;": '\U00002957', - "DownTee;": '\U000022A4', - "DownTeeArrow;": '\U000021A7', - "Downarrow;": '\U000021D3', - "Dscr;": '\U0001D49F', - "Dstrok;": '\U00000110', - "ENG;": '\U0000014A', - "ETH;": '\U000000D0', - "Eacute;": '\U000000C9', - "Ecaron;": '\U0000011A', - "Ecirc;": '\U000000CA', - "Ecy;": '\U0000042D', - "Edot;": '\U00000116', - "Efr;": '\U0001D508', - "Egrave;": '\U000000C8', - "Element;": '\U00002208', - "Emacr;": '\U00000112', - "EmptySmallSquare;": '\U000025FB', - "EmptyVerySmallSquare;": '\U000025AB', - "Eogon;": '\U00000118', - "Eopf;": '\U0001D53C', - "Epsilon;": '\U00000395', - "Equal;": '\U00002A75', - "EqualTilde;": '\U00002242', - "Equilibrium;": '\U000021CC', - "Escr;": '\U00002130', - "Esim;": '\U00002A73', - "Eta;": '\U00000397', - "Euml;": '\U000000CB', - "Exists;": '\U00002203', - "ExponentialE;": '\U00002147', - "Fcy;": '\U00000424', - "Ffr;": '\U0001D509', - "FilledSmallSquare;": '\U000025FC', - "FilledVerySmallSquare;": '\U000025AA', - "Fopf;": '\U0001D53D', - "ForAll;": '\U00002200', - "Fouriertrf;": '\U00002131', - "Fscr;": '\U00002131', - "GJcy;": '\U00000403', - "GT;": '\U0000003E', - "Gamma;": '\U00000393', - "Gammad;": '\U000003DC', - "Gbreve;": '\U0000011E', - "Gcedil;": '\U00000122', - "Gcirc;": '\U0000011C', - "Gcy;": '\U00000413', - "Gdot;": '\U00000120', - "Gfr;": '\U0001D50A', - "Gg;": '\U000022D9', - "Gopf;": '\U0001D53E', - "GreaterEqual;": '\U00002265', - "GreaterEqualLess;": '\U000022DB', - "GreaterFullEqual;": '\U00002267', - "GreaterGreater;": '\U00002AA2', - "GreaterLess;": '\U00002277', - "GreaterSlantEqual;": '\U00002A7E', - "GreaterTilde;": '\U00002273', - "Gscr;": '\U0001D4A2', - "Gt;": '\U0000226B', - "HARDcy;": '\U0000042A', - "Hacek;": '\U000002C7', - "Hat;": '\U0000005E', - "Hcirc;": '\U00000124', - "Hfr;": '\U0000210C', - "HilbertSpace;": '\U0000210B', - "Hopf;": '\U0000210D', - "HorizontalLine;": '\U00002500', - "Hscr;": '\U0000210B', - "Hstrok;": '\U00000126', - "HumpDownHump;": '\U0000224E', - "HumpEqual;": '\U0000224F', - "IEcy;": '\U00000415', - "IJlig;": '\U00000132', - "IOcy;": '\U00000401', - "Iacute;": '\U000000CD', - "Icirc;": '\U000000CE', - "Icy;": '\U00000418', - "Idot;": '\U00000130', - "Ifr;": '\U00002111', - "Igrave;": '\U000000CC', - "Im;": '\U00002111', - "Imacr;": '\U0000012A', - "ImaginaryI;": '\U00002148', - "Implies;": '\U000021D2', - "Int;": '\U0000222C', - "Integral;": '\U0000222B', - "Intersection;": '\U000022C2', - "InvisibleComma;": '\U00002063', - "InvisibleTimes;": '\U00002062', - "Iogon;": '\U0000012E', - "Iopf;": '\U0001D540', - "Iota;": '\U00000399', - "Iscr;": '\U00002110', - "Itilde;": '\U00000128', - "Iukcy;": '\U00000406', - "Iuml;": '\U000000CF', - "Jcirc;": '\U00000134', - "Jcy;": '\U00000419', - "Jfr;": '\U0001D50D', - "Jopf;": '\U0001D541', - "Jscr;": '\U0001D4A5', - "Jsercy;": '\U00000408', - "Jukcy;": '\U00000404', - "KHcy;": '\U00000425', - "KJcy;": '\U0000040C', - "Kappa;": '\U0000039A', - "Kcedil;": '\U00000136', - "Kcy;": '\U0000041A', - "Kfr;": '\U0001D50E', - "Kopf;": '\U0001D542', - "Kscr;": '\U0001D4A6', - "LJcy;": '\U00000409', - "LT;": '\U0000003C', - "Lacute;": '\U00000139', - "Lambda;": '\U0000039B', - "Lang;": '\U000027EA', - "Laplacetrf;": '\U00002112', - "Larr;": '\U0000219E', - "Lcaron;": '\U0000013D', - "Lcedil;": '\U0000013B', - "Lcy;": '\U0000041B', - "LeftAngleBracket;": '\U000027E8', - "LeftArrow;": '\U00002190', - "LeftArrowBar;": '\U000021E4', - "LeftArrowRightArrow;": '\U000021C6', - "LeftCeiling;": '\U00002308', - "LeftDoubleBracket;": '\U000027E6', - "LeftDownTeeVector;": '\U00002961', - "LeftDownVector;": '\U000021C3', - "LeftDownVectorBar;": '\U00002959', - "LeftFloor;": '\U0000230A', - "LeftRightArrow;": '\U00002194', - "LeftRightVector;": '\U0000294E', - "LeftTee;": '\U000022A3', - "LeftTeeArrow;": '\U000021A4', - "LeftTeeVector;": '\U0000295A', - "LeftTriangle;": '\U000022B2', - "LeftTriangleBar;": '\U000029CF', - "LeftTriangleEqual;": '\U000022B4', - "LeftUpDownVector;": '\U00002951', - "LeftUpTeeVector;": '\U00002960', - "LeftUpVector;": '\U000021BF', - "LeftUpVectorBar;": '\U00002958', - "LeftVector;": '\U000021BC', - "LeftVectorBar;": '\U00002952', - "Leftarrow;": '\U000021D0', - "Leftrightarrow;": '\U000021D4', - "LessEqualGreater;": '\U000022DA', - "LessFullEqual;": '\U00002266', - "LessGreater;": '\U00002276', - "LessLess;": '\U00002AA1', - "LessSlantEqual;": '\U00002A7D', - "LessTilde;": '\U00002272', - "Lfr;": '\U0001D50F', - "Ll;": '\U000022D8', - "Lleftarrow;": '\U000021DA', - "Lmidot;": '\U0000013F', - "LongLeftArrow;": '\U000027F5', - "LongLeftRightArrow;": '\U000027F7', - "LongRightArrow;": '\U000027F6', - "Longleftarrow;": '\U000027F8', - "Longleftrightarrow;": '\U000027FA', - "Longrightarrow;": '\U000027F9', - "Lopf;": '\U0001D543', - "LowerLeftArrow;": '\U00002199', - "LowerRightArrow;": '\U00002198', - "Lscr;": '\U00002112', - "Lsh;": '\U000021B0', - "Lstrok;": '\U00000141', - "Lt;": '\U0000226A', - "Map;": '\U00002905', - "Mcy;": '\U0000041C', - "MediumSpace;": '\U0000205F', - "Mellintrf;": '\U00002133', - "Mfr;": '\U0001D510', - "MinusPlus;": '\U00002213', - "Mopf;": '\U0001D544', - "Mscr;": '\U00002133', - "Mu;": '\U0000039C', - "NJcy;": '\U0000040A', - "Nacute;": '\U00000143', - "Ncaron;": '\U00000147', - "Ncedil;": '\U00000145', - "Ncy;": '\U0000041D', - "NegativeMediumSpace;": '\U0000200B', - "NegativeThickSpace;": '\U0000200B', - "NegativeThinSpace;": '\U0000200B', - "NegativeVeryThinSpace;": '\U0000200B', - "NestedGreaterGreater;": '\U0000226B', - "NestedLessLess;": '\U0000226A', - "NewLine;": '\U0000000A', - "Nfr;": '\U0001D511', - "NoBreak;": '\U00002060', - "NonBreakingSpace;": '\U000000A0', - "Nopf;": '\U00002115', - "Not;": '\U00002AEC', - "NotCongruent;": '\U00002262', - "NotCupCap;": '\U0000226D', - "NotDoubleVerticalBar;": '\U00002226', - "NotElement;": '\U00002209', - "NotEqual;": '\U00002260', - "NotExists;": '\U00002204', - "NotGreater;": '\U0000226F', - "NotGreaterEqual;": '\U00002271', - "NotGreaterLess;": '\U00002279', - "NotGreaterTilde;": '\U00002275', - "NotLeftTriangle;": '\U000022EA', - "NotLeftTriangleEqual;": '\U000022EC', - "NotLess;": '\U0000226E', - "NotLessEqual;": '\U00002270', - "NotLessGreater;": '\U00002278', - "NotLessTilde;": '\U00002274', - "NotPrecedes;": '\U00002280', - "NotPrecedesSlantEqual;": '\U000022E0', - "NotReverseElement;": '\U0000220C', - "NotRightTriangle;": '\U000022EB', - "NotRightTriangleEqual;": '\U000022ED', - "NotSquareSubsetEqual;": '\U000022E2', - "NotSquareSupersetEqual;": '\U000022E3', - "NotSubsetEqual;": '\U00002288', - "NotSucceeds;": '\U00002281', - "NotSucceedsSlantEqual;": '\U000022E1', - "NotSupersetEqual;": '\U00002289', - "NotTilde;": '\U00002241', - "NotTildeEqual;": '\U00002244', - "NotTildeFullEqual;": '\U00002247', - "NotTildeTilde;": '\U00002249', - "NotVerticalBar;": '\U00002224', - "Nscr;": '\U0001D4A9', - "Ntilde;": '\U000000D1', - "Nu;": '\U0000039D', - "OElig;": '\U00000152', - "Oacute;": '\U000000D3', - "Ocirc;": '\U000000D4', - "Ocy;": '\U0000041E', - "Odblac;": '\U00000150', - "Ofr;": '\U0001D512', - "Ograve;": '\U000000D2', - "Omacr;": '\U0000014C', - "Omega;": '\U000003A9', - "Omicron;": '\U0000039F', - "Oopf;": '\U0001D546', - "OpenCurlyDoubleQuote;": '\U0000201C', - "OpenCurlyQuote;": '\U00002018', - "Or;": '\U00002A54', - "Oscr;": '\U0001D4AA', - "Oslash;": '\U000000D8', - "Otilde;": '\U000000D5', - "Otimes;": '\U00002A37', - "Ouml;": '\U000000D6', - "OverBar;": '\U0000203E', - "OverBrace;": '\U000023DE', - "OverBracket;": '\U000023B4', - "OverParenthesis;": '\U000023DC', - "PartialD;": '\U00002202', - "Pcy;": '\U0000041F', - "Pfr;": '\U0001D513', - "Phi;": '\U000003A6', - "Pi;": '\U000003A0', - "PlusMinus;": '\U000000B1', - "Poincareplane;": '\U0000210C', - "Popf;": '\U00002119', - "Pr;": '\U00002ABB', - "Precedes;": '\U0000227A', - "PrecedesEqual;": '\U00002AAF', - "PrecedesSlantEqual;": '\U0000227C', - "PrecedesTilde;": '\U0000227E', - "Prime;": '\U00002033', - "Product;": '\U0000220F', - "Proportion;": '\U00002237', - "Proportional;": '\U0000221D', - "Pscr;": '\U0001D4AB', - "Psi;": '\U000003A8', - "QUOT;": '\U00000022', - "Qfr;": '\U0001D514', - "Qopf;": '\U0000211A', - "Qscr;": '\U0001D4AC', - "RBarr;": '\U00002910', - "REG;": '\U000000AE', - "Racute;": '\U00000154', - "Rang;": '\U000027EB', - "Rarr;": '\U000021A0', - "Rarrtl;": '\U00002916', - "Rcaron;": '\U00000158', - "Rcedil;": '\U00000156', - "Rcy;": '\U00000420', - "Re;": '\U0000211C', - "ReverseElement;": '\U0000220B', - "ReverseEquilibrium;": '\U000021CB', - "ReverseUpEquilibrium;": '\U0000296F', - "Rfr;": '\U0000211C', - "Rho;": '\U000003A1', - "RightAngleBracket;": '\U000027E9', - "RightArrow;": '\U00002192', - "RightArrowBar;": '\U000021E5', - "RightArrowLeftArrow;": '\U000021C4', - "RightCeiling;": '\U00002309', - "RightDoubleBracket;": '\U000027E7', - "RightDownTeeVector;": '\U0000295D', - "RightDownVector;": '\U000021C2', - "RightDownVectorBar;": '\U00002955', - "RightFloor;": '\U0000230B', - "RightTee;": '\U000022A2', - "RightTeeArrow;": '\U000021A6', - "RightTeeVector;": '\U0000295B', - "RightTriangle;": '\U000022B3', - "RightTriangleBar;": '\U000029D0', - "RightTriangleEqual;": '\U000022B5', - "RightUpDownVector;": '\U0000294F', - "RightUpTeeVector;": '\U0000295C', - "RightUpVector;": '\U000021BE', - "RightUpVectorBar;": '\U00002954', - "RightVector;": '\U000021C0', - "RightVectorBar;": '\U00002953', - "Rightarrow;": '\U000021D2', - "Ropf;": '\U0000211D', - "RoundImplies;": '\U00002970', - "Rrightarrow;": '\U000021DB', - "Rscr;": '\U0000211B', - "Rsh;": '\U000021B1', - "RuleDelayed;": '\U000029F4', - "SHCHcy;": '\U00000429', - "SHcy;": '\U00000428', - "SOFTcy;": '\U0000042C', - "Sacute;": '\U0000015A', - "Sc;": '\U00002ABC', - "Scaron;": '\U00000160', - "Scedil;": '\U0000015E', - "Scirc;": '\U0000015C', - "Scy;": '\U00000421', - "Sfr;": '\U0001D516', - "ShortDownArrow;": '\U00002193', - "ShortLeftArrow;": '\U00002190', - "ShortRightArrow;": '\U00002192', - "ShortUpArrow;": '\U00002191', - "Sigma;": '\U000003A3', - "SmallCircle;": '\U00002218', - "Sopf;": '\U0001D54A', - "Sqrt;": '\U0000221A', - "Square;": '\U000025A1', - "SquareIntersection;": '\U00002293', - "SquareSubset;": '\U0000228F', - "SquareSubsetEqual;": '\U00002291', - "SquareSuperset;": '\U00002290', - "SquareSupersetEqual;": '\U00002292', - "SquareUnion;": '\U00002294', - "Sscr;": '\U0001D4AE', - "Star;": '\U000022C6', - "Sub;": '\U000022D0', - "Subset;": '\U000022D0', - "SubsetEqual;": '\U00002286', - "Succeeds;": '\U0000227B', - "SucceedsEqual;": '\U00002AB0', - "SucceedsSlantEqual;": '\U0000227D', - "SucceedsTilde;": '\U0000227F', - "SuchThat;": '\U0000220B', - "Sum;": '\U00002211', - "Sup;": '\U000022D1', - "Superset;": '\U00002283', - "SupersetEqual;": '\U00002287', - "Supset;": '\U000022D1', - "THORN;": '\U000000DE', - "TRADE;": '\U00002122', - "TSHcy;": '\U0000040B', - "TScy;": '\U00000426', - "Tab;": '\U00000009', - "Tau;": '\U000003A4', - "Tcaron;": '\U00000164', - "Tcedil;": '\U00000162', - "Tcy;": '\U00000422', - "Tfr;": '\U0001D517', - "Therefore;": '\U00002234', - "Theta;": '\U00000398', - "ThinSpace;": '\U00002009', - "Tilde;": '\U0000223C', - "TildeEqual;": '\U00002243', - "TildeFullEqual;": '\U00002245', - "TildeTilde;": '\U00002248', - "Topf;": '\U0001D54B', - "TripleDot;": '\U000020DB', - "Tscr;": '\U0001D4AF', - "Tstrok;": '\U00000166', - "Uacute;": '\U000000DA', - "Uarr;": '\U0000219F', - "Uarrocir;": '\U00002949', - "Ubrcy;": '\U0000040E', - "Ubreve;": '\U0000016C', - "Ucirc;": '\U000000DB', - "Ucy;": '\U00000423', - "Udblac;": '\U00000170', - "Ufr;": '\U0001D518', - "Ugrave;": '\U000000D9', - "Umacr;": '\U0000016A', - "UnderBar;": '\U0000005F', - "UnderBrace;": '\U000023DF', - "UnderBracket;": '\U000023B5', - "UnderParenthesis;": '\U000023DD', - "Union;": '\U000022C3', - "UnionPlus;": '\U0000228E', - "Uogon;": '\U00000172', - "Uopf;": '\U0001D54C', - "UpArrow;": '\U00002191', - "UpArrowBar;": '\U00002912', - "UpArrowDownArrow;": '\U000021C5', - "UpDownArrow;": '\U00002195', - "UpEquilibrium;": '\U0000296E', - "UpTee;": '\U000022A5', - "UpTeeArrow;": '\U000021A5', - "Uparrow;": '\U000021D1', - "Updownarrow;": '\U000021D5', - "UpperLeftArrow;": '\U00002196', - "UpperRightArrow;": '\U00002197', - "Upsi;": '\U000003D2', - "Upsilon;": '\U000003A5', - "Uring;": '\U0000016E', - "Uscr;": '\U0001D4B0', - "Utilde;": '\U00000168', - "Uuml;": '\U000000DC', - "VDash;": '\U000022AB', - "Vbar;": '\U00002AEB', - "Vcy;": '\U00000412', - "Vdash;": '\U000022A9', - "Vdashl;": '\U00002AE6', - "Vee;": '\U000022C1', - "Verbar;": '\U00002016', - "Vert;": '\U00002016', - "VerticalBar;": '\U00002223', - "VerticalLine;": '\U0000007C', - "VerticalSeparator;": '\U00002758', - "VerticalTilde;": '\U00002240', - "VeryThinSpace;": '\U0000200A', - "Vfr;": '\U0001D519', - "Vopf;": '\U0001D54D', - "Vscr;": '\U0001D4B1', - "Vvdash;": '\U000022AA', - "Wcirc;": '\U00000174', - "Wedge;": '\U000022C0', - "Wfr;": '\U0001D51A', - "Wopf;": '\U0001D54E', - "Wscr;": '\U0001D4B2', - "Xfr;": '\U0001D51B', - "Xi;": '\U0000039E', - "Xopf;": '\U0001D54F', - "Xscr;": '\U0001D4B3', - "YAcy;": '\U0000042F', - "YIcy;": '\U00000407', - "YUcy;": '\U0000042E', - "Yacute;": '\U000000DD', - "Ycirc;": '\U00000176', - "Ycy;": '\U0000042B', - "Yfr;": '\U0001D51C', - "Yopf;": '\U0001D550', - "Yscr;": '\U0001D4B4', - "Yuml;": '\U00000178', - "ZHcy;": '\U00000416', - "Zacute;": '\U00000179', - "Zcaron;": '\U0000017D', - "Zcy;": '\U00000417', - "Zdot;": '\U0000017B', - "ZeroWidthSpace;": '\U0000200B', - "Zeta;": '\U00000396', - "Zfr;": '\U00002128', - "Zopf;": '\U00002124', - "Zscr;": '\U0001D4B5', - "aacute;": '\U000000E1', - "abreve;": '\U00000103', - "ac;": '\U0000223E', - "acd;": '\U0000223F', - "acirc;": '\U000000E2', - "acute;": '\U000000B4', - "acy;": '\U00000430', - "aelig;": '\U000000E6', - "af;": '\U00002061', - "afr;": '\U0001D51E', - "agrave;": '\U000000E0', - "alefsym;": '\U00002135', - "aleph;": '\U00002135', - "alpha;": '\U000003B1', - "amacr;": '\U00000101', - "amalg;": '\U00002A3F', - "amp;": '\U00000026', - "and;": '\U00002227', - "andand;": '\U00002A55', - "andd;": '\U00002A5C', - "andslope;": '\U00002A58', - "andv;": '\U00002A5A', - "ang;": '\U00002220', - "ange;": '\U000029A4', - "angle;": '\U00002220', - "angmsd;": '\U00002221', - "angmsdaa;": '\U000029A8', - "angmsdab;": '\U000029A9', - "angmsdac;": '\U000029AA', - "angmsdad;": '\U000029AB', - "angmsdae;": '\U000029AC', - "angmsdaf;": '\U000029AD', - "angmsdag;": '\U000029AE', - "angmsdah;": '\U000029AF', - "angrt;": '\U0000221F', - "angrtvb;": '\U000022BE', - "angrtvbd;": '\U0000299D', - "angsph;": '\U00002222', - "angst;": '\U000000C5', - "angzarr;": '\U0000237C', - "aogon;": '\U00000105', - "aopf;": '\U0001D552', - "ap;": '\U00002248', - "apE;": '\U00002A70', - "apacir;": '\U00002A6F', - "ape;": '\U0000224A', - "apid;": '\U0000224B', - "apos;": '\U00000027', - "approx;": '\U00002248', - "approxeq;": '\U0000224A', - "aring;": '\U000000E5', - "ascr;": '\U0001D4B6', - "ast;": '\U0000002A', - "asymp;": '\U00002248', - "asympeq;": '\U0000224D', - "atilde;": '\U000000E3', - "auml;": '\U000000E4', - "awconint;": '\U00002233', - "awint;": '\U00002A11', - "bNot;": '\U00002AED', - "backcong;": '\U0000224C', - "backepsilon;": '\U000003F6', - "backprime;": '\U00002035', - "backsim;": '\U0000223D', - "backsimeq;": '\U000022CD', - "barvee;": '\U000022BD', - "barwed;": '\U00002305', - "barwedge;": '\U00002305', - "bbrk;": '\U000023B5', - "bbrktbrk;": '\U000023B6', - "bcong;": '\U0000224C', - "bcy;": '\U00000431', - "bdquo;": '\U0000201E', - "becaus;": '\U00002235', - "because;": '\U00002235', - "bemptyv;": '\U000029B0', - "bepsi;": '\U000003F6', - "bernou;": '\U0000212C', - "beta;": '\U000003B2', - "beth;": '\U00002136', - "between;": '\U0000226C', - "bfr;": '\U0001D51F', - "bigcap;": '\U000022C2', - "bigcirc;": '\U000025EF', - "bigcup;": '\U000022C3', - "bigodot;": '\U00002A00', - "bigoplus;": '\U00002A01', - "bigotimes;": '\U00002A02', - "bigsqcup;": '\U00002A06', - "bigstar;": '\U00002605', - "bigtriangledown;": '\U000025BD', - "bigtriangleup;": '\U000025B3', - "biguplus;": '\U00002A04', - "bigvee;": '\U000022C1', - "bigwedge;": '\U000022C0', - "bkarow;": '\U0000290D', - "blacklozenge;": '\U000029EB', - "blacksquare;": '\U000025AA', - "blacktriangle;": '\U000025B4', - "blacktriangledown;": '\U000025BE', - "blacktriangleleft;": '\U000025C2', - "blacktriangleright;": '\U000025B8', - "blank;": '\U00002423', - "blk12;": '\U00002592', - "blk14;": '\U00002591', - "blk34;": '\U00002593', - "block;": '\U00002588', - "bnot;": '\U00002310', - "bopf;": '\U0001D553', - "bot;": '\U000022A5', - "bottom;": '\U000022A5', - "bowtie;": '\U000022C8', - "boxDL;": '\U00002557', - "boxDR;": '\U00002554', - "boxDl;": '\U00002556', - "boxDr;": '\U00002553', - "boxH;": '\U00002550', - "boxHD;": '\U00002566', - "boxHU;": '\U00002569', - "boxHd;": '\U00002564', - "boxHu;": '\U00002567', - "boxUL;": '\U0000255D', - "boxUR;": '\U0000255A', - "boxUl;": '\U0000255C', - "boxUr;": '\U00002559', - "boxV;": '\U00002551', - "boxVH;": '\U0000256C', - "boxVL;": '\U00002563', - "boxVR;": '\U00002560', - "boxVh;": '\U0000256B', - "boxVl;": '\U00002562', - "boxVr;": '\U0000255F', - "boxbox;": '\U000029C9', - "boxdL;": '\U00002555', - "boxdR;": '\U00002552', - "boxdl;": '\U00002510', - "boxdr;": '\U0000250C', - "boxh;": '\U00002500', - "boxhD;": '\U00002565', - "boxhU;": '\U00002568', - "boxhd;": '\U0000252C', - "boxhu;": '\U00002534', - "boxminus;": '\U0000229F', - "boxplus;": '\U0000229E', - "boxtimes;": '\U000022A0', - "boxuL;": '\U0000255B', - "boxuR;": '\U00002558', - "boxul;": '\U00002518', - "boxur;": '\U00002514', - "boxv;": '\U00002502', - "boxvH;": '\U0000256A', - "boxvL;": '\U00002561', - "boxvR;": '\U0000255E', - "boxvh;": '\U0000253C', - "boxvl;": '\U00002524', - "boxvr;": '\U0000251C', - "bprime;": '\U00002035', - "breve;": '\U000002D8', - "brvbar;": '\U000000A6', - "bscr;": '\U0001D4B7', - "bsemi;": '\U0000204F', - "bsim;": '\U0000223D', - "bsime;": '\U000022CD', - "bsol;": '\U0000005C', - "bsolb;": '\U000029C5', - "bsolhsub;": '\U000027C8', - "bull;": '\U00002022', - "bullet;": '\U00002022', - "bump;": '\U0000224E', - "bumpE;": '\U00002AAE', - "bumpe;": '\U0000224F', - "bumpeq;": '\U0000224F', - "cacute;": '\U00000107', - "cap;": '\U00002229', - "capand;": '\U00002A44', - "capbrcup;": '\U00002A49', - "capcap;": '\U00002A4B', - "capcup;": '\U00002A47', - "capdot;": '\U00002A40', - "caret;": '\U00002041', - "caron;": '\U000002C7', - "ccaps;": '\U00002A4D', - "ccaron;": '\U0000010D', - "ccedil;": '\U000000E7', - "ccirc;": '\U00000109', - "ccups;": '\U00002A4C', - "ccupssm;": '\U00002A50', - "cdot;": '\U0000010B', - "cedil;": '\U000000B8', - "cemptyv;": '\U000029B2', - "cent;": '\U000000A2', - "centerdot;": '\U000000B7', - "cfr;": '\U0001D520', - "chcy;": '\U00000447', - "check;": '\U00002713', - "checkmark;": '\U00002713', - "chi;": '\U000003C7', - "cir;": '\U000025CB', - "cirE;": '\U000029C3', - "circ;": '\U000002C6', - "circeq;": '\U00002257', - "circlearrowleft;": '\U000021BA', - "circlearrowright;": '\U000021BB', - "circledR;": '\U000000AE', - "circledS;": '\U000024C8', - "circledast;": '\U0000229B', - "circledcirc;": '\U0000229A', - "circleddash;": '\U0000229D', - "cire;": '\U00002257', - "cirfnint;": '\U00002A10', - "cirmid;": '\U00002AEF', - "cirscir;": '\U000029C2', - "clubs;": '\U00002663', - "clubsuit;": '\U00002663', - "colon;": '\U0000003A', - "colone;": '\U00002254', - "coloneq;": '\U00002254', - "comma;": '\U0000002C', - "commat;": '\U00000040', - "comp;": '\U00002201', - "compfn;": '\U00002218', - "complement;": '\U00002201', - "complexes;": '\U00002102', - "cong;": '\U00002245', - "congdot;": '\U00002A6D', - "conint;": '\U0000222E', - "copf;": '\U0001D554', - "coprod;": '\U00002210', - "copy;": '\U000000A9', - "copysr;": '\U00002117', - "crarr;": '\U000021B5', - "cross;": '\U00002717', - "cscr;": '\U0001D4B8', - "csub;": '\U00002ACF', - "csube;": '\U00002AD1', - "csup;": '\U00002AD0', - "csupe;": '\U00002AD2', - "ctdot;": '\U000022EF', - "cudarrl;": '\U00002938', - "cudarrr;": '\U00002935', - "cuepr;": '\U000022DE', - "cuesc;": '\U000022DF', - "cularr;": '\U000021B6', - "cularrp;": '\U0000293D', - "cup;": '\U0000222A', - "cupbrcap;": '\U00002A48', - "cupcap;": '\U00002A46', - "cupcup;": '\U00002A4A', - "cupdot;": '\U0000228D', - "cupor;": '\U00002A45', - "curarr;": '\U000021B7', - "curarrm;": '\U0000293C', - "curlyeqprec;": '\U000022DE', - "curlyeqsucc;": '\U000022DF', - "curlyvee;": '\U000022CE', - "curlywedge;": '\U000022CF', - "curren;": '\U000000A4', - "curvearrowleft;": '\U000021B6', - "curvearrowright;": '\U000021B7', - "cuvee;": '\U000022CE', - "cuwed;": '\U000022CF', - "cwconint;": '\U00002232', - "cwint;": '\U00002231', - "cylcty;": '\U0000232D', - "dArr;": '\U000021D3', - "dHar;": '\U00002965', - "dagger;": '\U00002020', - "daleth;": '\U00002138', - "darr;": '\U00002193', - "dash;": '\U00002010', - "dashv;": '\U000022A3', - "dbkarow;": '\U0000290F', - "dblac;": '\U000002DD', - "dcaron;": '\U0000010F', - "dcy;": '\U00000434', - "dd;": '\U00002146', - "ddagger;": '\U00002021', - "ddarr;": '\U000021CA', - "ddotseq;": '\U00002A77', - "deg;": '\U000000B0', - "delta;": '\U000003B4', - "demptyv;": '\U000029B1', - "dfisht;": '\U0000297F', - "dfr;": '\U0001D521', - "dharl;": '\U000021C3', - "dharr;": '\U000021C2', - "diam;": '\U000022C4', - "diamond;": '\U000022C4', - "diamondsuit;": '\U00002666', - "diams;": '\U00002666', - "die;": '\U000000A8', - "digamma;": '\U000003DD', - "disin;": '\U000022F2', - "div;": '\U000000F7', - "divide;": '\U000000F7', - "divideontimes;": '\U000022C7', - "divonx;": '\U000022C7', - "djcy;": '\U00000452', - "dlcorn;": '\U0000231E', - "dlcrop;": '\U0000230D', - "dollar;": '\U00000024', - "dopf;": '\U0001D555', - "dot;": '\U000002D9', - "doteq;": '\U00002250', - "doteqdot;": '\U00002251', - "dotminus;": '\U00002238', - "dotplus;": '\U00002214', - "dotsquare;": '\U000022A1', - "doublebarwedge;": '\U00002306', - "downarrow;": '\U00002193', - "downdownarrows;": '\U000021CA', - "downharpoonleft;": '\U000021C3', - "downharpoonright;": '\U000021C2', - "drbkarow;": '\U00002910', - "drcorn;": '\U0000231F', - "drcrop;": '\U0000230C', - "dscr;": '\U0001D4B9', - "dscy;": '\U00000455', - "dsol;": '\U000029F6', - "dstrok;": '\U00000111', - "dtdot;": '\U000022F1', - "dtri;": '\U000025BF', - "dtrif;": '\U000025BE', - "duarr;": '\U000021F5', - "duhar;": '\U0000296F', - "dwangle;": '\U000029A6', - "dzcy;": '\U0000045F', - "dzigrarr;": '\U000027FF', - "eDDot;": '\U00002A77', - "eDot;": '\U00002251', - "eacute;": '\U000000E9', - "easter;": '\U00002A6E', - "ecaron;": '\U0000011B', - "ecir;": '\U00002256', - "ecirc;": '\U000000EA', - "ecolon;": '\U00002255', - "ecy;": '\U0000044D', - "edot;": '\U00000117', - "ee;": '\U00002147', - "efDot;": '\U00002252', - "efr;": '\U0001D522', - "eg;": '\U00002A9A', - "egrave;": '\U000000E8', - "egs;": '\U00002A96', - "egsdot;": '\U00002A98', - "el;": '\U00002A99', - "elinters;": '\U000023E7', - "ell;": '\U00002113', - "els;": '\U00002A95', - "elsdot;": '\U00002A97', - "emacr;": '\U00000113', - "empty;": '\U00002205', - "emptyset;": '\U00002205', - "emptyv;": '\U00002205', - "emsp;": '\U00002003', - "emsp13;": '\U00002004', - "emsp14;": '\U00002005', - "eng;": '\U0000014B', - "ensp;": '\U00002002', - "eogon;": '\U00000119', - "eopf;": '\U0001D556', - "epar;": '\U000022D5', - "eparsl;": '\U000029E3', - "eplus;": '\U00002A71', - "epsi;": '\U000003B5', - "epsilon;": '\U000003B5', - "epsiv;": '\U000003F5', - "eqcirc;": '\U00002256', - "eqcolon;": '\U00002255', - "eqsim;": '\U00002242', - "eqslantgtr;": '\U00002A96', - "eqslantless;": '\U00002A95', - "equals;": '\U0000003D', - "equest;": '\U0000225F', - "equiv;": '\U00002261', - "equivDD;": '\U00002A78', - "eqvparsl;": '\U000029E5', - "erDot;": '\U00002253', - "erarr;": '\U00002971', - "escr;": '\U0000212F', - "esdot;": '\U00002250', - "esim;": '\U00002242', - "eta;": '\U000003B7', - "eth;": '\U000000F0', - "euml;": '\U000000EB', - "euro;": '\U000020AC', - "excl;": '\U00000021', - "exist;": '\U00002203', - "expectation;": '\U00002130', - "exponentiale;": '\U00002147', - "fallingdotseq;": '\U00002252', - "fcy;": '\U00000444', - "female;": '\U00002640', - "ffilig;": '\U0000FB03', - "fflig;": '\U0000FB00', - "ffllig;": '\U0000FB04', - "ffr;": '\U0001D523', - "filig;": '\U0000FB01', - "flat;": '\U0000266D', - "fllig;": '\U0000FB02', - "fltns;": '\U000025B1', - "fnof;": '\U00000192', - "fopf;": '\U0001D557', - "forall;": '\U00002200', - "fork;": '\U000022D4', - "forkv;": '\U00002AD9', - "fpartint;": '\U00002A0D', - "frac12;": '\U000000BD', - "frac13;": '\U00002153', - "frac14;": '\U000000BC', - "frac15;": '\U00002155', - "frac16;": '\U00002159', - "frac18;": '\U0000215B', - "frac23;": '\U00002154', - "frac25;": '\U00002156', - "frac34;": '\U000000BE', - "frac35;": '\U00002157', - "frac38;": '\U0000215C', - "frac45;": '\U00002158', - "frac56;": '\U0000215A', - "frac58;": '\U0000215D', - "frac78;": '\U0000215E', - "frasl;": '\U00002044', - "frown;": '\U00002322', - "fscr;": '\U0001D4BB', - "gE;": '\U00002267', - "gEl;": '\U00002A8C', - "gacute;": '\U000001F5', - "gamma;": '\U000003B3', - "gammad;": '\U000003DD', - "gap;": '\U00002A86', - "gbreve;": '\U0000011F', - "gcirc;": '\U0000011D', - "gcy;": '\U00000433', - "gdot;": '\U00000121', - "ge;": '\U00002265', - "gel;": '\U000022DB', - "geq;": '\U00002265', - "geqq;": '\U00002267', - "geqslant;": '\U00002A7E', - "ges;": '\U00002A7E', - "gescc;": '\U00002AA9', - "gesdot;": '\U00002A80', - "gesdoto;": '\U00002A82', - "gesdotol;": '\U00002A84', - "gesles;": '\U00002A94', - "gfr;": '\U0001D524', - "gg;": '\U0000226B', - "ggg;": '\U000022D9', - "gimel;": '\U00002137', - "gjcy;": '\U00000453', - "gl;": '\U00002277', - "glE;": '\U00002A92', - "gla;": '\U00002AA5', - "glj;": '\U00002AA4', - "gnE;": '\U00002269', - "gnap;": '\U00002A8A', - "gnapprox;": '\U00002A8A', - "gne;": '\U00002A88', - "gneq;": '\U00002A88', - "gneqq;": '\U00002269', - "gnsim;": '\U000022E7', - "gopf;": '\U0001D558', - "grave;": '\U00000060', - "gscr;": '\U0000210A', - "gsim;": '\U00002273', - "gsime;": '\U00002A8E', - "gsiml;": '\U00002A90', - "gt;": '\U0000003E', - "gtcc;": '\U00002AA7', - "gtcir;": '\U00002A7A', - "gtdot;": '\U000022D7', - "gtlPar;": '\U00002995', - "gtquest;": '\U00002A7C', - "gtrapprox;": '\U00002A86', - "gtrarr;": '\U00002978', - "gtrdot;": '\U000022D7', - "gtreqless;": '\U000022DB', - "gtreqqless;": '\U00002A8C', - "gtrless;": '\U00002277', - "gtrsim;": '\U00002273', - "hArr;": '\U000021D4', - "hairsp;": '\U0000200A', - "half;": '\U000000BD', - "hamilt;": '\U0000210B', - "hardcy;": '\U0000044A', - "harr;": '\U00002194', - "harrcir;": '\U00002948', - "harrw;": '\U000021AD', - "hbar;": '\U0000210F', - "hcirc;": '\U00000125', - "hearts;": '\U00002665', - "heartsuit;": '\U00002665', - "hellip;": '\U00002026', - "hercon;": '\U000022B9', - "hfr;": '\U0001D525', - "hksearow;": '\U00002925', - "hkswarow;": '\U00002926', - "hoarr;": '\U000021FF', - "homtht;": '\U0000223B', - "hookleftarrow;": '\U000021A9', - "hookrightarrow;": '\U000021AA', - "hopf;": '\U0001D559', - "horbar;": '\U00002015', - "hscr;": '\U0001D4BD', - "hslash;": '\U0000210F', - "hstrok;": '\U00000127', - "hybull;": '\U00002043', - "hyphen;": '\U00002010', - "iacute;": '\U000000ED', - "ic;": '\U00002063', - "icirc;": '\U000000EE', - "icy;": '\U00000438', - "iecy;": '\U00000435', - "iexcl;": '\U000000A1', - "iff;": '\U000021D4', - "ifr;": '\U0001D526', - "igrave;": '\U000000EC', - "ii;": '\U00002148', - "iiiint;": '\U00002A0C', - "iiint;": '\U0000222D', - "iinfin;": '\U000029DC', - "iiota;": '\U00002129', - "ijlig;": '\U00000133', - "imacr;": '\U0000012B', - "image;": '\U00002111', - "imagline;": '\U00002110', - "imagpart;": '\U00002111', - "imath;": '\U00000131', - "imof;": '\U000022B7', - "imped;": '\U000001B5', - "in;": '\U00002208', - "incare;": '\U00002105', - "infin;": '\U0000221E', - "infintie;": '\U000029DD', - "inodot;": '\U00000131', - "int;": '\U0000222B', - "intcal;": '\U000022BA', - "integers;": '\U00002124', - "intercal;": '\U000022BA', - "intlarhk;": '\U00002A17', - "intprod;": '\U00002A3C', - "iocy;": '\U00000451', - "iogon;": '\U0000012F', - "iopf;": '\U0001D55A', - "iota;": '\U000003B9', - "iprod;": '\U00002A3C', - "iquest;": '\U000000BF', - "iscr;": '\U0001D4BE', - "isin;": '\U00002208', - "isinE;": '\U000022F9', - "isindot;": '\U000022F5', - "isins;": '\U000022F4', - "isinsv;": '\U000022F3', - "isinv;": '\U00002208', - "it;": '\U00002062', - "itilde;": '\U00000129', - "iukcy;": '\U00000456', - "iuml;": '\U000000EF', - "jcirc;": '\U00000135', - "jcy;": '\U00000439', - "jfr;": '\U0001D527', - "jmath;": '\U00000237', - "jopf;": '\U0001D55B', - "jscr;": '\U0001D4BF', - "jsercy;": '\U00000458', - "jukcy;": '\U00000454', - "kappa;": '\U000003BA', - "kappav;": '\U000003F0', - "kcedil;": '\U00000137', - "kcy;": '\U0000043A', - "kfr;": '\U0001D528', - "kgreen;": '\U00000138', - "khcy;": '\U00000445', - "kjcy;": '\U0000045C', - "kopf;": '\U0001D55C', - "kscr;": '\U0001D4C0', - "lAarr;": '\U000021DA', - "lArr;": '\U000021D0', - "lAtail;": '\U0000291B', - "lBarr;": '\U0000290E', - "lE;": '\U00002266', - "lEg;": '\U00002A8B', - "lHar;": '\U00002962', - "lacute;": '\U0000013A', - "laemptyv;": '\U000029B4', - "lagran;": '\U00002112', - "lambda;": '\U000003BB', - "lang;": '\U000027E8', - "langd;": '\U00002991', - "langle;": '\U000027E8', - "lap;": '\U00002A85', - "laquo;": '\U000000AB', - "larr;": '\U00002190', - "larrb;": '\U000021E4', - "larrbfs;": '\U0000291F', - "larrfs;": '\U0000291D', - "larrhk;": '\U000021A9', - "larrlp;": '\U000021AB', - "larrpl;": '\U00002939', - "larrsim;": '\U00002973', - "larrtl;": '\U000021A2', - "lat;": '\U00002AAB', - "latail;": '\U00002919', - "late;": '\U00002AAD', - "lbarr;": '\U0000290C', - "lbbrk;": '\U00002772', - "lbrace;": '\U0000007B', - "lbrack;": '\U0000005B', - "lbrke;": '\U0000298B', - "lbrksld;": '\U0000298F', - "lbrkslu;": '\U0000298D', - "lcaron;": '\U0000013E', - "lcedil;": '\U0000013C', - "lceil;": '\U00002308', - "lcub;": '\U0000007B', - "lcy;": '\U0000043B', - "ldca;": '\U00002936', - "ldquo;": '\U0000201C', - "ldquor;": '\U0000201E', - "ldrdhar;": '\U00002967', - "ldrushar;": '\U0000294B', - "ldsh;": '\U000021B2', - "le;": '\U00002264', - "leftarrow;": '\U00002190', - "leftarrowtail;": '\U000021A2', - "leftharpoondown;": '\U000021BD', - "leftharpoonup;": '\U000021BC', - "leftleftarrows;": '\U000021C7', - "leftrightarrow;": '\U00002194', - "leftrightarrows;": '\U000021C6', - "leftrightharpoons;": '\U000021CB', - "leftrightsquigarrow;": '\U000021AD', - "leftthreetimes;": '\U000022CB', - "leg;": '\U000022DA', - "leq;": '\U00002264', - "leqq;": '\U00002266', - "leqslant;": '\U00002A7D', - "les;": '\U00002A7D', - "lescc;": '\U00002AA8', - "lesdot;": '\U00002A7F', - "lesdoto;": '\U00002A81', - "lesdotor;": '\U00002A83', - "lesges;": '\U00002A93', - "lessapprox;": '\U00002A85', - "lessdot;": '\U000022D6', - "lesseqgtr;": '\U000022DA', - "lesseqqgtr;": '\U00002A8B', - "lessgtr;": '\U00002276', - "lesssim;": '\U00002272', - "lfisht;": '\U0000297C', - "lfloor;": '\U0000230A', - "lfr;": '\U0001D529', - "lg;": '\U00002276', - "lgE;": '\U00002A91', - "lhard;": '\U000021BD', - "lharu;": '\U000021BC', - "lharul;": '\U0000296A', - "lhblk;": '\U00002584', - "ljcy;": '\U00000459', - "ll;": '\U0000226A', - "llarr;": '\U000021C7', - "llcorner;": '\U0000231E', - "llhard;": '\U0000296B', - "lltri;": '\U000025FA', - "lmidot;": '\U00000140', - "lmoust;": '\U000023B0', - "lmoustache;": '\U000023B0', - "lnE;": '\U00002268', - "lnap;": '\U00002A89', - "lnapprox;": '\U00002A89', - "lne;": '\U00002A87', - "lneq;": '\U00002A87', - "lneqq;": '\U00002268', - "lnsim;": '\U000022E6', - "loang;": '\U000027EC', - "loarr;": '\U000021FD', - "lobrk;": '\U000027E6', - "longleftarrow;": '\U000027F5', - "longleftrightarrow;": '\U000027F7', - "longmapsto;": '\U000027FC', - "longrightarrow;": '\U000027F6', - "looparrowleft;": '\U000021AB', - "looparrowright;": '\U000021AC', - "lopar;": '\U00002985', - "lopf;": '\U0001D55D', - "loplus;": '\U00002A2D', - "lotimes;": '\U00002A34', - "lowast;": '\U00002217', - "lowbar;": '\U0000005F', - "loz;": '\U000025CA', - "lozenge;": '\U000025CA', - "lozf;": '\U000029EB', - "lpar;": '\U00000028', - "lparlt;": '\U00002993', - "lrarr;": '\U000021C6', - "lrcorner;": '\U0000231F', - "lrhar;": '\U000021CB', - "lrhard;": '\U0000296D', - "lrm;": '\U0000200E', - "lrtri;": '\U000022BF', - "lsaquo;": '\U00002039', - "lscr;": '\U0001D4C1', - "lsh;": '\U000021B0', - "lsim;": '\U00002272', - "lsime;": '\U00002A8D', - "lsimg;": '\U00002A8F', - "lsqb;": '\U0000005B', - "lsquo;": '\U00002018', - "lsquor;": '\U0000201A', - "lstrok;": '\U00000142', - "lt;": '\U0000003C', - "ltcc;": '\U00002AA6', - "ltcir;": '\U00002A79', - "ltdot;": '\U000022D6', - "lthree;": '\U000022CB', - "ltimes;": '\U000022C9', - "ltlarr;": '\U00002976', - "ltquest;": '\U00002A7B', - "ltrPar;": '\U00002996', - "ltri;": '\U000025C3', - "ltrie;": '\U000022B4', - "ltrif;": '\U000025C2', - "lurdshar;": '\U0000294A', - "luruhar;": '\U00002966', - "mDDot;": '\U0000223A', - "macr;": '\U000000AF', - "male;": '\U00002642', - "malt;": '\U00002720', - "maltese;": '\U00002720', - "map;": '\U000021A6', - "mapsto;": '\U000021A6', - "mapstodown;": '\U000021A7', - "mapstoleft;": '\U000021A4', - "mapstoup;": '\U000021A5', - "marker;": '\U000025AE', - "mcomma;": '\U00002A29', - "mcy;": '\U0000043C', - "mdash;": '\U00002014', - "measuredangle;": '\U00002221', - "mfr;": '\U0001D52A', - "mho;": '\U00002127', - "micro;": '\U000000B5', - "mid;": '\U00002223', - "midast;": '\U0000002A', - "midcir;": '\U00002AF0', - "middot;": '\U000000B7', - "minus;": '\U00002212', - "minusb;": '\U0000229F', - "minusd;": '\U00002238', - "minusdu;": '\U00002A2A', - "mlcp;": '\U00002ADB', - "mldr;": '\U00002026', - "mnplus;": '\U00002213', - "models;": '\U000022A7', - "mopf;": '\U0001D55E', - "mp;": '\U00002213', - "mscr;": '\U0001D4C2', - "mstpos;": '\U0000223E', - "mu;": '\U000003BC', - "multimap;": '\U000022B8', - "mumap;": '\U000022B8', - "nLeftarrow;": '\U000021CD', - "nLeftrightarrow;": '\U000021CE', - "nRightarrow;": '\U000021CF', - "nVDash;": '\U000022AF', - "nVdash;": '\U000022AE', - "nabla;": '\U00002207', - "nacute;": '\U00000144', - "nap;": '\U00002249', - "napos;": '\U00000149', - "napprox;": '\U00002249', - "natur;": '\U0000266E', - "natural;": '\U0000266E', - "naturals;": '\U00002115', - "nbsp;": '\U000000A0', - "ncap;": '\U00002A43', - "ncaron;": '\U00000148', - "ncedil;": '\U00000146', - "ncong;": '\U00002247', - "ncup;": '\U00002A42', - "ncy;": '\U0000043D', - "ndash;": '\U00002013', - "ne;": '\U00002260', - "neArr;": '\U000021D7', - "nearhk;": '\U00002924', - "nearr;": '\U00002197', - "nearrow;": '\U00002197', - "nequiv;": '\U00002262', - "nesear;": '\U00002928', - "nexist;": '\U00002204', - "nexists;": '\U00002204', - "nfr;": '\U0001D52B', - "nge;": '\U00002271', - "ngeq;": '\U00002271', - "ngsim;": '\U00002275', - "ngt;": '\U0000226F', - "ngtr;": '\U0000226F', - "nhArr;": '\U000021CE', - "nharr;": '\U000021AE', - "nhpar;": '\U00002AF2', - "ni;": '\U0000220B', - "nis;": '\U000022FC', - "nisd;": '\U000022FA', - "niv;": '\U0000220B', - "njcy;": '\U0000045A', - "nlArr;": '\U000021CD', - "nlarr;": '\U0000219A', - "nldr;": '\U00002025', - "nle;": '\U00002270', - "nleftarrow;": '\U0000219A', - "nleftrightarrow;": '\U000021AE', - "nleq;": '\U00002270', - "nless;": '\U0000226E', - "nlsim;": '\U00002274', - "nlt;": '\U0000226E', - "nltri;": '\U000022EA', - "nltrie;": '\U000022EC', - "nmid;": '\U00002224', - "nopf;": '\U0001D55F', - "not;": '\U000000AC', - "notin;": '\U00002209', - "notinva;": '\U00002209', - "notinvb;": '\U000022F7', - "notinvc;": '\U000022F6', - "notni;": '\U0000220C', - "notniva;": '\U0000220C', - "notnivb;": '\U000022FE', - "notnivc;": '\U000022FD', - "npar;": '\U00002226', - "nparallel;": '\U00002226', - "npolint;": '\U00002A14', - "npr;": '\U00002280', - "nprcue;": '\U000022E0', - "nprec;": '\U00002280', - "nrArr;": '\U000021CF', - "nrarr;": '\U0000219B', - "nrightarrow;": '\U0000219B', - "nrtri;": '\U000022EB', - "nrtrie;": '\U000022ED', - "nsc;": '\U00002281', - "nsccue;": '\U000022E1', - "nscr;": '\U0001D4C3', - "nshortmid;": '\U00002224', - "nshortparallel;": '\U00002226', - "nsim;": '\U00002241', - "nsime;": '\U00002244', - "nsimeq;": '\U00002244', - "nsmid;": '\U00002224', - "nspar;": '\U00002226', - "nsqsube;": '\U000022E2', - "nsqsupe;": '\U000022E3', - "nsub;": '\U00002284', - "nsube;": '\U00002288', - "nsubseteq;": '\U00002288', - "nsucc;": '\U00002281', - "nsup;": '\U00002285', - "nsupe;": '\U00002289', - "nsupseteq;": '\U00002289', - "ntgl;": '\U00002279', - "ntilde;": '\U000000F1', - "ntlg;": '\U00002278', - "ntriangleleft;": '\U000022EA', - "ntrianglelefteq;": '\U000022EC', - "ntriangleright;": '\U000022EB', - "ntrianglerighteq;": '\U000022ED', - "nu;": '\U000003BD', - "num;": '\U00000023', - "numero;": '\U00002116', - "numsp;": '\U00002007', - "nvDash;": '\U000022AD', - "nvHarr;": '\U00002904', - "nvdash;": '\U000022AC', - "nvinfin;": '\U000029DE', - "nvlArr;": '\U00002902', - "nvrArr;": '\U00002903', - "nwArr;": '\U000021D6', - "nwarhk;": '\U00002923', - "nwarr;": '\U00002196', - "nwarrow;": '\U00002196', - "nwnear;": '\U00002927', - "oS;": '\U000024C8', - "oacute;": '\U000000F3', - "oast;": '\U0000229B', - "ocir;": '\U0000229A', - "ocirc;": '\U000000F4', - "ocy;": '\U0000043E', - "odash;": '\U0000229D', - "odblac;": '\U00000151', - "odiv;": '\U00002A38', - "odot;": '\U00002299', - "odsold;": '\U000029BC', - "oelig;": '\U00000153', - "ofcir;": '\U000029BF', - "ofr;": '\U0001D52C', - "ogon;": '\U000002DB', - "ograve;": '\U000000F2', - "ogt;": '\U000029C1', - "ohbar;": '\U000029B5', - "ohm;": '\U000003A9', - "oint;": '\U0000222E', - "olarr;": '\U000021BA', - "olcir;": '\U000029BE', - "olcross;": '\U000029BB', - "oline;": '\U0000203E', - "olt;": '\U000029C0', - "omacr;": '\U0000014D', - "omega;": '\U000003C9', - "omicron;": '\U000003BF', - "omid;": '\U000029B6', - "ominus;": '\U00002296', - "oopf;": '\U0001D560', - "opar;": '\U000029B7', - "operp;": '\U000029B9', - "oplus;": '\U00002295', - "or;": '\U00002228', - "orarr;": '\U000021BB', - "ord;": '\U00002A5D', - "order;": '\U00002134', - "orderof;": '\U00002134', - "ordf;": '\U000000AA', - "ordm;": '\U000000BA', - "origof;": '\U000022B6', - "oror;": '\U00002A56', - "orslope;": '\U00002A57', - "orv;": '\U00002A5B', - "oscr;": '\U00002134', - "oslash;": '\U000000F8', - "osol;": '\U00002298', - "otilde;": '\U000000F5', - "otimes;": '\U00002297', - "otimesas;": '\U00002A36', - "ouml;": '\U000000F6', - "ovbar;": '\U0000233D', - "par;": '\U00002225', - "para;": '\U000000B6', - "parallel;": '\U00002225', - "parsim;": '\U00002AF3', - "parsl;": '\U00002AFD', - "part;": '\U00002202', - "pcy;": '\U0000043F', - "percnt;": '\U00000025', - "period;": '\U0000002E', - "permil;": '\U00002030', - "perp;": '\U000022A5', - "pertenk;": '\U00002031', - "pfr;": '\U0001D52D', - "phi;": '\U000003C6', - "phiv;": '\U000003D5', - "phmmat;": '\U00002133', - "phone;": '\U0000260E', - "pi;": '\U000003C0', - "pitchfork;": '\U000022D4', - "piv;": '\U000003D6', - "planck;": '\U0000210F', - "planckh;": '\U0000210E', - "plankv;": '\U0000210F', - "plus;": '\U0000002B', - "plusacir;": '\U00002A23', - "plusb;": '\U0000229E', - "pluscir;": '\U00002A22', - "plusdo;": '\U00002214', - "plusdu;": '\U00002A25', - "pluse;": '\U00002A72', - "plusmn;": '\U000000B1', - "plussim;": '\U00002A26', - "plustwo;": '\U00002A27', - "pm;": '\U000000B1', - "pointint;": '\U00002A15', - "popf;": '\U0001D561', - "pound;": '\U000000A3', - "pr;": '\U0000227A', - "prE;": '\U00002AB3', - "prap;": '\U00002AB7', - "prcue;": '\U0000227C', - "pre;": '\U00002AAF', - "prec;": '\U0000227A', - "precapprox;": '\U00002AB7', - "preccurlyeq;": '\U0000227C', - "preceq;": '\U00002AAF', - "precnapprox;": '\U00002AB9', - "precneqq;": '\U00002AB5', - "precnsim;": '\U000022E8', - "precsim;": '\U0000227E', - "prime;": '\U00002032', - "primes;": '\U00002119', - "prnE;": '\U00002AB5', - "prnap;": '\U00002AB9', - "prnsim;": '\U000022E8', - "prod;": '\U0000220F', - "profalar;": '\U0000232E', - "profline;": '\U00002312', - "profsurf;": '\U00002313', - "prop;": '\U0000221D', - "propto;": '\U0000221D', - "prsim;": '\U0000227E', - "prurel;": '\U000022B0', - "pscr;": '\U0001D4C5', - "psi;": '\U000003C8', - "puncsp;": '\U00002008', - "qfr;": '\U0001D52E', - "qint;": '\U00002A0C', - "qopf;": '\U0001D562', - "qprime;": '\U00002057', - "qscr;": '\U0001D4C6', - "quaternions;": '\U0000210D', - "quatint;": '\U00002A16', - "quest;": '\U0000003F', - "questeq;": '\U0000225F', - "quot;": '\U00000022', - "rAarr;": '\U000021DB', - "rArr;": '\U000021D2', - "rAtail;": '\U0000291C', - "rBarr;": '\U0000290F', - "rHar;": '\U00002964', - "racute;": '\U00000155', - "radic;": '\U0000221A', - "raemptyv;": '\U000029B3', - "rang;": '\U000027E9', - "rangd;": '\U00002992', - "range;": '\U000029A5', - "rangle;": '\U000027E9', - "raquo;": '\U000000BB', - "rarr;": '\U00002192', - "rarrap;": '\U00002975', - "rarrb;": '\U000021E5', - "rarrbfs;": '\U00002920', - "rarrc;": '\U00002933', - "rarrfs;": '\U0000291E', - "rarrhk;": '\U000021AA', - "rarrlp;": '\U000021AC', - "rarrpl;": '\U00002945', - "rarrsim;": '\U00002974', - "rarrtl;": '\U000021A3', - "rarrw;": '\U0000219D', - "ratail;": '\U0000291A', - "ratio;": '\U00002236', - "rationals;": '\U0000211A', - "rbarr;": '\U0000290D', - "rbbrk;": '\U00002773', - "rbrace;": '\U0000007D', - "rbrack;": '\U0000005D', - "rbrke;": '\U0000298C', - "rbrksld;": '\U0000298E', - "rbrkslu;": '\U00002990', - "rcaron;": '\U00000159', - "rcedil;": '\U00000157', - "rceil;": '\U00002309', - "rcub;": '\U0000007D', - "rcy;": '\U00000440', - "rdca;": '\U00002937', - "rdldhar;": '\U00002969', - "rdquo;": '\U0000201D', - "rdquor;": '\U0000201D', - "rdsh;": '\U000021B3', - "real;": '\U0000211C', - "realine;": '\U0000211B', - "realpart;": '\U0000211C', - "reals;": '\U0000211D', - "rect;": '\U000025AD', - "reg;": '\U000000AE', - "rfisht;": '\U0000297D', - "rfloor;": '\U0000230B', - "rfr;": '\U0001D52F', - "rhard;": '\U000021C1', - "rharu;": '\U000021C0', - "rharul;": '\U0000296C', - "rho;": '\U000003C1', - "rhov;": '\U000003F1', - "rightarrow;": '\U00002192', - "rightarrowtail;": '\U000021A3', - "rightharpoondown;": '\U000021C1', - "rightharpoonup;": '\U000021C0', - "rightleftarrows;": '\U000021C4', - "rightleftharpoons;": '\U000021CC', - "rightrightarrows;": '\U000021C9', - "rightsquigarrow;": '\U0000219D', - "rightthreetimes;": '\U000022CC', - "ring;": '\U000002DA', - "risingdotseq;": '\U00002253', - "rlarr;": '\U000021C4', - "rlhar;": '\U000021CC', - "rlm;": '\U0000200F', - "rmoust;": '\U000023B1', - "rmoustache;": '\U000023B1', - "rnmid;": '\U00002AEE', - "roang;": '\U000027ED', - "roarr;": '\U000021FE', - "robrk;": '\U000027E7', - "ropar;": '\U00002986', - "ropf;": '\U0001D563', - "roplus;": '\U00002A2E', - "rotimes;": '\U00002A35', - "rpar;": '\U00000029', - "rpargt;": '\U00002994', - "rppolint;": '\U00002A12', - "rrarr;": '\U000021C9', - "rsaquo;": '\U0000203A', - "rscr;": '\U0001D4C7', - "rsh;": '\U000021B1', - "rsqb;": '\U0000005D', - "rsquo;": '\U00002019', - "rsquor;": '\U00002019', - "rthree;": '\U000022CC', - "rtimes;": '\U000022CA', - "rtri;": '\U000025B9', - "rtrie;": '\U000022B5', - "rtrif;": '\U000025B8', - "rtriltri;": '\U000029CE', - "ruluhar;": '\U00002968', - "rx;": '\U0000211E', - "sacute;": '\U0000015B', - "sbquo;": '\U0000201A', - "sc;": '\U0000227B', - "scE;": '\U00002AB4', - "scap;": '\U00002AB8', - "scaron;": '\U00000161', - "sccue;": '\U0000227D', - "sce;": '\U00002AB0', - "scedil;": '\U0000015F', - "scirc;": '\U0000015D', - "scnE;": '\U00002AB6', - "scnap;": '\U00002ABA', - "scnsim;": '\U000022E9', - "scpolint;": '\U00002A13', - "scsim;": '\U0000227F', - "scy;": '\U00000441', - "sdot;": '\U000022C5', - "sdotb;": '\U000022A1', - "sdote;": '\U00002A66', - "seArr;": '\U000021D8', - "searhk;": '\U00002925', - "searr;": '\U00002198', - "searrow;": '\U00002198', - "sect;": '\U000000A7', - "semi;": '\U0000003B', - "seswar;": '\U00002929', - "setminus;": '\U00002216', - "setmn;": '\U00002216', - "sext;": '\U00002736', - "sfr;": '\U0001D530', - "sfrown;": '\U00002322', - "sharp;": '\U0000266F', - "shchcy;": '\U00000449', - "shcy;": '\U00000448', - "shortmid;": '\U00002223', - "shortparallel;": '\U00002225', - "shy;": '\U000000AD', - "sigma;": '\U000003C3', - "sigmaf;": '\U000003C2', - "sigmav;": '\U000003C2', - "sim;": '\U0000223C', - "simdot;": '\U00002A6A', - "sime;": '\U00002243', - "simeq;": '\U00002243', - "simg;": '\U00002A9E', - "simgE;": '\U00002AA0', - "siml;": '\U00002A9D', - "simlE;": '\U00002A9F', - "simne;": '\U00002246', - "simplus;": '\U00002A24', - "simrarr;": '\U00002972', - "slarr;": '\U00002190', - "smallsetminus;": '\U00002216', - "smashp;": '\U00002A33', - "smeparsl;": '\U000029E4', - "smid;": '\U00002223', - "smile;": '\U00002323', - "smt;": '\U00002AAA', - "smte;": '\U00002AAC', - "softcy;": '\U0000044C', - "sol;": '\U0000002F', - "solb;": '\U000029C4', - "solbar;": '\U0000233F', - "sopf;": '\U0001D564', - "spades;": '\U00002660', - "spadesuit;": '\U00002660', - "spar;": '\U00002225', - "sqcap;": '\U00002293', - "sqcup;": '\U00002294', - "sqsub;": '\U0000228F', - "sqsube;": '\U00002291', - "sqsubset;": '\U0000228F', - "sqsubseteq;": '\U00002291', - "sqsup;": '\U00002290', - "sqsupe;": '\U00002292', - "sqsupset;": '\U00002290', - "sqsupseteq;": '\U00002292', - "squ;": '\U000025A1', - "square;": '\U000025A1', - "squarf;": '\U000025AA', - "squf;": '\U000025AA', - "srarr;": '\U00002192', - "sscr;": '\U0001D4C8', - "ssetmn;": '\U00002216', - "ssmile;": '\U00002323', - "sstarf;": '\U000022C6', - "star;": '\U00002606', - "starf;": '\U00002605', - "straightepsilon;": '\U000003F5', - "straightphi;": '\U000003D5', - "strns;": '\U000000AF', - "sub;": '\U00002282', - "subE;": '\U00002AC5', - "subdot;": '\U00002ABD', - "sube;": '\U00002286', - "subedot;": '\U00002AC3', - "submult;": '\U00002AC1', - "subnE;": '\U00002ACB', - "subne;": '\U0000228A', - "subplus;": '\U00002ABF', - "subrarr;": '\U00002979', - "subset;": '\U00002282', - "subseteq;": '\U00002286', - "subseteqq;": '\U00002AC5', - "subsetneq;": '\U0000228A', - "subsetneqq;": '\U00002ACB', - "subsim;": '\U00002AC7', - "subsub;": '\U00002AD5', - "subsup;": '\U00002AD3', - "succ;": '\U0000227B', - "succapprox;": '\U00002AB8', - "succcurlyeq;": '\U0000227D', - "succeq;": '\U00002AB0', - "succnapprox;": '\U00002ABA', - "succneqq;": '\U00002AB6', - "succnsim;": '\U000022E9', - "succsim;": '\U0000227F', - "sum;": '\U00002211', - "sung;": '\U0000266A', - "sup;": '\U00002283', - "sup1;": '\U000000B9', - "sup2;": '\U000000B2', - "sup3;": '\U000000B3', - "supE;": '\U00002AC6', - "supdot;": '\U00002ABE', - "supdsub;": '\U00002AD8', - "supe;": '\U00002287', - "supedot;": '\U00002AC4', - "suphsol;": '\U000027C9', - "suphsub;": '\U00002AD7', - "suplarr;": '\U0000297B', - "supmult;": '\U00002AC2', - "supnE;": '\U00002ACC', - "supne;": '\U0000228B', - "supplus;": '\U00002AC0', - "supset;": '\U00002283', - "supseteq;": '\U00002287', - "supseteqq;": '\U00002AC6', - "supsetneq;": '\U0000228B', - "supsetneqq;": '\U00002ACC', - "supsim;": '\U00002AC8', - "supsub;": '\U00002AD4', - "supsup;": '\U00002AD6', - "swArr;": '\U000021D9', - "swarhk;": '\U00002926', - "swarr;": '\U00002199', - "swarrow;": '\U00002199', - "swnwar;": '\U0000292A', - "szlig;": '\U000000DF', - "target;": '\U00002316', - "tau;": '\U000003C4', - "tbrk;": '\U000023B4', - "tcaron;": '\U00000165', - "tcedil;": '\U00000163', - "tcy;": '\U00000442', - "tdot;": '\U000020DB', - "telrec;": '\U00002315', - "tfr;": '\U0001D531', - "there4;": '\U00002234', - "therefore;": '\U00002234', - "theta;": '\U000003B8', - "thetasym;": '\U000003D1', - "thetav;": '\U000003D1', - "thickapprox;": '\U00002248', - "thicksim;": '\U0000223C', - "thinsp;": '\U00002009', - "thkap;": '\U00002248', - "thksim;": '\U0000223C', - "thorn;": '\U000000FE', - "tilde;": '\U000002DC', - "times;": '\U000000D7', - "timesb;": '\U000022A0', - "timesbar;": '\U00002A31', - "timesd;": '\U00002A30', - "tint;": '\U0000222D', - "toea;": '\U00002928', - "top;": '\U000022A4', - "topbot;": '\U00002336', - "topcir;": '\U00002AF1', - "topf;": '\U0001D565', - "topfork;": '\U00002ADA', - "tosa;": '\U00002929', - "tprime;": '\U00002034', - "trade;": '\U00002122', - "triangle;": '\U000025B5', - "triangledown;": '\U000025BF', - "triangleleft;": '\U000025C3', - "trianglelefteq;": '\U000022B4', - "triangleq;": '\U0000225C', - "triangleright;": '\U000025B9', - "trianglerighteq;": '\U000022B5', - "tridot;": '\U000025EC', - "trie;": '\U0000225C', - "triminus;": '\U00002A3A', - "triplus;": '\U00002A39', - "trisb;": '\U000029CD', - "tritime;": '\U00002A3B', - "trpezium;": '\U000023E2', - "tscr;": '\U0001D4C9', - "tscy;": '\U00000446', - "tshcy;": '\U0000045B', - "tstrok;": '\U00000167', - "twixt;": '\U0000226C', - "twoheadleftarrow;": '\U0000219E', - "twoheadrightarrow;": '\U000021A0', - "uArr;": '\U000021D1', - "uHar;": '\U00002963', - "uacute;": '\U000000FA', - "uarr;": '\U00002191', - "ubrcy;": '\U0000045E', - "ubreve;": '\U0000016D', - "ucirc;": '\U000000FB', - "ucy;": '\U00000443', - "udarr;": '\U000021C5', - "udblac;": '\U00000171', - "udhar;": '\U0000296E', - "ufisht;": '\U0000297E', - "ufr;": '\U0001D532', - "ugrave;": '\U000000F9', - "uharl;": '\U000021BF', - "uharr;": '\U000021BE', - "uhblk;": '\U00002580', - "ulcorn;": '\U0000231C', - "ulcorner;": '\U0000231C', - "ulcrop;": '\U0000230F', - "ultri;": '\U000025F8', - "umacr;": '\U0000016B', - "uml;": '\U000000A8', - "uogon;": '\U00000173', - "uopf;": '\U0001D566', - "uparrow;": '\U00002191', - "updownarrow;": '\U00002195', - "upharpoonleft;": '\U000021BF', - "upharpoonright;": '\U000021BE', - "uplus;": '\U0000228E', - "upsi;": '\U000003C5', - "upsih;": '\U000003D2', - "upsilon;": '\U000003C5', - "upuparrows;": '\U000021C8', - "urcorn;": '\U0000231D', - "urcorner;": '\U0000231D', - "urcrop;": '\U0000230E', - "uring;": '\U0000016F', - "urtri;": '\U000025F9', - "uscr;": '\U0001D4CA', - "utdot;": '\U000022F0', - "utilde;": '\U00000169', - "utri;": '\U000025B5', - "utrif;": '\U000025B4', - "uuarr;": '\U000021C8', - "uuml;": '\U000000FC', - "uwangle;": '\U000029A7', - "vArr;": '\U000021D5', - "vBar;": '\U00002AE8', - "vBarv;": '\U00002AE9', - "vDash;": '\U000022A8', - "vangrt;": '\U0000299C', - "varepsilon;": '\U000003F5', - "varkappa;": '\U000003F0', - "varnothing;": '\U00002205', - "varphi;": '\U000003D5', - "varpi;": '\U000003D6', - "varpropto;": '\U0000221D', - "varr;": '\U00002195', - "varrho;": '\U000003F1', - "varsigma;": '\U000003C2', - "vartheta;": '\U000003D1', - "vartriangleleft;": '\U000022B2', - "vartriangleright;": '\U000022B3', - "vcy;": '\U00000432', - "vdash;": '\U000022A2', - "vee;": '\U00002228', - "veebar;": '\U000022BB', - "veeeq;": '\U0000225A', - "vellip;": '\U000022EE', - "verbar;": '\U0000007C', - "vert;": '\U0000007C', - "vfr;": '\U0001D533', - "vltri;": '\U000022B2', - "vopf;": '\U0001D567', - "vprop;": '\U0000221D', - "vrtri;": '\U000022B3', - "vscr;": '\U0001D4CB', - "vzigzag;": '\U0000299A', - "wcirc;": '\U00000175', - "wedbar;": '\U00002A5F', - "wedge;": '\U00002227', - "wedgeq;": '\U00002259', - "weierp;": '\U00002118', - "wfr;": '\U0001D534', - "wopf;": '\U0001D568', - "wp;": '\U00002118', - "wr;": '\U00002240', - "wreath;": '\U00002240', - "wscr;": '\U0001D4CC', - "xcap;": '\U000022C2', - "xcirc;": '\U000025EF', - "xcup;": '\U000022C3', - "xdtri;": '\U000025BD', - "xfr;": '\U0001D535', - "xhArr;": '\U000027FA', - "xharr;": '\U000027F7', - "xi;": '\U000003BE', - "xlArr;": '\U000027F8', - "xlarr;": '\U000027F5', - "xmap;": '\U000027FC', - "xnis;": '\U000022FB', - "xodot;": '\U00002A00', - "xopf;": '\U0001D569', - "xoplus;": '\U00002A01', - "xotime;": '\U00002A02', - "xrArr;": '\U000027F9', - "xrarr;": '\U000027F6', - "xscr;": '\U0001D4CD', - "xsqcup;": '\U00002A06', - "xuplus;": '\U00002A04', - "xutri;": '\U000025B3', - "xvee;": '\U000022C1', - "xwedge;": '\U000022C0', - "yacute;": '\U000000FD', - "yacy;": '\U0000044F', - "ycirc;": '\U00000177', - "ycy;": '\U0000044B', - "yen;": '\U000000A5', - "yfr;": '\U0001D536', - "yicy;": '\U00000457', - "yopf;": '\U0001D56A', - "yscr;": '\U0001D4CE', - "yucy;": '\U0000044E', - "yuml;": '\U000000FF', - "zacute;": '\U0000017A', - "zcaron;": '\U0000017E', - "zcy;": '\U00000437', - "zdot;": '\U0000017C', - "zeetrf;": '\U00002128', - "zeta;": '\U000003B6', - "zfr;": '\U0001D537', - "zhcy;": '\U00000436', - "zigrarr;": '\U000021DD', - "zopf;": '\U0001D56B', - "zscr;": '\U0001D4CF', - "zwj;": '\U0000200D', - "zwnj;": '\U0000200C', - "AElig": '\U000000C6', - "AMP": '\U00000026', - "Aacute": '\U000000C1', - "Acirc": '\U000000C2', - "Agrave": '\U000000C0', - "Aring": '\U000000C5', - "Atilde": '\U000000C3', - "Auml": '\U000000C4', - "COPY": '\U000000A9', - "Ccedil": '\U000000C7', - "ETH": '\U000000D0', - "Eacute": '\U000000C9', - "Ecirc": '\U000000CA', - "Egrave": '\U000000C8', - "Euml": '\U000000CB', - "GT": '\U0000003E', - "Iacute": '\U000000CD', - "Icirc": '\U000000CE', - "Igrave": '\U000000CC', - "Iuml": '\U000000CF', - "LT": '\U0000003C', - "Ntilde": '\U000000D1', - "Oacute": '\U000000D3', - "Ocirc": '\U000000D4', - "Ograve": '\U000000D2', - "Oslash": '\U000000D8', - "Otilde": '\U000000D5', - "Ouml": '\U000000D6', - "QUOT": '\U00000022', - "REG": '\U000000AE', - "THORN": '\U000000DE', - "Uacute": '\U000000DA', - "Ucirc": '\U000000DB', - "Ugrave": '\U000000D9', - "Uuml": '\U000000DC', - "Yacute": '\U000000DD', - "aacute": '\U000000E1', - "acirc": '\U000000E2', - "acute": '\U000000B4', - "aelig": '\U000000E6', - "agrave": '\U000000E0', - "amp": '\U00000026', - "aring": '\U000000E5', - "atilde": '\U000000E3', - "auml": '\U000000E4', - "brvbar": '\U000000A6', - "ccedil": '\U000000E7', - "cedil": '\U000000B8', - "cent": '\U000000A2', - "copy": '\U000000A9', - "curren": '\U000000A4', - "deg": '\U000000B0', - "divide": '\U000000F7', - "eacute": '\U000000E9', - "ecirc": '\U000000EA', - "egrave": '\U000000E8', - "eth": '\U000000F0', - "euml": '\U000000EB', - "frac12": '\U000000BD', - "frac14": '\U000000BC', - "frac34": '\U000000BE', - "gt": '\U0000003E', - "iacute": '\U000000ED', - "icirc": '\U000000EE', - "iexcl": '\U000000A1', - "igrave": '\U000000EC', - "iquest": '\U000000BF', - "iuml": '\U000000EF', - "laquo": '\U000000AB', - "lt": '\U0000003C', - "macr": '\U000000AF', - "micro": '\U000000B5', - "middot": '\U000000B7', - "nbsp": '\U000000A0', - "not": '\U000000AC', - "ntilde": '\U000000F1', - "oacute": '\U000000F3', - "ocirc": '\U000000F4', - "ograve": '\U000000F2', - "ordf": '\U000000AA', - "ordm": '\U000000BA', - "oslash": '\U000000F8', - "otilde": '\U000000F5', - "ouml": '\U000000F6', - "para": '\U000000B6', - "plusmn": '\U000000B1', - "pound": '\U000000A3', - "quot": '\U00000022', - "raquo": '\U000000BB', - "reg": '\U000000AE', - "sect": '\U000000A7', - "shy": '\U000000AD', - "sup1": '\U000000B9', - "sup2": '\U000000B2', - "sup3": '\U000000B3', - "szlig": '\U000000DF', - "thorn": '\U000000FE', - "times": '\U000000D7', - "uacute": '\U000000FA', - "ucirc": '\U000000FB', - "ugrave": '\U000000F9', - "uml": '\U000000A8', - "uuml": '\U000000FC', - "yacute": '\U000000FD', - "yen": '\U000000A5', - "yuml": '\U000000FF', + "Cross;": '\U00002A2F', + "Cscr;": '\U0001D49E', + "Cup;": '\U000022D3', + "CupCap;": '\U0000224D', + "DD;": '\U00002145', + "DDotrahd;": '\U00002911', + "DJcy;": '\U00000402', + "DScy;": '\U00000405', + "DZcy;": '\U0000040F', + "Dagger;": '\U00002021', + "Darr;": '\U000021A1', + "Dashv;": '\U00002AE4', + "Dcaron;": '\U0000010E', + "Dcy;": '\U00000414', + "Del;": '\U00002207', + "Delta;": '\U00000394', + "Dfr;": '\U0001D507', + "DiacriticalAcute;": '\U000000B4', + "DiacriticalDot;": '\U000002D9', + "DiacriticalDoubleAcute;": '\U000002DD', + "DiacriticalGrave;": '\U00000060', + "DiacriticalTilde;": '\U000002DC', + "Diamond;": '\U000022C4', + "DifferentialD;": '\U00002146', + "Dopf;": '\U0001D53B', + "Dot;": '\U000000A8', + "DotDot;": '\U000020DC', + "DotEqual;": '\U00002250', + "DoubleContourIntegral;": '\U0000222F', + "DoubleDot;": '\U000000A8', + "DoubleDownArrow;": '\U000021D3', + "DoubleLeftArrow;": '\U000021D0', + "DoubleLeftRightArrow;": '\U000021D4', + "DoubleLeftTee;": '\U00002AE4', + "DoubleLongLeftArrow;": '\U000027F8', + "DoubleLongLeftRightArrow;": '\U000027FA', + "DoubleLongRightArrow;": '\U000027F9', + "DoubleRightArrow;": '\U000021D2', + "DoubleRightTee;": '\U000022A8', + "DoubleUpArrow;": '\U000021D1', + "DoubleUpDownArrow;": '\U000021D5', + "DoubleVerticalBar;": '\U00002225', + "DownArrow;": '\U00002193', + "DownArrowBar;": '\U00002913', + "DownArrowUpArrow;": '\U000021F5', + "DownBreve;": '\U00000311', + "DownLeftRightVector;": '\U00002950', + "DownLeftTeeVector;": '\U0000295E', + "DownLeftVector;": '\U000021BD', + "DownLeftVectorBar;": '\U00002956', + "DownRightTeeVector;": '\U0000295F', + "DownRightVector;": '\U000021C1', + "DownRightVectorBar;": '\U00002957', + "DownTee;": '\U000022A4', + "DownTeeArrow;": '\U000021A7', + "Downarrow;": '\U000021D3', + "Dscr;": '\U0001D49F', + "Dstrok;": '\U00000110', + "ENG;": '\U0000014A', + "ETH;": '\U000000D0', + "Eacute;": '\U000000C9', + "Ecaron;": '\U0000011A', + "Ecirc;": '\U000000CA', + "Ecy;": '\U0000042D', + "Edot;": '\U00000116', + "Efr;": '\U0001D508', + "Egrave;": '\U000000C8', + "Element;": '\U00002208', + "Emacr;": '\U00000112', + "EmptySmallSquare;": '\U000025FB', + "EmptyVerySmallSquare;": '\U000025AB', + "Eogon;": '\U00000118', + "Eopf;": '\U0001D53C', + "Epsilon;": '\U00000395', + "Equal;": '\U00002A75', + "EqualTilde;": '\U00002242', + "Equilibrium;": '\U000021CC', + "Escr;": '\U00002130', + "Esim;": '\U00002A73', + "Eta;": '\U00000397', + "Euml;": '\U000000CB', + "Exists;": '\U00002203', + "ExponentialE;": '\U00002147', + "Fcy;": '\U00000424', + "Ffr;": '\U0001D509', + "FilledSmallSquare;": '\U000025FC', + "FilledVerySmallSquare;": '\U000025AA', + "Fopf;": '\U0001D53D', + "ForAll;": '\U00002200', + "Fouriertrf;": '\U00002131', + "Fscr;": '\U00002131', + "GJcy;": '\U00000403', + "GT;": '\U0000003E', + "Gamma;": '\U00000393', + "Gammad;": '\U000003DC', + "Gbreve;": '\U0000011E', + "Gcedil;": '\U00000122', + "Gcirc;": '\U0000011C', + "Gcy;": '\U00000413', + "Gdot;": '\U00000120', + "Gfr;": '\U0001D50A', + "Gg;": '\U000022D9', + "Gopf;": '\U0001D53E', + "GreaterEqual;": '\U00002265', + "GreaterEqualLess;": '\U000022DB', + "GreaterFullEqual;": '\U00002267', + "GreaterGreater;": '\U00002AA2', + "GreaterLess;": '\U00002277', + "GreaterSlantEqual;": '\U00002A7E', + "GreaterTilde;": '\U00002273', + "Gscr;": '\U0001D4A2', + "Gt;": '\U0000226B', + "HARDcy;": '\U0000042A', + "Hacek;": '\U000002C7', + "Hat;": '\U0000005E', + "Hcirc;": '\U00000124', + "Hfr;": '\U0000210C', + "HilbertSpace;": '\U0000210B', + "Hopf;": '\U0000210D', + "HorizontalLine;": '\U00002500', + "Hscr;": '\U0000210B', + "Hstrok;": '\U00000126', + "HumpDownHump;": '\U0000224E', + "HumpEqual;": '\U0000224F', + "IEcy;": '\U00000415', + "IJlig;": '\U00000132', + "IOcy;": '\U00000401', + "Iacute;": '\U000000CD', + "Icirc;": '\U000000CE', + "Icy;": '\U00000418', + "Idot;": '\U00000130', + "Ifr;": '\U00002111', + "Igrave;": '\U000000CC', + "Im;": '\U00002111', + "Imacr;": '\U0000012A', + "ImaginaryI;": '\U00002148', + "Implies;": '\U000021D2', + "Int;": '\U0000222C', + "Integral;": '\U0000222B', + "Intersection;": '\U000022C2', + "InvisibleComma;": '\U00002063', + "InvisibleTimes;": '\U00002062', + "Iogon;": '\U0000012E', + "Iopf;": '\U0001D540', + "Iota;": '\U00000399', + "Iscr;": '\U00002110', + "Itilde;": '\U00000128', + "Iukcy;": '\U00000406', + "Iuml;": '\U000000CF', + "Jcirc;": '\U00000134', + "Jcy;": '\U00000419', + "Jfr;": '\U0001D50D', + "Jopf;": '\U0001D541', + "Jscr;": '\U0001D4A5', + "Jsercy;": '\U00000408', + "Jukcy;": '\U00000404', + "KHcy;": '\U00000425', + "KJcy;": '\U0000040C', + "Kappa;": '\U0000039A', + "Kcedil;": '\U00000136', + "Kcy;": '\U0000041A', + "Kfr;": '\U0001D50E', + "Kopf;": '\U0001D542', + "Kscr;": '\U0001D4A6', + "LJcy;": '\U00000409', + "LT;": '\U0000003C', + "Lacute;": '\U00000139', + "Lambda;": '\U0000039B', + "Lang;": '\U000027EA', + "Laplacetrf;": '\U00002112', + "Larr;": '\U0000219E', + "Lcaron;": '\U0000013D', + "Lcedil;": '\U0000013B', + "Lcy;": '\U0000041B', + "LeftAngleBracket;": '\U000027E8', + "LeftArrow;": '\U00002190', + "LeftArrowBar;": '\U000021E4', + "LeftArrowRightArrow;": '\U000021C6', + "LeftCeiling;": '\U00002308', + "LeftDoubleBracket;": '\U000027E6', + "LeftDownTeeVector;": '\U00002961', + "LeftDownVector;": '\U000021C3', + "LeftDownVectorBar;": '\U00002959', + "LeftFloor;": '\U0000230A', + "LeftRightArrow;": '\U00002194', + "LeftRightVector;": '\U0000294E', + "LeftTee;": '\U000022A3', + "LeftTeeArrow;": '\U000021A4', + "LeftTeeVector;": '\U0000295A', + "LeftTriangle;": '\U000022B2', + "LeftTriangleBar;": '\U000029CF', + "LeftTriangleEqual;": '\U000022B4', + "LeftUpDownVector;": '\U00002951', + "LeftUpTeeVector;": '\U00002960', + "LeftUpVector;": '\U000021BF', + "LeftUpVectorBar;": '\U00002958', + "LeftVector;": '\U000021BC', + "LeftVectorBar;": '\U00002952', + "Leftarrow;": '\U000021D0', + "Leftrightarrow;": '\U000021D4', + "LessEqualGreater;": '\U000022DA', + "LessFullEqual;": '\U00002266', + "LessGreater;": '\U00002276', + "LessLess;": '\U00002AA1', + "LessSlantEqual;": '\U00002A7D', + "LessTilde;": '\U00002272', + "Lfr;": '\U0001D50F', + "Ll;": '\U000022D8', + "Lleftarrow;": '\U000021DA', + "Lmidot;": '\U0000013F', + "LongLeftArrow;": '\U000027F5', + "LongLeftRightArrow;": '\U000027F7', + "LongRightArrow;": '\U000027F6', + "Longleftarrow;": '\U000027F8', + "Longleftrightarrow;": '\U000027FA', + "Longrightarrow;": '\U000027F9', + "Lopf;": '\U0001D543', + "LowerLeftArrow;": '\U00002199', + "LowerRightArrow;": '\U00002198', + "Lscr;": '\U00002112', + "Lsh;": '\U000021B0', + "Lstrok;": '\U00000141', + "Lt;": '\U0000226A', + "Map;": '\U00002905', + "Mcy;": '\U0000041C', + "MediumSpace;": '\U0000205F', + "Mellintrf;": '\U00002133', + "Mfr;": '\U0001D510', + "MinusPlus;": '\U00002213', + "Mopf;": '\U0001D544', + "Mscr;": '\U00002133', + "Mu;": '\U0000039C', + "NJcy;": '\U0000040A', + "Nacute;": '\U00000143', + "Ncaron;": '\U00000147', + "Ncedil;": '\U00000145', + "Ncy;": '\U0000041D', + "NegativeMediumSpace;": '\U0000200B', + "NegativeThickSpace;": '\U0000200B', + "NegativeThinSpace;": '\U0000200B', + "NegativeVeryThinSpace;": '\U0000200B', + "NestedGreaterGreater;": '\U0000226B', + "NestedLessLess;": '\U0000226A', + "NewLine;": '\U0000000A', + "Nfr;": '\U0001D511', + "NoBreak;": '\U00002060', + "NonBreakingSpace;": '\U000000A0', + "Nopf;": '\U00002115', + "Not;": '\U00002AEC', + "NotCongruent;": '\U00002262', + "NotCupCap;": '\U0000226D', + "NotDoubleVerticalBar;": '\U00002226', + "NotElement;": '\U00002209', + "NotEqual;": '\U00002260', + "NotExists;": '\U00002204', + "NotGreater;": '\U0000226F', + "NotGreaterEqual;": '\U00002271', + "NotGreaterLess;": '\U00002279', + "NotGreaterTilde;": '\U00002275', + "NotLeftTriangle;": '\U000022EA', + "NotLeftTriangleEqual;": '\U000022EC', + "NotLess;": '\U0000226E', + "NotLessEqual;": '\U00002270', + "NotLessGreater;": '\U00002278', + "NotLessTilde;": '\U00002274', + "NotPrecedes;": '\U00002280', + "NotPrecedesSlantEqual;": '\U000022E0', + "NotReverseElement;": '\U0000220C', + "NotRightTriangle;": '\U000022EB', + "NotRightTriangleEqual;": '\U000022ED', + "NotSquareSubsetEqual;": '\U000022E2', + "NotSquareSupersetEqual;": '\U000022E3', + "NotSubsetEqual;": '\U00002288', + "NotSucceeds;": '\U00002281', + "NotSucceedsSlantEqual;": '\U000022E1', + "NotSupersetEqual;": '\U00002289', + "NotTilde;": '\U00002241', + "NotTildeEqual;": '\U00002244', + "NotTildeFullEqual;": '\U00002247', + "NotTildeTilde;": '\U00002249', + "NotVerticalBar;": '\U00002224', + "Nscr;": '\U0001D4A9', + "Ntilde;": '\U000000D1', + "Nu;": '\U0000039D', + "OElig;": '\U00000152', + "Oacute;": '\U000000D3', + "Ocirc;": '\U000000D4', + "Ocy;": '\U0000041E', + "Odblac;": '\U00000150', + "Ofr;": '\U0001D512', + "Ograve;": '\U000000D2', + "Omacr;": '\U0000014C', + "Omega;": '\U000003A9', + "Omicron;": '\U0000039F', + "Oopf;": '\U0001D546', + "OpenCurlyDoubleQuote;": '\U0000201C', + "OpenCurlyQuote;": '\U00002018', + "Or;": '\U00002A54', + "Oscr;": '\U0001D4AA', + "Oslash;": '\U000000D8', + "Otilde;": '\U000000D5', + "Otimes;": '\U00002A37', + "Ouml;": '\U000000D6', + "OverBar;": '\U0000203E', + "OverBrace;": '\U000023DE', + "OverBracket;": '\U000023B4', + "OverParenthesis;": '\U000023DC', + "PartialD;": '\U00002202', + "Pcy;": '\U0000041F', + "Pfr;": '\U0001D513', + "Phi;": '\U000003A6', + "Pi;": '\U000003A0', + "PlusMinus;": '\U000000B1', + "Poincareplane;": '\U0000210C', + "Popf;": '\U00002119', + "Pr;": '\U00002ABB', + "Precedes;": '\U0000227A', + "PrecedesEqual;": '\U00002AAF', + "PrecedesSlantEqual;": '\U0000227C', + "PrecedesTilde;": '\U0000227E', + "Prime;": '\U00002033', + "Product;": '\U0000220F', + "Proportion;": '\U00002237', + "Proportional;": '\U0000221D', + "Pscr;": '\U0001D4AB', + "Psi;": '\U000003A8', + "QUOT;": '\U00000022', + "Qfr;": '\U0001D514', + "Qopf;": '\U0000211A', + "Qscr;": '\U0001D4AC', + "RBarr;": '\U00002910', + "REG;": '\U000000AE', + "Racute;": '\U00000154', + "Rang;": '\U000027EB', + "Rarr;": '\U000021A0', + "Rarrtl;": '\U00002916', + "Rcaron;": '\U00000158', + "Rcedil;": '\U00000156', + "Rcy;": '\U00000420', + "Re;": '\U0000211C', + "ReverseElement;": '\U0000220B', + "ReverseEquilibrium;": '\U000021CB', + "ReverseUpEquilibrium;": '\U0000296F', + "Rfr;": '\U0000211C', + "Rho;": '\U000003A1', + "RightAngleBracket;": '\U000027E9', + "RightArrow;": '\U00002192', + "RightArrowBar;": '\U000021E5', + "RightArrowLeftArrow;": '\U000021C4', + "RightCeiling;": '\U00002309', + "RightDoubleBracket;": '\U000027E7', + "RightDownTeeVector;": '\U0000295D', + "RightDownVector;": '\U000021C2', + "RightDownVectorBar;": '\U00002955', + "RightFloor;": '\U0000230B', + "RightTee;": '\U000022A2', + "RightTeeArrow;": '\U000021A6', + "RightTeeVector;": '\U0000295B', + "RightTriangle;": '\U000022B3', + "RightTriangleBar;": '\U000029D0', + "RightTriangleEqual;": '\U000022B5', + "RightUpDownVector;": '\U0000294F', + "RightUpTeeVector;": '\U0000295C', + "RightUpVector;": '\U000021BE', + "RightUpVectorBar;": '\U00002954', + "RightVector;": '\U000021C0', + "RightVectorBar;": '\U00002953', + "Rightarrow;": '\U000021D2', + "Ropf;": '\U0000211D', + "RoundImplies;": '\U00002970', + "Rrightarrow;": '\U000021DB', + "Rscr;": '\U0000211B', + "Rsh;": '\U000021B1', + "RuleDelayed;": '\U000029F4', + "SHCHcy;": '\U00000429', + "SHcy;": '\U00000428', + "SOFTcy;": '\U0000042C', + "Sacute;": '\U0000015A', + "Sc;": '\U00002ABC', + "Scaron;": '\U00000160', + "Scedil;": '\U0000015E', + "Scirc;": '\U0000015C', + "Scy;": '\U00000421', + "Sfr;": '\U0001D516', + "ShortDownArrow;": '\U00002193', + "ShortLeftArrow;": '\U00002190', + "ShortRightArrow;": '\U00002192', + "ShortUpArrow;": '\U00002191', + "Sigma;": '\U000003A3', + "SmallCircle;": '\U00002218', + "Sopf;": '\U0001D54A', + "Sqrt;": '\U0000221A', + "Square;": '\U000025A1', + "SquareIntersection;": '\U00002293', + "SquareSubset;": '\U0000228F', + "SquareSubsetEqual;": '\U00002291', + "SquareSuperset;": '\U00002290', + "SquareSupersetEqual;": '\U00002292', + "SquareUnion;": '\U00002294', + "Sscr;": '\U0001D4AE', + "Star;": '\U000022C6', + "Sub;": '\U000022D0', + "Subset;": '\U000022D0', + "SubsetEqual;": '\U00002286', + "Succeeds;": '\U0000227B', + "SucceedsEqual;": '\U00002AB0', + "SucceedsSlantEqual;": '\U0000227D', + "SucceedsTilde;": '\U0000227F', + "SuchThat;": '\U0000220B', + "Sum;": '\U00002211', + "Sup;": '\U000022D1', + "Superset;": '\U00002283', + "SupersetEqual;": '\U00002287', + "Supset;": '\U000022D1', + "THORN;": '\U000000DE', + "TRADE;": '\U00002122', + "TSHcy;": '\U0000040B', + "TScy;": '\U00000426', + "Tab;": '\U00000009', + "Tau;": '\U000003A4', + "Tcaron;": '\U00000164', + "Tcedil;": '\U00000162', + "Tcy;": '\U00000422', + "Tfr;": '\U0001D517', + "Therefore;": '\U00002234', + "Theta;": '\U00000398', + "ThinSpace;": '\U00002009', + "Tilde;": '\U0000223C', + "TildeEqual;": '\U00002243', + "TildeFullEqual;": '\U00002245', + "TildeTilde;": '\U00002248', + "Topf;": '\U0001D54B', + "TripleDot;": '\U000020DB', + "Tscr;": '\U0001D4AF', + "Tstrok;": '\U00000166', + "Uacute;": '\U000000DA', + "Uarr;": '\U0000219F', + "Uarrocir;": '\U00002949', + "Ubrcy;": '\U0000040E', + "Ubreve;": '\U0000016C', + "Ucirc;": '\U000000DB', + "Ucy;": '\U00000423', + "Udblac;": '\U00000170', + "Ufr;": '\U0001D518', + "Ugrave;": '\U000000D9', + "Umacr;": '\U0000016A', + "UnderBar;": '\U0000005F', + "UnderBrace;": '\U000023DF', + "UnderBracket;": '\U000023B5', + "UnderParenthesis;": '\U000023DD', + "Union;": '\U000022C3', + "UnionPlus;": '\U0000228E', + "Uogon;": '\U00000172', + "Uopf;": '\U0001D54C', + "UpArrow;": '\U00002191', + "UpArrowBar;": '\U00002912', + "UpArrowDownArrow;": '\U000021C5', + "UpDownArrow;": '\U00002195', + "UpEquilibrium;": '\U0000296E', + "UpTee;": '\U000022A5', + "UpTeeArrow;": '\U000021A5', + "Uparrow;": '\U000021D1', + "Updownarrow;": '\U000021D5', + "UpperLeftArrow;": '\U00002196', + "UpperRightArrow;": '\U00002197', + "Upsi;": '\U000003D2', + "Upsilon;": '\U000003A5', + "Uring;": '\U0000016E', + "Uscr;": '\U0001D4B0', + "Utilde;": '\U00000168', + "Uuml;": '\U000000DC', + "VDash;": '\U000022AB', + "Vbar;": '\U00002AEB', + "Vcy;": '\U00000412', + "Vdash;": '\U000022A9', + "Vdashl;": '\U00002AE6', + "Vee;": '\U000022C1', + "Verbar;": '\U00002016', + "Vert;": '\U00002016', + "VerticalBar;": '\U00002223', + "VerticalLine;": '\U0000007C', + "VerticalSeparator;": '\U00002758', + "VerticalTilde;": '\U00002240', + "VeryThinSpace;": '\U0000200A', + "Vfr;": '\U0001D519', + "Vopf;": '\U0001D54D', + "Vscr;": '\U0001D4B1', + "Vvdash;": '\U000022AA', + "Wcirc;": '\U00000174', + "Wedge;": '\U000022C0', + "Wfr;": '\U0001D51A', + "Wopf;": '\U0001D54E', + "Wscr;": '\U0001D4B2', + "Xfr;": '\U0001D51B', + "Xi;": '\U0000039E', + "Xopf;": '\U0001D54F', + "Xscr;": '\U0001D4B3', + "YAcy;": '\U0000042F', + "YIcy;": '\U00000407', + "YUcy;": '\U0000042E', + "Yacute;": '\U000000DD', + "Ycirc;": '\U00000176', + "Ycy;": '\U0000042B', + "Yfr;": '\U0001D51C', + "Yopf;": '\U0001D550', + "Yscr;": '\U0001D4B4', + "Yuml;": '\U00000178', + "ZHcy;": '\U00000416', + "Zacute;": '\U00000179', + "Zcaron;": '\U0000017D', + "Zcy;": '\U00000417', + "Zdot;": '\U0000017B', + "ZeroWidthSpace;": '\U0000200B', + "Zeta;": '\U00000396', + "Zfr;": '\U00002128', + "Zopf;": '\U00002124', + "Zscr;": '\U0001D4B5', + "aacute;": '\U000000E1', + "abreve;": '\U00000103', + "ac;": '\U0000223E', + "acd;": '\U0000223F', + "acirc;": '\U000000E2', + "acute;": '\U000000B4', + "acy;": '\U00000430', + "aelig;": '\U000000E6', + "af;": '\U00002061', + "afr;": '\U0001D51E', + "agrave;": '\U000000E0', + "alefsym;": '\U00002135', + "aleph;": '\U00002135', + "alpha;": '\U000003B1', + "amacr;": '\U00000101', + "amalg;": '\U00002A3F', + "amp;": '\U00000026', + "and;": '\U00002227', + "andand;": '\U00002A55', + "andd;": '\U00002A5C', + "andslope;": '\U00002A58', + "andv;": '\U00002A5A', + "ang;": '\U00002220', + "ange;": '\U000029A4', + "angle;": '\U00002220', + "angmsd;": '\U00002221', + "angmsdaa;": '\U000029A8', + "angmsdab;": '\U000029A9', + "angmsdac;": '\U000029AA', + "angmsdad;": '\U000029AB', + "angmsdae;": '\U000029AC', + "angmsdaf;": '\U000029AD', + "angmsdag;": '\U000029AE', + "angmsdah;": '\U000029AF', + "angrt;": '\U0000221F', + "angrtvb;": '\U000022BE', + "angrtvbd;": '\U0000299D', + "angsph;": '\U00002222', + "angst;": '\U000000C5', + "angzarr;": '\U0000237C', + "aogon;": '\U00000105', + "aopf;": '\U0001D552', + "ap;": '\U00002248', + "apE;": '\U00002A70', + "apacir;": '\U00002A6F', + "ape;": '\U0000224A', + "apid;": '\U0000224B', + "apos;": '\U00000027', + "approx;": '\U00002248', + "approxeq;": '\U0000224A', + "aring;": '\U000000E5', + "ascr;": '\U0001D4B6', + "ast;": '\U0000002A', + "asymp;": '\U00002248', + "asympeq;": '\U0000224D', + "atilde;": '\U000000E3', + "auml;": '\U000000E4', + "awconint;": '\U00002233', + "awint;": '\U00002A11', + "bNot;": '\U00002AED', + "backcong;": '\U0000224C', + "backepsilon;": '\U000003F6', + "backprime;": '\U00002035', + "backsim;": '\U0000223D', + "backsimeq;": '\U000022CD', + "barvee;": '\U000022BD', + "barwed;": '\U00002305', + "barwedge;": '\U00002305', + "bbrk;": '\U000023B5', + "bbrktbrk;": '\U000023B6', + "bcong;": '\U0000224C', + "bcy;": '\U00000431', + "bdquo;": '\U0000201E', + "becaus;": '\U00002235', + "because;": '\U00002235', + "bemptyv;": '\U000029B0', + "bepsi;": '\U000003F6', + "bernou;": '\U0000212C', + "beta;": '\U000003B2', + "beth;": '\U00002136', + "between;": '\U0000226C', + "bfr;": '\U0001D51F', + "bigcap;": '\U000022C2', + "bigcirc;": '\U000025EF', + "bigcup;": '\U000022C3', + "bigodot;": '\U00002A00', + "bigoplus;": '\U00002A01', + "bigotimes;": '\U00002A02', + "bigsqcup;": '\U00002A06', + "bigstar;": '\U00002605', + "bigtriangledown;": '\U000025BD', + "bigtriangleup;": '\U000025B3', + "biguplus;": '\U00002A04', + "bigvee;": '\U000022C1', + "bigwedge;": '\U000022C0', + "bkarow;": '\U0000290D', + "blacklozenge;": '\U000029EB', + "blacksquare;": '\U000025AA', + "blacktriangle;": '\U000025B4', + "blacktriangledown;": '\U000025BE', + "blacktriangleleft;": '\U000025C2', + "blacktriangleright;": '\U000025B8', + "blank;": '\U00002423', + "blk12;": '\U00002592', + "blk14;": '\U00002591', + "blk34;": '\U00002593', + "block;": '\U00002588', + "bnot;": '\U00002310', + "bopf;": '\U0001D553', + "bot;": '\U000022A5', + "bottom;": '\U000022A5', + "bowtie;": '\U000022C8', + "boxDL;": '\U00002557', + "boxDR;": '\U00002554', + "boxDl;": '\U00002556', + "boxDr;": '\U00002553', + "boxH;": '\U00002550', + "boxHD;": '\U00002566', + "boxHU;": '\U00002569', + "boxHd;": '\U00002564', + "boxHu;": '\U00002567', + "boxUL;": '\U0000255D', + "boxUR;": '\U0000255A', + "boxUl;": '\U0000255C', + "boxUr;": '\U00002559', + "boxV;": '\U00002551', + "boxVH;": '\U0000256C', + "boxVL;": '\U00002563', + "boxVR;": '\U00002560', + "boxVh;": '\U0000256B', + "boxVl;": '\U00002562', + "boxVr;": '\U0000255F', + "boxbox;": '\U000029C9', + "boxdL;": '\U00002555', + "boxdR;": '\U00002552', + "boxdl;": '\U00002510', + "boxdr;": '\U0000250C', + "boxh;": '\U00002500', + "boxhD;": '\U00002565', + "boxhU;": '\U00002568', + "boxhd;": '\U0000252C', + "boxhu;": '\U00002534', + "boxminus;": '\U0000229F', + "boxplus;": '\U0000229E', + "boxtimes;": '\U000022A0', + "boxuL;": '\U0000255B', + "boxuR;": '\U00002558', + "boxul;": '\U00002518', + "boxur;": '\U00002514', + "boxv;": '\U00002502', + "boxvH;": '\U0000256A', + "boxvL;": '\U00002561', + "boxvR;": '\U0000255E', + "boxvh;": '\U0000253C', + "boxvl;": '\U00002524', + "boxvr;": '\U0000251C', + "bprime;": '\U00002035', + "breve;": '\U000002D8', + "brvbar;": '\U000000A6', + "bscr;": '\U0001D4B7', + "bsemi;": '\U0000204F', + "bsim;": '\U0000223D', + "bsime;": '\U000022CD', + "bsol;": '\U0000005C', + "bsolb;": '\U000029C5', + "bsolhsub;": '\U000027C8', + "bull;": '\U00002022', + "bullet;": '\U00002022', + "bump;": '\U0000224E', + "bumpE;": '\U00002AAE', + "bumpe;": '\U0000224F', + "bumpeq;": '\U0000224F', + "cacute;": '\U00000107', + "cap;": '\U00002229', + "capand;": '\U00002A44', + "capbrcup;": '\U00002A49', + "capcap;": '\U00002A4B', + "capcup;": '\U00002A47', + "capdot;": '\U00002A40', + "caret;": '\U00002041', + "caron;": '\U000002C7', + "ccaps;": '\U00002A4D', + "ccaron;": '\U0000010D', + "ccedil;": '\U000000E7', + "ccirc;": '\U00000109', + "ccups;": '\U00002A4C', + "ccupssm;": '\U00002A50', + "cdot;": '\U0000010B', + "cedil;": '\U000000B8', + "cemptyv;": '\U000029B2', + "cent;": '\U000000A2', + "centerdot;": '\U000000B7', + "cfr;": '\U0001D520', + "chcy;": '\U00000447', + "check;": '\U00002713', + "checkmark;": '\U00002713', + "chi;": '\U000003C7', + "cir;": '\U000025CB', + "cirE;": '\U000029C3', + "circ;": '\U000002C6', + "circeq;": '\U00002257', + "circlearrowleft;": '\U000021BA', + "circlearrowright;": '\U000021BB', + "circledR;": '\U000000AE', + "circledS;": '\U000024C8', + "circledast;": '\U0000229B', + "circledcirc;": '\U0000229A', + "circleddash;": '\U0000229D', + "cire;": '\U00002257', + "cirfnint;": '\U00002A10', + "cirmid;": '\U00002AEF', + "cirscir;": '\U000029C2', + "clubs;": '\U00002663', + "clubsuit;": '\U00002663', + "colon;": '\U0000003A', + "colone;": '\U00002254', + "coloneq;": '\U00002254', + "comma;": '\U0000002C', + "commat;": '\U00000040', + "comp;": '\U00002201', + "compfn;": '\U00002218', + "complement;": '\U00002201', + "complexes;": '\U00002102', + "cong;": '\U00002245', + "congdot;": '\U00002A6D', + "conint;": '\U0000222E', + "copf;": '\U0001D554', + "coprod;": '\U00002210', + "copy;": '\U000000A9', + "copysr;": '\U00002117', + "crarr;": '\U000021B5', + "cross;": '\U00002717', + "cscr;": '\U0001D4B8', + "csub;": '\U00002ACF', + "csube;": '\U00002AD1', + "csup;": '\U00002AD0', + "csupe;": '\U00002AD2', + "ctdot;": '\U000022EF', + "cudarrl;": '\U00002938', + "cudarrr;": '\U00002935', + "cuepr;": '\U000022DE', + "cuesc;": '\U000022DF', + "cularr;": '\U000021B6', + "cularrp;": '\U0000293D', + "cup;": '\U0000222A', + "cupbrcap;": '\U00002A48', + "cupcap;": '\U00002A46', + "cupcup;": '\U00002A4A', + "cupdot;": '\U0000228D', + "cupor;": '\U00002A45', + "curarr;": '\U000021B7', + "curarrm;": '\U0000293C', + "curlyeqprec;": '\U000022DE', + "curlyeqsucc;": '\U000022DF', + "curlyvee;": '\U000022CE', + "curlywedge;": '\U000022CF', + "curren;": '\U000000A4', + "curvearrowleft;": '\U000021B6', + "curvearrowright;": '\U000021B7', + "cuvee;": '\U000022CE', + "cuwed;": '\U000022CF', + "cwconint;": '\U00002232', + "cwint;": '\U00002231', + "cylcty;": '\U0000232D', + "dArr;": '\U000021D3', + "dHar;": '\U00002965', + "dagger;": '\U00002020', + "daleth;": '\U00002138', + "darr;": '\U00002193', + "dash;": '\U00002010', + "dashv;": '\U000022A3', + "dbkarow;": '\U0000290F', + "dblac;": '\U000002DD', + "dcaron;": '\U0000010F', + "dcy;": '\U00000434', + "dd;": '\U00002146', + "ddagger;": '\U00002021', + "ddarr;": '\U000021CA', + "ddotseq;": '\U00002A77', + "deg;": '\U000000B0', + "delta;": '\U000003B4', + "demptyv;": '\U000029B1', + "dfisht;": '\U0000297F', + "dfr;": '\U0001D521', + "dharl;": '\U000021C3', + "dharr;": '\U000021C2', + "diam;": '\U000022C4', + "diamond;": '\U000022C4', + "diamondsuit;": '\U00002666', + "diams;": '\U00002666', + "die;": '\U000000A8', + "digamma;": '\U000003DD', + "disin;": '\U000022F2', + "div;": '\U000000F7', + "divide;": '\U000000F7', + "divideontimes;": '\U000022C7', + "divonx;": '\U000022C7', + "djcy;": '\U00000452', + "dlcorn;": '\U0000231E', + "dlcrop;": '\U0000230D', + "dollar;": '\U00000024', + "dopf;": '\U0001D555', + "dot;": '\U000002D9', + "doteq;": '\U00002250', + "doteqdot;": '\U00002251', + "dotminus;": '\U00002238', + "dotplus;": '\U00002214', + "dotsquare;": '\U000022A1', + "doublebarwedge;": '\U00002306', + "downarrow;": '\U00002193', + "downdownarrows;": '\U000021CA', + "downharpoonleft;": '\U000021C3', + "downharpoonright;": '\U000021C2', + "drbkarow;": '\U00002910', + "drcorn;": '\U0000231F', + "drcrop;": '\U0000230C', + "dscr;": '\U0001D4B9', + "dscy;": '\U00000455', + "dsol;": '\U000029F6', + "dstrok;": '\U00000111', + "dtdot;": '\U000022F1', + "dtri;": '\U000025BF', + "dtrif;": '\U000025BE', + "duarr;": '\U000021F5', + "duhar;": '\U0000296F', + "dwangle;": '\U000029A6', + "dzcy;": '\U0000045F', + "dzigrarr;": '\U000027FF', + "eDDot;": '\U00002A77', + "eDot;": '\U00002251', + "eacute;": '\U000000E9', + "easter;": '\U00002A6E', + "ecaron;": '\U0000011B', + "ecir;": '\U00002256', + "ecirc;": '\U000000EA', + "ecolon;": '\U00002255', + "ecy;": '\U0000044D', + "edot;": '\U00000117', + "ee;": '\U00002147', + "efDot;": '\U00002252', + "efr;": '\U0001D522', + "eg;": '\U00002A9A', + "egrave;": '\U000000E8', + "egs;": '\U00002A96', + "egsdot;": '\U00002A98', + "el;": '\U00002A99', + "elinters;": '\U000023E7', + "ell;": '\U00002113', + "els;": '\U00002A95', + "elsdot;": '\U00002A97', + "emacr;": '\U00000113', + "empty;": '\U00002205', + "emptyset;": '\U00002205', + "emptyv;": '\U00002205', + "emsp;": '\U00002003', + "emsp13;": '\U00002004', + "emsp14;": '\U00002005', + "eng;": '\U0000014B', + "ensp;": '\U00002002', + "eogon;": '\U00000119', + "eopf;": '\U0001D556', + "epar;": '\U000022D5', + "eparsl;": '\U000029E3', + "eplus;": '\U00002A71', + "epsi;": '\U000003B5', + "epsilon;": '\U000003B5', + "epsiv;": '\U000003F5', + "eqcirc;": '\U00002256', + "eqcolon;": '\U00002255', + "eqsim;": '\U00002242', + "eqslantgtr;": '\U00002A96', + "eqslantless;": '\U00002A95', + "equals;": '\U0000003D', + "equest;": '\U0000225F', + "equiv;": '\U00002261', + "equivDD;": '\U00002A78', + "eqvparsl;": '\U000029E5', + "erDot;": '\U00002253', + "erarr;": '\U00002971', + "escr;": '\U0000212F', + "esdot;": '\U00002250', + "esim;": '\U00002242', + "eta;": '\U000003B7', + "eth;": '\U000000F0', + "euml;": '\U000000EB', + "euro;": '\U000020AC', + "excl;": '\U00000021', + "exist;": '\U00002203', + "expectation;": '\U00002130', + "exponentiale;": '\U00002147', + "fallingdotseq;": '\U00002252', + "fcy;": '\U00000444', + "female;": '\U00002640', + "ffilig;": '\U0000FB03', + "fflig;": '\U0000FB00', + "ffllig;": '\U0000FB04', + "ffr;": '\U0001D523', + "filig;": '\U0000FB01', + "flat;": '\U0000266D', + "fllig;": '\U0000FB02', + "fltns;": '\U000025B1', + "fnof;": '\U00000192', + "fopf;": '\U0001D557', + "forall;": '\U00002200', + "fork;": '\U000022D4', + "forkv;": '\U00002AD9', + "fpartint;": '\U00002A0D', + "frac12;": '\U000000BD', + "frac13;": '\U00002153', + "frac14;": '\U000000BC', + "frac15;": '\U00002155', + "frac16;": '\U00002159', + "frac18;": '\U0000215B', + "frac23;": '\U00002154', + "frac25;": '\U00002156', + "frac34;": '\U000000BE', + "frac35;": '\U00002157', + "frac38;": '\U0000215C', + "frac45;": '\U00002158', + "frac56;": '\U0000215A', + "frac58;": '\U0000215D', + "frac78;": '\U0000215E', + "frasl;": '\U00002044', + "frown;": '\U00002322', + "fscr;": '\U0001D4BB', + "gE;": '\U00002267', + "gEl;": '\U00002A8C', + "gacute;": '\U000001F5', + "gamma;": '\U000003B3', + "gammad;": '\U000003DD', + "gap;": '\U00002A86', + "gbreve;": '\U0000011F', + "gcirc;": '\U0000011D', + "gcy;": '\U00000433', + "gdot;": '\U00000121', + "ge;": '\U00002265', + "gel;": '\U000022DB', + "geq;": '\U00002265', + "geqq;": '\U00002267', + "geqslant;": '\U00002A7E', + "ges;": '\U00002A7E', + "gescc;": '\U00002AA9', + "gesdot;": '\U00002A80', + "gesdoto;": '\U00002A82', + "gesdotol;": '\U00002A84', + "gesles;": '\U00002A94', + "gfr;": '\U0001D524', + "gg;": '\U0000226B', + "ggg;": '\U000022D9', + "gimel;": '\U00002137', + "gjcy;": '\U00000453', + "gl;": '\U00002277', + "glE;": '\U00002A92', + "gla;": '\U00002AA5', + "glj;": '\U00002AA4', + "gnE;": '\U00002269', + "gnap;": '\U00002A8A', + "gnapprox;": '\U00002A8A', + "gne;": '\U00002A88', + "gneq;": '\U00002A88', + "gneqq;": '\U00002269', + "gnsim;": '\U000022E7', + "gopf;": '\U0001D558', + "grave;": '\U00000060', + "gscr;": '\U0000210A', + "gsim;": '\U00002273', + "gsime;": '\U00002A8E', + "gsiml;": '\U00002A90', + "gt;": '\U0000003E', + "gtcc;": '\U00002AA7', + "gtcir;": '\U00002A7A', + "gtdot;": '\U000022D7', + "gtlPar;": '\U00002995', + "gtquest;": '\U00002A7C', + "gtrapprox;": '\U00002A86', + "gtrarr;": '\U00002978', + "gtrdot;": '\U000022D7', + "gtreqless;": '\U000022DB', + "gtreqqless;": '\U00002A8C', + "gtrless;": '\U00002277', + "gtrsim;": '\U00002273', + "hArr;": '\U000021D4', + "hairsp;": '\U0000200A', + "half;": '\U000000BD', + "hamilt;": '\U0000210B', + "hardcy;": '\U0000044A', + "harr;": '\U00002194', + "harrcir;": '\U00002948', + "harrw;": '\U000021AD', + "hbar;": '\U0000210F', + "hcirc;": '\U00000125', + "hearts;": '\U00002665', + "heartsuit;": '\U00002665', + "hellip;": '\U00002026', + "hercon;": '\U000022B9', + "hfr;": '\U0001D525', + "hksearow;": '\U00002925', + "hkswarow;": '\U00002926', + "hoarr;": '\U000021FF', + "homtht;": '\U0000223B', + "hookleftarrow;": '\U000021A9', + "hookrightarrow;": '\U000021AA', + "hopf;": '\U0001D559', + "horbar;": '\U00002015', + "hscr;": '\U0001D4BD', + "hslash;": '\U0000210F', + "hstrok;": '\U00000127', + "hybull;": '\U00002043', + "hyphen;": '\U00002010', + "iacute;": '\U000000ED', + "ic;": '\U00002063', + "icirc;": '\U000000EE', + "icy;": '\U00000438', + "iecy;": '\U00000435', + "iexcl;": '\U000000A1', + "iff;": '\U000021D4', + "ifr;": '\U0001D526', + "igrave;": '\U000000EC', + "ii;": '\U00002148', + "iiiint;": '\U00002A0C', + "iiint;": '\U0000222D', + "iinfin;": '\U000029DC', + "iiota;": '\U00002129', + "ijlig;": '\U00000133', + "imacr;": '\U0000012B', + "image;": '\U00002111', + "imagline;": '\U00002110', + "imagpart;": '\U00002111', + "imath;": '\U00000131', + "imof;": '\U000022B7', + "imped;": '\U000001B5', + "in;": '\U00002208', + "incare;": '\U00002105', + "infin;": '\U0000221E', + "infintie;": '\U000029DD', + "inodot;": '\U00000131', + "int;": '\U0000222B', + "intcal;": '\U000022BA', + "integers;": '\U00002124', + "intercal;": '\U000022BA', + "intlarhk;": '\U00002A17', + "intprod;": '\U00002A3C', + "iocy;": '\U00000451', + "iogon;": '\U0000012F', + "iopf;": '\U0001D55A', + "iota;": '\U000003B9', + "iprod;": '\U00002A3C', + "iquest;": '\U000000BF', + "iscr;": '\U0001D4BE', + "isin;": '\U00002208', + "isinE;": '\U000022F9', + "isindot;": '\U000022F5', + "isins;": '\U000022F4', + "isinsv;": '\U000022F3', + "isinv;": '\U00002208', + "it;": '\U00002062', + "itilde;": '\U00000129', + "iukcy;": '\U00000456', + "iuml;": '\U000000EF', + "jcirc;": '\U00000135', + "jcy;": '\U00000439', + "jfr;": '\U0001D527', + "jmath;": '\U00000237', + "jopf;": '\U0001D55B', + "jscr;": '\U0001D4BF', + "jsercy;": '\U00000458', + "jukcy;": '\U00000454', + "kappa;": '\U000003BA', + "kappav;": '\U000003F0', + "kcedil;": '\U00000137', + "kcy;": '\U0000043A', + "kfr;": '\U0001D528', + "kgreen;": '\U00000138', + "khcy;": '\U00000445', + "kjcy;": '\U0000045C', + "kopf;": '\U0001D55C', + "kscr;": '\U0001D4C0', + "lAarr;": '\U000021DA', + "lArr;": '\U000021D0', + "lAtail;": '\U0000291B', + "lBarr;": '\U0000290E', + "lE;": '\U00002266', + "lEg;": '\U00002A8B', + "lHar;": '\U00002962', + "lacute;": '\U0000013A', + "laemptyv;": '\U000029B4', + "lagran;": '\U00002112', + "lambda;": '\U000003BB', + "lang;": '\U000027E8', + "langd;": '\U00002991', + "langle;": '\U000027E8', + "lap;": '\U00002A85', + "laquo;": '\U000000AB', + "larr;": '\U00002190', + "larrb;": '\U000021E4', + "larrbfs;": '\U0000291F', + "larrfs;": '\U0000291D', + "larrhk;": '\U000021A9', + "larrlp;": '\U000021AB', + "larrpl;": '\U00002939', + "larrsim;": '\U00002973', + "larrtl;": '\U000021A2', + "lat;": '\U00002AAB', + "latail;": '\U00002919', + "late;": '\U00002AAD', + "lbarr;": '\U0000290C', + "lbbrk;": '\U00002772', + "lbrace;": '\U0000007B', + "lbrack;": '\U0000005B', + "lbrke;": '\U0000298B', + "lbrksld;": '\U0000298F', + "lbrkslu;": '\U0000298D', + "lcaron;": '\U0000013E', + "lcedil;": '\U0000013C', + "lceil;": '\U00002308', + "lcub;": '\U0000007B', + "lcy;": '\U0000043B', + "ldca;": '\U00002936', + "ldquo;": '\U0000201C', + "ldquor;": '\U0000201E', + "ldrdhar;": '\U00002967', + "ldrushar;": '\U0000294B', + "ldsh;": '\U000021B2', + "le;": '\U00002264', + "leftarrow;": '\U00002190', + "leftarrowtail;": '\U000021A2', + "leftharpoondown;": '\U000021BD', + "leftharpoonup;": '\U000021BC', + "leftleftarrows;": '\U000021C7', + "leftrightarrow;": '\U00002194', + "leftrightarrows;": '\U000021C6', + "leftrightharpoons;": '\U000021CB', + "leftrightsquigarrow;": '\U000021AD', + "leftthreetimes;": '\U000022CB', + "leg;": '\U000022DA', + "leq;": '\U00002264', + "leqq;": '\U00002266', + "leqslant;": '\U00002A7D', + "les;": '\U00002A7D', + "lescc;": '\U00002AA8', + "lesdot;": '\U00002A7F', + "lesdoto;": '\U00002A81', + "lesdotor;": '\U00002A83', + "lesges;": '\U00002A93', + "lessapprox;": '\U00002A85', + "lessdot;": '\U000022D6', + "lesseqgtr;": '\U000022DA', + "lesseqqgtr;": '\U00002A8B', + "lessgtr;": '\U00002276', + "lesssim;": '\U00002272', + "lfisht;": '\U0000297C', + "lfloor;": '\U0000230A', + "lfr;": '\U0001D529', + "lg;": '\U00002276', + "lgE;": '\U00002A91', + "lhard;": '\U000021BD', + "lharu;": '\U000021BC', + "lharul;": '\U0000296A', + "lhblk;": '\U00002584', + "ljcy;": '\U00000459', + "ll;": '\U0000226A', + "llarr;": '\U000021C7', + "llcorner;": '\U0000231E', + "llhard;": '\U0000296B', + "lltri;": '\U000025FA', + "lmidot;": '\U00000140', + "lmoust;": '\U000023B0', + "lmoustache;": '\U000023B0', + "lnE;": '\U00002268', + "lnap;": '\U00002A89', + "lnapprox;": '\U00002A89', + "lne;": '\U00002A87', + "lneq;": '\U00002A87', + "lneqq;": '\U00002268', + "lnsim;": '\U000022E6', + "loang;": '\U000027EC', + "loarr;": '\U000021FD', + "lobrk;": '\U000027E6', + "longleftarrow;": '\U000027F5', + "longleftrightarrow;": '\U000027F7', + "longmapsto;": '\U000027FC', + "longrightarrow;": '\U000027F6', + "looparrowleft;": '\U000021AB', + "looparrowright;": '\U000021AC', + "lopar;": '\U00002985', + "lopf;": '\U0001D55D', + "loplus;": '\U00002A2D', + "lotimes;": '\U00002A34', + "lowast;": '\U00002217', + "lowbar;": '\U0000005F', + "loz;": '\U000025CA', + "lozenge;": '\U000025CA', + "lozf;": '\U000029EB', + "lpar;": '\U00000028', + "lparlt;": '\U00002993', + "lrarr;": '\U000021C6', + "lrcorner;": '\U0000231F', + "lrhar;": '\U000021CB', + "lrhard;": '\U0000296D', + "lrm;": '\U0000200E', + "lrtri;": '\U000022BF', + "lsaquo;": '\U00002039', + "lscr;": '\U0001D4C1', + "lsh;": '\U000021B0', + "lsim;": '\U00002272', + "lsime;": '\U00002A8D', + "lsimg;": '\U00002A8F', + "lsqb;": '\U0000005B', + "lsquo;": '\U00002018', + "lsquor;": '\U0000201A', + "lstrok;": '\U00000142', + "lt;": '\U0000003C', + "ltcc;": '\U00002AA6', + "ltcir;": '\U00002A79', + "ltdot;": '\U000022D6', + "lthree;": '\U000022CB', + "ltimes;": '\U000022C9', + "ltlarr;": '\U00002976', + "ltquest;": '\U00002A7B', + "ltrPar;": '\U00002996', + "ltri;": '\U000025C3', + "ltrie;": '\U000022B4', + "ltrif;": '\U000025C2', + "lurdshar;": '\U0000294A', + "luruhar;": '\U00002966', + "mDDot;": '\U0000223A', + "macr;": '\U000000AF', + "male;": '\U00002642', + "malt;": '\U00002720', + "maltese;": '\U00002720', + "map;": '\U000021A6', + "mapsto;": '\U000021A6', + "mapstodown;": '\U000021A7', + "mapstoleft;": '\U000021A4', + "mapstoup;": '\U000021A5', + "marker;": '\U000025AE', + "mcomma;": '\U00002A29', + "mcy;": '\U0000043C', + "mdash;": '\U00002014', + "measuredangle;": '\U00002221', + "mfr;": '\U0001D52A', + "mho;": '\U00002127', + "micro;": '\U000000B5', + "mid;": '\U00002223', + "midast;": '\U0000002A', + "midcir;": '\U00002AF0', + "middot;": '\U000000B7', + "minus;": '\U00002212', + "minusb;": '\U0000229F', + "minusd;": '\U00002238', + "minusdu;": '\U00002A2A', + "mlcp;": '\U00002ADB', + "mldr;": '\U00002026', + "mnplus;": '\U00002213', + "models;": '\U000022A7', + "mopf;": '\U0001D55E', + "mp;": '\U00002213', + "mscr;": '\U0001D4C2', + "mstpos;": '\U0000223E', + "mu;": '\U000003BC', + "multimap;": '\U000022B8', + "mumap;": '\U000022B8', + "nLeftarrow;": '\U000021CD', + "nLeftrightarrow;": '\U000021CE', + "nRightarrow;": '\U000021CF', + "nVDash;": '\U000022AF', + "nVdash;": '\U000022AE', + "nabla;": '\U00002207', + "nacute;": '\U00000144', + "nap;": '\U00002249', + "napos;": '\U00000149', + "napprox;": '\U00002249', + "natur;": '\U0000266E', + "natural;": '\U0000266E', + "naturals;": '\U00002115', + "nbsp;": '\U000000A0', + "ncap;": '\U00002A43', + "ncaron;": '\U00000148', + "ncedil;": '\U00000146', + "ncong;": '\U00002247', + "ncup;": '\U00002A42', + "ncy;": '\U0000043D', + "ndash;": '\U00002013', + "ne;": '\U00002260', + "neArr;": '\U000021D7', + "nearhk;": '\U00002924', + "nearr;": '\U00002197', + "nearrow;": '\U00002197', + "nequiv;": '\U00002262', + "nesear;": '\U00002928', + "nexist;": '\U00002204', + "nexists;": '\U00002204', + "nfr;": '\U0001D52B', + "nge;": '\U00002271', + "ngeq;": '\U00002271', + "ngsim;": '\U00002275', + "ngt;": '\U0000226F', + "ngtr;": '\U0000226F', + "nhArr;": '\U000021CE', + "nharr;": '\U000021AE', + "nhpar;": '\U00002AF2', + "ni;": '\U0000220B', + "nis;": '\U000022FC', + "nisd;": '\U000022FA', + "niv;": '\U0000220B', + "njcy;": '\U0000045A', + "nlArr;": '\U000021CD', + "nlarr;": '\U0000219A', + "nldr;": '\U00002025', + "nle;": '\U00002270', + "nleftarrow;": '\U0000219A', + "nleftrightarrow;": '\U000021AE', + "nleq;": '\U00002270', + "nless;": '\U0000226E', + "nlsim;": '\U00002274', + "nlt;": '\U0000226E', + "nltri;": '\U000022EA', + "nltrie;": '\U000022EC', + "nmid;": '\U00002224', + "nopf;": '\U0001D55F', + "not;": '\U000000AC', + "notin;": '\U00002209', + "notinva;": '\U00002209', + "notinvb;": '\U000022F7', + "notinvc;": '\U000022F6', + "notni;": '\U0000220C', + "notniva;": '\U0000220C', + "notnivb;": '\U000022FE', + "notnivc;": '\U000022FD', + "npar;": '\U00002226', + "nparallel;": '\U00002226', + "npolint;": '\U00002A14', + "npr;": '\U00002280', + "nprcue;": '\U000022E0', + "nprec;": '\U00002280', + "nrArr;": '\U000021CF', + "nrarr;": '\U0000219B', + "nrightarrow;": '\U0000219B', + "nrtri;": '\U000022EB', + "nrtrie;": '\U000022ED', + "nsc;": '\U00002281', + "nsccue;": '\U000022E1', + "nscr;": '\U0001D4C3', + "nshortmid;": '\U00002224', + "nshortparallel;": '\U00002226', + "nsim;": '\U00002241', + "nsime;": '\U00002244', + "nsimeq;": '\U00002244', + "nsmid;": '\U00002224', + "nspar;": '\U00002226', + "nsqsube;": '\U000022E2', + "nsqsupe;": '\U000022E3', + "nsub;": '\U00002284', + "nsube;": '\U00002288', + "nsubseteq;": '\U00002288', + "nsucc;": '\U00002281', + "nsup;": '\U00002285', + "nsupe;": '\U00002289', + "nsupseteq;": '\U00002289', + "ntgl;": '\U00002279', + "ntilde;": '\U000000F1', + "ntlg;": '\U00002278', + "ntriangleleft;": '\U000022EA', + "ntrianglelefteq;": '\U000022EC', + "ntriangleright;": '\U000022EB', + "ntrianglerighteq;": '\U000022ED', + "nu;": '\U000003BD', + "num;": '\U00000023', + "numero;": '\U00002116', + "numsp;": '\U00002007', + "nvDash;": '\U000022AD', + "nvHarr;": '\U00002904', + "nvdash;": '\U000022AC', + "nvinfin;": '\U000029DE', + "nvlArr;": '\U00002902', + "nvrArr;": '\U00002903', + "nwArr;": '\U000021D6', + "nwarhk;": '\U00002923', + "nwarr;": '\U00002196', + "nwarrow;": '\U00002196', + "nwnear;": '\U00002927', + "oS;": '\U000024C8', + "oacute;": '\U000000F3', + "oast;": '\U0000229B', + "ocir;": '\U0000229A', + "ocirc;": '\U000000F4', + "ocy;": '\U0000043E', + "odash;": '\U0000229D', + "odblac;": '\U00000151', + "odiv;": '\U00002A38', + "odot;": '\U00002299', + "odsold;": '\U000029BC', + "oelig;": '\U00000153', + "ofcir;": '\U000029BF', + "ofr;": '\U0001D52C', + "ogon;": '\U000002DB', + "ograve;": '\U000000F2', + "ogt;": '\U000029C1', + "ohbar;": '\U000029B5', + "ohm;": '\U000003A9', + "oint;": '\U0000222E', + "olarr;": '\U000021BA', + "olcir;": '\U000029BE', + "olcross;": '\U000029BB', + "oline;": '\U0000203E', + "olt;": '\U000029C0', + "omacr;": '\U0000014D', + "omega;": '\U000003C9', + "omicron;": '\U000003BF', + "omid;": '\U000029B6', + "ominus;": '\U00002296', + "oopf;": '\U0001D560', + "opar;": '\U000029B7', + "operp;": '\U000029B9', + "oplus;": '\U00002295', + "or;": '\U00002228', + "orarr;": '\U000021BB', + "ord;": '\U00002A5D', + "order;": '\U00002134', + "orderof;": '\U00002134', + "ordf;": '\U000000AA', + "ordm;": '\U000000BA', + "origof;": '\U000022B6', + "oror;": '\U00002A56', + "orslope;": '\U00002A57', + "orv;": '\U00002A5B', + "oscr;": '\U00002134', + "oslash;": '\U000000F8', + "osol;": '\U00002298', + "otilde;": '\U000000F5', + "otimes;": '\U00002297', + "otimesas;": '\U00002A36', + "ouml;": '\U000000F6', + "ovbar;": '\U0000233D', + "par;": '\U00002225', + "para;": '\U000000B6', + "parallel;": '\U00002225', + "parsim;": '\U00002AF3', + "parsl;": '\U00002AFD', + "part;": '\U00002202', + "pcy;": '\U0000043F', + "percnt;": '\U00000025', + "period;": '\U0000002E', + "permil;": '\U00002030', + "perp;": '\U000022A5', + "pertenk;": '\U00002031', + "pfr;": '\U0001D52D', + "phi;": '\U000003C6', + "phiv;": '\U000003D5', + "phmmat;": '\U00002133', + "phone;": '\U0000260E', + "pi;": '\U000003C0', + "pitchfork;": '\U000022D4', + "piv;": '\U000003D6', + "planck;": '\U0000210F', + "planckh;": '\U0000210E', + "plankv;": '\U0000210F', + "plus;": '\U0000002B', + "plusacir;": '\U00002A23', + "plusb;": '\U0000229E', + "pluscir;": '\U00002A22', + "plusdo;": '\U00002214', + "plusdu;": '\U00002A25', + "pluse;": '\U00002A72', + "plusmn;": '\U000000B1', + "plussim;": '\U00002A26', + "plustwo;": '\U00002A27', + "pm;": '\U000000B1', + "pointint;": '\U00002A15', + "popf;": '\U0001D561', + "pound;": '\U000000A3', + "pr;": '\U0000227A', + "prE;": '\U00002AB3', + "prap;": '\U00002AB7', + "prcue;": '\U0000227C', + "pre;": '\U00002AAF', + "prec;": '\U0000227A', + "precapprox;": '\U00002AB7', + "preccurlyeq;": '\U0000227C', + "preceq;": '\U00002AAF', + "precnapprox;": '\U00002AB9', + "precneqq;": '\U00002AB5', + "precnsim;": '\U000022E8', + "precsim;": '\U0000227E', + "prime;": '\U00002032', + "primes;": '\U00002119', + "prnE;": '\U00002AB5', + "prnap;": '\U00002AB9', + "prnsim;": '\U000022E8', + "prod;": '\U0000220F', + "profalar;": '\U0000232E', + "profline;": '\U00002312', + "profsurf;": '\U00002313', + "prop;": '\U0000221D', + "propto;": '\U0000221D', + "prsim;": '\U0000227E', + "prurel;": '\U000022B0', + "pscr;": '\U0001D4C5', + "psi;": '\U000003C8', + "puncsp;": '\U00002008', + "qfr;": '\U0001D52E', + "qint;": '\U00002A0C', + "qopf;": '\U0001D562', + "qprime;": '\U00002057', + "qscr;": '\U0001D4C6', + "quaternions;": '\U0000210D', + "quatint;": '\U00002A16', + "quest;": '\U0000003F', + "questeq;": '\U0000225F', + "quot;": '\U00000022', + "rAarr;": '\U000021DB', + "rArr;": '\U000021D2', + "rAtail;": '\U0000291C', + "rBarr;": '\U0000290F', + "rHar;": '\U00002964', + "racute;": '\U00000155', + "radic;": '\U0000221A', + "raemptyv;": '\U000029B3', + "rang;": '\U000027E9', + "rangd;": '\U00002992', + "range;": '\U000029A5', + "rangle;": '\U000027E9', + "raquo;": '\U000000BB', + "rarr;": '\U00002192', + "rarrap;": '\U00002975', + "rarrb;": '\U000021E5', + "rarrbfs;": '\U00002920', + "rarrc;": '\U00002933', + "rarrfs;": '\U0000291E', + "rarrhk;": '\U000021AA', + "rarrlp;": '\U000021AC', + "rarrpl;": '\U00002945', + "rarrsim;": '\U00002974', + "rarrtl;": '\U000021A3', + "rarrw;": '\U0000219D', + "ratail;": '\U0000291A', + "ratio;": '\U00002236', + "rationals;": '\U0000211A', + "rbarr;": '\U0000290D', + "rbbrk;": '\U00002773', + "rbrace;": '\U0000007D', + "rbrack;": '\U0000005D', + "rbrke;": '\U0000298C', + "rbrksld;": '\U0000298E', + "rbrkslu;": '\U00002990', + "rcaron;": '\U00000159', + "rcedil;": '\U00000157', + "rceil;": '\U00002309', + "rcub;": '\U0000007D', + "rcy;": '\U00000440', + "rdca;": '\U00002937', + "rdldhar;": '\U00002969', + "rdquo;": '\U0000201D', + "rdquor;": '\U0000201D', + "rdsh;": '\U000021B3', + "real;": '\U0000211C', + "realine;": '\U0000211B', + "realpart;": '\U0000211C', + "reals;": '\U0000211D', + "rect;": '\U000025AD', + "reg;": '\U000000AE', + "rfisht;": '\U0000297D', + "rfloor;": '\U0000230B', + "rfr;": '\U0001D52F', + "rhard;": '\U000021C1', + "rharu;": '\U000021C0', + "rharul;": '\U0000296C', + "rho;": '\U000003C1', + "rhov;": '\U000003F1', + "rightarrow;": '\U00002192', + "rightarrowtail;": '\U000021A3', + "rightharpoondown;": '\U000021C1', + "rightharpoonup;": '\U000021C0', + "rightleftarrows;": '\U000021C4', + "rightleftharpoons;": '\U000021CC', + "rightrightarrows;": '\U000021C9', + "rightsquigarrow;": '\U0000219D', + "rightthreetimes;": '\U000022CC', + "ring;": '\U000002DA', + "risingdotseq;": '\U00002253', + "rlarr;": '\U000021C4', + "rlhar;": '\U000021CC', + "rlm;": '\U0000200F', + "rmoust;": '\U000023B1', + "rmoustache;": '\U000023B1', + "rnmid;": '\U00002AEE', + "roang;": '\U000027ED', + "roarr;": '\U000021FE', + "robrk;": '\U000027E7', + "ropar;": '\U00002986', + "ropf;": '\U0001D563', + "roplus;": '\U00002A2E', + "rotimes;": '\U00002A35', + "rpar;": '\U00000029', + "rpargt;": '\U00002994', + "rppolint;": '\U00002A12', + "rrarr;": '\U000021C9', + "rsaquo;": '\U0000203A', + "rscr;": '\U0001D4C7', + "rsh;": '\U000021B1', + "rsqb;": '\U0000005D', + "rsquo;": '\U00002019', + "rsquor;": '\U00002019', + "rthree;": '\U000022CC', + "rtimes;": '\U000022CA', + "rtri;": '\U000025B9', + "rtrie;": '\U000022B5', + "rtrif;": '\U000025B8', + "rtriltri;": '\U000029CE', + "ruluhar;": '\U00002968', + "rx;": '\U0000211E', + "sacute;": '\U0000015B', + "sbquo;": '\U0000201A', + "sc;": '\U0000227B', + "scE;": '\U00002AB4', + "scap;": '\U00002AB8', + "scaron;": '\U00000161', + "sccue;": '\U0000227D', + "sce;": '\U00002AB0', + "scedil;": '\U0000015F', + "scirc;": '\U0000015D', + "scnE;": '\U00002AB6', + "scnap;": '\U00002ABA', + "scnsim;": '\U000022E9', + "scpolint;": '\U00002A13', + "scsim;": '\U0000227F', + "scy;": '\U00000441', + "sdot;": '\U000022C5', + "sdotb;": '\U000022A1', + "sdote;": '\U00002A66', + "seArr;": '\U000021D8', + "searhk;": '\U00002925', + "searr;": '\U00002198', + "searrow;": '\U00002198', + "sect;": '\U000000A7', + "semi;": '\U0000003B', + "seswar;": '\U00002929', + "setminus;": '\U00002216', + "setmn;": '\U00002216', + "sext;": '\U00002736', + "sfr;": '\U0001D530', + "sfrown;": '\U00002322', + "sharp;": '\U0000266F', + "shchcy;": '\U00000449', + "shcy;": '\U00000448', + "shortmid;": '\U00002223', + "shortparallel;": '\U00002225', + "shy;": '\U000000AD', + "sigma;": '\U000003C3', + "sigmaf;": '\U000003C2', + "sigmav;": '\U000003C2', + "sim;": '\U0000223C', + "simdot;": '\U00002A6A', + "sime;": '\U00002243', + "simeq;": '\U00002243', + "simg;": '\U00002A9E', + "simgE;": '\U00002AA0', + "siml;": '\U00002A9D', + "simlE;": '\U00002A9F', + "simne;": '\U00002246', + "simplus;": '\U00002A24', + "simrarr;": '\U00002972', + "slarr;": '\U00002190', + "smallsetminus;": '\U00002216', + "smashp;": '\U00002A33', + "smeparsl;": '\U000029E4', + "smid;": '\U00002223', + "smile;": '\U00002323', + "smt;": '\U00002AAA', + "smte;": '\U00002AAC', + "softcy;": '\U0000044C', + "sol;": '\U0000002F', + "solb;": '\U000029C4', + "solbar;": '\U0000233F', + "sopf;": '\U0001D564', + "spades;": '\U00002660', + "spadesuit;": '\U00002660', + "spar;": '\U00002225', + "sqcap;": '\U00002293', + "sqcup;": '\U00002294', + "sqsub;": '\U0000228F', + "sqsube;": '\U00002291', + "sqsubset;": '\U0000228F', + "sqsubseteq;": '\U00002291', + "sqsup;": '\U00002290', + "sqsupe;": '\U00002292', + "sqsupset;": '\U00002290', + "sqsupseteq;": '\U00002292', + "squ;": '\U000025A1', + "square;": '\U000025A1', + "squarf;": '\U000025AA', + "squf;": '\U000025AA', + "srarr;": '\U00002192', + "sscr;": '\U0001D4C8', + "ssetmn;": '\U00002216', + "ssmile;": '\U00002323', + "sstarf;": '\U000022C6', + "star;": '\U00002606', + "starf;": '\U00002605', + "straightepsilon;": '\U000003F5', + "straightphi;": '\U000003D5', + "strns;": '\U000000AF', + "sub;": '\U00002282', + "subE;": '\U00002AC5', + "subdot;": '\U00002ABD', + "sube;": '\U00002286', + "subedot;": '\U00002AC3', + "submult;": '\U00002AC1', + "subnE;": '\U00002ACB', + "subne;": '\U0000228A', + "subplus;": '\U00002ABF', + "subrarr;": '\U00002979', + "subset;": '\U00002282', + "subseteq;": '\U00002286', + "subseteqq;": '\U00002AC5', + "subsetneq;": '\U0000228A', + "subsetneqq;": '\U00002ACB', + "subsim;": '\U00002AC7', + "subsub;": '\U00002AD5', + "subsup;": '\U00002AD3', + "succ;": '\U0000227B', + "succapprox;": '\U00002AB8', + "succcurlyeq;": '\U0000227D', + "succeq;": '\U00002AB0', + "succnapprox;": '\U00002ABA', + "succneqq;": '\U00002AB6', + "succnsim;": '\U000022E9', + "succsim;": '\U0000227F', + "sum;": '\U00002211', + "sung;": '\U0000266A', + "sup;": '\U00002283', + "sup1;": '\U000000B9', + "sup2;": '\U000000B2', + "sup3;": '\U000000B3', + "supE;": '\U00002AC6', + "supdot;": '\U00002ABE', + "supdsub;": '\U00002AD8', + "supe;": '\U00002287', + "supedot;": '\U00002AC4', + "suphsol;": '\U000027C9', + "suphsub;": '\U00002AD7', + "suplarr;": '\U0000297B', + "supmult;": '\U00002AC2', + "supnE;": '\U00002ACC', + "supne;": '\U0000228B', + "supplus;": '\U00002AC0', + "supset;": '\U00002283', + "supseteq;": '\U00002287', + "supseteqq;": '\U00002AC6', + "supsetneq;": '\U0000228B', + "supsetneqq;": '\U00002ACC', + "supsim;": '\U00002AC8', + "supsub;": '\U00002AD4', + "supsup;": '\U00002AD6', + "swArr;": '\U000021D9', + "swarhk;": '\U00002926', + "swarr;": '\U00002199', + "swarrow;": '\U00002199', + "swnwar;": '\U0000292A', + "szlig;": '\U000000DF', + "target;": '\U00002316', + "tau;": '\U000003C4', + "tbrk;": '\U000023B4', + "tcaron;": '\U00000165', + "tcedil;": '\U00000163', + "tcy;": '\U00000442', + "tdot;": '\U000020DB', + "telrec;": '\U00002315', + "tfr;": '\U0001D531', + "there4;": '\U00002234', + "therefore;": '\U00002234', + "theta;": '\U000003B8', + "thetasym;": '\U000003D1', + "thetav;": '\U000003D1', + "thickapprox;": '\U00002248', + "thicksim;": '\U0000223C', + "thinsp;": '\U00002009', + "thkap;": '\U00002248', + "thksim;": '\U0000223C', + "thorn;": '\U000000FE', + "tilde;": '\U000002DC', + "times;": '\U000000D7', + "timesb;": '\U000022A0', + "timesbar;": '\U00002A31', + "timesd;": '\U00002A30', + "tint;": '\U0000222D', + "toea;": '\U00002928', + "top;": '\U000022A4', + "topbot;": '\U00002336', + "topcir;": '\U00002AF1', + "topf;": '\U0001D565', + "topfork;": '\U00002ADA', + "tosa;": '\U00002929', + "tprime;": '\U00002034', + "trade;": '\U00002122', + "triangle;": '\U000025B5', + "triangledown;": '\U000025BF', + "triangleleft;": '\U000025C3', + "trianglelefteq;": '\U000022B4', + "triangleq;": '\U0000225C', + "triangleright;": '\U000025B9', + "trianglerighteq;": '\U000022B5', + "tridot;": '\U000025EC', + "trie;": '\U0000225C', + "triminus;": '\U00002A3A', + "triplus;": '\U00002A39', + "trisb;": '\U000029CD', + "tritime;": '\U00002A3B', + "trpezium;": '\U000023E2', + "tscr;": '\U0001D4C9', + "tscy;": '\U00000446', + "tshcy;": '\U0000045B', + "tstrok;": '\U00000167', + "twixt;": '\U0000226C', + "twoheadleftarrow;": '\U0000219E', + "twoheadrightarrow;": '\U000021A0', + "uArr;": '\U000021D1', + "uHar;": '\U00002963', + "uacute;": '\U000000FA', + "uarr;": '\U00002191', + "ubrcy;": '\U0000045E', + "ubreve;": '\U0000016D', + "ucirc;": '\U000000FB', + "ucy;": '\U00000443', + "udarr;": '\U000021C5', + "udblac;": '\U00000171', + "udhar;": '\U0000296E', + "ufisht;": '\U0000297E', + "ufr;": '\U0001D532', + "ugrave;": '\U000000F9', + "uharl;": '\U000021BF', + "uharr;": '\U000021BE', + "uhblk;": '\U00002580', + "ulcorn;": '\U0000231C', + "ulcorner;": '\U0000231C', + "ulcrop;": '\U0000230F', + "ultri;": '\U000025F8', + "umacr;": '\U0000016B', + "uml;": '\U000000A8', + "uogon;": '\U00000173', + "uopf;": '\U0001D566', + "uparrow;": '\U00002191', + "updownarrow;": '\U00002195', + "upharpoonleft;": '\U000021BF', + "upharpoonright;": '\U000021BE', + "uplus;": '\U0000228E', + "upsi;": '\U000003C5', + "upsih;": '\U000003D2', + "upsilon;": '\U000003C5', + "upuparrows;": '\U000021C8', + "urcorn;": '\U0000231D', + "urcorner;": '\U0000231D', + "urcrop;": '\U0000230E', + "uring;": '\U0000016F', + "urtri;": '\U000025F9', + "uscr;": '\U0001D4CA', + "utdot;": '\U000022F0', + "utilde;": '\U00000169', + "utri;": '\U000025B5', + "utrif;": '\U000025B4', + "uuarr;": '\U000021C8', + "uuml;": '\U000000FC', + "uwangle;": '\U000029A7', + "vArr;": '\U000021D5', + "vBar;": '\U00002AE8', + "vBarv;": '\U00002AE9', + "vDash;": '\U000022A8', + "vangrt;": '\U0000299C', + "varepsilon;": '\U000003F5', + "varkappa;": '\U000003F0', + "varnothing;": '\U00002205', + "varphi;": '\U000003D5', + "varpi;": '\U000003D6', + "varpropto;": '\U0000221D', + "varr;": '\U00002195', + "varrho;": '\U000003F1', + "varsigma;": '\U000003C2', + "vartheta;": '\U000003D1', + "vartriangleleft;": '\U000022B2', + "vartriangleright;": '\U000022B3', + "vcy;": '\U00000432', + "vdash;": '\U000022A2', + "vee;": '\U00002228', + "veebar;": '\U000022BB', + "veeeq;": '\U0000225A', + "vellip;": '\U000022EE', + "verbar;": '\U0000007C', + "vert;": '\U0000007C', + "vfr;": '\U0001D533', + "vltri;": '\U000022B2', + "vopf;": '\U0001D567', + "vprop;": '\U0000221D', + "vrtri;": '\U000022B3', + "vscr;": '\U0001D4CB', + "vzigzag;": '\U0000299A', + "wcirc;": '\U00000175', + "wedbar;": '\U00002A5F', + "wedge;": '\U00002227', + "wedgeq;": '\U00002259', + "weierp;": '\U00002118', + "wfr;": '\U0001D534', + "wopf;": '\U0001D568', + "wp;": '\U00002118', + "wr;": '\U00002240', + "wreath;": '\U00002240', + "wscr;": '\U0001D4CC', + "xcap;": '\U000022C2', + "xcirc;": '\U000025EF', + "xcup;": '\U000022C3', + "xdtri;": '\U000025BD', + "xfr;": '\U0001D535', + "xhArr;": '\U000027FA', + "xharr;": '\U000027F7', + "xi;": '\U000003BE', + "xlArr;": '\U000027F8', + "xlarr;": '\U000027F5', + "xmap;": '\U000027FC', + "xnis;": '\U000022FB', + "xodot;": '\U00002A00', + "xopf;": '\U0001D569', + "xoplus;": '\U00002A01', + "xotime;": '\U00002A02', + "xrArr;": '\U000027F9', + "xrarr;": '\U000027F6', + "xscr;": '\U0001D4CD', + "xsqcup;": '\U00002A06', + "xuplus;": '\U00002A04', + "xutri;": '\U000025B3', + "xvee;": '\U000022C1', + "xwedge;": '\U000022C0', + "yacute;": '\U000000FD', + "yacy;": '\U0000044F', + "ycirc;": '\U00000177', + "ycy;": '\U0000044B', + "yen;": '\U000000A5', + "yfr;": '\U0001D536', + "yicy;": '\U00000457', + "yopf;": '\U0001D56A', + "yscr;": '\U0001D4CE', + "yucy;": '\U0000044E', + "yuml;": '\U000000FF', + "zacute;": '\U0000017A', + "zcaron;": '\U0000017E', + "zcy;": '\U00000437', + "zdot;": '\U0000017C', + "zeetrf;": '\U00002128', + "zeta;": '\U000003B6', + "zfr;": '\U0001D537', + "zhcy;": '\U00000436', + "zigrarr;": '\U000021DD', + "zopf;": '\U0001D56B', + "zscr;": '\U0001D4CF', + "zwj;": '\U0000200D', + "zwnj;": '\U0000200C', + "AElig": '\U000000C6', + "AMP": '\U00000026', + "Aacute": '\U000000C1', + "Acirc": '\U000000C2', + "Agrave": '\U000000C0', + "Aring": '\U000000C5', + "Atilde": '\U000000C3', + "Auml": '\U000000C4', + "COPY": '\U000000A9', + "Ccedil": '\U000000C7', + "ETH": '\U000000D0', + "Eacute": '\U000000C9', + "Ecirc": '\U000000CA', + "Egrave": '\U000000C8', + "Euml": '\U000000CB', + "GT": '\U0000003E', + "Iacute": '\U000000CD', + "Icirc": '\U000000CE', + "Igrave": '\U000000CC', + "Iuml": '\U000000CF', + "LT": '\U0000003C', + "Ntilde": '\U000000D1', + "Oacute": '\U000000D3', + "Ocirc": '\U000000D4', + "Ograve": '\U000000D2', + "Oslash": '\U000000D8', + "Otilde": '\U000000D5', + "Ouml": '\U000000D6', + "QUOT": '\U00000022', + "REG": '\U000000AE', + "THORN": '\U000000DE', + "Uacute": '\U000000DA', + "Ucirc": '\U000000DB', + "Ugrave": '\U000000D9', + "Uuml": '\U000000DC', + "Yacute": '\U000000DD', + "aacute": '\U000000E1', + "acirc": '\U000000E2', + "acute": '\U000000B4', + "aelig": '\U000000E6', + "agrave": '\U000000E0', + "amp": '\U00000026', + "aring": '\U000000E5', + "atilde": '\U000000E3', + "auml": '\U000000E4', + "brvbar": '\U000000A6', + "ccedil": '\U000000E7', + "cedil": '\U000000B8', + "cent": '\U000000A2', + "copy": '\U000000A9', + "curren": '\U000000A4', + "deg": '\U000000B0', + "divide": '\U000000F7', + "eacute": '\U000000E9', + "ecirc": '\U000000EA', + "egrave": '\U000000E8', + "eth": '\U000000F0', + "euml": '\U000000EB', + "frac12": '\U000000BD', + "frac14": '\U000000BC', + "frac34": '\U000000BE', + "gt": '\U0000003E', + "iacute": '\U000000ED', + "icirc": '\U000000EE', + "iexcl": '\U000000A1', + "igrave": '\U000000EC', + "iquest": '\U000000BF', + "iuml": '\U000000EF', + "laquo": '\U000000AB', + "lt": '\U0000003C', + "macr": '\U000000AF', + "micro": '\U000000B5', + "middot": '\U000000B7', + "nbsp": '\U000000A0', + "not": '\U000000AC', + "ntilde": '\U000000F1', + "oacute": '\U000000F3', + "ocirc": '\U000000F4', + "ograve": '\U000000F2', + "ordf": '\U000000AA', + "ordm": '\U000000BA', + "oslash": '\U000000F8', + "otilde": '\U000000F5', + "ouml": '\U000000F6', + "para": '\U000000B6', + "plusmn": '\U000000B1', + "pound": '\U000000A3', + "quot": '\U00000022', + "raquo": '\U000000BB', + "reg": '\U000000AE', + "sect": '\U000000A7', + "shy": '\U000000AD', + "sup1": '\U000000B9', + "sup2": '\U000000B2', + "sup3": '\U000000B3', + "szlig": '\U000000DF', + "thorn": '\U000000FE', + "times": '\U000000D7', + "uacute": '\U000000FA', + "ucirc": '\U000000FB', + "ugrave": '\U000000F9', + "uml": '\U000000A8', + "uuml": '\U000000FC', + "yacute": '\U000000FD', + "yen": '\U000000A5', + "yuml": '\U000000FF', } // HTML entities that are two unicode codepoints. diff --git a/src/pkg/html/escape.go b/src/pkg/html/escape.go index 24cb7af85..eff0384e0 100644 --- a/src/pkg/html/escape.go +++ b/src/pkg/html/escape.go @@ -15,7 +15,7 @@ type writer interface { WriteString(string) (int, error) } -// These replacements permit compatibility with old numeric entities that +// These replacements permit compatibility with old numeric entities that // assumed Windows-1252 encoding. // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference var replacementTable = [...]rune{ @@ -51,7 +51,7 @@ var replacementTable = [...]rune{ '\u009D', '\u017E', '\u0178', // Last entry is 0x9F. - // 0x00->'\uFFFD' is handled programmatically. + // 0x00->'\uFFFD' is handled programmatically. // 0x0D->'\u000D' is a no-op. } diff --git a/src/pkg/html/template/content.go b/src/pkg/html/template/content.go index c1bd2e494..9d1f74f6f 100644 --- a/src/pkg/html/template/content.go +++ b/src/pkg/html/template/content.go @@ -30,7 +30,7 @@ type ( HTMLAttr string // JS encapsulates a known safe EcmaScript5 Expression, for example, - // `(x + y * z())`. + // `(x + y * z())`. // Template authors are responsible for ensuring that typed expressions // do not break the intended precedence and that there is no // statement/expression ambiguity as when passing an expression like @@ -47,7 +47,7 @@ type ( // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not. JSStr string - // URL encapsulates a known safe URL as defined in RFC 3896. + // URL encapsulates a known safe URL or URL substring (see RFC 3986). // A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()` // from a trusted source should go in the page, but by default dynamic // `javascript:` URLs are filtered out since they are a frequently diff --git a/src/pkg/html/template/doc.go b/src/pkg/html/template/doc.go index f470facfd..d422ada37 100644 --- a/src/pkg/html/template/doc.go +++ b/src/pkg/html/template/doc.go @@ -119,7 +119,7 @@ If {{.}} is the innocuous word, `left`, then it can appear more widely, Non-string values can be used in JavaScript contexts. If {{.}} is - []struct{A,B string}{ "foo", "bar" } + struct{A,B string}{ "foo", "bar" } in the escaped template diff --git a/src/pkg/html/template/error.go b/src/pkg/html/template/error.go index dcac74896..46e49ccf8 100644 --- a/src/pkg/html/template/error.go +++ b/src/pkg/html/template/error.go @@ -102,7 +102,7 @@ const ( // // {{define "main"}} {{end}} // {{define "helper"}} document.write('
var x = ['firstValue,'secondValue] ErrRangeLoopReentry diff --git a/src/pkg/html/template/escape.go b/src/pkg/html/template/escape.go index 5f0e28e8c..4829bfcc4 100644 --- a/src/pkg/html/template/escape.go +++ b/src/pkg/html/template/escape.go @@ -220,10 +220,7 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { idents := p.Cmds for i := n - 1; i >= 0; i-- { if cmd := p.Cmds[i]; len(cmd.Args) != 0 { - if id, ok := cmd.Args[0].(*parse.IdentifierNode); ok { - if id.Ident == "noescape" { - return - } + if _, ok := cmd.Args[0].(*parse.IdentifierNode); ok { continue } } @@ -242,10 +239,11 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { copy(newCmds, p.Cmds) // Merge existing identifier commands with the sanitizers needed. for _, id := range idents { + pos := id.Args[0].Position() i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq) if i != -1 { for _, name := range s[:i] { - newCmds = appendCmd(newCmds, newIdentCmd(name)) + newCmds = appendCmd(newCmds, newIdentCmd(name, pos)) } s = s[i+1:] } @@ -253,7 +251,7 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) { } // Create any remaining sanitizers. for _, name := range s { - newCmds = appendCmd(newCmds, newIdentCmd(name)) + newCmds = appendCmd(newCmds, newIdentCmd(name, p.Position())) } p.Cmds = newCmds } @@ -315,10 +313,10 @@ func escFnsEq(a, b string) bool { } // newIdentCmd produces a command containing a single identifier node. -func newIdentCmd(identifier string) *parse.CommandNode { +func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode { return &parse.CommandNode{ NodeType: parse.NodeCommand, - Args: []parse.Node{parse.NewIdentifier(identifier)}, + Args: []parse.Node{parse.NewIdentifier(identifier).SetPos(pos)}, } } diff --git a/src/pkg/html/template/escape_test.go b/src/pkg/html/template/escape_test.go index ce12c1795..de3659ba8 100644 --- a/src/pkg/html/template/escape_test.go +++ b/src/pkg/html/template/escape_test.go @@ -550,11 +550,6 @@ func TestEscape(t *testing.T) { "", "", }, - { - "auditable exemption from escaping", - "{{range .A}}{{. | noescape}}{{end}}", - "", - }, { "No tag injection", `{{"10$"}}<{{"script src,evil.org/pwnd.js"}}...`, @@ -659,12 +654,6 @@ func TestEscape(t *testing.T) { for _, test := range tests { tmpl := New(test.name) - // TODO: Move noescape into template/func.go - tmpl.Funcs(FuncMap{ - "noescape": func(a ...interface{}) string { - return fmt.Sprint(a...) - }, - }) tmpl = Must(tmpl.Parse(test.input)) b := new(bytes.Buffer) if err := tmpl.Execute(b, data); err != nil { @@ -1539,6 +1528,11 @@ func TestEnsurePipelineContains(t *testing.T) { ".X | urlquery | html | print", []string{"urlquery", "html"}, }, + { + "{{($).X | html | print}}", + "($).X | urlquery | html | print", + []string{"urlquery", "html"}, + }, } for i, test := range tests { tmpl := template.Must(template.New("test").Parse(test.input)) diff --git a/src/pkg/html/template/html.go b/src/pkg/html/template/html.go index 36c88e23e..f25f1074c 100644 --- a/src/pkg/html/template/html.go +++ b/src/pkg/html/template/html.go @@ -106,7 +106,7 @@ var htmlNospaceReplacementTable = []string{ '<': "<", '=': "=", '>': ">", - // A parse error in the attribute value (unquoted) and + // A parse error in the attribute value (unquoted) and // before attribute value states. // Treated as a quoting character by IE. '`': "`", @@ -128,7 +128,7 @@ var htmlNospaceNormReplacementTable = []string{ '<': "<", '=': "=", '>': ">", - // A parse error in the attribute value (unquoted) and + // A parse error in the attribute value (unquoted) and // before attribute value states. // Treated as a quoting character by IE. '`': "`", @@ -143,7 +143,7 @@ func htmlReplacer(s string, replacementTable []string, badRunes bool) string { if repl := replacementTable[r]; len(repl) != 0 { b.WriteString(s[written:i]) b.WriteString(repl) - // Valid as long as replacementTable doesn't + // Valid as long as replacementTable doesn't // include anything above 0x7f. written = i + utf8.RuneLen(r) } diff --git a/src/pkg/html/template/js.go b/src/pkg/html/template/js.go index a895a50aa..a9740931f 100644 --- a/src/pkg/html/template/js.go +++ b/src/pkg/html/template/js.go @@ -14,7 +14,7 @@ import ( ) // nextJSCtx returns the context that determines whether a slash after the -// given run of tokens tokens starts a regular expression instead of a division +// given run of tokens starts a regular expression instead of a division // operator: / or /=. // // This assumes that the token run does not include any string tokens, comment diff --git a/src/pkg/html/template/template.go b/src/pkg/html/template/template.go index edac7335c..768cee7d5 100644 --- a/src/pkg/html/template/template.go +++ b/src/pkg/html/template/template.go @@ -14,8 +14,8 @@ import ( "text/template/parse" ) -// Template is a specialized template.Template that produces a safe HTML -// document fragment. +// Template is a specialized Template from "text/template" that produces a safe +// HTML document fragment. type Template struct { escaped bool // We could embed the text/template field, but it's safer not to because @@ -238,7 +238,8 @@ func (t *Template) Name() string { // return values of which the second has type error. In that case, if the // second (error) argument evaluates to non-nil during execution, execution // terminates and Execute returns that error. FuncMap has the same base type -// as template.FuncMap, copied here so clients need not import "text/template". +// as FuncMap in "text/template", copied here so clients need not import +// "text/template". type FuncMap map[string]interface{} // Funcs adds the elements of the argument map to the template's function map. @@ -268,7 +269,10 @@ func (t *Template) Lookup(name string) *Template { return t.set[name] } -// Must panics if err is non-nil in the same way as template.Must. +// Must is a helper that wraps a call to a function returning (*Template, error) +// and panics if the error is non-nil. It is intended for use in variable initializations +// such as +// var t = template.Must(template.New("name").Parse("html")) func Must(t *Template, err error) *Template { if err != nil { panic(err) diff --git a/src/pkg/html/template/url.go b/src/pkg/html/template/url.go index 454c791ec..2ca76bf38 100644 --- a/src/pkg/html/template/url.go +++ b/src/pkg/html/template/url.go @@ -60,7 +60,7 @@ func urlProcessor(norm bool, args ...interface{}) string { c := s[i] switch c { // Single quote and parens are sub-delims in RFC 3986, but we - // escape them so the output can be embedded in in single + // escape them so the output can be embedded in single // quoted attributes and unquoted CSS url(...) constructs. // Single quotes are reserved in URLs, but are only used in // the obsolete "mark" rule in an appendix in RFC 3986 diff --git a/src/pkg/image/decode_test.go b/src/pkg/image/decode_test.go index d65986724..8dee57ee4 100644 --- a/src/pkg/image/decode_test.go +++ b/src/pkg/image/decode_test.go @@ -31,6 +31,7 @@ var imageTests = []imageTest{ {"testdata/video-001.png", "testdata/video-001.5bpp.gif", 128 << 8}, // JPEG is a lossy format and hence needs a non-zero tolerance. {"testdata/video-001.png", "testdata/video-001.jpeg", 8 << 8}, + {"testdata/video-001.png", "testdata/video-001.progressive.jpeg", 8 << 8}, // Grayscale images. {"testdata/video-005.gray.png", "testdata/video-005.gray.jpeg", 8 << 8}, {"testdata/video-005.gray.png", "testdata/video-005.gray.png", 0}, diff --git a/src/pkg/image/draw/draw.go b/src/pkg/image/draw/draw.go index bef325c0c..56d30dd6f 100644 --- a/src/pkg/image/draw/draw.go +++ b/src/pkg/image/draw/draw.go @@ -81,8 +81,9 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas drawNRGBAOver(dst0, r, src0, sp) return case *image.YCbCr: - drawYCbCr(dst0, r, src0, sp) - return + if drawYCbCr(dst0, r, src0, sp) { + return + } } } else if mask0, ok := mask.(*image.Alpha); ok { switch src0 := src.(type) { @@ -104,8 +105,9 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas drawNRGBASrc(dst0, r, src0, sp) return case *image.YCbCr: - drawYCbCr(dst0, r, src0, sp) - return + if drawYCbCr(dst0, r, src0, sp) { + return + } } } } @@ -345,7 +347,7 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image } } -func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) { +func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) { // An image.YCbCr is always fully opaque, and so if the mask is implicitly nil // (i.e. fully opaque) then the op is effectively always Src. x0 := (r.Min.X - dst.Rect.Min.X) * 4 @@ -353,6 +355,19 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po y0 := r.Min.Y - dst.Rect.Min.Y y1 := r.Max.Y - dst.Rect.Min.Y switch src.SubsampleRatio { + case image.YCbCrSubsampleRatio444: + for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { + dpix := dst.Pix[y*dst.Stride:] + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X) + for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { + rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) + dpix[x+0] = rr + dpix[x+1] = gg + dpix[x+2] = bb + dpix[x+3] = 255 + } + } case image.YCbCrSubsampleRatio422: for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { dpix := dst.Pix[y*dst.Stride:] @@ -381,12 +396,11 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po dpix[x+3] = 255 } } - default: - // Default to 4:4:4 subsampling. + case image.YCbCrSubsampleRatio440: for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { dpix := dst.Pix[y*dst.Stride:] yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) - ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X) + ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X) for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) dpix[x+0] = rr @@ -395,7 +409,10 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po dpix[x+3] = 255 } } + default: + return false } + return true } func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) { diff --git a/src/pkg/image/format.go b/src/pkg/image/format.go index f93d356b0..36635bcc5 100644 --- a/src/pkg/image/format.go +++ b/src/pkg/image/format.go @@ -39,7 +39,7 @@ type reader interface { Peek(int) ([]byte, error) } -// AsReader converts an io.Reader to a reader. +// asReader converts an io.Reader to a reader. func asReader(r io.Reader) reader { if rr, ok := r.(reader); ok { return rr diff --git a/src/pkg/image/jpeg/dct_test.go b/src/pkg/image/jpeg/dct_test.go new file mode 100644 index 000000000..7389f7e4f --- /dev/null +++ b/src/pkg/image/jpeg/dct_test.go @@ -0,0 +1,299 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jpeg + +import ( + "bytes" + "fmt" + "math" + "math/rand" + "testing" +) + +func benchmarkDCT(b *testing.B, f func(*block)) { + b.StopTimer() + blocks := make([]block, 0, b.N*len(testBlocks)) + for i := 0; i < b.N; i++ { + blocks = append(blocks, testBlocks[:]...) + } + b.StartTimer() + for i := range blocks { + f(&blocks[i]) + } +} + +func BenchmarkFDCT(b *testing.B) { + benchmarkDCT(b, fdct) +} + +func BenchmarkIDCT(b *testing.B) { + benchmarkDCT(b, idct) +} + +func TestDCT(t *testing.T) { + blocks := make([]block, len(testBlocks)) + copy(blocks, testBlocks[:]) + + // Append some randomly generated blocks of varying sparseness. + r := rand.New(rand.NewSource(123)) + for i := 0; i < 100; i++ { + b := block{} + n := r.Int() % 64 + for j := 0; j < n; j++ { + b[r.Int()%len(b)] = r.Int31() % 256 + } + blocks = append(blocks, b) + } + + // Check that the FDCT and IDCT functions are inverses, after a scale and + // level shift. Scaling reduces the rounding errors in the conversion from + // floats to ints. + for i, b := range blocks { + got, want := b, b + for j := range got { + got[j] = (got[j] - 128) * 8 + } + slowFDCT(&got) + slowIDCT(&got) + for j := range got { + got[j] = got[j]/8 + 128 + } + if differ(&got, &want) { + t.Errorf("i=%d: IDCT(FDCT)\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want) + } + } + + // Check that the optimized and slow FDCT implementations agree. + // The fdct function already does a scale and level shift. + for i, b := range blocks { + got, want := b, b + fdct(&got) + for j := range want { + want[j] = (want[j] - 128) * 8 + } + slowFDCT(&want) + if differ(&got, &want) { + t.Errorf("i=%d: FDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want) + } + } + + // Check that the optimized and slow IDCT implementations agree. + for i, b := range blocks { + got, want := b, b + idct(&got) + slowIDCT(&want) + if differ(&got, &want) { + t.Errorf("i=%d: IDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want) + } + } +} + +// differ returns whether any pair-wise elements in b0 and b1 differ by 2 or +// more. That tolerance is because there isn't a single definitive decoding of +// a given JPEG image, even before the YCbCr to RGB conversion; implementations +// can have different IDCT rounding errors. +func differ(b0, b1 *block) bool { + for i := range b0 { + delta := b0[i] - b1[i] + if delta < -2 || +2 < delta { + return true + } + } + return false +} + +// alpha returns 1 if i is 0 and returns √2 otherwise. +func alpha(i int) float64 { + if i == 0 { + return 1 + } + return math.Sqrt2 +} + +var cosines [32]float64 // cosines[k] = cos(π/2 * k/8) + +func init() { + for k := range cosines { + cosines[k] = math.Cos(math.Pi * float64(k) / 16) + } +} + +// slowFDCT performs the 8*8 2-dimensional forward discrete cosine transform: +// +// dst[u,v] = (1/8) * Σ_x Σ_y alpha(u) * alpha(v) * src[x,y] * +// cos((π/2) * (2*x + 1) * u / 8) * +// cos((π/2) * (2*y + 1) * v / 8) +// +// x and y are in pixel space, and u and v are in transform space. +// +// b acts as both dst and src. +func slowFDCT(b *block) { + var dst [blockSize]float64 + for v := 0; v < 8; v++ { + for u := 0; u < 8; u++ { + sum := 0.0 + for y := 0; y < 8; y++ { + for x := 0; x < 8; x++ { + sum += alpha(u) * alpha(v) * float64(b[8*y+x]) * + cosines[((2*x+1)*u)%32] * + cosines[((2*y+1)*v)%32] + } + } + dst[8*v+u] = sum / 8 + } + } + // Convert from float64 to int32. + for i := range dst { + b[i] = int32(dst[i] + 0.5) + } +} + +// slowIDCT performs the 8*8 2-dimensional inverse discrete cosine transform: +// +// dst[x,y] = (1/8) * Σ_u Σ_v alpha(u) * alpha(v) * src[u,v] * +// cos((π/2) * (2*x + 1) * u / 8) * +// cos((π/2) * (2*y + 1) * v / 8) +// +// x and y are in pixel space, and u and v are in transform space. +// +// b acts as both dst and src. +func slowIDCT(b *block) { + var dst [blockSize]float64 + for y := 0; y < 8; y++ { + for x := 0; x < 8; x++ { + sum := 0.0 + for v := 0; v < 8; v++ { + for u := 0; u < 8; u++ { + sum += alpha(u) * alpha(v) * float64(b[8*v+u]) * + cosines[((2*x+1)*u)%32] * + cosines[((2*y+1)*v)%32] + } + } + dst[8*y+x] = sum / 8 + } + } + // Convert from float64 to int32. + for i := range dst { + b[i] = int32(dst[i] + 0.5) + } +} + +func (b *block) String() string { + s := bytes.NewBuffer(nil) + fmt.Fprintf(s, "{\n") + for y := 0; y < 8; y++ { + fmt.Fprintf(s, "\t") + for x := 0; x < 8; x++ { + fmt.Fprintf(s, "0x%04x, ", uint16(b[8*y+x])) + } + fmt.Fprintln(s) + } + fmt.Fprintf(s, "}") + return s.String() +} + +// testBlocks are the first 10 pre-IDCT blocks from ../testdata/video-001.jpeg. +var testBlocks = [10]block{ + { + 0x7f, 0xf6, 0x01, 0x07, 0xff, 0x00, 0x00, 0x00, + 0xf5, 0x01, 0xfa, 0x01, 0xfe, 0x00, 0x01, 0x00, + 0x05, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xff, 0xf8, 0x00, 0x01, 0xff, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, + 0xff, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0xfe, + }, + { + 0x29, 0x07, 0x00, 0xfc, 0x01, 0x01, 0x00, 0x00, + 0x07, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, + 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0xff, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xfa, 0x01, 0x00, 0x01, 0x00, 0x01, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x02, + }, + { + 0xc5, 0xfa, 0x01, 0x00, 0x00, 0x01, 0x00, 0xff, + 0x02, 0xff, 0x01, 0x00, 0x01, 0x00, 0xff, 0x00, + 0xff, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + { + 0x86, 0x05, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0xf2, 0x06, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0xf6, 0xfa, 0xf9, 0x00, 0xff, 0x01, 0x00, 0x00, + 0xf9, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, + }, + { + 0x24, 0xfe, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x08, 0xfd, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, + 0x06, 0x03, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0xff, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x01, + }, + { + 0xcd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, + }, + { + 0x81, 0xfe, 0x05, 0xff, 0x01, 0xff, 0x01, 0x00, + 0xef, 0xf9, 0x00, 0xf9, 0x00, 0xff, 0x00, 0xff, + 0x05, 0xf9, 0x00, 0xf8, 0x01, 0xff, 0x01, 0xff, + 0x00, 0xff, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, + 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + }, + { + 0x28, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x02, 0x01, 0x03, 0x00, 0xff, 0x00, 0x01, + 0xfe, 0x02, 0x01, 0x03, 0xff, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xfd, 0x00, 0x01, 0x00, 0xff, 0x00, + 0x01, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, + }, + { + 0xdf, 0xf9, 0xfe, 0x00, 0x03, 0x01, 0xff, 0xff, + 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x01, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + }, + { + 0x88, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, + 0xe1, 0x06, 0x06, 0x01, 0xff, 0x00, 0x01, 0x00, + 0x08, 0x00, 0xfa, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x08, 0x01, 0x00, 0xff, 0x01, 0xff, 0x00, 0x00, + 0xf5, 0xff, 0x00, 0x01, 0xff, 0x01, 0x01, 0x00, + 0xff, 0xff, 0x01, 0xff, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x01, 0xff, 0x00, 0xff, 0x00, 0x01, + 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, + }, +} diff --git a/src/pkg/image/jpeg/huffman.go b/src/pkg/image/jpeg/huffman.go index d2382490f..9b731fdc4 100644 --- a/src/pkg/image/jpeg/huffman.go +++ b/src/pkg/image/jpeg/huffman.go @@ -15,9 +15,9 @@ const maxNumValues = 256 // Bit stream for the Huffman decoder. // The n least significant bits of a form the unread bits, to be read in MSB to LSB order. type bits struct { - a int // accumulator. - n int // the number of unread bits in a. - m int // mask. m==1<<(n-1) when n>0, with m==0 when n==0. + a uint32 // accumulator. + m uint32 // mask. m==1<<(n-1) when n>0, with m==0 when n==0. + n int // the number of unread bits in a. } // Huffman table decoder, specified in section C. @@ -39,7 +39,7 @@ func (d *decoder) ensureNBits(n int) error { if err != nil { return err } - d.b.a = d.b.a<<8 | int(c) + d.b.a = d.b.a<<8 | uint32(c) d.b.n += 8 if d.b.m == 0 { d.b.m = 1 << 7 @@ -61,15 +61,16 @@ func (d *decoder) ensureNBits(n int) error { } // The composition of RECEIVE and EXTEND, specified in section F.2.2.1. -func (d *decoder) receiveExtend(t uint8) (int, error) { - err := d.ensureNBits(int(t)) - if err != nil { - return 0, err +func (d *decoder) receiveExtend(t uint8) (int32, error) { + if d.b.n < int(t) { + if err := d.ensureNBits(int(t)); err != nil { + return 0, err + } } d.b.n -= int(t) d.b.m >>= t - s := 1 << t - x := (d.b.a >> uint8(d.b.n)) & (s - 1) + s := int32(1) << t + x := int32(d.b.a>>uint8(d.b.n)) & (s - 1) if x < s>>1 { x += ((-1) << t) + 1 } @@ -92,8 +93,7 @@ func (d *decoder) processDHT(n int) error { return FormatError("bad Tc value") } th := d.tmp[0] & 0x0f - const isBaseline = true // Progressive mode is not yet supported. - if th > maxTh || isBaseline && th > 1 { + if th > maxTh || !d.progressive && th > 1 { return FormatError("bad Th value") } h := &d.huff[tc][th] @@ -163,15 +163,16 @@ func (d *decoder) processDHT(n int) error { // Returns the next Huffman-coded value from the bit stream, decoded according to h. // TODO(nigeltao): This decoding algorithm is simple, but slow. A lookahead table, instead of always -// peeling off only 1 bit at at time, ought to be faster. +// peeling off only 1 bit at time, ought to be faster. func (d *decoder) decodeHuffman(h *huffman) (uint8, error) { if h.length == 0 { return 0, FormatError("uninitialized Huffman table") } for i, code := 0, 0; i < maxCodeLength; i++ { - err := d.ensureNBits(1) - if err != nil { - return 0, err + if d.b.n == 0 { + if err := d.ensureNBits(1); err != nil { + return 0, err + } } if d.b.a&d.b.m != 0 { code |= 1 @@ -185,3 +186,28 @@ func (d *decoder) decodeHuffman(h *huffman) (uint8, error) { } return 0, FormatError("bad Huffman code") } + +func (d *decoder) decodeBit() (bool, error) { + if d.b.n == 0 { + if err := d.ensureNBits(1); err != nil { + return false, err + } + } + ret := d.b.a&d.b.m != 0 + d.b.n-- + d.b.m >>= 1 + return ret, nil +} + +func (d *decoder) decodeBits(n int) (uint32, error) { + if d.b.n < n { + if err := d.ensureNBits(n); err != nil { + return 0, err + } + } + ret := d.b.a >> uint(d.b.n-n) + ret &= (1 << uint(n)) - 1 + d.b.n -= n + d.b.m >>= uint(n) + return ret, nil +} diff --git a/src/pkg/image/jpeg/idct.go b/src/pkg/image/jpeg/idct.go index b387dfdff..46fcaecb7 100644 --- a/src/pkg/image/jpeg/idct.go +++ b/src/pkg/image/jpeg/idct.go @@ -37,6 +37,10 @@ package jpeg * */ +const blockSize = 64 // A DCT block is 8x8. + +type block [blockSize]int32 + const ( w1 = 2841 // 2048*sqrt(2)*cos(1*pi/16) w2 = 2676 // 2048*sqrt(2)*cos(2*pi/16) @@ -55,9 +59,7 @@ const ( r2 = 181 // 256/sqrt(2) ) -// idct performs a 2-D Inverse Discrete Cosine Transformation, followed by a -// +128 level shift and a clip to [0, 255], writing the results to dst. -// stride is the number of elements between successive rows of dst. +// idct performs a 2-D Inverse Discrete Cosine Transformation. // // The input coefficients should already have been multiplied by the // appropriate quantization table. We use fixed-point computation, with the @@ -67,33 +69,34 @@ const ( // For more on the actual algorithm, see Z. Wang, "Fast algorithms for the // discrete W transform and for the discrete Fourier transform", IEEE Trans. on // ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984. -func idct(dst []byte, stride int, src *block) { +func idct(src *block) { // Horizontal 1-D IDCT. for y := 0; y < 8; y++ { + y8 := y * 8 // If all the AC components are zero, then the IDCT is trivial. - if src[y*8+1] == 0 && src[y*8+2] == 0 && src[y*8+3] == 0 && - src[y*8+4] == 0 && src[y*8+5] == 0 && src[y*8+6] == 0 && src[y*8+7] == 0 { - dc := src[y*8+0] << 3 - src[y*8+0] = dc - src[y*8+1] = dc - src[y*8+2] = dc - src[y*8+3] = dc - src[y*8+4] = dc - src[y*8+5] = dc - src[y*8+6] = dc - src[y*8+7] = dc + if src[y8+1] == 0 && src[y8+2] == 0 && src[y8+3] == 0 && + src[y8+4] == 0 && src[y8+5] == 0 && src[y8+6] == 0 && src[y8+7] == 0 { + dc := src[y8+0] << 3 + src[y8+0] = dc + src[y8+1] = dc + src[y8+2] = dc + src[y8+3] = dc + src[y8+4] = dc + src[y8+5] = dc + src[y8+6] = dc + src[y8+7] = dc continue } // Prescale. - x0 := (src[y*8+0] << 11) + 128 - x1 := src[y*8+4] << 11 - x2 := src[y*8+6] - x3 := src[y*8+2] - x4 := src[y*8+1] - x5 := src[y*8+7] - x6 := src[y*8+5] - x7 := src[y*8+3] + x0 := (src[y8+0] << 11) + 128 + x1 := src[y8+4] << 11 + x2 := src[y8+6] + x3 := src[y8+2] + x4 := src[y8+1] + x5 := src[y8+7] + x6 := src[y8+5] + x7 := src[y8+3] // Stage 1. x8 := w7 * (x4 + x5) @@ -123,14 +126,14 @@ func idct(dst []byte, stride int, src *block) { x4 = (r2*(x4-x5) + 128) >> 8 // Stage 4. - src[8*y+0] = (x7 + x1) >> 8 - src[8*y+1] = (x3 + x2) >> 8 - src[8*y+2] = (x0 + x4) >> 8 - src[8*y+3] = (x8 + x6) >> 8 - src[8*y+4] = (x8 - x6) >> 8 - src[8*y+5] = (x0 - x4) >> 8 - src[8*y+6] = (x3 - x2) >> 8 - src[8*y+7] = (x7 - x1) >> 8 + src[y8+0] = (x7 + x1) >> 8 + src[y8+1] = (x3 + x2) >> 8 + src[y8+2] = (x0 + x4) >> 8 + src[y8+3] = (x8 + x6) >> 8 + src[y8+4] = (x8 - x6) >> 8 + src[y8+5] = (x0 - x4) >> 8 + src[y8+6] = (x3 - x2) >> 8 + src[y8+7] = (x7 - x1) >> 8 } // Vertical 1-D IDCT. @@ -186,19 +189,4 @@ func idct(dst []byte, stride int, src *block) { src[8*6+x] = (y3 - y2) >> 14 src[8*7+x] = (y7 - y1) >> 14 } - - // Level shift by +128, clip to [0, 255], and write to dst. - for y := 0; y < 8; y++ { - for x := 0; x < 8; x++ { - c := src[y*8+x] - if c < -128 { - c = 0 - } else if c > 127 { - c = 255 - } else { - c += 128 - } - dst[y*stride+x] = uint8(c) - } - } } diff --git a/src/pkg/image/jpeg/reader.go b/src/pkg/image/jpeg/reader.go index d9adf6e58..1ee6bbcd1 100644 --- a/src/pkg/image/jpeg/reader.go +++ b/src/pkg/image/jpeg/reader.go @@ -35,11 +35,7 @@ type component struct { tq uint8 // Quantization table destination selector. } -type block [blockSize]int - const ( - blockSize = 64 // A DCT block is 8x8. - dcTable = 0 acTable = 1 maxTc = 1 @@ -51,7 +47,7 @@ const ( // A color JPEG image has Y, Cb and Cr components. nColorComponent = 3 - // We only support 4:4:4, 4:2:2 and 4:2:0 downsampling, and therefore the + // We only support 4:4:4, 4:4:0, 4:2:2 and 4:2:0 downsampling, and therefore the // number of luma samples per chroma sample is at most 2 in the horizontal // and 2 in the vertical direction. maxH = 2 @@ -74,7 +70,9 @@ const ( comMarker = 0xfe // COMment. ) -// Maps from the zig-zag ordering to the natural ordering. +// unzig maps from the zig-zag ordering to the natural ordering. For example, +// unzig[3] is the column and row of the fourth element in zig-zag order. The +// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). var unzig = [blockSize]int{ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, @@ -94,15 +92,18 @@ type Reader interface { type decoder struct { r Reader + b bits width, height int img1 *image.Gray img3 *image.YCbCr ri int // Restart Interval. nComp int + progressive bool + eobRun uint16 // End-of-Band run, specified in section G.1.2.2. comp [nColorComponent]component + progCoeffs [nColorComponent][]block // Saved state between progressive-mode scans. huff [maxTc + 1][maxTh + 1]huffman - quant [maxTq + 1]block - b bits + quant [maxTq + 1]block // Quantization tables, in zig-zag order. tmp [1024]byte } @@ -146,20 +147,33 @@ func (d *decoder) processSOF(n int) error { return UnsupportedError("SOF has wrong number of image components") } for i := 0; i < d.nComp; i++ { - hv := d.tmp[7+3*i] - d.comp[i].h = int(hv >> 4) - d.comp[i].v = int(hv & 0x0f) d.comp[i].c = d.tmp[6+3*i] d.comp[i].tq = d.tmp[8+3*i] if d.nComp == nGrayComponent { + // If a JPEG image has only one component, section A.2 says "this data + // is non-interleaved by definition" and section A.2.2 says "[in this + // case...] the order of data units within a scan shall be left-to-right + // and top-to-bottom... regardless of the values of H_1 and V_1". Section + // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be + // one data unit". Similarly, section A.1.1 explains that it is the ratio + // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale + // images, H_1 is the maximum H_j for all components j, so that ratio is + // always 1. The component's (h, v) is effectively always (1, 1): even if + // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 + // MCUs, not two 16x8 MCUs. + d.comp[i].h = 1 + d.comp[i].v = 1 continue } - // For color images, we only support 4:4:4, 4:2:2 or 4:2:0 chroma + hv := d.tmp[7+3*i] + d.comp[i].h = int(hv >> 4) + d.comp[i].v = int(hv & 0x0f) + // For color images, we only support 4:4:4, 4:4:0, 4:2:2 or 4:2:0 chroma // downsampling ratios. This implies that the (h, v) values for the Y - // component are either (1, 1), (2, 1) or (2, 2), and the (h, v) + // component are either (1, 1), (1, 2), (2, 1) or (2, 2), and the (h, v) // values for the Cr and Cb components must be (1, 1). if i == 0 { - if hv != 0x11 && hv != 0x21 && hv != 0x22 { + if hv != 0x11 && hv != 0x21 && hv != 0x22 && hv != 0x12 { return UnsupportedError("luma downsample ratio") } } else if hv != 0x11 { @@ -186,7 +200,7 @@ func (d *decoder) processDQT(n int) error { return FormatError("bad Tq value") } for i := range d.quant[tq] { - d.quant[tq][i] = int(d.tmp[i+1]) + d.quant[tq][i] = int32(d.tmp[i+1]) } } if n != 0 { @@ -195,161 +209,6 @@ func (d *decoder) processDQT(n int) error { return nil } -// makeImg allocates and initializes the destination image. -func (d *decoder) makeImg(h0, v0, mxx, myy int) { - if d.nComp == nGrayComponent { - m := image.NewGray(image.Rect(0, 0, 8*mxx, 8*myy)) - d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray) - return - } - var subsampleRatio image.YCbCrSubsampleRatio - switch h0 * v0 { - case 1: - subsampleRatio = image.YCbCrSubsampleRatio444 - case 2: - subsampleRatio = image.YCbCrSubsampleRatio422 - case 4: - subsampleRatio = image.YCbCrSubsampleRatio420 - default: - panic("unreachable") - } - m := image.NewYCbCr(image.Rect(0, 0, 8*h0*mxx, 8*v0*myy), subsampleRatio) - d.img3 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.YCbCr) -} - -// Specified in section B.2.3. -func (d *decoder) processSOS(n int) error { - if d.nComp == 0 { - return FormatError("missing SOF marker") - } - if n != 4+2*d.nComp { - return UnsupportedError("SOS has wrong length") - } - _, err := io.ReadFull(d.r, d.tmp[0:4+2*d.nComp]) - if err != nil { - return err - } - if int(d.tmp[0]) != d.nComp { - return UnsupportedError("SOS has wrong number of image components") - } - var scan [nColorComponent]struct { - td uint8 // DC table selector. - ta uint8 // AC table selector. - } - for i := 0; i < d.nComp; i++ { - cs := d.tmp[1+2*i] // Component selector. - if cs != d.comp[i].c { - return UnsupportedError("scan components out of order") - } - scan[i].td = d.tmp[2+2*i] >> 4 - scan[i].ta = d.tmp[2+2*i] & 0x0f - } - // mxx and myy are the number of MCUs (Minimum Coded Units) in the image. - h0, v0 := d.comp[0].h, d.comp[0].v // The h and v values from the Y components. - mxx := (d.width + 8*h0 - 1) / (8 * h0) - myy := (d.height + 8*v0 - 1) / (8 * v0) - if d.img1 == nil && d.img3 == nil { - d.makeImg(h0, v0, mxx, myy) - } - - mcu, expectedRST := 0, uint8(rst0Marker) - var ( - b block - dc [nColorComponent]int - ) - for my := 0; my < myy; my++ { - for mx := 0; mx < mxx; mx++ { - for i := 0; i < d.nComp; i++ { - qt := &d.quant[d.comp[i].tq] - for j := 0; j < d.comp[i].h*d.comp[i].v; j++ { - // TODO(nigeltao): make this a "var b block" once the compiler's escape - // analysis is good enough to allocate it on the stack, not the heap. - b = block{} - - // Decode the DC coefficient, as specified in section F.2.2.1. - value, err := d.decodeHuffman(&d.huff[dcTable][scan[i].td]) - if err != nil { - return err - } - if value > 16 { - return UnsupportedError("excessive DC component") - } - dcDelta, err := d.receiveExtend(value) - if err != nil { - return err - } - dc[i] += dcDelta - b[0] = dc[i] * qt[0] - - // Decode the AC coefficients, as specified in section F.2.2.2. - for k := 1; k < blockSize; k++ { - value, err := d.decodeHuffman(&d.huff[acTable][scan[i].ta]) - if err != nil { - return err - } - val0 := value >> 4 - val1 := value & 0x0f - if val1 != 0 { - k += int(val0) - if k > blockSize { - return FormatError("bad DCT index") - } - ac, err := d.receiveExtend(val1) - if err != nil { - return err - } - b[unzig[k]] = ac * qt[k] - } else { - if val0 != 0x0f { - break - } - k += 0x0f - } - } - - // Perform the inverse DCT and store the MCU component to the image. - if d.nComp == nGrayComponent { - idct(d.img1.Pix[8*(my*d.img1.Stride+mx):], d.img1.Stride, &b) - } else { - switch i { - case 0: - mx0 := h0*mx + (j % 2) - my0 := v0*my + (j / 2) - idct(d.img3.Y[8*(my0*d.img3.YStride+mx0):], d.img3.YStride, &b) - case 1: - idct(d.img3.Cb[8*(my*d.img3.CStride+mx):], d.img3.CStride, &b) - case 2: - idct(d.img3.Cr[8*(my*d.img3.CStride+mx):], d.img3.CStride, &b) - } - } - } // for j - } // for i - mcu++ - if d.ri > 0 && mcu%d.ri == 0 && mcu < mxx*myy { - // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, - // but this one assumes well-formed input, and hence the restart marker follows immediately. - _, err := io.ReadFull(d.r, d.tmp[0:2]) - if err != nil { - return err - } - if d.tmp[0] != 0xff || d.tmp[1] != expectedRST { - return FormatError("bad RST marker") - } - expectedRST++ - if expectedRST == rst7Marker+1 { - expectedRST = rst0Marker - } - // Reset the Huffman decoder. - d.b = bits{} - // Reset the DC components, as per section F.2.1.3.1. - dc = [nColorComponent]int{} - } - } // for mx - } // for my - - return nil -} - // Specified in section B.2.4.4. func (d *decoder) processDRI(n int) error { if n != 2 { @@ -390,9 +249,26 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { return nil, FormatError("missing 0xff marker start") } marker := d.tmp[1] + for marker == 0xff { + // Section B.1.1.2 says, "Any marker may optionally be preceded by any + // number of fill bytes, which are bytes assigned code X'FF'". + marker, err = d.r.ReadByte() + if err != nil { + return nil, err + } + } if marker == eoiMarker { // End Of Image. break } + if rst0Marker <= marker && marker <= rst7Marker { + // Figures B.2 and B.16 of the specification suggest that restart markers should + // only occur between Entropy Coded Segments and not after the final ECS. + // However, some encoders may generate incorrect JPEGs with a final restart + // marker. That restart marker will be seen here instead of inside the processSOS + // method, and is ignored as a harmless error. Restart markers have no extra data, + // so we check for this before we read the 16-bit length of the segment. + continue + } // Read the 16-bit length of the segment. The value includes the 2 bytes for the // length itself, so we subtract 2 to get the number of remaining bytes. @@ -406,13 +282,12 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { } switch { - case marker == sof0Marker: // Start Of Frame (Baseline). + case marker == sof0Marker || marker == sof2Marker: // Start Of Frame. + d.progressive = marker == sof2Marker err = d.processSOF(n) if configOnly { return nil, err } - case marker == sof2Marker: // Start Of Frame (Progressive). - err = UnsupportedError("progressive mode") case marker == dhtMarker: // Define Huffman Table. err = d.processDHT(n) case marker == dqtMarker: // Define Quantization Table. @@ -421,7 +296,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) { err = d.processSOS(n) case marker == driMarker: // Define Restart Interval. err = d.processDRI(n) - case marker >= app0Marker && marker <= app15Marker || marker == comMarker: // APPlication specific, or COMment. + case app0Marker <= marker && marker <= app15Marker || marker == comMarker: // APPlication specific, or COMment. err = d.ignore(n) default: err = UnsupportedError("unknown marker") diff --git a/src/pkg/image/jpeg/reader_test.go b/src/pkg/image/jpeg/reader_test.go new file mode 100644 index 000000000..b520a8ab1 --- /dev/null +++ b/src/pkg/image/jpeg/reader_test.go @@ -0,0 +1,157 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jpeg + +import ( + "bytes" + "fmt" + "image" + "io/ioutil" + "os" + "testing" +) + +// TestDecodeProgressive tests that decoding the baseline and progressive +// versions of the same image result in exactly the same pixel data, in YCbCr +// space for color images, and Y space for grayscale images. +func TestDecodeProgressive(t *testing.T) { + testCases := []string{ + "../testdata/video-001", + "../testdata/video-001.q50.420", + "../testdata/video-001.q50.422", + "../testdata/video-001.q50.440", + "../testdata/video-001.q50.444", + "../testdata/video-005.gray.q50", + "../testdata/video-005.gray.q50.2x2", + } + for _, tc := range testCases { + m0, err := decodeFile(tc + ".jpeg") + if err != nil { + t.Errorf("%s: %v", tc+".jpeg", err) + continue + } + m1, err := decodeFile(tc + ".progressive.jpeg") + if err != nil { + t.Errorf("%s: %v", tc+".progressive.jpeg", err) + continue + } + if m0.Bounds() != m1.Bounds() { + t.Errorf("%s: bounds differ: %v and %v", tc, m0.Bounds(), m1.Bounds()) + continue + } + switch m0 := m0.(type) { + case *image.YCbCr: + m1 := m1.(*image.YCbCr) + if err := check(m0.Bounds(), m0.Y, m1.Y, m0.YStride, m1.YStride); err != nil { + t.Errorf("%s (Y): %v", tc, err) + continue + } + if err := check(m0.Bounds(), m0.Cb, m1.Cb, m0.CStride, m1.CStride); err != nil { + t.Errorf("%s (Cb): %v", tc, err) + continue + } + if err := check(m0.Bounds(), m0.Cr, m1.Cr, m0.CStride, m1.CStride); err != nil { + t.Errorf("%s (Cr): %v", tc, err) + continue + } + case *image.Gray: + m1 := m1.(*image.Gray) + if err := check(m0.Bounds(), m0.Pix, m1.Pix, m0.Stride, m1.Stride); err != nil { + t.Errorf("%s: %v", tc, err) + continue + } + default: + t.Errorf("%s: unexpected image type %T", tc, m0) + continue + } + } +} + +func decodeFile(filename string) (image.Image, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + return Decode(f) + +} + +// check checks that the two pix data are equal, within the given bounds. +func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error { + if len(pix0) != len(pix1) { + return fmt.Errorf("len(pix) %d and %d differ", len(pix0), len(pix1)) + } + if stride0 != stride1 { + return fmt.Errorf("strides %d and %d differ", stride0, stride1) + } + if stride0%8 != 0 { + return fmt.Errorf("stride %d is not a multiple of 8", stride0) + } + // Compare the two pix data, one 8x8 block at a time. + for y := 0; y < len(pix0)/stride0; y += 8 { + for x := 0; x < stride0; x += 8 { + if x >= bounds.Max.X || y >= bounds.Max.Y { + // We don't care if the two pix data differ if the 8x8 block is + // entirely outside of the image's bounds. For example, this can + // occur with a 4:2:0 chroma subsampling and a 1x1 image. Baseline + // decoding works on the one 16x16 MCU as a whole; progressive + // decoding's first pass works on that 16x16 MCU as a whole but + // refinement passes only process one 8x8 block within the MCU. + continue + } + + for j := 0; j < 8; j++ { + for i := 0; i < 8; i++ { + index := (y+j)*stride0 + (x + i) + if pix0[index] != pix1[index] { + return fmt.Errorf("blocks at (%d, %d) differ:\n%sand\n%s", x, y, + pixString(pix0, stride0, x, y), + pixString(pix1, stride1, x, y), + ) + } + } + } + } + } + return nil +} + +func pixString(pix []byte, stride, x, y int) string { + s := bytes.NewBuffer(nil) + for j := 0; j < 8; j++ { + fmt.Fprintf(s, "\t") + for i := 0; i < 8; i++ { + fmt.Fprintf(s, "%02x ", pix[(y+j)*stride+(x+i)]) + } + fmt.Fprintf(s, "\n") + } + return s.String() +} + +func benchmarkDecode(b *testing.B, filename string) { + b.StopTimer() + data, err := ioutil.ReadFile(filename) + if err != nil { + b.Fatal(err) + } + cfg, err := DecodeConfig(bytes.NewReader(data)) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(cfg.Width * cfg.Height * 4)) + b.StartTimer() + for i := 0; i < b.N; i++ { + Decode(bytes.NewReader(data)) + } +} + +func BenchmarkDecodeBaseline(b *testing.B) { + benchmarkDecode(b, "../testdata/video-001.jpeg") +} + +func BenchmarkDecodeProgressive(b *testing.B) { + benchmarkDecode(b, "../testdata/video-001.progressive.jpeg") +} diff --git a/src/pkg/image/jpeg/scan.go b/src/pkg/image/jpeg/scan.go new file mode 100644 index 000000000..e3ae8ae44 --- /dev/null +++ b/src/pkg/image/jpeg/scan.go @@ -0,0 +1,432 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jpeg + +import ( + "image" + "io" +) + +// makeImg allocates and initializes the destination image. +func (d *decoder) makeImg(h0, v0, mxx, myy int) { + if d.nComp == nGrayComponent { + m := image.NewGray(image.Rect(0, 0, 8*mxx, 8*myy)) + d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray) + return + } + var subsampleRatio image.YCbCrSubsampleRatio + switch { + case h0 == 1 && v0 == 1: + subsampleRatio = image.YCbCrSubsampleRatio444 + case h0 == 1 && v0 == 2: + subsampleRatio = image.YCbCrSubsampleRatio440 + case h0 == 2 && v0 == 1: + subsampleRatio = image.YCbCrSubsampleRatio422 + case h0 == 2 && v0 == 2: + subsampleRatio = image.YCbCrSubsampleRatio420 + default: + panic("unreachable") + } + m := image.NewYCbCr(image.Rect(0, 0, 8*h0*mxx, 8*v0*myy), subsampleRatio) + d.img3 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.YCbCr) +} + +// Specified in section B.2.3. +func (d *decoder) processSOS(n int) error { + if d.nComp == 0 { + return FormatError("missing SOF marker") + } + if n < 6 || 4+2*d.nComp < n || n%2 != 0 { + return FormatError("SOS has wrong length") + } + _, err := io.ReadFull(d.r, d.tmp[:n]) + if err != nil { + return err + } + nComp := int(d.tmp[0]) + if n != 4+2*nComp { + return FormatError("SOS length inconsistent with number of components") + } + var scan [nColorComponent]struct { + compIndex uint8 + td uint8 // DC table selector. + ta uint8 // AC table selector. + } + for i := 0; i < nComp; i++ { + cs := d.tmp[1+2*i] // Component selector. + compIndex := -1 + for j, comp := range d.comp { + if cs == comp.c { + compIndex = j + } + } + if compIndex < 0 { + return FormatError("unknown component selector") + } + scan[i].compIndex = uint8(compIndex) + scan[i].td = d.tmp[2+2*i] >> 4 + scan[i].ta = d.tmp[2+2*i] & 0x0f + } + + // zigStart and zigEnd are the spectral selection bounds. + // ah and al are the successive approximation high and low values. + // The spec calls these values Ss, Se, Ah and Al. + // + // For progressive JPEGs, these are the two more-or-less independent + // aspects of progression. Spectral selection progression is when not + // all of a block's 64 DCT coefficients are transmitted in one pass. + // For example, three passes could transmit coefficient 0 (the DC + // component), coefficients 1-5, and coefficients 6-63, in zig-zag + // order. Successive approximation is when not all of the bits of a + // band of coefficients are transmitted in one pass. For example, + // three passes could transmit the 6 most significant bits, followed + // by the second-least significant bit, followed by the least + // significant bit. + // + // For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. + zigStart, zigEnd, ah, al := int32(0), int32(blockSize-1), uint32(0), uint32(0) + if d.progressive { + zigStart = int32(d.tmp[1+2*nComp]) + zigEnd = int32(d.tmp[2+2*nComp]) + ah = uint32(d.tmp[3+2*nComp] >> 4) + al = uint32(d.tmp[3+2*nComp] & 0x0f) + if (zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || blockSize <= zigEnd { + return FormatError("bad spectral selection bounds") + } + if zigStart != 0 && nComp != 1 { + return FormatError("progressive AC coefficients for more than one component") + } + if ah != 0 && ah != al+1 { + return FormatError("bad successive approximation values") + } + } + + // mxx and myy are the number of MCUs (Minimum Coded Units) in the image. + h0, v0 := d.comp[0].h, d.comp[0].v // The h and v values from the Y components. + mxx := (d.width + 8*h0 - 1) / (8 * h0) + myy := (d.height + 8*v0 - 1) / (8 * v0) + if d.img1 == nil && d.img3 == nil { + d.makeImg(h0, v0, mxx, myy) + if d.progressive { + for i := 0; i < nComp; i++ { + compIndex := scan[i].compIndex + d.progCoeffs[compIndex] = make([]block, mxx*myy*d.comp[compIndex].h*d.comp[compIndex].v) + } + } + } + + d.b = bits{} + mcu, expectedRST := 0, uint8(rst0Marker) + var ( + // b is the decoded coefficients, in natural (not zig-zag) order. + b block + dc [nColorComponent]int32 + // mx0 and my0 are the location of the current (in terms of 8x8 blocks). + // For example, with 4:2:0 chroma subsampling, the block whose top left + // pixel co-ordinates are (16, 8) is the third block in the first row: + // mx0 is 2 and my0 is 0, even though the pixel is in the second MCU. + // TODO(nigeltao): rename mx0 and my0 to bx and by? + mx0, my0 int + blockCount int + ) + for my := 0; my < myy; my++ { + for mx := 0; mx < mxx; mx++ { + for i := 0; i < nComp; i++ { + compIndex := scan[i].compIndex + qt := &d.quant[d.comp[compIndex].tq] + for j := 0; j < d.comp[compIndex].h*d.comp[compIndex].v; j++ { + // The blocks are traversed one MCU at a time. For 4:2:0 chroma + // subsampling, there are four Y 8x8 blocks in every 16x16 MCU. + // For a baseline 32x16 pixel image, the Y blocks visiting order is: + // 0 1 4 5 + // 2 3 6 7 + // + // For progressive images, the DC data blocks (zigStart == 0) are traversed + // as above, but AC data blocks are traversed left to right, top to bottom: + // 0 1 2 3 + // 4 5 6 7 + // + // To further complicate matters, there is no AC data for any blocks that + // are inside the image at the MCU level but outside the image at the pixel + // level. For example, a 24x16 pixel 4:2:0 progressive image consists of + // two 16x16 MCUs. The earlier scans will process 8 Y blocks: + // 0 1 4 5 + // 2 3 6 7 + // The later scans will process only 6 Y blocks: + // 0 1 2 + // 3 4 5 + if zigStart == 0 { + mx0, my0 = d.comp[compIndex].h*mx, d.comp[compIndex].v*my + if h0 == 1 { + my0 += j + } else { + mx0 += j % 2 + my0 += j / 2 + } + } else { + q := mxx * d.comp[compIndex].h + mx0 = blockCount % q + my0 = blockCount / q + blockCount++ + if mx0*8 >= d.width || my0*8 >= d.height { + continue + } + } + + // Load the previous partially decoded coefficients, if applicable. + if d.progressive { + b = d.progCoeffs[compIndex][my0*mxx*d.comp[compIndex].h+mx0] + } else { + b = block{} + } + + if ah != 0 { + if err := d.refine(&b, &d.huff[acTable][scan[i].ta], zigStart, zigEnd, 1< 16 { + return UnsupportedError("excessive DC component") + } + dcDelta, err := d.receiveExtend(value) + if err != nil { + return err + } + dc[compIndex] += dcDelta + b[0] = dc[compIndex] << al + } + + if zig <= zigEnd && d.eobRun > 0 { + d.eobRun-- + } else { + // Decode the AC coefficients, as specified in section F.2.2.2. + for ; zig <= zigEnd; zig++ { + value, err := d.decodeHuffman(&d.huff[acTable][scan[i].ta]) + if err != nil { + return err + } + val0 := value >> 4 + val1 := value & 0x0f + if val1 != 0 { + zig += int32(val0) + if zig > zigEnd { + break + } + ac, err := d.receiveExtend(val1) + if err != nil { + return err + } + b[unzig[zig]] = ac << al + } else { + if val0 != 0x0f { + d.eobRun = uint16(1 << val0) + if val0 != 0 { + bits, err := d.decodeBits(int(val0)) + if err != nil { + return err + } + d.eobRun |= uint16(bits) + } + d.eobRun-- + break + } + zig += 0x0f + } + } + } + } + + if d.progressive { + if zigEnd != blockSize-1 || al != 0 { + // We haven't completely decoded this 8x8 block. Save the coefficients. + d.progCoeffs[compIndex][my0*mxx*d.comp[compIndex].h+mx0] = b + // At this point, we could execute the rest of the loop body to dequantize and + // perform the inverse DCT, to save early stages of a progressive image to the + // *image.YCbCr buffers (the whole point of progressive encoding), but in Go, + // the jpeg.Decode function does not return until the entire image is decoded, + // so we "continue" here to avoid wasted computation. + continue + } + } + + // Dequantize, perform the inverse DCT and store the block to the image. + for zig := 0; zig < blockSize; zig++ { + b[unzig[zig]] *= qt[zig] + } + idct(&b) + dst, stride := []byte(nil), 0 + if d.nComp == nGrayComponent { + dst, stride = d.img1.Pix[8*(my0*d.img1.Stride+mx0):], d.img1.Stride + } else { + switch compIndex { + case 0: + dst, stride = d.img3.Y[8*(my0*d.img3.YStride+mx0):], d.img3.YStride + case 1: + dst, stride = d.img3.Cb[8*(my0*d.img3.CStride+mx0):], d.img3.CStride + case 2: + dst, stride = d.img3.Cr[8*(my0*d.img3.CStride+mx0):], d.img3.CStride + default: + return UnsupportedError("too many components") + } + } + // Level shift by +128, clip to [0, 255], and write to dst. + for y := 0; y < 8; y++ { + y8 := y * 8 + yStride := y * stride + for x := 0; x < 8; x++ { + c := b[y8+x] + if c < -128 { + c = 0 + } else if c > 127 { + c = 255 + } else { + c += 128 + } + dst[yStride+x] = uint8(c) + } + } + } // for j + } // for i + mcu++ + if d.ri > 0 && mcu%d.ri == 0 && mcu < mxx*myy { + // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, + // but this one assumes well-formed input, and hence the restart marker follows immediately. + _, err := io.ReadFull(d.r, d.tmp[0:2]) + if err != nil { + return err + } + if d.tmp[0] != 0xff || d.tmp[1] != expectedRST { + return FormatError("bad RST marker") + } + expectedRST++ + if expectedRST == rst7Marker+1 { + expectedRST = rst0Marker + } + // Reset the Huffman decoder. + d.b = bits{} + // Reset the DC components, as per section F.2.1.3.1. + dc = [nColorComponent]int32{} + // Reset the progressive decoder state, as per section G.1.2.2. + d.eobRun = 0 + } + } // for mx + } // for my + + return nil +} + +// refine decodes a successive approximation refinement block, as specified in +// section G.1.2. +func (d *decoder) refine(b *block, h *huffman, zigStart, zigEnd, delta int32) error { + // Refining a DC component is trivial. + if zigStart == 0 { + if zigEnd != 0 { + panic("unreachable") + } + bit, err := d.decodeBit() + if err != nil { + return err + } + if bit { + b[0] |= delta + } + return nil + } + + // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. + zig := zigStart + if d.eobRun == 0 { + loop: + for ; zig <= zigEnd; zig++ { + z := int32(0) + value, err := d.decodeHuffman(h) + if err != nil { + return err + } + val0 := value >> 4 + val1 := value & 0x0f + + switch val1 { + case 0: + if val0 != 0x0f { + d.eobRun = uint16(1 << val0) + if val0 != 0 { + bits, err := d.decodeBits(int(val0)) + if err != nil { + return err + } + d.eobRun |= uint16(bits) + } + break loop + } + case 1: + z = delta + bit, err := d.decodeBit() + if err != nil { + return err + } + if !bit { + z = -z + } + default: + return FormatError("unexpected Huffman code") + } + + zig, err = d.refineNonZeroes(b, zig, zigEnd, int32(val0), delta) + if err != nil { + return err + } + if zig > zigEnd { + return FormatError("too many coefficients") + } + if z != 0 { + b[unzig[zig]] = z + } + } + } + if d.eobRun > 0 { + d.eobRun-- + if _, err := d.refineNonZeroes(b, zig, zigEnd, -1, delta); err != nil { + return err + } + } + return nil +} + +// refineNonZeroes refines non-zero entries of b in zig-zag order. If nz >= 0, +// the first nz zero entries are skipped over. +func (d *decoder) refineNonZeroes(b *block, zig, zigEnd, nz, delta int32) (int32, error) { + for ; zig <= zigEnd; zig++ { + u := unzig[zig] + if b[u] == 0 { + if nz == 0 { + break + } + nz-- + continue + } + bit, err := d.decodeBit() + if err != nil { + return 0, err + } + if !bit { + continue + } + if b[u] >= 0 { + b[u] += delta + } else { + b[u] -= delta + } + } + return zig, nil +} diff --git a/src/pkg/image/jpeg/writer.go b/src/pkg/image/jpeg/writer.go index 3322c09fe..c58fbf305 100644 --- a/src/pkg/image/jpeg/writer.go +++ b/src/pkg/image/jpeg/writer.go @@ -21,7 +21,7 @@ func min(x, y int) int { } // div returns a/b rounded to the nearest integer, instead of rounded to zero. -func div(a int, b int) int { +func div(a, b int32) int32 { if a >= 0 { return (a + (b >> 1)) / b } @@ -56,26 +56,28 @@ const ( nQuantIndex ) -// unscaledQuant are the unscaled quantization tables. Each encoder copies and -// scales the tables according to its quality parameter. +// unscaledQuant are the unscaled quantization tables in zig-zag order. Each +// encoder copies and scales the tables according to its quality parameter. +// The values are derived from section K.1 after converting from natural to +// zig-zag order. var unscaledQuant = [nQuantIndex][blockSize]byte{ // Luminance. { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, - 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, - 72, 92, 95, 98, 112, 100, 103, 99, + 16, 11, 12, 14, 12, 10, 16, 14, + 13, 14, 18, 17, 16, 19, 24, 40, + 26, 24, 22, 22, 24, 49, 35, 37, + 29, 40, 58, 51, 61, 60, 57, 51, + 56, 55, 64, 72, 92, 78, 64, 68, + 87, 69, 55, 56, 80, 109, 81, 87, + 95, 98, 103, 104, 103, 62, 77, 113, + 121, 112, 100, 120, 92, 101, 103, 99, }, // Chrominance. { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, + 17, 18, 18, 24, 21, 24, 47, 26, + 26, 47, 99, 66, 56, 66, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, @@ -208,8 +210,8 @@ func init() { // writer is a buffered writer. type writer interface { Flush() error - Write([]byte) (int, error) - WriteByte(byte) error + io.Writer + io.ByteWriter } // encoder encodes an image to the JPEG format. @@ -222,7 +224,7 @@ type encoder struct { buf [16]byte // bits and nBits are accumulated bits to write to w. bits, nBits uint32 - // quant is the scaled quantization tables. + // quant is the scaled quantization tables, in zig-zag order. quant [nQuantIndex][blockSize]byte } @@ -266,14 +268,14 @@ func (e *encoder) emit(bits, nBits uint32) { } // emitHuff emits the given value with the given Huffman encoder. -func (e *encoder) emitHuff(h huffIndex, value int) { +func (e *encoder) emitHuff(h huffIndex, value int32) { x := theHuffmanLUT[h][value] e.emit(x&(1<<24-1), x>>24) } // emitHuffRLE emits a run of runLength copies of value encoded with the given // Huffman encoder. -func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int) { +func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int32) { a, b := value, value if a < 0 { a, b = -value, value-1 @@ -284,7 +286,7 @@ func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int) { } else { nBits = 8 + uint32(bitCount[a>>8]) } - e.emitHuff(h, runLength<<4|int(nBits)) + e.emitHuff(h, runLength<<4|int32(nBits)) if nBits > 0 { e.emit(uint32(b)&(1<> 8) @@ -344,15 +346,16 @@ func (e *encoder) writeDHT() { // writeBlock writes a block of pixel data using the given quantization table, // returning the post-quantized DC value of the DCT-transformed block. -func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int) int { +// b is in natural (not zig-zag) order. +func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int32) int32 { fdct(b) // Emit the DC delta. - dc := div(b[0], (8 * int(e.quant[q][0]))) + dc := div(b[0], 8*int32(e.quant[q][0])) e.emitHuffRLE(huffIndex(2*q+0), 0, dc-prevDC) // Emit the AC components. - h, runLength := huffIndex(2*q+1), 0 - for k := 1; k < blockSize; k++ { - ac := div(b[unzig[k]], (8 * int(e.quant[q][k]))) + h, runLength := huffIndex(2*q+1), int32(0) + for zig := 1; zig < blockSize; zig++ { + ac := div(b[unzig[zig]], 8*int32(e.quant[q][zig])) if ac == 0 { runLength++ } else { @@ -380,9 +383,9 @@ func toYCbCr(m image.Image, p image.Point, yBlock, cbBlock, crBlock *block) { for i := 0; i < 8; i++ { r, g, b, _ := m.At(min(p.X+i, xmax), min(p.Y+j, ymax)).RGBA() yy, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) - yBlock[8*j+i] = int(yy) - cbBlock[8*j+i] = int(cb) - crBlock[8*j+i] = int(cr) + yBlock[8*j+i] = int32(yy) + cbBlock[8*j+i] = int32(cb) + crBlock[8*j+i] = int32(cr) } } } @@ -405,9 +408,9 @@ func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block) } pix := m.Pix[offset+sx*4:] yy, cb, cr := color.RGBToYCbCr(pix[0], pix[1], pix[2]) - yBlock[8*j+i] = int(yy) - cbBlock[8*j+i] = int(cb) - crBlock[8*j+i] = int(cr) + yBlock[8*j+i] = int32(yy) + cbBlock[8*j+i] = int32(cb) + crBlock[8*j+i] = int32(cr) } } } @@ -433,10 +436,12 @@ func scale(dst *block, src *[4]block) { // - component 1 uses DC table 0 and AC table 0 "\x01\x00", // - component 2 uses DC table 1 and AC table 1 "\x02\x11", // - component 3 uses DC table 1 and AC table 1 "\x03\x11", -// - padding "\x00\x00\x00". +// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for +// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) +// should be 0x00, 0x3f, 0x00<<4 | 0x00. var sosHeader = []byte{ 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, - 0x11, 0x03, 0x11, 0x00, 0x00, 0x00, + 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, } // writeSOS writes the StartOfScan marker. @@ -444,12 +449,11 @@ func (e *encoder) writeSOS(m image.Image) { e.write(sosHeader) var ( // Scratch buffers to hold the YCbCr values. - yBlock block - cbBlock [4]block - crBlock [4]block - cBlock block + // The blocks are in natural (not zig-zag) order. + b block + cb, cr [4]block // DC components are delta-encoded. - prevDCY, prevDCCb, prevDCCr int + prevDCY, prevDCCb, prevDCCr int32 ) bounds := m.Bounds() rgba, _ := m.(*image.RGBA) @@ -460,16 +464,16 @@ func (e *encoder) writeSOS(m image.Image) { yOff := (i & 2) * 4 p := image.Pt(x+xOff, y+yOff) if rgba != nil { - rgbaToYCbCr(rgba, p, &yBlock, &cbBlock[i], &crBlock[i]) + rgbaToYCbCr(rgba, p, &b, &cb[i], &cr[i]) } else { - toYCbCr(m, p, &yBlock, &cbBlock[i], &crBlock[i]) + toYCbCr(m, p, &b, &cb[i], &cr[i]) } - prevDCY = e.writeBlock(&yBlock, 0, prevDCY) + prevDCY = e.writeBlock(&b, 0, prevDCY) } - scale(&cBlock, &cbBlock) - prevDCCb = e.writeBlock(&cBlock, 1, prevDCCb) - scale(&cBlock, &crBlock) - prevDCCr = e.writeBlock(&cBlock, 1, prevDCCr) + scale(&b, &cb) + prevDCCb = e.writeBlock(&b, 1, prevDCCb) + scale(&b, &cr) + prevDCCr = e.writeBlock(&b, 1, prevDCCr) } } // Pad the last byte with 1's. diff --git a/src/pkg/image/jpeg/writer_test.go b/src/pkg/image/jpeg/writer_test.go index b8e8fa34e..0b2143f5b 100644 --- a/src/pkg/image/jpeg/writer_test.go +++ b/src/pkg/image/jpeg/writer_test.go @@ -6,6 +6,7 @@ package jpeg import ( "bytes" + "fmt" "image" "image/color" "image/png" @@ -15,6 +16,87 @@ import ( "testing" ) +// zigzag maps from the natural ordering to the zig-zag ordering. For example, +// zigzag[0*8 + 3] is the zig-zag sequence number of the element in the fourth +// column and first row. +var zigzag = [blockSize]int{ + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63, +} + +func TestZigUnzig(t *testing.T) { + for i := 0; i < blockSize; i++ { + if unzig[zigzag[i]] != i { + t.Errorf("unzig[zigzag[%d]] == %d", i, unzig[zigzag[i]]) + } + if zigzag[unzig[i]] != i { + t.Errorf("zigzag[unzig[%d]] == %d", i, zigzag[unzig[i]]) + } + } +} + +// unscaledQuantInNaturalOrder are the unscaled quantization tables in +// natural (not zig-zag) order, as specified in section K.1. +var unscaledQuantInNaturalOrder = [nQuantIndex][blockSize]byte{ + // Luminance. + { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99, + }, + // Chrominance. + { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + }, +} + +func TestUnscaledQuant(t *testing.T) { + bad := false + for i := quantIndex(0); i < nQuantIndex; i++ { + for zig := 0; zig < blockSize; zig++ { + got := unscaledQuant[i][zig] + want := unscaledQuantInNaturalOrder[i][unzig[zig]] + if got != want { + t.Errorf("i=%d, zig=%d: got %d, want %d", i, zig, got, want) + bad = true + } + } + } + if bad { + names := [nQuantIndex]string{"Luminance", "Chrominance"} + buf := &bytes.Buffer{} + for i, name := range names { + fmt.Fprintf(buf, "// %s.\n{\n", name) + for zig := 0; zig < blockSize; zig++ { + fmt.Fprintf(buf, "%d, ", unscaledQuantInNaturalOrder[i][unzig[zig]]) + if zig%8 == 7 { + buf.WriteString("\n") + } + } + buf.WriteString("},\n") + } + t.Logf("expected unscaledQuant values:\n%s", buf.String()) + } +} + var testCase = []struct { filename string quality int @@ -89,24 +171,21 @@ func TestWriter(t *testing.T) { } } -func BenchmarkEncodeRGBOpaque(b *testing.B) { +func BenchmarkEncode(b *testing.B) { b.StopTimer() img := image.NewRGBA(image.Rect(0, 0, 640, 480)) - // Set all pixels to 0xFF alpha to force opaque mode. bo := img.Bounds() rnd := rand.New(rand.NewSource(123)) for y := bo.Min.Y; y < bo.Max.Y; y++ { for x := bo.Min.X; x < bo.Max.X; x++ { - img.Set(x, y, color.RGBA{ + img.SetRGBA(x, y, color.RGBA{ uint8(rnd.Intn(256)), uint8(rnd.Intn(256)), uint8(rnd.Intn(256)), - 255}) + 255, + }) } } - if !img.Opaque() { - b.Fatal("expected image to be opaque") - } b.SetBytes(640 * 480 * 4) b.StartTimer() options := &Options{Quality: 90} diff --git a/src/pkg/image/names.go b/src/pkg/image/names.go index 55f634c17..04ee2cfb4 100644 --- a/src/pkg/image/names.go +++ b/src/pkg/image/names.go @@ -20,7 +20,7 @@ var ( ) // Uniform is an infinite-sized Image of uniform color. -// It implements the color.Color, color.ColorModel, and Image interfaces. +// It implements the color.Color, color.Model, and Image interfaces. type Uniform struct { C color.Color } diff --git a/src/pkg/image/png/paeth.go b/src/pkg/image/png/paeth.go new file mode 100644 index 000000000..37978aa66 --- /dev/null +++ b/src/pkg/image/png/paeth.go @@ -0,0 +1,70 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package png + +// paeth implements the Paeth filter function, as per the PNG specification. +func paeth(a, b, c uint8) uint8 { + // This is an optimized version of the sample code in the PNG spec. + // For example, the sample code starts with: + // p := int(a) + int(b) - int(c) + // pa := abs(p - int(a)) + // but the optimized form uses fewer arithmetic operations: + // pa := int(b) - int(c) + // pa = abs(pa) + pc := int(c) + pa := int(b) - pc + pb := int(a) - pc + pc = pa + pb + if pa < 0 { + pa = -pa + } + if pb < 0 { + pb = -pb + } + if pc < 0 { + pc = -pc + } + if pa <= pb && pa <= pc { + return a + } else if pb <= pc { + return b + } + return c +} + +// filterPaeth applies the Paeth filter to the cdat slice. +// cdat is the current row's data, pdat is the previous row's data. +func filterPaeth(cdat, pdat []byte, bytesPerPixel int) { + var a, b, c, pa, pb, pc int + for i := 0; i < bytesPerPixel; i++ { + a, c = 0, 0 + for j := i; j < len(cdat); j += bytesPerPixel { + b = int(pdat[j]) + pa = b - c + pb = a - c + pc = pa + pb + if pa < 0 { + pa = -pa + } + if pb < 0 { + pb = -pb + } + if pc < 0 { + pc = -pc + } + if pa <= pb && pa <= pc { + // No-op. + } else if pb <= pc { + a = b + } else { + a = c + } + a += int(cdat[j]) + a &= 0xff + cdat[j] = uint8(a) + c = b + } + } +} diff --git a/src/pkg/image/png/paeth_test.go b/src/pkg/image/png/paeth_test.go new file mode 100644 index 000000000..bb084861a --- /dev/null +++ b/src/pkg/image/png/paeth_test.go @@ -0,0 +1,91 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package png + +import ( + "bytes" + "math/rand" + "testing" +) + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} + +// slowPaeth is a slow but simple implementation of the Paeth function. +// It is a straight port of the sample code in the PNG spec, section 9.4. +func slowPaeth(a, b, c uint8) uint8 { + p := int(a) + int(b) - int(c) + pa := abs(p - int(a)) + pb := abs(p - int(b)) + pc := abs(p - int(c)) + if pa <= pb && pa <= pc { + return a + } else if pb <= pc { + return b + } + return c +} + +// slowFilterPaeth is a slow but simple implementation of func filterPaeth. +func slowFilterPaeth(cdat, pdat []byte, bytesPerPixel int) { + for i := 0; i < bytesPerPixel; i++ { + cdat[i] += paeth(0, pdat[i], 0) + } + for i := bytesPerPixel; i < len(cdat); i++ { + cdat[i] += paeth(cdat[i-bytesPerPixel], pdat[i], pdat[i-bytesPerPixel]) + } +} + +func TestPaeth(t *testing.T) { + for a := 0; a < 256; a += 15 { + for b := 0; b < 256; b += 15 { + for c := 0; c < 256; c += 15 { + got := paeth(uint8(a), uint8(b), uint8(c)) + want := slowPaeth(uint8(a), uint8(b), uint8(c)) + if got != want { + t.Errorf("a, b, c = %d, %d, %d: got %d, want %d", a, b, c, got, want) + } + } + } + } +} + +func BenchmarkPaeth(b *testing.B) { + for i := 0; i < b.N; i++ { + paeth(uint8(i>>16), uint8(i>>8), uint8(i)) + } +} + +func TestPaethDecode(t *testing.T) { + pdat0 := make([]byte, 32) + pdat1 := make([]byte, 32) + pdat2 := make([]byte, 32) + cdat0 := make([]byte, 32) + cdat1 := make([]byte, 32) + cdat2 := make([]byte, 32) + r := rand.New(rand.NewSource(1)) + for bytesPerPixel := 1; bytesPerPixel <= 8; bytesPerPixel++ { + for i := 0; i < 100; i++ { + for j := range pdat0 { + pdat0[j] = uint8(r.Uint32()) + cdat0[j] = uint8(r.Uint32()) + } + copy(pdat1, pdat0) + copy(pdat2, pdat0) + copy(cdat1, cdat0) + copy(cdat2, cdat0) + filterPaeth(cdat1, pdat1, bytesPerPixel) + slowFilterPaeth(cdat2, pdat2, bytesPerPixel) + if !bytes.Equal(cdat1, cdat2) { + t.Errorf("bytesPerPixel: %d\npdat0: % x\ncdat0: % x\ngot: % x\nwant: % x", bytesPerPixel, pdat0, cdat0, cdat1, cdat2) + break + } + } + } +} diff --git a/src/pkg/image/png/reader.go b/src/pkg/image/png/reader.go index fe07d60a9..a6bf86ede 100644 --- a/src/pkg/image/png/reader.go +++ b/src/pkg/image/png/reader.go @@ -98,13 +98,6 @@ type UnsupportedError string func (e UnsupportedError) Error() string { return "png: unsupported feature: " + string(e) } -func abs(x int) int { - if x < 0 { - return -x - } - return x -} - func min(a, b int) int { if a < b { return a @@ -200,10 +193,19 @@ func (d *decoder) parsePLTE(length uint32) error { d.crc.Write(d.tmp[:n]) switch d.cb { case cbP1, cbP2, cbP4, cbP8: - d.palette = color.Palette(make([]color.Color, np)) + d.palette = make(color.Palette, 256) for i := 0; i < np; i++ { d.palette[i] = color.RGBA{d.tmp[3*i+0], d.tmp[3*i+1], d.tmp[3*i+2], 0xff} } + for i := np; i < 256; i++ { + // Initialize the rest of the palette to opaque black. The spec (section + // 11.2.3) says that "any out-of-range pixel value found in the image data + // is an error", but some real-world PNG files have out-of-range pixel + // values. We fall back to opaque black, the same as libpng 1.5.13; + // ImageMagick 6.5.7 returns an error. + d.palette[i] = color.RGBA{0x00, 0x00, 0x00, 0xff} + } + d.palette = d.palette[:np] case cbTC8, cbTCA8, cbTC16, cbTCA16: // As per the PNG spec, a PLTE chunk is optional (and for practical purposes, // ignorable) for the ctTrueColor and ctTrueColorAlpha color types (section 4.1.2). @@ -228,12 +230,12 @@ func (d *decoder) parsetRNS(length uint32) error { case cbTC8, cbTC16: return UnsupportedError("truecolor transparency") case cbP1, cbP2, cbP4, cbP8: - if n > len(d.palette) { - return FormatError("bad tRNS length") + if len(d.palette) < n { + d.palette = d.palette[:n] } for i := 0; i < n; i++ { rgba := d.palette[i].(color.RGBA) - d.palette[i] = color.RGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]} + d.palette[i] = color.NRGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]} } case cbGA8, cbGA16, cbTCA8, cbTCA16: return FormatError("tRNS, color type mismatch") @@ -241,20 +243,6 @@ func (d *decoder) parsetRNS(length uint32) error { return d.verifyChecksum() } -// The Paeth filter function, as per the PNG specification. -func paeth(a, b, c uint8) uint8 { - p := int(a) + int(b) - int(c) - pa := abs(p - int(a)) - pb := abs(p - int(b)) - pc := abs(p - int(c)) - if pa <= pb && pa <= pc { - return a - } else if pb <= pc { - return b - } - return c -} - // Read presents one or more IDAT chunks as one continuous stream (minus the // intermediate chunk headers and footers). If the PNG data looked like: // ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2 @@ -300,7 +288,7 @@ func (d *decoder) decode() (image.Image, error) { } defer r.Close() bitsPerPixel := 0 - maxPalette := uint8(0) + pixOffset := 0 var ( gray *image.Gray rgba *image.RGBA @@ -328,7 +316,6 @@ func (d *decoder) decode() (image.Image, error) { bitsPerPixel = d.depth paletted = image.NewPaletted(image.Rect(0, 0, d.width, d.height), d.palette) img = paletted - maxPalette = uint8(len(d.palette) - 1) case cbTCA8: bitsPerPixel = 32 nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height)) @@ -375,8 +362,8 @@ func (d *decoder) decode() (image.Image, error) { cdat[i] += cdat[i-bytesPerPixel] } case ftUp: - for i := 0; i < len(cdat); i++ { - cdat[i] += pdat[i] + for i, p := range pdat { + cdat[i] += p } case ftAverage: for i := 0; i < bytesPerPixel; i++ { @@ -386,12 +373,7 @@ func (d *decoder) decode() (image.Image, error) { cdat[i] += uint8((int(cdat[i-bytesPerPixel]) + int(pdat[i])) / 2) } case ftPaeth: - for i := 0; i < bytesPerPixel; i++ { - cdat[i] += paeth(0, pdat[i], 0) - } - for i := bytesPerPixel; i < len(cdat); i++ { - cdat[i] += paeth(cdat[i-bytesPerPixel], pdat[i], pdat[i-bytesPerPixel]) - } + filterPaeth(cdat, pdat, bytesPerPixel) default: return nil, FormatError("bad filter type") } @@ -423,25 +405,31 @@ func (d *decoder) decode() (image.Image, error) { } } case cbG8: - for x := 0; x < d.width; x++ { - gray.SetGray(x, y, color.Gray{cdat[x]}) - } + copy(gray.Pix[pixOffset:], cdat) + pixOffset += gray.Stride case cbGA8: for x := 0; x < d.width; x++ { ycol := cdat[2*x+0] nrgba.SetNRGBA(x, y, color.NRGBA{ycol, ycol, ycol, cdat[2*x+1]}) } case cbTC8: + pix, i, j := rgba.Pix, pixOffset, 0 for x := 0; x < d.width; x++ { - rgba.SetRGBA(x, y, color.RGBA{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff}) + pix[i+0] = cdat[j+0] + pix[i+1] = cdat[j+1] + pix[i+2] = cdat[j+2] + pix[i+3] = 0xff + i += 4 + j += 3 } + pixOffset += rgba.Stride case cbP1: for x := 0; x < d.width; x += 8 { b := cdat[x/8] for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ { idx := b >> 7 - if idx > maxPalette { - return nil, FormatError("palette index out of range") + if len(paletted.Palette) <= int(idx) { + paletted.Palette = paletted.Palette[:int(idx)+1] } paletted.SetColorIndex(x+x2, y, idx) b <<= 1 @@ -452,8 +440,8 @@ func (d *decoder) decode() (image.Image, error) { b := cdat[x/4] for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ { idx := b >> 6 - if idx > maxPalette { - return nil, FormatError("palette index out of range") + if len(paletted.Palette) <= int(idx) { + paletted.Palette = paletted.Palette[:int(idx)+1] } paletted.SetColorIndex(x+x2, y, idx) b <<= 2 @@ -464,24 +452,26 @@ func (d *decoder) decode() (image.Image, error) { b := cdat[x/2] for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ { idx := b >> 4 - if idx > maxPalette { - return nil, FormatError("palette index out of range") + if len(paletted.Palette) <= int(idx) { + paletted.Palette = paletted.Palette[:int(idx)+1] } paletted.SetColorIndex(x+x2, y, idx) b <<= 4 } } case cbP8: - for x := 0; x < d.width; x++ { - if cdat[x] > maxPalette { - return nil, FormatError("palette index out of range") + if len(paletted.Palette) != 255 { + for x := 0; x < d.width; x++ { + if len(paletted.Palette) <= int(cdat[x]) { + paletted.Palette = paletted.Palette[:int(cdat[x])+1] + } } - paletted.SetColorIndex(x, y, cdat[x]) } + copy(paletted.Pix[pixOffset:], cdat) + pixOffset += paletted.Stride case cbTCA8: - for x := 0; x < d.width; x++ { - nrgba.SetNRGBA(x, y, color.NRGBA{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]}) - } + copy(nrgba.Pix[pixOffset:], cdat) + pixOffset += nrgba.Stride case cbG16: for x := 0; x < d.width; x++ { ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1]) @@ -662,10 +652,11 @@ func DecodeConfig(r io.Reader) (image.Config, error) { } return image.Config{}, err } - if d.stage == dsSeenIHDR && d.cb != cbP8 { + paletted := d.cb == cbP8 || d.cb == cbP4 || d.cb == cbP2 || d.cb == cbP1 + if d.stage == dsSeenIHDR && !paletted { break } - if d.stage == dsSeenPLTE && d.cb == cbP8 { + if d.stage == dsSeenPLTE && paletted { break } } diff --git a/src/pkg/image/png/reader_test.go b/src/pkg/image/png/reader_test.go index 24c4ea448..ac0d949a9 100644 --- a/src/pkg/image/png/reader_test.go +++ b/src/pkg/image/png/reader_test.go @@ -10,6 +10,7 @@ import ( "image" "image/color" "io" + "io/ioutil" "os" "strings" "testing" @@ -37,6 +38,14 @@ var filenames = []string{ "basn6a16", } +var filenamesPaletted = []string{ + "basn3p01", + "basn3p02", + "basn3p04", + "basn3p08", + "basn3p08-trns", +} + var filenamesShort = []string{ "basn0g01", "basn0g04-31", @@ -106,13 +115,18 @@ func sng(w io.WriteCloser, filename string, png image.Image) { lastAlpha := -1 io.WriteString(w, "PLTE {\n") for i, c := range cpm { - r, g, b, a := c.RGBA() - if a != 0xffff { + var r, g, b, a uint8 + switch c := c.(type) { + case color.RGBA: + r, g, b, a = c.R, c.G, c.B, 0xff + case color.NRGBA: + r, g, b, a = c.R, c.G, c.B, c.A + default: + panic("unknown palette color type") + } + if a != 0xff { lastAlpha = i } - r >>= 8 - g >>= 8 - b >>= 8 fmt.Fprintf(w, " (%3d,%3d,%3d) # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b) } io.WriteString(w, "}\n") @@ -202,7 +216,7 @@ func TestReader(t *testing.T) { } piper, pipew := io.Pipe() - pb := bufio.NewReader(piper) + pb := bufio.NewScanner(piper) go sng(pipew, fn, img) defer piper.Close() @@ -213,7 +227,7 @@ func TestReader(t *testing.T) { continue } defer sf.Close() - sb := bufio.NewReader(sf) + sb := bufio.NewScanner(sf) if err != nil { t.Error(fn, err) continue @@ -221,24 +235,28 @@ func TestReader(t *testing.T) { // Compare the two, in SNG format, line by line. for { - ps, perr := pb.ReadString('\n') - ss, serr := sb.ReadString('\n') - if perr == io.EOF && serr == io.EOF { + pdone := pb.Scan() + sdone := sb.Scan() + if pdone && sdone { break } - if perr != nil { - t.Error(fn, perr) - break - } - if serr != nil { - t.Error(fn, serr) + if pdone || sdone { + t.Errorf("%s: Different sizes", fn) break } + ps := pb.Text() + ss := sb.Text() if ps != ss { t.Errorf("%s: Mismatch\n%sversus\n%s\n", fn, ps, ss) break } } + if pb.Err() != nil { + t.Error(fn, pb.Err()) + } + if sb.Err() != nil { + t.Error(fn, sb.Err()) + } } } @@ -267,3 +285,66 @@ func TestReaderError(t *testing.T) { } } } + +func TestPalettedDecodeConfig(t *testing.T) { + for _, fn := range filenamesPaletted { + f, err := os.Open("testdata/pngsuite/" + fn + ".png") + if err != nil { + t.Errorf("%s: open failed: %v", fn, err) + continue + } + defer f.Close() + cfg, err := DecodeConfig(f) + if err != nil { + t.Errorf("%s: %v", fn, err) + continue + } + pal, ok := cfg.ColorModel.(color.Palette) + if !ok { + t.Errorf("%s: expected paletted color model", fn) + continue + } + if pal == nil { + t.Errorf("%s: palette not initialized", fn) + continue + } + } +} + +func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) { + b.StopTimer() + data, err := ioutil.ReadFile(filename) + if err != nil { + b.Fatal(err) + } + s := string(data) + cfg, err := DecodeConfig(strings.NewReader(s)) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(cfg.Width * cfg.Height * bytesPerPixel)) + b.StartTimer() + for i := 0; i < b.N; i++ { + Decode(strings.NewReader(s)) + } +} + +func BenchmarkDecodeGray(b *testing.B) { + benchmarkDecode(b, "testdata/benchGray.png", 1) +} + +func BenchmarkDecodeNRGBAGradient(b *testing.B) { + benchmarkDecode(b, "testdata/benchNRGBA-gradient.png", 4) +} + +func BenchmarkDecodeNRGBAOpaque(b *testing.B) { + benchmarkDecode(b, "testdata/benchNRGBA-opaque.png", 4) +} + +func BenchmarkDecodePaletted(b *testing.B) { + benchmarkDecode(b, "testdata/benchPaletted.png", 1) +} + +func BenchmarkDecodeRGB(b *testing.B) { + benchmarkDecode(b, "testdata/benchRGB.png", 4) +} diff --git a/src/pkg/image/png/testdata/benchGray.png b/src/pkg/image/png/testdata/benchGray.png new file mode 100644 index 000000000..42bc6c3a0 Binary files /dev/null and b/src/pkg/image/png/testdata/benchGray.png differ diff --git a/src/pkg/image/png/testdata/benchNRGBA-gradient.png b/src/pkg/image/png/testdata/benchNRGBA-gradient.png new file mode 100644 index 000000000..961934cca Binary files /dev/null and b/src/pkg/image/png/testdata/benchNRGBA-gradient.png differ diff --git a/src/pkg/image/png/testdata/benchNRGBA-opaque.png b/src/pkg/image/png/testdata/benchNRGBA-opaque.png new file mode 100644 index 000000000..ca4f4a037 Binary files /dev/null and b/src/pkg/image/png/testdata/benchNRGBA-opaque.png differ diff --git a/src/pkg/image/png/testdata/benchPaletted.png b/src/pkg/image/png/testdata/benchPaletted.png new file mode 100644 index 000000000..4b4d5b992 Binary files /dev/null and b/src/pkg/image/png/testdata/benchPaletted.png differ diff --git a/src/pkg/image/png/testdata/benchRGB.png b/src/pkg/image/png/testdata/benchRGB.png new file mode 100644 index 000000000..31ac65a3f Binary files /dev/null and b/src/pkg/image/png/testdata/benchRGB.png differ diff --git a/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go index 57c03792b..093d47193 100644 --- a/src/pkg/image/png/writer.go +++ b/src/pkg/image/png/writer.go @@ -21,7 +21,7 @@ type encoder struct { err error header [8]byte footer [4]byte - tmp [3 * 256]byte + tmp [4 * 256]byte } // Big-endian. @@ -70,7 +70,7 @@ func (e *encoder) writeChunk(b []byte, name string) { e.err = UnsupportedError(name + " chunk is too large: " + strconv.Itoa(len(b))) return } - writeUint32(e.header[0:4], n) + writeUint32(e.header[:4], n) e.header[4] = name[0] e.header[5] = name[1] e.header[6] = name[2] @@ -78,9 +78,9 @@ func (e *encoder) writeChunk(b []byte, name string) { crc := crc32.NewIEEE() crc.Write(e.header[4:8]) crc.Write(b) - writeUint32(e.footer[0:4], crc.Sum32()) + writeUint32(e.footer[:4], crc.Sum32()) - _, e.err = e.w.Write(e.header[0:8]) + _, e.err = e.w.Write(e.header[:8]) if e.err != nil { return } @@ -88,7 +88,7 @@ func (e *encoder) writeChunk(b []byte, name string) { if e.err != nil { return } - _, e.err = e.w.Write(e.footer[0:4]) + _, e.err = e.w.Write(e.footer[:4]) } func (e *encoder) writeIHDR() { @@ -122,36 +122,29 @@ func (e *encoder) writeIHDR() { e.tmp[10] = 0 // default compression method e.tmp[11] = 0 // default filter method e.tmp[12] = 0 // non-interlaced - e.writeChunk(e.tmp[0:13], "IHDR") + e.writeChunk(e.tmp[:13], "IHDR") } -func (e *encoder) writePLTE(p color.Palette) { +func (e *encoder) writePLTEAndTRNS(p color.Palette) { if len(p) < 1 || len(p) > 256 { e.err = FormatError("bad palette length: " + strconv.Itoa(len(p))) return } - for i, c := range p { - r, g, b, _ := c.RGBA() - e.tmp[3*i+0] = uint8(r >> 8) - e.tmp[3*i+1] = uint8(g >> 8) - e.tmp[3*i+2] = uint8(b >> 8) - } - e.writeChunk(e.tmp[0:3*len(p)], "PLTE") -} - -func (e *encoder) maybeWritetRNS(p color.Palette) { last := -1 for i, c := range p { - _, _, _, a := c.RGBA() - if a != 0xffff { + c1 := color.NRGBAModel.Convert(c).(color.NRGBA) + e.tmp[3*i+0] = c1.R + e.tmp[3*i+1] = c1.G + e.tmp[3*i+2] = c1.B + if c1.A != 0xff { last = i } - e.tmp[i] = uint8(a >> 8) + e.tmp[3*256+i] = c1.A } - if last == -1 { - return + e.writeChunk(e.tmp[:3*len(p)], "PLTE") + if last != -1 { + e.writeChunk(e.tmp[3*256:3*256+1+last], "tRNS") } - e.writeChunk(e.tmp[:last+1], "tRNS") } // An encoder is an io.Writer that satisfies writes by writing PNG IDAT chunks, @@ -297,26 +290,42 @@ func writeImage(w io.Writer, m image.Image, cb int) error { } pr := make([]uint8, 1+bpp*b.Dx()) + gray, _ := m.(*image.Gray) + rgba, _ := m.(*image.RGBA) + paletted, _ := m.(*image.Paletted) + nrgba, _ := m.(*image.NRGBA) + for y := b.Min.Y; y < b.Max.Y; y++ { // Convert from colors to bytes. i := 1 switch cb { case cbG8: - for x := b.Min.X; x < b.Max.X; x++ { - c := color.GrayModel.Convert(m.At(x, y)).(color.Gray) - cr[0][i] = c.Y - i++ + if gray != nil { + offset := (y - b.Min.Y) * gray.Stride + copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()]) + } else { + for x := b.Min.X; x < b.Max.X; x++ { + c := color.GrayModel.Convert(m.At(x, y)).(color.Gray) + cr[0][i] = c.Y + i++ + } } case cbTC8: // We have previously verified that the alpha value is fully opaque. cr0 := cr[0] - if rgba, _ := m.(*image.RGBA); rgba != nil { - j0 := (y - b.Min.Y) * rgba.Stride + stride, pix := 0, []byte(nil) + if rgba != nil { + stride, pix = rgba.Stride, rgba.Pix + } else if nrgba != nil { + stride, pix = nrgba.Stride, nrgba.Pix + } + if stride != 0 { + j0 := (y - b.Min.Y) * stride j1 := j0 + b.Dx()*4 for j := j0; j < j1; j += 4 { - cr0[i+0] = rgba.Pix[j+0] - cr0[i+1] = rgba.Pix[j+1] - cr0[i+2] = rgba.Pix[j+2] + cr0[i+0] = pix[j+0] + cr0[i+1] = pix[j+1] + cr0[i+2] = pix[j+2] i += 3 } } else { @@ -329,9 +338,9 @@ func writeImage(w io.Writer, m image.Image, cb int) error { } } case cbP8: - if p, _ := m.(*image.Paletted); p != nil { - offset := (y - b.Min.Y) * p.Stride - copy(cr[0][1:], p.Pix[offset:offset+b.Dx()]) + if paletted != nil { + offset := (y - b.Min.Y) * paletted.Stride + copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()]) } else { pi := m.(image.PalettedImage) for x := b.Min.X; x < b.Max.X; x++ { @@ -340,14 +349,19 @@ func writeImage(w io.Writer, m image.Image, cb int) error { } } case cbTCA8: - // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied. - for x := b.Min.X; x < b.Max.X; x++ { - c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA) - cr[0][i+0] = c.R - cr[0][i+1] = c.G - cr[0][i+2] = c.B - cr[0][i+3] = c.A - i += 4 + if nrgba != nil { + offset := (y - b.Min.Y) * nrgba.Stride + copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4]) + } else { + // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied. + for x := b.Min.X; x < b.Max.X; x++ { + c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA) + cr[0][i+0] = c.R + cr[0][i+1] = c.G + cr[0][i+2] = c.B + cr[0][i+3] = c.A + i += 4 + } } case cbG16: for x := b.Min.X; x < b.Max.X; x++ { @@ -412,7 +426,7 @@ func (e *encoder) writeIDATs() { e.err = bw.Flush() } -func (e *encoder) writeIEND() { e.writeChunk(e.tmp[0:0], "IEND") } +func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") } // Encode writes the Image m to w in PNG format. Any Image may be encoded, but // images that are not image.NRGBA might be encoded lossily. @@ -460,8 +474,7 @@ func Encode(w io.Writer, m image.Image) error { _, e.err = io.WriteString(w, pngHeader) e.writeIHDR() if pal != nil { - e.writePLTE(pal) - e.maybeWritetRNS(pal) + e.writePLTEAndTRNS(pal) } e.writeIDATs() e.writeIEND() diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go index 644c4fb44..3116fc9ff 100644 --- a/src/pkg/image/png/writer_test.go +++ b/src/pkg/image/png/writer_test.go @@ -101,6 +101,49 @@ func TestSubImage(t *testing.T) { } } +func BenchmarkEncodeGray(b *testing.B) { + b.StopTimer() + img := image.NewGray(image.Rect(0, 0, 640, 480)) + b.SetBytes(640 * 480 * 1) + b.StartTimer() + for i := 0; i < b.N; i++ { + Encode(ioutil.Discard, img) + } +} + +func BenchmarkEncodeNRGBOpaque(b *testing.B) { + b.StopTimer() + img := image.NewNRGBA(image.Rect(0, 0, 640, 480)) + // Set all pixels to 0xFF alpha to force opaque mode. + bo := img.Bounds() + for y := bo.Min.Y; y < bo.Max.Y; y++ { + for x := bo.Min.X; x < bo.Max.X; x++ { + img.Set(x, y, color.NRGBA{0, 0, 0, 255}) + } + } + if !img.Opaque() { + b.Fatal("expected image to be opaque") + } + b.SetBytes(640 * 480 * 4) + b.StartTimer() + for i := 0; i < b.N; i++ { + Encode(ioutil.Discard, img) + } +} + +func BenchmarkEncodeNRGBA(b *testing.B) { + b.StopTimer() + img := image.NewNRGBA(image.Rect(0, 0, 640, 480)) + if img.Opaque() { + b.Fatal("expected image not to be opaque") + } + b.SetBytes(640 * 480 * 4) + b.StartTimer() + for i := 0; i < b.N; i++ { + Encode(ioutil.Discard, img) + } +} + func BenchmarkEncodePaletted(b *testing.B) { b.StopTimer() img := image.NewPaletted(image.Rect(0, 0, 640, 480), color.Palette{ @@ -138,7 +181,7 @@ func BenchmarkEncodeRGBA(b *testing.B) { b.StopTimer() img := image.NewRGBA(image.Rect(0, 0, 640, 480)) if img.Opaque() { - b.Fatal("expected image to not be opaque") + b.Fatal("expected image not to be opaque") } b.SetBytes(640 * 480 * 4) b.StartTimer() diff --git a/src/pkg/image/testdata/video-001.progressive.jpeg b/src/pkg/image/testdata/video-001.progressive.jpeg new file mode 100644 index 000000000..b8cae2359 Binary files /dev/null and b/src/pkg/image/testdata/video-001.progressive.jpeg differ diff --git a/src/pkg/image/testdata/video-001.q50.420.jpeg b/src/pkg/image/testdata/video-001.q50.420.jpeg new file mode 100644 index 000000000..83fb0f8ab Binary files /dev/null and b/src/pkg/image/testdata/video-001.q50.420.jpeg differ diff --git a/src/pkg/image/testdata/video-001.q50.420.progressive.jpeg b/src/pkg/image/testdata/video-001.q50.420.progressive.jpeg new file mode 100644 index 000000000..b048eb205 Binary files /dev/null and b/src/pkg/image/testdata/video-001.q50.420.progressive.jpeg differ diff --git a/src/pkg/image/testdata/video-001.q50.422.jpeg b/src/pkg/image/testdata/video-001.q50.422.jpeg new file mode 100644 index 000000000..60fff4ff9 Binary files /dev/null and b/src/pkg/image/testdata/video-001.q50.422.jpeg differ diff --git a/src/pkg/image/testdata/video-001.q50.422.progressive.jpeg b/src/pkg/image/testdata/video-001.q50.422.progressive.jpeg new file mode 100644 index 000000000..926d005de Binary files /dev/null and b/src/pkg/image/testdata/video-001.q50.422.progressive.jpeg differ diff --git a/src/pkg/image/testdata/video-001.q50.440.jpeg b/src/pkg/image/testdata/video-001.q50.440.jpeg new file mode 100644 index 000000000..32eeeaef6 Binary files /dev/null and b/src/pkg/image/testdata/video-001.q50.440.jpeg differ diff --git a/src/pkg/image/testdata/video-001.q50.440.progressive.jpeg b/src/pkg/image/testdata/video-001.q50.440.progressive.jpeg new file mode 100644 index 000000000..e641a3bbb Binary files /dev/null and b/src/pkg/image/testdata/video-001.q50.440.progressive.jpeg differ diff --git a/src/pkg/image/testdata/video-001.q50.444.jpeg b/src/pkg/image/testdata/video-001.q50.444.jpeg new file mode 100644 index 000000000..7d5743382 Binary files /dev/null and b/src/pkg/image/testdata/video-001.q50.444.jpeg differ diff --git a/src/pkg/image/testdata/video-001.q50.444.progressive.jpeg b/src/pkg/image/testdata/video-001.q50.444.progressive.jpeg new file mode 100644 index 000000000..ff7d5f9ff Binary files /dev/null and b/src/pkg/image/testdata/video-001.q50.444.progressive.jpeg differ diff --git a/src/pkg/image/testdata/video-005.gray.q50.2x2.jpeg b/src/pkg/image/testdata/video-005.gray.q50.2x2.jpeg new file mode 100644 index 000000000..630b615f7 Binary files /dev/null and b/src/pkg/image/testdata/video-005.gray.q50.2x2.jpeg differ diff --git a/src/pkg/image/testdata/video-005.gray.q50.2x2.progressive.jpeg b/src/pkg/image/testdata/video-005.gray.q50.2x2.progressive.jpeg new file mode 100644 index 000000000..c6b93608c Binary files /dev/null and b/src/pkg/image/testdata/video-005.gray.q50.2x2.progressive.jpeg differ diff --git a/src/pkg/image/testdata/video-005.gray.q50.jpeg b/src/pkg/image/testdata/video-005.gray.q50.jpeg new file mode 100644 index 000000000..c65b5a794 Binary files /dev/null and b/src/pkg/image/testdata/video-005.gray.q50.jpeg differ diff --git a/src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg b/src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg new file mode 100644 index 000000000..24b70e8bf Binary files /dev/null and b/src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg differ diff --git a/src/pkg/image/ycbcr.go b/src/pkg/image/ycbcr.go index c1a0b666f..5b73bef78 100644 --- a/src/pkg/image/ycbcr.go +++ b/src/pkg/image/ycbcr.go @@ -15,6 +15,7 @@ const ( YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota YCbCrSubsampleRatio422 YCbCrSubsampleRatio420 + YCbCrSubsampleRatio440 ) func (s YCbCrSubsampleRatio) String() string { @@ -25,6 +26,8 @@ func (s YCbCrSubsampleRatio) String() string { return "YCbCrSubsampleRatio422" case YCbCrSubsampleRatio420: return "YCbCrSubsampleRatio420" + case YCbCrSubsampleRatio440: + return "YCbCrSubsampleRatio440" } return "YCbCrSubsampleRatioUnknown" } @@ -39,6 +42,7 @@ func (s YCbCrSubsampleRatio) String() string { // For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1. // For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2. // For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4. +// For 4:4:0, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/2. type YCbCr struct { Y, Cb, Cr []uint8 YStride int @@ -82,6 +86,8 @@ func (p *YCbCr) COffset(x, y int) int { return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2) case YCbCrSubsampleRatio420: return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2) + case YCbCrSubsampleRatio440: + return (y/2-p.Rect.Min.Y/2)*p.CStride + (x - p.Rect.Min.X) } // Default to 4:4:4 subsampling. return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X) @@ -126,6 +132,9 @@ func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr { case YCbCrSubsampleRatio420: cw = (r.Max.X+1)/2 - r.Min.X/2 ch = (r.Max.Y+1)/2 - r.Min.Y/2 + case YCbCrSubsampleRatio440: + cw = w + ch = (r.Max.Y+1)/2 - r.Min.Y/2 default: // Default to 4:4:4 subsampling. cw = w diff --git a/src/pkg/image/ycbcr_test.go b/src/pkg/image/ycbcr_test.go index 5fa95be3e..a5f448265 100644 --- a/src/pkg/image/ycbcr_test.go +++ b/src/pkg/image/ycbcr_test.go @@ -36,6 +36,7 @@ func TestYCbCr(t *testing.T) { YCbCrSubsampleRatio444, YCbCrSubsampleRatio422, YCbCrSubsampleRatio420, + YCbCrSubsampleRatio440, } deltas := []Point{ Pt(0, 0), diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go index 54bf159eb..23d05e575 100644 --- a/src/pkg/io/io.go +++ b/src/pkg/io/io.go @@ -130,11 +130,23 @@ type ReadWriteSeeker interface { } // ReaderFrom is the interface that wraps the ReadFrom method. +// +// ReadFrom reads data from r until EOF or error. +// The return value n is the number of bytes read. +// Any error except io.EOF encountered during the read is also returned. +// +// The Copy function uses ReaderFrom if available. type ReaderFrom interface { ReadFrom(r Reader) (n int64, err error) } // WriterTo is the interface that wraps the WriteTo method. +// +// WriteTo writes data to w until there's no more data to write or +// when an error occurs. The return value n is the number of bytes +// written. Any error encountered during the write is also returned. +// +// The Copy function uses WriterTo if available. type WriterTo interface { WriteTo(w Writer) (n int64, err error) } @@ -204,6 +216,11 @@ type ByteScanner interface { UnreadByte() error } +// ByteWriter is the interface that wraps the WriteByte method. +type ByteWriter interface { + WriteByte(c byte) error +} + // RuneReader is the interface that wraps the ReadRune method. // // ReadRune reads a single UTF-8 encoded Unicode character @@ -245,6 +262,7 @@ func WriteString(w Writer, s string) (n int, err error) { // If an EOF happens after reading fewer than min bytes, // ReadAtLeast returns ErrUnexpectedEOF. // If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer. +// On return, n >= min if and only if err == nil. func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) { if len(buf) < min { return 0, ErrShortBuffer @@ -254,12 +272,10 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) { nn, err = r.Read(buf[n:]) n += nn } - if err == EOF { - if n >= min { - err = nil - } else if n > 0 { - err = ErrUnexpectedEOF - } + if n >= min { + err = nil + } else if n > 0 && err == EOF { + err = ErrUnexpectedEOF } return } @@ -269,56 +285,28 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) { // The error is EOF only if no bytes were read. // If an EOF happens after reading some but not all the bytes, // ReadFull returns ErrUnexpectedEOF. +// On return, n == len(buf) if and only if err == nil. func ReadFull(r Reader, buf []byte) (n int, err error) { return ReadAtLeast(r, buf, len(buf)) } // CopyN copies n bytes (or until an error) from src to dst. // It returns the number of bytes copied and the earliest -// error encountered while copying. Because Read can -// return the full amount requested as well as an error -// (including EOF), so can CopyN. +// error encountered while copying. +// On return, written == n if and only if err == nil. // // If dst implements the ReaderFrom interface, // the copy is implemented using it. func CopyN(dst Writer, src Reader, n int64) (written int64, err error) { - // If the writer has a ReadFrom method, use it to do the copy. - // Avoids a buffer allocation and a copy. - if rt, ok := dst.(ReaderFrom); ok { - written, err = rt.ReadFrom(LimitReader(src, n)) - if written < n && err == nil { - // rt stopped early; must have been EOF. - err = EOF - } - return + written, err = Copy(dst, LimitReader(src, n)) + if written == n { + return n, nil } - buf := make([]byte, 32*1024) - for written < n { - l := len(buf) - if d := n - written; d < int64(l) { - l = int(d) - } - nr, er := src.Read(buf[0:l]) - if nr > 0 { - nw, ew := dst.Write(buf[0:nr]) - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - if nr != nw { - err = ErrShortWrite - break - } - } - if er != nil { - err = er - break - } + if written < n && err == nil { + // src stopped early; must have been EOF. + err = EOF } - return written, err + return } // Copy copies from src to dst until either EOF is reached @@ -451,6 +439,11 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) { off += s.base if max := s.limit - off; int64(len(p)) > max { p = p[0:max] + n, err = s.r.ReadAt(p, off) + if err == nil { + err = EOF + } + return n, err } return s.r.ReadAt(p, off) } diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go index 1e671b59b..1bc451e44 100644 --- a/src/pkg/io/io_test.go +++ b/src/pkg/io/io_test.go @@ -6,6 +6,8 @@ package io_test import ( "bytes" + "errors" + "fmt" . "io" "strings" "testing" @@ -88,6 +90,12 @@ func (w *noReadFrom) Write(p []byte) (n int, err error) { return w.w.Write(p) } +type wantedAndErrReader struct{} + +func (wantedAndErrReader) Read(p []byte) (int, error) { + return len(p), errors.New("wantedAndErrReader error") +} + func TestCopyNEOF(t *testing.T) { // Test that EOF behavior is the same regardless of whether // argument to CopyN has ReadFrom. @@ -113,6 +121,16 @@ func TestCopyNEOF(t *testing.T) { if n != 3 || err != EOF { t.Errorf("CopyN(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err) } + + n, err = CopyN(b, wantedAndErrReader{}, 5) + if n != 5 || err != nil { + t.Errorf("CopyN(bytes.Buffer, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err) + } + + n, err = CopyN(&noReadFrom{b}, wantedAndErrReader{}, 5) + if n != 5 || err != nil { + t.Errorf("CopyN(noReadFrom, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err) + } } func TestReadAtLeast(t *testing.T) { @@ -120,22 +138,30 @@ func TestReadAtLeast(t *testing.T) { testReadAtLeast(t, &rb) } -// A version of bytes.Buffer that returns n > 0, EOF on Read +// A version of bytes.Buffer that returns n > 0, err on Read // when the input is exhausted. -type dataAndEOFBuffer struct { +type dataAndErrorBuffer struct { + err error bytes.Buffer } -func (r *dataAndEOFBuffer) Read(p []byte) (n int, err error) { +func (r *dataAndErrorBuffer) Read(p []byte) (n int, err error) { n, err = r.Buffer.Read(p) if n > 0 && r.Buffer.Len() == 0 && err == nil { - err = EOF + err = r.err } return } func TestReadAtLeastWithDataAndEOF(t *testing.T) { - var rb dataAndEOFBuffer + var rb dataAndErrorBuffer + rb.err = EOF + testReadAtLeast(t, &rb) +} + +func TestReadAtLeastWithDataAndError(t *testing.T) { + var rb dataAndErrorBuffer + rb.err = fmt.Errorf("fake error") testReadAtLeast(t, &rb) } @@ -169,8 +195,12 @@ func testReadAtLeast(t *testing.T, rb ReadWriter) { } rb.Write([]byte("4")) n, err = ReadAtLeast(rb, buf, 2) - if err != ErrUnexpectedEOF { - t.Errorf("expected ErrUnexpectedEOF, got %v", err) + want := ErrUnexpectedEOF + if rb, ok := rb.(*dataAndErrorBuffer); ok && rb.err != EOF { + want = rb.err + } + if err != want { + t.Errorf("expected %v, got %v", want, err) } if n != 1 { t.Errorf("expected to have read 1 bytes, got %v", n) @@ -203,3 +233,35 @@ func TestTeeReader(t *testing.T) { t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err) } } + +func TestSectionReader_ReadAt(tst *testing.T) { + dat := "a long sample data, 1234567890" + tests := []struct { + data string + off int + n int + bufLen int + at int + exp string + err error + }{ + {data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: EOF}, + {data: dat, off: 0, n: len(dat), bufLen: 0, at: 0, exp: "", err: nil}, + {data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: EOF}, + {data: dat, off: 0, n: len(dat) + 2, bufLen: len(dat), at: 0, exp: dat, err: nil}, + {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 0, exp: dat[:len(dat)/2], err: nil}, + {data: dat, off: 0, n: len(dat), bufLen: len(dat), at: 0, exp: dat, err: nil}, + {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[2 : 2+len(dat)/2], err: nil}, + {data: dat, off: 3, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[5 : 5+len(dat)/2], err: nil}, + {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil}, + {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF}, + } + for i, t := range tests { + r := strings.NewReader(t.data) + s := NewSectionReader(r, int64(t.off), int64(t.n)) + buf := make([]byte, t.bufLen) + if n, err := s.ReadAt(buf, int64(t.at)); n != len(t.exp) || string(buf[:n]) != t.exp || err != t.err { + tst.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, t.at, buf[:n], err, t.exp, t.err) + } + } +} diff --git a/src/pkg/io/ioutil/blackhole.go b/src/pkg/io/ioutil/blackhole.go new file mode 100644 index 000000000..101d2c121 --- /dev/null +++ b/src/pkg/io/ioutil/blackhole.go @@ -0,0 +1,23 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ioutil + +var blackHoleBuf = make(chan []byte, 1) + +func blackHole() []byte { + select { + case b := <-blackHoleBuf: + return b + default: + } + return make([]byte, 8192) +} + +func blackHolePut(p []byte) { + select { + case blackHoleBuf <- p: + default: + } +} diff --git a/src/pkg/io/ioutil/ioutil.go b/src/pkg/io/ioutil/ioutil.go index f072b8c75..0eb146c0a 100644 --- a/src/pkg/io/ioutil/ioutil.go +++ b/src/pkg/io/ioutil/ioutil.go @@ -130,12 +130,12 @@ func (devNull) Write(p []byte) (int, error) { return len(p), nil } -var blackHole = make([]byte, 8192) - func (devNull) ReadFrom(r io.Reader) (n int64, err error) { + buf := blackHole() + defer blackHolePut(buf) readSize := 0 for { - readSize, err = r.Read(blackHole) + readSize, err = r.Read(buf) n += int64(readSize) if err != nil { if err == io.EOF { diff --git a/src/pkg/io/ioutil/ioutil_test.go b/src/pkg/io/ioutil/ioutil_test.go index d9c43bead..c297847b4 100644 --- a/src/pkg/io/ioutil/ioutil_test.go +++ b/src/pkg/io/ioutil/ioutil_test.go @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package ioutil_test +package ioutil import ( - . "io/ioutil" "os" "testing" ) diff --git a/src/pkg/io/ioutil/tempfile.go b/src/pkg/io/ioutil/tempfile.go index 42d2e6758..4a06e9756 100644 --- a/src/pkg/io/ioutil/tempfile.go +++ b/src/pkg/io/ioutil/tempfile.go @@ -8,26 +8,30 @@ import ( "os" "path/filepath" "strconv" + "sync" "time" ) -// Random number state, accessed without lock; racy but harmless. +// Random number state. // We generate random temporary file names so that there's a good // chance the file doesn't exist yet - keeps the number of tries in // TempFile to a minimum. var rand uint32 +var randmu sync.Mutex func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) } func nextSuffix() string { + randmu.Lock() r := rand if r == 0 { r = reseed() } r = r*1664525 + 1013904223 // constants from Numerical Recipes rand = r + randmu.Unlock() return strconv.Itoa(int(1e9 + r%1e9))[1:] } @@ -38,8 +42,8 @@ func nextSuffix() string { // for temporary files (see os.TempDir). // Multiple programs calling TempFile simultaneously // will not choose the same file. The caller can use f.Name() -// to find the name of the file. It is the caller's responsibility to -// remove the file when no longer needed. +// to find the pathname of the file. It is the caller's responsibility +// to remove the file when no longer needed. func TempFile(dir, prefix string) (f *os.File, err error) { if dir == "" { dir = os.TempDir() diff --git a/src/pkg/io/ioutil/tempfile_test.go b/src/pkg/io/ioutil/tempfile_test.go index 80c62f672..d2a132a11 100644 --- a/src/pkg/io/ioutil/tempfile_test.go +++ b/src/pkg/io/ioutil/tempfile_test.go @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package ioutil_test +package ioutil import ( - . "io/ioutil" "os" "path/filepath" "regexp" diff --git a/src/pkg/log/syslog/syslog.go b/src/pkg/log/syslog/syslog.go index f53310cb0..8bdd9825e 100644 --- a/src/pkg/log/syslog/syslog.go +++ b/src/pkg/log/syslog/syslog.go @@ -4,9 +4,13 @@ // +build !windows,!plan9 -// Package syslog provides a simple interface to the system log service. It -// can send messages to the syslog daemon using UNIX domain sockets, UDP, or -// TCP connections. +// Package syslog provides a simple interface to the system log +// service. It can send messages to the syslog daemon using UNIX +// domain sockets, UDP or TCP. +// +// Only one call to Dial is necessary. On write failures, +// the syslog client will attempt to reconnect to the server +// and write again. package syslog import ( @@ -15,11 +19,23 @@ import ( "log" "net" "os" + "strings" + "sync" + "time" ) +// The Priority is a combination of the syslog facility and +// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity +// message from the FTP facility. The default severity is LOG_EMERG; +// the default facility is LOG_KERN. type Priority int +const severityMask = 0x07 +const facilityMask = 0xf8 + const ( + // Severity. + // From /usr/include/sys/syslog.h. // These are the same on Linux, BSD, and OS X. LOG_EMERG Priority = iota @@ -32,129 +48,220 @@ const ( LOG_DEBUG ) +const ( + // Facility. + + // From /usr/include/sys/syslog.h. + // These are the same up to LOG_FTP on Linux, BSD, and OS X. + LOG_KERN Priority = iota << 3 + LOG_USER + LOG_MAIL + LOG_DAEMON + LOG_AUTH + LOG_SYSLOG + LOG_LPR + LOG_NEWS + LOG_UUCP + LOG_CRON + LOG_AUTHPRIV + LOG_FTP + _ // unused + _ // unused + _ // unused + _ // unused + LOG_LOCAL0 + LOG_LOCAL1 + LOG_LOCAL2 + LOG_LOCAL3 + LOG_LOCAL4 + LOG_LOCAL5 + LOG_LOCAL6 + LOG_LOCAL7 +) + // A Writer is a connection to a syslog server. type Writer struct { priority Priority - prefix string - conn serverConn -} + tag string + hostname string + network string + raddr string -type serverConn interface { - writeBytes(p Priority, prefix string, b []byte) (int, error) - writeString(p Priority, prefix string, s string) (int, error) - close() error + mu sync.Mutex // guards conn + conn net.Conn } -type netConn struct { - conn net.Conn +// New establishes a new connection to the system log daemon. Each +// write to the returned writer sends a log message with the given +// priority and prefix. +func New(priority Priority, tag string) (w *Writer, err error) { + return Dial("", "", priority, tag) } -// New establishes a new connection to the system log daemon. -// Each write to the returned writer sends a log message with -// the given priority and prefix. -func New(priority Priority, prefix string) (w *Writer, err error) { - return Dial("", "", priority, prefix) +// Dial establishes a connection to a log daemon by connecting to +// address raddr on the network net. Each write to the returned +// writer sends a log message with the given facility, severity and +// tag. +func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) { + if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG { + return nil, errors.New("log/syslog: invalid priority") + } + + if tag == "" { + tag = os.Args[0] + } + hostname, _ := os.Hostname() + + w := &Writer{ + priority: priority, + tag: tag, + hostname: hostname, + network: network, + raddr: raddr, + } + + w.mu.Lock() + defer w.mu.Unlock() + + err := w.connect() + if err != nil { + return nil, err + } + return w, err } -// Dial establishes a connection to a log daemon by connecting -// to address raddr on the network net. -// Each write to the returned writer sends a log message with -// the given priority and prefix. -func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, err error) { - if prefix == "" { - prefix = os.Args[0] +// connect makes a connection to the syslog server. +// It must be called with w.mu held. +func (w *Writer) connect() (err error) { + if w.conn != nil { + // ignore err from close, it makes sense to continue anyway + w.conn.Close() + w.conn = nil } - var conn serverConn - if network == "" { - conn, err = unixSyslog() + + if w.network == "" { + w.conn, err = unixSyslog() + if w.hostname == "" { + w.hostname = "localhost" + } } else { var c net.Conn - c, err = net.Dial(network, raddr) - conn = netConn{c} + c, err = net.Dial(w.network, w.raddr) + if err == nil { + w.conn = c + if w.hostname == "" { + w.hostname = c.LocalAddr().String() + } + } } - return &Writer{priority, prefix, conn}, err + return } // Write sends a log message to the syslog daemon. func (w *Writer) Write(b []byte) (int, error) { - if w.priority > LOG_DEBUG || w.priority < LOG_EMERG { - return 0, errors.New("log/syslog: invalid priority") - } - return w.conn.writeBytes(w.priority, w.prefix, b) + return w.writeAndRetry(w.priority, string(b)) } -func (w *Writer) writeString(p Priority, s string) (int, error) { - return w.conn.writeString(p, w.prefix, s) -} +// Close closes a connection to the syslog daemon. +func (w *Writer) Close() error { + w.mu.Lock() + defer w.mu.Unlock() -func (w *Writer) Close() error { return w.conn.close() } + if w.conn != nil { + err := w.conn.Close() + w.conn = nil + return err + } + return nil +} -// Emerg logs a message using the LOG_EMERG priority. +// Emerg logs a message with severity LOG_EMERG, ignoring the severity +// passed to New. func (w *Writer) Emerg(m string) (err error) { - _, err = w.writeString(LOG_EMERG, m) + _, err = w.writeAndRetry(LOG_EMERG, m) return err } -// Alert logs a message using the LOG_ALERT priority. +// Alert logs a message with severity LOG_ALERT, ignoring the severity +// passed to New. func (w *Writer) Alert(m string) (err error) { - _, err = w.writeString(LOG_ALERT, m) + _, err = w.writeAndRetry(LOG_ALERT, m) return err } -// Crit logs a message using the LOG_CRIT priority. +// Crit logs a message with severity LOG_CRIT, ignoring the severity +// passed to New. func (w *Writer) Crit(m string) (err error) { - _, err = w.writeString(LOG_CRIT, m) + _, err = w.writeAndRetry(LOG_CRIT, m) return err } -// Err logs a message using the LOG_ERR priority. +// Err logs a message with severity LOG_ERR, ignoring the severity +// passed to New. func (w *Writer) Err(m string) (err error) { - _, err = w.writeString(LOG_ERR, m) + _, err = w.writeAndRetry(LOG_ERR, m) return err } -// Warning logs a message using the LOG_WARNING priority. +// Wanring logs a message with severity LOG_WARNING, ignoring the +// severity passed to New. func (w *Writer) Warning(m string) (err error) { - _, err = w.writeString(LOG_WARNING, m) + _, err = w.writeAndRetry(LOG_WARNING, m) return err } -// Notice logs a message using the LOG_NOTICE priority. +// Notice logs a message with severity LOG_NOTICE, ignoring the +// severity passed to New. func (w *Writer) Notice(m string) (err error) { - _, err = w.writeString(LOG_NOTICE, m) + _, err = w.writeAndRetry(LOG_NOTICE, m) return err } -// Info logs a message using the LOG_INFO priority. +// Info logs a message with severity LOG_INFO, ignoring the severity +// passed to New. func (w *Writer) Info(m string) (err error) { - _, err = w.writeString(LOG_INFO, m) + _, err = w.writeAndRetry(LOG_INFO, m) return err } -// Debug logs a message using the LOG_DEBUG priority. +// Debug logs a message with severity LOG_DEBUG, ignoring the severity +// passed to New. func (w *Writer) Debug(m string) (err error) { - _, err = w.writeString(LOG_DEBUG, m) + _, err = w.writeAndRetry(LOG_DEBUG, m) return err } -func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) { - _, err := fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, b) - if err != nil { +func (w *Writer) writeAndRetry(p Priority, s string) (int, error) { + pr := (w.priority & facilityMask) | (p & severityMask) + + w.mu.Lock() + defer w.mu.Unlock() + + if w.conn != nil { + if n, err := w.write(pr, s); err == nil { + return n, err + } + } + if err := w.connect(); err != nil { return 0, err } - return len(b), nil + return w.write(pr, s) } -func (n netConn) writeString(p Priority, prefix string, s string) (int, error) { - _, err := fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, s) - if err != nil { - return 0, err +// write generates and writes a syslog formatted string. The +// format is as follows: TIMESTAMP HOSTNAME TAG[PID]: MSG +func (w *Writer) write(p Priority, msg string) (int, error) { + // ensure it ends in a \n + nl := "" + if !strings.HasSuffix(msg, "\n") { + nl = "\n" } - return len(s), nil -} -func (n netConn) close() error { - return n.conn.Close() + timestamp := time.Now().Format(time.RFC3339) + fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s", + p, timestamp, w.hostname, + w.tag, os.Getpid(), msg, nl) + return len(msg), nil } // NewLogger creates a log.Logger whose output is written to diff --git a/src/pkg/log/syslog/syslog_plan9.go b/src/pkg/log/syslog/syslog_plan9.go new file mode 100644 index 000000000..0c05f6f83 --- /dev/null +++ b/src/pkg/log/syslog/syslog_plan9.go @@ -0,0 +1,8 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package syslog provides a simple interface to the system log service. +package syslog + +// BUG(akumar): This package is not implemented on Plan 9 yet. diff --git a/src/pkg/log/syslog/syslog_test.go b/src/pkg/log/syslog/syslog_test.go index 0fd623905..ec4525190 100644 --- a/src/pkg/log/syslog/syslog_test.go +++ b/src/pkg/log/syslog/syslog_test.go @@ -7,52 +7,181 @@ package syslog import ( + "bufio" + "fmt" "io" + "io/ioutil" "log" "net" + "os" + "sync" "testing" "time" ) -var serverAddr string - -func runSyslog(c net.PacketConn, done chan<- string) { +func runPktSyslog(c net.PacketConn, done chan<- string) { var buf [4096]byte - var rcvd string = "" + var rcvd string + ct := 0 for { - n, _, err := c.ReadFrom(buf[0:]) - if err != nil || n == 0 { + var n int + var err error + + c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + n, _, err = c.ReadFrom(buf[:]) + rcvd += string(buf[:n]) + if err != nil { + if oe, ok := err.(*net.OpError); ok { + if ct < 3 && oe.Temporary() { + ct++ + continue + } + } break } - rcvd += string(buf[0:n]) } + c.Close() done <- rcvd } -func startServer(done chan<- string) { - c, e := net.ListenPacket("udp", "127.0.0.1:0") - if e != nil { - log.Fatalf("net.ListenPacket failed udp :0 %v", e) +var crashy = false + +func runStreamSyslog(l net.Listener, done chan<- string, wg *sync.WaitGroup) { + for { + var c net.Conn + var err error + if c, err = l.Accept(); err != nil { + return + } + wg.Add(1) + go func(c net.Conn) { + defer wg.Done() + c.SetReadDeadline(time.Now().Add(5 * time.Second)) + b := bufio.NewReader(c) + for ct := 1; !crashy || ct&7 != 0; ct++ { + s, err := b.ReadString('\n') + if err != nil { + break + } + done <- s + } + c.Close() + }(c) } - serverAddr = c.LocalAddr().String() - c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) - go runSyslog(c, done) } -func skipNetTest(t *testing.T) bool { - if testing.Short() { - // Depends on syslog daemon running, and sometimes it's not. - t.Logf("skipping syslog test during -short") - return true +func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) { + if n == "udp" || n == "tcp" { + la = "127.0.0.1:0" + } else { + // unix and unixgram: choose an address if none given + if la == "" { + // use ioutil.TempFile to get a name that is unique + f, err := ioutil.TempFile("", "syslogtest") + if err != nil { + log.Fatal("TempFile: ", err) + } + f.Close() + la = f.Name() + } + os.Remove(la) + } + + wg = new(sync.WaitGroup) + if n == "udp" || n == "unixgram" { + l, e := net.ListenPacket(n, la) + if e != nil { + log.Fatalf("startServer failed: %v", e) + } + addr = l.LocalAddr().String() + sock = l + wg.Add(1) + go func() { + defer wg.Done() + runPktSyslog(l, done) + }() + } else { + l, e := net.Listen(n, la) + if e != nil { + log.Fatalf("startServer failed: %v", e) + } + addr = l.Addr().String() + sock = l + wg.Add(1) + go func() { + defer wg.Done() + runStreamSyslog(l, done, wg) + }() + } + return +} + +func TestWithSimulated(t *testing.T) { + msg := "Test 123" + transport := []string{"unix", "unixgram", "udp", "tcp"} + + for _, tr := range transport { + done := make(chan string) + addr, _, _ := startServer(tr, "", done) + if tr == "unix" || tr == "unixgram" { + defer os.Remove(addr) + } + s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test") + if err != nil { + t.Fatalf("Dial() failed: %v", err) + } + err = s.Info(msg) + if err != nil { + t.Fatalf("log failed: %v", err) + } + check(t, msg, <-done) + s.Close() + } +} + +func TestFlap(t *testing.T) { + net := "unix" + done := make(chan string) + addr, sock, _ := startServer(net, "", done) + defer os.Remove(addr) + defer sock.Close() + + s, err := Dial(net, addr, LOG_INFO|LOG_USER, "syslog_test") + if err != nil { + t.Fatalf("Dial() failed: %v", err) + } + msg := "Moo 2" + err = s.Info(msg) + if err != nil { + t.Fatalf("log failed: %v", err) + } + check(t, msg, <-done) + + // restart the server + _, sock2, _ := startServer(net, addr, done) + defer sock2.Close() + + // and try retransmitting + msg = "Moo 3" + err = s.Info(msg) + if err != nil { + t.Fatalf("log failed: %v", err) } - return false + check(t, msg, <-done) + + s.Close() } func TestNew(t *testing.T) { - if skipNetTest(t) { - return + if LOG_LOCAL7 != 23<<3 { + t.Fatalf("LOG_LOCAL7 has wrong value") + } + if testing.Short() { + // Depends on syslog daemon running, and sometimes it's not. + t.Skip("skipping syslog test during -short") } - s, err := New(LOG_INFO, "") + + s, err := New(LOG_INFO|LOG_USER, "the_tag") if err != nil { t.Fatalf("New() failed: %s", err) } @@ -61,57 +190,159 @@ func TestNew(t *testing.T) { } func TestNewLogger(t *testing.T) { - if skipNetTest(t) { - return + if testing.Short() { + t.Skip("skipping syslog test during -short") } - f, err := NewLogger(LOG_INFO, 0) + f, err := NewLogger(LOG_USER|LOG_INFO, 0) if f == nil { t.Error(err) } } func TestDial(t *testing.T) { - if skipNetTest(t) { - return + if testing.Short() { + t.Skip("skipping syslog test during -short") } - l, err := Dial("", "", LOG_ERR, "syslog_test") + f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test") + if f != nil { + t.Fatalf("Should have trapped bad priority") + } + f, err = Dial("", "", -1, "syslog_test") + if f != nil { + t.Fatalf("Should have trapped bad priority") + } + l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test") if err != nil { t.Fatalf("Dial() failed: %s", err) } l.Close() } -func TestUDPDial(t *testing.T) { - done := make(chan string) - startServer(done) - l, err := Dial("udp", serverAddr, LOG_INFO, "syslog_test") - if err != nil { - t.Fatalf("syslog.Dial() failed: %s", err) - } - msg := "udp test" - l.Info(msg) - expected := "<6>syslog_test: udp test\n" - rcvd := <-done - if rcvd != expected { - t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, expected) +func check(t *testing.T, in, out string) { + tmpl := fmt.Sprintf("<%d>%%s %%s syslog_test[%%d]: %s\n", LOG_USER+LOG_INFO, in) + if hostname, err := os.Hostname(); err != nil { + t.Error("Error retrieving hostname") + } else { + var parsedHostname, timestamp string + var pid int + if n, err := fmt.Sscanf(out, tmpl, ×tamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname { + t.Errorf("Got %q, does not match template %q (%d %s)", out, tmpl, n, err) + } } } func TestWrite(t *testing.T) { - done := make(chan string) - startServer(done) - l, err := Dial("udp", serverAddr, LOG_ERR, "syslog_test") - if err != nil { - t.Fatalf("syslog.Dial() failed: %s", err) + tests := []struct { + pri Priority + pre string + msg string + exp string + }{ + {LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"}, + {LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"}, + // Write should not add \n if there already is one + {LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"}, } - msg := "write test" - _, err = io.WriteString(l, msg) + + if hostname, err := os.Hostname(); err != nil { + t.Fatalf("Error retrieving hostname") + } else { + for _, test := range tests { + done := make(chan string) + addr, sock, _ := startServer("udp", "", done) + defer sock.Close() + l, err := Dial("udp", addr, test.pri, test.pre) + if err != nil { + t.Fatalf("syslog.Dial() failed: %v", err) + } + _, err = io.WriteString(l, test.msg) + if err != nil { + t.Fatalf("WriteString() failed: %v", err) + } + rcvd := <-done + test.exp = fmt.Sprintf("<%d>", test.pri) + test.exp + var parsedHostname, timestamp string + var pid int + if n, err := fmt.Sscanf(rcvd, test.exp, ×tamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname { + t.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, test.exp, n, err) + } + } + } +} + +func TestConcurrentWrite(t *testing.T) { + addr, sock, _ := startServer("udp", "", make(chan string)) + defer sock.Close() + w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?") if err != nil { - t.Fatalf("WriteString() failed: %s", err) + t.Fatalf("syslog.Dial() failed: %v", err) + } + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + err := w.Info("test") + if err != nil { + t.Errorf("Info() failed: %v", err) + return + } + wg.Done() + }() } - expected := "<3>syslog_test: write test\n" - rcvd := <-done - if rcvd != expected { - t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, expected) + wg.Wait() +} + +func TestConcurrentReconnect(t *testing.T) { + crashy = true + defer func() { crashy = false }() + + net := "unix" + done := make(chan string) + addr, sock, srvWG := startServer(net, "", done) + defer os.Remove(addr) + + // count all the messages arriving + count := make(chan int) + go func() { + ct := 0 + for _ = range done { + ct++ + // we are looking for 500 out of 1000 events + // here because lots of log messages are lost + // in buffers (kernel and/or bufio) + if ct > 500 { + break + } + } + count <- ct + }() + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag") + if err != nil { + t.Fatalf("syslog.Dial() failed: %v", err) + } + for i := 0; i < 100; i++ { + err := w.Info("test") + if err != nil { + t.Errorf("Info() failed: %v", err) + return + } + } + wg.Done() + }() + } + wg.Wait() + sock.Close() + srvWG.Wait() + close(done) + + select { + case <-count: + case <-time.After(100 * time.Millisecond): + t.Error("timeout in concurrent reconnect") } } diff --git a/src/pkg/log/syslog/syslog_unix.go b/src/pkg/log/syslog/syslog_unix.go index 46a164dd5..a0001ccae 100644 --- a/src/pkg/log/syslog/syslog_unix.go +++ b/src/pkg/log/syslog/syslog_unix.go @@ -14,18 +14,16 @@ import ( // unixSyslog opens a connection to the syslog daemon running on the // local machine using a Unix domain socket. -func unixSyslog() (conn serverConn, err error) { +func unixSyslog() (conn net.Conn, err error) { logTypes := []string{"unixgram", "unix"} logPaths := []string{"/dev/log", "/var/run/syslog"} - var raddr string for _, network := range logTypes { for _, path := range logPaths { - raddr = path - conn, err := net.Dial(network, raddr) + conn, err := net.Dial(network, path) if err != nil { continue } else { - return netConn{conn}, nil + return conn, nil } } } diff --git a/src/pkg/math/abs_arm.s b/src/pkg/math/abs_arm.s index 23f6a2a2d..37a1459fe 100644 --- a/src/pkg/math/abs_arm.s +++ b/src/pkg/math/abs_arm.s @@ -3,4 +3,9 @@ // license that can be found in the LICENSE file. TEXT ·Abs(SB),7,$0 - B ·abs(SB) + MOVW x+0(FP), R0 + MOVW x+4(FP), R1 + AND $((1<<31)-1), R1 + MOVW R0, r+8(FP) + MOVW R1, r+12(FP) + RET diff --git a/src/pkg/math/acosh.go b/src/pkg/math/acosh.go index c6c8645e1..e394008b0 100644 --- a/src/pkg/math/acosh.go +++ b/src/pkg/math/acosh.go @@ -33,7 +33,7 @@ package math // acosh(NaN) is NaN without signal. // -// Acosh(x) calculates the inverse hyperbolic cosine of x. +// Acosh returns the inverse hyperbolic cosine of x. // // Special cases are: // Acosh(+Inf) = +Inf diff --git a/src/pkg/math/all_test.go b/src/pkg/math/all_test.go index ed66a42fb..0d8b10f67 100644 --- a/src/pkg/math/all_test.go +++ b/src/pkg/math/all_test.go @@ -1128,11 +1128,11 @@ var vfgammaSC = []float64{ NaN(), } var gammaSC = []float64{ + NaN(), + NaN(), Inf(-1), Inf(1), Inf(1), - Inf(1), - Inf(1), NaN(), } @@ -1693,6 +1693,17 @@ func alike(a, b float64) bool { return false } +func TestNaN(t *testing.T) { + f64 := NaN() + if f64 == f64 { + t.Fatalf("NaN() returns %g, expected NaN", f64) + } + f32 := float32(f64) + if f32 == f32 { + t.Fatalf("float32(NaN()) is %g, expected NaN", f32) + } +} + func TestAcos(t *testing.T) { for i := 0; i < len(vf); i++ { a := vf[i] / 10 @@ -2270,6 +2281,13 @@ func TestLog2(t *testing.T) { t.Errorf("Log2(%g) = %g, want %g", vflogSC[i], f, logSC[i]) } } + for i := -1074; i <= 1023; i++ { + f := Ldexp(1, i) + l := Log2(f) + if l != float64(i) { + t.Errorf("Log2(2**%d) = %g, want %d", i, l, i) + } + } } func TestModf(t *testing.T) { diff --git a/src/pkg/math/asinh.go b/src/pkg/math/asinh.go index 0defbb9be..ff2de0215 100644 --- a/src/pkg/math/asinh.go +++ b/src/pkg/math/asinh.go @@ -30,7 +30,7 @@ package math // := sign(x)*log1p(|x| + x**2/(1 + sqrt(1+x**2))) // -// Asinh(x) calculates the inverse hyperbolic sine of x. +// Asinh returns the inverse hyperbolic sine of x. // // Special cases are: // Asinh(±0) = ±0 diff --git a/src/pkg/math/atan.go b/src/pkg/math/atan.go index d424a2be4..c107d388d 100644 --- a/src/pkg/math/atan.go +++ b/src/pkg/math/atan.go @@ -6,51 +6,92 @@ package math /* Floating-point arctangent. - - Atan returns the value of the arctangent of its - argument in the range [-pi/2,pi/2]. - There are no error returns. - Coefficients are #5077 from Hart & Cheney. (19.56D) */ -// xatan evaluates a series valid in the -// range [-0.414...,+0.414...]. (tan(pi/8)) -func xatan(arg float64) float64 { +// The original C code, the long comment, and the constants below were +// from http://netlib.sandia.gov/cephes/cmath/atan.c, available from +// http://www.netlib.org/cephes/cmath.tgz. +// The go code is a version of the original C. +// +// atan.c +// Inverse circular tangent (arctangent) +// +// SYNOPSIS: +// double x, y, atan(); +// y = atan( x ); +// +// DESCRIPTION: +// Returns radian angle between -pi/2 and +pi/2 whose tangent is x. +// +// Range reduction is from three intervals into the interval from zero to 0.66. +// The approximant uses a rational function of degree 4/5 of the form +// x + x**3 P(x)/Q(x). +// +// ACCURACY: +// Relative error: +// arithmetic domain # trials peak rms +// DEC -10, 10 50000 2.4e-17 8.3e-18 +// IEEE -10, 10 10^6 1.8e-16 5.0e-17 +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +// xatan evaluates a series valid in the range [0, 0.66]. +func xatan(x float64) float64 { const ( - P4 = .161536412982230228262e2 - P3 = .26842548195503973794141e3 - P2 = .11530293515404850115428136e4 - P1 = .178040631643319697105464587e4 - P0 = .89678597403663861959987488e3 - Q4 = .5895697050844462222791e2 - Q3 = .536265374031215315104235e3 - Q2 = .16667838148816337184521798e4 - Q1 = .207933497444540981287275926e4 - Q0 = .89678597403663861962481162e3 + P0 = -8.750608600031904122785e-01 + P1 = -1.615753718733365076637e+01 + P2 = -7.500855792314704667340e+01 + P3 = -1.228866684490136173410e+02 + P4 = -6.485021904942025371773e+01 + Q0 = +2.485846490142306297962e+01 + Q1 = +1.650270098316988542046e+02 + Q2 = +4.328810604912902668951e+02 + Q3 = +4.853903996359136964868e+02 + Q4 = +1.945506571482613964425e+02 ) - sq := arg * arg - value := ((((P4*sq+P3)*sq+P2)*sq+P1)*sq + P0) - value = value / (((((sq+Q4)*sq+Q3)*sq+Q2)*sq+Q1)*sq + Q0) - return value * arg + z := x * x + z = z * ((((P0*z+P1)*z+P2)*z+P3)*z + P4) / (((((z+Q0)*z+Q1)*z+Q2)*z+Q3)*z + Q4) + z = x*z + x + return z } // satan reduces its argument (known to be positive) -// to the range [0,0.414...] and calls xatan. -func satan(arg float64) float64 { - if arg < Sqrt2-1 { - return xatan(arg) +// to the range [0, 0.66] and calls xatan. +func satan(x float64) float64 { + const ( + Morebits = 6.123233995736765886130e-17 // pi/2 = PIO2 + Morebits + Tan3pio8 = 2.41421356237309504880 // tan(3*pi/8) + ) + if x <= 0.66 { + return xatan(x) } - if arg > Sqrt2+1 { - return Pi/2 - xatan(1/arg) + if x > Tan3pio8 { + return Pi/2 - xatan(1/x) + Morebits } - return Pi/4 + xatan((arg-1)/(arg+1)) + return Pi/4 + xatan((x-1)/(x+1)) + 0.5*Morebits } // Atan returns the arctangent of x. // // Special cases are: -// Atan(±0) = ±0 -// Atan(±Inf) = ±Pi/2 +// Atan(±0) = ±0 +// Atan(±Inf) = ±Pi/2 func Atan(x float64) float64 func atan(x float64) float64 { diff --git a/src/pkg/math/atanh.go b/src/pkg/math/atanh.go index 5b5d46855..113d5c103 100644 --- a/src/pkg/math/atanh.go +++ b/src/pkg/math/atanh.go @@ -36,7 +36,7 @@ package math // atanh(+-1) is +-INF with signal. // -// Atanh(x) calculates the inverse hyperbolic tangent of x. +// Atanh returns the inverse hyperbolic tangent of x. // // Special cases are: // Atanh(1) = +Inf diff --git a/src/pkg/math/big/arith_386.s b/src/pkg/math/big/arith_386.s index f1262c651..c62483317 100644 --- a/src/pkg/math/big/arith_386.s +++ b/src/pkg/math/big/arith_386.s @@ -29,7 +29,7 @@ TEXT ·addVV(SB),7,$0 MOVL z+0(FP), DI MOVL x+12(FP), SI MOVL y+24(FP), CX - MOVL n+4(FP), BP + MOVL z+4(FP), BP MOVL $0, BX // i = 0 MOVL $0, DX // c = 0 JMP E1 @@ -54,7 +54,7 @@ TEXT ·subVV(SB),7,$0 MOVL z+0(FP), DI MOVL x+12(FP), SI MOVL y+24(FP), CX - MOVL n+4(FP), BP + MOVL z+4(FP), BP MOVL $0, BX // i = 0 MOVL $0, DX // c = 0 JMP E2 @@ -78,7 +78,7 @@ TEXT ·addVW(SB),7,$0 MOVL z+0(FP), DI MOVL x+12(FP), SI MOVL y+24(FP), AX // c = y - MOVL n+4(FP), BP + MOVL z+4(FP), BP MOVL $0, BX // i = 0 JMP E3 @@ -100,7 +100,7 @@ TEXT ·subVW(SB),7,$0 MOVL z+0(FP), DI MOVL x+12(FP), SI MOVL y+24(FP), AX // c = y - MOVL n+4(FP), BP + MOVL z+4(FP), BP MOVL $0, BX // i = 0 JMP E4 @@ -120,7 +120,7 @@ E4: CMPL BX, BP // i < n // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),7,$0 - MOVL n+4(FP), BX // i = n + MOVL z+4(FP), BX // i = z SUBL $1, BX // i-- JL X8b // i < 0 (n <= 0) @@ -155,7 +155,7 @@ X8b: MOVL $0, c+28(FP) // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),7,$0 - MOVL n+4(FP), BP + MOVL z+4(FP), BP SUBL $1, BP // n-- JL X9b // n < 0 (n <= 0) @@ -196,7 +196,7 @@ TEXT ·mulAddVWW(SB),7,$0 MOVL x+12(FP), SI MOVL y+24(FP), BP MOVL r+28(FP), CX // c = r - MOVL n+4(FP), BX + MOVL z+4(FP), BX LEAL (DI)(BX*4), DI LEAL (SI)(BX*4), SI NEGL BX // i = -n @@ -222,7 +222,7 @@ TEXT ·addMulVVW(SB),7,$0 MOVL z+0(FP), DI MOVL x+12(FP), SI MOVL y+24(FP), BP - MOVL n+4(FP), BX + MOVL z+4(FP), BX LEAL (DI)(BX*4), DI LEAL (SI)(BX*4), SI NEGL BX // i = -n @@ -251,7 +251,7 @@ TEXT ·divWVW(SB),7,$0 MOVL xn+12(FP), DX // r = xn MOVL x+16(FP), SI MOVL y+28(FP), CX - MOVL n+4(FP), BX // i = n + MOVL z+4(FP), BX // i = z JMP E7 L7: MOVL (SI)(BX*4), AX diff --git a/src/pkg/math/big/arith_amd64.s b/src/pkg/math/big/arith_amd64.s index 54f647322..d85964502 100644 --- a/src/pkg/math/big/arith_amd64.s +++ b/src/pkg/math/big/arith_amd64.s @@ -5,7 +5,15 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -// TODO(gri) - experiment with unrolled loops for faster execution +// Literal instruction for MOVQ $0, CX. +// (MOVQ $0, reg is translated to XORQ reg, reg and clears CF.) +#define ZERO_CX BYTE $0x48; \ + BYTE $0xc7; \ + BYTE $0xc1; \ + BYTE $0x00; \ + BYTE $0x00; \ + BYTE $0x00; \ + BYTE $0x00 // func mulWW(x, y Word) (z1, z0 Word) TEXT ·mulWW(SB),7,$0 @@ -28,114 +36,231 @@ TEXT ·divWW(SB),7,$0 // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),7,$0 + MOVQ z+8(FP), DI + MOVQ x+24(FP), R8 + MOVQ y+48(FP), R9 MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), R9 - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - MOVQ $0, DX // c = 0 - JMP E1 - -L1: MOVQ (R8)(BX*8), AX - RCRQ $1, DX - ADCQ (R9)(BX*8), AX - RCLQ $1, DX - MOVQ AX, (R10)(BX*8) - ADDL $1, BX // i++ -E1: CMPQ BX, R11 // i < n - JL L1 - - MOVQ DX, c+48(FP) + MOVQ $0, CX // c = 0 + MOVQ $0, SI // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUBQ $4, DI // n -= 4 + JL V1 // if n < 0 goto V1 + +U1: // n >= 0 + // regular loop body unrolled 4x + RCRQ $1, CX // CF = c + MOVQ 0(R8)(SI*8), R11 + MOVQ 8(R8)(SI*8), R12 + MOVQ 16(R8)(SI*8), R13 + MOVQ 24(R8)(SI*8), R14 + ADCQ 0(R9)(SI*8), R11 + ADCQ 8(R9)(SI*8), R12 + ADCQ 16(R9)(SI*8), R13 + ADCQ 24(R9)(SI*8), R14 + MOVQ R11, 0(R10)(SI*8) + MOVQ R12, 8(R10)(SI*8) + MOVQ R13, 16(R10)(SI*8) + MOVQ R14, 24(R10)(SI*8) + RCLQ $1, CX // c = CF + + ADDQ $4, SI // i += 4 + SUBQ $4, DI // n -= 4 + JGE U1 // if n >= 0 goto U1 + +V1: ADDQ $4, DI // n += 4 + JLE E1 // if n <= 0 goto E1 + +L1: // n > 0 + RCRQ $1, CX // CF = c + MOVQ 0(R8)(SI*8), R11 + ADCQ 0(R9)(SI*8), R11 + MOVQ R11, 0(R10)(SI*8) + RCLQ $1, CX // c = CF + + ADDQ $1, SI // i++ + SUBQ $1, DI // n-- + JG L1 // if n > 0 goto L1 + +E1: MOVQ CX, c+72(FP) // return c RET // func subVV(z, x, y []Word) (c Word) -// (same as addVV_s except for SBBQ instead of ADCQ and label names) +// (same as addVV except for SBBQ instead of ADCQ and label names) TEXT ·subVV(SB),7,$0 + MOVQ z+8(FP), DI + MOVQ x+24(FP), R8 + MOVQ y+48(FP), R9 MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), R9 - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - MOVQ $0, DX // c = 0 - JMP E2 - -L2: MOVQ (R8)(BX*8), AX - RCRQ $1, DX - SBBQ (R9)(BX*8), AX - RCLQ $1, DX - MOVQ AX, (R10)(BX*8) - ADDL $1, BX // i++ - -E2: CMPQ BX, R11 // i < n - JL L2 - MOVQ DX, c+48(FP) + MOVQ $0, CX // c = 0 + MOVQ $0, SI // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUBQ $4, DI // n -= 4 + JL V2 // if n < 0 goto V2 + +U2: // n >= 0 + // regular loop body unrolled 4x + RCRQ $1, CX // CF = c + MOVQ 0(R8)(SI*8), R11 + MOVQ 8(R8)(SI*8), R12 + MOVQ 16(R8)(SI*8), R13 + MOVQ 24(R8)(SI*8), R14 + SBBQ 0(R9)(SI*8), R11 + SBBQ 8(R9)(SI*8), R12 + SBBQ 16(R9)(SI*8), R13 + SBBQ 24(R9)(SI*8), R14 + MOVQ R11, 0(R10)(SI*8) + MOVQ R12, 8(R10)(SI*8) + MOVQ R13, 16(R10)(SI*8) + MOVQ R14, 24(R10)(SI*8) + RCLQ $1, CX // c = CF + + ADDQ $4, SI // i += 4 + SUBQ $4, DI // n -= 4 + JGE U2 // if n >= 0 goto U2 + +V2: ADDQ $4, DI // n += 4 + JLE E2 // if n <= 0 goto E2 + +L2: // n > 0 + RCRQ $1, CX // CF = c + MOVQ 0(R8)(SI*8), R11 + SBBQ 0(R9)(SI*8), R11 + MOVQ R11, 0(R10)(SI*8) + RCLQ $1, CX // c = CF + + ADDQ $1, SI // i++ + SUBQ $1, DI // n-- + JG L2 // if n > 0 goto L2 + +E2: MOVQ CX, c+72(FP) // return c RET // func addVW(z, x []Word, y Word) (c Word) TEXT ·addVW(SB),7,$0 + MOVQ z+8(FP), DI + MOVQ x+24(FP), R8 + MOVQ y+48(FP), CX // c = y MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), AX // c = y - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - JMP E3 -L3: ADDQ (R8)(BX*8), AX - MOVQ AX, (R10)(BX*8) - RCLQ $1, AX - ANDQ $1, AX - ADDL $1, BX // i++ - -E3: CMPQ BX, R11 // i < n - JL L3 - - MOVQ AX, c+40(FP) + MOVQ $0, SI // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUBQ $4, DI // n -= 4 + JL V3 // if n < 4 goto V3 + +U3: // n >= 0 + // regular loop body unrolled 4x + MOVQ 0(R8)(SI*8), R11 + MOVQ 8(R8)(SI*8), R12 + MOVQ 16(R8)(SI*8), R13 + MOVQ 24(R8)(SI*8), R14 + ADDQ CX, R11 + ZERO_CX + ADCQ $0, R12 + ADCQ $0, R13 + ADCQ $0, R14 + SETCS CX // c = CF + MOVQ R11, 0(R10)(SI*8) + MOVQ R12, 8(R10)(SI*8) + MOVQ R13, 16(R10)(SI*8) + MOVQ R14, 24(R10)(SI*8) + + ADDQ $4, SI // i += 4 + SUBQ $4, DI // n -= 4 + JGE U3 // if n >= 0 goto U3 + +V3: ADDQ $4, DI // n += 4 + JLE E3 // if n <= 0 goto E3 + +L3: // n > 0 + ADDQ 0(R8)(SI*8), CX + MOVQ CX, 0(R10)(SI*8) + ZERO_CX + RCLQ $1, CX // c = CF + + ADDQ $1, SI // i++ + SUBQ $1, DI // n-- + JG L3 // if n > 0 goto L3 + +E3: MOVQ CX, c+56(FP) // return c RET // func subVW(z, x []Word, y Word) (c Word) +// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names) TEXT ·subVW(SB),7,$0 + MOVQ z+8(FP), DI + MOVQ x+24(FP), R8 + MOVQ y+48(FP), CX // c = y MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), AX // c = y - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - JMP E4 - -L4: MOVQ (R8)(BX*8), DX // TODO(gri) is there a reverse SUBQ? - SUBQ AX, DX - MOVQ DX, (R10)(BX*8) - RCLQ $1, AX - ANDQ $1, AX - ADDL $1, BX // i++ - -E4: CMPQ BX, R11 // i < n - JL L4 - - MOVQ AX, c+40(FP) + + MOVQ $0, SI // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUBQ $4, DI // n -= 4 + JL V4 // if n < 4 goto V4 + +U4: // n >= 0 + // regular loop body unrolled 4x + MOVQ 0(R8)(SI*8), R11 + MOVQ 8(R8)(SI*8), R12 + MOVQ 16(R8)(SI*8), R13 + MOVQ 24(R8)(SI*8), R14 + SUBQ CX, R11 + ZERO_CX + SBBQ $0, R12 + SBBQ $0, R13 + SBBQ $0, R14 + SETCS CX // c = CF + MOVQ R11, 0(R10)(SI*8) + MOVQ R12, 8(R10)(SI*8) + MOVQ R13, 16(R10)(SI*8) + MOVQ R14, 24(R10)(SI*8) + + ADDQ $4, SI // i += 4 + SUBQ $4, DI // n -= 4 + JGE U4 // if n >= 0 goto U4 + +V4: ADDQ $4, DI // n += 4 + JLE E4 // if n <= 0 goto E4 + +L4: // n > 0 + MOVQ 0(R8)(SI*8), R11 + SUBQ CX, R11 + MOVQ R11, 0(R10)(SI*8) + ZERO_CX + RCLQ $1, CX // c = CF + + ADDQ $1, SI // i++ + SUBQ $1, DI // n-- + JG L4 // if n > 0 goto L4 + +E4: MOVQ CX, c+56(FP) // return c RET // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),7,$0 - MOVL n+8(FP), BX // i = n - SUBL $1, BX // i-- + MOVQ z+8(FP), BX // i = z + SUBQ $1, BX // i-- JL X8b // i < 0 (n <= 0) // n > 0 MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVL s+32(FP), CX + MOVQ x+24(FP), R8 + MOVQ s+48(FP), CX MOVQ (R8)(BX*8), AX // w1 = x[n-1] MOVQ $0, DX SHLQ CX, DX:AX // w1>>ŝ - MOVQ DX, c+40(FP) + MOVQ DX, c+56(FP) - CMPL BX, $0 + CMPQ BX, $0 JLE X8a // i <= 0 // i > 0 @@ -143,7 +268,7 @@ L8: MOVQ AX, DX // w = w1 MOVQ -8(R8)(BX*8), AX // w1 = x[i-1] SHLQ CX, DX:AX // w<>ŝ MOVQ DX, (R10)(BX*8) // z[i] = w<>ŝ - SUBL $1, BX // i-- + SUBQ $1, BX // i-- JG L8 // i > 0 // i <= 0 @@ -151,24 +276,24 @@ X8a: SHLQ CX, AX // w1< 0 MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVL s+32(FP), CX + MOVQ x+24(FP), R8 + MOVQ s+48(FP), CX MOVQ (R8), AX // w1 = x[0] MOVQ $0, DX SHRQ CX, DX:AX // w1<<ŝ - MOVQ DX, c+40(FP) + MOVQ DX, c+56(FP) MOVQ $0, BX // i = 0 JMP E9 @@ -178,7 +303,7 @@ L9: MOVQ AX, DX // w = w1 MOVQ 8(R8)(BX*8), AX // w1 = x[i+1] SHRQ CX, DX:AX // w>>s | w1<<ŝ MOVQ DX, (R10)(BX*8) // z[i] = w>>s | w1<<ŝ - ADDL $1, BX // i++ + ADDQ $1, BX // i++ E9: CMPQ BX, R11 JL L9 // i < n-1 @@ -188,17 +313,17 @@ X9a: SHRQ CX, AX // w1>>s MOVQ AX, (R10)(R11*8) // z[n-1] = w1>>s RET -X9b: MOVQ $0, c+40(FP) +X9b: MOVQ $0, c+56(FP) RET // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB),7,$0 MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), R9 - MOVQ r+40(FP), CX // c = r - MOVL n+8(FP), R11 + MOVQ x+24(FP), R8 + MOVQ y+48(FP), R9 + MOVQ r+56(FP), CX // c = r + MOVQ z+8(FP), R11 MOVQ $0, BX // i = 0 JMP E5 @@ -208,21 +333,21 @@ L5: MOVQ (R8)(BX*8), AX ADCQ $0, DX MOVQ AX, (R10)(BX*8) MOVQ DX, CX - ADDL $1, BX // i++ + ADDQ $1, BX // i++ E5: CMPQ BX, R11 // i < n JL L5 - MOVQ CX, c+48(FP) + MOVQ CX, c+64(FP) RET // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),7,$0 MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), R9 - MOVL n+8(FP), R11 + MOVQ x+24(FP), R8 + MOVQ y+48(FP), R9 + MOVQ z+8(FP), R11 MOVQ $0, BX // i = 0 MOVQ $0, CX // c = 0 JMP E6 @@ -234,41 +359,41 @@ L6: MOVQ (R8)(BX*8), AX ADDQ AX, (R10)(BX*8) ADCQ $0, DX MOVQ DX, CX - ADDL $1, BX // i++ + ADDQ $1, BX // i++ E6: CMPQ BX, R11 // i < n JL L6 - MOVQ CX, c+40(FP) + MOVQ CX, c+56(FP) RET // func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) TEXT ·divWVW(SB),7,$0 MOVQ z+0(FP), R10 - MOVQ xn+16(FP), DX // r = xn - MOVQ x+24(FP), R8 - MOVQ y+40(FP), R9 - MOVL n+8(FP), BX // i = n + MOVQ xn+24(FP), DX // r = xn + MOVQ x+32(FP), R8 + MOVQ y+56(FP), R9 + MOVQ z+8(FP), BX // i = z JMP E7 L7: MOVQ (R8)(BX*8), AX DIVQ R9 MOVQ AX, (R10)(BX*8) -E7: SUBL $1, BX // i-- +E7: SUBQ $1, BX // i-- JGE L7 // i >= 0 - MOVQ DX, r+48(FP) + MOVQ DX, r+64(FP) RET // func bitLen(x Word) (n int) TEXT ·bitLen(SB),7,$0 BSRQ x+0(FP), AX JZ Z1 - INCL AX - MOVL AX, n+8(FP) + ADDQ $1, AX + MOVQ AX, n+8(FP) RET -Z1: MOVL $0, n+8(FP) +Z1: MOVQ $0, n+8(FP) RET diff --git a/src/pkg/math/big/arith_arm.s b/src/pkg/math/big/arith_arm.s index dbf3360b5..64610f915 100644 --- a/src/pkg/math/big/arith_arm.s +++ b/src/pkg/math/big/arith_arm.s @@ -13,7 +13,7 @@ TEXT ·addVV(SB),7,$0 MOVW z+0(FP), R1 MOVW x+12(FP), R2 MOVW y+24(FP), R3 - MOVW n+4(FP), R4 + MOVW z+4(FP), R4 MOVW R4<<2, R4 ADD R1, R4 B E1 @@ -41,7 +41,7 @@ TEXT ·subVV(SB),7,$0 MOVW z+0(FP), R1 MOVW x+12(FP), R2 MOVW y+24(FP), R3 - MOVW n+4(FP), R4 + MOVW z+4(FP), R4 MOVW R4<<2, R4 ADD R1, R4 B E2 @@ -68,7 +68,7 @@ TEXT ·addVW(SB),7,$0 MOVW z+0(FP), R1 MOVW x+12(FP), R2 MOVW y+24(FP), R3 - MOVW n+4(FP), R4 + MOVW z+4(FP), R4 MOVW R4<<2, R4 ADD R1, R4 CMP R1, R4 @@ -102,7 +102,7 @@ TEXT ·subVW(SB),7,$0 MOVW z+0(FP), R1 MOVW x+12(FP), R2 MOVW y+24(FP), R3 - MOVW n+4(FP), R4 + MOVW z+4(FP), R4 MOVW R4<<2, R4 ADD R1, R4 CMP R1, R4 @@ -134,7 +134,7 @@ E4: // func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB),7,$0 - MOVW n+4(FP), R5 + MOVW z+4(FP), R5 CMP $0, R5 BEQ X7 @@ -183,7 +183,7 @@ X7: // func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB),7,$0 - MOVW n+4(FP), R5 + MOVW z+4(FP), R5 CMP $0, R5 BEQ X6 @@ -238,7 +238,7 @@ TEXT ·mulAddVWW(SB),7,$0 MOVW x+12(FP), R2 MOVW y+24(FP), R3 MOVW r+28(FP), R4 - MOVW n+4(FP), R5 + MOVW z+4(FP), R5 MOVW R5<<2, R5 ADD R1, R5 B E8 @@ -265,7 +265,7 @@ TEXT ·addMulVVW(SB),7,$0 MOVW z+0(FP), R1 MOVW x+12(FP), R2 MOVW y+24(FP), R3 - MOVW n+4(FP), R5 + MOVW z+4(FP), R5 MOVW R5<<2, R5 ADD R1, R5 MOVW $0, R4 @@ -314,7 +314,7 @@ TEXT ·mulWW(SB),7,$0 // func bitLen(x Word) (n int) TEXT ·bitLen(SB),7,$0 MOVW x+0(FP), R0 - WORD $0xe16f0f10 // CLZ R0, R0 (count leading zeros) + CLZ R0, R0 MOVW $32, R1 SUB.S R0, R1 MOVW R1, n+4(FP) diff --git a/src/pkg/math/big/arith_test.go b/src/pkg/math/big/arith_test.go index c7e3d284c..3615a659c 100644 --- a/src/pkg/math/big/arith_test.go +++ b/src/pkg/math/big/arith_test.go @@ -4,7 +4,10 @@ package big -import "testing" +import ( + "math/rand" + "testing" +) type funWW func(x, y, c Word) (z1, z0 Word) type argWW struct { @@ -100,6 +103,43 @@ func TestFunVV(t *testing.T) { } } +// Always the same seed for reproducible results. +var rnd = rand.New(rand.NewSource(0)) + +func rndW() Word { + return Word(rnd.Int63()<<1 | rnd.Int63n(2)) +} + +func rndV(n int) []Word { + v := make([]Word, n) + for i := range v { + v[i] = rndW() + } + return v +} + +func benchmarkFunVV(b *testing.B, f funVV, n int) { + x := rndV(n) + y := rndV(n) + z := make([]Word, n) + b.SetBytes(int64(n * _W)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + f(z, x, y) + } +} + +func BenchmarkAddVV_1(b *testing.B) { benchmarkFunVV(b, addVV, 1) } +func BenchmarkAddVV_2(b *testing.B) { benchmarkFunVV(b, addVV, 2) } +func BenchmarkAddVV_3(b *testing.B) { benchmarkFunVV(b, addVV, 3) } +func BenchmarkAddVV_4(b *testing.B) { benchmarkFunVV(b, addVV, 4) } +func BenchmarkAddVV_5(b *testing.B) { benchmarkFunVV(b, addVV, 5) } +func BenchmarkAddVV_1e1(b *testing.B) { benchmarkFunVV(b, addVV, 1e1) } +func BenchmarkAddVV_1e2(b *testing.B) { benchmarkFunVV(b, addVV, 1e2) } +func BenchmarkAddVV_1e3(b *testing.B) { benchmarkFunVV(b, addVV, 1e3) } +func BenchmarkAddVV_1e4(b *testing.B) { benchmarkFunVV(b, addVV, 1e4) } +func BenchmarkAddVV_1e5(b *testing.B) { benchmarkFunVV(b, addVV, 1e5) } + type funVW func(z, x []Word, y Word) (c Word) type argVW struct { z, x nat @@ -210,6 +250,28 @@ func TestFunVW(t *testing.T) { } } +func benchmarkFunVW(b *testing.B, f funVW, n int) { + x := rndV(n) + y := rndW() + z := make([]Word, n) + b.SetBytes(int64(n * _W)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + f(z, x, y) + } +} + +func BenchmarkAddVW_1(b *testing.B) { benchmarkFunVW(b, addVW, 1) } +func BenchmarkAddVW_2(b *testing.B) { benchmarkFunVW(b, addVW, 2) } +func BenchmarkAddVW_3(b *testing.B) { benchmarkFunVW(b, addVW, 3) } +func BenchmarkAddVW_4(b *testing.B) { benchmarkFunVW(b, addVW, 4) } +func BenchmarkAddVW_5(b *testing.B) { benchmarkFunVW(b, addVW, 5) } +func BenchmarkAddVW_1e1(b *testing.B) { benchmarkFunVW(b, addVW, 1e1) } +func BenchmarkAddVW_1e2(b *testing.B) { benchmarkFunVW(b, addVW, 1e2) } +func BenchmarkAddVW_1e3(b *testing.B) { benchmarkFunVW(b, addVW, 1e3) } +func BenchmarkAddVW_1e4(b *testing.B) { benchmarkFunVW(b, addVW, 1e4) } +func BenchmarkAddVW_1e5(b *testing.B) { benchmarkFunVW(b, addVW, 1e5) } + type funVWW func(z, x []Word, y, r Word) (c Word) type argVWW struct { z, x nat @@ -334,6 +396,28 @@ func TestMulAddWWW(t *testing.T) { } } +func benchmarkAddMulVVW(b *testing.B, n int) { + x := rndV(n) + y := rndW() + z := make([]Word, n) + b.SetBytes(int64(n * _W)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + addMulVVW(z, x, y) + } +} + +func BenchmarkAddMulVVW_1(b *testing.B) { benchmarkAddMulVVW(b, 1) } +func BenchmarkAddMulVVW_2(b *testing.B) { benchmarkAddMulVVW(b, 2) } +func BenchmarkAddMulVVW_3(b *testing.B) { benchmarkAddMulVVW(b, 3) } +func BenchmarkAddMulVVW_4(b *testing.B) { benchmarkAddMulVVW(b, 4) } +func BenchmarkAddMulVVW_5(b *testing.B) { benchmarkAddMulVVW(b, 5) } +func BenchmarkAddMulVVW_1e1(b *testing.B) { benchmarkAddMulVVW(b, 1e1) } +func BenchmarkAddMulVVW_1e2(b *testing.B) { benchmarkAddMulVVW(b, 1e2) } +func BenchmarkAddMulVVW_1e3(b *testing.B) { benchmarkAddMulVVW(b, 1e3) } +func BenchmarkAddMulVVW_1e4(b *testing.B) { benchmarkAddMulVVW(b, 1e4) } +func BenchmarkAddMulVVW_1e5(b *testing.B) { benchmarkAddMulVVW(b, 1e5) } + func testWordBitLen(t *testing.T, fname string, f func(Word) int) { for i := 0; i <= _W; i++ { x := Word(1) << uint(i-1) // i == 0 => x == 0 diff --git a/src/pkg/math/big/calibrate_test.go b/src/pkg/math/big/calibrate_test.go index efe1837bb..f69ffbf5c 100644 --- a/src/pkg/math/big/calibrate_test.go +++ b/src/pkg/math/big/calibrate_test.go @@ -21,15 +21,17 @@ import ( var calibrate = flag.Bool("calibrate", false, "run calibration test") -// measure returns the time to run f -func measure(f func()) time.Duration { - const N = 100 - start := time.Now() - for i := N; i > 0; i-- { - f() - } - stop := time.Now() - return stop.Sub(start) / N +func karatsubaLoad(b *testing.B) { + BenchmarkMul(b) +} + +// measureKaratsuba returns the time to run a Karatsuba-relevant benchmark +// given Karatsuba threshold th. +func measureKaratsuba(th int) time.Duration { + th, karatsubaThreshold = karatsubaThreshold, th + res := testing.Benchmark(karatsubaLoad) + karatsubaThreshold = th + return time.Duration(res.NsPerOp()) } func computeThresholds() { @@ -37,35 +39,33 @@ func computeThresholds() { fmt.Printf("(run repeatedly for good results)\n") // determine Tk, the work load execution time using basic multiplication - karatsubaThreshold = 1e9 // disable karatsuba - Tb := measure(benchmarkMulLoad) - fmt.Printf("Tb = %dns\n", Tb) + Tb := measureKaratsuba(1e9) // th == 1e9 => Karatsuba multiplication disabled + fmt.Printf("Tb = %10s\n", Tb) // thresholds - n := 8 // any lower values for the threshold lead to very slow multiplies + th := 4 th1 := -1 th2 := -1 var deltaOld time.Duration - for count := -1; count != 0; count-- { + for count := -1; count != 0 && th < 128; count-- { // determine Tk, the work load execution time using Karatsuba multiplication - karatsubaThreshold = n // enable karatsuba - Tk := measure(benchmarkMulLoad) + Tk := measureKaratsuba(th) // improvement over Tb delta := (Tb - Tk) * 100 / Tb - fmt.Printf("n = %3d Tk = %8dns %4d%%", n, Tk, delta) + fmt.Printf("th = %3d Tk = %10s %4d%%", th, Tk, delta) // determine break-even point if Tk < Tb && th1 < 0 { - th1 = n + th1 = th fmt.Print(" break-even point") } // determine diminishing return if 0 < delta && delta < deltaOld && th2 < 0 { - th2 = n + th2 = th fmt.Print(" diminishing return") } deltaOld = delta @@ -74,10 +74,10 @@ func computeThresholds() { // trigger counter if th1 >= 0 && th2 >= 0 && count < 0 { - count = 20 // this many extra measurements after we got both thresholds + count = 10 // this many extra measurements after we got both thresholds } - n++ + th++ } } diff --git a/src/pkg/math/big/gcd_test.go b/src/pkg/math/big/gcd_test.go new file mode 100644 index 000000000..c0b9f5830 --- /dev/null +++ b/src/pkg/math/big/gcd_test.go @@ -0,0 +1,47 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements a GCD benchmark. +// Usage: go test math/big -test.bench GCD + +package big + +import ( + "math/rand" + "testing" +) + +// randInt returns a pseudo-random Int in the range [1<<(size-1), (1< 1<<(size-1) +} + +func runGCD(b *testing.B, aSize, bSize uint) { + b.StopTimer() + var r = rand.New(rand.NewSource(1234)) + aa := randInt(r, aSize) + bb := randInt(r, bSize) + b.StartTimer() + for i := 0; i < b.N; i++ { + new(Int).GCD(nil, nil, aa, bb) + } +} + +func BenchmarkGCD10x10(b *testing.B) { runGCD(b, 10, 10) } +func BenchmarkGCD10x100(b *testing.B) { runGCD(b, 10, 100) } +func BenchmarkGCD10x1000(b *testing.B) { runGCD(b, 10, 1000) } +func BenchmarkGCD10x10000(b *testing.B) { runGCD(b, 10, 10000) } +func BenchmarkGCD10x100000(b *testing.B) { runGCD(b, 10, 100000) } +func BenchmarkGCD100x100(b *testing.B) { runGCD(b, 100, 100) } +func BenchmarkGCD100x1000(b *testing.B) { runGCD(b, 100, 1000) } +func BenchmarkGCD100x10000(b *testing.B) { runGCD(b, 100, 10000) } +func BenchmarkGCD100x100000(b *testing.B) { runGCD(b, 100, 100000) } +func BenchmarkGCD1000x1000(b *testing.B) { runGCD(b, 1000, 1000) } +func BenchmarkGCD1000x10000(b *testing.B) { runGCD(b, 1000, 10000) } +func BenchmarkGCD1000x100000(b *testing.B) { runGCD(b, 1000, 100000) } +func BenchmarkGCD10000x10000(b *testing.B) { runGCD(b, 10000, 10000) } +func BenchmarkGCD10000x100000(b *testing.B) { runGCD(b, 10000, 100000) } +func BenchmarkGCD100000x100000(b *testing.B) { runGCD(b, 100000, 100000) } diff --git a/src/pkg/math/big/int.go b/src/pkg/math/big/int.go index cd2cd0e2d..bf2fd2009 100644 --- a/src/pkg/math/big/int.go +++ b/src/pkg/math/big/int.go @@ -51,6 +51,13 @@ func (z *Int) SetInt64(x int64) *Int { return z } +// SetUint64 sets z to x and returns z. +func (z *Int) SetUint64(x uint64) *Int { + z.abs = z.abs.setUint64(uint64(x)) + z.neg = false + return z +} + // NewInt allocates and returns a new Int set to x. func NewInt(x int64) *Int { return new(Int).SetInt64(x) @@ -412,7 +419,7 @@ func (x *Int) Format(s fmt.State, ch rune) { if precisionSet { switch { case len(digits) < precision: - zeroes = precision - len(digits) // count of zero padding + zeroes = precision - len(digits) // count of zero padding case digits == "0" && precision == 0: return // print nothing if zero value (x == 0) and zero precision ("." or ".0") } @@ -519,6 +526,19 @@ func (x *Int) Int64() int64 { return v } +// Uint64 returns the uint64 representation of x. +// If x cannot be represented in an uint64, the result is undefined. +func (x *Int) Uint64() uint64 { + if len(x.abs) == 0 { + return 0 + } + v := uint64(x.abs[0]) + if _W == 32 && len(x.abs) > 1 { + v |= uint64(x.abs[1]) << 32 + } + return v +} + // SetString sets z to the value of s, interpreted in the given base, // and returns z and a boolean indicating success. If SetString fails, // the value of z is undefined but the returned value is nil. @@ -561,19 +581,18 @@ func (x *Int) BitLen() int { return x.abs.bitLen() } -// Exp sets z = x**y mod m and returns z. If m is nil, z = x**y. +// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z. +// If y <= 0, the result is 1; if m == nil or m == 0, z = x**y. // See Knuth, volume 2, section 4.6.3. func (z *Int) Exp(x, y, m *Int) *Int { if y.neg || len(y.abs) == 0 { - neg := x.neg - z.SetInt64(1) - z.neg = neg - return z + return z.SetInt64(1) } + // y > 0 var mWords nat if m != nil { - mWords = m.abs + mWords = m.abs // m.abs may be nil for m == 0 } z.abs = z.abs.expNN(x.abs, y.abs, mWords) @@ -581,12 +600,12 @@ func (z *Int) Exp(x, y, m *Int) *Int { return z } -// GCD sets z to the greatest common divisor of a and b, which must be -// positive numbers, and returns z. +// GCD sets z to the greatest common divisor of a and b, which both must +// be > 0, and returns z. // If x and y are not nil, GCD sets x and y such that z = a*x + b*y. -// If either a or b is not positive, GCD sets z = x = y = 0. +// If either a or b is <= 0, GCD sets z = x = y = 0. func (z *Int) GCD(x, y, a, b *Int) *Int { - if a.neg || b.neg { + if a.Sign() <= 0 || b.Sign() <= 0 { z.SetInt64(0) if x != nil { x.SetInt64(0) @@ -596,6 +615,9 @@ func (z *Int) GCD(x, y, a, b *Int) *Int { } return z } + if x == nil && y == nil { + return z.binaryGCD(a, b) + } A := new(Int).Set(a) B := new(Int).Set(b) @@ -640,6 +662,63 @@ func (z *Int) GCD(x, y, a, b *Int) *Int { return z } +// binaryGCD sets z to the greatest common divisor of a and b, which both must +// be > 0, and returns z. +// See Knuth, The Art of Computer Programming, Vol. 2, Section 4.5.2, Algorithm B. +func (z *Int) binaryGCD(a, b *Int) *Int { + u := z + v := new(Int) + + // use one Euclidean iteration to ensure that u and v are approx. the same size + switch { + case len(a.abs) > len(b.abs): + u.Set(b) + v.Rem(a, b) + case len(a.abs) < len(b.abs): + u.Set(a) + v.Rem(b, a) + default: + u.Set(a) + v.Set(b) + } + + // v might be 0 now + if len(v.abs) == 0 { + return u + } + // u > 0 && v > 0 + + // determine largest k such that u = u' << k, v = v' << k + k := u.abs.trailingZeroBits() + if vk := v.abs.trailingZeroBits(); vk < k { + k = vk + } + u.Rsh(u, k) + v.Rsh(v, k) + + // determine t (we know that u > 0) + t := new(Int) + if u.abs[0]&1 != 0 { + // u is odd + t.Neg(v) + } else { + t.Set(u) + } + + for len(t.abs) > 0 { + // reduce t + t.Rsh(t, t.abs.trailingZeroBits()) + if t.neg { + v.Neg(t) + } else { + u.Set(t) + } + t.Sub(u, v) + } + + return u.Lsh(u, k) +} + // ProbablyPrime performs n Miller-Rabin tests to check whether x is prime. // If it returns true, x is prime with probability 1 - 1/4^n. // If it returns false, x is not prime. @@ -697,6 +776,13 @@ func (z *Int) Rsh(x *Int, n uint) *Int { // Bit returns the value of the i'th bit of x. That is, it // returns (x>>i)&1. The bit index i must be >= 0. func (x *Int) Bit(i int) uint { + if i == 0 { + // optimization for common case: odd/even test of x + if len(x.abs) > 0 { + return uint(x.abs[0] & 1) // bit 0 is same for -x + } + return 0 + } if i < 0 { panic("negative bit index") } @@ -894,3 +980,19 @@ func (z *Int) GobDecode(buf []byte) error { z.abs = z.abs.setBytes(buf[1:]) return nil } + +// MarshalJSON implements the json.Marshaler interface. +func (x *Int) MarshalJSON() ([]byte, error) { + // TODO(gri): get rid of the []byte/string conversions + return []byte(x.String()), nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (z *Int) UnmarshalJSON(x []byte) error { + // TODO(gri): get rid of the []byte/string conversions + _, ok := z.SetString(string(x), 0) + if !ok { + return fmt.Errorf("math/big: cannot unmarshal %s into a *big.Int", x) + } + return nil +} diff --git a/src/pkg/math/big/int_test.go b/src/pkg/math/big/int_test.go index 9700a9b5a..6c981e775 100644 --- a/src/pkg/math/big/int_test.go +++ b/src/pkg/math/big/int_test.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/gob" "encoding/hex" + "encoding/json" "fmt" "math/rand" "testing" @@ -642,7 +643,7 @@ func TestSetBytes(t *testing.T) { func checkBytes(b []byte) bool { b2 := new(Int).SetBytes(b).Bytes() - return bytes.Compare(b, b2) == 0 + return bytes.Equal(b, b2) } func TestBytes(t *testing.T) { @@ -766,8 +767,10 @@ var expTests = []struct { x, y, m string out string }{ + {"5", "-7", "", "1"}, + {"-5", "-7", "", "1"}, {"5", "0", "", "1"}, - {"-5", "0", "", "-1"}, + {"-5", "0", "", "1"}, {"5", "1", "", "5"}, {"-5", "1", "", "-5"}, {"-2", "3", "2", "0"}, @@ -778,6 +781,7 @@ var expTests = []struct { {"0x8000000000000000", "3", "6719", "5447"}, {"0x8000000000000000", "1000", "6719", "1603"}, {"0x8000000000000000", "1000000", "6719", "3199"}, + {"0x8000000000000000", "-1000000", "6719", "1"}, { "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", "298472983472983471903246121093472394872319615612417471234712061", @@ -806,25 +810,33 @@ func TestExp(t *testing.T) { continue } - z := y.Exp(x, y, m) - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) + z1 := new(Int).Exp(x, y, m) + if !isNormalized(z1) { + t.Errorf("#%d: %v is not normalized", i, *z1) } - if z.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, z, out) + if z1.Cmp(out) != 0 { + t.Errorf("#%d: got %s want %s", i, z1, out) + } + + if m == nil { + // the result should be the same as for m == 0; + // specifically, there should be no div-zero panic + m = &Int{abs: nat{}} // m != nil && len(m.abs) == 0 + z2 := new(Int).Exp(x, y, m) + if z2.Cmp(z1) != 0 { + t.Errorf("#%d: got %s want %s", i, z1, z2) + } } } } func checkGcd(aBytes, bBytes []byte) bool { - a := new(Int).SetBytes(aBytes) - b := new(Int).SetBytes(bBytes) - x := new(Int) y := new(Int) - d := new(Int) + a := new(Int).SetBytes(aBytes) + b := new(Int).SetBytes(bBytes) - d.GCD(x, y, a, b) + d := new(Int).GCD(x, y, a, b) x.Mul(x, a) y.Mul(y, b) x.Add(x, y) @@ -833,32 +845,70 @@ func checkGcd(aBytes, bBytes []byte) bool { } var gcdTests = []struct { - a, b int64 - d, x, y int64 + d, x, y, a, b string }{ - {120, 23, 1, -9, 47}, -} - -func TestGcd(t *testing.T) { - for i, test := range gcdTests { - a := NewInt(test.a) - b := NewInt(test.b) + // a <= 0 || b <= 0 + {"0", "0", "0", "0", "0"}, + {"0", "0", "0", "0", "7"}, + {"0", "0", "0", "11", "0"}, + {"0", "0", "0", "-77", "35"}, + {"0", "0", "0", "64515", "-24310"}, + {"0", "0", "0", "-64515", "-24310"}, + + {"1", "-9", "47", "120", "23"}, + {"7", "1", "-2", "77", "35"}, + {"935", "-3", "8", "64515", "24310"}, + {"935000000000000000", "-3", "8", "64515000000000000000", "24310000000000000000"}, + {"1", "-221", "22059940471369027483332068679400581064239780177629666810348940098015901108344", "98920366548084643601728869055592650835572950932266967461790948584315647051443", "991"}, + + // test early exit (after one Euclidean iteration) in binaryGCD + {"1", "", "", "1", "98920366548084643601728869055592650835572950932266967461790948584315647051443"}, +} + +func testGcd(t *testing.T, d, x, y, a, b *Int) { + var X *Int + if x != nil { + X = new(Int) + } + var Y *Int + if y != nil { + Y = new(Int) + } - x := new(Int) - y := new(Int) - d := new(Int) + D := new(Int).GCD(X, Y, a, b) + if D.Cmp(d) != 0 { + t.Errorf("GCD(%s, %s): got d = %s, want %s", a, b, D, d) + } + if x != nil && X.Cmp(x) != 0 { + t.Errorf("GCD(%s, %s): got x = %s, want %s", a, b, X, x) + } + if y != nil && Y.Cmp(y) != 0 { + t.Errorf("GCD(%s, %s): got y = %s, want %s", a, b, Y, y) + } - expectedX := NewInt(test.x) - expectedY := NewInt(test.y) - expectedD := NewInt(test.d) + // binaryGCD requires a > 0 && b > 0 + if a.Sign() <= 0 || b.Sign() <= 0 { + return + } - d.GCD(x, y, a, b) + D.binaryGCD(a, b) + if D.Cmp(d) != 0 { + t.Errorf("binaryGcd(%s, %s): got d = %s, want %s", a, b, D, d) + } +} - if expectedX.Cmp(x) != 0 || - expectedY.Cmp(y) != 0 || - expectedD.Cmp(d) != 0 { - t.Errorf("#%d got (%s %s %s) want (%s %s %s)", i, x, y, d, expectedX, expectedY, expectedD) - } +func TestGcd(t *testing.T) { + for _, test := range gcdTests { + d, _ := new(Int).SetString(test.d, 0) + x, _ := new(Int).SetString(test.x, 0) + y, _ := new(Int).SetString(test.y, 0) + a, _ := new(Int).SetString(test.a, 0) + b, _ := new(Int).SetString(test.b, 0) + + testGcd(t, d, nil, nil, a, b) + testGcd(t, d, x, nil, a, b) + testGcd(t, d, nil, y, a, b) + testGcd(t, d, x, y, a, b) } quick.Check(checkGcd, nil) @@ -1085,6 +1135,36 @@ func TestInt64(t *testing.T) { } } +var uint64Tests = []uint64{ + 0, + 1, + 4294967295, + 4294967296, + 8589934591, + 8589934592, + 9223372036854775807, + 9223372036854775808, + 18446744073709551615, // 1<<64 - 1 +} + +func TestUint64(t *testing.T) { + in := new(Int) + for i, testVal := range uint64Tests { + in.SetUint64(testVal) + out := in.Uint64() + + if out != testVal { + t.Errorf("#%d got %d want %d", i, out, testVal) + } + + str := fmt.Sprint(testVal) + strOut := in.String() + if strOut != str { + t.Errorf("#%d.String got %s want %s", i, strOut, str) + } + } +} + var bitwiseTests = []struct { x, y string and, or, xor, andNot string @@ -1368,8 +1448,12 @@ func TestModInverse(t *testing.T) { } } -// used by TestIntGobEncoding and TestRatGobEncoding -var gobEncodingTests = []string{ +var encodingTests = []string{ + "-539345864568634858364538753846587364875430589374589", + "-678645873", + "-100", + "-2", + "-1", "0", "1", "2", @@ -1383,26 +1467,37 @@ func TestIntGobEncoding(t *testing.T) { var medium bytes.Buffer enc := gob.NewEncoder(&medium) dec := gob.NewDecoder(&medium) - for i, test := range gobEncodingTests { - for j := 0; j < 2; j++ { - medium.Reset() // empty buffer for each test case (in case of failures) - stest := test - if j != 0 { - // negative numbers - stest = "-" + test - } - var tx Int - tx.SetString(stest, 10) - if err := enc.Encode(&tx); err != nil { - t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err) - } - var rx Int - if err := dec.Decode(&rx); err != nil { - t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err) - } - if rx.Cmp(&tx) != 0 { - t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx) - } + for _, test := range encodingTests { + medium.Reset() // empty buffer for each test case (in case of failures) + var tx Int + tx.SetString(test, 10) + if err := enc.Encode(&tx); err != nil { + t.Errorf("encoding of %s failed: %s", &tx, err) + } + var rx Int + if err := dec.Decode(&rx); err != nil { + t.Errorf("decoding of %s failed: %s", &tx, err) + } + if rx.Cmp(&tx) != 0 { + t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx) + } + } +} + +func TestIntJSONEncoding(t *testing.T) { + for _, test := range encodingTests { + var tx Int + tx.SetString(test, 10) + b, err := json.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + } + var rx Int + if err := json.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + } + if rx.Cmp(&tx) != 0 { + t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) } } } diff --git a/src/pkg/math/big/nat.go b/src/pkg/math/big/nat.go index eaa6ff066..9d09f97b7 100644 --- a/src/pkg/math/big/nat.go +++ b/src/pkg/math/big/nat.go @@ -236,7 +236,7 @@ func karatsubaSub(z, x nat, n int) { // Operands that are shorter than karatsubaThreshold are multiplied using // "grade school" multiplication; for longer operands the Karatsuba algorithm // is used. -var karatsubaThreshold int = 32 // computed by calibrate.go +var karatsubaThreshold int = 40 // computed by calibrate.go // karatsuba multiplies x and y and leaves the result in z. // Both x and y must have the same length n and n must be a @@ -342,7 +342,7 @@ func alias(x, y nat) bool { return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1] } -// addAt implements z += x*(1<<(_W*i)); z must be long enough. +// addAt implements z += x<<(_W*i); z must be long enough. // (we don't use nat.add because we need z to stay the same // slice, and we don't need to normalize z after each addition) func addAt(z, x nat, i int) { @@ -396,7 +396,7 @@ func (z nat) mul(x, y nat) nat { } // use basic multiplication if the numbers are small - if n < karatsubaThreshold || n < 2 { + if n < karatsubaThreshold { z = z.make(m + n) basicMul(z, x, y) return z.norm() @@ -405,8 +405,8 @@ func (z nat) mul(x, y nat) nat { // determine Karatsuba length k such that // - // x = x1*b + x0 - // y = y1*b + y0 (and k <= len(y), which implies k <= len(x)) + // x = xh*b + x0 (0 <= x0 < b) + // y = yh*b + y0 (0 <= y0 < b) // b = 1<<(_W*k) ("base" of digits xi, yi) // k := karatsubaLen(n) @@ -417,27 +417,44 @@ func (z nat) mul(x, y nat) nat { y0 := y[0:k] // y0 is not normalized z = z.make(max(6*k, m+n)) // enough space for karatsuba of x0*y0 and full result of x*y karatsuba(z, x0, y0) - z = z[0 : m+n] // z has final length but may be incomplete, upper portion is garbage + z = z[0 : m+n] // z has final length but may be incomplete + z[2*k:].clear() // upper portion of z is garbage (and 2*k <= m+n since k <= n <= m) - // If x1 and/or y1 are not 0, add missing terms to z explicitly: + // If xh != 0 or yh != 0, add the missing terms to z. For + // + // xh = xi*b^i + ... + x2*b^2 + x1*b (0 <= xi < b) + // yh = y1*b (0 <= y1 < b) + // + // the missing terms are + // + // x0*y1*b and xi*y0*b^i, xi*y1*b^(i+1) for i > 0 // - // m+n 2*k 0 - // z = [ ... | x0*y0 ] - // + [ x1*y1 ] - // + [ x1*y0 ] - // + [ x0*y1 ] + // since all the yi for i > 1 are 0 by choice of k: If any of them + // were > 0, then yh >= b^2 and thus y >= b^2. Then k' = k*2 would + // be a larger valid threshold contradicting the assumption about k. // if k < n || m != n { - x1 := x[k:] // x1 is normalized because x is - y1 := y[k:] // y1 is normalized because y is var t nat - t = t.mul(x1, y1) - copy(z[2*k:], t) - z[2*k+len(t):].clear() // upper portion of z is garbage - t = t.mul(x1, y0.norm()) - addAt(z, t, k) - t = t.mul(x0.norm(), y1) + + // add x0*y1*b + x0 := x0.norm() + y1 := y[k:] // y1 is normalized because y is + t = t.mul(x0, y1) // update t so we don't lose t's underlying array addAt(z, t, k) + + // add xi*y0< k { + xi = xi[:k] + } + xi = xi.norm() + t = t.mul(xi, y0) + addAt(z, t, i) + t = t.mul(xi, y1) + addAt(z, t, i+k) + } } return z.norm() @@ -493,14 +510,9 @@ func (z nat) div(z2, u, v nat) (q, r nat) { } if len(v) == 1 { - var rprime Word - q, rprime = z.divW(u, v[0]) - if rprime > 0 { - r = z2.make(1) - r[0] = rprime - } else { - r = z2.make(0) - } + var r2 Word + q, r2 = z.divW(u, v[0]) + r = z2.setWord(r2) return } @@ -740,7 +752,7 @@ func (x nat) string(charset string) string { // convert power of two and non power of two bases separately if b == b&-b { // shift is base-b digit size in bits - shift := uint(trailingZeroBits(b)) // shift > 0 because b >= 2 + shift := trailingZeroBits(b) // shift > 0 because b >= 2 mask := Word(1)<= sqrt(x) k := 1 - for words := leafSize; words < m>>1 && k < len(cacheBase10); words <<= 1 { + for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 { k++ } - // create new table of divisors or extend and reuse existing table as appropriate - var table []divisor - var cached bool - switch b { - case 10: - table = cacheBase10[0:k] // reuse old table for this conversion - cached = true - default: - table = make([]divisor, k) // new table for this conversion + // reuse and extend existing table of divisors or create new table as appropriate + var table []divisor // for b == 10, table overlaps with cacheBase10.table + if b == 10 { + cacheBase10.Lock() + table = cacheBase10.table[0:k] // reuse old table for this conversion + } else { + table = make([]divisor, k) // create new table for this conversion } // extend table if table[k-1].ndigits == 0 { - if cached { - cacheLock.Lock() // begin critical section - } - // add new entries as needed var larger nat for i := 0; i < k; i++ { if table[i].ndigits == 0 { if i == 0 { - table[i].bbb = nat(nil).expWW(bb, Word(leafSize)) - table[i].ndigits = ndigits * leafSize + table[0].bbb = nat(nil).expWW(bb, Word(leafSize)) + table[0].ndigits = ndigits * leafSize } else { table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb) table[i].ndigits = 2 * table[i-1].ndigits @@ -968,10 +976,10 @@ func divisors(m int, b Word, ndigits int, bb Word) []divisor { table[i].nbits = table[i].bbb.bitLen() } } + } - if cached { - cacheLock.Unlock() // end critical section - } + if b == 10 { + cacheBase10.Unlock() } return table @@ -993,10 +1001,9 @@ var deBruijn64Lookup = []byte{ 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, } -// trailingZeroBits returns the number of consecutive zero bits on the right -// side of the given Word. -// See Knuth, volume 4, section 7.3.1 -func trailingZeroBits(x Word) int { +// trailingZeroBits returns the number of consecutive least significant zero +// bits of x. +func trailingZeroBits(x Word) uint { // x & -x leaves only the right-most bit set in the word. Let k be the // index of that bit. Since only a single bit is set, the value is two // to the power of k. Multiplying by a power of two is equivalent to @@ -1005,18 +1012,33 @@ func trailingZeroBits(x Word) int { // Therefore, if we have a left shifted version of this constant we can // find by how many bits it was shifted by looking at which six bit // substring ended up at the top of the word. + // (Knuth, volume 4, section 7.3.1) switch _W { case 32: - return int(deBruijn32Lookup[((x&-x)*deBruijn32)>>27]) + return uint(deBruijn32Lookup[((x&-x)*deBruijn32)>>27]) case 64: - return int(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58]) + return uint(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58]) default: - panic("Unknown word size") + panic("unknown word size") } return 0 } +// trailingZeroBits returns the number of consecutive least significant zero +// bits of x. +func (x nat) trailingZeroBits() uint { + if len(x) == 0 { + return 0 + } + var i uint + for x[i] == 0 { + i++ + } + // x[i] != 0 + return i*_W + trailingZeroBits(x[i]) +} + // z = x << s func (z nat) shl(x nat, s uint) nat { m := len(x) @@ -1169,29 +1191,6 @@ func (x nat) modW(d Word) (r Word) { return divWVW(q, 0, x, d) } -// powersOfTwoDecompose finds q and k with x = q * 1< 0 - if m != nil { + if len(m) != 0 { // We likely end up being as long as the modulus. z = z.make(len(m)) } z = z.set(x) - v := y[len(y)-1] - // It's invalid for the most significant word to be zero, therefore we - // will find a one bit. + + // If the base is non-trivial and the exponent is large, we use + // 4-bit, windowed exponentiation. This involves precomputing 14 values + // (x^2...x^15) but then reduces the number of multiply-reduces by a + // third. Even for a 32-bit exponent, this reduces the number of + // operations. + if len(x) > 1 && len(y) > 1 && len(m) > 0 { + return z.expNNWindowed(x, y, m) + } + + v := y[len(y)-1] // v > 0 because y is normalized and y > 0 shift := leadingZeros(v) + 1 v <<= shift var q nat @@ -1259,15 +1269,21 @@ func (z nat) expNN(x, y, m nat) nat { // we also multiply by x, thus adding one to the power. w := _W - int(shift) + // zz and r are used to avoid allocating in mul and div as + // otherwise the arguments would alias. + var zz, r nat for j := 0; j < w; j++ { - z = z.mul(z, z) + zz = zz.mul(z, z) + zz, z = z, zz if v&mask != 0 { - z = z.mul(z, x) + zz = zz.mul(z, x) + zz, z = z, zz } - if m != nil { - q, z = q.div(z, z, m) + if len(m) != 0 { + zz, r = zz.div(r, z, m) + zz, r, q, z = q, z, zz, r } v <<= 1 @@ -1277,14 +1293,17 @@ func (z nat) expNN(x, y, m nat) nat { v = y[i] for j := 0; j < _W; j++ { - z = z.mul(z, z) + zz = zz.mul(z, z) + zz, z = z, zz if v&mask != 0 { - z = z.mul(z, x) + zz = zz.mul(z, x) + zz, z = z, zz } - if m != nil { - q, z = q.div(z, z, m) + if len(m) != 0 { + zz, r = zz.div(r, z, m) + zz, r, q, z = q, z, zz, r } v <<= 1 @@ -1294,6 +1313,69 @@ func (z nat) expNN(x, y, m nat) nat { return z.norm() } +// expNNWindowed calculates x**y mod m using a fixed, 4-bit window. +func (z nat) expNNWindowed(x, y, m nat) nat { + // zz and r are used to avoid allocating in mul and div as otherwise + // the arguments would alias. + var zz, r nat + + const n = 4 + // powers[i] contains x^i. + var powers [1 << n]nat + powers[0] = natOne + powers[1] = x + for i := 2; i < 1<= 0; i-- { + yi := y[i] + for j := 0; j < _W; j += n { + if i != len(y)-1 || j != 0 { + // Unrolled loop for significant performance + // gain. Use go test -bench=".*" in crypto/rsa + // to check performance before making changes. + zz = zz.mul(z, z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.mul(z, z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.mul(z, z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.mul(z, z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + } + + zz = zz.mul(z, powers[yi>>(_W-n)]) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + yi <<= n + } + } + + return z.norm() +} + // probablyPrime performs reps Miller-Rabin tests to check whether n is prime. // If it returns true, n is prime with probability 1 - 1/4^reps. // If it returns false, n is not prime. @@ -1343,8 +1425,9 @@ func (n nat) probablyPrime(reps int) bool { } nm1 := nat(nil).sub(n, natOne) - // 1< 10 { + t.Errorf("multiplication uses too much memory (%d > %d times the size of inputs)", allocSize, ratio) } } -func benchmarkMulLoad() { - for j := 1; j <= 10; j++ { - x := mulArg[0 : j*100] - mulTmp.mul(x, x) - } +func rndNat(n int) nat { + return nat(rndV(n)).norm() } func BenchmarkMul(b *testing.B) { + mulx := rndNat(1e4) + muly := rndNat(1e4) + b.ResetTimer() for i := 0; i < b.N; i++ { - benchmarkMulLoad() + var z nat + z.mul(mulx, muly) } } @@ -362,6 +410,20 @@ func TestScanPi(t *testing.T) { } } +func TestScanPiParallel(t *testing.T) { + const n = 2 + c := make(chan int) + for i := 0; i < n; i++ { + go func() { + TestScanPi(t) + c <- 0 + }() + } + for i := 0; i < n; i++ { + <-c + } +} + func BenchmarkScanPi(b *testing.B) { for i := 0; i < b.N; i++ { var x nat @@ -369,6 +431,28 @@ func BenchmarkScanPi(b *testing.B) { } } +func BenchmarkStringPiParallel(b *testing.B) { + var x nat + x, _, _ = x.scan(strings.NewReader(pi), 0) + if x.decimalString() != pi { + panic("benchmark incorrect: conversion failed") + } + n := runtime.GOMAXPROCS(0) + m := b.N / n // n*m <= b.N due to flooring, but the error is neglibible (n is not very large) + c := make(chan int, n) + for i := 0; i < n; i++ { + go func() { + for j := 0; j < m; j++ { + x.decimalString() + } + c <- 0 + }() + } + for i := 0; i < n; i++ { + <-c + } +} + func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) } func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) } func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) } @@ -463,13 +547,13 @@ func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) } func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) } func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) } func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) } -func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths +func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) } func LeafSizeHelper(b *testing.B, base Word, size int) { b.StopTimer() originalLeafSize := leafSize - resetTable(cacheBase10[:]) + resetTable(cacheBase10.table[:]) leafSize = size b.StartTimer() @@ -486,7 +570,7 @@ func LeafSizeHelper(b *testing.B, base Word, size int) { } b.StopTimer() - resetTable(cacheBase10[:]) + resetTable(cacheBase10.table[:]) leafSize = originalLeafSize b.StartTimer() } @@ -616,14 +700,23 @@ func TestModW(t *testing.T) { } func TestTrailingZeroBits(t *testing.T) { - var x Word - x-- - for i := 0; i < _W; i++ { - if trailingZeroBits(x) != i { - t.Errorf("Failed at step %d: x: %x got: %d", i, x, trailingZeroBits(x)) + x := Word(1) + for i := uint(0); i <= _W; i++ { + n := trailingZeroBits(x) + if n != i%_W { + t.Errorf("got trailingZeroBits(%#x) = %d; want %d", x, n, i%_W) } x <<= 1 } + + y := nat(nil).set(natOne) + for i := uint(0); i <= 3*_W; i++ { + n := y.trailingZeroBits() + if n != i { + t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.string(lowercaseDigits[0:16]), n, i) + } + y = y.shl(y, 1) + } } var expNNTests = []struct { diff --git a/src/pkg/math/big/rat.go b/src/pkg/math/big/rat.go index 7bd83fc0f..3e6473d92 100644 --- a/src/pkg/math/big/rat.go +++ b/src/pkg/math/big/rat.go @@ -10,14 +10,17 @@ import ( "encoding/binary" "errors" "fmt" + "math" "strings" ) // A Rat represents a quotient a/b of arbitrary precision. // The zero value for a Rat represents the value 0. type Rat struct { - a Int - b nat // len(b) == 0 acts like b == 1 + // To make zero values for Rat work w/o initialization, + // a zero value of b (len(b) == 0) acts like b == 1. + // a.neg determines the sign of the Rat, b.neg is ignored. + a, b Int } // NewRat creates a new Rat with numerator a and denominator b. @@ -25,6 +28,156 @@ func NewRat(a, b int64) *Rat { return new(Rat).SetFrac64(a, b) } +// SetFloat64 sets z to exactly f and returns z. +// If f is not finite, SetFloat returns nil. +func (z *Rat) SetFloat64(f float64) *Rat { + const expMask = 1<<11 - 1 + bits := math.Float64bits(f) + mantissa := bits & (1<<52 - 1) + exp := int((bits >> 52) & expMask) + switch exp { + case expMask: // non-finite + return nil + case 0: // denormal + exp -= 1022 + default: // normal + mantissa |= 1 << 52 + exp -= 1023 + } + + shift := 52 - exp + + // Optimisation (?): partially pre-normalise. + for mantissa&1 == 0 && shift > 0 { + mantissa >>= 1 + shift-- + } + + z.a.SetUint64(mantissa) + z.a.neg = f < 0 + z.b.Set(intOne) + if shift > 0 { + z.b.Lsh(&z.b, uint(shift)) + } else { + z.a.Lsh(&z.a, uint(-shift)) + } + return z.norm() +} + +// isFinite reports whether f represents a finite rational value. +// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0). +func isFinite(f float64) bool { + return math.Abs(f) <= math.MaxFloat64 +} + +// low64 returns the least significant 64 bits of natural number z. +func low64(z nat) uint64 { + if len(z) == 0 { + return 0 + } + if _W == 32 && len(z) > 1 { + return uint64(z[1])<<32 | uint64(z[0]) + } + return uint64(z[0]) +} + +// quotToFloat returns the non-negative IEEE 754 double-precision +// value nearest to the quotient a/b, using round-to-even in halfway +// cases. It does not mutate its arguments. +// Preconditions: b is non-zero; a and b have no common factors. +func quotToFloat(a, b nat) (f float64, exact bool) { + // TODO(adonovan): specialize common degenerate cases: 1.0, integers. + alen := a.bitLen() + if alen == 0 { + return 0, true + } + blen := b.bitLen() + if blen == 0 { + panic("division by zero") + } + + // 1. Left-shift A or B such that quotient A/B is in [1<<53, 1<<55). + // (54 bits if A=B.) + // This is 2 or 3 more than the float64 mantissa field width of 52: + // - the optional extra bit is shifted away in step 3 below. + // - the high-order 1 is omitted in float64 "normal" representation; + // - the low-order 1 will be used during rounding then discarded. + exp := alen - blen + var a2, b2 nat + a2 = a2.set(a) + b2 = b2.set(b) + if shift := 54 - exp; shift > 0 { + a2 = a2.shl(a2, uint(shift)) + } else if shift < 0 { + b2 = b2.shl(b2, uint(-shift)) + } + + // 2. Compute quotient and remainder (q, r). NB: due to the + // extra shift, the low-order bit of q is logically the + // high-order bit of r. + var q nat + q, r := q.div(a2, a2, b2) // (recycle a2) + mantissa := low64(q) + haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half + + // 3. If quotient didn't fit in 54 bits, re-do division by b2<<1 + // (in effect---we accomplish this incrementally). + if mantissa>>54 == 1 { + if mantissa&1 == 1 { + haveRem = true + } + mantissa >>= 1 + exp++ + } + if mantissa>>53 != 1 { + panic("expected exactly 54 bits of result") + } + + // 4. Rounding. + if -1022-52 <= exp && exp <= -1022 { + // Denormal case; lose 'shift' bits of precision. + shift := uint64(-1022 - (exp - 1)) // [1..53) + lostbits := mantissa & (1<>= shift + exp = -1023 + 2 + } + // Round q using round-half-to-even. + exact = !haveRem + if mantissa&1 != 0 { + exact = false + if haveRem || mantissa&2 != 0 { + if mantissa++; mantissa >= 1<<54 { + // Complete rollover 11...1 => 100...0, so shift is safe + mantissa >>= 1 + exp++ + } + } + } + mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 2^53. + + f = math.Ldexp(float64(mantissa), exp-53) + if math.IsInf(f, 0) { + exact = false + } + return +} + +// Float64 returns the nearest float64 value to z. +// If z is exactly representable as a float64, Float64 returns exact=true. +// If z is negative, so too is f, even if f==0. +func (z *Rat) Float64() (f float64, exact bool) { + b := z.b.abs + if len(b) == 0 { + b = b.set(natOne) // materialize denominator + } + f, exact = quotToFloat(z.a.abs, b) + if z.a.neg { + f = -f + } + return +} + // SetFrac sets z to a/b and returns z. func (z *Rat) SetFrac(a, b *Int) *Rat { z.a.neg = a.neg != b.neg @@ -36,7 +189,7 @@ func (z *Rat) SetFrac(a, b *Int) *Rat { babs = nat(nil).set(babs) // make a copy } z.a.abs = z.a.abs.set(a.abs) - z.b = z.b.set(babs) + z.b.abs = z.b.abs.set(babs) return z.norm() } @@ -50,21 +203,21 @@ func (z *Rat) SetFrac64(a, b int64) *Rat { b = -b z.a.neg = !z.a.neg } - z.b = z.b.setUint64(uint64(b)) + z.b.abs = z.b.abs.setUint64(uint64(b)) return z.norm() } // SetInt sets z to x (by making a copy of x) and returns z. func (z *Rat) SetInt(x *Int) *Rat { z.a.Set(x) - z.b = z.b.make(0) + z.b.abs = z.b.abs.make(0) return z } // SetInt64 sets z to x and returns z. func (z *Rat) SetInt64(x int64) *Rat { z.a.SetInt64(x) - z.b = z.b.make(0) + z.b.abs = z.b.abs.make(0) return z } @@ -72,7 +225,7 @@ func (z *Rat) SetInt64(x int64) *Rat { func (z *Rat) Set(x *Rat) *Rat { if z != x { z.a.Set(&x.a) - z.b = z.b.set(x.b) + z.b.Set(&x.b) } return z } @@ -97,15 +250,15 @@ func (z *Rat) Inv(x *Rat) *Rat { panic("division by zero") } z.Set(x) - a := z.b + a := z.b.abs if len(a) == 0 { - a = a.setWord(1) // materialize numerator + a = a.set(natOne) // materialize numerator } b := z.a.abs if b.cmp(natOne) == 0 { b = b.make(0) // normalize denominator } - z.a.abs, z.b = a, b // sign doesn't change + z.a.abs, z.b.abs = a, b // sign doesn't change return z } @@ -121,38 +274,26 @@ func (x *Rat) Sign() int { // IsInt returns true if the denominator of x is 1. func (x *Rat) IsInt() bool { - return len(x.b) == 0 || x.b.cmp(natOne) == 0 + return len(x.b.abs) == 0 || x.b.abs.cmp(natOne) == 0 } // Num returns the numerator of x; it may be <= 0. // The result is a reference to x's numerator; it -// may change if a new value is assigned to x. +// may change if a new value is assigned to x, and vice versa. +// The sign of the numerator corresponds to the sign of x. func (x *Rat) Num() *Int { return &x.a } // Denom returns the denominator of x; it is always > 0. // The result is a reference to x's denominator; it -// may change if a new value is assigned to x. +// may change if a new value is assigned to x, and vice versa. func (x *Rat) Denom() *Int { - if len(x.b) == 0 { - return &Int{abs: nat{1}} + x.b.neg = false // the result is always >= 0 + if len(x.b.abs) == 0 { + x.b.abs = x.b.abs.set(natOne) // materialize denominator } - return &Int{abs: x.b} -} - -func gcd(x, y nat) nat { - // Euclidean algorithm. - var a, b nat - a = a.set(x) - b = b.set(y) - for len(b) != 0 { - var q, r nat - _, r = q.div(r, a, b) - a = b - b = r - } - return a + return &x.b } func (z *Rat) norm() *Rat { @@ -160,17 +301,25 @@ func (z *Rat) norm() *Rat { case len(z.a.abs) == 0: // z == 0 - normalize sign and denominator z.a.neg = false - z.b = z.b.make(0) - case len(z.b) == 0: + z.b.abs = z.b.abs.make(0) + case len(z.b.abs) == 0: // z is normalized int - nothing to do - case z.b.cmp(natOne) == 0: + case z.b.abs.cmp(natOne) == 0: // z is int - normalize denominator - z.b = z.b.make(0) + z.b.abs = z.b.abs.make(0) default: - if f := gcd(z.a.abs, z.b); f.cmp(natOne) != 0 { - z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f) - z.b, _ = z.b.div(nil, z.b, f) + neg := z.a.neg + z.a.neg = false + z.b.neg = false + if f := NewInt(0).binaryGCD(&z.a, &z.b); f.Cmp(intOne) != 0 { + z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs) + z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs) + if z.b.abs.cmp(natOne) == 0 { + // z is int - normalize denominator + z.b.abs = z.b.abs.make(0) + } } + z.a.neg = neg } return z } @@ -207,31 +356,31 @@ func scaleDenom(x *Int, f nat) *Int { // +1 if x > y // func (x *Rat) Cmp(y *Rat) int { - return scaleDenom(&x.a, y.b).Cmp(scaleDenom(&y.a, x.b)) + return scaleDenom(&x.a, y.b.abs).Cmp(scaleDenom(&y.a, x.b.abs)) } // Add sets z to the sum x+y and returns z. func (z *Rat) Add(x, y *Rat) *Rat { - a1 := scaleDenom(&x.a, y.b) - a2 := scaleDenom(&y.a, x.b) + a1 := scaleDenom(&x.a, y.b.abs) + a2 := scaleDenom(&y.a, x.b.abs) z.a.Add(a1, a2) - z.b = mulDenom(z.b, x.b, y.b) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) return z.norm() } // Sub sets z to the difference x-y and returns z. func (z *Rat) Sub(x, y *Rat) *Rat { - a1 := scaleDenom(&x.a, y.b) - a2 := scaleDenom(&y.a, x.b) + a1 := scaleDenom(&x.a, y.b.abs) + a2 := scaleDenom(&y.a, x.b.abs) z.a.Sub(a1, a2) - z.b = mulDenom(z.b, x.b, y.b) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) return z.norm() } // Mul sets z to the product x*y and returns z. func (z *Rat) Mul(x, y *Rat) *Rat { z.a.Mul(&x.a, &y.a) - z.b = mulDenom(z.b, x.b, y.b) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) return z.norm() } @@ -241,10 +390,10 @@ func (z *Rat) Quo(x, y *Rat) *Rat { if len(y.a.abs) == 0 { panic("division by zero") } - a := scaleDenom(&x.a, y.b) - b := scaleDenom(&y.a, x.b) + a := scaleDenom(&x.a, y.b.abs) + b := scaleDenom(&y.a, x.b.abs) z.a.abs = a.abs - z.b = b.abs + z.b.abs = b.abs z.a.neg = a.neg != b.neg return z.norm() } @@ -286,7 +435,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) { } s = s[sep+1:] var err error - if z.b, _, err = z.b.scan(strings.NewReader(s), 10); err != nil { + if z.b.abs, _, err = z.b.abs.scan(strings.NewReader(s), 10); err != nil { return nil, false } return z.norm(), true @@ -317,11 +466,11 @@ func (z *Rat) SetString(s string) (*Rat, bool) { } powTen := nat(nil).expNN(natTen, exp.abs, nil) if exp.neg { - z.b = powTen + z.b.abs = powTen z.norm() } else { z.a.abs = z.a.abs.mul(z.a.abs, powTen) - z.b = z.b.make(0) + z.b.abs = z.b.abs.make(0) } return z, true @@ -330,8 +479,8 @@ func (z *Rat) SetString(s string) (*Rat, bool) { // String returns a string representation of z in the form "a/b" (even if b == 1). func (x *Rat) String() string { s := "/1" - if len(x.b) != 0 { - s = "/" + x.b.decimalString() + if len(x.b.abs) != 0 { + s = "/" + x.b.abs.decimalString() } return x.a.String() + s } @@ -355,9 +504,9 @@ func (x *Rat) FloatString(prec int) string { } return s } - // x.b != 0 + // x.b.abs != 0 - q, r := nat(nil).div(nat(nil), x.a.abs, x.b) + q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs) p := natOne if prec > 0 { @@ -365,11 +514,11 @@ func (x *Rat) FloatString(prec int) string { } r = r.mul(r, p) - r, r2 := r.div(nat(nil), r, x.b) + r, r2 := r.div(nat(nil), r, x.b.abs) // see if we need to round up r2 = r2.add(r2, r2) - if x.b.cmp(r2) <= 0 { + if x.b.abs.cmp(r2) <= 0 { r = r.add(r, natOne) if r.cmp(p) >= 0 { q = nat(nil).add(q, natOne) @@ -396,8 +545,8 @@ const ratGobVersion byte = 1 // GobEncode implements the gob.GobEncoder interface. func (x *Rat) GobEncode() ([]byte, error) { - buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4) - i := x.b.bytes(buf) + buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4) + i := x.b.abs.bytes(buf) j := x.a.abs.bytes(buf[0:i]) n := i - j if int(uint32(n)) != n { @@ -427,6 +576,6 @@ func (z *Rat) GobDecode(buf []byte) error { i := j + binary.BigEndian.Uint32(buf[j-4:j]) z.a.neg = b&1 != 0 z.a.abs = z.a.abs.setBytes(buf[j:i]) - z.b = z.b.setBytes(buf[i:]) + z.b.abs = z.b.abs.setBytes(buf[i:]) return nil } diff --git a/src/pkg/math/big/rat_test.go b/src/pkg/math/big/rat_test.go index f7f31ae1a..462dfb723 100644 --- a/src/pkg/math/big/rat_test.go +++ b/src/pkg/math/big/rat_test.go @@ -8,6 +8,9 @@ import ( "bytes" "encoding/gob" "fmt" + "math" + "strconv" + "strings" "testing" ) @@ -387,30 +390,19 @@ func TestRatGobEncoding(t *testing.T) { var medium bytes.Buffer enc := gob.NewEncoder(&medium) dec := gob.NewDecoder(&medium) - for i, test := range gobEncodingTests { - for j := 0; j < 4; j++ { - medium.Reset() // empty buffer for each test case (in case of failures) - stest := test - if j&1 != 0 { - // negative numbers - stest = "-" + test - } - if j%2 != 0 { - // fractions - stest = stest + "." + test - } - var tx Rat - tx.SetString(stest) - if err := enc.Encode(&tx); err != nil { - t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err) - } - var rx Rat - if err := dec.Decode(&rx); err != nil { - t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err) - } - if rx.Cmp(&tx) != 0 { - t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx) - } + for _, test := range encodingTests { + medium.Reset() // empty buffer for each test case (in case of failures) + var tx Rat + tx.SetString(test + ".14159265") + if err := enc.Encode(&tx); err != nil { + t.Errorf("encoding of %s failed: %s", &tx, err) + } + var rx Rat + if err := dec.Decode(&rx); err != nil { + t.Errorf("decoding of %s failed: %s", &tx, err) + } + if rx.Cmp(&tx) != 0 { + t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx) } } } @@ -454,3 +446,462 @@ func TestIssue2379(t *testing.T) { t.Errorf("5) got %s want %s", x, q) } } + +func TestIssue3521(t *testing.T) { + a := new(Int) + b := new(Int) + a.SetString("64375784358435883458348587", 0) + b.SetString("4789759874531", 0) + + // 0) a raw zero value has 1 as denominator + zero := new(Rat) + one := NewInt(1) + if zero.Denom().Cmp(one) != 0 { + t.Errorf("0) got %s want %s", zero.Denom(), one) + } + + // 1a) a zero value remains zero independent of denominator + x := new(Rat) + x.Denom().Set(new(Int).Neg(b)) + if x.Cmp(zero) != 0 { + t.Errorf("1a) got %s want %s", x, zero) + } + + // 1b) a zero value may have a denominator != 0 and != 1 + x.Num().Set(a) + qab := new(Rat).SetFrac(a, b) + if x.Cmp(qab) != 0 { + t.Errorf("1b) got %s want %s", x, qab) + } + + // 2a) an integral value becomes a fraction depending on denominator + x.SetFrac64(10, 2) + x.Denom().SetInt64(3) + q53 := NewRat(5, 3) + if x.Cmp(q53) != 0 { + t.Errorf("2a) got %s want %s", x, q53) + } + + // 2b) an integral value becomes a fraction depending on denominator + x = NewRat(10, 2) + x.Denom().SetInt64(3) + if x.Cmp(q53) != 0 { + t.Errorf("2b) got %s want %s", x, q53) + } + + // 3) changing the numerator/denominator of a Rat changes the Rat + x.SetFrac(a, b) + a = x.Num() + b = x.Denom() + a.SetInt64(5) + b.SetInt64(3) + if x.Cmp(q53) != 0 { + t.Errorf("3) got %s want %s", x, q53) + } +} + +// Test inputs to Rat.SetString. The prefix "long:" causes the test +// to be skipped in --test.short mode. (The threshold is about 500us.) +var float64inputs = []string{ + // + // Constants plundered from strconv/testfp.txt. + // + + // Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP + "5e+125", + "69e+267", + "999e-026", + "7861e-034", + "75569e-254", + "928609e-261", + "9210917e+080", + "84863171e+114", + "653777767e+273", + "5232604057e-298", + "27235667517e-109", + "653532977297e-123", + "3142213164987e-294", + "46202199371337e-072", + "231010996856685e-073", + "9324754620109615e+212", + "78459735791271921e+049", + "272104041512242479e+200", + "6802601037806061975e+198", + "20505426358836677347e-221", + "836168422905420598437e-234", + "4891559871276714924261e+222", + + // Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP + "9e-265", + "85e-037", + "623e+100", + "3571e+263", + "81661e+153", + "920657e-023", + "4603285e-024", + "87575437e-309", + "245540327e+122", + "6138508175e+120", + "83356057653e+193", + "619534293513e+124", + "2335141086879e+218", + "36167929443327e-159", + "609610927149051e-255", + "3743626360493413e-165", + "94080055902682397e-242", + "899810892172646163e+283", + "7120190517612959703e+120", + "25188282901709339043e-252", + "308984926168550152811e-052", + "6372891218502368041059e+064", + + // Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP + "5e-20", + "67e+14", + "985e+15", + "7693e-42", + "55895e-16", + "996622e-44", + "7038531e-32", + "60419369e-46", + "702990899e-20", + "6930161142e-48", + "25933168707e+13", + "596428896559e+20", + + // Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP + "3e-23", + "57e+18", + "789e-35", + "2539e-18", + "76173e+28", + "887745e-11", + "5382571e-37", + "82381273e-35", + "750486563e-38", + "3752432815e-39", + "75224575729e-45", + "459926601011e+15", + + // + // Constants plundered from strconv/atof_test.go. + // + + "0", + "1", + "+1", + "1e23", + "1E23", + "100000000000000000000000", + "1e-100", + "123456700", + "99999999999999974834176", + "100000000000000000000001", + "100000000000000008388608", + "100000000000000016777215", + "100000000000000016777216", + "-1", + "-0.1", + "-0", // NB: exception made for this input + "1e-20", + "625e-3", + + // largest float64 + "1.7976931348623157e308", + "-1.7976931348623157e308", + // next float64 - too large + "1.7976931348623159e308", + "-1.7976931348623159e308", + // the border is ...158079 + // borderline - okay + "1.7976931348623158e308", + "-1.7976931348623158e308", + // borderline - too large + "1.797693134862315808e308", + "-1.797693134862315808e308", + + // a little too large + "1e308", + "2e308", + "1e309", + + // way too large + "1e310", + "-1e310", + "1e400", + "-1e400", + "long:1e400000", + "long:-1e400000", + + // denormalized + "1e-305", + "1e-306", + "1e-307", + "1e-308", + "1e-309", + "1e-310", + "1e-322", + // smallest denormal + "5e-324", + "4e-324", + "3e-324", + // too small + "2e-324", + // way too small + "1e-350", + "long:1e-400000", + // way too small, negative + "-1e-350", + "long:-1e-400000", + + // try to overflow exponent + // [Disabled: too slow and memory-hungry with rationals.] + // "1e-4294967296", + // "1e+4294967296", + // "1e-18446744073709551616", + // "1e+18446744073709551616", + + // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + "2.2250738585072012e-308", + // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + + "2.2250738585072011e-308", + + // A very large number (initially wrongly parsed by the fast algorithm). + "4.630813248087435e+307", + + // A different kind of very large number. + "22.222222222222222", + "long:2." + strings.Repeat("2", 4000) + "e+1", + + // Exactly halfway between 1 and math.Nextafter(1, 2). + // Round to even (down). + "1.00000000000000011102230246251565404236316680908203125", + // Slightly lower; still round down. + "1.00000000000000011102230246251565404236316680908203124", + // Slightly higher; round up. + "1.00000000000000011102230246251565404236316680908203126", + // Slightly higher, but you have to read all the way to the end. + "long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1", + + // Smallest denormal, 2^(-1022-52) + "4.940656458412465441765687928682213723651e-324", + // Half of smallest denormal, 2^(-1022-53) + "2.470328229206232720882843964341106861825e-324", + // A little more than the exact half of smallest denormal + // 2^-1075 + 2^-1100. (Rounds to 1p-1074.) + "2.470328302827751011111470718709768633275e-324", + // The exact halfway between smallest normal and largest denormal: + // 2^-1022 - 2^-1075. (Rounds to 2^-1022.) + "2.225073858507201136057409796709131975935e-308", + + "1152921504606846975", // 1<<60 - 1 + "-1152921504606846975", // -(1<<60 - 1) + "1152921504606846977", // 1<<60 + 1 + "-1152921504606846977", // -(1<<60 + 1) + + "1/3", +} + +func TestFloat64SpecialCases(t *testing.T) { + for _, input := range float64inputs { + if strings.HasPrefix(input, "long:") { + if testing.Short() { + continue + } + input = input[len("long:"):] + } + + r, ok := new(Rat).SetString(input) + if !ok { + t.Errorf("Rat.SetString(%q) failed", input) + continue + } + f, exact := r.Float64() + + // 1. Check string -> Rat -> float64 conversions are + // consistent with strconv.ParseFloat. + // Skip this check if the input uses "a/b" rational syntax. + if !strings.Contains(input, "/") { + e, _ := strconv.ParseFloat(input, 64) + + // Careful: negative Rats too small for + // float64 become -0, but Rat obviously cannot + // preserve the sign from SetString("-0"). + switch { + case math.Float64bits(e) == math.Float64bits(f): + // Ok: bitwise equal. + case f == 0 && r.Num().BitLen() == 0: + // Ok: Rat(0) is equivalent to both +/- float64(0). + default: + t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta=%g", input, e, e, f, f, f-e) + } + } + + if !isFinite(f) { + continue + } + + // 2. Check f is best approximation to r. + if !checkIsBestApprox(t, f, r) { + // Append context information. + t.Errorf("(input was %q)", input) + } + + // 3. Check f->R->f roundtrip is non-lossy. + checkNonLossyRoundtrip(t, f) + + // 4. Check exactness using slow algorithm. + if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact { + t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact) + } + } +} + +func TestFloat64Distribution(t *testing.T) { + // Generate a distribution of (sign, mantissa, exp) values + // broader than the float64 range, and check Rat.Float64() + // always picks the closest float64 approximation. + var add = []int64{ + 0, + 1, + 3, + 5, + 7, + 9, + 11, + } + var winc, einc = uint64(1), int(1) // soak test (~75s on x86-64) + if testing.Short() { + winc, einc = 10, 500 // quick test (~12ms on x86-64) + } + + for _, sign := range "+-" { + for _, a := range add { + for wid := uint64(0); wid < 60; wid += winc { + b := int64(1< 0 { + num.Lsh(num, uint(exp)) + } else { + den.Lsh(den, uint(-exp)) + } + r := new(Rat).SetFrac(num, den) + f, _ := r.Float64() + + if !checkIsBestApprox(t, f, r) { + // Append context information. + t.Errorf("(input was mantissa %#x, exp %d; f=%g (%b); f~%g; r=%v)", + b, exp, f, f, math.Ldexp(float64(b), exp), r) + } + + checkNonLossyRoundtrip(t, f) + } + } + } + } +} + +// TestFloat64NonFinite checks that SetFloat64 of a non-finite value +// returns nil. +func TestSetFloat64NonFinite(t *testing.T) { + for _, f := range []float64{math.NaN(), math.Inf(+1), math.Inf(-1)} { + var r Rat + if r2 := r.SetFloat64(f); r2 != nil { + t.Errorf("SetFloat64(%g) was %v, want nil", f, r2) + } + } +} + +// checkNonLossyRoundtrip checks that a float->Rat->float roundtrip is +// non-lossy for finite f. +func checkNonLossyRoundtrip(t *testing.T, f float64) { + if !isFinite(f) { + return + } + r := new(Rat).SetFloat64(f) + if r == nil { + t.Errorf("Rat.SetFloat64(%g (%b)) == nil", f, f) + return + } + f2, exact := r.Float64() + if f != f2 || !exact { + t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta=%b", + f, f2, f2, exact, f, f, true, f2-f) + } +} + +// delta returns the absolute difference between r and f. +func delta(r *Rat, f float64) *Rat { + d := new(Rat).Sub(r, new(Rat).SetFloat64(f)) + return d.Abs(d) +} + +// checkIsBestApprox checks that f is the best possible float64 +// approximation of r. +// Returns true on success. +func checkIsBestApprox(t *testing.T, f float64, r *Rat) bool { + if math.Abs(f) >= math.MaxFloat64 { + // Cannot check +Inf, -Inf, nor the float next to them (MaxFloat64). + // But we have tests for these special cases. + return true + } + + // r must be strictly between f0 and f1, the floats bracketing f. + f0 := math.Nextafter(f, math.Inf(-1)) + f1 := math.Nextafter(f, math.Inf(+1)) + + // For f to be correct, r must be closer to f than to f0 or f1. + df := delta(r, f) + df0 := delta(r, f0) + df1 := delta(r, f1) + if df.Cmp(df0) > 0 { + t.Errorf("Rat(%v).Float64() = %g (%b), but previous float64 %g (%b) is closer", r, f, f, f0, f0) + return false + } + if df.Cmp(df1) > 0 { + t.Errorf("Rat(%v).Float64() = %g (%b), but next float64 %g (%b) is closer", r, f, f, f1, f1) + return false + } + if df.Cmp(df0) == 0 && !isEven(f) { + t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0) + return false + } + if df.Cmp(df1) == 0 && !isEven(f) { + t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1) + return false + } + return true +} + +func isEven(f float64) bool { return math.Float64bits(f)&1 == 0 } + +func TestIsFinite(t *testing.T) { + finites := []float64{ + 1.0 / 3, + 4891559871276714924261e+222, + math.MaxFloat64, + math.SmallestNonzeroFloat64, + -math.MaxFloat64, + -math.SmallestNonzeroFloat64, + } + for _, f := range finites { + if !isFinite(f) { + t.Errorf("!IsFinite(%g (%b))", f, f) + } + } + nonfinites := []float64{ + math.NaN(), + math.Inf(-1), + math.Inf(+1), + } + for _, f := range nonfinites { + if isFinite(f) { + t.Errorf("IsFinite(%g, (%b))", f, f) + } + } +} diff --git a/src/pkg/math/bits.go b/src/pkg/math/bits.go index 1cf60ce7d..0df0b1cc9 100644 --- a/src/pkg/math/bits.go +++ b/src/pkg/math/bits.go @@ -5,7 +5,7 @@ package math const ( - uvnan = 0x7FF0000000000001 + uvnan = 0x7FF8000000000001 uvinf = 0x7FF0000000000000 uvneginf = 0xFFF0000000000000 mask = 0x7FF diff --git a/src/pkg/math/cbrt.go b/src/pkg/math/cbrt.go index 8c43f0afb..272e30923 100644 --- a/src/pkg/math/cbrt.go +++ b/src/pkg/math/cbrt.go @@ -12,7 +12,7 @@ package math (http://www.jstor.org/stable/2006387?seq=9, accessed 11-Feb-2010) */ -// Cbrt returns the cube root of its argument. +// Cbrt returns the cube root of x. // // Special cases are: // Cbrt(±0) = ±0 diff --git a/src/pkg/math/copysign.go b/src/pkg/math/copysign.go index ee65456a1..719c64b9e 100644 --- a/src/pkg/math/copysign.go +++ b/src/pkg/math/copysign.go @@ -4,7 +4,7 @@ package math -// Copysign(x, y) returns a value with the magnitude +// Copysign returns a value with the magnitude // of x and the sign of y. func Copysign(x, y float64) float64 { const sign = 1 << 63 diff --git a/src/pkg/math/dim_amd64.s b/src/pkg/math/dim_amd64.s index c867db553..a1505ce44 100644 --- a/src/pkg/math/dim_amd64.s +++ b/src/pkg/math/dim_amd64.s @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. #define PosInf 0x7FF0000000000000 -#define NaN 0x7FF0000000000001 +#define NaN 0x7FF8000000000001 #define NegInf 0xFFF0000000000000 // func Dim(x, y float64) float64 diff --git a/src/pkg/math/erf.go b/src/pkg/math/erf.go index c6f32bdbe..4cd80f80c 100644 --- a/src/pkg/math/erf.go +++ b/src/pkg/math/erf.go @@ -179,7 +179,7 @@ const ( sb7 = -2.24409524465858183362e+01 // 0xC03670E242712D62 ) -// Erf(x) returns the error function of x. +// Erf returns the error function of x. // // Special cases are: // Erf(+Inf) = 1 @@ -256,7 +256,7 @@ func Erf(x float64) float64 { return 1 - r/x } -// Erfc(x) returns the complementary error function of x. +// Erfc returns the complementary error function of x. // // Special cases are: // Erfc(+Inf) = 0 diff --git a/src/pkg/math/floor_amd64.s b/src/pkg/math/floor_amd64.s index 9fc49a56f..e72cc3cf9 100644 --- a/src/pkg/math/floor_amd64.s +++ b/src/pkg/math/floor_amd64.s @@ -1,12 +1,74 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#define Big 0x4330000000000000 // 2**52 + +// func Floor(x float64) float64 TEXT ·Floor(SB),7,$0 - JMP ·floor(SB) + MOVQ x+0(FP), AX + MOVQ $~(1<<63), DX // sign bit mask + ANDQ AX,DX // DX = |x| + SUBQ $1,DX + MOVQ $(Big - 1), CX // if |x| >= 2**52-1 or IsNaN(x) or |x| == 0, return x + CMPQ DX,CX + JAE isBig_floor + MOVQ AX, X0 // X0 = x + CVTTSD2SQ X0, AX + CVTSQ2SD AX, X1 // X1 = float(int(x)) + CMPSD X1, X0, 1 // compare LT; X0 = 0xffffffffffffffff or 0 + MOVSD $(-1.0), X2 + ANDPD X2, X0 // if x < float(int(x)) {X0 = -1} else {X0 = 0} + ADDSD X1, X0 + MOVSD X0, r+8(FP) + RET +isBig_floor: + MOVQ AX, r+8(FP) // return x + RET +// func Ceil(x float64) float64 TEXT ·Ceil(SB),7,$0 - JMP ·ceil(SB) + MOVQ x+0(FP), AX + MOVQ $~(1<<63), DX // sign bit mask + MOVQ AX, BX // BX = copy of x + ANDQ DX, BX // BX = |x| + MOVQ $Big, CX // if |x| >= 2**52 or IsNaN(x), return x + CMPQ BX, CX + JAE isBig_ceil + MOVQ AX, X0 // X0 = x + MOVQ DX, X2 // X2 = sign bit mask + CVTTSD2SQ X0, AX + ANDNPD X0, X2 // X2 = sign + CVTSQ2SD AX, X1 // X1 = float(int(x)) + CMPSD X1, X0, 2 // compare LE; X0 = 0xffffffffffffffff or 0 + ORPD X2, X1 // if X1 = 0.0, incorporate sign + MOVSD $1.0, X3 + ANDNPD X3, X0 + ORPD X2, X0 // if float(int(x)) <= x {X0 = 1} else {X0 = -0} + ADDSD X1, X0 + MOVSD X0, r+8(FP) + RET +isBig_ceil: + MOVQ AX, r+8(FP) + RET +// func Trunc(x float64) float64 TEXT ·Trunc(SB),7,$0 - JMP ·trunc(SB) + MOVQ x+0(FP), AX + MOVQ $~(1<<63), DX // sign bit mask + MOVQ AX, BX // BX = copy of x + ANDQ DX, BX // BX = |x| + MOVQ $Big, CX // if |x| >= 2**52 or IsNaN(x), return x + CMPQ BX, CX + JAE isBig_trunc + MOVQ AX, X0 + MOVQ DX, X2 // X2 = sign bit mask + CVTTSD2SQ X0, AX + ANDNPD X0, X2 // X2 = sign + CVTSQ2SD AX, X0 // X0 = float(int(x)) + ORPD X2, X0 // if X0 = 0.0, incorporate sign + MOVSD X0, r+8(FP) + RET +isBig_trunc: + MOVQ AX, r+8(FP) // return x + RET diff --git a/src/pkg/math/frexp_386.s b/src/pkg/math/frexp_386.s index 177c4b97b..95e50de02 100644 --- a/src/pkg/math/frexp_386.s +++ b/src/pkg/math/frexp_386.s @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// func Frexp(x float64) (f float64, e int) +// func Frexp(f float64) (frac float64, exp int) TEXT ·Frexp(SB),7,$0 - FMOVD x+0(FP), F0 // F0=x + FMOVD f+0(FP), F0 // F0=f FXAM FSTSW AX SAHF @@ -12,12 +12,12 @@ TEXT ·Frexp(SB),7,$0 JCS nan_zero_inf FXTRACT // F0=f (0<=f<1), F1=e FMULD $(0.5), F0 // F0=f (0.5<=f<1), F1=e - FMOVDP F0, f+8(FP) // F0=e + FMOVDP F0, frac+8(FP) // F0=e FLD1 // F0=1, F1=e FADDDP F0, F1 // F0=e+1 - FMOVLP F0, e+16(FP) // (int=int32) + FMOVLP F0, exp+16(FP) // (int=int32) RET nan_zero_inf: - FMOVDP F0, f+8(FP) // F0=e - MOVL $0, e+16(FP) // e=0 + FMOVDP F0, frac+8(FP) // F0=e + MOVL $0, exp+16(FP) // exp=0 RET diff --git a/src/pkg/math/gamma.go b/src/pkg/math/gamma.go index 7c6f421ba..164f54f33 100644 --- a/src/pkg/math/gamma.go +++ b/src/pkg/math/gamma.go @@ -110,19 +110,26 @@ func stirling(x float64) float64 { return y } -// Gamma(x) returns the Gamma function of x. +// Gamma returns the Gamma function of x. // // Special cases are: -// Gamma(±Inf) = ±Inf +// Gamma(+Inf) = +Inf +// Gamma(+0) = +Inf +// Gamma(-0) = -Inf +// Gamma(x) = NaN for integer x < 0 +// Gamma(-Inf) = NaN // Gamma(NaN) = NaN -// Large values overflow to +Inf. -// Zero and negative integer arguments return ±Inf. func Gamma(x float64) float64 { const Euler = 0.57721566490153286060651209008240243104215933593992 // A001620 // special cases switch { - case IsInf(x, -1) || IsNaN(x): - return x + case isNegInt(x) || IsInf(x, -1) || IsNaN(x): + return NaN() + case x == 0: + if Signbit(x) { + return Inf(-1) + } + return Inf(1) case x < -170.5674972726612 || x > 171.61447887182298: return Inf(1) } @@ -185,3 +192,11 @@ small: } return z / ((1 + Euler*x) * x) } + +func isNegInt(x float64) bool { + if x < 0 { + _, xf := Modf(x) + return xf == 0 + } + return false +} diff --git a/src/pkg/math/hypot.go b/src/pkg/math/hypot.go index df4d3eb70..3846e6d87 100644 --- a/src/pkg/math/hypot.go +++ b/src/pkg/math/hypot.go @@ -8,7 +8,7 @@ package math Hypot -- sqrt(p*p + q*q), but overflows only if the result does. */ -// Hypot computes Sqrt(p*p + q*q), taking care to avoid +// Hypot returns Sqrt(p*p + q*q), taking care to avoid // unnecessary overflow and underflow. // // Special cases are: diff --git a/src/pkg/math/hypot_386.s b/src/pkg/math/hypot_386.s index 70ff19a17..51cd90419 100644 --- a/src/pkg/math/hypot_386.s +++ b/src/pkg/math/hypot_386.s @@ -2,35 +2,35 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// func Hypot(x, y float64) float64 +// func Hypot(p, q float64) float64 TEXT ·Hypot(SB),7,$0 // test bits for not-finite - MOVL xh+4(FP), AX // high word x + MOVL p+4(FP), AX // high word p ANDL $0x7ff00000, AX CMPL AX, $0x7ff00000 JEQ not_finite - MOVL yh+12(FP), AX // high word y + MOVL q+12(FP), AX // high word q ANDL $0x7ff00000, AX CMPL AX, $0x7ff00000 JEQ not_finite - FMOVD x+0(FP), F0 // F0=x - FABS // F0=|x| - FMOVD y+8(FP), F0 // F0=y, F1=|x| - FABS // F0=|y|, F1=|x| + FMOVD p+0(FP), F0 // F0=p + FABS // F0=|p| + FMOVD q+8(FP), F0 // F0=q, F1=|p| + FABS // F0=|q|, F1=|p| FUCOMI F0, F1 // compare F0 to F1 JCC 2(PC) // jump if F0 >= F1 - FXCHD F0, F1 // F0=|x| (larger), F1=|y| (smaller) + FXCHD F0, F1 // F0=|p| (larger), F1=|q| (smaller) FTST // compare F0 to 0 FSTSW AX ANDW $0x4000, AX JNE 10(PC) // jump if F0 = 0 - FXCHD F0, F1 // F0=y (smaller), F1=x (larger) - FDIVD F1, F0 // F0=y(=y/x), F1=x - FMULD F0, F0 // F0=y*y, F1=x - FLD1 // F0=1, F1=y*y, F2=x - FADDDP F0, F1 // F0=1+y*y, F1=x - FSQRT // F0=sqrt(1+y*y), F1=x - FMULDP F0, F1 // F0=x*sqrt(1+y*y) + FXCHD F0, F1 // F0=q (smaller), F1=p (larger) + FDIVD F1, F0 // F0=q(=q/p), F1=p + FMULD F0, F0 // F0=q*q, F1=p + FLD1 // F0=1, F1=q*q, F2=p + FADDDP F0, F1 // F0=1+q*q, F1=p + FSQRT // F0=sqrt(1+q*q), F1=p + FMULDP F0, F1 // F0=p*sqrt(1+q*q) FMOVDP F0, r+16(FP) RET FMOVDP F0, F1 // F0=0 @@ -38,20 +38,20 @@ TEXT ·Hypot(SB),7,$0 RET not_finite: // test bits for -Inf or +Inf - MOVL xh+4(FP), AX // high word x - ORL xl+0(FP), AX // low word x + MOVL p+4(FP), AX // high word p + ORL p+0(FP), AX // low word p ANDL $0x7fffffff, AX CMPL AX, $0x7ff00000 JEQ is_inf - MOVL yh+12(FP), AX // high word y - ORL yl+8(FP), AX // low word y + MOVL q+12(FP), AX // high word q + ORL q+8(FP), AX // low word q ANDL $0x7fffffff, AX CMPL AX, $0x7ff00000 JEQ is_inf - MOVL $0x7ff00000, rh+20(FP) // return NaN = 0x7FF0000000000001 - MOVL $0x00000001, rl+16(FP) + MOVL $0x7ff80000, r+20(FP) // return NaN = 0x7FF8000000000001 + MOVL $0x00000001, r+16(FP) RET is_inf: - MOVL AX, rh+20(FP) // return +Inf = 0x7FF0000000000000 - MOVL $0x00000000, rl+16(FP) + MOVL AX, r+20(FP) // return +Inf = 0x7FF0000000000000 + MOVL $0x00000000, r+16(FP) RET diff --git a/src/pkg/math/hypot_amd64.s b/src/pkg/math/hypot_amd64.s index 1f691e70e..02fff5b92 100644 --- a/src/pkg/math/hypot_amd64.s +++ b/src/pkg/math/hypot_amd64.s @@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#define PosInf 0x7ff0000000000000 -#define NaN 0x7FF0000000000001 +#define PosInf 0x7FF0000000000000 +#define NaN 0x7FF8000000000001 -// func Hypot(x, y float64) float64 +// func Hypot(p, q float64) float64 TEXT ·Hypot(SB),7,$0 // test bits for special cases - MOVQ x+0(FP), BX + MOVQ p+0(FP), BX MOVQ $~(1<<63), AX - ANDQ AX, BX // x = |x| - MOVQ y+8(FP), CX - ANDQ AX, CX // y = |y| + ANDQ AX, BX // p = |p| + MOVQ q+8(FP), CX + ANDQ AX, CX // q = |q| MOVQ $PosInf, AX CMPQ AX, BX JLE isInfOrNaN diff --git a/src/pkg/math/ldexp_386.s b/src/pkg/math/ldexp_386.s index ed91ffcd3..3a65629d2 100644 --- a/src/pkg/math/ldexp_386.s +++ b/src/pkg/math/ldexp_386.s @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// func Ldexp(f float64, e int) float64 +// func Ldexp(frac float64, exp int) float64 TEXT ·Ldexp(SB),7,$0 - FMOVL e+8(FP), F0 // F0=e - FMOVD x+0(FP), F0 // F0=x, F1=e + FMOVL exp+8(FP), F0 // F0=exp + FMOVD frac+0(FP), F0 // F0=frac, F1=e FSCALE // F0=x*2**e, F1=e FMOVDP F0, F1 // F0=x*2**e FMOVDP F0, r+12(FP) diff --git a/src/pkg/math/log10.go b/src/pkg/math/log10.go index 67c163a49..95cfbf47c 100644 --- a/src/pkg/math/log10.go +++ b/src/pkg/math/log10.go @@ -17,5 +17,6 @@ func log10(x float64) float64 { func Log2(x float64) float64 func log2(x float64) float64 { - return Log(x) * (1 / Ln2) + frac, exp := Frexp(x) + return Log(frac)*(1/Ln2) + float64(exp) } diff --git a/src/pkg/math/log_amd64.s b/src/pkg/math/log_amd64.s index 79e35907c..75bc55764 100644 --- a/src/pkg/math/log_amd64.s +++ b/src/pkg/math/log_amd64.s @@ -12,7 +12,7 @@ #define L5 1.818357216161805012e-01 // 0x3FC7466496CB03DE #define L6 1.531383769920937332e-01 // 0x3FC39A09D078C69F #define L7 1.479819860511658591e-01 // 0x3FC2F112DF3E5244 -#define NaN 0x7FF0000000000001 +#define NaN 0x7FF8000000000001 #define NegInf 0xFFF0000000000000 #define PosInf 0x7FF0000000000000 @@ -54,13 +54,13 @@ TEXT ·Log(SB),7,$0 // s := f / (2 + f) MOVSD $2.0, X0 ADDSD X2, X0 - MOVSD X2, X3 + MOVAPD X2, X3 DIVSD X0, X3 // x1=k, x2= f, x3= s // s2 := s * s - MOVSD X3, X4 // x1= k, x2= f, x3= s + MOVAPD X3, X4 // x1= k, x2= f, x3= s MULSD X4, X4 // x1= k, x2= f, x3= s, x4= s2 // s4 := s2 * s2 - MOVSD X4, X5 // x1= k, x2= f, x3= s, x4= s2 + MOVAPD X4, X5 // x1= k, x2= f, x3= s, x4= s2 MULSD X5, X5 // x1= k, x2= f, x3= s, x4= s2, x5= s4 // t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7))) MOVSD $L7, X6 diff --git a/src/pkg/math/logb.go b/src/pkg/math/logb.go index d32f9f100..f2769d4fd 100644 --- a/src/pkg/math/logb.go +++ b/src/pkg/math/logb.go @@ -4,7 +4,7 @@ package math -// Logb(x) returns the binary exponent of x. +// Logb returns the binary exponent of x. // // Special cases are: // Logb(±Inf) = +Inf @@ -23,7 +23,7 @@ func Logb(x float64) float64 { return float64(ilogb(x)) } -// Ilogb(x) returns the binary exponent of x as an integer. +// Ilogb returns the binary exponent of x as an integer. // // Special cases are: // Ilogb(±Inf) = MaxInt32 diff --git a/src/pkg/math/modf_386.s b/src/pkg/math/modf_386.s index 5ccab9812..f5dc415c3 100644 --- a/src/pkg/math/modf_386.s +++ b/src/pkg/math/modf_386.s @@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// func Modf(x float64) (int float64, frac float64) +// func Modf(f float64) (int float64, frac float64) TEXT ·Modf(SB),7,$0 - FMOVD x+0(FP), F0 // F0=x - FMOVD F0, F1 // F0=x, F1=x + FMOVD f+0(FP), F0 // F0=f + FMOVD F0, F1 // F0=f, F1=f FSTCW -2(SP) // save old Control Word MOVW -2(SP), AX ORW $0x0c00, AX // Rounding Control set to truncate MOVW AX, -4(SP) // store new Control Word FLDCW -4(SP) // load new Control Word - FRNDINT // F0=trunc(x), F1=x + FRNDINT // F0=trunc(f), F1=f FLDCW -2(SP) // load old Control Word - FSUBD F0, F1 // F0=trunc(x), F1=x-trunc(x) - FMOVDP F0, i+8(FP) // F0=x-trunc(x) - FMOVDP F0, f+16(FP) + FSUBD F0, F1 // F0=trunc(f), F1=f-trunc(f) + FMOVDP F0, int+8(FP) // F0=f-trunc(f) + FMOVDP F0, frac+16(FP) RET diff --git a/src/pkg/math/rand/example_test.go b/src/pkg/math/rand/example_test.go new file mode 100644 index 000000000..4fe207d85 --- /dev/null +++ b/src/pkg/math/rand/example_test.go @@ -0,0 +1,69 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand_test + +import ( + "fmt" + "math/rand" + "os" + "text/tabwriter" +) + +// This test serves as an example but also makes sure we don't change +// the output of the random number generator when given a fixed seed. + +// This example shows the use of each of the methods on a *Rand. +// The use of the global functions is the same, without the receiver. +func Example() { + // Create and seed the generator. + // Typically a non-fixed seed should be used, such as time.Now().UnixNano(). + // Using a fixed seed will produce the same output on every run. + r := rand.New(rand.NewSource(99)) + + // The tabwriter here helps us generate aligned output. + w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) + defer w.Flush() + show := func(name string, v1, v2, v3 interface{}) { + fmt.Fprintf(w, "%s\t%v\t%v\t%v\n", name, v1, v2, v3) + } + + // Float32 and Float64 values are in [0, 1). + show("Float32", r.Float32(), r.Float32(), r.Float32()) + show("Float64", r.Float64(), r.Float64(), r.Float64()) + + // ExpFloat64 values have an average of 1 but decay exponentially. + show("ExpFloat64", r.ExpFloat64(), r.ExpFloat64(), r.ExpFloat64()) + + // NormFloat64 values have an average of 0 and a standard deviation of 1. + show("NormFloat64", r.NormFloat64(), r.NormFloat64(), r.NormFloat64()) + + // Int31, Int63, and Uint32 generate values of the given width. + // The Int method (not shown) is like either Int31 or Int63 + // depending on the size of 'int'. + show("Int31", r.Int31(), r.Int31(), r.Int31()) + show("Int63", r.Int63(), r.Int63(), r.Int63()) + show("Uint32", r.Int63(), r.Int63(), r.Int63()) + + // Intn, Int31n, and Int63n limit their output to be < n. + // They do so more carefully than using r.Int()%n. + show("Intn(10)", r.Intn(10), r.Intn(10), r.Intn(10)) + show("Int31n(10)", r.Int31n(10), r.Int31n(10), r.Int31n(10)) + show("Int63n(10)", r.Int63n(10), r.Int63n(10), r.Int63n(10)) + + // Perm generates a random permutation of the numbers [0, n). + show("Perm", r.Perm(5), r.Perm(5), r.Perm(5)) + // Output: + // Float32 0.2635776 0.6358173 0.6718283 + // Float64 0.628605430454327 0.4504798828572669 0.9562755949377957 + // ExpFloat64 0.3362240648200941 1.4256072328483647 0.24354758816173044 + // NormFloat64 0.17233959114940064 1.577014951434847 0.04259129641113857 + // Int31 1501292890 1486668269 182840835 + // Int63 3546343826724305832 5724354148158589552 5239846799706671610 + // Uint32 5927547564735367388 637072299495207830 4128311955958246186 + // Intn(10) 1 2 5 + // Int31n(10) 4 7 8 + // Int63n(10) 7 6 3 + // Perm [1 4 2 3 0] [4 2 1 3 0] [1 2 4 0 3] +} diff --git a/src/pkg/math/rand/rand_test.go b/src/pkg/math/rand/rand_test.go index bbd44e3f8..4d3abdb60 100644 --- a/src/pkg/math/rand/rand_test.go +++ b/src/pkg/math/rand/rand_test.go @@ -57,16 +57,13 @@ func (this *statsResults) checkSimilarDistribution(expected *statsResults) error func getStatsResults(samples []float64) *statsResults { res := new(statsResults) - var sum float64 - for i := range samples { - sum += samples[i] + var sum, squaresum float64 + for _, s := range samples { + sum += s + squaresum += s * s } res.mean = sum / float64(len(samples)) - var devsum float64 - for i := range samples { - devsum += math.Pow(samples[i]-res.mean, 2) - } - res.stddev = math.Sqrt(devsum / float64(len(samples))) + res.stddev = math.Sqrt(squaresum/float64(len(samples)) - res.mean*res.mean) return res } diff --git a/src/pkg/math/remainder.go b/src/pkg/math/remainder.go index 41efd7908..9a4e4154c 100644 --- a/src/pkg/math/remainder.go +++ b/src/pkg/math/remainder.go @@ -4,7 +4,7 @@ package math -// The original C code and the the comment below are from +// The original C code and the comment below are from // FreeBSD's /usr/src/lib/msun/src/e_remainder.c and came // with this notice. The go code is a simplified version of // the original C. diff --git a/src/pkg/math/sincos.go b/src/pkg/math/sincos.go index 730042920..718030319 100644 --- a/src/pkg/math/sincos.go +++ b/src/pkg/math/sincos.go @@ -6,7 +6,7 @@ package math // Coefficients _sin[] and _cos[] are found in pkg/math/sin.go. -// Sincos(x) returns Sin(x), Cos(x). +// Sincos returns Sin(x), Cos(x). // // Special cases are: // Sincos(±0) = ±0, 1 diff --git a/src/pkg/math/sincos_386.s b/src/pkg/math/sincos_386.s index 9dd37a3b7..8f5e0f8d1 100644 --- a/src/pkg/math/sincos_386.s +++ b/src/pkg/math/sincos_386.s @@ -9,8 +9,8 @@ TEXT ·Sincos(SB),7,$0 FSTSW AX // AX=status word ANDW $0x0400, AX JNE 4(PC) // jump if x outside range - FMOVDP F0, c+16(FP) // F0=sin(x) - FMOVDP F0, s+8(FP) + FMOVDP F0, cos+16(FP) // F0=sin(x) + FMOVDP F0, sin+8(FP) RET FLDPI // F0=Pi, F1=x FADDD F0, F0 // F0=2*Pi, F1=x @@ -21,6 +21,6 @@ TEXT ·Sincos(SB),7,$0 JNE -3(PC) // jump if reduction incomplete FMOVDP F0, F1 // F0=reduced_x FSINCOS // F0=cos(reduced_x), F1=sin(reduced_x) - FMOVDP F0, c+16(FP) // F0=sin(reduced_x) - FMOVDP F0, s+8(FP) + FMOVDP F0, cos+16(FP) // F0=sin(reduced_x) + FMOVDP F0, sin+8(FP) RET diff --git a/src/pkg/math/sincos_amd64.s b/src/pkg/math/sincos_amd64.s index 18c824e51..c9dea0916 100644 --- a/src/pkg/math/sincos_amd64.s +++ b/src/pkg/math/sincos_amd64.s @@ -19,7 +19,7 @@ #define PosOne 0x3FF0000000000000 #define PosInf 0x7FF0000000000000 -#define NaN 0x7FF0000000000001 +#define NaN 0x7FF8000000000001 #define PI4A 0.7853981554508209228515625 // pi/4 split into three parts #define PI4B 0.794662735614792836713604629039764404296875e-8 #define PI4C 0.306161699786838294306516483068750264552437361480769e-16 diff --git a/src/pkg/math/tanh.go b/src/pkg/math/tanh.go index 03a641b4d..7305be66c 100644 --- a/src/pkg/math/tanh.go +++ b/src/pkg/math/tanh.go @@ -4,12 +4,66 @@ package math -/* - Floating-point hyperbolic tangent. +// The original C code, the long comment, and the constants +// below were from http://netlib.sandia.gov/cephes/cmath/sin.c, +// available from http://www.netlib.org/cephes/cmath.tgz. +// The go code is a simplified version of the original C. +// tanh.c +// +// Hyperbolic tangent +// +// SYNOPSIS: +// +// double x, y, tanh(); +// +// y = tanh( x ); +// +// DESCRIPTION: +// +// Returns hyperbolic tangent of argument in the range MINLOG to MAXLOG. +// MAXLOG = 8.8029691931113054295988e+01 = log(2**127) +// MINLOG = -8.872283911167299960540e+01 = log(2**-128) +// +// A rational function is used for |x| < 0.625. The form +// x + x**3 P(x)/Q(x) of Cody & Waite is employed. +// Otherwise, +// tanh(x) = sinh(x)/cosh(x) = 1 - 2/(exp(2x) + 1). +// +// ACCURACY: +// +// Relative error: +// arithmetic domain # trials peak rms +// IEEE -2,2 30000 2.5e-16 5.8e-17 +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov +// - Sinh and Cosh are called except for large arguments, which - would cause overflow improperly. -*/ +var tanhP = [...]float64{ + -9.64399179425052238628E-1, + -9.92877231001918586564E1, + -1.61468768441708447952E3, +} +var tanhQ = [...]float64{ + 1.12811678491632931402E2, + 2.23548839060100448583E3, + 4.84406305325125486048E3, +} // Tanh computes the hyperbolic tangent of x. // @@ -18,15 +72,26 @@ package math // Tanh(±Inf) = ±1 // Tanh(NaN) = NaN func Tanh(x float64) float64 { - if x < 0 { - x = -x - if x > 21 { + const MAXLOG = 8.8029691931113054295988e+01 // log(2**127) + z := Abs(x) + switch { + case z > 0.5*MAXLOG: + if x < 0 { return -1 } - return -Sinh(x) / Cosh(x) - } - if x > 21 { return 1 + case z >= 0.625: + s := Exp(2 * z) + z = 1 - 2/(s+1) + if x < 0 { + z = -z + } + default: + if x == 0 { + return x + } + s := x * x + z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2]) } - return Sinh(x) / Cosh(x) + return z } diff --git a/src/pkg/mime/grammar.go b/src/pkg/mime/grammar.go index 83cc41134..09e941e3e 100644 --- a/src/pkg/mime/grammar.go +++ b/src/pkg/mime/grammar.go @@ -22,7 +22,7 @@ func isTokenChar(r rune) bool { return r > 0x20 && r < 0x7f && !isTSpecial(r) } -// isToken returns true if s is a 'token' as as defined by RFC 1521 +// isToken returns true if s is a 'token' as defined by RFC 1521 // and RFC 2045. func isToken(s string) bool { if s == "" { diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go index e9e337b92..77e969b41 100644 --- a/src/pkg/mime/multipart/multipart.go +++ b/src/pkg/mime/multipart/multipart.go @@ -37,6 +37,11 @@ type Part struct { disposition string dispositionParams map[string]string + + // r is either a reader directly reading from mr, or it's a + // wrapper around such a reader, decoding the + // Content-Transfer-Encoding + r io.Reader } // FormName returns the name parameter if p has a Content-Disposition @@ -71,7 +76,7 @@ func (p *Part) parseContentDisposition() { } } -// NewReader creates a new multipart Reader reading from r using the +// NewReader creates a new multipart Reader reading from reader using the // given MIME boundary. func NewReader(reader io.Reader, boundary string) *Reader { b := []byte("\r\n--" + boundary + "--") @@ -94,6 +99,12 @@ func newPart(mr *Reader) (*Part, error) { if err := bp.populateHeaders(); err != nil { return nil, err } + bp.r = partReader{bp} + const cte = "Content-Transfer-Encoding" + if bp.Header.Get(cte) == "quoted-printable" { + bp.Header.Del(cte) + bp.r = newQuotedPrintableReader(bp.r) + } return bp, nil } @@ -109,6 +120,17 @@ func (bp *Part) populateHeaders() error { // Read reads the body of a part, after its headers and before the // next part (if any) begins. func (p *Part) Read(d []byte) (n int, err error) { + return p.r.Read(d) +} + +// partReader implements io.Reader by reading raw bytes directly from the +// wrapped *Part, without doing any Transfer-Encoding decoding. +type partReader struct { + p *Part +} + +func (pr partReader) Read(d []byte) (n int, err error) { + p := pr.p defer func() { p.bytesRead += n }() diff --git a/src/pkg/mime/multipart/multipart_test.go b/src/pkg/mime/multipart/multipart_test.go index cd65e177e..d662e8340 100644 --- a/src/pkg/mime/multipart/multipart_test.go +++ b/src/pkg/mime/multipart/multipart_test.go @@ -339,9 +339,10 @@ func TestLineContinuation(t *testing.T) { if err != nil { t.Fatalf("didn't get a part") } - n, err := io.Copy(ioutil.Discard, part) + var buf bytes.Buffer + n, err := io.Copy(&buf, part) if err != nil { - t.Errorf("error reading part: %v", err) + t.Errorf("error reading part: %v\nread so far: %q", err, buf.String()) } if n <= 0 { t.Errorf("read %d bytes; expected >0", n) @@ -349,6 +350,29 @@ func TestLineContinuation(t *testing.T) { } } +func TestQuotedPrintableEncoding(t *testing.T) { + // From http://golang.org/issue/4411 + body := "--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=text\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words\r\n--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--0016e68ee29c5d515f04cedf6733--" + r := NewReader(strings.NewReader(body), "0016e68ee29c5d515f04cedf6733") + part, err := r.NextPart() + if err != nil { + t.Fatal(err) + } + if te, ok := part.Header["Content-Transfer-Encoding"]; ok { + t.Errorf("unexpected Content-Transfer-Encoding of %q", te) + } + var buf bytes.Buffer + _, err = io.Copy(&buf, part) + if err != nil { + t.Error(err) + } + got := buf.String() + want := "words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words" + if got != want { + t.Errorf("wrong part value:\n got: %q\nwant: %q", got, want) + } +} + // Test parsing an image attachment from gmail, which previously failed. func TestNested(t *testing.T) { // nested-mime is the body part of a multipart/mixed email diff --git a/src/pkg/mime/multipart/quotedprintable.go b/src/pkg/mime/multipart/quotedprintable.go new file mode 100644 index 000000000..9e18e1ea1 --- /dev/null +++ b/src/pkg/mime/multipart/quotedprintable.go @@ -0,0 +1,130 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The file define a quoted-printable decoder, as specified in RFC 2045. +// Deviations: +// 1. in addition to "=\r\n", "=\n" is also treated as soft line break. +// 2. it will pass through a '\r' or '\n' not preceded by '=', consistent +// with other broken QP encoders & decoders. + +package multipart + +import ( + "bufio" + "bytes" + "fmt" + "io" +) + +type qpReader struct { + br *bufio.Reader + skipWhite bool + rerr error // last read error + line []byte // to be consumed before more of br +} + +func newQuotedPrintableReader(r io.Reader) io.Reader { + return &qpReader{ + br: bufio.NewReader(r), + skipWhite: true, + } +} + +func fromHex(b byte) (byte, error) { + switch { + case b >= '0' && b <= '9': + return b - '0', nil + case b >= 'A' && b <= 'F': + return b - 'A' + 10, nil + } + return 0, fmt.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b) +} + +func (q *qpReader) readHexByte(v []byte) (b byte, err error) { + if len(v) < 2 { + return 0, io.ErrUnexpectedEOF + } + var hb, lb byte + if hb, err = fromHex(v[0]); err != nil { + return 0, err + } + if lb, err = fromHex(v[1]); err != nil { + return 0, err + } + return hb<<4 | lb, nil +} + +func isQPSkipWhiteByte(b byte) bool { + return b == ' ' || b == '\t' +} + +func isQPDiscardWhitespace(r rune) bool { + switch r { + case '\n', '\r', ' ', '\t': + return true + } + return false +} + +var ( + crlf = []byte("\r\n") + lf = []byte("\n") + softSuffix = []byte("=") +) + +func (q *qpReader) Read(p []byte) (n int, err error) { + for len(p) > 0 { + if len(q.line) == 0 { + if q.rerr != nil { + return n, q.rerr + } + q.skipWhite = true + q.line, q.rerr = q.br.ReadSlice('\n') + + // Does the line end in CRLF instead of just LF? + hasLF := bytes.HasSuffix(q.line, lf) + hasCR := bytes.HasSuffix(q.line, crlf) + wholeLine := q.line + q.line = bytes.TrimRightFunc(wholeLine, isQPDiscardWhitespace) + if bytes.HasSuffix(q.line, softSuffix) { + rightStripped := wholeLine[len(q.line):] + q.line = q.line[:len(q.line)-1] + if !bytes.HasPrefix(rightStripped, lf) && !bytes.HasPrefix(rightStripped, crlf) { + q.rerr = fmt.Errorf("multipart: invalid bytes after =: %q", rightStripped) + } + } else if hasLF { + if hasCR { + q.line = append(q.line, '\r', '\n') + } else { + q.line = append(q.line, '\n') + } + } + continue + } + b := q.line[0] + if q.skipWhite && isQPSkipWhiteByte(b) { + q.line = q.line[1:] + continue + } + q.skipWhite = false + + switch { + case b == '=': + b, err = q.readHexByte(q.line[1:]) + if err != nil { + return n, err + } + q.line = q.line[2:] // 2 of the 3; other 1 is done below + case b == '\t' || b == '\r' || b == '\n': + break + case b < ' ' || b > '~': + return n, fmt.Errorf("multipart: invalid unescaped byte 0x%02x in quoted-printable body", b) + } + p[0] = b + p = p[1:] + q.line = q.line[1:] + n++ + } + return n, nil +} diff --git a/src/pkg/mime/multipart/quotedprintable_test.go b/src/pkg/mime/multipart/quotedprintable_test.go new file mode 100644 index 000000000..7bcf5767b --- /dev/null +++ b/src/pkg/mime/multipart/quotedprintable_test.go @@ -0,0 +1,199 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package multipart + +import ( + "bufio" + "bytes" + "errors" + "flag" + "fmt" + "io" + "os/exec" + "regexp" + "sort" + "strings" + "testing" + "time" +) + +func TestQuotedPrintable(t *testing.T) { + tests := []struct { + in, want string + err interface{} + }{ + {in: "", want: ""}, + {in: "foo bar", want: "foo bar"}, + {in: "foo bar=3D", want: "foo bar="}, + {in: "foo bar=\n", want: "foo bar"}, + {in: "foo bar\n", want: "foo bar\n"}, // somewhat lax. + {in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF}, + {in: "foo bar=ab", want: "foo bar", err: "multipart: invalid quoted-printable hex byte 0x61"}, + {in: "foo bar=0D=0A", want: "foo bar\r\n"}, + {in: " A B =\r\n C ", want: "A B C"}, + {in: " A B =\n C ", want: "A B C"}, // lax. treating LF as CRLF + {in: "foo=\nbar", want: "foobar"}, + {in: "foo\x00bar", want: "foo", err: "multipart: invalid unescaped byte 0x00 in quoted-printable body"}, + {in: "foo bar\xff", want: "foo bar", err: "multipart: invalid unescaped byte 0xff in quoted-printable body"}, + + // Equal sign. + {in: "=3D30\n", want: "=30\n"}, + {in: "=00=FF0=\n", want: "\x00\xff0"}, + + // Trailing whitespace + {in: "foo \n", want: "foo\n"}, + {in: "foo \n\nfoo =\n\nfoo=20\n\n", want: "foo\n\nfoo \nfoo \n\n"}, + + // Tests that we allow bare \n and \r through, despite it being strictly + // not permitted per RFC 2045, Section 6.7 Page 22 bullet (4). + {in: "foo\nbar", want: "foo\nbar"}, + {in: "foo\rbar", want: "foo\rbar"}, + {in: "foo\r\nbar", want: "foo\r\nbar"}, + + // Different types of soft line-breaks. + {in: "foo=\r\nbar", want: "foobar"}, + {in: "foo=\nbar", want: "foobar"}, + {in: "foo=\rbar", want: "foo", err: "multipart: invalid quoted-printable hex byte 0x0d"}, + {in: "foo=\r\r\r \nbar", want: "foo", err: `multipart: invalid bytes after =: "\r\r\r \n"`}, + } + for _, tt := range tests { + var buf bytes.Buffer + _, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(tt.in))) + if got := buf.String(); got != tt.want { + t.Errorf("for %q, got %q; want %q", tt.in, got, tt.want) + } + switch verr := tt.err.(type) { + case nil: + if err != nil { + t.Errorf("for %q, got unexpected error: %v", tt.in, err) + } + case string: + if got := fmt.Sprint(err); got != verr { + t.Errorf("for %q, got error %q; want %q", tt.in, got, verr) + } + case error: + if err != verr { + t.Errorf("for %q, got error %q; want %q", tt.in, err, verr) + } + } + } + +} + +func everySequence(base, alpha string, length int, fn func(string)) { + if len(base) == length { + fn(base) + return + } + for i := 0; i < len(alpha); i++ { + everySequence(base+alpha[i:i+1], alpha, length, fn) + } +} + +var useQprint = flag.Bool("qprint", false, "Compare against the 'qprint' program.") + +var badSoftRx = regexp.MustCompile(`=([^\r\n]+?\n)|([^\r\n]+$)|(\r$)|(\r[^\n]+\n)|( \r\n)`) + +func TestQPExhaustive(t *testing.T) { + if *useQprint { + _, err := exec.LookPath("qprint") + if err != nil { + t.Fatalf("Error looking for qprint: %v", err) + } + } + + var buf bytes.Buffer + res := make(map[string]int) + everySequence("", "0A \r\n=", 6, func(s string) { + if strings.HasSuffix(s, "=") || strings.Contains(s, "==") { + return + } + buf.Reset() + _, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(s))) + if err != nil { + errStr := err.Error() + if strings.Contains(errStr, "invalid bytes after =:") { + errStr = "invalid bytes after =" + } + res[errStr]++ + if strings.Contains(errStr, "invalid quoted-printable hex byte ") { + if strings.HasSuffix(errStr, "0x20") && (strings.Contains(s, "=0 ") || strings.Contains(s, "=A ") || strings.Contains(s, "= ")) { + return + } + if strings.HasSuffix(errStr, "0x3d") && (strings.Contains(s, "=0=") || strings.Contains(s, "=A=")) { + return + } + if strings.HasSuffix(errStr, "0x0a") || strings.HasSuffix(errStr, "0x0d") { + // bunch of cases; since whitespace at the end of of a line before \n is removed. + return + } + } + if strings.Contains(errStr, "unexpected EOF") { + return + } + if errStr == "invalid bytes after =" && badSoftRx.MatchString(s) { + return + } + t.Errorf("decode(%q) = %v", s, err) + return + } + if *useQprint { + cmd := exec.Command("qprint", "-d") + cmd.Stdin = strings.NewReader(s) + stderr, err := cmd.StderrPipe() + if err != nil { + panic(err) + } + qpres := make(chan interface{}, 2) + go func() { + br := bufio.NewReader(stderr) + s, _ := br.ReadString('\n') + if s != "" { + qpres <- errors.New(s) + if cmd.Process != nil { + // It can get stuck on invalid input, like: + // echo -n "0000= " | qprint -d + cmd.Process.Kill() + } + } + }() + go func() { + want, err := cmd.Output() + if err == nil { + qpres <- want + } + }() + select { + case got := <-qpres: + if want, ok := got.([]byte); ok { + if string(want) != buf.String() { + t.Errorf("go decode(%q) = %q; qprint = %q", s, want, buf.String()) + } + } else { + t.Logf("qprint -d(%q) = %v", s, got) + } + case <-time.After(5 * time.Second): + t.Logf("qprint timeout on %q", s) + } + } + res["OK"]++ + }) + var outcomes []string + for k, v := range res { + outcomes = append(outcomes, fmt.Sprintf("%v: %d", k, v)) + } + sort.Strings(outcomes) + got := strings.Join(outcomes, "\n") + want := `OK: 21576 +invalid bytes after =: 3397 +multipart: invalid quoted-printable hex byte 0x0a: 1400 +multipart: invalid quoted-printable hex byte 0x0d: 2700 +multipart: invalid quoted-printable hex byte 0x20: 2490 +multipart: invalid quoted-printable hex byte 0x3d: 440 +unexpected EOF: 3122` + if got != want { + t.Errorf("Got:\n%s\nWant:\n%s", got, want) + } +} diff --git a/src/pkg/mime/multipart/testdata/nested-mime b/src/pkg/mime/multipart/testdata/nested-mime old mode 100755 new mode 100644 diff --git a/src/pkg/mime/multipart/writer.go b/src/pkg/mime/multipart/writer.go index ec70be492..e13a956af 100644 --- a/src/pkg/mime/multipart/writer.go +++ b/src/pkg/mime/multipart/writer.go @@ -30,11 +30,38 @@ func NewWriter(w io.Writer) *Writer { } } -// Boundary returns the Writer's randomly selected boundary string. +// Boundary returns the Writer's boundary. func (w *Writer) Boundary() string { return w.boundary } +// SetBoundary overrides the Writer's default randomly-generated +// boundary separator with an explicit value. +// +// SetBoundary must be called before any parts are created, may only +// contain certain ASCII characters, and must be 1-69 bytes long. +func (w *Writer) SetBoundary(boundary string) error { + if w.lastpart != nil { + return errors.New("mime: SetBoundary called after write") + } + // rfc2046#section-5.1.1 + if len(boundary) < 1 || len(boundary) > 69 { + return errors.New("mime: invalid boundary length") + } + for _, b := range boundary { + if 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' || '0' <= b && b <= '9' { + continue + } + switch b { + case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?': + continue + } + return errors.New("mime: invalid boundary character") + } + w.boundary = boundary + return nil +} + // FormDataContentType returns the Content-Type for an HTTP // multipart/form-data with this Writer's Boundary. func (w *Writer) FormDataContentType() string { diff --git a/src/pkg/mime/multipart/writer_test.go b/src/pkg/mime/multipart/writer_test.go index 494e936c4..52d68bcb6 100644 --- a/src/pkg/mime/multipart/writer_test.go +++ b/src/pkg/mime/multipart/writer_test.go @@ -7,6 +7,7 @@ package multipart import ( "bytes" "io/ioutil" + "strings" "testing" ) @@ -76,3 +77,37 @@ func TestWriter(t *testing.T) { t.Fatalf("expected end of parts; got %v, %v", part, err) } } + +func TestWriterSetBoundary(t *testing.T) { + var b bytes.Buffer + w := NewWriter(&b) + tests := []struct { + b string + ok bool + }{ + {"abc", true}, + {"", false}, + {"ungültig", false}, + {"!", false}, + {strings.Repeat("x", 69), true}, + {strings.Repeat("x", 70), false}, + {"bad!ascii!", false}, + {"my-separator", true}, + } + for i, tt := range tests { + err := w.SetBoundary(tt.b) + got := err == nil + if got != tt.ok { + t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok) + } else if tt.ok { + got := w.Boundary() + if got != tt.b { + t.Errorf("boundary = %q; want %q", got, tt.b) + } + } + } + w.Close() + if got := b.String(); !strings.Contains(got, "\r\n--my-separator--\r\n") { + t.Errorf("expected my-separator in output. got: %q", got) + } +} diff --git a/src/pkg/mime/test.types b/src/pkg/mime/test.types deleted file mode 100644 index 9b040edd7..000000000 --- a/src/pkg/mime/test.types +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - - - # mime package test -application/test t1 # Simple test -text/test t2 # Text test diff --git a/src/pkg/mime/testdata/test.types b/src/pkg/mime/testdata/test.types new file mode 100644 index 000000000..9b040edd7 --- /dev/null +++ b/src/pkg/mime/testdata/test.types @@ -0,0 +1,8 @@ +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + + + # mime package test +application/test t1 # Simple test +text/test t2 # Text test diff --git a/src/pkg/mime/type_unix.go b/src/pkg/mime/type_unix.go index 2dab1eac7..857a0ab67 100644 --- a/src/pkg/mime/type_unix.go +++ b/src/pkg/mime/type_unix.go @@ -23,15 +23,11 @@ func loadMimeFile(filename string) { if err != nil { return } + defer f.Close() - reader := bufio.NewReader(f) - for { - line, err := reader.ReadString('\n') - if err != nil { - f.Close() - return - } - fields := strings.Fields(line) + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) if len(fields) <= 1 || fields[0][0] == '#' { continue } @@ -43,6 +39,9 @@ func loadMimeFile(filename string) { setExtensionType("."+ext, mimeType) } } + if err := scanner.Err(); err != nil { + panic(err) + } } func initMime() { @@ -52,7 +51,7 @@ func initMime() { } func initMimeForTests() map[string]string { - typeFiles = []string{"test.types"} + typeFiles = []string{"testdata/test.types"} return map[string]string{ ".t1": "application/test", ".t2": "text/test; charset=utf-8", diff --git a/src/pkg/mime/type_windows.go b/src/pkg/mime/type_windows.go index bc388893b..180f948d1 100644 --- a/src/pkg/mime/type_windows.go +++ b/src/pkg/mime/type_windows.go @@ -11,7 +11,8 @@ import ( func initMime() { var root syscall.Handle - if syscall.RegOpenKeyEx(syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`), + rootpathp, _ := syscall.UTF16PtrFromString(`\`) + if syscall.RegOpenKeyEx(syscall.HKEY_CLASSES_ROOT, rootpathp, 0, syscall.KEY_READ, &root) != nil { return } @@ -31,15 +32,17 @@ func initMime() { continue } var h syscall.Handle + extpathp, _ := syscall.UTF16PtrFromString(`\` + ext) if syscall.RegOpenKeyEx( - syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`+ext), + syscall.HKEY_CLASSES_ROOT, extpathp, 0, syscall.KEY_READ, &h) != nil { continue } var typ uint32 n = uint32(len(buf) * 2) // api expects array of bytes, not uint16 + contenttypep, _ := syscall.UTF16PtrFromString("Content Type") if syscall.RegQueryValueEx( - h, syscall.StringToUTF16Ptr("Content Type"), + h, contenttypep, nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n) != nil { syscall.RegCloseKey(h) continue @@ -55,7 +58,6 @@ func initMime() { func initMimeForTests() map[string]string { return map[string]string{ - ".bmp": "image/bmp", ".png": "image/png", } } diff --git a/src/pkg/net/cgo_bsd.go b/src/pkg/net/cgo_bsd.go index 63750f7a3..3b38e3d83 100644 --- a/src/pkg/net/cgo_bsd.go +++ b/src/pkg/net/cgo_bsd.go @@ -11,6 +11,6 @@ package net */ import "C" -func cgoAddrInfoMask() C.int { - return C.AI_MASK +func cgoAddrInfoFlags() C.int { + return (C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK } diff --git a/src/pkg/net/cgo_linux.go b/src/pkg/net/cgo_linux.go index 8d4413d2d..f6cefa89a 100644 --- a/src/pkg/net/cgo_linux.go +++ b/src/pkg/net/cgo_linux.go @@ -9,6 +9,12 @@ package net */ import "C" -func cgoAddrInfoMask() C.int { +func cgoAddrInfoFlags() C.int { + // NOTE(rsc): In theory there are approximately balanced + // arguments for and against including AI_ADDRCONFIG + // in the flags (it includes IPv4 results only on IPv4 systems, + // and similarly for IPv6), but in practice setting it causes + // getaddrinfo to return the wrong canonical name on Linux. + // So definitely leave it out. return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL } diff --git a/src/pkg/net/cgo_netbsd.go b/src/pkg/net/cgo_netbsd.go new file mode 100644 index 000000000..aeaf8e568 --- /dev/null +++ b/src/pkg/net/cgo_netbsd.go @@ -0,0 +1,14 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +/* +#include +*/ +import "C" + +func cgoAddrInfoFlags() C.int { + return C.AI_CANONNAME +} diff --git a/src/pkg/net/cgo_openbsd.go b/src/pkg/net/cgo_openbsd.go new file mode 100644 index 000000000..aeaf8e568 --- /dev/null +++ b/src/pkg/net/cgo_openbsd.go @@ -0,0 +1,14 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +/* +#include +*/ +import "C" + +func cgoAddrInfoFlags() C.int { + return C.AI_CANONNAME +} diff --git a/src/pkg/net/cgo_unix.go b/src/pkg/net/cgo_unix.go index 36a3f3d34..7476140eb 100644 --- a/src/pkg/net/cgo_unix.go +++ b/src/pkg/net/cgo_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux +// +build darwin freebsd linux netbsd openbsd package net @@ -81,13 +81,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet var res *C.struct_addrinfo var hints C.struct_addrinfo - // NOTE(rsc): In theory there are approximately balanced - // arguments for and against including AI_ADDRCONFIG - // in the flags (it includes IPv4 results only on IPv4 systems, - // and similarly for IPv6), but in practice setting it causes - // getaddrinfo to return the wrong canonical name on Linux. - // So definitely leave it out. - hints.ai_flags = (C.AI_ALL | C.AI_V4MAPPED | C.AI_CANONNAME) & cgoAddrInfoMask() + hints.ai_flags = cgoAddrInfoFlags() h := C.CString(name) defer C.free(unsafe.Pointer(h)) diff --git a/src/pkg/net/conn_test.go b/src/pkg/net/conn_test.go new file mode 100644 index 000000000..fdb90862f --- /dev/null +++ b/src/pkg/net/conn_test.go @@ -0,0 +1,114 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements API tests across platforms and will never have a build +// tag. + +package net + +import ( + "os" + "runtime" + "testing" + "time" +) + +var connTests = []struct { + net string + addr string +}{ + {"tcp", "127.0.0.1:0"}, + {"unix", testUnixAddr()}, + {"unixpacket", testUnixAddr()}, +} + +// someTimeout is used just to test that net.Conn implementations +// don't explode when their SetFooDeadline methods are called. +// It isn't actually used for testing timeouts. +const someTimeout = 10 * time.Second + +func TestConnAndListener(t *testing.T) { + for _, tt := range connTests { + switch tt.net { + case "unix", "unixpacket": + switch runtime.GOOS { + case "plan9", "windows": + continue + } + if tt.net == "unixpacket" && runtime.GOOS != "linux" { + continue + } + } + + ln, err := Listen(tt.net, tt.addr) + if err != nil { + t.Fatalf("Listen failed: %v", err) + } + defer func(ln Listener, net, addr string) { + ln.Close() + switch net { + case "unix", "unixpacket": + os.Remove(addr) + } + }(ln, tt.net, tt.addr) + ln.Addr() + + done := make(chan int) + go transponder(t, ln, done) + + c, err := Dial(tt.net, ln.Addr().String()) + if err != nil { + t.Fatalf("Dial failed: %v", err) + } + defer c.Close() + c.LocalAddr() + c.RemoteAddr() + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + + if _, err := c.Write([]byte("CONN TEST")); err != nil { + t.Fatalf("Conn.Write failed: %v", err) + } + rb := make([]byte, 128) + if _, err := c.Read(rb); err != nil { + t.Fatalf("Conn.Read failed: %v", err) + } + + <-done + } +} + +func transponder(t *testing.T, ln Listener, done chan<- int) { + defer func() { done <- 1 }() + + switch ln := ln.(type) { + case *TCPListener: + ln.SetDeadline(time.Now().Add(someTimeout)) + case *UnixListener: + ln.SetDeadline(time.Now().Add(someTimeout)) + } + c, err := ln.Accept() + if err != nil { + t.Errorf("Listener.Accept failed: %v", err) + return + } + defer c.Close() + c.LocalAddr() + c.RemoteAddr() + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + + b := make([]byte, 128) + n, err := c.Read(b) + if err != nil { + t.Errorf("Conn.Read failed: %v", err) + return + } + if _, err := c.Write(b[:n]); err != nil { + t.Errorf("Conn.Write failed: %v", err) + return + } +} diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go index 10ca5faf7..22e1e7dd8 100644 --- a/src/pkg/net/dial.go +++ b/src/pkg/net/dial.go @@ -5,15 +5,91 @@ package net import ( + "errors" "time" ) -func parseDialNetwork(net string) (afnet string, proto int, err error) { +// A DialOption modifies a DialOpt call. +type DialOption interface { + dialOption() +} + +var ( + // TCP is a dial option to dial with TCP (over IPv4 or IPv6). + TCP = Network("tcp") + + // UDP is a dial option to dial with UDP (over IPv4 or IPv6). + UDP = Network("udp") +) + +// Network returns a DialOption to dial using the given network. +// +// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), +// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" +// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and +// "unixpacket". +// +// For IP networks, net must be "ip", "ip4" or "ip6" followed +// by a colon and a protocol number or name, such as +// "ipv4:1" or "ip6:ospf". +func Network(net string) DialOption { + return dialNetwork(net) +} + +type dialNetwork string + +func (dialNetwork) dialOption() {} + +// Deadline returns a DialOption to fail a dial that doesn't +// complete before t. +func Deadline(t time.Time) DialOption { + return dialDeadline(t) +} + +// Timeout returns a DialOption to fail a dial that doesn't +// complete within the provided duration. +func Timeout(d time.Duration) DialOption { + return dialDeadline(time.Now().Add(d)) +} + +type dialDeadline time.Time + +func (dialDeadline) dialOption() {} + +type tcpFastOpen struct{} + +func (tcpFastOpen) dialOption() {} + +// TODO(bradfitz): implement this (golang.org/issue/4842) and unexport this. +// +// TCPFastTimeout returns an option to use TCP Fast Open (TFO) when +// doing this dial. It is only valid for use with TCP connections. +// Data sent over a TFO connection may be processed by the peer +// multiple times, so should be used with caution. +func todo_TCPFastTimeout() DialOption { + return tcpFastOpen{} +} + +type localAddrOption struct { + la Addr +} + +func (localAddrOption) dialOption() {} + +// LocalAddress returns a dial option to perform a dial with the +// provided local address. The address must be of a compatible type +// for the network being dialed. +func LocalAddress(addr Addr) DialOption { + return localAddrOption{addr} +} + +func parseNetwork(net string) (afnet string, proto int, err error) { i := last(net, ':') if i < 0 { // no colon switch net { case "tcp", "tcp4", "tcp6": case "udp", "udp4", "udp6": + case "ip", "ip4", "ip6": case "unix", "unixgram", "unixpacket": default: return "", 0, UnknownNetworkError(net) @@ -36,40 +112,27 @@ func parseDialNetwork(net string) (afnet string, proto int, err error) { return "", 0, UnknownNetworkError(net) } -func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) { - afnet, _, err = parseDialNetwork(net) +func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) { + afnet, _, err := parseNetwork(net) if err != nil { - return "", nil, &OpError{op, net, nil, err} + return nil, &OpError{op, net, nil, err} } if op == "dial" && addr == "" { - return "", nil, &OpError{op, net, nil, errMissingAddress} + return nil, &OpError{op, net, nil, errMissingAddress} } switch afnet { - case "tcp", "tcp4", "tcp6": - if addr != "" { - a, err = ResolveTCPAddr(afnet, addr) - } - case "udp", "udp4", "udp6": - if addr != "" { - a, err = ResolveUDPAddr(afnet, addr) - } - case "ip", "ip4", "ip6": - if addr != "" { - a, err = ResolveIPAddr(afnet, addr) - } case "unix", "unixgram", "unixpacket": - if addr != "" { - a, err = ResolveUnixAddr(afnet, addr) - } + return ResolveUnixAddr(afnet, addr) } - return + return resolveInternetAddr(afnet, addr, deadline) } // Dial connects to the address addr on the network net. // // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), // "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" -// (IPv4-only), "ip6" (IPv6-only), "unix" and "unixpacket". +// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and +// "unixpacket". // // For TCP and UDP networks, addresses have the form host:port. // If host is a literal IPv6 address, it must be enclosed @@ -81,7 +144,7 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) { // Dial("tcp", "google.com:80") // Dial("tcp", "[de:ad:be:ef::ca:fe]:80") // -// For IP networks, addr must be "ip", "ip4" or "ip6" followed +// For IP networks, net must be "ip", "ip4" or "ip6" followed // by a colon and a protocol number or name. // // Examples: @@ -89,25 +152,71 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) { // Dial("ip6:ospf", "::1") // func Dial(net, addr string) (Conn, error) { - _, addri, err := resolveNetAddr("dial", net, addr) + return DialOpt(addr, dialNetwork(net)) +} + +func netFromOptions(opts []DialOption) string { + for _, opt := range opts { + if p, ok := opt.(dialNetwork); ok { + return string(p) + } + } + return "tcp" +} + +func deadlineFromOptions(opts []DialOption) time.Time { + for _, opt := range opts { + if d, ok := opt.(dialDeadline); ok { + return time.Time(d) + } + } + return noDeadline +} + +var noLocalAddr Addr // nil + +func localAddrFromOptions(opts []DialOption) Addr { + for _, opt := range opts { + if o, ok := opt.(localAddrOption); ok { + return o.la + } + } + return noLocalAddr +} + +// DialOpt dials addr using the provided options. +// If no options are provided, DialOpt(addr) is equivalent +// to Dial("tcp", addr). See Dial for the syntax of addr. +func DialOpt(addr string, opts ...DialOption) (Conn, error) { + net := netFromOptions(opts) + deadline := deadlineFromOptions(opts) + la := localAddrFromOptions(opts) + ra, err := resolveAddr("dial", net, addr, deadline) if err != nil { return nil, err } - return dialAddr(net, addr, addri) + return dial(net, addr, la, ra, deadline) } -func dialAddr(net, addr string, addri Addr) (c Conn, err error) { - switch ra := addri.(type) { +func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) { + if la != nil && la.Network() != ra.Network() { + return nil, &OpError{"dial", net, ra, errors.New("mismatched local addr type " + la.Network())} + } + switch ra := ra.(type) { case *TCPAddr: - c, err = DialTCP(net, nil, ra) + la, _ := la.(*TCPAddr) + c, err = dialTCP(net, la, ra, deadline) case *UDPAddr: - c, err = DialUDP(net, nil, ra) + la, _ := la.(*UDPAddr) + c, err = dialUDP(net, la, ra, deadline) case *IPAddr: - c, err = DialIP(net, nil, ra) + la, _ := la.(*IPAddr) + c, err = dialIP(net, la, ra, deadline) case *UnixAddr: - c, err = DialUnix(net, nil, ra) + la, _ := la.(*UnixAddr) + c, err = dialUnix(net, la, ra, deadline) default: - err = &OpError{"dial", net + " " + addr, nil, UnknownNetworkError(net)} + err = &OpError{"dial", net + " " + addr, ra, UnknownNetworkError(net)} } if err != nil { return nil, err @@ -118,10 +227,14 @@ func dialAddr(net, addr string, addri Addr) (c Conn, err error) { // DialTimeout acts like Dial but takes a timeout. // The timeout includes name resolution, if required. func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) { - // TODO(bradfitz): the timeout should be pushed down into the - // net package's event loop, so on timeout to dead hosts we - // don't have a goroutine sticking around for the default of - // ~3 minutes. + return dialTimeout(net, addr, timeout) +} + +// dialTimeoutRace is the old implementation of DialTimeout, still used +// on operating systems where the deadline hasn't been pushed down +// into the pollserver. +// TODO: fix this on plan9. +func dialTimeoutRace(net, addr string, timeout time.Duration) (Conn, error) { t := time.NewTimer(timeout) defer t.Stop() type pair struct { @@ -131,30 +244,30 @@ func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) { ch := make(chan pair, 1) resolvedAddr := make(chan Addr, 1) go func() { - _, addri, err := resolveNetAddr("dial", net, addr) + ra, err := resolveAddr("dial", net, addr, noDeadline) if err != nil { ch <- pair{nil, err} return } - resolvedAddr <- addri // in case we need it for OpError - c, err := dialAddr(net, addr, addri) + resolvedAddr <- ra // in case we need it for OpError + c, err := dial(net, addr, noLocalAddr, ra, noDeadline) ch <- pair{c, err} }() select { case <-t.C: // Try to use the real Addr in our OpError, if we resolved it // before the timeout. Otherwise we just use stringAddr. - var addri Addr + var ra Addr select { case a := <-resolvedAddr: - addri = a + ra = a default: - addri = &stringAddr{net, addr} + ra = &stringAddr{net, addr} } err := &OpError{ Op: "dial", Net: net, - Addr: addri, + Addr: ra, Err: &timeoutError{}, } return nil, err @@ -173,24 +286,16 @@ func (a stringAddr) String() string { return a.addr } // Listen announces on the local network address laddr. // The network string net must be a stream-oriented network: -// "tcp", "tcp4", "tcp6", or "unix", or "unixpacket". +// "tcp", "tcp4", "tcp6", "unix" or "unixpacket". func Listen(net, laddr string) (Listener, error) { - afnet, a, err := resolveNetAddr("listen", net, laddr) + la, err := resolveAddr("listen", net, laddr, noDeadline) if err != nil { return nil, err } - switch afnet { - case "tcp", "tcp4", "tcp6": - var la *TCPAddr - if a != nil { - la = a.(*TCPAddr) - } + switch la := la.(type) { + case *TCPAddr: return ListenTCP(net, la) - case "unix", "unixpacket": - var la *UnixAddr - if a != nil { - la = a.(*UnixAddr) - } + case *UnixAddr: return ListenUnix(net, la) } return nil, UnknownNetworkError(net) @@ -199,30 +304,18 @@ func Listen(net, laddr string) (Listener, error) { // ListenPacket announces on the local network address laddr. // The network string net must be a packet-oriented network: // "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram". -func ListenPacket(net, addr string) (PacketConn, error) { - afnet, a, err := resolveNetAddr("listen", net, addr) +func ListenPacket(net, laddr string) (PacketConn, error) { + la, err := resolveAddr("listen", net, laddr, noDeadline) if err != nil { return nil, err } - switch afnet { - case "udp", "udp4", "udp6": - var la *UDPAddr - if a != nil { - la = a.(*UDPAddr) - } + switch la := la.(type) { + case *UDPAddr: return ListenUDP(net, la) - case "ip", "ip4", "ip6": - var la *IPAddr - if a != nil { - la = a.(*IPAddr) - } + case *IPAddr: return ListenIP(net, la) - case "unixgram": - var la *UnixAddr - if a != nil { - la = a.(*UnixAddr) - } - return DialUnix(net, la, nil) + case *UnixAddr: + return ListenUnixgram(net, la) } return nil, UnknownNetworkError(net) } diff --git a/src/pkg/net/dial_test.go b/src/pkg/net/dial_test.go index 7212087fe..2303e8fa4 100644 --- a/src/pkg/net/dial_test.go +++ b/src/pkg/net/dial_test.go @@ -7,6 +7,9 @@ package net import ( "flag" "fmt" + "io" + "os" + "reflect" "regexp" "runtime" "testing" @@ -55,7 +58,7 @@ func TestDialTimeout(t *testing.T) { // on our 386 builder, this Dial succeeds, connecting // to an IIS web server somewhere. The data center // or VM or firewall must be stealing the TCP connection. - // + // // IANA Service Name and Transport Protocol Port Number Registry // go func() { @@ -72,8 +75,7 @@ func TestDialTimeout(t *testing.T) { // by default. FreeBSD likely works, but is untested. // TODO(rsc): // The timeout never happens on Windows. Why? Issue 3016. - t.Logf("skipping test on %q; untested.", runtime.GOOS) - return + t.Skipf("skipping test on %q; untested.", runtime.GOOS) } connected := 0 @@ -105,8 +107,7 @@ func TestDialTimeout(t *testing.T) { func TestSelfConnect(t *testing.T) { if runtime.GOOS == "windows" { // TODO(brainman): do not know why it hangs. - t.Logf("skipping known-broken test on windows") - return + t.Skip("skipping known-broken test on windows") } // Test that Dial does not honor self-connects. // See the comment in DialTCP. @@ -130,7 +131,7 @@ func TestSelfConnect(t *testing.T) { n = 1000 } switch runtime.GOOS { - case "darwin", "freebsd", "openbsd", "windows": + case "darwin", "freebsd", "netbsd", "openbsd", "plan9", "windows": // Non-Linux systems take a long time to figure // out that there is nothing listening on localhost. n = 100 @@ -222,3 +223,104 @@ func TestDialError(t *testing.T) { } } } + +var invalidDialAndListenArgTests = []struct { + net string + addr string + err error +}{ + {"foo", "bar", &OpError{Op: "dial", Net: "foo", Addr: nil, Err: UnknownNetworkError("foo")}}, + {"baz", "", &OpError{Op: "listen", Net: "baz", Addr: nil, Err: UnknownNetworkError("baz")}}, + {"tcp", "", &OpError{Op: "dial", Net: "tcp", Addr: nil, Err: errMissingAddress}}, +} + +func TestInvalidDialAndListenArgs(t *testing.T) { + for _, tt := range invalidDialAndListenArgTests { + var err error + switch tt.err.(*OpError).Op { + case "dial": + _, err = Dial(tt.net, tt.addr) + case "listen": + _, err = Listen(tt.net, tt.addr) + } + if !reflect.DeepEqual(tt.err, err) { + t.Fatalf("got %#v; expected %#v", err, tt.err) + } + } +} + +func TestDialTimeoutFDLeak(t *testing.T) { + if runtime.GOOS != "linux" { + // TODO(bradfitz): test on other platforms + t.Skipf("skipping test on %q", runtime.GOOS) + } + + ln := newLocalListener(t) + defer ln.Close() + + type connErr struct { + conn Conn + err error + } + dials := listenerBacklog + 100 + // used to be listenerBacklog + 5, but was found to be unreliable, issue 4384. + maxGoodConnect := listenerBacklog + runtime.NumCPU()*10 + resc := make(chan connErr) + for i := 0; i < dials; i++ { + go func() { + conn, err := DialTimeout("tcp", ln.Addr().String(), 500*time.Millisecond) + resc <- connErr{conn, err} + }() + } + + var firstErr string + var ngood int + var toClose []io.Closer + for i := 0; i < dials; i++ { + ce := <-resc + if ce.err == nil { + ngood++ + if ngood > maxGoodConnect { + t.Errorf("%d good connects; expected at most %d", ngood, maxGoodConnect) + } + toClose = append(toClose, ce.conn) + continue + } + err := ce.err + if firstErr == "" { + firstErr = err.Error() + } else if err.Error() != firstErr { + t.Fatalf("inconsistent error messages: first was %q, then later %q", firstErr, err) + } + } + for _, c := range toClose { + c.Close() + } + for i := 0; i < 100; i++ { + if got := numFD(); got < dials { + // Test passes. + return + } + time.Sleep(10 * time.Millisecond) + } + if got := numFD(); got >= dials { + t.Errorf("num fds after %d timeouts = %d; want <%d", dials, got, dials) + } +} + +func numFD() int { + if runtime.GOOS == "linux" { + f, err := os.Open("/proc/self/fd") + if err != nil { + panic(err) + } + defer f.Close() + names, err := f.Readdirnames(0) + if err != nil { + panic(err) + } + return len(names) + } + // All tests using this should be skipped anyway, but: + panic("numFDs not implemented on " + runtime.GOOS) +} diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go index 03c449972..73a94f5bf 100644 --- a/src/pkg/net/dialgoogle_test.go +++ b/src/pkg/net/dialgoogle_test.go @@ -41,17 +41,6 @@ func doDial(t *testing.T, network, addr string) { fd.Close() } -func TestLookupCNAME(t *testing.T) { - if testing.Short() || !*testExternal { - t.Logf("skipping test to avoid external network") - return - } - cname, err := LookupCNAME("www.google.com") - if !strings.HasSuffix(cname, ".l.google.com.") || err != nil { - t.Errorf(`LookupCNAME("www.google.com.") = %q, %v, want "*.l.google.com.", nil`, cname, err) - } -} - var googleaddrsipv4 = []string{ "%d.%d.%d.%d:80", "www.google.com:80", @@ -67,8 +56,7 @@ var googleaddrsipv4 = []string{ func TestDialGoogleIPv4(t *testing.T) { if testing.Short() || !*testExternal { - t.Logf("skipping test to avoid external network") - return + t.Skip("skipping test to avoid external network") } // Insert an actual IPv4 address for google.com @@ -123,12 +111,14 @@ var googleaddrsipv6 = []string{ func TestDialGoogleIPv6(t *testing.T) { if testing.Short() || !*testExternal { - t.Logf("skipping test to avoid external network") - return + t.Skip("skipping test to avoid external network") } // Only run tcp6 if the kernel will take it. - if !*testIPv6 || !supportsIPv6 { - return + if !supportsIPv6 { + t.Skip("skipping test; ipv6 is not supported") + } + if !*testIPv6 { + t.Skip("test disabled; use -ipv6 to enable") } // Insert an actual IPv6 address for ipv6.google.com diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go index e69cb3188..76b192645 100644 --- a/src/pkg/net/dnsclient.go +++ b/src/pkg/net/dnsclient.go @@ -183,7 +183,7 @@ func (s byPriorityWeight) Less(i, j int) bool { } // shuffleByWeight shuffles SRV records by weight using the algorithm -// described in RFC 2782. +// described in RFC 2782. func (addrs byPriorityWeight) shuffleByWeight() { sum := 0 for _, addr := range addrs { @@ -244,3 +244,8 @@ func (s byPref) sort() { } sort.Sort(s) } + +// An NS represents a single DNS NS record. +type NS struct { + Host string +} diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go index 18c39360e..9e21bb4a0 100644 --- a/src/pkg/net/dnsclient_unix.go +++ b/src/pkg/net/dnsclient_unix.go @@ -237,24 +237,30 @@ func goLookupIP(name string) (addrs []IP, err error) { } var records []dnsRR var cname string - cname, records, err = lookup(name, dnsTypeA) - if err != nil { - return - } + var err4, err6 error + cname, records, err4 = lookup(name, dnsTypeA) addrs = convertRR_A(records) if cname != "" { name = cname } - _, records, err = lookup(name, dnsTypeAAAA) - if err != nil && len(addrs) > 0 { - // Ignore error because A lookup succeeded. - err = nil + _, records, err6 = lookup(name, dnsTypeAAAA) + if err4 != nil && err6 == nil { + // Ignore A error because AAAA lookup succeeded. + err4 = nil } - if err != nil { - return + if err6 != nil && len(addrs) > 0 { + // Ignore AAAA error because A lookup succeeded. + err6 = nil } + if err4 != nil { + return nil, err4 + } + if err6 != nil { + return nil, err6 + } + addrs = append(addrs, convertRR_AAAA(records)...) - return + return addrs, nil } // goLookupCNAME is the native Go implementation of LookupCNAME. diff --git a/src/pkg/net/dnsconfig.go b/src/pkg/net/dnsconfig.go deleted file mode 100644 index bb46cc900..000000000 --- a/src/pkg/net/dnsconfig.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd - -// Read system DNS config from /etc/resolv.conf - -package net - -type dnsConfig struct { - servers []string // servers to use - search []string // suffixes to append to local name - ndots int // number of dots in name to trigger absolute lookup - timeout int // seconds before giving up on packet - attempts int // lost packets before giving up on server - rotate bool // round robin among servers -} - -// See resolv.conf(5) on a Linux machine. -// TODO(rsc): Supposed to call uname() and chop the beginning -// of the host name to get the default search domain. -// We assume it's in resolv.conf anyway. -func dnsReadConfig() (*dnsConfig, error) { - file, err := open("/etc/resolv.conf") - if err != nil { - return nil, &DNSConfigError{err} - } - conf := new(dnsConfig) - conf.servers = make([]string, 3)[0:0] // small, but the standard limit - conf.search = make([]string, 0) - conf.ndots = 1 - conf.timeout = 5 - conf.attempts = 2 - conf.rotate = false - for line, ok := file.readLine(); ok; line, ok = file.readLine() { - f := getFields(line) - if len(f) < 1 { - continue - } - switch f[0] { - case "nameserver": // add one name server - a := conf.servers - n := len(a) - if len(f) > 1 && n < cap(a) { - // One more check: make sure server name is - // just an IP address. Otherwise we need DNS - // to look it up. - name := f[1] - switch len(ParseIP(name)) { - case 16: - name = "[" + name + "]" - fallthrough - case 4: - a = a[0 : n+1] - a[n] = name - conf.servers = a - } - } - - case "domain": // set search path to just this domain - if len(f) > 1 { - conf.search = make([]string, 1) - conf.search[0] = f[1] - } else { - conf.search = make([]string, 0) - } - - case "search": // set search path to given servers - conf.search = make([]string, len(f)-1) - for i := 0; i < len(conf.search); i++ { - conf.search[i] = f[i+1] - } - - case "options": // magic options - for i := 1; i < len(f); i++ { - s := f[i] - switch { - case len(s) >= 6 && s[0:6] == "ndots:": - n, _, _ := dtoi(s, 6) - if n < 1 { - n = 1 - } - conf.ndots = n - case len(s) >= 8 && s[0:8] == "timeout:": - n, _, _ := dtoi(s, 8) - if n < 1 { - n = 1 - } - conf.timeout = n - case len(s) >= 8 && s[0:9] == "attempts:": - n, _, _ := dtoi(s, 9) - if n < 1 { - n = 1 - } - conf.attempts = n - case s == "rotate": - conf.rotate = true - } - } - } - } - file.close() - - return conf, nil -} diff --git a/src/pkg/net/dnsconfig_unix.go b/src/pkg/net/dnsconfig_unix.go new file mode 100644 index 000000000..bb46cc900 --- /dev/null +++ b/src/pkg/net/dnsconfig_unix.go @@ -0,0 +1,106 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +// Read system DNS config from /etc/resolv.conf + +package net + +type dnsConfig struct { + servers []string // servers to use + search []string // suffixes to append to local name + ndots int // number of dots in name to trigger absolute lookup + timeout int // seconds before giving up on packet + attempts int // lost packets before giving up on server + rotate bool // round robin among servers +} + +// See resolv.conf(5) on a Linux machine. +// TODO(rsc): Supposed to call uname() and chop the beginning +// of the host name to get the default search domain. +// We assume it's in resolv.conf anyway. +func dnsReadConfig() (*dnsConfig, error) { + file, err := open("/etc/resolv.conf") + if err != nil { + return nil, &DNSConfigError{err} + } + conf := new(dnsConfig) + conf.servers = make([]string, 3)[0:0] // small, but the standard limit + conf.search = make([]string, 0) + conf.ndots = 1 + conf.timeout = 5 + conf.attempts = 2 + conf.rotate = false + for line, ok := file.readLine(); ok; line, ok = file.readLine() { + f := getFields(line) + if len(f) < 1 { + continue + } + switch f[0] { + case "nameserver": // add one name server + a := conf.servers + n := len(a) + if len(f) > 1 && n < cap(a) { + // One more check: make sure server name is + // just an IP address. Otherwise we need DNS + // to look it up. + name := f[1] + switch len(ParseIP(name)) { + case 16: + name = "[" + name + "]" + fallthrough + case 4: + a = a[0 : n+1] + a[n] = name + conf.servers = a + } + } + + case "domain": // set search path to just this domain + if len(f) > 1 { + conf.search = make([]string, 1) + conf.search[0] = f[1] + } else { + conf.search = make([]string, 0) + } + + case "search": // set search path to given servers + conf.search = make([]string, len(f)-1) + for i := 0; i < len(conf.search); i++ { + conf.search[i] = f[i+1] + } + + case "options": // magic options + for i := 1; i < len(f); i++ { + s := f[i] + switch { + case len(s) >= 6 && s[0:6] == "ndots:": + n, _, _ := dtoi(s, 6) + if n < 1 { + n = 1 + } + conf.ndots = n + case len(s) >= 8 && s[0:8] == "timeout:": + n, _, _ := dtoi(s, 8) + if n < 1 { + n = 1 + } + conf.timeout = n + case len(s) >= 8 && s[0:9] == "attempts:": + n, _, _ := dtoi(s, 9) + if n < 1 { + n = 1 + } + conf.attempts = n + case s == "rotate": + conf.rotate = true + } + } + } + } + file.close() + + return conf, nil +} diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go index b6ebe1173..161afb2a5 100644 --- a/src/pkg/net/dnsmsg.go +++ b/src/pkg/net/dnsmsg.go @@ -618,7 +618,7 @@ func printStruct(any dnsStruct) string { s += name + "=" switch tag { case "ipv4": - i := val.(uint32) + i := *val.(*uint32) s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() case "ipv6": i := val.([]byte) diff --git a/src/pkg/net/doc.go b/src/pkg/net/doc.go deleted file mode 100644 index 3a44e528e..000000000 --- a/src/pkg/net/doc.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package net - -// LookupHost looks up the given host using the local resolver. -// It returns an array of that host's addresses. -func LookupHost(host string) (addrs []string, err error) { - return lookupHost(host) -} - -// LookupIP looks up host using the local resolver. -// It returns an array of that host's IPv4 and IPv6 addresses. -func LookupIP(host string) (addrs []IP, err error) { - return lookupIP(host) -} - -// LookupPort looks up the port for the given network and service. -func LookupPort(network, service string) (port int, err error) { - return lookupPort(network, service) -} - -// LookupCNAME returns the canonical DNS host for the given name. -// Callers that do not care about the canonical name can call -// LookupHost or LookupIP directly; both take care of resolving -// the canonical name as part of the lookup. -func LookupCNAME(name string) (cname string, err error) { - return lookupCNAME(name) -} - -// LookupSRV tries to resolve an SRV query of the given service, -// protocol, and domain name. The proto is "tcp" or "udp". -// The returned records are sorted by priority and randomized -// by weight within a priority. -// -// LookupSRV constructs the DNS name to look up following RFC 2782. -// That is, it looks up _service._proto.name. To accommodate services -// publishing SRV records under non-standard names, if both service -// and proto are empty strings, LookupSRV looks up name directly. -func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { - return lookupSRV(service, proto, name) -} - -// LookupMX returns the DNS MX records for the given domain name sorted by preference. -func LookupMX(name string) (mx []*MX, err error) { - return lookupMX(name) -} - -// LookupTXT returns the DNS TXT records for the given domain name. -func LookupTXT(name string) (txt []string, err error) { - return lookupTXT(name) -} - -// LookupAddr performs a reverse lookup for the given address, returning a list -// of names mapping to that address. -func LookupAddr(addr string) (name []string, err error) { - return lookupAddr(addr) -} diff --git a/src/pkg/net/example_test.go b/src/pkg/net/example_test.go index 1a1c2edfe..eefe84fa7 100644 --- a/src/pkg/net/example_test.go +++ b/src/pkg/net/example_test.go @@ -17,7 +17,7 @@ func ExampleListener() { log.Fatal(err) } for { - // Wait for a connection. + // Wait for a connection. conn, err := l.Accept() if err != nil { log.Fatal(err) diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go deleted file mode 100644 index 76c953b9b..000000000 --- a/src/pkg/net/fd.go +++ /dev/null @@ -1,663 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd - -package net - -import ( - "errors" - "io" - "os" - "sync" - "syscall" - "time" -) - -// Network file descriptor. -type netFD struct { - // locking/lifetime of sysfd - sysmu sync.Mutex - sysref int - - // must lock both sysmu and pollserver to write - // can lock either to read - closing bool - - // immutable until Close - sysfd int - family int - sotype int - isConnected bool - sysfile *os.File - cr chan error - cw chan error - net string - laddr Addr - raddr Addr - - // owned by client - rdeadline int64 - rio sync.Mutex - wdeadline int64 - wio sync.Mutex - - // owned by fd wait server - ncr, ncw int -} - -// A pollServer helps FDs determine when to retry a non-blocking -// read or write after they get EAGAIN. When an FD needs to wait, -// send the fd on s.cr (for a read) or s.cw (for a write) to pass the -// request to the poll server. Then receive on fd.cr/fd.cw. -// When the pollServer finds that i/o on FD should be possible -// again, it will send fd on fd.cr/fd.cw to wake any waiting processes. -// This protocol is implemented as s.WaitRead() and s.WaitWrite(). -// -// There is one subtlety: when sending on s.cr/s.cw, the -// poll server is probably in a system call, waiting for an fd -// to become ready. It's not looking at the request channels. -// To resolve this, the poll server waits not just on the FDs it has -// been given but also its own pipe. After sending on the -// buffered channel s.cr/s.cw, WaitRead/WaitWrite writes a -// byte to the pipe, causing the pollServer's poll system call to -// return. In response to the pipe being readable, the pollServer -// re-polls its request channels. -// -// Note that the ordering is "send request" and then "wake up server". -// If the operations were reversed, there would be a race: the poll -// server might wake up and look at the request channel, see that it -// was empty, and go back to sleep, all before the requester managed -// to send the request. Because the send must complete before the wakeup, -// the request channel must be buffered. A buffer of size 1 is sufficient -// for any request load. If many processes are trying to submit requests, -// one will succeed, the pollServer will read the request, and then the -// channel will be empty for the next process's request. A larger buffer -// might help batch requests. -// -// To avoid races in closing, all fd operations are locked and -// refcounted. when netFD.Close() is called, it calls syscall.Shutdown -// and sets a closing flag. Only when the last reference is removed -// will the fd be closed. - -type pollServer struct { - cr, cw chan *netFD // buffered >= 1 - pr, pw *os.File - poll *pollster // low-level OS hooks - sync.Mutex // controls pending and deadline - pending map[int]*netFD - deadline int64 // next deadline (nsec since 1970) -} - -func (s *pollServer) AddFD(fd *netFD, mode int) error { - s.Lock() - intfd := fd.sysfd - if intfd < 0 || fd.closing { - // fd closed underfoot - s.Unlock() - return errClosing - } - - var t int64 - key := intfd << 1 - if mode == 'r' { - fd.ncr++ - t = fd.rdeadline - } else { - fd.ncw++ - key++ - t = fd.wdeadline - } - s.pending[key] = fd - doWakeup := false - if t > 0 && (s.deadline == 0 || t < s.deadline) { - s.deadline = t - doWakeup = true - } - - wake, err := s.poll.AddFD(intfd, mode, false) - if err != nil { - panic("pollServer AddFD " + err.Error()) - } - if wake { - doWakeup = true - } - s.Unlock() - - if doWakeup { - s.Wakeup() - } - return nil -} - -// Evict evicts fd from the pending list, unblocking -// any I/O running on fd. The caller must have locked -// pollserver. -func (s *pollServer) Evict(fd *netFD) { - if s.pending[fd.sysfd<<1] == fd { - s.WakeFD(fd, 'r', errClosing) - s.poll.DelFD(fd.sysfd, 'r') - delete(s.pending, fd.sysfd<<1) - } - if s.pending[fd.sysfd<<1|1] == fd { - s.WakeFD(fd, 'w', errClosing) - s.poll.DelFD(fd.sysfd, 'w') - delete(s.pending, fd.sysfd<<1|1) - } -} - -var wakeupbuf [1]byte - -func (s *pollServer) Wakeup() { s.pw.Write(wakeupbuf[0:]) } - -func (s *pollServer) LookupFD(fd int, mode int) *netFD { - key := fd << 1 - if mode == 'w' { - key++ - } - netfd, ok := s.pending[key] - if !ok { - return nil - } - delete(s.pending, key) - return netfd -} - -func (s *pollServer) WakeFD(fd *netFD, mode int, err error) { - if mode == 'r' { - for fd.ncr > 0 { - fd.ncr-- - fd.cr <- err - } - } else { - for fd.ncw > 0 { - fd.ncw-- - fd.cw <- err - } - } -} - -func (s *pollServer) Now() int64 { - return time.Now().UnixNano() -} - -func (s *pollServer) CheckDeadlines() { - now := s.Now() - // TODO(rsc): This will need to be handled more efficiently, - // probably with a heap indexed by wakeup time. - - var next_deadline int64 - for key, fd := range s.pending { - var t int64 - var mode int - if key&1 == 0 { - mode = 'r' - } else { - mode = 'w' - } - if mode == 'r' { - t = fd.rdeadline - } else { - t = fd.wdeadline - } - if t > 0 { - if t <= now { - delete(s.pending, key) - if mode == 'r' { - s.poll.DelFD(fd.sysfd, mode) - fd.rdeadline = -1 - } else { - s.poll.DelFD(fd.sysfd, mode) - fd.wdeadline = -1 - } - s.WakeFD(fd, mode, nil) - } else if next_deadline == 0 || t < next_deadline { - next_deadline = t - } - } - } - s.deadline = next_deadline -} - -func (s *pollServer) Run() { - var scratch [100]byte - s.Lock() - defer s.Unlock() - for { - var t = s.deadline - if t > 0 { - t = t - s.Now() - if t <= 0 { - s.CheckDeadlines() - continue - } - } - fd, mode, err := s.poll.WaitFD(s, t) - if err != nil { - print("pollServer WaitFD: ", err.Error(), "\n") - return - } - if fd < 0 { - // Timeout happened. - s.CheckDeadlines() - continue - } - if fd == int(s.pr.Fd()) { - // Drain our wakeup pipe (we could loop here, - // but it's unlikely that there are more than - // len(scratch) wakeup calls). - s.pr.Read(scratch[0:]) - s.CheckDeadlines() - } else { - netfd := s.LookupFD(fd, mode) - if netfd == nil { - // This can happen because the WaitFD runs without - // holding s's lock, so there might be a pending wakeup - // for an fd that has been evicted. No harm done. - continue - } - s.WakeFD(netfd, mode, nil) - } - } -} - -func (s *pollServer) WaitRead(fd *netFD) error { - err := s.AddFD(fd, 'r') - if err == nil { - err = <-fd.cr - } - return err -} - -func (s *pollServer) WaitWrite(fd *netFD) error { - err := s.AddFD(fd, 'w') - if err == nil { - err = <-fd.cw - } - return err -} - -// Network FD methods. -// All the network FDs use a single pollServer. - -var pollserver *pollServer -var onceStartServer sync.Once - -func startServer() { - p, err := newPollServer() - if err != nil { - print("Start pollServer: ", err.Error(), "\n") - } - pollserver = p -} - -func newFD(fd, family, sotype int, net string) (*netFD, error) { - onceStartServer.Do(startServer) - if err := syscall.SetNonblock(fd, true); err != nil { - return nil, err - } - netfd := &netFD{ - sysfd: fd, - family: family, - sotype: sotype, - net: net, - } - netfd.cr = make(chan error, 1) - netfd.cw = make(chan error, 1) - return netfd, nil -} - -func (fd *netFD) setAddr(laddr, raddr Addr) { - fd.laddr = laddr - fd.raddr = raddr - var ls, rs string - if laddr != nil { - ls = laddr.String() - } - if raddr != nil { - rs = raddr.String() - } - fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net+":"+ls+"->"+rs) -} - -func (fd *netFD) connect(ra syscall.Sockaddr) error { - err := syscall.Connect(fd.sysfd, ra) - if err == syscall.EINPROGRESS { - if err = pollserver.WaitWrite(fd); err != nil { - return err - } - var e int - e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) - if err != nil { - return os.NewSyscallError("getsockopt", err) - } - if e != 0 { - err = syscall.Errno(e) - } - } - return err -} - -var errClosing = errors.New("use of closed network connection") - -// Add a reference to this fd. -// If closing==true, pollserver must be locked; mark the fd as closing. -// Returns an error if the fd cannot be used. -func (fd *netFD) incref(closing bool) error { - if fd == nil { - return errClosing - } - fd.sysmu.Lock() - if fd.closing { - fd.sysmu.Unlock() - return errClosing - } - fd.sysref++ - if closing { - fd.closing = true - } - fd.sysmu.Unlock() - return nil -} - -// Remove a reference to this FD and close if we've been asked to do so (and -// there are no references left. -func (fd *netFD) decref() { - if fd == nil { - return - } - fd.sysmu.Lock() - fd.sysref-- - if fd.closing && fd.sysref == 0 && fd.sysfile != nil { - fd.sysfile.Close() - fd.sysfile = nil - fd.sysfd = -1 - } - fd.sysmu.Unlock() -} - -func (fd *netFD) Close() error { - pollserver.Lock() // needed for both fd.incref(true) and pollserver.Evict - defer pollserver.Unlock() - if err := fd.incref(true); err != nil { - return err - } - // Unblock any I/O. Once it all unblocks and returns, - // so that it cannot be referring to fd.sysfd anymore, - // the final decref will close fd.sysfd. This should happen - // fairly quickly, since all the I/O is non-blocking, and any - // attempts to block in the pollserver will return errClosing. - pollserver.Evict(fd) - fd.decref() - return nil -} - -func (fd *netFD) shutdown(how int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.Shutdown(fd.sysfd, how) - if err != nil { - return &OpError{"shutdown", fd.net, fd.laddr, err} - } - return nil -} - -func (fd *netFD) CloseRead() error { - return fd.shutdown(syscall.SHUT_RD) -} - -func (fd *netFD) CloseWrite() error { - return fd.shutdown(syscall.SHUT_WR) -} - -func (fd *netFD) Read(p []byte) (n int, err error) { - fd.rio.Lock() - defer fd.rio.Unlock() - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - for { - n, err = syscall.Read(int(fd.sysfd), p) - if err == syscall.EAGAIN { - err = errTimeout - if fd.rdeadline >= 0 { - if err = pollserver.WaitRead(fd); err == nil { - continue - } - } - } - if err != nil { - n = 0 - } else if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM { - err = io.EOF - } - break - } - if err != nil && err != io.EOF { - err = &OpError{"read", fd.net, fd.raddr, err} - } - return -} - -func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { - fd.rio.Lock() - defer fd.rio.Unlock() - if err := fd.incref(false); err != nil { - return 0, nil, err - } - defer fd.decref() - for { - n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) - if err == syscall.EAGAIN { - err = errTimeout - if fd.rdeadline >= 0 { - if err = pollserver.WaitRead(fd); err == nil { - continue - } - } - } - if err != nil { - n = 0 - } - break - } - if err != nil && err != io.EOF { - err = &OpError{"read", fd.net, fd.laddr, err} - } - return -} - -func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { - fd.rio.Lock() - defer fd.rio.Unlock() - if err := fd.incref(false); err != nil { - return 0, 0, 0, nil, err - } - defer fd.decref() - for { - n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) - if err == syscall.EAGAIN { - err = errTimeout - if fd.rdeadline >= 0 { - if err = pollserver.WaitRead(fd); err == nil { - continue - } - } - } - if err == nil && n == 0 { - err = io.EOF - } - break - } - if err != nil && err != io.EOF { - err = &OpError{"read", fd.net, fd.laddr, err} - return - } - return -} - -func (fd *netFD) Write(p []byte) (int, error) { - fd.wio.Lock() - defer fd.wio.Unlock() - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - if fd.sysfile == nil { - return 0, syscall.EINVAL - } - - var err error - nn := 0 - for { - var n int - n, err = syscall.Write(int(fd.sysfd), p[nn:]) - if n > 0 { - nn += n - } - if nn == len(p) { - break - } - if err == syscall.EAGAIN { - err = errTimeout - if fd.wdeadline >= 0 { - if err = pollserver.WaitWrite(fd); err == nil { - continue - } - } - } - if err != nil { - n = 0 - break - } - if n == 0 { - err = io.ErrUnexpectedEOF - break - } - } - if err != nil { - err = &OpError{"write", fd.net, fd.raddr, err} - } - return nn, err -} - -func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { - fd.wio.Lock() - defer fd.wio.Unlock() - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - for { - err = syscall.Sendto(fd.sysfd, p, 0, sa) - if err == syscall.EAGAIN { - err = errTimeout - if fd.wdeadline >= 0 { - if err = pollserver.WaitWrite(fd); err == nil { - continue - } - } - } - break - } - if err == nil { - n = len(p) - } else { - err = &OpError{"write", fd.net, fd.raddr, err} - } - return -} - -func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { - fd.wio.Lock() - defer fd.wio.Unlock() - if err := fd.incref(false); err != nil { - return 0, 0, err - } - defer fd.decref() - for { - err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) - if err == syscall.EAGAIN { - err = errTimeout - if fd.wdeadline >= 0 { - if err = pollserver.WaitWrite(fd); err == nil { - continue - } - } - } - break - } - if err == nil { - n = len(p) - oobn = len(oob) - } else { - err = &OpError{"write", fd.net, fd.raddr, err} - } - return -} - -func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err error) { - if err := fd.incref(false); err != nil { - return nil, err - } - defer fd.decref() - - // See ../syscall/exec.go for description of ForkLock. - // It is okay to hold the lock across syscall.Accept - // because we have put fd.sysfd into non-blocking mode. - var s int - var rsa syscall.Sockaddr - for { - syscall.ForkLock.RLock() - s, rsa, err = syscall.Accept(fd.sysfd) - if err != nil { - syscall.ForkLock.RUnlock() - if err == syscall.EAGAIN { - err = errTimeout - if fd.rdeadline >= 0 { - if err = pollserver.WaitRead(fd); err == nil { - continue - } - } - } else if err == syscall.ECONNABORTED { - // This means that a socket on the listen queue was closed - // before we Accept()ed it; it's a silly error, so try again. - continue - } - return nil, &OpError{"accept", fd.net, fd.laddr, err} - } - break - } - syscall.CloseOnExec(s) - syscall.ForkLock.RUnlock() - - if netfd, err = newFD(s, fd.family, fd.sotype, fd.net); err != nil { - syscall.Close(s) - return nil, err - } - lsa, _ := syscall.Getsockname(netfd.sysfd) - netfd.setAddr(toAddr(lsa), toAddr(rsa)) - return netfd, nil -} - -func (fd *netFD) dup() (f *os.File, err error) { - ns, err := syscall.Dup(fd.sysfd) - if err != nil { - return nil, &OpError{"dup", fd.net, fd.laddr, err} - } - - // We want blocking mode for the new fd, hence the double negative. - if err = syscall.SetNonblock(ns, false); err != nil { - return nil, &OpError{"setnonblock", fd.net, fd.laddr, err} - } - - return os.NewFile(uintptr(ns), fd.sysfile.Name()), nil -} - -func closesocket(s int) error { - return syscall.Close(s) -} diff --git a/src/pkg/net/fd_bsd.go b/src/pkg/net/fd_bsd.go new file mode 100644 index 000000000..8bb1ae538 --- /dev/null +++ b/src/pkg/net/fd_bsd.go @@ -0,0 +1,123 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd netbsd openbsd + +// Waiting for FDs via kqueue/kevent. + +package net + +import ( + "os" + "syscall" +) + +type pollster struct { + kq int + eventbuf [10]syscall.Kevent_t + events []syscall.Kevent_t + + // An event buffer for AddFD/DelFD. + // Must hold pollServer lock. + kbuf [1]syscall.Kevent_t +} + +func newpollster() (p *pollster, err error) { + p = new(pollster) + if p.kq, err = syscall.Kqueue(); err != nil { + return nil, os.NewSyscallError("kqueue", err) + } + syscall.CloseOnExec(p.kq) + p.events = p.eventbuf[0:0] + return p, nil +} + +// First return value is whether the pollServer should be woken up. +// This version always returns false. +func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { + // pollServer is locked. + + var kmode int + if mode == 'r' { + kmode = syscall.EVFILT_READ + } else { + kmode = syscall.EVFILT_WRITE + } + ev := &p.kbuf[0] + // EV_ADD - add event to kqueue list + // EV_ONESHOT - delete the event the first time it triggers + flags := syscall.EV_ADD + if !repeat { + flags |= syscall.EV_ONESHOT + } + syscall.SetKevent(ev, fd, kmode, flags) + + n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) + if err != nil { + return false, os.NewSyscallError("kevent", err) + } + if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { + return false, os.NewSyscallError("kqueue phase error", err) + } + if ev.Data != 0 { + return false, syscall.Errno(int(ev.Data)) + } + return false, nil +} + +// Return value is whether the pollServer should be woken up. +// This version always returns false. +func (p *pollster) DelFD(fd int, mode int) bool { + // pollServer is locked. + + var kmode int + if mode == 'r' { + kmode = syscall.EVFILT_READ + } else { + kmode = syscall.EVFILT_WRITE + } + ev := &p.kbuf[0] + // EV_DELETE - delete event from kqueue list + syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE) + syscall.Kevent(p.kq, p.kbuf[:], nil, nil) + return false +} + +func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) { + var t *syscall.Timespec + for len(p.events) == 0 { + if nsec > 0 { + if t == nil { + t = new(syscall.Timespec) + } + *t = syscall.NsecToTimespec(nsec) + } + + s.Unlock() + n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) + s.Lock() + + if err != nil { + if err == syscall.EINTR { + continue + } + return -1, 0, os.NewSyscallError("kevent", err) + } + if n == 0 { + return -1, 0, nil + } + p.events = p.eventbuf[:n] + } + ev := &p.events[0] + p.events = p.events[1:] + fd = int(ev.Ident) + if ev.Filter == syscall.EVFILT_READ { + mode = 'r' + } else { + mode = 'w' + } + return fd, mode, nil +} + +func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) } diff --git a/src/pkg/net/fd_darwin.go b/src/pkg/net/fd_darwin.go index 3dd33edc2..382465ba6 100644 --- a/src/pkg/net/fd_darwin.go +++ b/src/pkg/net/fd_darwin.go @@ -32,6 +32,8 @@ func newpollster() (p *pollster, err error) { return p, nil } +// First return value is whether the pollServer should be woken up. +// This version always returns false. func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { // pollServer is locked. @@ -65,7 +67,9 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { return false, nil } -func (p *pollster) DelFD(fd int, mode int) { +// Return value is whether the pollServer should be woken up. +// This version always returns false. +func (p *pollster) DelFD(fd int, mode int) bool { // pollServer is locked. var kmode int @@ -80,6 +84,7 @@ func (p *pollster) DelFD(fd int, mode int) { // rather than waiting for real event syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE|syscall.EV_RECEIPT) syscall.Kevent(p.kq, p.kbuf[0:], p.kbuf[0:], nil) + return false } func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) { diff --git a/src/pkg/net/fd_freebsd.go b/src/pkg/net/fd_freebsd.go deleted file mode 100644 index 35d84c30e..000000000 --- a/src/pkg/net/fd_freebsd.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Waiting for FDs via kqueue/kevent. - -package net - -import ( - "os" - "syscall" -) - -type pollster struct { - kq int - eventbuf [10]syscall.Kevent_t - events []syscall.Kevent_t - - // An event buffer for AddFD/DelFD. - // Must hold pollServer lock. - kbuf [1]syscall.Kevent_t -} - -func newpollster() (p *pollster, err error) { - p = new(pollster) - if p.kq, err = syscall.Kqueue(); err != nil { - return nil, os.NewSyscallError("kqueue", err) - } - syscall.CloseOnExec(p.kq) - p.events = p.eventbuf[0:0] - return p, nil -} - -func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { - // pollServer is locked. - - var kmode int - if mode == 'r' { - kmode = syscall.EVFILT_READ - } else { - kmode = syscall.EVFILT_WRITE - } - ev := &p.kbuf[0] - // EV_ADD - add event to kqueue list - // EV_ONESHOT - delete the event the first time it triggers - flags := syscall.EV_ADD - if !repeat { - flags |= syscall.EV_ONESHOT - } - syscall.SetKevent(ev, fd, kmode, flags) - - n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) - if err != nil { - return false, os.NewSyscallError("kevent", err) - } - if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { - return false, os.NewSyscallError("kqueue phase error", err) - } - if ev.Data != 0 { - return false, syscall.Errno(int(ev.Data)) - } - return false, nil -} - -func (p *pollster) DelFD(fd int, mode int) { - // pollServer is locked. - - var kmode int - if mode == 'r' { - kmode = syscall.EVFILT_READ - } else { - kmode = syscall.EVFILT_WRITE - } - ev := &p.kbuf[0] - // EV_DELETE - delete event from kqueue list - syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE) - syscall.Kevent(p.kq, p.kbuf[:], nil, nil) -} - -func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) { - var t *syscall.Timespec - for len(p.events) == 0 { - if nsec > 0 { - if t == nil { - t = new(syscall.Timespec) - } - *t = syscall.NsecToTimespec(nsec) - } - - s.Unlock() - n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) - s.Lock() - - if err != nil { - if err == syscall.EINTR { - continue - } - return -1, 0, os.NewSyscallError("kevent", err) - } - if n == 0 { - return -1, 0, nil - } - p.events = p.eventbuf[:n] - } - ev := &p.events[0] - p.events = p.events[1:] - fd = int(ev.Ident) - if ev.Filter == syscall.EVFILT_READ { - mode = 'r' - } else { - mode = 'w' - } - return fd, mode, nil -} - -func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) } diff --git a/src/pkg/net/fd_linux.go b/src/pkg/net/fd_linux.go index 085e42307..03679196d 100644 --- a/src/pkg/net/fd_linux.go +++ b/src/pkg/net/fd_linux.go @@ -51,6 +51,8 @@ func newpollster() (p *pollster, err error) { return p, nil } +// First return value is whether the pollServer should be woken up. +// This version always returns false. func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { // pollServer is locked. @@ -114,7 +116,9 @@ func (p *pollster) StopWaiting(fd int, bits uint) { } } -func (p *pollster) DelFD(fd int, mode int) { +// Return value is whether the pollServer should be woken up. +// This version always returns false. +func (p *pollster) DelFD(fd int, mode int) bool { // pollServer is locked. if mode == 'r' { @@ -133,6 +137,7 @@ func (p *pollster) DelFD(fd int, mode int) { i++ } } + return false } func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) { diff --git a/src/pkg/net/fd_netbsd.go b/src/pkg/net/fd_netbsd.go deleted file mode 100644 index 35d84c30e..000000000 --- a/src/pkg/net/fd_netbsd.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Waiting for FDs via kqueue/kevent. - -package net - -import ( - "os" - "syscall" -) - -type pollster struct { - kq int - eventbuf [10]syscall.Kevent_t - events []syscall.Kevent_t - - // An event buffer for AddFD/DelFD. - // Must hold pollServer lock. - kbuf [1]syscall.Kevent_t -} - -func newpollster() (p *pollster, err error) { - p = new(pollster) - if p.kq, err = syscall.Kqueue(); err != nil { - return nil, os.NewSyscallError("kqueue", err) - } - syscall.CloseOnExec(p.kq) - p.events = p.eventbuf[0:0] - return p, nil -} - -func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { - // pollServer is locked. - - var kmode int - if mode == 'r' { - kmode = syscall.EVFILT_READ - } else { - kmode = syscall.EVFILT_WRITE - } - ev := &p.kbuf[0] - // EV_ADD - add event to kqueue list - // EV_ONESHOT - delete the event the first time it triggers - flags := syscall.EV_ADD - if !repeat { - flags |= syscall.EV_ONESHOT - } - syscall.SetKevent(ev, fd, kmode, flags) - - n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) - if err != nil { - return false, os.NewSyscallError("kevent", err) - } - if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { - return false, os.NewSyscallError("kqueue phase error", err) - } - if ev.Data != 0 { - return false, syscall.Errno(int(ev.Data)) - } - return false, nil -} - -func (p *pollster) DelFD(fd int, mode int) { - // pollServer is locked. - - var kmode int - if mode == 'r' { - kmode = syscall.EVFILT_READ - } else { - kmode = syscall.EVFILT_WRITE - } - ev := &p.kbuf[0] - // EV_DELETE - delete event from kqueue list - syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE) - syscall.Kevent(p.kq, p.kbuf[:], nil, nil) -} - -func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) { - var t *syscall.Timespec - for len(p.events) == 0 { - if nsec > 0 { - if t == nil { - t = new(syscall.Timespec) - } - *t = syscall.NsecToTimespec(nsec) - } - - s.Unlock() - n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) - s.Lock() - - if err != nil { - if err == syscall.EINTR { - continue - } - return -1, 0, os.NewSyscallError("kevent", err) - } - if n == 0 { - return -1, 0, nil - } - p.events = p.eventbuf[:n] - } - ev := &p.events[0] - p.events = p.events[1:] - fd = int(ev.Ident) - if ev.Filter == syscall.EVFILT_READ { - mode = 'r' - } else { - mode = 'w' - } - return fd, mode, nil -} - -func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) } diff --git a/src/pkg/net/fd_openbsd.go b/src/pkg/net/fd_openbsd.go deleted file mode 100644 index 35d84c30e..000000000 --- a/src/pkg/net/fd_openbsd.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Waiting for FDs via kqueue/kevent. - -package net - -import ( - "os" - "syscall" -) - -type pollster struct { - kq int - eventbuf [10]syscall.Kevent_t - events []syscall.Kevent_t - - // An event buffer for AddFD/DelFD. - // Must hold pollServer lock. - kbuf [1]syscall.Kevent_t -} - -func newpollster() (p *pollster, err error) { - p = new(pollster) - if p.kq, err = syscall.Kqueue(); err != nil { - return nil, os.NewSyscallError("kqueue", err) - } - syscall.CloseOnExec(p.kq) - p.events = p.eventbuf[0:0] - return p, nil -} - -func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { - // pollServer is locked. - - var kmode int - if mode == 'r' { - kmode = syscall.EVFILT_READ - } else { - kmode = syscall.EVFILT_WRITE - } - ev := &p.kbuf[0] - // EV_ADD - add event to kqueue list - // EV_ONESHOT - delete the event the first time it triggers - flags := syscall.EV_ADD - if !repeat { - flags |= syscall.EV_ONESHOT - } - syscall.SetKevent(ev, fd, kmode, flags) - - n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) - if err != nil { - return false, os.NewSyscallError("kevent", err) - } - if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { - return false, os.NewSyscallError("kqueue phase error", err) - } - if ev.Data != 0 { - return false, syscall.Errno(int(ev.Data)) - } - return false, nil -} - -func (p *pollster) DelFD(fd int, mode int) { - // pollServer is locked. - - var kmode int - if mode == 'r' { - kmode = syscall.EVFILT_READ - } else { - kmode = syscall.EVFILT_WRITE - } - ev := &p.kbuf[0] - // EV_DELETE - delete event from kqueue list - syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE) - syscall.Kevent(p.kq, p.kbuf[:], nil, nil) -} - -func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) { - var t *syscall.Timespec - for len(p.events) == 0 { - if nsec > 0 { - if t == nil { - t = new(syscall.Timespec) - } - *t = syscall.NsecToTimespec(nsec) - } - - s.Unlock() - n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) - s.Lock() - - if err != nil { - if err == syscall.EINTR { - continue - } - return -1, 0, os.NewSyscallError("kevent", err) - } - if n == 0 { - return -1, 0, nil - } - p.events = p.eventbuf[:n] - } - ev := &p.events[0] - p.events = p.events[1:] - fd = int(ev.Ident) - if ev.Filter == syscall.EVFILT_READ { - mode = 'r' - } else { - mode = 'w' - } - return fd, mode, nil -} - -func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) } diff --git a/src/pkg/net/fd_plan9.go b/src/pkg/net/fd_plan9.go new file mode 100644 index 000000000..169087999 --- /dev/null +++ b/src/pkg/net/fd_plan9.go @@ -0,0 +1,129 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "io" + "os" + "syscall" + "time" +) + +// Network file descritor. +type netFD struct { + proto, name, dir string + ctl, data *os.File + laddr, raddr Addr +} + +var canCancelIO = true // used for testing current package + +func sysInit() { +} + +func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) { + // On plan9, use the relatively inefficient + // goroutine-racing implementation. + return dialTimeoutRace(net, addr, timeout) +} + +func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD { + return &netFD{proto, name, "/net/" + proto + "/" + name, ctl, data, laddr, raddr} +} + +func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil } + +func (fd *netFD) Read(b []byte) (n int, err error) { + if !fd.ok() || fd.data == nil { + return 0, syscall.EINVAL + } + n, err = fd.data.Read(b) + if fd.proto == "udp" && err == io.EOF { + n = 0 + err = nil + } + return +} + +func (fd *netFD) Write(b []byte) (n int, err error) { + if !fd.ok() || fd.data == nil { + return 0, syscall.EINVAL + } + return fd.data.Write(b) +} + +func (fd *netFD) CloseRead() error { + if !fd.ok() { + return syscall.EINVAL + } + return syscall.EPLAN9 +} + +func (fd *netFD) CloseWrite() error { + if !fd.ok() { + return syscall.EINVAL + } + return syscall.EPLAN9 +} + +func (fd *netFD) Close() error { + if !fd.ok() { + return syscall.EINVAL + } + err := fd.ctl.Close() + if fd.data != nil { + if err1 := fd.data.Close(); err1 != nil && err == nil { + err = err1 + } + } + fd.ctl = nil + fd.data = nil + return err +} + +// This method is only called via Conn. +func (fd *netFD) dup() (*os.File, error) { + if !fd.ok() || fd.data == nil { + return nil, syscall.EINVAL + } + return fd.file(fd.data, fd.dir+"/data") +} + +func (l *TCPListener) dup() (*os.File, error) { + if !l.fd.ok() { + return nil, syscall.EINVAL + } + return l.fd.file(l.fd.ctl, l.fd.dir+"/ctl") +} + +func (fd *netFD) file(f *os.File, s string) (*os.File, error) { + syscall.ForkLock.RLock() + dfd, err := syscall.Dup(int(f.Fd()), -1) + syscall.ForkLock.RUnlock() + if err != nil { + return nil, &OpError{"dup", s, fd.laddr, err} + } + return os.NewFile(uintptr(dfd), s), nil +} + +func setDeadline(fd *netFD, t time.Time) error { + return syscall.EPLAN9 +} + +func setReadDeadline(fd *netFD, t time.Time) error { + return syscall.EPLAN9 +} + +func setWriteDeadline(fd *netFD, t time.Time) error { + return syscall.EPLAN9 +} + +func setReadBuffer(fd *netFD, bytes int) error { + return syscall.EPLAN9 +} + +func setWriteBuffer(fd *netFD, bytes int) error { + return syscall.EPLAN9 +} diff --git a/src/pkg/net/fd_posix_test.go b/src/pkg/net/fd_posix_test.go new file mode 100644 index 000000000..8be0335d6 --- /dev/null +++ b/src/pkg/net/fd_posix_test.go @@ -0,0 +1,57 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd windows + +package net + +import ( + "testing" + "time" +) + +var deadlineSetTimeTests = []struct { + input time.Time + expected int64 +}{ + {time.Time{}, 0}, + {time.Date(2009, 11, 10, 23, 00, 00, 00, time.UTC), 1257894000000000000}, // 2009-11-10 23:00:00 +0000 UTC +} + +func TestDeadlineSetTime(t *testing.T) { + for _, tt := range deadlineSetTimeTests { + var d deadline + d.setTime(tt.input) + actual := d.value() + expected := int64(0) + if !tt.input.IsZero() { + expected = tt.input.UnixNano() + } + if actual != expected { + t.Errorf("set/value failed: expected %v, actual %v", expected, actual) + } + } +} + +var deadlineExpiredTests = []struct { + deadline time.Time + expired bool +}{ + // note, times are relative to the start of the test run, not + // the start of TestDeadlineExpired + {time.Now().Add(5 * time.Minute), false}, + {time.Now().Add(-5 * time.Minute), true}, + {time.Time{}, false}, // no deadline set +} + +func TestDeadlineExpired(t *testing.T) { + for _, tt := range deadlineExpiredTests { + var d deadline + d.set(tt.deadline.UnixNano()) + expired := d.expired() + if expired != tt.expired { + t.Errorf("expire failed: expected %v, actual %v", tt.expired, expired) + } + } +} diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go new file mode 100644 index 000000000..0540df825 --- /dev/null +++ b/src/pkg/net/fd_unix.go @@ -0,0 +1,672 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package net + +import ( + "io" + "os" + "runtime" + "sync" + "syscall" + "time" +) + +// Network file descriptor. +type netFD struct { + // locking/lifetime of sysfd + sysmu sync.Mutex + sysref int + + // must lock both sysmu and pollserver to write + // can lock either to read + closing bool + + // immutable until Close + sysfd int + family int + sotype int + isConnected bool + sysfile *os.File + cr chan error + cw chan error + net string + laddr Addr + raddr Addr + + // serialize access to Read and Write methods + rio, wio sync.Mutex + + // read and write deadlines + rdeadline, wdeadline deadline + + // owned by fd wait server + ncr, ncw int + + // wait server + pollServer *pollServer +} + +// A pollServer helps FDs determine when to retry a non-blocking +// read or write after they get EAGAIN. When an FD needs to wait, +// call s.WaitRead() or s.WaitWrite() to pass the request to the poll server. +// When the pollServer finds that i/o on FD should be possible +// again, it will send on fd.cr/fd.cw to wake any waiting goroutines. +// +// To avoid races in closing, all fd operations are locked and +// refcounted. when netFD.Close() is called, it calls syscall.Shutdown +// and sets a closing flag. Only when the last reference is removed +// will the fd be closed. + +type pollServer struct { + pr, pw *os.File + poll *pollster // low-level OS hooks + sync.Mutex // controls pending and deadline + pending map[int]*netFD + deadline int64 // next deadline (nsec since 1970) +} + +func (s *pollServer) AddFD(fd *netFD, mode int) error { + s.Lock() + intfd := fd.sysfd + if intfd < 0 || fd.closing { + // fd closed underfoot + s.Unlock() + return errClosing + } + + var t int64 + key := intfd << 1 + if mode == 'r' { + fd.ncr++ + t = fd.rdeadline.value() + } else { + fd.ncw++ + key++ + t = fd.wdeadline.value() + } + s.pending[key] = fd + doWakeup := false + if t > 0 && (s.deadline == 0 || t < s.deadline) { + s.deadline = t + doWakeup = true + } + + wake, err := s.poll.AddFD(intfd, mode, false) + s.Unlock() + if err != nil { + return &OpError{"addfd", fd.net, fd.laddr, err} + } + if wake || doWakeup { + s.Wakeup() + } + return nil +} + +// Evict evicts fd from the pending list, unblocking +// any I/O running on fd. The caller must have locked +// pollserver. +// Return value is whether the pollServer should be woken up. +func (s *pollServer) Evict(fd *netFD) bool { + doWakeup := false + if s.pending[fd.sysfd<<1] == fd { + s.WakeFD(fd, 'r', errClosing) + if s.poll.DelFD(fd.sysfd, 'r') { + doWakeup = true + } + delete(s.pending, fd.sysfd<<1) + } + if s.pending[fd.sysfd<<1|1] == fd { + s.WakeFD(fd, 'w', errClosing) + if s.poll.DelFD(fd.sysfd, 'w') { + doWakeup = true + } + delete(s.pending, fd.sysfd<<1|1) + } + return doWakeup +} + +var wakeupbuf [1]byte + +func (s *pollServer) Wakeup() { s.pw.Write(wakeupbuf[0:]) } + +func (s *pollServer) LookupFD(fd int, mode int) *netFD { + key := fd << 1 + if mode == 'w' { + key++ + } + netfd, ok := s.pending[key] + if !ok { + return nil + } + delete(s.pending, key) + return netfd +} + +func (s *pollServer) WakeFD(fd *netFD, mode int, err error) { + if mode == 'r' { + for fd.ncr > 0 { + fd.ncr-- + fd.cr <- err + } + } else { + for fd.ncw > 0 { + fd.ncw-- + fd.cw <- err + } + } +} + +func (s *pollServer) CheckDeadlines() { + now := time.Now().UnixNano() + // TODO(rsc): This will need to be handled more efficiently, + // probably with a heap indexed by wakeup time. + + var nextDeadline int64 + for key, fd := range s.pending { + var t int64 + var mode int + if key&1 == 0 { + mode = 'r' + } else { + mode = 'w' + } + if mode == 'r' { + t = fd.rdeadline.value() + } else { + t = fd.wdeadline.value() + } + if t > 0 { + if t <= now { + delete(s.pending, key) + s.poll.DelFD(fd.sysfd, mode) + s.WakeFD(fd, mode, errTimeout) + } else if nextDeadline == 0 || t < nextDeadline { + nextDeadline = t + } + } + } + s.deadline = nextDeadline +} + +func (s *pollServer) Run() { + var scratch [100]byte + s.Lock() + defer s.Unlock() + for { + var timeout int64 // nsec to wait for or 0 for none + if s.deadline > 0 { + timeout = s.deadline - time.Now().UnixNano() + if timeout <= 0 { + s.CheckDeadlines() + continue + } + } + fd, mode, err := s.poll.WaitFD(s, timeout) + if err != nil { + print("pollServer WaitFD: ", err.Error(), "\n") + return + } + if fd < 0 { + // Timeout happened. + s.CheckDeadlines() + continue + } + if fd == int(s.pr.Fd()) { + // Drain our wakeup pipe (we could loop here, + // but it's unlikely that there are more than + // len(scratch) wakeup calls). + s.pr.Read(scratch[0:]) + s.CheckDeadlines() + } else { + netfd := s.LookupFD(fd, mode) + if netfd == nil { + // This can happen because the WaitFD runs without + // holding s's lock, so there might be a pending wakeup + // for an fd that has been evicted. No harm done. + continue + } + s.WakeFD(netfd, mode, nil) + } + } +} + +func (s *pollServer) WaitRead(fd *netFD) error { + err := s.AddFD(fd, 'r') + if err == nil { + err = <-fd.cr + } + return err +} + +func (s *pollServer) WaitWrite(fd *netFD) error { + err := s.AddFD(fd, 'w') + if err == nil { + err = <-fd.cw + } + return err +} + +// Network FD methods. +// Spread network FDs over several pollServers. + +var pollMaxN int +var pollservers []*pollServer +var startServersOnce []func() + +var canCancelIO = true // used for testing current package + +func sysInit() { + pollMaxN = runtime.NumCPU() + if pollMaxN > 8 { + pollMaxN = 8 // No improvement then. + } + pollservers = make([]*pollServer, pollMaxN) + startServersOnce = make([]func(), pollMaxN) + for i := 0; i < pollMaxN; i++ { + k := i + once := new(sync.Once) + startServersOnce[i] = func() { once.Do(func() { startServer(k) }) } + } +} + +func startServer(k int) { + p, err := newPollServer() + if err != nil { + panic(err) + } + pollservers[k] = p +} + +func server(fd int) *pollServer { + pollN := runtime.GOMAXPROCS(0) + if pollN > pollMaxN { + pollN = pollMaxN + } + k := fd % pollN + startServersOnce[k]() + return pollservers[k] +} + +func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) { + deadline := time.Now().Add(timeout) + ra, err := resolveAddr("dial", net, addr, deadline) + if err != nil { + return nil, err + } + return dial(net, addr, noLocalAddr, ra, deadline) +} + +func newFD(fd, family, sotype int, net string) (*netFD, error) { + netfd := &netFD{ + sysfd: fd, + family: family, + sotype: sotype, + net: net, + } + netfd.cr = make(chan error, 1) + netfd.cw = make(chan error, 1) + netfd.pollServer = server(fd) + return netfd, nil +} + +func (fd *netFD) setAddr(laddr, raddr Addr) { + fd.laddr = laddr + fd.raddr = raddr + fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net) +} + +func (fd *netFD) name() string { + var ls, rs string + if fd.laddr != nil { + ls = fd.laddr.String() + } + if fd.raddr != nil { + rs = fd.raddr.String() + } + return fd.net + ":" + ls + "->" + rs +} + +func (fd *netFD) connect(ra syscall.Sockaddr) error { + err := syscall.Connect(fd.sysfd, ra) + if err == syscall.EINPROGRESS { + if err = fd.pollServer.WaitWrite(fd); err != nil { + return err + } + var e int + e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) + if err != nil { + return os.NewSyscallError("getsockopt", err) + } + if e != 0 { + err = syscall.Errno(e) + } + } + return err +} + +// Add a reference to this fd. +// If closing==true, pollserver must be locked; mark the fd as closing. +// Returns an error if the fd cannot be used. +func (fd *netFD) incref(closing bool) error { + fd.sysmu.Lock() + if fd.closing { + fd.sysmu.Unlock() + return errClosing + } + fd.sysref++ + if closing { + fd.closing = true + } + fd.sysmu.Unlock() + return nil +} + +// Remove a reference to this FD and close if we've been asked to do so (and +// there are no references left. +func (fd *netFD) decref() { + fd.sysmu.Lock() + fd.sysref-- + if fd.closing && fd.sysref == 0 && fd.sysfile != nil { + fd.sysfile.Close() + fd.sysfile = nil + fd.sysfd = -1 + } + fd.sysmu.Unlock() +} + +func (fd *netFD) Close() error { + fd.pollServer.Lock() // needed for both fd.incref(true) and pollserver.Evict + if err := fd.incref(true); err != nil { + fd.pollServer.Unlock() + return err + } + // Unblock any I/O. Once it all unblocks and returns, + // so that it cannot be referring to fd.sysfd anymore, + // the final decref will close fd.sysfd. This should happen + // fairly quickly, since all the I/O is non-blocking, and any + // attempts to block in the pollserver will return errClosing. + doWakeup := fd.pollServer.Evict(fd) + fd.pollServer.Unlock() + fd.decref() + if doWakeup { + fd.pollServer.Wakeup() + } + return nil +} + +func (fd *netFD) shutdown(how int) error { + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + err := syscall.Shutdown(fd.sysfd, how) + if err != nil { + return &OpError{"shutdown", fd.net, fd.laddr, err} + } + return nil +} + +func (fd *netFD) CloseRead() error { + return fd.shutdown(syscall.SHUT_RD) +} + +func (fd *netFD) CloseWrite() error { + return fd.shutdown(syscall.SHUT_WR) +} + +func (fd *netFD) Read(p []byte) (n int, err error) { + fd.rio.Lock() + defer fd.rio.Unlock() + if err := fd.incref(false); err != nil { + return 0, err + } + defer fd.decref() + for { + if fd.rdeadline.expired() { + err = errTimeout + break + } + n, err = syscall.Read(int(fd.sysfd), p) + if err != nil { + n = 0 + if err == syscall.EAGAIN { + if err = fd.pollServer.WaitRead(fd); err == nil { + continue + } + } + } + err = chkReadErr(n, err, fd) + break + } + if err != nil && err != io.EOF { + err = &OpError{"read", fd.net, fd.raddr, err} + } + return +} + +func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { + fd.rio.Lock() + defer fd.rio.Unlock() + if err := fd.incref(false); err != nil { + return 0, nil, err + } + defer fd.decref() + for { + if fd.rdeadline.expired() { + err = errTimeout + break + } + n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) + if err != nil { + n = 0 + if err == syscall.EAGAIN { + if err = fd.pollServer.WaitRead(fd); err == nil { + continue + } + } + } + err = chkReadErr(n, err, fd) + break + } + if err != nil && err != io.EOF { + err = &OpError{"read", fd.net, fd.laddr, err} + } + return +} + +func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { + fd.rio.Lock() + defer fd.rio.Unlock() + if err := fd.incref(false); err != nil { + return 0, 0, 0, nil, err + } + defer fd.decref() + for { + if fd.rdeadline.expired() { + err = errTimeout + break + } + n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) + if err != nil { + // TODO(dfc) should n and oobn be set to 0 + if err == syscall.EAGAIN { + if err = fd.pollServer.WaitRead(fd); err == nil { + continue + } + } + } + err = chkReadErr(n, err, fd) + break + } + if err != nil && err != io.EOF { + err = &OpError{"read", fd.net, fd.laddr, err} + } + return +} + +func chkReadErr(n int, err error, fd *netFD) error { + if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM && fd.sotype != syscall.SOCK_RAW { + return io.EOF + } + return err +} + +func (fd *netFD) Write(p []byte) (nn int, err error) { + fd.wio.Lock() + defer fd.wio.Unlock() + if err := fd.incref(false); err != nil { + return 0, err + } + defer fd.decref() + for { + if fd.wdeadline.expired() { + err = errTimeout + break + } + var n int + n, err = syscall.Write(int(fd.sysfd), p[nn:]) + if n > 0 { + nn += n + } + if nn == len(p) { + break + } + if err == syscall.EAGAIN { + if err = fd.pollServer.WaitWrite(fd); err == nil { + continue + } + } + if err != nil { + n = 0 + break + } + if n == 0 { + err = io.ErrUnexpectedEOF + break + } + } + if err != nil { + err = &OpError{"write", fd.net, fd.raddr, err} + } + return nn, err +} + +func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { + fd.wio.Lock() + defer fd.wio.Unlock() + if err := fd.incref(false); err != nil { + return 0, err + } + defer fd.decref() + for { + if fd.wdeadline.expired() { + err = errTimeout + break + } + err = syscall.Sendto(fd.sysfd, p, 0, sa) + if err == syscall.EAGAIN { + if err = fd.pollServer.WaitWrite(fd); err == nil { + continue + } + } + break + } + if err == nil { + n = len(p) + } else { + err = &OpError{"write", fd.net, fd.raddr, err} + } + return +} + +func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { + fd.wio.Lock() + defer fd.wio.Unlock() + if err := fd.incref(false); err != nil { + return 0, 0, err + } + defer fd.decref() + for { + if fd.wdeadline.expired() { + err = errTimeout + break + } + err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) + if err == syscall.EAGAIN { + if err = fd.pollServer.WaitWrite(fd); err == nil { + continue + } + } + break + } + if err == nil { + n = len(p) + oobn = len(oob) + } else { + err = &OpError{"write", fd.net, fd.raddr, err} + } + return +} + +func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err error) { + if err := fd.incref(false); err != nil { + return nil, err + } + defer fd.decref() + + var s int + var rsa syscall.Sockaddr + for { + s, rsa, err = accept(fd.sysfd) + if err != nil { + if err == syscall.EAGAIN { + if err = fd.pollServer.WaitRead(fd); err == nil { + continue + } + } else if err == syscall.ECONNABORTED { + // This means that a socket on the listen queue was closed + // before we Accept()ed it; it's a silly error, so try again. + continue + } + return nil, &OpError{"accept", fd.net, fd.laddr, err} + } + break + } + + if netfd, err = newFD(s, fd.family, fd.sotype, fd.net); err != nil { + closesocket(s) + return nil, err + } + lsa, _ := syscall.Getsockname(netfd.sysfd) + netfd.setAddr(toAddr(lsa), toAddr(rsa)) + return netfd, nil +} + +func (fd *netFD) dup() (f *os.File, err error) { + syscall.ForkLock.RLock() + ns, err := syscall.Dup(fd.sysfd) + if err != nil { + syscall.ForkLock.RUnlock() + return nil, &OpError{"dup", fd.net, fd.laddr, err} + } + syscall.CloseOnExec(ns) + syscall.ForkLock.RUnlock() + + // We want blocking mode for the new fd, hence the double negative. + // This also puts the old fd into blocking mode, meaning that + // I/O will block the thread instead of letting us use the epoll server. + // Everything will still work, just with more threads. + if err = syscall.SetNonblock(ns, false); err != nil { + return nil, &OpError{"setnonblock", fd.net, fd.laddr, err} + } + + return os.NewFile(uintptr(ns), fd.name()), nil +} + +func closesocket(s int) error { + return syscall.Close(s) +} diff --git a/src/pkg/net/fd_unix_test.go b/src/pkg/net/fd_unix_test.go new file mode 100644 index 000000000..664ef1bf1 --- /dev/null +++ b/src/pkg/net/fd_unix_test.go @@ -0,0 +1,58 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package net + +import ( + "io" + "syscall" + "testing" +) + +var chkReadErrTests = []struct { + n int + err error + fd *netFD + expected error +}{ + + {100, nil, &netFD{sotype: syscall.SOCK_STREAM}, nil}, + {100, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, + {100, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing}, + {0, nil, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, + {0, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, + {0, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing}, + + {100, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil}, + {100, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF}, + {100, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing}, + {0, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil}, + {0, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF}, + {0, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing}, + + {100, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, nil}, + {100, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, + {100, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing}, + {0, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, + {0, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, + {0, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing}, + + {100, nil, &netFD{sotype: syscall.SOCK_RAW}, nil}, + {100, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF}, + {100, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing}, + {0, nil, &netFD{sotype: syscall.SOCK_RAW}, nil}, + {0, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF}, + {0, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing}, +} + +func TestChkReadErr(t *testing.T) { + for _, tt := range chkReadErrTests { + actual := chkReadErr(tt.n, tt.err, tt.fd) + if actual != tt.expected { + t.Errorf("chkReadError(%v, %v, %v): expected %v, actual %v", tt.n, tt.err, tt.fd.sotype, tt.expected, actual) + } + } +} diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go index 45f5c2d88..0e331b44d 100644 --- a/src/pkg/net/fd_windows.go +++ b/src/pkg/net/fd_windows.go @@ -17,19 +17,58 @@ import ( var initErr error -func init() { +// CancelIo Windows API cancels all outstanding IO for a particular +// socket on current thread. To overcome that limitation, we run +// special goroutine, locked to OS single thread, that both starts +// and cancels IO. It means, there are 2 unavoidable thread switches +// for every IO. +// Some newer versions of Windows has new CancelIoEx API, that does +// not have that limitation and can be used from any thread. This +// package uses CancelIoEx API, if present, otherwise it fallback +// to CancelIo. + +var canCancelIO bool // determines if CancelIoEx API is present + +func sysInit() { var d syscall.WSAData e := syscall.WSAStartup(uint32(0x202), &d) if e != nil { initErr = os.NewSyscallError("WSAStartup", e) } + canCancelIO = syscall.LoadCancelIoEx() == nil + if syscall.LoadGetAddrInfo() == nil { + lookupPort = newLookupPort + lookupIP = newLookupIP + } } func closesocket(s syscall.Handle) error { return syscall.Closesocket(s) } -// Interface for all io operations. +func canUseConnectEx(net string) bool { + if net == "udp" || net == "udp4" || net == "udp6" { + // ConnectEx windows API does not support connectionless sockets. + return false + } + return syscall.LoadConnectEx() == nil +} + +func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) { + if !canUseConnectEx(net) { + // Use the relatively inefficient goroutine-racing + // implementation of DialTimeout. + return dialTimeoutRace(net, addr, timeout) + } + deadline := time.Now().Add(timeout) + ra, err := resolveAddr("dial", net, addr, deadline) + if err != nil { + return nil, err + } + return dial(net, addr, noLocalAddr, ra, deadline) +} + +// Interface for all IO operations. type anOpIface interface { Op() *anOp Name() string @@ -42,7 +81,7 @@ type ioResult struct { err error } -// anOp implements functionality common to all io operations. +// anOp implements functionality common to all IO operations. type anOp struct { // Used by IOCP interface, it must be first field // of the struct, as our code rely on it. @@ -75,7 +114,7 @@ func (o *anOp) Op() *anOp { return o } -// bufOp is used by io operations that read / write +// bufOp is used by IO operations that read / write // data from / to client buffer. type bufOp struct { anOp @@ -92,7 +131,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte, mode int) { } } -// resultSrv will retrieve all io completion results from +// resultSrv will retrieve all IO completion results from // iocp and send them to the correspondent waiting client // goroutine via channel supplied in the request. type resultSrv struct { @@ -107,7 +146,7 @@ func (s *resultSrv) Run() { r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE) switch { case r.err == nil: - // Dequeued successfully completed io packet. + // Dequeued successfully completed IO packet. case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil: // Wait has timed out (should not happen now, but might be used in the future). panic("GetQueuedCompletionStatus timed out") @@ -115,22 +154,23 @@ func (s *resultSrv) Run() { // Failed to dequeue anything -> report the error. panic("GetQueuedCompletionStatus failed " + r.err.Error()) default: - // Dequeued failed io packet. + // Dequeued failed IO packet. } (*anOp)(unsafe.Pointer(o)).resultc <- r } } -// ioSrv executes net io requests. +// ioSrv executes net IO requests. type ioSrv struct { - submchan chan anOpIface // submit io requests - canchan chan anOpIface // cancel io requests + submchan chan anOpIface // submit IO requests + canchan chan anOpIface // cancel IO requests } -// ProcessRemoteIO will execute submit io requests on behalf +// ProcessRemoteIO will execute submit IO requests on behalf // of other goroutines, all on a single os thread, so it can // cancel them later. Results of all operations will be sent // back to their requesters via channel supplied in request. +// It is used only when the CancelIoEx API is unavailable. func (s *ioSrv) ProcessRemoteIO() { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -144,20 +184,30 @@ func (s *ioSrv) ProcessRemoteIO() { } } -// ExecIO executes a single io operation. It either executes it -// inline, or, if a deadline is employed, passes the request onto +// ExecIO executes a single IO operation oi. It submits and cancels +// IO in the current thread for systems where Windows CancelIoEx API +// is available. Alternatively, it passes the request onto // a special goroutine and waits for completion or cancels request. // deadline is unix nanos. func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) { var err error o := oi.Op() + // Calculate timeout delta. + var delta int64 if deadline != 0 { + delta = deadline - time.Now().UnixNano() + if delta <= 0 { + return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, errTimeout} + } + } + // Start IO. + if canCancelIO { + err = oi.Submit() + } else { // Send request to a special dedicated thread, - // so it can stop the io with CancelIO later. + // so it can stop the IO with CancelIO later. s.submchan <- oi err = <-o.errnoc - } else { - err = oi.Submit() } switch err { case nil: @@ -168,27 +218,46 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) { default: return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err} } + // Setup timer, if deadline is given. + var timer <-chan time.Time + if delta > 0 { + t := time.NewTimer(time.Duration(delta) * time.Nanosecond) + defer t.Stop() + timer = t.C + } // Wait for our request to complete. var r ioResult - if deadline != 0 { - dt := deadline - time.Now().UnixNano() - if dt < 1 { - dt = 1 - } - timer := time.NewTimer(time.Duration(dt) * time.Nanosecond) - defer timer.Stop() - select { - case r = <-o.resultc: - case <-timer.C: + var cancelled, timeout bool + select { + case r = <-o.resultc: + case <-timer: + cancelled = true + timeout = true + case <-o.fd.closec: + cancelled = true + } + if cancelled { + // Cancel it. + if canCancelIO { + err := syscall.CancelIoEx(syscall.Handle(o.Op().fd.sysfd), &o.o) + // Assuming ERROR_NOT_FOUND is returned, if IO is completed. + if err != nil && err != syscall.ERROR_NOT_FOUND { + // TODO(brainman): maybe do something else, but panic. + panic(err) + } + } else { s.canchan <- oi <-o.errnoc - r = <-o.resultc - if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled - r.err = syscall.EWOULDBLOCK - } } - } else { + // Wait for IO to be canceled or complete successfully. r = <-o.resultc + if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled + if timeout { + r.err = errTimeout + } else { + r.err = errClosing + } + } } if r.err != nil { err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err} @@ -211,9 +280,13 @@ func startServer() { go resultsrv.Run() iosrv = new(ioSrv) - iosrv.submchan = make(chan anOpIface) - iosrv.canchan = make(chan anOpIface) - go iosrv.ProcessRemoteIO() + if !canCancelIO { + // Only CancelIo API is available. Lets start special goroutine + // locked to an OS thread, that both starts and cancels IO. + iosrv.submchan = make(chan anOpIface) + iosrv.canchan = make(chan anOpIface) + go iosrv.ProcessRemoteIO() + } } // Network file descriptor. @@ -233,12 +306,13 @@ type netFD struct { raddr Addr resultc [2]chan ioResult // read/write completion results errnoc [2]chan error // read/write submit or cancel operation errors + closec chan bool // used by Close to cancel pending IO + + // serialize access to Read and Write methods + rio, wio sync.Mutex - // owned by client - rdeadline int64 - rio sync.Mutex - wdeadline int64 - wio sync.Mutex + // read and write deadlines + rdeadline, wdeadline deadline } func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD { @@ -247,8 +321,8 @@ func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD { family: family, sotype: sotype, net: net, + closec: make(chan bool), } - runtime.SetFinalizer(netfd, (*netFD).Close) return netfd } @@ -267,13 +341,52 @@ func newFD(fd syscall.Handle, family, proto int, net string) (*netFD, error) { func (fd *netFD) setAddr(laddr, raddr Addr) { fd.laddr = laddr fd.raddr = raddr + runtime.SetFinalizer(fd, (*netFD).closesocket) } -func (fd *netFD) connect(ra syscall.Sockaddr) error { - return syscall.Connect(fd.sysfd, ra) +// Make new connection. + +type connectOp struct { + anOp + ra syscall.Sockaddr +} + +func (o *connectOp) Submit() error { + return syscall.ConnectEx(o.fd.sysfd, o.ra, nil, 0, nil, &o.o) } -var errClosing = errors.New("use of closed network connection") +func (o *connectOp) Name() string { + return "ConnectEx" +} + +func (fd *netFD) connect(ra syscall.Sockaddr) error { + if !canUseConnectEx(fd.net) { + return syscall.Connect(fd.sysfd, ra) + } + // ConnectEx windows API requires an unconnected, previously bound socket. + var la syscall.Sockaddr + switch ra.(type) { + case *syscall.SockaddrInet4: + la = &syscall.SockaddrInet4{} + case *syscall.SockaddrInet6: + la = &syscall.SockaddrInet6{} + default: + panic("unexpected type in connect") + } + if err := syscall.Bind(fd.sysfd, la); err != nil { + return err + } + // Call ConnectEx API. + var o connectOp + o.Init(fd, 'w') + o.ra = ra + _, err := iosrv.ExecIO(&o, fd.wdeadline.value()) + if err != nil { + return err + } + // Refresh socket properties. + return syscall.Setsockopt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) +} // Add a reference to this fd. // If closing==true, mark the fd as closing. @@ -299,24 +412,12 @@ func (fd *netFD) incref(closing bool) error { // Remove a reference to this FD and close if we've been asked to do so (and // there are no references left. func (fd *netFD) decref() { + if fd == nil { + return + } fd.sysmu.Lock() fd.sysref-- - // NOTE(rsc): On Unix we check fd.sysref == 0 here before closing, - // but on Windows we have no way to wake up the blocked I/O other - // than closing the socket (or calling Shutdown, which breaks other - // programs that might have a reference to the socket). So there is - // a small race here that we might close fd.sysfd and then some other - // goroutine might start a read of fd.sysfd (having read it before we - // write InvalidHandle to it), which might refer to some other file - // if the specific handle value gets reused. I think handle values on - // Windows are not reused as aggressively as file descriptors on Unix, - // so this might be tolerable. - if fd.closing && fd.sysfd != syscall.InvalidHandle { - // In case the user has set linger, switch to blocking mode so - // the close blocks. As long as this doesn't happen often, we - // can handle the extra OS processes. Otherwise we'll need to - // use the resultsrv for Close too. Sigh. - syscall.SetNonblock(fd.sysfd, false) + if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle { closesocket(fd.sysfd) fd.sysfd = syscall.InvalidHandle // no need for a finalizer anymore @@ -329,14 +430,22 @@ func (fd *netFD) Close() error { if err := fd.incref(true); err != nil { return err } - fd.decref() + defer fd.decref() + // unblock pending reader and writer + close(fd.closec) + // wait for both reader and writer to exit + fd.rio.Lock() + defer fd.rio.Unlock() + fd.wio.Lock() + defer fd.wio.Unlock() return nil } func (fd *netFD) shutdown(how int) error { - if fd == nil || fd.sysfd == syscall.InvalidHandle { - return syscall.EINVAL + if err := fd.incref(false); err != nil { + return err } + defer fd.decref() err := syscall.Shutdown(fd.sysfd, how) if err != nil { return &OpError{"shutdown", fd.net, fd.laddr, err} @@ -352,6 +461,10 @@ func (fd *netFD) CloseWrite() error { return fd.shutdown(syscall.SHUT_WR) } +func (fd *netFD) closesocket() error { + return closesocket(fd.sysfd) +} + // Read from network. type readOp struct { @@ -368,21 +481,15 @@ func (o *readOp) Name() string { } func (fd *netFD) Read(buf []byte) (int, error) { - if fd == nil { - return 0, syscall.EINVAL - } - fd.rio.Lock() - defer fd.rio.Unlock() if err := fd.incref(false); err != nil { return 0, err } defer fd.decref() - if fd.sysfd == syscall.InvalidHandle { - return 0, syscall.EINVAL - } + fd.rio.Lock() + defer fd.rio.Unlock() var o readOp o.Init(fd, buf, 'r') - n, err := iosrv.ExecIO(&o, fd.rdeadline) + n, err := iosrv.ExecIO(&o, fd.rdeadline.value()) if err == nil && n == 0 { err = io.EOF } @@ -407,22 +514,19 @@ func (o *readFromOp) Name() string { } func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) { - if fd == nil { - return 0, nil, syscall.EINVAL - } if len(buf) == 0 { return 0, nil, nil } - fd.rio.Lock() - defer fd.rio.Unlock() if err := fd.incref(false); err != nil { return 0, nil, err } defer fd.decref() + fd.rio.Lock() + defer fd.rio.Unlock() var o readFromOp o.Init(fd, buf, 'r') o.rsan = int32(unsafe.Sizeof(o.rsa)) - n, err = iosrv.ExecIO(&o, fd.rdeadline) + n, err = iosrv.ExecIO(&o, fd.rdeadline.value()) if err != nil { return 0, nil, err } @@ -446,18 +550,15 @@ func (o *writeOp) Name() string { } func (fd *netFD) Write(buf []byte) (int, error) { - if fd == nil { - return 0, syscall.EINVAL - } - fd.wio.Lock() - defer fd.wio.Unlock() if err := fd.incref(false); err != nil { return 0, err } defer fd.decref() + fd.wio.Lock() + defer fd.wio.Unlock() var o writeOp o.Init(fd, buf, 'w') - return iosrv.ExecIO(&o, fd.wdeadline) + return iosrv.ExecIO(&o, fd.wdeadline.value()) } // WriteTo to network. @@ -477,25 +578,19 @@ func (o *writeToOp) Name() string { } func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { - if fd == nil { - return 0, syscall.EINVAL - } if len(buf) == 0 { return 0, nil } - fd.wio.Lock() - defer fd.wio.Unlock() if err := fd.incref(false); err != nil { return 0, err } defer fd.decref() - if fd.sysfd == syscall.InvalidHandle { - return 0, syscall.EINVAL - } + fd.wio.Lock() + defer fd.wio.Unlock() var o writeToOp o.Init(fd, buf, 'w') o.sa = sa - return iosrv.ExecIO(&o, fd.wdeadline) + return iosrv.ExecIO(&o, fd.wdeadline.value()) } // Accept new network connections. @@ -524,19 +619,15 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { defer fd.decref() // Get new socket. - // See ../syscall/exec.go for description of ForkLock. - syscall.ForkLock.RLock() - s, err := syscall.Socket(fd.family, fd.sotype, 0) + s, err := sysSocket(fd.family, fd.sotype, 0) if err != nil { - syscall.ForkLock.RUnlock() - return nil, err + return nil, &OpError{"socket", fd.net, fd.laddr, err} } - syscall.CloseOnExec(s) - syscall.ForkLock.RUnlock() // Associate our new socket with IOCP. onceStartServer.Do(startServer) if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil { + closesocket(s) return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err} } @@ -544,7 +635,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { var o acceptOp o.Init(fd, 'r') o.newsock = s - _, err = iosrv.ExecIO(&o, 0) + _, err = iosrv.ExecIO(&o, fd.rdeadline.value()) if err != nil { closesocket(s) return nil, err @@ -554,7 +645,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) if err != nil { closesocket(s) - return nil, err + return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err} } // Get local and peer addr out of AcceptEx buffer. diff --git a/src/pkg/net/file.go b/src/pkg/net/file.go deleted file mode 100644 index fc6c6fad8..000000000 --- a/src/pkg/net/file.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd - -package net - -import ( - "os" - "syscall" -) - -func newFileFD(f *os.File) (*netFD, error) { - fd, err := syscall.Dup(int(f.Fd())) - if err != nil { - return nil, os.NewSyscallError("dup", err) - } - - proto, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - - family := syscall.AF_UNSPEC - toAddr := sockaddrToTCP - sa, _ := syscall.Getsockname(fd) - switch sa.(type) { - default: - closesocket(fd) - return nil, syscall.EINVAL - case *syscall.SockaddrInet4: - family = syscall.AF_INET - if proto == syscall.SOCK_DGRAM { - toAddr = sockaddrToUDP - } else if proto == syscall.SOCK_RAW { - toAddr = sockaddrToIP - } - case *syscall.SockaddrInet6: - family = syscall.AF_INET6 - if proto == syscall.SOCK_DGRAM { - toAddr = sockaddrToUDP - } else if proto == syscall.SOCK_RAW { - toAddr = sockaddrToIP - } - case *syscall.SockaddrUnix: - family = syscall.AF_UNIX - toAddr = sockaddrToUnix - if proto == syscall.SOCK_DGRAM { - toAddr = sockaddrToUnixgram - } else if proto == syscall.SOCK_SEQPACKET { - toAddr = sockaddrToUnixpacket - } - } - laddr := toAddr(sa) - sa, _ = syscall.Getpeername(fd) - raddr := toAddr(sa) - - netfd, err := newFD(fd, family, proto, laddr.Network()) - if err != nil { - return nil, err - } - netfd.setAddr(laddr, raddr) - return netfd, nil -} - -// FileConn returns a copy of the network connection corresponding to -// the open file f. It is the caller's responsibility to close f when -// finished. Closing c does not affect f, and closing f does not -// affect c. -func FileConn(f *os.File) (c Conn, err error) { - fd, err := newFileFD(f) - if err != nil { - return nil, err - } - switch fd.laddr.(type) { - case *TCPAddr: - return newTCPConn(fd), nil - case *UDPAddr: - return newUDPConn(fd), nil - case *UnixAddr: - return newUnixConn(fd), nil - case *IPAddr: - return newIPConn(fd), nil - } - fd.Close() - return nil, syscall.EINVAL -} - -// FileListener returns a copy of the network listener corresponding -// to the open file f. It is the caller's responsibility to close l -// when finished. Closing l does not affect f, and closing f does not -// affect l. -func FileListener(f *os.File) (l Listener, err error) { - fd, err := newFileFD(f) - if err != nil { - return nil, err - } - switch laddr := fd.laddr.(type) { - case *TCPAddr: - return &TCPListener{fd}, nil - case *UnixAddr: - return &UnixListener{fd, laddr.Name}, nil - } - fd.Close() - return nil, syscall.EINVAL -} - -// FilePacketConn returns a copy of the packet network connection -// corresponding to the open file f. It is the caller's -// responsibility to close f when finished. Closing c does not affect -// f, and closing f does not affect c. -func FilePacketConn(f *os.File) (c PacketConn, err error) { - fd, err := newFileFD(f) - if err != nil { - return nil, err - } - switch fd.laddr.(type) { - case *UDPAddr: - return newUDPConn(fd), nil - case *UnixAddr: - return newUnixConn(fd), nil - } - fd.Close() - return nil, syscall.EINVAL -} diff --git a/src/pkg/net/file_plan9.go b/src/pkg/net/file_plan9.go index 04f7ee040..f6ee1c29e 100644 --- a/src/pkg/net/file_plan9.go +++ b/src/pkg/net/file_plan9.go @@ -5,24 +5,147 @@ package net import ( + "errors" + "io" "os" "syscall" ) +func (fd *netFD) status(ln int) (string, error) { + if !fd.ok() { + return "", syscall.EINVAL + } + + status, err := os.Open(fd.dir + "/status") + if err != nil { + return "", err + } + defer status.Close() + buf := make([]byte, ln) + n, err := io.ReadFull(status, buf[:]) + if err != nil { + return "", err + } + return string(buf[:n]), nil +} + +func newFileFD(f *os.File) (net *netFD, err error) { + var ctl *os.File + close := func(fd int) { + if err != nil { + syscall.Close(fd) + } + } + + path, err := syscall.Fd2path(int(f.Fd())) + if err != nil { + return nil, os.NewSyscallError("fd2path", err) + } + comp := splitAtBytes(path, "/") + n := len(comp) + if n < 3 || comp[0] != "net" { + return nil, syscall.EPLAN9 + } + + name := comp[2] + switch file := comp[n-1]; file { + case "ctl", "clone": + syscall.ForkLock.RLock() + fd, err := syscall.Dup(int(f.Fd()), -1) + syscall.ForkLock.RUnlock() + if err != nil { + return nil, os.NewSyscallError("dup", err) + } + defer close(fd) + + dir := "/net/" + comp[n-2] + ctl = os.NewFile(uintptr(fd), dir+"/"+file) + ctl.Seek(0, 0) + var buf [16]byte + n, err := ctl.Read(buf[:]) + if err != nil { + return nil, err + } + name = string(buf[:n]) + default: + if len(comp) < 4 { + return nil, errors.New("could not find control file for connection") + } + dir := "/net/" + comp[1] + "/" + name + ctl, err = os.OpenFile(dir+"/ctl", os.O_RDWR, 0) + if err != nil { + return nil, err + } + defer close(int(ctl.Fd())) + } + dir := "/net/" + comp[1] + "/" + name + laddr, err := readPlan9Addr(comp[1], dir+"/local") + if err != nil { + return nil, err + } + return newFD(comp[1], name, ctl, nil, laddr, nil), nil +} + +func newFileConn(f *os.File) (c Conn, err error) { + fd, err := newFileFD(f) + if err != nil { + return nil, err + } + if !fd.ok() { + return nil, syscall.EINVAL + } + + fd.data, err = os.OpenFile(fd.dir+"/data", os.O_RDWR, 0) + if err != nil { + return nil, err + } + + switch fd.laddr.(type) { + case *TCPAddr: + return newTCPConn(fd), nil + case *UDPAddr: + return newUDPConn(fd), nil + } + return nil, syscall.EPLAN9 +} + +func newFileListener(f *os.File) (l Listener, err error) { + fd, err := newFileFD(f) + if err != nil { + return nil, err + } + switch fd.laddr.(type) { + case *TCPAddr: + default: + return nil, syscall.EPLAN9 + } + + // check that file corresponds to a listener + s, err := fd.status(len("Listen")) + if err != nil { + return nil, err + } + if s != "Listen" { + return nil, errors.New("file does not represent a listener") + } + + return &TCPListener{fd}, nil +} + // FileConn returns a copy of the network connection corresponding to // the open file f. It is the caller's responsibility to close f when // finished. Closing c does not affect f, and closing f does not // affect c. func FileConn(f *os.File) (c Conn, err error) { - return nil, syscall.EPLAN9 + return newFileConn(f) } // FileListener returns a copy of the network listener corresponding // to the open file f. It is the caller's responsibility to close l -// when finished. Closing c does not affect l, and closing l does not -// affect c. +// when finished. Closing l does not affect f, and closing f does not +// affect l. func FileListener(f *os.File) (l Listener, err error) { - return nil, syscall.EPLAN9 + return newFileListener(f) } // FilePacketConn returns a copy of the packet network connection diff --git a/src/pkg/net/file_test.go b/src/pkg/net/file_test.go index 95c0b6699..acaf18851 100644 --- a/src/pkg/net/file_test.go +++ b/src/pkg/net/file_test.go @@ -89,9 +89,8 @@ var fileListenerTests = []struct { func TestFileListener(t *testing.T) { switch runtime.GOOS { - case "plan9", "windows": - t.Logf("skipping test on %q", runtime.GOOS) - return + case "windows": + t.Skipf("skipping test on %q", runtime.GOOS) } for _, tt := range fileListenerTests { @@ -181,8 +180,7 @@ var filePacketConnTests = []struct { func TestFilePacketConn(t *testing.T) { switch runtime.GOOS { case "plan9", "windows": - t.Logf("skipping test on %q", runtime.GOOS) - return + t.Skipf("skipping test on %q", runtime.GOOS) } for _, tt := range filePacketConnTests { diff --git a/src/pkg/net/file_unix.go b/src/pkg/net/file_unix.go new file mode 100644 index 000000000..4c8403e40 --- /dev/null +++ b/src/pkg/net/file_unix.go @@ -0,0 +1,136 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package net + +import ( + "os" + "syscall" +) + +func newFileFD(f *os.File) (*netFD, error) { + syscall.ForkLock.RLock() + fd, err := syscall.Dup(int(f.Fd())) + if err != nil { + syscall.ForkLock.RUnlock() + return nil, os.NewSyscallError("dup", err) + } + syscall.CloseOnExec(fd) + syscall.ForkLock.RUnlock() + if err = syscall.SetNonblock(fd, true); err != nil { + closesocket(fd) + return nil, err + } + + sotype, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE) + if err != nil { + closesocket(fd) + return nil, os.NewSyscallError("getsockopt", err) + } + + family := syscall.AF_UNSPEC + toAddr := sockaddrToTCP + lsa, _ := syscall.Getsockname(fd) + switch lsa.(type) { + default: + closesocket(fd) + return nil, syscall.EINVAL + case *syscall.SockaddrInet4: + family = syscall.AF_INET + if sotype == syscall.SOCK_DGRAM { + toAddr = sockaddrToUDP + } else if sotype == syscall.SOCK_RAW { + toAddr = sockaddrToIP + } + case *syscall.SockaddrInet6: + family = syscall.AF_INET6 + if sotype == syscall.SOCK_DGRAM { + toAddr = sockaddrToUDP + } else if sotype == syscall.SOCK_RAW { + toAddr = sockaddrToIP + } + case *syscall.SockaddrUnix: + family = syscall.AF_UNIX + toAddr = sockaddrToUnix + if sotype == syscall.SOCK_DGRAM { + toAddr = sockaddrToUnixgram + } else if sotype == syscall.SOCK_SEQPACKET { + toAddr = sockaddrToUnixpacket + } + } + laddr := toAddr(lsa) + rsa, _ := syscall.Getpeername(fd) + raddr := toAddr(rsa) + + netfd, err := newFD(fd, family, sotype, laddr.Network()) + if err != nil { + closesocket(fd) + return nil, err + } + netfd.setAddr(laddr, raddr) + return netfd, nil +} + +// FileConn returns a copy of the network connection corresponding to +// the open file f. It is the caller's responsibility to close f when +// finished. Closing c does not affect f, and closing f does not +// affect c. +func FileConn(f *os.File) (c Conn, err error) { + fd, err := newFileFD(f) + if err != nil { + return nil, err + } + switch fd.laddr.(type) { + case *TCPAddr: + return newTCPConn(fd), nil + case *UDPAddr: + return newUDPConn(fd), nil + case *IPAddr: + return newIPConn(fd), nil + case *UnixAddr: + return newUnixConn(fd), nil + } + fd.Close() + return nil, syscall.EINVAL +} + +// FileListener returns a copy of the network listener corresponding +// to the open file f. It is the caller's responsibility to close l +// when finished. Closing l does not affect f, and closing f does not +// affect l. +func FileListener(f *os.File) (l Listener, err error) { + fd, err := newFileFD(f) + if err != nil { + return nil, err + } + switch laddr := fd.laddr.(type) { + case *TCPAddr: + return &TCPListener{fd}, nil + case *UnixAddr: + return &UnixListener{fd, laddr.Name}, nil + } + fd.Close() + return nil, syscall.EINVAL +} + +// FilePacketConn returns a copy of the packet network connection +// corresponding to the open file f. It is the caller's +// responsibility to close f when finished. Closing c does not affect +// f, and closing f does not affect c. +func FilePacketConn(f *os.File) (c PacketConn, err error) { + fd, err := newFileFD(f) + if err != nil { + return nil, err + } + switch fd.laddr.(type) { + case *UDPAddr: + return newUDPConn(fd), nil + case *UnixAddr: + return newUnixConn(fd), nil + } + fd.Close() + return nil, syscall.EINVAL +} diff --git a/src/pkg/net/http/cgi/child.go b/src/pkg/net/http/cgi/child.go index 1ba7bec5f..100b8b777 100644 --- a/src/pkg/net/http/cgi/child.go +++ b/src/pkg/net/http/cgi/child.go @@ -91,10 +91,19 @@ func RequestFromMap(params map[string]string) (*http.Request, error) { // TODO: cookies. parsing them isn't exported, though. + uriStr := params["REQUEST_URI"] + if uriStr == "" { + // Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING. + uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"] + s := params["QUERY_STRING"] + if s != "" { + uriStr += "?" + s + } + } if r.Host != "" { // Hostname is provided, so we can reasonably construct a URL, // even if we have to assume 'http' for the scheme. - rawurl := "http://" + r.Host + params["REQUEST_URI"] + rawurl := "http://" + r.Host + uriStr url, err := url.Parse(rawurl) if err != nil { return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl) @@ -104,7 +113,6 @@ func RequestFromMap(params map[string]string) (*http.Request, error) { // Fallback logic if we don't have a Host header or the URL // failed to parse if r.URL == nil { - uriStr := params["REQUEST_URI"] url, err := url.Parse(uriStr) if err != nil { return nil, errors.New("cgi: failed to parse REQUEST_URI into a URL: " + uriStr) diff --git a/src/pkg/net/http/cgi/child_test.go b/src/pkg/net/http/cgi/child_test.go index ec53ab851..74e068014 100644 --- a/src/pkg/net/http/cgi/child_test.go +++ b/src/pkg/net/http/cgi/child_test.go @@ -82,6 +82,28 @@ func TestRequestWithoutHost(t *testing.T) { t.Fatalf("unexpected nil URL") } if g, e := req.URL.String(), "/path?a=b"; e != g { - t.Errorf("expected URL %q; got %q", e, g) + t.Errorf("URL = %q; want %q", g, e) + } +} + +func TestRequestWithoutRequestURI(t *testing.T) { + env := map[string]string{ + "SERVER_PROTOCOL": "HTTP/1.1", + "HTTP_HOST": "example.com", + "REQUEST_METHOD": "GET", + "SCRIPT_NAME": "/dir/scriptname", + "PATH_INFO": "/p1/p2", + "QUERY_STRING": "a=1&b=2", + "CONTENT_LENGTH": "123", + } + req, err := RequestFromMap(env) + if err != nil { + t.Fatalf("RequestFromMap: %v", err) + } + if req.URL == nil { + t.Fatalf("unexpected nil URL") + } + if g, e := req.URL.String(), "http://example.com/dir/scriptname/p1/p2?a=1&b=2"; e != g { + t.Errorf("URL = %q; want %q", g, e) } } diff --git a/src/pkg/net/http/cgi/host_test.go b/src/pkg/net/http/cgi/host_test.go index 4db3d850c..8c16e6897 100644 --- a/src/pkg/net/http/cgi/host_test.go +++ b/src/pkg/net/http/cgi/host_test.go @@ -19,7 +19,6 @@ import ( "runtime" "strconv" "strings" - "syscall" "testing" "time" ) @@ -63,17 +62,25 @@ readlines: } for key, expected := range expectedMap { - if got := m[key]; got != expected { + got := m[key] + if key == "cwd" { + // For Windows. golang.org/issue/4645. + fi1, _ := os.Stat(got) + fi2, _ := os.Stat(expected) + if os.SameFile(fi1, fi2) { + got = expected + } + } + if got != expected { t.Errorf("for key %q got %q; expected %q", key, got, expected) } } return rw } -var cgiTested = false -var cgiWorks bool +var cgiTested, cgiWorks bool -func skipTest(t *testing.T) bool { +func check(t *testing.T) { if !cgiTested { cgiTested = true cgiWorks = exec.Command("./testdata/test.cgi").Run() == nil @@ -81,16 +88,12 @@ func skipTest(t *testing.T) bool { if !cgiWorks { // No Perl on Windows, needed by test.cgi // TODO: make the child process be Go, not Perl. - t.Logf("Skipping test: test.cgi failed.") - return true + t.Skip("Skipping test: test.cgi failed.") } - return false } func TestCGIBasicGet(t *testing.T) { - if skipTest(t) { - return - } + check(t) h := &Handler{ Path: "testdata/test.cgi", Root: "/test.cgi", @@ -124,9 +127,7 @@ func TestCGIBasicGet(t *testing.T) { } func TestCGIBasicGetAbsPath(t *testing.T) { - if skipTest(t) { - return - } + check(t) pwd, err := os.Getwd() if err != nil { t.Fatalf("getwd error: %v", err) @@ -144,9 +145,7 @@ func TestCGIBasicGetAbsPath(t *testing.T) { } func TestPathInfo(t *testing.T) { - if skipTest(t) { - return - } + check(t) h := &Handler{ Path: "testdata/test.cgi", Root: "/test.cgi", @@ -163,9 +162,7 @@ func TestPathInfo(t *testing.T) { } func TestPathInfoDirRoot(t *testing.T) { - if skipTest(t) { - return - } + check(t) h := &Handler{ Path: "testdata/test.cgi", Root: "/myscript/", @@ -181,9 +178,7 @@ func TestPathInfoDirRoot(t *testing.T) { } func TestDupHeaders(t *testing.T) { - if skipTest(t) { - return - } + check(t) h := &Handler{ Path: "testdata/test.cgi", } @@ -203,9 +198,7 @@ func TestDupHeaders(t *testing.T) { } func TestPathInfoNoRoot(t *testing.T) { - if skipTest(t) { - return - } + check(t) h := &Handler{ Path: "testdata/test.cgi", Root: "", @@ -221,9 +214,7 @@ func TestPathInfoNoRoot(t *testing.T) { } func TestCGIBasicPost(t *testing.T) { - if skipTest(t) { - return - } + check(t) postReq := `POST /test.cgi?a=b HTTP/1.0 Host: example.com Content-Type: application/x-www-form-urlencoded @@ -250,9 +241,7 @@ func chunk(s string) string { // The CGI spec doesn't allow chunked requests. func TestCGIPostChunked(t *testing.T) { - if skipTest(t) { - return - } + check(t) postReq := `POST /test.cgi?a=b HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded @@ -273,9 +262,7 @@ Transfer-Encoding: chunked } func TestRedirect(t *testing.T) { - if skipTest(t) { - return - } + check(t) h := &Handler{ Path: "testdata/test.cgi", Root: "/test.cgi", @@ -290,9 +277,7 @@ func TestRedirect(t *testing.T) { } func TestInternalRedirect(t *testing.T) { - if skipTest(t) { - return - } + check(t) baseHandler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { fmt.Fprintf(rw, "basepath=%s\n", req.URL.Path) fmt.Fprintf(rw, "remoteaddr=%s\n", req.RemoteAddr) @@ -312,8 +297,9 @@ func TestInternalRedirect(t *testing.T) { // TestCopyError tests that we kill the process if there's an error copying // its output. (for example, from the client having gone away) func TestCopyError(t *testing.T) { - if skipTest(t) || runtime.GOOS == "windows" { - return + check(t) + if runtime.GOOS == "windows" { + t.Skipf("skipping test on %q", runtime.GOOS) } h := &Handler{ Path: "testdata/test.cgi", @@ -353,11 +339,7 @@ func TestCopyError(t *testing.T) { } childRunning := func() bool { - p, err := os.FindProcess(pid) - if err != nil { - return false - } - return p.Signal(syscall.Signal(0)) == nil + return isProcessRunning(t, pid) } if !childRunning() { @@ -376,10 +358,10 @@ func TestCopyError(t *testing.T) { } func TestDirUnix(t *testing.T) { - if skipTest(t) || runtime.GOOS == "windows" { - return + check(t) + if runtime.GOOS == "windows" { + t.Skipf("skipping test on %q", runtime.GOOS) } - cwd, _ := os.Getwd() h := &Handler{ Path: "testdata/test.cgi", @@ -404,8 +386,8 @@ func TestDirUnix(t *testing.T) { } func TestDirWindows(t *testing.T) { - if skipTest(t) || runtime.GOOS != "windows" { - return + if runtime.GOOS != "windows" { + t.Skip("Skipping windows specific test.") } cgifile, _ := filepath.Abs("testdata/test.cgi") @@ -414,7 +396,7 @@ func TestDirWindows(t *testing.T) { var err error perl, err = exec.LookPath("perl") if err != nil { - return + t.Skip("Skipping test: perl not found.") } perl, _ = filepath.Abs(perl) @@ -456,7 +438,7 @@ func TestEnvOverride(t *testing.T) { var err error perl, err = exec.LookPath("perl") if err != nil { - return + t.Skipf("Skipping test: perl not found.") } perl, _ = filepath.Abs(perl) diff --git a/src/pkg/net/http/cgi/plan9_test.go b/src/pkg/net/http/cgi/plan9_test.go new file mode 100644 index 000000000..c8235831b --- /dev/null +++ b/src/pkg/net/http/cgi/plan9_test.go @@ -0,0 +1,18 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build plan9 + +package cgi + +import ( + "os" + "strconv" + "testing" +) + +func isProcessRunning(t *testing.T, pid int) bool { + _, err := os.Stat("/proc/" + strconv.Itoa(pid)) + return err == nil +} diff --git a/src/pkg/net/http/cgi/posix_test.go b/src/pkg/net/http/cgi/posix_test.go new file mode 100644 index 000000000..5ff9e7d5e --- /dev/null +++ b/src/pkg/net/http/cgi/posix_test.go @@ -0,0 +1,21 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9 + +package cgi + +import ( + "os" + "syscall" + "testing" +) + +func isProcessRunning(t *testing.T, pid int) bool { + p, err := os.FindProcess(pid) + if err != nil { + return false + } + return p.Signal(syscall.Signal(0)) == nil +} diff --git a/src/pkg/net/http/cgi/testdata/test.cgi b/src/pkg/net/http/cgi/testdata/test.cgi index b46b1330f..3214df6f0 100755 --- a/src/pkg/net/http/cgi/testdata/test.cgi +++ b/src/pkg/net/http/cgi/testdata/test.cgi @@ -8,6 +8,8 @@ use strict; use Cwd; +binmode STDOUT; + my $q = MiniCGI->new; my $params = $q->Vars; @@ -16,51 +18,44 @@ if ($params->{"loc"}) { exit(0); } -my $NL = "\r\n"; -$NL = "\n" if $params->{mode} eq "NL"; - -my $p = sub { - print "$_[0]$NL"; -}; - -# With carriage returns -$p->("Content-Type: text/html"); -$p->("X-CGI-Pid: $$"); -$p->("X-Test-Header: X-Test-Value"); -$p->(""); +print "Content-Type: text/html\r\n"; +print "X-CGI-Pid: $$\r\n"; +print "X-Test-Header: X-Test-Value\r\n"; +print "\r\n"; if ($params->{"bigresponse"}) { - for (1..1024) { - print "A" x 1024, "\n"; + # 17 MB, for OS X: golang.org/issue/4958 + for (1..(17 * 1024)) { + print "A" x 1024, "\r\n"; } exit 0; } -print "test=Hello CGI\n"; +print "test=Hello CGI\r\n"; foreach my $k (sort keys %$params) { - print "param-$k=$params->{$k}\n"; + print "param-$k=$params->{$k}\r\n"; } foreach my $k (sort keys %ENV) { - my $clean_env = $ENV{$k}; - $clean_env =~ s/[\n\r]//g; - print "env-$k=$clean_env\n"; + my $clean_env = $ENV{$k}; + $clean_env =~ s/[\n\r]//g; + print "env-$k=$clean_env\r\n"; } -# NOTE: don't call getcwd() for windows. -# msys return /c/go/src/... not C:\go\... -my $dir; +# NOTE: msys perl returns /c/go/src/... not C:\go\.... +my $dir = getcwd(); if ($^O eq 'MSWin32' || $^O eq 'msys') { - my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe'; - $cmd =~ s!\\!/!g; - $dir = `$cmd /c cd`; - chomp $dir; -} else { - $dir = getcwd(); + if ($dir =~ /^.:/) { + $dir =~ s!/!\\!g; + } else { + my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe'; + $cmd =~ s!\\!/!g; + $dir = `$cmd /c cd`; + chomp $dir; + } } -print "cwd=$dir\n"; - +print "cwd=$dir\r\n"; # A minimal version of CGI.pm, for people without the perl-modules # package installed. (CGI.pm used to be part of the Perl core, but diff --git a/src/pkg/net/http/chunked.go b/src/pkg/net/http/chunked.go index 60a478fd8..91db01724 100644 --- a/src/pkg/net/http/chunked.go +++ b/src/pkg/net/http/chunked.go @@ -11,10 +11,9 @@ package http import ( "bufio" - "bytes" "errors" + "fmt" "io" - "strconv" ) const maxLineLength = 4096 // assumed <= bufio.defaultBufSize @@ -22,7 +21,7 @@ const maxLineLength = 4096 // assumed <= bufio.defaultBufSize var ErrLineTooLong = errors.New("header line too long") // newChunkedReader returns a new chunkedReader that translates the data read from r -// out of HTTP "chunked" format before returning it. +// out of HTTP "chunked" format before returning it. // The chunkedReader returns io.EOF when the final 0-length chunk is read. // // newChunkedReader is not needed by normal applications. The http package @@ -39,16 +38,17 @@ type chunkedReader struct { r *bufio.Reader n uint64 // unread bytes in chunk err error + buf [2]byte } func (cr *chunkedReader) beginChunk() { // chunk-size CRLF - var line string + var line []byte line, cr.err = readLine(cr.r) if cr.err != nil { return } - cr.n, cr.err = strconv.ParseUint(line, 16, 64) + cr.n, cr.err = parseHexUint(line) if cr.err != nil { return } @@ -74,9 +74,8 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) { cr.n -= uint64(n) if cr.n == 0 && cr.err == nil { // end of chunk (CRLF) - b := make([]byte, 2) - if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { - if b[0] != '\r' || b[1] != '\n' { + if _, cr.err = io.ReadFull(cr.r, cr.buf[:]); cr.err == nil { + if cr.buf[0] != '\r' || cr.buf[1] != '\n' { cr.err = errors.New("malformed chunked encoding") } } @@ -88,7 +87,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) { // Give up if the line exceeds maxLineLength. // The returned bytes are a pointer into storage in // the bufio, so they are only valid until the next bufio read. -func readLineBytes(b *bufio.Reader) (p []byte, err error) { +func readLine(b *bufio.Reader) (p []byte, err error) { if p, err = b.ReadSlice('\n'); err != nil { // We always know when EOF is coming. // If the caller asked for a line, there should be a line. @@ -102,20 +101,18 @@ func readLineBytes(b *bufio.Reader) (p []byte, err error) { if len(p) >= maxLineLength { return nil, ErrLineTooLong } - - // Chop off trailing white space. - p = bytes.TrimRight(p, " \r\t\n") - - return p, nil + return trimTrailingWhitespace(p), nil } -// readLineBytes, but convert the bytes into a string. -func readLine(b *bufio.Reader) (s string, err error) { - p, e := readLineBytes(b) - if e != nil { - return "", e +func trimTrailingWhitespace(b []byte) []byte { + for len(b) > 0 && isASCIISpace(b[len(b)-1]) { + b = b[:len(b)-1] } - return string(p), nil + return b +} + +func isASCIISpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' } // newChunkedWriter returns a new chunkedWriter that translates writes into HTTP @@ -147,9 +144,7 @@ func (cw *chunkedWriter) Write(data []byte) (n int, err error) { return 0, nil } - head := strconv.FormatInt(int64(len(data)), 16) + "\r\n" - - if _, err = io.WriteString(cw.Wire, head); err != nil { + if _, err = fmt.Fprintf(cw.Wire, "%x\r\n", len(data)); err != nil { return 0, err } if n, err = cw.Wire.Write(data); err != nil { @@ -168,3 +163,21 @@ func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } + +func parseHexUint(v []byte) (n uint64, err error) { + for _, b := range v { + n <<= 4 + switch { + case '0' <= b && b <= '9': + b = b - '0' + case 'a' <= b && b <= 'f': + b = b - 'a' + 10 + case 'A' <= b && b <= 'F': + b = b - 'A' + 10 + default: + return 0, errors.New("invalid byte in chunk length") + } + n |= uint64(b) + } + return +} diff --git a/src/pkg/net/http/chunked_test.go b/src/pkg/net/http/chunked_test.go index b77ee2ff2..0b18c7b55 100644 --- a/src/pkg/net/http/chunked_test.go +++ b/src/pkg/net/http/chunked_test.go @@ -9,7 +9,10 @@ package http import ( "bytes" + "fmt" + "io" "io/ioutil" + "runtime" "testing" ) @@ -37,3 +40,54 @@ func TestChunk(t *testing.T) { t.Errorf("chunk reader read %q; want %q", g, e) } } + +func TestChunkReaderAllocs(t *testing.T) { + // temporarily set GOMAXPROCS to 1 as we are testing memory allocations + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + var buf bytes.Buffer + w := newChunkedWriter(&buf) + a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc") + w.Write(a) + w.Write(b) + w.Write(c) + w.Close() + + r := newChunkedReader(&buf) + readBuf := make([]byte, len(a)+len(b)+len(c)+1) + + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + m0 := ms.Mallocs + + n, err := io.ReadFull(r, readBuf) + + runtime.ReadMemStats(&ms) + mallocs := ms.Mallocs - m0 + if mallocs > 1 { + t.Errorf("%d mallocs; want <= 1", mallocs) + } + + if n != len(readBuf)-1 { + t.Errorf("read %d bytes; want %d", n, len(readBuf)-1) + } + if err != io.ErrUnexpectedEOF { + t.Errorf("read error = %v; want ErrUnexpectedEOF", err) + } +} + +func TestParseHexUint(t *testing.T) { + for i := uint64(0); i <= 1234; i++ { + line := []byte(fmt.Sprintf("%x", i)) + got, err := parseHexUint(line) + if err != nil { + t.Fatalf("on %d: %v", i, err) + } + if got != i { + t.Errorf("for input %q = %d; want %d", line, got, i) + } + } + _, err := parseHexUint([]byte("bogus")) + if err == nil { + t.Error("expected error on bogus input") + } +} diff --git a/src/pkg/net/http/client.go b/src/pkg/net/http/client.go index 54564e098..5ee0804c7 100644 --- a/src/pkg/net/http/client.go +++ b/src/pkg/net/http/client.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // HTTP client. See RFC 2616. -// +// // This is the high-level Client interface. // The low-level implementation is in transport.go. @@ -14,6 +14,7 @@ import ( "errors" "fmt" "io" + "log" "net/url" "strings" ) @@ -32,17 +33,19 @@ type Client struct { // CheckRedirect specifies the policy for handling redirects. // If CheckRedirect is not nil, the client calls it before - // following an HTTP redirect. The arguments req and via - // are the upcoming request and the requests made already, - // oldest first. If CheckRedirect returns an error, the client - // returns that error instead of issue the Request req. + // following an HTTP redirect. The arguments req and via are + // the upcoming request and the requests made already, oldest + // first. If CheckRedirect returns an error, the Client's Get + // method returns both the previous Response and + // CheckRedirect's error (wrapped in a url.Error) instead of + // issuing the Request req. // // If CheckRedirect is nil, the Client uses its default policy, // which is to stop after 10 consecutive requests. CheckRedirect func(req *Request, via []*Request) error - // Jar specifies the cookie jar. - // If Jar is nil, cookies are not sent in requests and ignored + // Jar specifies the cookie jar. + // If Jar is nil, cookies are not sent in requests and ignored // in responses. Jar CookieJar } @@ -84,10 +87,32 @@ type readClose struct { io.Closer } +func (c *Client) send(req *Request) (*Response, error) { + if c.Jar != nil { + for _, cookie := range c.Jar.Cookies(req.URL) { + req.AddCookie(cookie) + } + } + resp, err := send(req, c.Transport) + if err != nil { + return nil, err + } + if c.Jar != nil { + if rc := resp.Cookies(); len(rc) > 0 { + c.Jar.SetCookies(req.URL, rc) + } + } + return resp, err +} + // Do sends an HTTP request and returns an HTTP response, following // policy (e.g. redirects, cookies, auth) as configured on the client. // -// A non-nil response always contains a non-nil resp.Body. +// An error is returned if caused by client policy (such as +// CheckRedirect), or if there was an HTTP protocol error. +// A non-2xx response doesn't cause an error. +// +// When err is nil, resp always contains a non-nil resp.Body. // // Callers should close resp.Body when done reading from it. If // resp.Body is not closed, the Client's underlying RoundTripper @@ -97,12 +122,16 @@ type readClose struct { // Generally Get, Post, or PostForm will be used instead of Do. func (c *Client) Do(req *Request) (resp *Response, err error) { if req.Method == "GET" || req.Method == "HEAD" { - return c.doFollowingRedirects(req) + return c.doFollowingRedirects(req, shouldRedirectGet) } - return send(req, c.Transport) + if req.Method == "POST" || req.Method == "PUT" { + return c.doFollowingRedirects(req, shouldRedirectPost) + } + return c.send(req) } -// send issues an HTTP request. Caller should close resp.Body when done reading from it. +// send issues an HTTP request. +// Caller should close resp.Body when done reading from it. func send(req *Request, t RoundTripper) (resp *Response, err error) { if t == nil { t = DefaultTransport @@ -130,12 +159,19 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) { if u := req.URL.User; u != nil { req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(u.String()))) } - return t.RoundTrip(req) + resp, err = t.RoundTrip(req) + if err != nil { + if resp != nil { + log.Printf("RoundTripper returned a response & error; ignoring response") + } + return nil, err + } + return resp, nil } // True if the specified HTTP status code is one for which the Get utility should // automatically redirect. -func shouldRedirect(statusCode int) bool { +func shouldRedirectGet(statusCode int) bool { switch statusCode { case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect: return true @@ -143,6 +179,16 @@ func shouldRedirect(statusCode int) bool { return false } +// True if the specified HTTP status code is one for which the Post utility should +// automatically redirect. +func shouldRedirectPost(statusCode int) bool { + switch statusCode { + case StatusFound, StatusSeeOther: + return true + } + return false +} + // Get issues a GET to the specified URL. If the response is one of the following // redirect codes, Get follows the redirect, up to a maximum of 10 redirects: // @@ -151,10 +197,15 @@ func shouldRedirect(statusCode int) bool { // 303 (See Other) // 307 (Temporary Redirect) // -// Caller should close r.Body when done reading from it. +// An error is returned if there were too many redirects or if there +// was an HTTP protocol error. A non-2xx response doesn't cause an +// error. +// +// When err is nil, resp always contains a non-nil resp.Body. +// Caller should close resp.Body when done reading from it. // // Get is a wrapper around DefaultClient.Get. -func Get(url string) (r *Response, err error) { +func Get(url string) (resp *Response, err error) { return DefaultClient.Get(url) } @@ -167,18 +218,21 @@ func Get(url string) (r *Response, err error) { // 303 (See Other) // 307 (Temporary Redirect) // -// Caller should close r.Body when done reading from it. -func (c *Client) Get(url string) (r *Response, err error) { +// An error is returned if the Client's CheckRedirect function fails +// or if there was an HTTP protocol error. A non-2xx response doesn't +// cause an error. +// +// When err is nil, resp always contains a non-nil resp.Body. +// Caller should close resp.Body when done reading from it. +func (c *Client) Get(url string) (resp *Response, err error) { req, err := NewRequest("GET", url, nil) if err != nil { return nil, err } - return c.doFollowingRedirects(req) + return c.doFollowingRedirects(req, shouldRedirectGet) } -func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) { - // TODO: if/when we add cookie support, the redirected request shouldn't - // necessarily supply the same cookies as the original. +func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) { var base *url.URL redirectChecker := c.CheckRedirect if redirectChecker == nil { @@ -190,17 +244,16 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) { return nil, errors.New("http: nil Request.URL") } - jar := c.Jar - if jar == nil { - jar = blackHoleJar{} - } - req := ireq urlStr := "" // next relative or absolute URL to fetch (after first request) + redirectFailed := false for redirect := 0; ; redirect++ { if redirect != 0 { req = new(Request) req.Method = ireq.Method + if ireq.Method == "POST" || ireq.Method == "PUT" { + req.Method = "GET" + } req.Header = make(Header) req.URL, err = base.Parse(urlStr) if err != nil { @@ -215,26 +268,21 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) { err = redirectChecker(req, via) if err != nil { + redirectFailed = true break } } } - for _, cookie := range jar.Cookies(req.URL) { - req.AddCookie(cookie) - } urlStr = req.URL.String() - if r, err = send(req, c.Transport); err != nil { + if resp, err = c.send(req); err != nil { break } - if c := r.Cookies(); len(c) > 0 { - jar.SetCookies(req.URL, c) - } - if shouldRedirect(r.StatusCode) { - r.Body.Close() - if urlStr = r.Header.Get("Location"); urlStr == "" { - err = errors.New(fmt.Sprintf("%d response missing Location header", r.StatusCode)) + if shouldRedirect(resp.StatusCode) { + resp.Body.Close() + if urlStr = resp.Header.Get("Location"); urlStr == "" { + err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode)) break } base = req.URL @@ -245,12 +293,23 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) { } method := ireq.Method - err = &url.Error{ + urlErr := &url.Error{ Op: method[0:1] + strings.ToLower(method[1:]), URL: urlStr, Err: err, } - return + + if redirectFailed { + // Special case for Go 1 compatibility: return both the response + // and an error if the CheckRedirect function failed. + // See http://golang.org/issue/3795 + return resp, urlErr + } + + if resp != nil { + resp.Body.Close() + } + return nil, urlErr } func defaultCheckRedirect(req *Request, via []*Request) error { @@ -262,49 +321,42 @@ func defaultCheckRedirect(req *Request, via []*Request) error { // Post issues a POST to the specified URL. // -// Caller should close r.Body when done reading from it. +// Caller should close resp.Body when done reading from it. // // Post is a wrapper around DefaultClient.Post -func Post(url string, bodyType string, body io.Reader) (r *Response, err error) { +func Post(url string, bodyType string, body io.Reader) (resp *Response, err error) { return DefaultClient.Post(url, bodyType, body) } // Post issues a POST to the specified URL. // -// Caller should close r.Body when done reading from it. -func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error) { +// Caller should close resp.Body when done reading from it. +func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) { req, err := NewRequest("POST", url, body) if err != nil { return nil, err } req.Header.Set("Content-Type", bodyType) - if c.Jar != nil { - for _, cookie := range c.Jar.Cookies(req.URL) { - req.AddCookie(cookie) - } - } - r, err = send(req, c.Transport) - if err == nil && c.Jar != nil { - c.Jar.SetCookies(req.URL, r.Cookies()) - } - return r, err + return c.doFollowingRedirects(req, shouldRedirectPost) } -// PostForm issues a POST to the specified URL, -// with data's keys and values urlencoded as the request body. +// PostForm issues a POST to the specified URL, with data's keys and +// values URL-encoded as the request body. // -// Caller should close r.Body when done reading from it. +// When err is nil, resp always contains a non-nil resp.Body. +// Caller should close resp.Body when done reading from it. // // PostForm is a wrapper around DefaultClient.PostForm -func PostForm(url string, data url.Values) (r *Response, err error) { +func PostForm(url string, data url.Values) (resp *Response, err error) { return DefaultClient.PostForm(url, data) } -// PostForm issues a POST to the specified URL, +// PostForm issues a POST to the specified URL, // with data's keys and values urlencoded as the request body. // -// Caller should close r.Body when done reading from it. -func (c *Client) PostForm(url string, data url.Values) (r *Response, err error) { +// When err is nil, resp always contains a non-nil resp.Body. +// Caller should close resp.Body when done reading from it. +func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) { return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) } @@ -318,7 +370,7 @@ func (c *Client) PostForm(url string, data url.Values) (r *Response, err error) // 307 (Temporary Redirect) // // Head is a wrapper around DefaultClient.Head -func Head(url string) (r *Response, err error) { +func Head(url string) (resp *Response, err error) { return DefaultClient.Head(url) } @@ -330,10 +382,10 @@ func Head(url string) (r *Response, err error) { // 302 (Found) // 303 (See Other) // 307 (Temporary Redirect) -func (c *Client) Head(url string) (r *Response, err error) { +func (c *Client) Head(url string) (resp *Response, err error) { req, err := NewRequest("HEAD", url, nil) if err != nil { return nil, err } - return c.doFollowingRedirects(req) + return c.doFollowingRedirects(req, shouldRedirectGet) } diff --git a/src/pkg/net/http/client_test.go b/src/pkg/net/http/client_test.go index 9b4261b9f..88649bb16 100644 --- a/src/pkg/net/http/client_test.go +++ b/src/pkg/net/http/client_test.go @@ -7,7 +7,9 @@ package http_test import ( + "bytes" "crypto/tls" + "crypto/x509" "errors" "fmt" "io" @@ -53,6 +55,7 @@ func pedanticReadAll(r io.Reader) (b []byte, err error) { } func TestClient(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(robotsTxtHandler) defer ts.Close() @@ -70,6 +73,7 @@ func TestClient(t *testing.T) { } func TestClientHead(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(robotsTxtHandler) defer ts.Close() @@ -92,6 +96,7 @@ func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err error) } func TestGetRequestFormat(t *testing.T) { + defer checkLeakedTransports(t) tr := &recordingTransport{} client := &Client{Transport: tr} url := "http://dummy.faketld/" @@ -108,6 +113,7 @@ func TestGetRequestFormat(t *testing.T) { } func TestPostRequestFormat(t *testing.T) { + defer checkLeakedTransports(t) tr := &recordingTransport{} client := &Client{Transport: tr} @@ -134,6 +140,7 @@ func TestPostRequestFormat(t *testing.T) { } func TestPostFormRequestFormat(t *testing.T) { + defer checkLeakedTransports(t) tr := &recordingTransport{} client := &Client{Transport: tr} @@ -175,6 +182,7 @@ func TestPostFormRequestFormat(t *testing.T) { } func TestRedirects(t *testing.T) { + defer checkLeakedTransports(t) var ts *httptest.Server ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { n, _ := strconv.Atoi(r.FormValue("n")) @@ -218,6 +226,10 @@ func TestRedirects(t *testing.T) { return checkErr }} res, err := c.Get(ts.URL) + if err != nil { + t.Fatalf("Get error: %v", err) + } + res.Body.Close() finalUrl := res.Request.URL.String() if e, g := "", fmt.Sprintf("%v", err); e != g { t.Errorf("with custom client, expected error %q, got %q", e, g) @@ -231,9 +243,63 @@ func TestRedirects(t *testing.T) { checkErr = errors.New("no redirects allowed") res, err = c.Get(ts.URL) - finalUrl = res.Request.URL.String() - if e, g := "Get /?n=1: no redirects allowed", fmt.Sprintf("%v", err); e != g { - t.Errorf("with redirects forbidden, expected error %q, got %q", e, g) + if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr { + t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err) + } + if res == nil { + t.Fatalf("Expected a non-nil Response on CheckRedirect failure (http://golang.org/issue/3795)") + } + res.Body.Close() + if res.Header.Get("Location") == "" { + t.Errorf("no Location header in Response") + } +} + +func TestPostRedirects(t *testing.T) { + defer checkLeakedTransports(t) + var log struct { + sync.Mutex + bytes.Buffer + } + var ts *httptest.Server + ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + log.Lock() + fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI) + log.Unlock() + if v := r.URL.Query().Get("code"); v != "" { + code, _ := strconv.Atoi(v) + if code/100 == 3 { + w.Header().Set("Location", ts.URL) + } + w.WriteHeader(code) + } + })) + defer ts.Close() + tests := []struct { + suffix string + want int // response code + }{ + {"/", 200}, + {"/?code=301", 301}, + {"/?code=302", 200}, + {"/?code=303", 200}, + {"/?code=404", 404}, + } + for _, tt := range tests { + res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content")) + if err != nil { + t.Fatal(err) + } + if res.StatusCode != tt.want { + t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want) + } + } + log.Lock() + got := log.String() + log.Unlock() + want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 " + if got != want { + t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want) } } @@ -279,6 +345,10 @@ func TestClientSendsCookieFromJar(t *testing.T) { req, _ := NewRequest("GET", us, nil) client.Do(req) // Note: doesn't hit network matchReturnedCookies(t, expectedCookies, tr.req.Cookies()) + + req, _ = NewRequest("POST", us, nil) + client.Do(req) // Note: doesn't hit network + matchReturnedCookies(t, expectedCookies, tr.req.Cookies()) } // Just enough correctness for our redirect tests. Uses the URL.Host as the @@ -291,6 +361,9 @@ type TestJar struct { func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) { j.m.Lock() defer j.m.Unlock() + if j.perURL == nil { + j.perURL = make(map[string][]*Cookie) + } j.perURL[u.Host] = cookies } @@ -301,6 +374,7 @@ func (j *TestJar) Cookies(u *url.URL) []*Cookie { } func TestRedirectCookiesOnRequest(t *testing.T) { + defer checkLeakedTransports(t) var ts *httptest.Server ts = httptest.NewServer(echoCookiesRedirectHandler) defer ts.Close() @@ -318,14 +392,20 @@ func TestRedirectCookiesOnRequest(t *testing.T) { } func TestRedirectCookiesJar(t *testing.T) { + defer checkLeakedTransports(t) var ts *httptest.Server ts = httptest.NewServer(echoCookiesRedirectHandler) defer ts.Close() - c := &Client{} - c.Jar = &TestJar{perURL: make(map[string][]*Cookie)} + c := &Client{ + Jar: new(TestJar), + } u, _ := url.Parse(ts.URL) c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]}) - resp, _ := c.Get(ts.URL) + resp, err := c.Get(ts.URL) + if err != nil { + t.Fatalf("Get: %v", err) + } + resp.Body.Close() matchReturnedCookies(t, expectedCookies, resp.Cookies()) } @@ -348,7 +428,72 @@ func matchReturnedCookies(t *testing.T, expected, given []*Cookie) { } } +func TestJarCalls(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + pathSuffix := r.RequestURI[1:] + if r.RequestURI == "/nosetcookie" { + return // dont set cookies for this path + } + SetCookie(w, &Cookie{Name: "name" + pathSuffix, Value: "val" + pathSuffix}) + if r.RequestURI == "/" { + Redirect(w, r, "http://secondhost.fake/secondpath", 302) + } + })) + defer ts.Close() + jar := new(RecordingJar) + c := &Client{ + Jar: jar, + Transport: &Transport{ + Dial: func(_ string, _ string) (net.Conn, error) { + return net.Dial("tcp", ts.Listener.Addr().String()) + }, + }, + } + _, err := c.Get("http://firsthost.fake/") + if err != nil { + t.Fatal(err) + } + _, err = c.Get("http://firsthost.fake/nosetcookie") + if err != nil { + t.Fatal(err) + } + got := jar.log.String() + want := `Cookies("http://firsthost.fake/") +SetCookie("http://firsthost.fake/", [name=val]) +Cookies("http://secondhost.fake/secondpath") +SetCookie("http://secondhost.fake/secondpath", [namesecondpath=valsecondpath]) +Cookies("http://firsthost.fake/nosetcookie") +` + if got != want { + t.Errorf("Got Jar calls:\n%s\nWant:\n%s", got, want) + } +} + +// RecordingJar keeps a log of calls made to it, without +// tracking any cookies. +type RecordingJar struct { + mu sync.Mutex + log bytes.Buffer +} + +func (j *RecordingJar) SetCookies(u *url.URL, cookies []*Cookie) { + j.logf("SetCookie(%q, %v)\n", u, cookies) +} + +func (j *RecordingJar) Cookies(u *url.URL) []*Cookie { + j.logf("Cookies(%q)\n", u) + return nil +} + +func (j *RecordingJar) logf(format string, args ...interface{}) { + j.mu.Lock() + defer j.mu.Unlock() + fmt.Fprintf(&j.log, format, args...) +} + func TestStreamingGet(t *testing.T) { + defer checkLeakedTransports(t) say := make(chan string) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.(Flusher).Flush() @@ -399,6 +544,7 @@ func (c *writeCountingConn) Write(p []byte) (int, error) { // TestClientWrites verifies that client requests are buffered and we // don't send a TCP packet per line of the http request + body. func TestClientWrites(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { })) defer ts.Close() @@ -432,6 +578,7 @@ func TestClientWrites(t *testing.T) { } func TestClientInsecureTransport(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Write([]byte("Hello")) })) @@ -446,15 +593,20 @@ func TestClientInsecureTransport(t *testing.T) { InsecureSkipVerify: insecure, }, } + defer tr.CloseIdleConnections() c := &Client{Transport: tr} - _, err := c.Get(ts.URL) + res, err := c.Get(ts.URL) if (err == nil) != insecure { t.Errorf("insecure=%v: got unexpected err=%v", insecure, err) } + if res != nil { + res.Body.Close() + } } } func TestClientErrorWithRequestURI(t *testing.T) { + defer checkLeakedTransports(t) req, _ := NewRequest("GET", "http://localhost:1234/", nil) req.RequestURI = "/this/field/is/illegal/and/should/error/" _, err := DefaultClient.Do(req) @@ -465,3 +617,87 @@ func TestClientErrorWithRequestURI(t *testing.T) { t.Errorf("wanted error mentioning RequestURI; got error: %v", err) } } + +func newTLSTransport(t *testing.T, ts *httptest.Server) *Transport { + certs := x509.NewCertPool() + for _, c := range ts.TLS.Certificates { + roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1]) + if err != nil { + t.Fatalf("error parsing server's root cert: %v", err) + } + for _, root := range roots { + certs.AddCert(root) + } + } + return &Transport{ + TLSClientConfig: &tls.Config{RootCAs: certs}, + } +} + +func TestClientWithCorrectTLSServerName(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) { + if r.TLS.ServerName != "127.0.0.1" { + t.Errorf("expected client to set ServerName 127.0.0.1, got: %q", r.TLS.ServerName) + } + })) + defer ts.Close() + + c := &Client{Transport: newTLSTransport(t, ts)} + if _, err := c.Get(ts.URL); err != nil { + t.Fatalf("expected successful TLS connection, got error: %v", err) + } +} + +func TestClientWithIncorrectTLSServerName(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {})) + defer ts.Close() + + trans := newTLSTransport(t, ts) + trans.TLSClientConfig.ServerName = "badserver" + c := &Client{Transport: trans} + _, err := c.Get(ts.URL) + if err == nil { + t.Fatalf("expected an error") + } + if !strings.Contains(err.Error(), "127.0.0.1") || !strings.Contains(err.Error(), "badserver") { + t.Errorf("wanted error mentioning 127.0.0.1 and badserver; got error: %v", err) + } +} + +// Verify Response.ContentLength is populated. http://golang.org/issue/4126 +func TestClientHeadContentLength(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + if v := r.FormValue("cl"); v != "" { + w.Header().Set("Content-Length", v) + } + })) + defer ts.Close() + tests := []struct { + suffix string + want int64 + }{ + {"/?cl=1234", 1234}, + {"/?cl=0", 0}, + {"", -1}, + } + for _, tt := range tests { + req, _ := NewRequest("HEAD", ts.URL+tt.suffix, nil) + res, err := DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + if res.ContentLength != tt.want { + t.Errorf("Content-Length = %d; want %d", res.ContentLength, tt.want) + } + bs, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if len(bs) != 0 { + t.Errorf("Unexpected content: %q", bs) + } + } +} diff --git a/src/pkg/net/http/cookie.go b/src/pkg/net/http/cookie.go index 2e30bbff1..155b09223 100644 --- a/src/pkg/net/http/cookie.go +++ b/src/pkg/net/http/cookie.go @@ -26,7 +26,7 @@ type Cookie struct { Expires time.Time RawExpires string - // MaxAge=0 means no 'Max-Age' attribute specified. + // MaxAge=0 means no 'Max-Age' attribute specified. // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' // MaxAge>0 means Max-Age attribute present and given in seconds MaxAge int @@ -258,10 +258,5 @@ func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) } func isCookieNameValid(raw string) bool { - for _, c := range raw { - if !isToken(byte(c)) { - return false - } - } - return true + return strings.IndexFunc(raw, isNotToken) < 0 } diff --git a/src/pkg/net/http/cookie_test.go b/src/pkg/net/http/cookie_test.go index 1e9186a05..f84f73936 100644 --- a/src/pkg/net/http/cookie_test.go +++ b/src/pkg/net/http/cookie_test.go @@ -217,7 +217,7 @@ var readCookiesTests = []struct { func TestReadCookies(t *testing.T) { for i, tt := range readCookiesTests { - for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input + for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input c := readCookies(tt.Header, tt.Filter) if !reflect.DeepEqual(c, tt.Cookies) { t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies)) diff --git a/src/pkg/net/http/cookiejar/jar.go b/src/pkg/net/http/cookiejar/jar.go new file mode 100644 index 000000000..5d1aeb87f --- /dev/null +++ b/src/pkg/net/http/cookiejar/jar.go @@ -0,0 +1,494 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar. +package cookiejar + +import ( + "errors" + "fmt" + "net" + "net/http" + "net/url" + "sort" + "strings" + "sync" + "time" +) + +// PublicSuffixList provides the public suffix of a domain. For example: +// - the public suffix of "example.com" is "com", +// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and +// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us". +// +// Implementations of PublicSuffixList must be safe for concurrent use by +// multiple goroutines. +// +// An implementation that always returns "" is valid and may be useful for +// testing but it is not secure: it means that the HTTP server for foo.com can +// set a cookie for bar.com. +type PublicSuffixList interface { + // PublicSuffix returns the public suffix of domain. + // + // TODO: specify which of the caller and callee is responsible for IP + // addresses, for leading and trailing dots, for case sensitivity, and + // for IDN/Punycode. + PublicSuffix(domain string) string + + // String returns a description of the source of this public suffix + // list. The description will typically contain something like a time + // stamp or version number. + String() string +} + +// Options are the options for creating a new Jar. +type Options struct { + // PublicSuffixList is the public suffix list that determines whether + // an HTTP server can set a cookie for a domain. + // + // A nil value is valid and may be useful for testing but it is not + // secure: it means that the HTTP server for foo.co.uk can set a cookie + // for bar.co.uk. + PublicSuffixList PublicSuffixList +} + +// Jar implements the http.CookieJar interface from the net/http package. +type Jar struct { + psList PublicSuffixList + + // mu locks the remaining fields. + mu sync.Mutex + + // entries is a set of entries, keyed by their eTLD+1 and subkeyed by + // their name/domain/path. + entries map[string]map[string]entry + + // nextSeqNum is the next sequence number assigned to a new cookie + // created SetCookies. + nextSeqNum uint64 +} + +// New returns a new cookie jar. A nil *Options is equivalent to a zero +// Options. +func New(o *Options) (*Jar, error) { + jar := &Jar{ + entries: make(map[string]map[string]entry), + } + if o != nil { + jar.psList = o.PublicSuffixList + } + return jar, nil +} + +// entry is the internal representation of a cookie. +// +// This struct type is not used outside of this package per se, but the exported +// fields are those of RFC 6265. +type entry struct { + Name string + Value string + Domain string + Path string + Secure bool + HttpOnly bool + Persistent bool + HostOnly bool + Expires time.Time + Creation time.Time + LastAccess time.Time + + // seqNum is a sequence number so that Cookies returns cookies in a + // deterministic order, even for cookies that have equal Path length and + // equal Creation time. This simplifies testing. + seqNum uint64 +} + +// Id returns the domain;path;name triple of e as an id. +func (e *entry) id() string { + return fmt.Sprintf("%s;%s;%s", e.Domain, e.Path, e.Name) +} + +// shouldSend determines whether e's cookie qualifies to be included in a +// request to host/path. It is the caller's responsibility to check if the +// cookie is expired. +func (e *entry) shouldSend(https bool, host, path string) bool { + return e.domainMatch(host) && e.pathMatch(path) && (https || !e.Secure) +} + +// domainMatch implements "domain-match" of RFC 6265 section 5.1.3. +func (e *entry) domainMatch(host string) bool { + if e.Domain == host { + return true + } + return !e.HostOnly && hasDotSuffix(host, e.Domain) +} + +// pathMatch implements "path-match" according to RFC 6265 section 5.1.4. +func (e *entry) pathMatch(requestPath string) bool { + if requestPath == e.Path { + return true + } + if strings.HasPrefix(requestPath, e.Path) { + if e.Path[len(e.Path)-1] == '/' { + return true // The "/any/" matches "/any/path" case. + } else if requestPath[len(e.Path)] == '/' { + return true // The "/any" matches "/any/path" case. + } + } + return false +} + +// hasDotSuffix returns whether s ends in "."+suffix. +func hasDotSuffix(s, suffix string) bool { + return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix +} + +// byPathLength is a []entry sort.Interface that sorts according to RFC 6265 +// section 5.4 point 2: by longest path and then by earliest creation time. +type byPathLength []entry + +func (s byPathLength) Len() int { return len(s) } + +func (s byPathLength) Less(i, j int) bool { + if len(s[i].Path) != len(s[j].Path) { + return len(s[i].Path) > len(s[j].Path) + } + if !s[i].Creation.Equal(s[j].Creation) { + return s[i].Creation.Before(s[j].Creation) + } + return s[i].seqNum < s[j].seqNum +} + +func (s byPathLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// Cookies implements the Cookies method of the http.CookieJar interface. +// +// It returns an empty slice if the URL's scheme is not HTTP or HTTPS. +func (j *Jar) Cookies(u *url.URL) (cookies []*http.Cookie) { + return j.cookies(u, time.Now()) +} + +// cookies is like Cookies but takes the current time as a parameter. +func (j *Jar) cookies(u *url.URL, now time.Time) (cookies []*http.Cookie) { + if u.Scheme != "http" && u.Scheme != "https" { + return cookies + } + host, err := canonicalHost(u.Host) + if err != nil { + return cookies + } + key := jarKey(host, j.psList) + + j.mu.Lock() + defer j.mu.Unlock() + + submap := j.entries[key] + if submap == nil { + return cookies + } + + https := u.Scheme == "https" + path := u.Path + if path == "" { + path = "/" + } + + modified := false + var selected []entry + for id, e := range submap { + if e.Persistent && !e.Expires.After(now) { + delete(submap, id) + modified = true + continue + } + if !e.shouldSend(https, host, path) { + continue + } + e.LastAccess = now + submap[id] = e + selected = append(selected, e) + modified = true + } + if modified { + if len(submap) == 0 { + delete(j.entries, key) + } else { + j.entries[key] = submap + } + } + + sort.Sort(byPathLength(selected)) + for _, e := range selected { + cookies = append(cookies, &http.Cookie{Name: e.Name, Value: e.Value}) + } + + return cookies +} + +// SetCookies implements the SetCookies method of the http.CookieJar interface. +// +// It does nothing if the URL's scheme is not HTTP or HTTPS. +func (j *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) { + j.setCookies(u, cookies, time.Now()) +} + +// setCookies is like SetCookies but takes the current time as parameter. +func (j *Jar) setCookies(u *url.URL, cookies []*http.Cookie, now time.Time) { + if len(cookies) == 0 { + return + } + if u.Scheme != "http" && u.Scheme != "https" { + return + } + host, err := canonicalHost(u.Host) + if err != nil { + return + } + key := jarKey(host, j.psList) + defPath := defaultPath(u.Path) + + j.mu.Lock() + defer j.mu.Unlock() + + submap := j.entries[key] + + modified := false + for _, cookie := range cookies { + e, remove, err := j.newEntry(cookie, now, defPath, host) + if err != nil { + continue + } + id := e.id() + if remove { + if submap != nil { + if _, ok := submap[id]; ok { + delete(submap, id) + modified = true + } + } + continue + } + if submap == nil { + submap = make(map[string]entry) + } + + if old, ok := submap[id]; ok { + e.Creation = old.Creation + e.seqNum = old.seqNum + } else { + e.Creation = now + e.seqNum = j.nextSeqNum + j.nextSeqNum++ + } + e.LastAccess = now + submap[id] = e + modified = true + } + + if modified { + if len(submap) == 0 { + delete(j.entries, key) + } else { + j.entries[key] = submap + } + } +} + +// canonicalHost strips port from host if present and returns the canonicalized +// host name. +func canonicalHost(host string) (string, error) { + var err error + host = strings.ToLower(host) + if hasPort(host) { + host, _, err = net.SplitHostPort(host) + if err != nil { + return "", err + } + } + if strings.HasSuffix(host, ".") { + // Strip trailing dot from fully qualified domain names. + host = host[:len(host)-1] + } + return toASCII(host) +} + +// hasPort returns whether host contains a port number. host may be a host +// name, an IPv4 or an IPv6 address. +func hasPort(host string) bool { + colons := strings.Count(host, ":") + if colons == 0 { + return false + } + if colons == 1 { + return true + } + return host[0] == '[' && strings.Contains(host, "]:") +} + +// jarKey returns the key to use for a jar. +func jarKey(host string, psl PublicSuffixList) string { + if isIP(host) { + return host + } + + var i int + if psl == nil { + i = strings.LastIndex(host, ".") + if i == -1 { + return host + } + } else { + suffix := psl.PublicSuffix(host) + if suffix == host { + return host + } + i = len(host) - len(suffix) + if i <= 0 || host[i-1] != '.' { + // The provided public suffix list psl is broken. + // Storing cookies under host is a safe stopgap. + return host + } + } + prevDot := strings.LastIndex(host[:i-1], ".") + return host[prevDot+1:] +} + +// isIP returns whether host is an IP address. +func isIP(host string) bool { + return net.ParseIP(host) != nil +} + +// defaultPath returns the directory part of an URL's path according to +// RFC 6265 section 5.1.4. +func defaultPath(path string) string { + if len(path) == 0 || path[0] != '/' { + return "/" // Path is empty or malformed. + } + + i := strings.LastIndex(path, "/") // Path starts with "/", so i != -1. + if i == 0 { + return "/" // Path has the form "/abc". + } + return path[:i] // Path is either of form "/abc/xyz" or "/abc/xyz/". +} + +// newEntry creates an entry from a http.Cookie c. now is the current time and +// is compared to c.Expires to determine deletion of c. defPath and host are the +// default-path and the canonical host name of the URL c was received from. +// +// remove is whether the jar should delete this cookie, as it has already +// expired with respect to now. In this case, e may be incomplete, but it will +// be valid to call e.id (which depends on e's Name, Domain and Path). +// +// A malformed c.Domain will result in an error. +func (j *Jar) newEntry(c *http.Cookie, now time.Time, defPath, host string) (e entry, remove bool, err error) { + e.Name = c.Name + + if c.Path == "" || c.Path[0] != '/' { + e.Path = defPath + } else { + e.Path = c.Path + } + + e.Domain, e.HostOnly, err = j.domainAndType(host, c.Domain) + if err != nil { + return e, false, err + } + + // MaxAge takes precedence over Expires. + if c.MaxAge < 0 { + return e, true, nil + } else if c.MaxAge > 0 { + e.Expires = now.Add(time.Duration(c.MaxAge) * time.Second) + e.Persistent = true + } else { + if c.Expires.IsZero() { + e.Expires = endOfTime + e.Persistent = false + } else { + if !c.Expires.After(now) { + return e, true, nil + } + e.Expires = c.Expires + e.Persistent = true + } + } + + e.Value = c.Value + e.Secure = c.Secure + e.HttpOnly = c.HttpOnly + + return e, false, nil +} + +var ( + errIllegalDomain = errors.New("cookiejar: illegal cookie domain attribute") + errMalformedDomain = errors.New("cookiejar: malformed cookie domain attribute") + errNoHostname = errors.New("cookiejar: no host name available (IP only)") +) + +// endOfTime is the time when session (non-persistent) cookies expire. +// This instant is representable in most date/time formats (not just +// Go's time.Time) and should be far enough in the future. +var endOfTime = time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC) + +// domainAndType determines the cookie's domain and hostOnly attribute. +func (j *Jar) domainAndType(host, domain string) (string, bool, error) { + if domain == "" { + // No domain attribute in the SetCookie header indicates a + // host cookie. + return host, true, nil + } + + if isIP(host) { + // According to RFC 6265 domain-matching includes not being + // an IP address. + // TODO: This might be relaxed as in common browsers. + return "", false, errNoHostname + } + + // From here on: If the cookie is valid, it is a domain cookie (with + // the one exception of a public suffix below). + // See RFC 6265 section 5.2.3. + if domain[0] == '.' { + domain = domain[1:] + } + + if len(domain) == 0 || domain[0] == '.' { + // Received either "Domain=." or "Domain=..some.thing", + // both are illegal. + return "", false, errMalformedDomain + } + domain = strings.ToLower(domain) + + if domain[len(domain)-1] == '.' { + // We received stuff like "Domain=www.example.com.". + // Browsers do handle such stuff (actually differently) but + // RFC 6265 seems to be clear here (e.g. section 4.1.2.3) in + // requiring a reject. 4.1.2.3 is not normative, but + // "Domain Matching" (5.1.3) and "Canonicalized Host Names" + // (5.1.2) are. + return "", false, errMalformedDomain + } + + // See RFC 6265 section 5.3 #5. + if j.psList != nil { + if ps := j.psList.PublicSuffix(domain); ps != "" && !hasDotSuffix(domain, ps) { + if host == domain { + // This is the one exception in which a cookie + // with a domain attribute is a host cookie. + return host, true, nil + } + return "", false, errIllegalDomain + } + } + + // The domain must domain-match host: www.mycompany.com cannot + // set cookies for .ourcompetitors.com. + if host != domain && !hasDotSuffix(host, domain) { + return "", false, errIllegalDomain + } + + return domain, false, nil +} diff --git a/src/pkg/net/http/cookiejar/jar_test.go b/src/pkg/net/http/cookiejar/jar_test.go new file mode 100644 index 000000000..3aa601586 --- /dev/null +++ b/src/pkg/net/http/cookiejar/jar_test.go @@ -0,0 +1,1267 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cookiejar + +import ( + "fmt" + "net/http" + "net/url" + "sort" + "strings" + "testing" + "time" +) + +// tNow is the synthetic current time used as now during testing. +var tNow = time.Date(2013, 1, 1, 12, 0, 0, 0, time.UTC) + +// testPSL implements PublicSuffixList with just two rules: "co.uk" +// and the default rule "*". +type testPSL struct{} + +func (testPSL) String() string { + return "testPSL" +} +func (testPSL) PublicSuffix(d string) string { + if d == "co.uk" || strings.HasSuffix(d, ".co.uk") { + return "co.uk" + } + return d[strings.LastIndex(d, ".")+1:] +} + +// newTestJar creates an empty Jar with testPSL as the public suffix list. +func newTestJar() *Jar { + jar, err := New(&Options{PublicSuffixList: testPSL{}}) + if err != nil { + panic(err) + } + return jar +} + +var hasDotSuffixTests = [...]struct { + s, suffix string +}{ + {"", ""}, + {"", "."}, + {"", "x"}, + {".", ""}, + {".", "."}, + {".", ".."}, + {".", "x"}, + {".", "x."}, + {".", ".x"}, + {".", ".x."}, + {"x", ""}, + {"x", "."}, + {"x", ".."}, + {"x", "x"}, + {"x", "x."}, + {"x", ".x"}, + {"x", ".x."}, + {".x", ""}, + {".x", "."}, + {".x", ".."}, + {".x", "x"}, + {".x", "x."}, + {".x", ".x"}, + {".x", ".x."}, + {"x.", ""}, + {"x.", "."}, + {"x.", ".."}, + {"x.", "x"}, + {"x.", "x."}, + {"x.", ".x"}, + {"x.", ".x."}, + {"com", ""}, + {"com", "m"}, + {"com", "om"}, + {"com", "com"}, + {"com", ".com"}, + {"com", "x.com"}, + {"com", "xcom"}, + {"com", "xorg"}, + {"com", "org"}, + {"com", "rg"}, + {"foo.com", ""}, + {"foo.com", "m"}, + {"foo.com", "om"}, + {"foo.com", "com"}, + {"foo.com", ".com"}, + {"foo.com", "o.com"}, + {"foo.com", "oo.com"}, + {"foo.com", "foo.com"}, + {"foo.com", ".foo.com"}, + {"foo.com", "x.foo.com"}, + {"foo.com", "xfoo.com"}, + {"foo.com", "xfoo.org"}, + {"foo.com", "foo.org"}, + {"foo.com", "oo.org"}, + {"foo.com", "o.org"}, + {"foo.com", ".org"}, + {"foo.com", "org"}, + {"foo.com", "rg"}, +} + +func TestHasDotSuffix(t *testing.T) { + for _, tc := range hasDotSuffixTests { + got := hasDotSuffix(tc.s, tc.suffix) + want := strings.HasSuffix(tc.s, "."+tc.suffix) + if got != want { + t.Errorf("s=%q, suffix=%q: got %v, want %v", tc.s, tc.suffix, got, want) + } + } +} + +var canonicalHostTests = map[string]string{ + "www.example.com": "www.example.com", + "WWW.EXAMPLE.COM": "www.example.com", + "wWw.eXAmple.CoM": "www.example.com", + "www.example.com:80": "www.example.com", + "192.168.0.10": "192.168.0.10", + "192.168.0.5:8080": "192.168.0.5", + "2001:4860:0:2001::68": "2001:4860:0:2001::68", + "[2001:4860:0:::68]:8080": "2001:4860:0:::68", + "www.bücher.de": "www.xn--bcher-kva.de", + "www.example.com.": "www.example.com", + "[bad.unmatched.bracket:": "error", +} + +func TestCanonicalHost(t *testing.T) { + for h, want := range canonicalHostTests { + got, err := canonicalHost(h) + if want == "error" { + if err == nil { + t.Errorf("%q: got nil error, want non-nil", h) + } + continue + } + if err != nil { + t.Errorf("%q: %v", h, err) + continue + } + if got != want { + t.Errorf("%q: got %q, want %q", h, got, want) + continue + } + } +} + +var hasPortTests = map[string]bool{ + "www.example.com": false, + "www.example.com:80": true, + "127.0.0.1": false, + "127.0.0.1:8080": true, + "2001:4860:0:2001::68": false, + "[2001::0:::68]:80": true, +} + +func TestHasPort(t *testing.T) { + for host, want := range hasPortTests { + if got := hasPort(host); got != want { + t.Errorf("%q: got %t, want %t", host, got, want) + } + } +} + +var jarKeyTests = map[string]string{ + "foo.www.example.com": "example.com", + "www.example.com": "example.com", + "example.com": "example.com", + "com": "com", + "foo.www.bbc.co.uk": "bbc.co.uk", + "www.bbc.co.uk": "bbc.co.uk", + "bbc.co.uk": "bbc.co.uk", + "co.uk": "co.uk", + "uk": "uk", + "192.168.0.5": "192.168.0.5", +} + +func TestJarKey(t *testing.T) { + for host, want := range jarKeyTests { + if got := jarKey(host, testPSL{}); got != want { + t.Errorf("%q: got %q, want %q", host, got, want) + } + } +} + +var jarKeyNilPSLTests = map[string]string{ + "foo.www.example.com": "example.com", + "www.example.com": "example.com", + "example.com": "example.com", + "com": "com", + "foo.www.bbc.co.uk": "co.uk", + "www.bbc.co.uk": "co.uk", + "bbc.co.uk": "co.uk", + "co.uk": "co.uk", + "uk": "uk", + "192.168.0.5": "192.168.0.5", +} + +func TestJarKeyNilPSL(t *testing.T) { + for host, want := range jarKeyNilPSLTests { + if got := jarKey(host, nil); got != want { + t.Errorf("%q: got %q, want %q", host, got, want) + } + } +} + +var isIPTests = map[string]bool{ + "127.0.0.1": true, + "1.2.3.4": true, + "2001:4860:0:2001::68": true, + "example.com": false, + "1.1.1.300": false, + "www.foo.bar.net": false, + "123.foo.bar.net": false, +} + +func TestIsIP(t *testing.T) { + for host, want := range isIPTests { + if got := isIP(host); got != want { + t.Errorf("%q: got %t, want %t", host, got, want) + } + } +} + +var defaultPathTests = map[string]string{ + "/": "/", + "/abc": "/", + "/abc/": "/abc", + "/abc/xyz": "/abc", + "/abc/xyz/": "/abc/xyz", + "/a/b/c.html": "/a/b", + "": "/", + "strange": "/", + "//": "/", + "/a//b": "/a/", + "/a/./b": "/a/.", + "/a/../b": "/a/..", +} + +func TestDefaultPath(t *testing.T) { + for path, want := range defaultPathTests { + if got := defaultPath(path); got != want { + t.Errorf("%q: got %q, want %q", path, got, want) + } + } +} + +var domainAndTypeTests = [...]struct { + host string // host Set-Cookie header was received from + domain string // domain attribute in Set-Cookie header + wantDomain string // expected domain of cookie + wantHostOnly bool // expected host-cookie flag + wantErr error // expected error +}{ + {"www.example.com", "", "www.example.com", true, nil}, + {"127.0.0.1", "", "127.0.0.1", true, nil}, + {"2001:4860:0:2001::68", "", "2001:4860:0:2001::68", true, nil}, + {"www.example.com", "example.com", "example.com", false, nil}, + {"www.example.com", ".example.com", "example.com", false, nil}, + {"www.example.com", "www.example.com", "www.example.com", false, nil}, + {"www.example.com", ".www.example.com", "www.example.com", false, nil}, + {"foo.sso.example.com", "sso.example.com", "sso.example.com", false, nil}, + {"bar.co.uk", "bar.co.uk", "bar.co.uk", false, nil}, + {"foo.bar.co.uk", ".bar.co.uk", "bar.co.uk", false, nil}, + {"127.0.0.1", "127.0.0.1", "", false, errNoHostname}, + {"2001:4860:0:2001::68", "2001:4860:0:2001::68", "2001:4860:0:2001::68", false, errNoHostname}, + {"www.example.com", ".", "", false, errMalformedDomain}, + {"www.example.com", "..", "", false, errMalformedDomain}, + {"www.example.com", "other.com", "", false, errIllegalDomain}, + {"www.example.com", "com", "", false, errIllegalDomain}, + {"www.example.com", ".com", "", false, errIllegalDomain}, + {"foo.bar.co.uk", ".co.uk", "", false, errIllegalDomain}, + {"127.www.0.0.1", "127.0.0.1", "", false, errIllegalDomain}, + {"com", "", "com", true, nil}, + {"com", "com", "com", true, nil}, + {"com", ".com", "com", true, nil}, + {"co.uk", "", "co.uk", true, nil}, + {"co.uk", "co.uk", "co.uk", true, nil}, + {"co.uk", ".co.uk", "co.uk", true, nil}, +} + +func TestDomainAndType(t *testing.T) { + jar := newTestJar() + for _, tc := range domainAndTypeTests { + domain, hostOnly, err := jar.domainAndType(tc.host, tc.domain) + if err != tc.wantErr { + t.Errorf("%q/%q: got %q error, want %q", + tc.host, tc.domain, err, tc.wantErr) + continue + } + if err != nil { + continue + } + if domain != tc.wantDomain || hostOnly != tc.wantHostOnly { + t.Errorf("%q/%q: got %q/%t want %q/%t", + tc.host, tc.domain, domain, hostOnly, + tc.wantDomain, tc.wantHostOnly) + } + } +} + +// expiresIn creates an expires attribute delta seconds from tNow. +func expiresIn(delta int) string { + t := tNow.Add(time.Duration(delta) * time.Second) + return "expires=" + t.Format(time.RFC1123) +} + +// mustParseURL parses s to an URL and panics on error. +func mustParseURL(s string) *url.URL { + u, err := url.Parse(s) + if err != nil || u.Scheme == "" || u.Host == "" { + panic(fmt.Sprintf("Unable to parse URL %s.", s)) + } + return u +} + +// jarTest encapsulates the following actions on a jar: +// 1. Perform SetCookies with fromURL and the cookies from setCookies. +// (Done at time tNow + 0 ms.) +// 2. Check that the entries in the jar matches content. +// (Done at time tNow + 1001 ms.) +// 3. For each query in tests: Check that Cookies with toURL yields the +// cookies in want. +// (Query n done at tNow + (n+2)*1001 ms.) +type jarTest struct { + description string // The description of what this test is supposed to test + fromURL string // The full URL of the request from which Set-Cookie headers where received + setCookies []string // All the cookies received from fromURL + content string // The whole (non-expired) content of the jar + queries []query // Queries to test the Jar.Cookies method +} + +// query contains one test of the cookies returned from Jar.Cookies. +type query struct { + toURL string // the URL in the Cookies call + want string // the expected list of cookies (order matters) +} + +// run runs the jarTest. +func (test jarTest) run(t *testing.T, jar *Jar) { + now := tNow + + // Populate jar with cookies. + setCookies := make([]*http.Cookie, len(test.setCookies)) + for i, cs := range test.setCookies { + cookies := (&http.Response{Header: http.Header{"Set-Cookie": {cs}}}).Cookies() + if len(cookies) != 1 { + panic(fmt.Sprintf("Wrong cookie line %q: %#v", cs, cookies)) + } + setCookies[i] = cookies[0] + } + jar.setCookies(mustParseURL(test.fromURL), setCookies, now) + now = now.Add(1001 * time.Millisecond) + + // Serialize non-expired entries in the form "name1=val1 name2=val2". + var cs []string + for _, submap := range jar.entries { + for _, cookie := range submap { + if !cookie.Expires.After(now) { + continue + } + cs = append(cs, cookie.Name+"="+cookie.Value) + } + } + sort.Strings(cs) + got := strings.Join(cs, " ") + + // Make sure jar content matches our expectations. + if got != test.content { + t.Errorf("Test %q Content\ngot %q\nwant %q", + test.description, got, test.content) + } + + // Test different calls to Cookies. + for i, query := range test.queries { + now = now.Add(1001 * time.Millisecond) + var s []string + for _, c := range jar.cookies(mustParseURL(query.toURL), now) { + s = append(s, c.Name+"="+c.Value) + } + if got := strings.Join(s, " "); got != query.want { + t.Errorf("Test %q #%d\ngot %q\nwant %q", test.description, i, got, query.want) + } + } +} + +// basicsTests contains fundamental tests. Each jarTest has to be performed on +// a fresh, empty Jar. +var basicsTests = [...]jarTest{ + { + "Retrieval of a plain host cookie.", + "http://www.host.test/", + []string{"A=a"}, + "A=a", + []query{ + {"http://www.host.test", "A=a"}, + {"http://www.host.test/", "A=a"}, + {"http://www.host.test/some/path", "A=a"}, + {"https://www.host.test", "A=a"}, + {"https://www.host.test/", "A=a"}, + {"https://www.host.test/some/path", "A=a"}, + {"ftp://www.host.test", ""}, + {"ftp://www.host.test/", ""}, + {"ftp://www.host.test/some/path", ""}, + {"http://www.other.org", ""}, + {"http://sibling.host.test", ""}, + {"http://deep.www.host.test", ""}, + }, + }, + { + "Secure cookies are not returned to http.", + "http://www.host.test/", + []string{"A=a; secure"}, + "A=a", + []query{ + {"http://www.host.test", ""}, + {"http://www.host.test/", ""}, + {"http://www.host.test/some/path", ""}, + {"https://www.host.test", "A=a"}, + {"https://www.host.test/", "A=a"}, + {"https://www.host.test/some/path", "A=a"}, + }, + }, + { + "Explicit path.", + "http://www.host.test/", + []string{"A=a; path=/some/path"}, + "A=a", + []query{ + {"http://www.host.test", ""}, + {"http://www.host.test/", ""}, + {"http://www.host.test/some", ""}, + {"http://www.host.test/some/", ""}, + {"http://www.host.test/some/path", "A=a"}, + {"http://www.host.test/some/paths", ""}, + {"http://www.host.test/some/path/foo", "A=a"}, + {"http://www.host.test/some/path/foo/", "A=a"}, + }, + }, + { + "Implicit path #1: path is a directory.", + "http://www.host.test/some/path/", + []string{"A=a"}, + "A=a", + []query{ + {"http://www.host.test", ""}, + {"http://www.host.test/", ""}, + {"http://www.host.test/some", ""}, + {"http://www.host.test/some/", ""}, + {"http://www.host.test/some/path", "A=a"}, + {"http://www.host.test/some/paths", ""}, + {"http://www.host.test/some/path/foo", "A=a"}, + {"http://www.host.test/some/path/foo/", "A=a"}, + }, + }, + { + "Implicit path #2: path is not a directory.", + "http://www.host.test/some/path/index.html", + []string{"A=a"}, + "A=a", + []query{ + {"http://www.host.test", ""}, + {"http://www.host.test/", ""}, + {"http://www.host.test/some", ""}, + {"http://www.host.test/some/", ""}, + {"http://www.host.test/some/path", "A=a"}, + {"http://www.host.test/some/paths", ""}, + {"http://www.host.test/some/path/foo", "A=a"}, + {"http://www.host.test/some/path/foo/", "A=a"}, + }, + }, + { + "Implicit path #3: no path in URL at all.", + "http://www.host.test", + []string{"A=a"}, + "A=a", + []query{ + {"http://www.host.test", "A=a"}, + {"http://www.host.test/", "A=a"}, + {"http://www.host.test/some/path", "A=a"}, + }, + }, + { + "Cookies are sorted by path length.", + "http://www.host.test/", + []string{ + "A=a; path=/foo/bar", + "B=b; path=/foo/bar/baz/qux", + "C=c; path=/foo/bar/baz", + "D=d; path=/foo"}, + "A=a B=b C=c D=d", + []query{ + {"http://www.host.test/foo/bar/baz/qux", "B=b C=c A=a D=d"}, + {"http://www.host.test/foo/bar/baz/", "C=c A=a D=d"}, + {"http://www.host.test/foo/bar", "A=a D=d"}, + }, + }, + { + "Creation time determines sorting on same length paths.", + "http://www.host.test/", + []string{ + "A=a; path=/foo/bar", + "X=x; path=/foo/bar", + "Y=y; path=/foo/bar/baz/qux", + "B=b; path=/foo/bar/baz/qux", + "C=c; path=/foo/bar/baz", + "W=w; path=/foo/bar/baz", + "Z=z; path=/foo", + "D=d; path=/foo"}, + "A=a B=b C=c D=d W=w X=x Y=y Z=z", + []query{ + {"http://www.host.test/foo/bar/baz/qux", "Y=y B=b C=c W=w A=a X=x Z=z D=d"}, + {"http://www.host.test/foo/bar/baz/", "C=c W=w A=a X=x Z=z D=d"}, + {"http://www.host.test/foo/bar", "A=a X=x Z=z D=d"}, + }, + }, + { + "Sorting of same-name cookies.", + "http://www.host.test/", + []string{ + "A=1; path=/", + "A=2; path=/path", + "A=3; path=/quux", + "A=4; path=/path/foo", + "A=5; domain=.host.test; path=/path", + "A=6; domain=.host.test; path=/quux", + "A=7; domain=.host.test; path=/path/foo", + }, + "A=1 A=2 A=3 A=4 A=5 A=6 A=7", + []query{ + {"http://www.host.test/path", "A=2 A=5 A=1"}, + {"http://www.host.test/path/foo", "A=4 A=7 A=2 A=5 A=1"}, + }, + }, + { + "Disallow domain cookie on public suffix.", + "http://www.bbc.co.uk", + []string{ + "a=1", + "b=2; domain=co.uk", + }, + "a=1", + []query{{"http://www.bbc.co.uk", "a=1"}}, + }, + { + "Host cookie on IP.", + "http://192.168.0.10", + []string{"a=1"}, + "a=1", + []query{{"http://192.168.0.10", "a=1"}}, + }, + { + "Port is ignored #1.", + "http://www.host.test/", + []string{"a=1"}, + "a=1", + []query{ + {"http://www.host.test", "a=1"}, + {"http://www.host.test:8080/", "a=1"}, + }, + }, + { + "Port is ignored #2.", + "http://www.host.test:8080/", + []string{"a=1"}, + "a=1", + []query{ + {"http://www.host.test", "a=1"}, + {"http://www.host.test:8080/", "a=1"}, + {"http://www.host.test:1234/", "a=1"}, + }, + }, +} + +func TestBasics(t *testing.T) { + for _, test := range basicsTests { + jar := newTestJar() + test.run(t, jar) + } +} + +// updateAndDeleteTests contains jarTests which must be performed on the same +// Jar. +var updateAndDeleteTests = [...]jarTest{ + { + "Set initial cookies.", + "http://www.host.test", + []string{ + "a=1", + "b=2; secure", + "c=3; httponly", + "d=4; secure; httponly"}, + "a=1 b=2 c=3 d=4", + []query{ + {"http://www.host.test", "a=1 c=3"}, + {"https://www.host.test", "a=1 b=2 c=3 d=4"}, + }, + }, + { + "Update value via http.", + "http://www.host.test", + []string{ + "a=w", + "b=x; secure", + "c=y; httponly", + "d=z; secure; httponly"}, + "a=w b=x c=y d=z", + []query{ + {"http://www.host.test", "a=w c=y"}, + {"https://www.host.test", "a=w b=x c=y d=z"}, + }, + }, + { + "Clear Secure flag from a http.", + "http://www.host.test/", + []string{ + "b=xx", + "d=zz; httponly"}, + "a=w b=xx c=y d=zz", + []query{{"http://www.host.test", "a=w b=xx c=y d=zz"}}, + }, + { + "Delete all.", + "http://www.host.test/", + []string{ + "a=1; max-Age=-1", // delete via MaxAge + "b=2; " + expiresIn(-10), // delete via Expires + "c=2; max-age=-1; " + expiresIn(-10), // delete via both + "d=4; max-age=-1; " + expiresIn(10)}, // MaxAge takes precedence + "", + []query{{"http://www.host.test", ""}}, + }, + { + "Refill #1.", + "http://www.host.test", + []string{ + "A=1", + "A=2; path=/foo", + "A=3; domain=.host.test", + "A=4; path=/foo; domain=.host.test"}, + "A=1 A=2 A=3 A=4", + []query{{"http://www.host.test/foo", "A=2 A=4 A=1 A=3"}}, + }, + { + "Refill #2.", + "http://www.google.com", + []string{ + "A=6", + "A=7; path=/foo", + "A=8; domain=.google.com", + "A=9; path=/foo; domain=.google.com"}, + "A=1 A=2 A=3 A=4 A=6 A=7 A=8 A=9", + []query{ + {"http://www.host.test/foo", "A=2 A=4 A=1 A=3"}, + {"http://www.google.com/foo", "A=7 A=9 A=6 A=8"}, + }, + }, + { + "Delete A7.", + "http://www.google.com", + []string{"A=; path=/foo; max-age=-1"}, + "A=1 A=2 A=3 A=4 A=6 A=8 A=9", + []query{ + {"http://www.host.test/foo", "A=2 A=4 A=1 A=3"}, + {"http://www.google.com/foo", "A=9 A=6 A=8"}, + }, + }, + { + "Delete A4.", + "http://www.host.test", + []string{"A=; path=/foo; domain=host.test; max-age=-1"}, + "A=1 A=2 A=3 A=6 A=8 A=9", + []query{ + {"http://www.host.test/foo", "A=2 A=1 A=3"}, + {"http://www.google.com/foo", "A=9 A=6 A=8"}, + }, + }, + { + "Delete A6.", + "http://www.google.com", + []string{"A=; max-age=-1"}, + "A=1 A=2 A=3 A=8 A=9", + []query{ + {"http://www.host.test/foo", "A=2 A=1 A=3"}, + {"http://www.google.com/foo", "A=9 A=8"}, + }, + }, + { + "Delete A3.", + "http://www.host.test", + []string{"A=; domain=host.test; max-age=-1"}, + "A=1 A=2 A=8 A=9", + []query{ + {"http://www.host.test/foo", "A=2 A=1"}, + {"http://www.google.com/foo", "A=9 A=8"}, + }, + }, + { + "No cross-domain delete.", + "http://www.host.test", + []string{ + "A=; domain=google.com; max-age=-1", + "A=; path=/foo; domain=google.com; max-age=-1"}, + "A=1 A=2 A=8 A=9", + []query{ + {"http://www.host.test/foo", "A=2 A=1"}, + {"http://www.google.com/foo", "A=9 A=8"}, + }, + }, + { + "Delete A8 and A9.", + "http://www.google.com", + []string{ + "A=; domain=google.com; max-age=-1", + "A=; path=/foo; domain=google.com; max-age=-1"}, + "A=1 A=2", + []query{ + {"http://www.host.test/foo", "A=2 A=1"}, + {"http://www.google.com/foo", ""}, + }, + }, +} + +func TestUpdateAndDelete(t *testing.T) { + jar := newTestJar() + for _, test := range updateAndDeleteTests { + test.run(t, jar) + } +} + +func TestExpiration(t *testing.T) { + jar := newTestJar() + jarTest{ + "Expiration.", + "http://www.host.test", + []string{ + "a=1", + "b=2; max-age=3", + "c=3; " + expiresIn(3), + "d=4; max-age=5", + "e=5; " + expiresIn(5), + "f=6; max-age=100", + }, + "a=1 b=2 c=3 d=4 e=5 f=6", // executed at t0 + 1001 ms + []query{ + {"http://www.host.test", "a=1 b=2 c=3 d=4 e=5 f=6"}, // t0 + 2002 ms + {"http://www.host.test", "a=1 d=4 e=5 f=6"}, // t0 + 3003 ms + {"http://www.host.test", "a=1 d=4 e=5 f=6"}, // t0 + 4004 ms + {"http://www.host.test", "a=1 f=6"}, // t0 + 5005 ms + {"http://www.host.test", "a=1 f=6"}, // t0 + 6006 ms + }, + }.run(t, jar) +} + +// +// Tests derived from Chromium's cookie_store_unittest.h. +// + +// See http://src.chromium.org/viewvc/chrome/trunk/src/net/cookies/cookie_store_unittest.h?revision=159685&content-type=text/plain +// Some of the original tests are in a bad condition (e.g. +// DomainWithTrailingDotTest) or are not RFC 6265 conforming (e.g. +// TestNonDottedAndTLD #1 and #6) and have not been ported. + +// chromiumBasicsTests contains fundamental tests. Each jarTest has to be +// performed on a fresh, empty Jar. +var chromiumBasicsTests = [...]jarTest{ + { + "DomainWithTrailingDotTest.", + "http://www.google.com/", + []string{ + "a=1; domain=.www.google.com.", + "b=2; domain=.www.google.com.."}, + "", + []query{ + {"http://www.google.com", ""}, + }, + }, + { + "ValidSubdomainTest #1.", + "http://a.b.c.d.com", + []string{ + "a=1; domain=.a.b.c.d.com", + "b=2; domain=.b.c.d.com", + "c=3; domain=.c.d.com", + "d=4; domain=.d.com"}, + "a=1 b=2 c=3 d=4", + []query{ + {"http://a.b.c.d.com", "a=1 b=2 c=3 d=4"}, + {"http://b.c.d.com", "b=2 c=3 d=4"}, + {"http://c.d.com", "c=3 d=4"}, + {"http://d.com", "d=4"}, + }, + }, + { + "ValidSubdomainTest #2.", + "http://a.b.c.d.com", + []string{ + "a=1; domain=.a.b.c.d.com", + "b=2; domain=.b.c.d.com", + "c=3; domain=.c.d.com", + "d=4; domain=.d.com", + "X=bcd; domain=.b.c.d.com", + "X=cd; domain=.c.d.com"}, + "X=bcd X=cd a=1 b=2 c=3 d=4", + []query{ + {"http://b.c.d.com", "b=2 c=3 d=4 X=bcd X=cd"}, + {"http://c.d.com", "c=3 d=4 X=cd"}, + }, + }, + { + "InvalidDomainTest #1.", + "http://foo.bar.com", + []string{ + "a=1; domain=.yo.foo.bar.com", + "b=2; domain=.foo.com", + "c=3; domain=.bar.foo.com", + "d=4; domain=.foo.bar.com.net", + "e=5; domain=ar.com", + "f=6; domain=.", + "g=7; domain=/", + "h=8; domain=http://foo.bar.com", + "i=9; domain=..foo.bar.com", + "j=10; domain=..bar.com", + "k=11; domain=.foo.bar.com?blah", + "l=12; domain=.foo.bar.com/blah", + "m=12; domain=.foo.bar.com:80", + "n=14; domain=.foo.bar.com:", + "o=15; domain=.foo.bar.com#sup", + }, + "", // Jar is empty. + []query{{"http://foo.bar.com", ""}}, + }, + { + "InvalidDomainTest #2.", + "http://foo.com.com", + []string{"a=1; domain=.foo.com.com.com"}, + "", + []query{{"http://foo.bar.com", ""}}, + }, + { + "DomainWithoutLeadingDotTest #1.", + "http://manage.hosted.filefront.com", + []string{"a=1; domain=filefront.com"}, + "a=1", + []query{{"http://www.filefront.com", "a=1"}}, + }, + { + "DomainWithoutLeadingDotTest #2.", + "http://www.google.com", + []string{"a=1; domain=www.google.com"}, + "a=1", + []query{ + {"http://www.google.com", "a=1"}, + {"http://sub.www.google.com", "a=1"}, + {"http://something-else.com", ""}, + }, + }, + { + "CaseInsensitiveDomainTest.", + "http://www.google.com", + []string{ + "a=1; domain=.GOOGLE.COM", + "b=2; domain=.www.gOOgLE.coM"}, + "a=1 b=2", + []query{{"http://www.google.com", "a=1 b=2"}}, + }, + { + "TestIpAddress #1.", + "http://1.2.3.4/foo", + []string{"a=1; path=/"}, + "a=1", + []query{{"http://1.2.3.4/foo", "a=1"}}, + }, + { + "TestIpAddress #2.", + "http://1.2.3.4/foo", + []string{ + "a=1; domain=.1.2.3.4", + "b=2; domain=.3.4"}, + "", + []query{{"http://1.2.3.4/foo", ""}}, + }, + { + "TestIpAddress #3.", + "http://1.2.3.4/foo", + []string{"a=1; domain=1.2.3.4"}, + "", + []query{{"http://1.2.3.4/foo", ""}}, + }, + { + "TestNonDottedAndTLD #2.", + "http://com./index.html", + []string{"a=1"}, + "a=1", + []query{ + {"http://com./index.html", "a=1"}, + {"http://no-cookies.com./index.html", ""}, + }, + }, + { + "TestNonDottedAndTLD #3.", + "http://a.b", + []string{ + "a=1; domain=.b", + "b=2; domain=b"}, + "", + []query{{"http://bar.foo", ""}}, + }, + { + "TestNonDottedAndTLD #4.", + "http://google.com", + []string{ + "a=1; domain=.com", + "b=2; domain=com"}, + "", + []query{{"http://google.com", ""}}, + }, + { + "TestNonDottedAndTLD #5.", + "http://google.co.uk", + []string{ + "a=1; domain=.co.uk", + "b=2; domain=.uk"}, + "", + []query{ + {"http://google.co.uk", ""}, + {"http://else.co.com", ""}, + {"http://else.uk", ""}, + }, + }, + { + "TestHostEndsWithDot.", + "http://www.google.com", + []string{ + "a=1", + "b=2; domain=.www.google.com."}, + "a=1", + []query{{"http://www.google.com", "a=1"}}, + }, + { + "PathTest", + "http://www.google.izzle", + []string{"a=1; path=/wee"}, + "a=1", + []query{ + {"http://www.google.izzle/wee", "a=1"}, + {"http://www.google.izzle/wee/", "a=1"}, + {"http://www.google.izzle/wee/war", "a=1"}, + {"http://www.google.izzle/wee/war/more/more", "a=1"}, + {"http://www.google.izzle/weehee", ""}, + {"http://www.google.izzle/", ""}, + }, + }, +} + +func TestChromiumBasics(t *testing.T) { + for _, test := range chromiumBasicsTests { + jar := newTestJar() + test.run(t, jar) + } +} + +// chromiumDomainTests contains jarTests which must be executed all on the +// same Jar. +var chromiumDomainTests = [...]jarTest{ + { + "Fill #1.", + "http://www.google.izzle", + []string{"A=B"}, + "A=B", + []query{{"http://www.google.izzle", "A=B"}}, + }, + { + "Fill #2.", + "http://www.google.izzle", + []string{"C=D; domain=.google.izzle"}, + "A=B C=D", + []query{{"http://www.google.izzle", "A=B C=D"}}, + }, + { + "Verify A is a host cookie and not accessible from subdomain.", + "http://unused.nil", + []string{}, + "A=B C=D", + []query{{"http://foo.www.google.izzle", "C=D"}}, + }, + { + "Verify domain cookies are found on proper domain.", + "http://www.google.izzle", + []string{"E=F; domain=.www.google.izzle"}, + "A=B C=D E=F", + []query{{"http://www.google.izzle", "A=B C=D E=F"}}, + }, + { + "Leading dots in domain attributes are optional.", + "http://www.google.izzle", + []string{"G=H; domain=www.google.izzle"}, + "A=B C=D E=F G=H", + []query{{"http://www.google.izzle", "A=B C=D E=F G=H"}}, + }, + { + "Verify domain enforcement works #1.", + "http://www.google.izzle", + []string{"K=L; domain=.bar.www.google.izzle"}, + "A=B C=D E=F G=H", + []query{{"http://bar.www.google.izzle", "C=D E=F G=H"}}, + }, + { + "Verify domain enforcement works #2.", + "http://unused.nil", + []string{}, + "A=B C=D E=F G=H", + []query{{"http://www.google.izzle", "A=B C=D E=F G=H"}}, + }, +} + +func TestChromiumDomain(t *testing.T) { + jar := newTestJar() + for _, test := range chromiumDomainTests { + test.run(t, jar) + } + +} + +// chromiumDeletionTests must be performed all on the same Jar. +var chromiumDeletionTests = [...]jarTest{ + { + "Create session cookie a1.", + "http://www.google.com", + []string{"a=1"}, + "a=1", + []query{{"http://www.google.com", "a=1"}}, + }, + { + "Delete sc a1 via MaxAge.", + "http://www.google.com", + []string{"a=1; max-age=-1"}, + "", + []query{{"http://www.google.com", ""}}, + }, + { + "Create session cookie b2.", + "http://www.google.com", + []string{"b=2"}, + "b=2", + []query{{"http://www.google.com", "b=2"}}, + }, + { + "Delete sc b2 via Expires.", + "http://www.google.com", + []string{"b=2; " + expiresIn(-10)}, + "", + []query{{"http://www.google.com", ""}}, + }, + { + "Create persistent cookie c3.", + "http://www.google.com", + []string{"c=3; max-age=3600"}, + "c=3", + []query{{"http://www.google.com", "c=3"}}, + }, + { + "Delete pc c3 via MaxAge.", + "http://www.google.com", + []string{"c=3; max-age=-1"}, + "", + []query{{"http://www.google.com", ""}}, + }, + { + "Create persistent cookie d4.", + "http://www.google.com", + []string{"d=4; max-age=3600"}, + "d=4", + []query{{"http://www.google.com", "d=4"}}, + }, + { + "Delete pc d4 via Expires.", + "http://www.google.com", + []string{"d=4; " + expiresIn(-10)}, + "", + []query{{"http://www.google.com", ""}}, + }, +} + +func TestChromiumDeletion(t *testing.T) { + jar := newTestJar() + for _, test := range chromiumDeletionTests { + test.run(t, jar) + } +} + +// domainHandlingTests tests and documents the rules for domain handling. +// Each test must be performed on an empty new Jar. +var domainHandlingTests = [...]jarTest{ + { + "Host cookie", + "http://www.host.test", + []string{"a=1"}, + "a=1", + []query{ + {"http://www.host.test", "a=1"}, + {"http://host.test", ""}, + {"http://bar.host.test", ""}, + {"http://foo.www.host.test", ""}, + {"http://other.test", ""}, + {"http://test", ""}, + }, + }, + { + "Domain cookie #1", + "http://www.host.test", + []string{"a=1; domain=host.test"}, + "a=1", + []query{ + {"http://www.host.test", "a=1"}, + {"http://host.test", "a=1"}, + {"http://bar.host.test", "a=1"}, + {"http://foo.www.host.test", "a=1"}, + {"http://other.test", ""}, + {"http://test", ""}, + }, + }, + { + "Domain cookie #2", + "http://www.host.test", + []string{"a=1; domain=.host.test"}, + "a=1", + []query{ + {"http://www.host.test", "a=1"}, + {"http://host.test", "a=1"}, + {"http://bar.host.test", "a=1"}, + {"http://foo.www.host.test", "a=1"}, + {"http://other.test", ""}, + {"http://test", ""}, + }, + }, + { + "Host cookie on IDNA domain #1", + "http://www.bücher.test", + []string{"a=1"}, + "a=1", + []query{ + {"http://www.bücher.test", "a=1"}, + {"http://www.xn--bcher-kva.test", "a=1"}, + {"http://bücher.test", ""}, + {"http://xn--bcher-kva.test", ""}, + {"http://bar.bücher.test", ""}, + {"http://bar.xn--bcher-kva.test", ""}, + {"http://foo.www.bücher.test", ""}, + {"http://foo.www.xn--bcher-kva.test", ""}, + {"http://other.test", ""}, + {"http://test", ""}, + }, + }, + { + "Host cookie on IDNA domain #2", + "http://www.xn--bcher-kva.test", + []string{"a=1"}, + "a=1", + []query{ + {"http://www.bücher.test", "a=1"}, + {"http://www.xn--bcher-kva.test", "a=1"}, + {"http://bücher.test", ""}, + {"http://xn--bcher-kva.test", ""}, + {"http://bar.bücher.test", ""}, + {"http://bar.xn--bcher-kva.test", ""}, + {"http://foo.www.bücher.test", ""}, + {"http://foo.www.xn--bcher-kva.test", ""}, + {"http://other.test", ""}, + {"http://test", ""}, + }, + }, + { + "Domain cookie on IDNA domain #1", + "http://www.bücher.test", + []string{"a=1; domain=xn--bcher-kva.test"}, + "a=1", + []query{ + {"http://www.bücher.test", "a=1"}, + {"http://www.xn--bcher-kva.test", "a=1"}, + {"http://bücher.test", "a=1"}, + {"http://xn--bcher-kva.test", "a=1"}, + {"http://bar.bücher.test", "a=1"}, + {"http://bar.xn--bcher-kva.test", "a=1"}, + {"http://foo.www.bücher.test", "a=1"}, + {"http://foo.www.xn--bcher-kva.test", "a=1"}, + {"http://other.test", ""}, + {"http://test", ""}, + }, + }, + { + "Domain cookie on IDNA domain #2", + "http://www.xn--bcher-kva.test", + []string{"a=1; domain=xn--bcher-kva.test"}, + "a=1", + []query{ + {"http://www.bücher.test", "a=1"}, + {"http://www.xn--bcher-kva.test", "a=1"}, + {"http://bücher.test", "a=1"}, + {"http://xn--bcher-kva.test", "a=1"}, + {"http://bar.bücher.test", "a=1"}, + {"http://bar.xn--bcher-kva.test", "a=1"}, + {"http://foo.www.bücher.test", "a=1"}, + {"http://foo.www.xn--bcher-kva.test", "a=1"}, + {"http://other.test", ""}, + {"http://test", ""}, + }, + }, + { + "Host cookie on TLD.", + "http://com", + []string{"a=1"}, + "a=1", + []query{ + {"http://com", "a=1"}, + {"http://any.com", ""}, + {"http://any.test", ""}, + }, + }, + { + "Domain cookie on TLD becomes a host cookie.", + "http://com", + []string{"a=1; domain=com"}, + "a=1", + []query{ + {"http://com", "a=1"}, + {"http://any.com", ""}, + {"http://any.test", ""}, + }, + }, + { + "Host cookie on public suffix.", + "http://co.uk", + []string{"a=1"}, + "a=1", + []query{ + {"http://co.uk", "a=1"}, + {"http://uk", ""}, + {"http://some.co.uk", ""}, + {"http://foo.some.co.uk", ""}, + {"http://any.uk", ""}, + }, + }, + { + "Domain cookie on public suffix is ignored.", + "http://some.co.uk", + []string{"a=1; domain=co.uk"}, + "", + []query{ + {"http://co.uk", ""}, + {"http://uk", ""}, + {"http://some.co.uk", ""}, + {"http://foo.some.co.uk", ""}, + {"http://any.uk", ""}, + }, + }, +} + +func TestDomainHandling(t *testing.T) { + for _, test := range domainHandlingTests { + jar := newTestJar() + test.run(t, jar) + } +} diff --git a/src/pkg/net/http/cookiejar/punycode.go b/src/pkg/net/http/cookiejar/punycode.go new file mode 100644 index 000000000..ea7ceb5ef --- /dev/null +++ b/src/pkg/net/http/cookiejar/punycode.go @@ -0,0 +1,159 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cookiejar + +// This file implements the Punycode algorithm from RFC 3492. + +import ( + "fmt" + "strings" + "unicode/utf8" +) + +// These parameter values are specified in section 5. +// +// All computation is done with int32s, so that overflow behavior is identical +// regardless of whether int is 32-bit or 64-bit. +const ( + base int32 = 36 + damp int32 = 700 + initialBias int32 = 72 + initialN int32 = 128 + skew int32 = 38 + tmax int32 = 26 + tmin int32 = 1 +) + +// encode encodes a string as specified in section 6.3 and prepends prefix to +// the result. +// +// The "while h < length(input)" line in the specification becomes "for +// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes. +func encode(prefix, s string) (string, error) { + output := make([]byte, len(prefix), len(prefix)+1+2*len(s)) + copy(output, prefix) + delta, n, bias := int32(0), initialN, initialBias + b, remaining := int32(0), int32(0) + for _, r := range s { + if r < 0x80 { + b++ + output = append(output, byte(r)) + } else { + remaining++ + } + } + h := b + if b > 0 { + output = append(output, '-') + } + for remaining != 0 { + m := int32(0x7fffffff) + for _, r := range s { + if m > r && r >= n { + m = r + } + } + delta += (m - n) * (h + 1) + if delta < 0 { + return "", fmt.Errorf("cookiejar: invalid label %q", s) + } + n = m + for _, r := range s { + if r < n { + delta++ + if delta < 0 { + return "", fmt.Errorf("cookiejar: invalid label %q", s) + } + continue + } + if r > n { + continue + } + q := delta + for k := base; ; k += base { + t := k - bias + if t < tmin { + t = tmin + } else if t > tmax { + t = tmax + } + if q < t { + break + } + output = append(output, encodeDigit(t+(q-t)%(base-t))) + q = (q - t) / (base - t) + } + output = append(output, encodeDigit(q)) + bias = adapt(delta, h+1, h == b) + delta = 0 + h++ + remaining-- + } + delta++ + n++ + } + return string(output), nil +} + +func encodeDigit(digit int32) byte { + switch { + case 0 <= digit && digit < 26: + return byte(digit + 'a') + case 26 <= digit && digit < 36: + return byte(digit + ('0' - 26)) + } + panic("cookiejar: internal error in punycode encoding") +} + +// adapt is the bias adaptation function specified in section 6.1. +func adapt(delta, numPoints int32, firstTime bool) int32 { + if firstTime { + delta /= damp + } else { + delta /= 2 + } + delta += delta / numPoints + k := int32(0) + for delta > ((base-tmin)*tmax)/2 { + delta /= base - tmin + k += base + } + return k + (base-tmin+1)*delta/(delta+skew) +} + +// Strictly speaking, the remaining code below deals with IDNA (RFC 5890 and +// friends) and not Punycode (RFC 3492) per se. + +// acePrefix is the ASCII Compatible Encoding prefix. +const acePrefix = "xn--" + +// toASCII converts a domain or domain label to its ASCII form. For example, +// toASCII("bücher.example.com") is "xn--bcher-kva.example.com", and +// toASCII("golang") is "golang". +func toASCII(s string) (string, error) { + if ascii(s) { + return s, nil + } + labels := strings.Split(s, ".") + for i, label := range labels { + if !ascii(label) { + a, err := encode(acePrefix, label) + if err != nil { + return "", err + } + labels[i] = a + } + } + return strings.Join(labels, "."), nil +} + +func ascii(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] >= utf8.RuneSelf { + return false + } + } + return true +} diff --git a/src/pkg/net/http/cookiejar/punycode_test.go b/src/pkg/net/http/cookiejar/punycode_test.go new file mode 100644 index 000000000..0301de14e --- /dev/null +++ b/src/pkg/net/http/cookiejar/punycode_test.go @@ -0,0 +1,161 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cookiejar + +import ( + "testing" +) + +var punycodeTestCases = [...]struct { + s, encoded string +}{ + {"", ""}, + {"-", "--"}, + {"-a", "-a-"}, + {"-a-", "-a--"}, + {"a", "a-"}, + {"a-", "a--"}, + {"a-b", "a-b-"}, + {"books", "books-"}, + {"bücher", "bcher-kva"}, + {"Hello世界", "Hello-ck1hg65u"}, + {"ü", "tda"}, + {"üý", "tdac"}, + + // The test cases below come from RFC 3492 section 7.1 with Errata 3026. + { + // (A) Arabic (Egyptian). + "\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644" + + "\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F", + "egbpdaj6bu4bxfgehfvwxn", + }, + { + // (B) Chinese (simplified). + "\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587", + "ihqwcrb4cv8a8dqg056pqjye", + }, + { + // (C) Chinese (traditional). + "\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587", + "ihqwctvzc91f659drss3x8bo0yb", + }, + { + // (D) Czech. + "\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074" + + "\u011B\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D" + + "\u0065\u0073\u006B\u0079", + "Proprostnemluvesky-uyb24dma41a", + }, + { + // (E) Hebrew. + "\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8" + + "\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2" + + "\u05D1\u05E8\u05D9\u05EA", + "4dbcagdahymbxekheh6e0a7fei0b", + }, + { + // (F) Hindi (Devanagari). + "\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D" + + "\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939" + + "\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947" + + "\u0939\u0948\u0902", + "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd", + }, + { + // (G) Japanese (kanji and hiragana). + "\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092" + + "\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B", + "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa", + }, + { + // (H) Korean (Hangul syllables). + "\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774" + + "\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74" + + "\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C", + "989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5j" + + "psd879ccm6fea98c", + }, + { + // (I) Russian (Cyrillic). + "\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E" + + "\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440" + + "\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A" + + "\u0438", + "b1abfaaepdrnnbgefbadotcwatmq2g4l", + }, + { + // (J) Spanish. + "\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070" + + "\u0075\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070" + + "\u006C\u0065\u006D\u0065\u006E\u0074\u0065\u0068\u0061" + + "\u0062\u006C\u0061\u0072\u0065\u006E\u0045\u0073\u0070" + + "\u0061\u00F1\u006F\u006C", + "PorqunopuedensimplementehablarenEspaol-fmd56a", + }, + { + // (K) Vietnamese. + "\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B" + + "\u0068\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068" + + "\u1EC9\u006E\u00F3\u0069\u0074\u0069\u1EBF\u006E\u0067" + + "\u0056\u0069\u1EC7\u0074", + "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g", + }, + { + // (L) 3B. + "\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F", + "3B-ww4c5e180e575a65lsy2b", + }, + { + // (M) -with-SUPER-MONKEYS. + "\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074" + + "\u0068\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D" + + "\u004F\u004E\u004B\u0045\u0059\u0053", + "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n", + }, + { + // (N) Hello-Another-Way-. + "\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F" + + "\u0074\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D" + + "\u305D\u308C\u305E\u308C\u306E\u5834\u6240", + "Hello-Another-Way--fc4qua05auwb3674vfr0b", + }, + { + // (O) 2. + "\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032", + "2-u9tlzr9756bt3uc0v", + }, + { + // (P) MajiKoi5 + "\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059" + + "\u308B\u0035\u79D2\u524D", + "MajiKoi5-783gue6qz075azm5e", + }, + { + // (Q) de + "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0", + "de-jg4avhby1noc0d", + }, + { + // (R) + "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067", + "d9juau41awczczp", + }, + { + // (S) -> $1.00 <- + "\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020" + + "\u003C\u002D", + "-> $1.00 <--", + }, +} + +func TestPunycode(t *testing.T) { + for _, tc := range punycodeTestCases { + if got, err := encode("", tc.s); err != nil { + t.Errorf(`encode("", %q): %v`, tc.s, err) + } else if got != tc.encoded { + t.Errorf(`encode("", %q): got %q, want %q`, tc.s, got, tc.encoded) + } + } +} diff --git a/src/pkg/net/http/example_test.go b/src/pkg/net/http/example_test.go index ec814407d..22073eaf7 100644 --- a/src/pkg/net/http/example_test.go +++ b/src/pkg/net/http/example_test.go @@ -43,10 +43,10 @@ func ExampleGet() { log.Fatal(err) } robots, err := ioutil.ReadAll(res.Body) + res.Body.Close() if err != nil { log.Fatal(err) } - res.Body.Close() fmt.Printf("%s", robots) } diff --git a/src/pkg/net/http/export_test.go b/src/pkg/net/http/export_test.go index 13640ca85..a7bca20a0 100644 --- a/src/pkg/net/http/export_test.go +++ b/src/pkg/net/http/export_test.go @@ -7,12 +7,25 @@ package http -import "time" +import ( + "net" + "time" +) + +func NewLoggingConn(baseName string, c net.Conn) net.Conn { + return newLoggingConn(baseName, c) +} + +func (t *Transport) NumPendingRequestsForTesting() int { + t.reqMu.Lock() + defer t.reqMu.Unlock() + return len(t.reqConn) +} func (t *Transport) IdleConnKeysForTesting() (keys []string) { keys = make([]string, 0) - t.lk.Lock() - defer t.lk.Unlock() + t.idleMu.Lock() + defer t.idleMu.Unlock() if t.idleConn == nil { return } @@ -23,8 +36,8 @@ func (t *Transport) IdleConnKeysForTesting() (keys []string) { } func (t *Transport) IdleConnCountForTesting(cacheKey string) int { - t.lk.Lock() - defer t.lk.Unlock() + t.idleMu.Lock() + defer t.idleMu.Unlock() if t.idleConn == nil { return 0 } diff --git a/src/pkg/net/http/filetransport_test.go b/src/pkg/net/http/filetransport_test.go index 039926b53..6f1a537e2 100644 --- a/src/pkg/net/http/filetransport_test.go +++ b/src/pkg/net/http/filetransport_test.go @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package http_test +package http import ( "io/ioutil" - "net/http" "os" "path/filepath" "testing" @@ -32,9 +31,9 @@ func TestFileTransport(t *testing.T) { defer os.Remove(dname) defer os.Remove(fname) - tr := &http.Transport{} - tr.RegisterProtocol("file", http.NewFileTransport(http.Dir(dname))) - c := &http.Client{Transport: tr} + tr := &Transport{} + tr.RegisterProtocol("file", NewFileTransport(Dir(dname))) + c := &Client{Transport: tr} fooURLs := []string{"file:///foo.txt", "file://../foo.txt"} for _, urlstr := range fooURLs { @@ -62,4 +61,5 @@ func TestFileTransport(t *testing.T) { if res.StatusCode != 404 { t.Errorf("for %s, StatusCode = %d, want 404", badURL, res.StatusCode) } + res.Body.Close() } diff --git a/src/pkg/net/http/fs.go b/src/pkg/net/http/fs.go index f35dd32c3..b6bea0dfa 100644 --- a/src/pkg/net/http/fs.go +++ b/src/pkg/net/http/fs.go @@ -11,6 +11,8 @@ import ( "fmt" "io" "mime" + "mime/multipart" + "net/textproto" "os" "path" "path/filepath" @@ -26,7 +28,8 @@ import ( type Dir string func (d Dir) Open(name string) (File, error) { - if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 { + if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || + strings.Contains(name, "\x00") { return nil, errors.New("http: invalid character in file path") } dir := string(d) @@ -97,6 +100,9 @@ func dirList(w ResponseWriter, f File) { // The content's Seek method must work: ServeContent uses // a seek to the end of the content to determine its size. // +// If the caller has set w's ETag header, ServeContent uses it to +// handle requests using If-Range and If-None-Match. +// // Note that *os.File implements the io.ReadSeeker interface. func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) { size, err := content.Seek(0, os.SEEK_END) @@ -119,12 +125,17 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, if checkLastModified(w, r, modtime) { return } + rangeReq, done := checkETag(w, r) + if done { + return + } code := StatusOK // If Content-Type isn't set, use the file's extension to find it. - if w.Header().Get("Content-Type") == "" { - ctype := mime.TypeByExtension(filepath.Ext(name)) + ctype := w.Header().Get("Content-Type") + if ctype == "" { + ctype = mime.TypeByExtension(filepath.Ext(name)) if ctype == "" { // read a chunk to decide between utf-8 text and binary var buf [1024]byte @@ -141,18 +152,34 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, } // handle Content-Range header. - // TODO(adg): handle multiple ranges sendSize := size + var sendContent io.Reader = content if size >= 0 { - ranges, err := parseRange(r.Header.Get("Range"), size) - if err == nil && len(ranges) > 1 { - err = errors.New("multiple ranges not supported") - } + ranges, err := parseRange(rangeReq, size) if err != nil { Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) return } - if len(ranges) == 1 { + if sumRangesSize(ranges) >= size { + // The total number of bytes in all the ranges + // is larger than the size of the file by + // itself, so this is probably an attack, or a + // dumb client. Ignore the range request. + ranges = nil + } + switch { + case len(ranges) == 1: + // RFC 2616, Section 14.16: + // "When an HTTP message includes the content of a single + // range (for example, a response to a request for a + // single range, or to a request for a set of ranges + // that overlap without any holes), this content is + // transmitted with a Content-Range header, and a + // Content-Length header showing the number of bytes + // actually transferred. + // ... + // A response to a request for a single range MUST NOT + // be sent using the multipart/byteranges media type." ra := ranges[0] if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil { Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) @@ -160,7 +187,41 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, } sendSize = ra.length code = StatusPartialContent - w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, size)) + w.Header().Set("Content-Range", ra.contentRange(size)) + case len(ranges) > 1: + for _, ra := range ranges { + if ra.start > size { + Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) + return + } + } + sendSize = rangesMIMESize(ranges, ctype, size) + code = StatusPartialContent + + pr, pw := io.Pipe() + mw := multipart.NewWriter(pw) + w.Header().Set("Content-Type", "multipart/byteranges; boundary="+mw.Boundary()) + sendContent = pr + defer pr.Close() // cause writing goroutine to fail and exit if CopyN doesn't finish. + go func() { + for _, ra := range ranges { + part, err := mw.CreatePart(ra.mimeHeader(ctype, size)) + if err != nil { + pw.CloseWithError(err) + return + } + if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil { + pw.CloseWithError(err) + return + } + if _, err := io.CopyN(part, content, ra.length); err != nil { + pw.CloseWithError(err) + return + } + } + mw.Close() + pw.Close() + }() } w.Header().Set("Accept-Ranges", "bytes") @@ -172,11 +233,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, w.WriteHeader(code) if r.Method != "HEAD" { - if sendSize == -1 { - io.Copy(w, content) - } else { - io.CopyN(w, content, sendSize) - } + io.CopyN(w, sendContent, sendSize) } } @@ -190,6 +247,9 @@ func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool { // The Date-Modified header truncates sub-second precision, so // use mtime < t+1s instead of mtime <= t to check for unmodified. if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) { + h := w.Header() + delete(h, "Content-Type") + delete(h, "Content-Length") w.WriteHeader(StatusNotModified) return true } @@ -197,6 +257,58 @@ func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool { return false } +// checkETag implements If-None-Match and If-Range checks. +// The ETag must have been previously set in the ResponseWriter's headers. +// +// The return value is the effective request "Range" header to use and +// whether this request is now considered done. +func checkETag(w ResponseWriter, r *Request) (rangeReq string, done bool) { + etag := w.Header().get("Etag") + rangeReq = r.Header.get("Range") + + // Invalidate the range request if the entity doesn't match the one + // the client was expecting. + // "If-Range: version" means "ignore the Range: header unless version matches the + // current file." + // We only support ETag versions. + // The caller must have set the ETag on the response already. + if ir := r.Header.get("If-Range"); ir != "" && ir != etag { + // TODO(bradfitz): handle If-Range requests with Last-Modified + // times instead of ETags? I'd rather not, at least for + // now. That seems like a bug/compromise in the RFC 2616, and + // I've never heard of anybody caring about that (yet). + rangeReq = "" + } + + if inm := r.Header.get("If-None-Match"); inm != "" { + // Must know ETag. + if etag == "" { + return rangeReq, false + } + + // TODO(bradfitz): non-GET/HEAD requests require more work: + // sending a different status code on matches, and + // also can't use weak cache validators (those with a "W/ + // prefix). But most users of ServeContent will be using + // it on GET or HEAD, so only support those for now. + if r.Method != "GET" && r.Method != "HEAD" { + return rangeReq, false + } + + // TODO(bradfitz): deal with comma-separated or multiple-valued + // list of If-None-match values. For now just handle the common + // case of a single item. + if inm == etag || inm == "*" { + h := w.Header() + delete(h, "Content-Type") + delete(h, "Content-Length") + w.WriteHeader(StatusNotModified) + return "", true + } + } + return rangeReq, false +} + // name is '/'-separated, not filepath.Separator. func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) { const indexPage = "/index.html" @@ -243,9 +355,6 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec // use contents of index.html for directory, if present if d.IsDir() { - if checkLastModified(w, r, d.ModTime()) { - return - } index := name + indexPage ff, err := fs.Open(index) if err == nil { @@ -259,11 +368,16 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec } } + // Still a directory? (we didn't find an index.html file) if d.IsDir() { + if checkLastModified(w, r, d.ModTime()) { + return + } dirList(w, f) return } + // serverContent will check modification time serveContent(w, r, d.Name(), d.ModTime(), d.Size(), f) } @@ -312,6 +426,17 @@ type httpRange struct { start, length int64 } +func (r httpRange) contentRange(size int64) string { + return fmt.Sprintf("bytes %d-%d/%d", r.start, r.start+r.length-1, size) +} + +func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader { + return textproto.MIMEHeader{ + "Content-Range": {r.contentRange(size)}, + "Content-Type": {contentType}, + } +} + // parseRange parses a Range header string as per RFC 2616. func parseRange(s string, size int64) ([]httpRange, error) { if s == "" { @@ -323,11 +448,15 @@ func parseRange(s string, size int64) ([]httpRange, error) { } var ranges []httpRange for _, ra := range strings.Split(s[len(b):], ",") { + ra = strings.TrimSpace(ra) + if ra == "" { + continue + } i := strings.Index(ra, "-") if i < 0 { return nil, errors.New("invalid range") } - start, end := ra[:i], ra[i+1:] + start, end := strings.TrimSpace(ra[:i]), strings.TrimSpace(ra[i+1:]) var r httpRange if start == "" { // If no start is specified, end specifies the @@ -365,3 +494,32 @@ func parseRange(s string, size int64) ([]httpRange, error) { } return ranges, nil } + +// countingWriter counts how many bytes have been written to it. +type countingWriter int64 + +func (w *countingWriter) Write(p []byte) (n int, err error) { + *w += countingWriter(len(p)) + return len(p), nil +} + +// rangesMIMESize returns the nunber of bytes it takes to encode the +// provided ranges as a multipart response. +func rangesMIMESize(ranges []httpRange, contentType string, contentSize int64) (encSize int64) { + var w countingWriter + mw := multipart.NewWriter(&w) + for _, ra := range ranges { + mw.CreatePart(ra.mimeHeader(contentType, contentSize)) + encSize += ra.length + } + mw.Close() + encSize += int64(w) + return +} + +func sumRangesSize(ranges []httpRange) (size int64) { + for _, ra := range ranges { + size += ra.length + } + return +} diff --git a/src/pkg/net/http/fs_test.go b/src/pkg/net/http/fs_test.go index 5aa93ce58..0dd6d0df9 100644 --- a/src/pkg/net/http/fs_test.go +++ b/src/pkg/net/http/fs_test.go @@ -10,12 +10,15 @@ import ( "fmt" "io" "io/ioutil" + "mime" + "mime/multipart" "net" . "net/http" "net/http/httptest" "net/url" "os" "os/exec" + "path" "path/filepath" "regexp" "runtime" @@ -25,24 +28,33 @@ import ( ) const ( - testFile = "testdata/file" - testFileLength = 11 + testFile = "testdata/file" + testFileLen = 11 ) +type wantRange struct { + start, end int64 // range [start,end) +} + var ServeFileRangeTests = []struct { - start, end int - r string - code int + r string + code int + ranges []wantRange }{ - {0, testFileLength, "", StatusOK}, - {0, 5, "0-4", StatusPartialContent}, - {2, testFileLength, "2-", StatusPartialContent}, - {testFileLength - 5, testFileLength, "-5", StatusPartialContent}, - {3, 8, "3-7", StatusPartialContent}, - {0, 0, "20-", StatusRequestedRangeNotSatisfiable}, + {r: "", code: StatusOK}, + {r: "bytes=0-4", code: StatusPartialContent, ranges: []wantRange{{0, 5}}}, + {r: "bytes=2-", code: StatusPartialContent, ranges: []wantRange{{2, testFileLen}}}, + {r: "bytes=-5", code: StatusPartialContent, ranges: []wantRange{{testFileLen - 5, testFileLen}}}, + {r: "bytes=3-7", code: StatusPartialContent, ranges: []wantRange{{3, 8}}}, + {r: "bytes=20-", code: StatusRequestedRangeNotSatisfiable}, + {r: "bytes=0-0,-2", code: StatusPartialContent, ranges: []wantRange{{0, 1}, {testFileLen - 2, testFileLen}}}, + {r: "bytes=0-1,5-8", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, 9}}}, + {r: "bytes=0-1,5-", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, testFileLen}}}, + {r: "bytes=0-,1-,2-,3-,4-", code: StatusOK}, // ignore wasteful range request } func TestServeFile(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "testdata/file") })) @@ -65,33 +77,86 @@ func TestServeFile(t *testing.T) { // straight GET _, body := getBody(t, "straight get", req) - if !equal(body, file) { + if !bytes.Equal(body, file) { t.Fatalf("body mismatch: got %q, want %q", body, file) } // Range tests - for i, rt := range ServeFileRangeTests { - req.Header.Set("Range", "bytes="+rt.r) - if rt.r == "" { - req.Header["Range"] = nil +Cases: + for _, rt := range ServeFileRangeTests { + if rt.r != "" { + req.Header.Set("Range", rt.r) } - r, body := getBody(t, fmt.Sprintf("test %d", i), req) - if r.StatusCode != rt.code { - t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code) + resp, body := getBody(t, fmt.Sprintf("range test %q", rt.r), req) + if resp.StatusCode != rt.code { + t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, resp.StatusCode, rt.code) } if rt.code == StatusRequestedRangeNotSatisfiable { continue } - h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength) - if rt.r == "" { - h = "" + wantContentRange := "" + if len(rt.ranges) == 1 { + rng := rt.ranges[0] + wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen) + } + cr := resp.Header.Get("Content-Range") + if cr != wantContentRange { + t.Errorf("range=%q: Content-Range = %q, want %q", rt.r, cr, wantContentRange) } - cr := r.Header.Get("Content-Range") - if cr != h { - t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h) + ct := resp.Header.Get("Content-Type") + if len(rt.ranges) == 1 { + rng := rt.ranges[0] + wantBody := file[rng.start:rng.end] + if !bytes.Equal(body, wantBody) { + t.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody) + } + if strings.HasPrefix(ct, "multipart/byteranges") { + t.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r, ct) + } } - if !equal(body, file[rt.start:rt.end]) { - t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end]) + if len(rt.ranges) > 1 { + typ, params, err := mime.ParseMediaType(ct) + if err != nil { + t.Errorf("range=%q content-type = %q; %v", rt.r, ct, err) + continue + } + if typ != "multipart/byteranges" { + t.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r, typ) + continue + } + if params["boundary"] == "" { + t.Errorf("range=%q content-type = %q; lacks boundary", rt.r, ct) + continue + } + if g, w := resp.ContentLength, int64(len(body)); g != w { + t.Errorf("range=%q Content-Length = %d; want %d", rt.r, g, w) + continue + } + mr := multipart.NewReader(bytes.NewReader(body), params["boundary"]) + for ri, rng := range rt.ranges { + part, err := mr.NextPart() + if err != nil { + t.Errorf("range=%q, reading part index %d: %v", rt.r, ri, err) + continue Cases + } + wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen) + if g, w := part.Header.Get("Content-Range"), wantContentRange; g != w { + t.Errorf("range=%q: part Content-Range = %q; want %q", rt.r, g, w) + } + body, err := ioutil.ReadAll(part) + if err != nil { + t.Errorf("range=%q, reading part index %d body: %v", rt.r, ri, err) + continue Cases + } + wantBody := file[rng.start:rng.end] + if !bytes.Equal(body, wantBody) { + t.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody) + } + } + _, err = mr.NextPart() + if err != io.EOF { + t.Errorf("range=%q; expected final error io.EOF; got %v", rt.r, err) + } } } } @@ -105,6 +170,7 @@ var fsRedirectTestData = []struct { } func TestFSRedirect(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir(".")))) defer ts.Close() @@ -129,6 +195,7 @@ func (fs *testFileSystem) Open(name string) (File, error) { } func TestFileServerCleans(t *testing.T) { + defer checkLeakedTransports(t) ch := make(chan string, 1) fs := FileServer(&testFileSystem{func(name string) (File, error) { ch <- name @@ -160,6 +227,7 @@ func mustRemoveAll(dir string) { } func TestFileServerImplicitLeadingSlash(t *testing.T) { + defer checkLeakedTransports(t) tempDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("TempDir: %v", err) @@ -193,8 +261,7 @@ func TestFileServerImplicitLeadingSlash(t *testing.T) { func TestDirJoin(t *testing.T) { wfi, err := os.Stat("/etc/hosts") if err != nil { - t.Logf("skipping test; no /etc/hosts file") - return + t.Skip("skipping test; no /etc/hosts file") } test := func(d Dir, name string) { f, err := d.Open(name) @@ -239,6 +306,7 @@ func TestEmptyDirOpenCWD(t *testing.T) { } func TestServeFileContentType(t *testing.T) { + defer checkLeakedTransports(t) const ctype = "icecream/chocolate" ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.FormValue("override") == "1" { @@ -255,12 +323,14 @@ func TestServeFileContentType(t *testing.T) { if h := resp.Header.Get("Content-Type"); h != want { t.Errorf("Content-Type mismatch: got %q, want %q", h, want) } + resp.Body.Close() } get("0", "text/plain; charset=utf-8") get("1", ctype) } func TestServeFileMimeType(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "testdata/style.css") })) @@ -269,6 +339,7 @@ func TestServeFileMimeType(t *testing.T) { if err != nil { t.Fatal(err) } + resp.Body.Close() want := "text/css; charset=utf-8" if h := resp.Header.Get("Content-Type"); h != want { t.Errorf("Content-Type mismatch: got %q, want %q", h, want) @@ -276,6 +347,7 @@ func TestServeFileMimeType(t *testing.T) { } func TestServeFileFromCWD(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "fs_test.go") })) @@ -284,12 +356,14 @@ func TestServeFileFromCWD(t *testing.T) { if err != nil { t.Fatal(err) } + r.Body.Close() if r.StatusCode != 200 { t.Fatalf("expected 200 OK, got %s", r.Status) } } func TestServeFileWithContentEncoding(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Content-Encoding", "foo") ServeFile(w, r, "testdata/file") @@ -299,12 +373,14 @@ func TestServeFileWithContentEncoding(t *testing.T) { if err != nil { t.Fatal(err) } + resp.Body.Close() if g, e := resp.ContentLength, int64(-1); g != e { t.Errorf("Content-Length mismatch: got %d, want %d", g, e) } } func TestServeIndexHtml(t *testing.T) { + defer checkLeakedTransports(t) const want = "index.html says hello\n" ts := httptest.NewServer(FileServer(Dir("."))) defer ts.Close() @@ -325,64 +401,289 @@ func TestServeIndexHtml(t *testing.T) { } } -func TestServeContent(t *testing.T) { - type req struct { - name string - modtime time.Time - content io.ReadSeeker - } - ch := make(chan req, 1) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - p := <-ch - ServeContent(w, r, p.name, p.modtime, p.content) - })) +func TestFileServerZeroByte(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewServer(FileServer(Dir("."))) defer ts.Close() - css, err := os.Open("testdata/style.css") + res, err := Get(ts.URL + "/..\x00") if err != nil { t.Fatal(err) } - defer css.Close() + b, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal("reading Body:", err) + } + if res.StatusCode == 200 { + t.Errorf("got status 200; want an error. Body is:\n%s", string(b)) + } +} + +type fakeFileInfo struct { + dir bool + basename string + modtime time.Time + ents []*fakeFileInfo + contents string +} + +func (f *fakeFileInfo) Name() string { return f.basename } +func (f *fakeFileInfo) Sys() interface{} { return nil } +func (f *fakeFileInfo) ModTime() time.Time { return f.modtime } +func (f *fakeFileInfo) IsDir() bool { return f.dir } +func (f *fakeFileInfo) Size() int64 { return int64(len(f.contents)) } +func (f *fakeFileInfo) Mode() os.FileMode { + if f.dir { + return 0755 | os.ModeDir + } + return 0644 +} + +type fakeFile struct { + io.ReadSeeker + fi *fakeFileInfo + path string // as opened +} + +func (f *fakeFile) Close() error { return nil } +func (f *fakeFile) Stat() (os.FileInfo, error) { return f.fi, nil } +func (f *fakeFile) Readdir(count int) ([]os.FileInfo, error) { + if !f.fi.dir { + return nil, os.ErrInvalid + } + var fis []os.FileInfo + for _, fi := range f.fi.ents { + fis = append(fis, fi) + } + return fis, nil +} + +type fakeFS map[string]*fakeFileInfo + +func (fs fakeFS) Open(name string) (File, error) { + name = path.Clean(name) + f, ok := fs[name] + if !ok { + println("fake filesystem didn't find file", name) + return nil, os.ErrNotExist + } + return &fakeFile{ReadSeeker: strings.NewReader(f.contents), fi: f, path: name}, nil +} + +func TestDirectoryIfNotModified(t *testing.T) { + defer checkLeakedTransports(t) + const indexContents = "I am a fake index.html file" + fileMod := time.Unix(1000000000, 0).UTC() + fileModStr := fileMod.Format(TimeFormat) + dirMod := time.Unix(123, 0).UTC() + indexFile := &fakeFileInfo{ + basename: "index.html", + modtime: fileMod, + contents: indexContents, + } + fs := fakeFS{ + "/": &fakeFileInfo{ + dir: true, + modtime: dirMod, + ents: []*fakeFileInfo{indexFile}, + }, + "/index.html": indexFile, + } + + ts := httptest.NewServer(FileServer(fs)) + defer ts.Close() - ch <- req{"style.css", time.Time{}, css} res, err := Get(ts.URL) if err != nil { t.Fatal(err) } - if g, e := res.Header.Get("Content-Type"), "text/css; charset=utf-8"; g != e { - t.Errorf("style.css: content type = %q, want %q", g, e) + b, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) } - if g := res.Header.Get("Last-Modified"); g != "" { - t.Errorf("want empty Last-Modified; got %q", g) + if string(b) != indexContents { + t.Fatalf("Got body %q; want %q", b, indexContents) } + res.Body.Close() + + lastMod := res.Header.Get("Last-Modified") + if lastMod != fileModStr { + t.Fatalf("initial Last-Modified = %q; want %q", lastMod, fileModStr) + } + + req, _ := NewRequest("GET", ts.URL, nil) + req.Header.Set("If-Modified-Since", lastMod) - fi, err := css.Stat() + res, err = DefaultClient.Do(req) if err != nil { t.Fatal(err) } - ch <- req{"style.html", fi.ModTime(), css} - res, err = Get(ts.URL) + if res.StatusCode != 304 { + t.Fatalf("Code after If-Modified-Since request = %v; want 304", res.StatusCode) + } + res.Body.Close() + + // Advance the index.html file's modtime, but not the directory's. + indexFile.modtime = indexFile.modtime.Add(1 * time.Hour) + + res, err = DefaultClient.Do(req) if err != nil { t.Fatal(err) } - if g, e := res.Header.Get("Content-Type"), "text/html; charset=utf-8"; g != e { - t.Errorf("style.html: content type = %q, want %q", g, e) + if res.StatusCode != 200 { + t.Fatalf("Code after second If-Modified-Since request = %v; want 200; res is %#v", res.StatusCode, res) } - if g := res.Header.Get("Last-Modified"); g == "" { - t.Errorf("want non-empty last-modified") + res.Body.Close() +} + +func mustStat(t *testing.T, fileName string) os.FileInfo { + fi, err := os.Stat(fileName) + if err != nil { + t.Fatal(err) + } + return fi +} + +func TestServeContent(t *testing.T) { + defer checkLeakedTransports(t) + type serveParam struct { + name string + modtime time.Time + content io.ReadSeeker + contentType string + etag string + } + servec := make(chan serveParam, 1) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + p := <-servec + if p.etag != "" { + w.Header().Set("ETag", p.etag) + } + if p.contentType != "" { + w.Header().Set("Content-Type", p.contentType) + } + ServeContent(w, r, p.name, p.modtime, p.content) + })) + defer ts.Close() + + type testCase struct { + file string + modtime time.Time + serveETag string // optional + serveContentType string // optional + reqHeader map[string]string + wantLastMod string + wantContentType string + wantStatus int + } + htmlModTime := mustStat(t, "testdata/index.html").ModTime() + tests := map[string]testCase{ + "no_last_modified": { + file: "testdata/style.css", + wantContentType: "text/css; charset=utf-8", + wantStatus: 200, + }, + "with_last_modified": { + file: "testdata/index.html", + wantContentType: "text/html; charset=utf-8", + modtime: htmlModTime, + wantLastMod: htmlModTime.UTC().Format(TimeFormat), + wantStatus: 200, + }, + "not_modified_modtime": { + file: "testdata/style.css", + modtime: htmlModTime, + reqHeader: map[string]string{ + "If-Modified-Since": htmlModTime.UTC().Format(TimeFormat), + }, + wantStatus: 304, + }, + "not_modified_modtime_with_contenttype": { + file: "testdata/style.css", + serveContentType: "text/css", // explicit content type + modtime: htmlModTime, + reqHeader: map[string]string{ + "If-Modified-Since": htmlModTime.UTC().Format(TimeFormat), + }, + wantStatus: 304, + }, + "not_modified_etag": { + file: "testdata/style.css", + serveETag: `"foo"`, + reqHeader: map[string]string{ + "If-None-Match": `"foo"`, + }, + wantStatus: 304, + }, + "range_good": { + file: "testdata/style.css", + serveETag: `"A"`, + reqHeader: map[string]string{ + "Range": "bytes=0-4", + }, + wantStatus: StatusPartialContent, + wantContentType: "text/css; charset=utf-8", + }, + // An If-Range resource for entity "A", but entity "B" is now current. + // The Range request should be ignored. + "range_no_match": { + file: "testdata/style.css", + serveETag: `"A"`, + reqHeader: map[string]string{ + "Range": "bytes=0-4", + "If-Range": `"B"`, + }, + wantStatus: 200, + wantContentType: "text/css; charset=utf-8", + }, + } + for testName, tt := range tests { + f, err := os.Open(tt.file) + if err != nil { + t.Fatalf("test %q: %v", testName, err) + } + defer f.Close() + + servec <- serveParam{ + name: filepath.Base(tt.file), + content: f, + modtime: tt.modtime, + etag: tt.serveETag, + contentType: tt.serveContentType, + } + req, err := NewRequest("GET", ts.URL, nil) + if err != nil { + t.Fatal(err) + } + for k, v := range tt.reqHeader { + req.Header.Set(k, v) + } + res, err := DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + io.Copy(ioutil.Discard, res.Body) + res.Body.Close() + if res.StatusCode != tt.wantStatus { + t.Errorf("test %q: status = %d; want %d", testName, res.StatusCode, tt.wantStatus) + } + if g, e := res.Header.Get("Content-Type"), tt.wantContentType; g != e { + t.Errorf("test %q: content-type = %q, want %q", testName, g, e) + } + if g, e := res.Header.Get("Last-Modified"), tt.wantLastMod; g != e { + t.Errorf("test %q: last-modified = %q, want %q", testName, g, e) + } } } // verifies that sendfile is being used on Linux func TestLinuxSendfile(t *testing.T) { + defer checkLeakedTransports(t) if runtime.GOOS != "linux" { - t.Logf("skipping; linux-only test") - return + t.Skip("skipping; linux-only test") } - _, err := exec.LookPath("strace") - if err != nil { - t.Logf("skipping; strace not found in path") - return + if _, err := exec.LookPath("strace"); err != nil { + t.Skip("skipping; strace not found in path") } ln, err := net.Listen("tcp", "127.0.0.1:0") @@ -401,10 +702,8 @@ func TestLinuxSendfile(t *testing.T) { child.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...) child.Stdout = &buf child.Stderr = &buf - err = child.Start() - if err != nil { - t.Logf("skipping; failed to start straced child: %v", err) - return + if err := child.Start(); err != nil { + t.Skipf("skipping; failed to start straced child: %v", err) } res, err := Get(fmt.Sprintf("http://%s/", ln.Addr())) @@ -464,15 +763,3 @@ func TestLinuxSendfileChild(*testing.T) { panic(err) } } - -func equal(a, b []byte) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} diff --git a/src/pkg/net/http/header.go b/src/pkg/net/http/header.go index b107c312d..f479b7b4e 100644 --- a/src/pkg/net/http/header.go +++ b/src/pkg/net/http/header.go @@ -5,11 +5,11 @@ package http import ( - "fmt" "io" "net/textproto" "sort" "strings" + "time" ) // A Header represents the key-value pairs in an HTTP header. @@ -36,6 +36,14 @@ func (h Header) Get(key string) string { return textproto.MIMEHeader(h).Get(key) } +// get is like Get, but key must already be in CanonicalHeaderKey form. +func (h Header) get(key string) string { + if v := h[key]; len(v) > 0 { + return v[0] + } + return "" +} + // Del deletes the values associated with key. func (h Header) Del(key string) { textproto.MIMEHeader(h).Del(key) @@ -46,24 +54,87 @@ func (h Header) Write(w io.Writer) error { return h.WriteSubset(w, nil) } +func (h Header) clone() Header { + h2 := make(Header, len(h)) + for k, vv := range h { + vv2 := make([]string, len(vv)) + copy(vv2, vv) + h2[k] = vv2 + } + return h2 +} + +var timeFormats = []string{ + TimeFormat, + time.RFC850, + time.ANSIC, +} + +// ParseTime parses a time header (such as the Date: header), +// trying each of the three formats allowed by HTTP/1.1: +// TimeFormat, time.RFC850, and time.ANSIC. +func ParseTime(text string) (t time.Time, err error) { + for _, layout := range timeFormats { + t, err = time.Parse(layout, text) + if err == nil { + return + } + } + return +} + var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ") +type writeStringer interface { + WriteString(string) (int, error) +} + +// stringWriter implements WriteString on a Writer. +type stringWriter struct { + w io.Writer +} + +func (w stringWriter) WriteString(s string) (n int, err error) { + return w.w.Write([]byte(s)) +} + +type keyValues struct { + key string + values []string +} + +type byKey []keyValues + +func (s byKey) Len() int { return len(s) } +func (s byKey) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s byKey) Less(i, j int) bool { return s[i].key < s[j].key } + +func (h Header) sortedKeyValues(exclude map[string]bool) []keyValues { + kvs := make([]keyValues, 0, len(h)) + for k, vv := range h { + if !exclude[k] { + kvs = append(kvs, keyValues{k, vv}) + } + } + sort.Sort(byKey(kvs)) + return kvs +} + // WriteSubset writes a header in wire format. // If exclude is not nil, keys where exclude[key] == true are not written. func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { - keys := make([]string, 0, len(h)) - for k := range h { - if exclude == nil || !exclude[k] { - keys = append(keys, k) - } + ws, ok := w.(writeStringer) + if !ok { + ws = stringWriter{w} } - sort.Strings(keys) - for _, k := range keys { - for _, v := range h[k] { + for _, kv := range h.sortedKeyValues(exclude) { + for _, v := range kv.values { v = headerNewlineToSpace.Replace(v) - v = strings.TrimSpace(v) - if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil { - return err + v = textproto.TrimString(v) + for _, s := range []string{kv.key, ": ", v, "\r\n"} { + if _, err := ws.WriteString(s); err != nil { + return err + } } } } @@ -76,3 +147,43 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { // the rest are converted to lowercase. For example, the // canonical key for "accept-encoding" is "Accept-Encoding". func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) } + +// hasToken returns whether token appears with v, ASCII +// case-insensitive, with space or comma boundaries. +// token must be all lowercase. +// v may contain mixed cased. +func hasToken(v, token string) bool { + if len(token) > len(v) || token == "" { + return false + } + if v == token { + return true + } + for sp := 0; sp <= len(v)-len(token); sp++ { + // Check that first character is good. + // The token is ASCII, so checking only a single byte + // is sufficient. We skip this potential starting + // position if both the first byte and its potential + // ASCII uppercase equivalent (b|0x20) don't match. + // False positives ('^' => '~') are caught by EqualFold. + if b := v[sp]; b != token[0] && b|0x20 != token[0] { + continue + } + // Check that start pos is on a valid token boundary. + if sp > 0 && !isTokenBoundary(v[sp-1]) { + continue + } + // Check that end pos is on a valid token boundary. + if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) { + continue + } + if strings.EqualFold(v[sp:sp+len(token)], token) { + return true + } + } + return false +} + +func isTokenBoundary(b byte) bool { + return b == ' ' || b == ',' || b == '\t' +} diff --git a/src/pkg/net/http/header_test.go b/src/pkg/net/http/header_test.go index ccdee8a97..2313b5549 100644 --- a/src/pkg/net/http/header_test.go +++ b/src/pkg/net/http/header_test.go @@ -7,6 +7,7 @@ package http import ( "bytes" "testing" + "time" ) var headerWriteTests = []struct { @@ -67,6 +68,24 @@ var headerWriteTests = []struct { nil, "Blank: \r\nDouble-Blank: \r\nDouble-Blank: \r\n", }, + // Tests header sorting when over the insertion sort threshold side: + { + Header{ + "k1": {"1a", "1b"}, + "k2": {"2a", "2b"}, + "k3": {"3a", "3b"}, + "k4": {"4a", "4b"}, + "k5": {"5a", "5b"}, + "k6": {"6a", "6b"}, + "k7": {"7a", "7b"}, + "k8": {"8a", "8b"}, + "k9": {"9a", "9b"}, + }, + map[string]bool{"k5": true}, + "k1: 1a\r\nk1: 1b\r\nk2: 2a\r\nk2: 2b\r\nk3: 3a\r\nk3: 3b\r\n" + + "k4: 4a\r\nk4: 4b\r\nk6: 6a\r\nk6: 6b\r\n" + + "k7: 7a\r\nk7: 7b\r\nk8: 8a\r\nk8: 8b\r\nk9: 9a\r\nk9: 9b\r\n", + }, } func TestHeaderWrite(t *testing.T) { @@ -79,3 +98,107 @@ func TestHeaderWrite(t *testing.T) { buf.Reset() } } + +var parseTimeTests = []struct { + h Header + err bool +}{ + {Header{"Date": {""}}, true}, + {Header{"Date": {"invalid"}}, true}, + {Header{"Date": {"1994-11-06T08:49:37Z00:00"}}, true}, + {Header{"Date": {"Sun, 06 Nov 1994 08:49:37 GMT"}}, false}, + {Header{"Date": {"Sunday, 06-Nov-94 08:49:37 GMT"}}, false}, + {Header{"Date": {"Sun Nov 6 08:49:37 1994"}}, false}, +} + +func TestParseTime(t *testing.T) { + expect := time.Date(1994, 11, 6, 8, 49, 37, 0, time.UTC) + for i, test := range parseTimeTests { + d, err := ParseTime(test.h.Get("Date")) + if err != nil { + if !test.err { + t.Errorf("#%d:\n got err: %v", i, err) + } + continue + } + if test.err { + t.Errorf("#%d:\n should err", i) + continue + } + if !expect.Equal(d) { + t.Errorf("#%d:\n got: %v\nwant: %v", i, d, expect) + } + } +} + +type hasTokenTest struct { + header string + token string + want bool +} + +var hasTokenTests = []hasTokenTest{ + {"", "", false}, + {"", "foo", false}, + {"foo", "foo", true}, + {"foo ", "foo", true}, + {" foo", "foo", true}, + {" foo ", "foo", true}, + {"foo,bar", "foo", true}, + {"bar,foo", "foo", true}, + {"bar, foo", "foo", true}, + {"bar,foo, baz", "foo", true}, + {"bar, foo,baz", "foo", true}, + {"bar,foo, baz", "foo", true}, + {"bar, foo, baz", "foo", true}, + {"FOO", "foo", true}, + {"FOO ", "foo", true}, + {" FOO", "foo", true}, + {" FOO ", "foo", true}, + {"FOO,BAR", "foo", true}, + {"BAR,FOO", "foo", true}, + {"BAR, FOO", "foo", true}, + {"BAR,FOO, baz", "foo", true}, + {"BAR, FOO,BAZ", "foo", true}, + {"BAR,FOO, BAZ", "foo", true}, + {"BAR, FOO, BAZ", "foo", true}, + {"foobar", "foo", false}, + {"barfoo ", "foo", false}, +} + +func TestHasToken(t *testing.T) { + for _, tt := range hasTokenTests { + if hasToken(tt.header, tt.token) != tt.want { + t.Errorf("hasToken(%q, %q) = %v; want %v", tt.header, tt.token, !tt.want, tt.want) + } + } +} + +var testHeader = Header{ + "Content-Length": {"123"}, + "Content-Type": {"text/plain"}, + "Date": {"some date at some time Z"}, + "Server": {"Go http package"}, +} + +var buf bytes.Buffer + +func BenchmarkHeaderWriteSubset(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + buf.Reset() + testHeader.WriteSubset(&buf, nil) + } +} + +func TestHeaderWriteSubsetMallocs(t *testing.T) { + n := testing.AllocsPerRun(100, func() { + buf.Reset() + testHeader.WriteSubset(&buf, nil) + }) + if n > 1 { + // TODO(bradfitz,rsc): once we can sort without allocating, + // make this an error. See http://golang.org/issue/3761 + // t.Errorf("got %v allocs, want <= %v", n, 1) + } +} diff --git a/src/pkg/net/http/httptest/example_test.go b/src/pkg/net/http/httptest/example_test.go new file mode 100644 index 000000000..239470d97 --- /dev/null +++ b/src/pkg/net/http/httptest/example_test.go @@ -0,0 +1,50 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package httptest_test + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "net/http/httptest" +) + +func ExampleRecorder() { + handler := func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "something failed", http.StatusInternalServerError) + } + + req, err := http.NewRequest("GET", "http://example.com/foo", nil) + if err != nil { + log.Fatal(err) + } + + w := httptest.NewRecorder() + handler(w, req) + + fmt.Printf("%d - %s", w.Code, w.Body.String()) + // Output: 500 - something failed +} + +func ExampleServer() { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, client") + })) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + log.Fatal(err) + } + greeting, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s", greeting) + // Output: Hello, client +} diff --git a/src/pkg/net/http/httptest/recorder.go b/src/pkg/net/http/httptest/recorder.go index 9aa0d510b..5451f5423 100644 --- a/src/pkg/net/http/httptest/recorder.go +++ b/src/pkg/net/http/httptest/recorder.go @@ -17,6 +17,8 @@ type ResponseRecorder struct { HeaderMap http.Header // the HTTP response headers Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to Flushed bool + + wroteHeader bool } // NewRecorder returns an initialized ResponseRecorder. @@ -24,6 +26,7 @@ func NewRecorder() *ResponseRecorder { return &ResponseRecorder{ HeaderMap: make(http.Header), Body: new(bytes.Buffer), + Code: 200, } } @@ -33,26 +36,37 @@ const DefaultRemoteAddr = "1.2.3.4" // Header returns the response headers. func (rw *ResponseRecorder) Header() http.Header { - return rw.HeaderMap + m := rw.HeaderMap + if m == nil { + m = make(http.Header) + rw.HeaderMap = m + } + return m } // Write always succeeds and writes to rw.Body, if not nil. func (rw *ResponseRecorder) Write(buf []byte) (int, error) { + if !rw.wroteHeader { + rw.WriteHeader(200) + } if rw.Body != nil { rw.Body.Write(buf) } - if rw.Code == 0 { - rw.Code = http.StatusOK - } return len(buf), nil } // WriteHeader sets rw.Code. func (rw *ResponseRecorder) WriteHeader(code int) { - rw.Code = code + if !rw.wroteHeader { + rw.Code = code + } + rw.wroteHeader = true } // Flush sets rw.Flushed to true. func (rw *ResponseRecorder) Flush() { + if !rw.wroteHeader { + rw.WriteHeader(200) + } rw.Flushed = true } diff --git a/src/pkg/net/http/httptest/recorder_test.go b/src/pkg/net/http/httptest/recorder_test.go new file mode 100644 index 000000000..2b563260c --- /dev/null +++ b/src/pkg/net/http/httptest/recorder_test.go @@ -0,0 +1,90 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package httptest + +import ( + "fmt" + "net/http" + "testing" +) + +func TestRecorder(t *testing.T) { + type checkFunc func(*ResponseRecorder) error + check := func(fns ...checkFunc) []checkFunc { return fns } + + hasStatus := func(wantCode int) checkFunc { + return func(rec *ResponseRecorder) error { + if rec.Code != wantCode { + return fmt.Errorf("Status = %d; want %d", rec.Code, wantCode) + } + return nil + } + } + hasContents := func(want string) checkFunc { + return func(rec *ResponseRecorder) error { + if rec.Body.String() != want { + return fmt.Errorf("wrote = %q; want %q", rec.Body.String(), want) + } + return nil + } + } + hasFlush := func(want bool) checkFunc { + return func(rec *ResponseRecorder) error { + if rec.Flushed != want { + return fmt.Errorf("Flushed = %v; want %v", rec.Flushed, want) + } + return nil + } + } + + tests := []struct { + name string + h func(w http.ResponseWriter, r *http.Request) + checks []checkFunc + }{ + { + "200 default", + func(w http.ResponseWriter, r *http.Request) {}, + check(hasStatus(200), hasContents("")), + }, + { + "first code only", + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(201) + w.WriteHeader(202) + w.Write([]byte("hi")) + }, + check(hasStatus(201), hasContents("hi")), + }, + { + "write sends 200", + func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("hi first")) + w.WriteHeader(201) + w.WriteHeader(202) + }, + check(hasStatus(200), hasContents("hi first"), hasFlush(false)), + }, + { + "flush", + func(w http.ResponseWriter, r *http.Request) { + w.(http.Flusher).Flush() // also sends a 200 + w.WriteHeader(201) + }, + check(hasStatus(200), hasFlush(true)), + }, + } + r, _ := http.NewRequest("GET", "http://foo.com/", nil) + for _, tt := range tests { + h := http.HandlerFunc(tt.h) + rec := NewRecorder() + h.ServeHTTP(rec, r) + for _, check := range tt.checks { + if err := check(rec); err != nil { + t.Errorf("%s: %v", tt.name, err) + } + } + } +} diff --git a/src/pkg/net/http/httptest/server.go b/src/pkg/net/http/httptest/server.go index 57cf0c941..7f265552f 100644 --- a/src/pkg/net/http/httptest/server.go +++ b/src/pkg/net/http/httptest/server.go @@ -21,7 +21,11 @@ import ( type Server struct { URL string // base URL of form http://ipaddr:port with no trailing slash Listener net.Listener - TLS *tls.Config // nil if not using using TLS + + // TLS is the optional TLS configuration, populated with a new config + // after TLS is started. If set on an unstarted server before StartTLS + // is called, existing fields are copied into the new config. + TLS *tls.Config // Config may be changed after calling NewUnstartedServer and // before Start or StartTLS. @@ -36,13 +40,16 @@ type Server struct { // accepted. type historyListener struct { net.Listener - history []net.Conn + sync.Mutex // protects history + history []net.Conn } func (hs *historyListener) Accept() (c net.Conn, err error) { c, err = hs.Listener.Accept() if err == nil { + hs.Lock() hs.history = append(hs.history, c) + hs.Unlock() } return } @@ -96,7 +103,7 @@ func (s *Server) Start() { if s.URL != "" { panic("Server already started") } - s.Listener = &historyListener{s.Listener, make([]net.Conn, 0)} + s.Listener = &historyListener{Listener: s.Listener} s.URL = "http://" + s.Listener.Addr().String() s.wrapHandler() go s.Config.Serve(s.Listener) @@ -116,13 +123,20 @@ func (s *Server) StartTLS() { panic(fmt.Sprintf("httptest: NewTLSServer: %v", err)) } - s.TLS = &tls.Config{ - NextProtos: []string{"http/1.1"}, - Certificates: []tls.Certificate{cert}, + existingConfig := s.TLS + s.TLS = new(tls.Config) + if existingConfig != nil { + *s.TLS = *existingConfig + } + if s.TLS.NextProtos == nil { + s.TLS.NextProtos = []string{"http/1.1"} + } + if len(s.TLS.Certificates) == 0 { + s.TLS.Certificates = []tls.Certificate{cert} } tlsListener := tls.NewListener(s.Listener, s.TLS) - s.Listener = &historyListener{tlsListener, make([]net.Conn, 0)} + s.Listener = &historyListener{Listener: tlsListener} s.URL = "https://" + s.Listener.Addr().String() s.wrapHandler() go s.Config.Serve(s.Listener) @@ -152,6 +166,10 @@ func NewTLSServer(handler http.Handler) *Server { func (s *Server) Close() { s.Listener.Close() s.wg.Wait() + s.CloseClientConnections() + if t, ok := http.DefaultTransport.(*http.Transport); ok { + t.CloseIdleConnections() + } } // CloseClientConnections closes any currently open HTTP connections @@ -161,9 +179,11 @@ func (s *Server) CloseClientConnections() { if !ok { return } + hl.Lock() for _, conn := range hl.history { conn.Close() } + hl.Unlock() } // waitGroupHandler wraps a handler, incrementing and decrementing a @@ -180,28 +200,29 @@ func (h *waitGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.h.ServeHTTP(w, r) } -// localhostCert is a PEM-encoded TLS cert with SAN DNS names +// localhostCert is a PEM-encoded TLS cert with SAN IPs // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end // of ASN.1 time). +// generated from src/pkg/crypto/tls: +// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h var localhostCert = []byte(`-----BEGIN CERTIFICATE----- -MIIBOTCB5qADAgECAgEAMAsGCSqGSIb3DQEBBTAAMB4XDTcwMDEwMTAwMDAwMFoX -DTQ5MTIzMTIzNTk1OVowADBaMAsGCSqGSIb3DQEBAQNLADBIAkEAsuA5mAFMj6Q7 -qoBzcvKzIq4kzuT5epSp2AkcQfyBHm7K13Ws7u+0b5Vb9gqTf5cAiIKcrtrXVqkL -8i1UQF6AzwIDAQABo08wTTAOBgNVHQ8BAf8EBAMCACQwDQYDVR0OBAYEBAECAwQw -DwYDVR0jBAgwBoAEAQIDBDAbBgNVHREEFDASggkxMjcuMC4wLjGCBVs6OjFdMAsG -CSqGSIb3DQEBBQNBAJH30zjLWRztrWpOCgJL8RQWLaKzhK79pVhAx6q/3NrF16C7 -+l1BRZstTwIGdoGId8BRpErK1TXkniFb95ZMynM= ------END CERTIFICATE----- -`) +MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD +bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj +bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa +IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA +AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud +EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA +AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk +Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA== +-----END CERTIFICATE-----`) // localhostKey is the private key for localhostCert. var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIBPQIBAAJBALLgOZgBTI+kO6qAc3LysyKuJM7k+XqUqdgJHEH8gR5uytd1rO7v -tG+VW/YKk3+XAIiCnK7a11apC/ItVEBegM8CAwEAAQJBAI5sxq7naeR9ahyqRkJi -SIv2iMxLuPEHaezf5CYOPWjSjBPyVhyRevkhtqEjF/WkgL7C2nWpYHsUcBDBQVF0 -3KECIQDtEGB2ulnkZAahl3WuJziXGLB+p8Wgx7wzSM6bHu1c6QIhAMEp++CaS+SJ -/TrU0zwY/fW4SvQeb49BPZUF3oqR8Xz3AiEA1rAJHBzBgdOQKdE3ksMUPcnvNJSN -poCcELmz2clVXtkCIQCLytuLV38XHToTipR4yMl6O+6arzAjZ56uq7m7ZRV0TwIh -AM65XAOw8Dsg9Kq78aYXiOEDc5DL0sbFUu/SlmRcCg93 ------END RSA PRIVATE KEY----- -`) +MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0 +0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV +NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d +AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW +MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD +EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA +1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE= +-----END RSA PRIVATE KEY-----`) diff --git a/src/pkg/net/http/httputil/chunked.go b/src/pkg/net/http/httputil/chunked.go index 29eaf3475..b66d40951 100644 --- a/src/pkg/net/http/httputil/chunked.go +++ b/src/pkg/net/http/httputil/chunked.go @@ -13,10 +13,9 @@ package httputil import ( "bufio" - "bytes" "errors" + "fmt" "io" - "strconv" ) const maxLineLength = 4096 // assumed <= bufio.defaultBufSize @@ -24,7 +23,7 @@ const maxLineLength = 4096 // assumed <= bufio.defaultBufSize var ErrLineTooLong = errors.New("header line too long") // NewChunkedReader returns a new chunkedReader that translates the data read from r -// out of HTTP "chunked" format before returning it. +// out of HTTP "chunked" format before returning it. // The chunkedReader returns io.EOF when the final 0-length chunk is read. // // NewChunkedReader is not needed by normal applications. The http package @@ -41,16 +40,17 @@ type chunkedReader struct { r *bufio.Reader n uint64 // unread bytes in chunk err error + buf [2]byte } func (cr *chunkedReader) beginChunk() { // chunk-size CRLF - var line string + var line []byte line, cr.err = readLine(cr.r) if cr.err != nil { return } - cr.n, cr.err = strconv.ParseUint(line, 16, 64) + cr.n, cr.err = parseHexUint(line) if cr.err != nil { return } @@ -76,9 +76,8 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) { cr.n -= uint64(n) if cr.n == 0 && cr.err == nil { // end of chunk (CRLF) - b := make([]byte, 2) - if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { - if b[0] != '\r' || b[1] != '\n' { + if _, cr.err = io.ReadFull(cr.r, cr.buf[:]); cr.err == nil { + if cr.buf[0] != '\r' || cr.buf[1] != '\n' { cr.err = errors.New("malformed chunked encoding") } } @@ -90,7 +89,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) { // Give up if the line exceeds maxLineLength. // The returned bytes are a pointer into storage in // the bufio, so they are only valid until the next bufio read. -func readLineBytes(b *bufio.Reader) (p []byte, err error) { +func readLine(b *bufio.Reader) (p []byte, err error) { if p, err = b.ReadSlice('\n'); err != nil { // We always know when EOF is coming. // If the caller asked for a line, there should be a line. @@ -104,20 +103,18 @@ func readLineBytes(b *bufio.Reader) (p []byte, err error) { if len(p) >= maxLineLength { return nil, ErrLineTooLong } - - // Chop off trailing white space. - p = bytes.TrimRight(p, " \r\t\n") - - return p, nil + return trimTrailingWhitespace(p), nil } -// readLineBytes, but convert the bytes into a string. -func readLine(b *bufio.Reader) (s string, err error) { - p, e := readLineBytes(b) - if e != nil { - return "", e +func trimTrailingWhitespace(b []byte) []byte { + for len(b) > 0 && isASCIISpace(b[len(b)-1]) { + b = b[:len(b)-1] } - return string(p), nil + return b +} + +func isASCIISpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' } // NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP @@ -149,9 +146,7 @@ func (cw *chunkedWriter) Write(data []byte) (n int, err error) { return 0, nil } - head := strconv.FormatInt(int64(len(data)), 16) + "\r\n" - - if _, err = io.WriteString(cw.Wire, head); err != nil { + if _, err = fmt.Fprintf(cw.Wire, "%x\r\n", len(data)); err != nil { return 0, err } if n, err = cw.Wire.Write(data); err != nil { @@ -170,3 +165,21 @@ func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } + +func parseHexUint(v []byte) (n uint64, err error) { + for _, b := range v { + n <<= 4 + switch { + case '0' <= b && b <= '9': + b = b - '0' + case 'a' <= b && b <= 'f': + b = b - 'a' + 10 + case 'A' <= b && b <= 'F': + b = b - 'A' + 10 + default: + return 0, errors.New("invalid byte in chunk length") + } + n |= uint64(b) + } + return +} diff --git a/src/pkg/net/http/httputil/chunked_test.go b/src/pkg/net/http/httputil/chunked_test.go index 155a32bdf..a06bffad5 100644 --- a/src/pkg/net/http/httputil/chunked_test.go +++ b/src/pkg/net/http/httputil/chunked_test.go @@ -11,7 +11,10 @@ package httputil import ( "bytes" + "fmt" + "io" "io/ioutil" + "runtime" "testing" ) @@ -39,3 +42,54 @@ func TestChunk(t *testing.T) { t.Errorf("chunk reader read %q; want %q", g, e) } } + +func TestChunkReaderAllocs(t *testing.T) { + // temporarily set GOMAXPROCS to 1 as we are testing memory allocations + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + var buf bytes.Buffer + w := NewChunkedWriter(&buf) + a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc") + w.Write(a) + w.Write(b) + w.Write(c) + w.Close() + + r := NewChunkedReader(&buf) + readBuf := make([]byte, len(a)+len(b)+len(c)+1) + + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + m0 := ms.Mallocs + + n, err := io.ReadFull(r, readBuf) + + runtime.ReadMemStats(&ms) + mallocs := ms.Mallocs - m0 + if mallocs > 1 { + t.Errorf("%d mallocs; want <= 1", mallocs) + } + + if n != len(readBuf)-1 { + t.Errorf("read %d bytes; want %d", n, len(readBuf)-1) + } + if err != io.ErrUnexpectedEOF { + t.Errorf("read error = %v; want ErrUnexpectedEOF", err) + } +} + +func TestParseHexUint(t *testing.T) { + for i := uint64(0); i <= 1234; i++ { + line := []byte(fmt.Sprintf("%x", i)) + got, err := parseHexUint(line) + if err != nil { + t.Fatalf("on %d: %v", i, err) + } + if got != i { + t.Errorf("for input %q = %d; want %d", line, got, i) + } + } + _, err := parseHexUint([]byte("bogus")) + if err == nil { + t.Error("expected error on bogus input") + } +} diff --git a/src/pkg/net/http/httputil/dump.go b/src/pkg/net/http/httputil/dump.go index 892ef4ede..0b0035661 100644 --- a/src/pkg/net/http/httputil/dump.go +++ b/src/pkg/net/http/httputil/dump.go @@ -75,7 +75,7 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) { // Use the actual Transport code to record what we would send // on the wire, but not using TCP. Use a Transport with a - // customer dialer that returns a fake net.Conn that waits + // custom dialer that returns a fake net.Conn that waits // for the full input (and recording it), and then responds // with a dummy response. var buf bytes.Buffer // records the output @@ -89,7 +89,7 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) { t := &http.Transport{ Dial: func(net, addr string) (net.Conn, error) { - return &dumpConn{io.MultiWriter(pw, &buf), dr}, nil + return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil }, } diff --git a/src/pkg/net/http/httputil/reverseproxy.go b/src/pkg/net/http/httputil/reverseproxy.go index 9c4bd6e09..134c45299 100644 --- a/src/pkg/net/http/httputil/reverseproxy.go +++ b/src/pkg/net/http/httputil/reverseproxy.go @@ -17,6 +17,10 @@ import ( "time" ) +// onExitFlushLoop is a callback set by tests to detect the state of the +// flushLoop() goroutine. +var onExitFlushLoop func() + // ReverseProxy is an HTTP Handler that takes an incoming request and // sends it to another server, proxying the response back to the // client. @@ -102,8 +106,14 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { outreq.Header.Del("Connection") } - if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { - outreq.Header.Set("X-Forwarded-For", clientIp) + if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { + // If we aren't the first proxy retain prior + // X-Forwarded-For information as a comma+space + // separated list and fold multiple headers into one. + if prior, ok := outreq.Header["X-Forwarded-For"]; ok { + clientIP = strings.Join(prior, ", ") + ", " + clientIP + } + outreq.Header.Set("X-Forwarded-For", clientIP) } res, err := transport.RoundTrip(outreq) @@ -112,20 +122,29 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusInternalServerError) return } + defer res.Body.Close() copyHeader(rw.Header(), res.Header) rw.WriteHeader(res.StatusCode) + p.copyResponse(rw, res.Body) +} - if res.Body != nil { - var dst io.Writer = rw - if p.FlushInterval != 0 { - if wf, ok := rw.(writeFlusher); ok { - dst = &maxLatencyWriter{dst: wf, latency: p.FlushInterval} +func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) { + if p.FlushInterval != 0 { + if wf, ok := dst.(writeFlusher); ok { + mlw := &maxLatencyWriter{ + dst: wf, + latency: p.FlushInterval, + done: make(chan bool), } + go mlw.flushLoop() + defer mlw.stop() + dst = mlw } - io.Copy(dst, res.Body) } + + io.Copy(dst, src) } type writeFlusher interface { @@ -137,22 +156,14 @@ type maxLatencyWriter struct { dst writeFlusher latency time.Duration - lk sync.Mutex // protects init of done, as well Write + Flush + lk sync.Mutex // protects Write + Flush done chan bool } -func (m *maxLatencyWriter) Write(p []byte) (n int, err error) { +func (m *maxLatencyWriter) Write(p []byte) (int, error) { m.lk.Lock() defer m.lk.Unlock() - if m.done == nil { - m.done = make(chan bool) - go m.flushLoop() - } - n, err = m.dst.Write(p) - if err != nil { - m.done <- true - } - return + return m.dst.Write(p) } func (m *maxLatencyWriter) flushLoop() { @@ -160,13 +171,18 @@ func (m *maxLatencyWriter) flushLoop() { defer t.Stop() for { select { + case <-m.done: + if onExitFlushLoop != nil { + onExitFlushLoop() + } + return case <-t.C: m.lk.Lock() m.dst.Flush() m.lk.Unlock() - case <-m.done: - return } } panic("unreached") } + +func (m *maxLatencyWriter) stop() { m.done <- true } diff --git a/src/pkg/net/http/httputil/reverseproxy_test.go b/src/pkg/net/http/httputil/reverseproxy_test.go index 28e9c90ad..863927162 100644 --- a/src/pkg/net/http/httputil/reverseproxy_test.go +++ b/src/pkg/net/http/httputil/reverseproxy_test.go @@ -11,7 +11,9 @@ import ( "net/http" "net/http/httptest" "net/url" + "strings" "testing" + "time" ) func TestReverseProxy(t *testing.T) { @@ -70,6 +72,47 @@ func TestReverseProxy(t *testing.T) { } } +func TestXForwardedFor(t *testing.T) { + const prevForwardedFor = "client ip" + const backendResponse = "I am the backend" + const backendStatus = 404 + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("X-Forwarded-For") == "" { + t.Errorf("didn't get X-Forwarded-For header") + } + if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) { + t.Errorf("X-Forwarded-For didn't contain prior data") + } + w.WriteHeader(backendStatus) + w.Write([]byte(backendResponse)) + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := NewSingleHostReverseProxy(backendURL) + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + + getReq, _ := http.NewRequest("GET", frontend.URL, nil) + getReq.Host = "some-name" + getReq.Header.Set("Connection", "close") + getReq.Header.Set("X-Forwarded-For", prevForwardedFor) + getReq.Close = true + res, err := http.DefaultClient.Do(getReq) + if err != nil { + t.Fatalf("Get: %v", err) + } + if g, e := res.StatusCode, backendStatus; g != e { + t.Errorf("got res.StatusCode %d; expected %d", g, e) + } + bodyBytes, _ := ioutil.ReadAll(res.Body) + if g, e := string(bodyBytes), backendResponse; g != e { + t.Errorf("got body %q; expected %q", g, e) + } +} + var proxyQueryTests = []struct { baseSuffix string // suffix to add to backend URL reqSuffix string // suffix to add to frontend's request URL @@ -107,3 +150,44 @@ func TestReverseProxyQuery(t *testing.T) { frontend.Close() } } + +func TestReverseProxyFlushInterval(t *testing.T) { + const expected = "hi" + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(expected)) + })) + defer backend.Close() + + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + + proxyHandler := NewSingleHostReverseProxy(backendURL) + proxyHandler.FlushInterval = time.Microsecond + + done := make(chan bool) + onExitFlushLoop = func() { done <- true } + defer func() { onExitFlushLoop = nil }() + + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + + req, _ := http.NewRequest("GET", frontend.URL, nil) + req.Close = true + res, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("Get: %v", err) + } + defer res.Body.Close() + if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected { + t.Errorf("got body %q; expected %q", bodyBytes, expected) + } + + select { + case <-done: + // OK + case <-time.After(5 * time.Second): + t.Error("maxLatencyWriter flushLoop() never exited") + } +} diff --git a/src/pkg/net/http/jar.go b/src/pkg/net/http/jar.go index 2c2caa251..5c3de0dad 100644 --- a/src/pkg/net/http/jar.go +++ b/src/pkg/net/http/jar.go @@ -8,23 +8,20 @@ import ( "net/url" ) -// A CookieJar manages storage and use of cookies in HTTP requests. +// A CookieJar manages storage and use of cookies in HTTP requests. // // Implementations of CookieJar must be safe for concurrent use by multiple // goroutines. +// +// The net/http/cookiejar package provides a CookieJar implementation. type CookieJar interface { - // SetCookies handles the receipt of the cookies in a reply for the - // given URL. It may or may not choose to save the cookies, depending - // on the jar's policy and implementation. + // SetCookies handles the receipt of the cookies in a reply for the + // given URL. It may or may not choose to save the cookies, depending + // on the jar's policy and implementation. SetCookies(u *url.URL, cookies []*Cookie) // Cookies returns the cookies to send in a request for the given URL. - // It is up to the implementation to honor the standard cookie use - // restrictions such as in RFC 6265. + // It is up to the implementation to honor the standard cookie use + // restrictions such as in RFC 6265. Cookies(u *url.URL) []*Cookie } - -type blackHoleJar struct{} - -func (blackHoleJar) SetCookies(u *url.URL, cookies []*Cookie) {} -func (blackHoleJar) Cookies(u *url.URL) []*Cookie { return nil } diff --git a/src/pkg/net/http/lex.go b/src/pkg/net/http/lex.go index ffb393ccf..cb33318f4 100644 --- a/src/pkg/net/http/lex.go +++ b/src/pkg/net/http/lex.go @@ -6,131 +6,91 @@ package http // This file deals with lexical matters of HTTP -func isSeparator(c byte) bool { - switch c { - case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t': - return true - } - return false +var isTokenTable = [127]bool{ + '!': true, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '*': true, + '+': true, + '-': true, + '.': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'W': true, + 'V': true, + 'X': true, + 'Y': true, + 'Z': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '|': true, + '~': true, } -func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 } - -func isChar(c byte) bool { return 0 <= c && c <= 127 } - -func isAnyText(c byte) bool { return !isCtl(c) } - -func isQdText(c byte) bool { return isAnyText(c) && c != '"' } - -func isToken(c byte) bool { return isChar(c) && !isCtl(c) && !isSeparator(c) } - -// Valid escaped sequences are not specified in RFC 2616, so for now, we assume -// that they coincide with the common sense ones used by GO. Malformed -// characters should probably not be treated as errors by a robust (forgiving) -// parser, so we replace them with the '?' character. -func httpUnquotePair(b byte) byte { - // skip the first byte, which should always be '\' - switch b { - case 'a': - return '\a' - case 'b': - return '\b' - case 'f': - return '\f' - case 'n': - return '\n' - case 'r': - return '\r' - case 't': - return '\t' - case 'v': - return '\v' - case '\\': - return '\\' - case '\'': - return '\'' - case '"': - return '"' - } - return '?' -} - -// raw must begin with a valid quoted string. Only the first quoted string is -// parsed and is unquoted in result. eaten is the number of bytes parsed, or -1 -// upon failure. -func httpUnquote(raw []byte) (eaten int, result string) { - buf := make([]byte, len(raw)) - if raw[0] != '"' { - return -1, "" - } - eaten = 1 - j := 0 // # of bytes written in buf - for i := 1; i < len(raw); i++ { - switch b := raw[i]; b { - case '"': - eaten++ - buf = buf[0:j] - return i + 1, string(buf) - case '\\': - if len(raw) < i+2 { - return -1, "" - } - buf[j] = httpUnquotePair(raw[i+1]) - eaten += 2 - j++ - i++ - default: - if isQdText(b) { - buf[j] = b - } else { - buf[j] = '?' - } - eaten++ - j++ - } - } - return -1, "" +func isToken(r rune) bool { + i := int(r) + return i < len(isTokenTable) && isTokenTable[i] } -// This is a best effort parse, so errors are not returned, instead not all of -// the input string might be parsed. result is always non-nil. -func httpSplitFieldValue(fv string) (eaten int, result []string) { - result = make([]string, 0, len(fv)) - raw := []byte(fv) - i := 0 - chunk := "" - for i < len(raw) { - b := raw[i] - switch { - case b == '"': - eaten, unq := httpUnquote(raw[i:len(raw)]) - if eaten < 0 { - return i, result - } else { - i += eaten - chunk += unq - } - case isSeparator(b): - if chunk != "" { - result = result[0 : len(result)+1] - result[len(result)-1] = chunk - chunk = "" - } - i++ - case isToken(b): - chunk += string(b) - i++ - case b == '\n' || b == '\r': - i++ - default: - chunk += "?" - i++ - } - } - if chunk != "" { - result = result[0 : len(result)+1] - result[len(result)-1] = chunk - chunk = "" - } - return i, result +func isNotToken(r rune) bool { + return !isToken(r) } diff --git a/src/pkg/net/http/lex_test.go b/src/pkg/net/http/lex_test.go index 5386f7534..6d9d294f7 100644 --- a/src/pkg/net/http/lex_test.go +++ b/src/pkg/net/http/lex_test.go @@ -8,63 +8,24 @@ import ( "testing" ) -type lexTest struct { - Raw string - Parsed int // # of parsed characters - Result []string -} +func isChar(c rune) bool { return c <= 127 } -var lexTests = []lexTest{ - { - Raw: `"abc"def,:ghi`, - Parsed: 13, - Result: []string{"abcdef", "ghi"}, - }, - // My understanding of the RFC is that escape sequences outside of - // quotes are not interpreted? - { - Raw: `"\t"\t"\t"`, - Parsed: 10, - Result: []string{"\t", "t\t"}, - }, - { - Raw: `"\yab"\r\n`, - Parsed: 10, - Result: []string{"?ab", "r", "n"}, - }, - { - Raw: "ab\f", - Parsed: 3, - Result: []string{"ab?"}, - }, - { - Raw: "\"ab \" c,de f, gh, ij\n\t\r", - Parsed: 23, - Result: []string{"ab ", "c", "de", "f", "gh", "ij"}, - }, -} +func isCtl(c rune) bool { return c <= 31 || c == 127 } -func min(x, y int) int { - if x <= y { - return x +func isSeparator(c rune) bool { + switch c { + case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t': + return true } - return y + return false } -func TestSplitFieldValue(t *testing.T) { - for k, l := range lexTests { - parsed, result := httpSplitFieldValue(l.Raw) - if parsed != l.Parsed { - t.Errorf("#%d: Parsed %d, expected %d", k, parsed, l.Parsed) - } - if len(result) != len(l.Result) { - t.Errorf("#%d: Result len %d, expected %d", k, len(result), len(l.Result)) - } - for i := 0; i < min(len(result), len(l.Result)); i++ { - if result[i] != l.Result[i] { - t.Errorf("#%d: %d-th entry mismatch. Have {%s}, expect {%s}", - k, i, result[i], l.Result[i]) - } +func TestIsToken(t *testing.T) { + for i := 0; i <= 130; i++ { + r := rune(i) + expected := isChar(r) && !isCtl(r) && !isSeparator(r) + if isToken(r) != expected { + t.Errorf("isToken(0x%x) = %v", r, !expected) } } } diff --git a/src/pkg/net/http/npn_test.go b/src/pkg/net/http/npn_test.go new file mode 100644 index 000000000..98b8930d0 --- /dev/null +++ b/src/pkg/net/http/npn_test.go @@ -0,0 +1,118 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http_test + +import ( + "bufio" + "crypto/tls" + "fmt" + "io" + "io/ioutil" + . "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func TestNextProtoUpgrade(t *testing.T) { + ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) { + fmt.Fprintf(w, "path=%s,proto=", r.URL.Path) + if r.TLS != nil { + w.Write([]byte(r.TLS.NegotiatedProtocol)) + } + if r.RemoteAddr == "" { + t.Error("request with no RemoteAddr") + } + if r.Body == nil { + t.Errorf("request with nil Body") + } + })) + ts.TLS = &tls.Config{ + NextProtos: []string{"unhandled-proto", "tls-0.9"}, + } + ts.Config.TLSNextProto = map[string]func(*Server, *tls.Conn, Handler){ + "tls-0.9": handleTLSProtocol09, + } + ts.StartTLS() + defer ts.Close() + + tr := newTLSTransport(t, ts) + defer tr.CloseIdleConnections() + c := &Client{Transport: tr} + + // Normal request, without NPN. + { + res, err := c.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if want := "path=/,proto="; string(body) != want { + t.Errorf("plain request = %q; want %q", body, want) + } + } + + // Request to an advertised but unhandled NPN protocol. + // Server will hang up. + { + tr.CloseIdleConnections() + tr.TLSClientConfig.NextProtos = []string{"unhandled-proto"} + _, err := c.Get(ts.URL) + if err == nil { + t.Errorf("expected error on unhandled-proto request") + } + } + + // Request using the "tls-0.9" protocol, which we register here. + // It is HTTP/0.9 over TLS. + { + tlsConfig := newTLSTransport(t, ts).TLSClientConfig + tlsConfig.NextProtos = []string{"tls-0.9"} + conn, err := tls.Dial("tcp", ts.Listener.Addr().String(), tlsConfig) + if err != nil { + t.Fatal(err) + } + conn.Write([]byte("GET /foo\n")) + body, err := ioutil.ReadAll(conn) + if err != nil { + t.Fatal(err) + } + if want := "path=/foo,proto=tls-0.9"; string(body) != want { + t.Errorf("plain request = %q; want %q", body, want) + } + } +} + +// handleTLSProtocol09 implements the HTTP/0.9 protocol over TLS, for the +// TestNextProtoUpgrade test. +func handleTLSProtocol09(srv *Server, conn *tls.Conn, h Handler) { + br := bufio.NewReader(conn) + line, err := br.ReadString('\n') + if err != nil { + return + } + line = strings.TrimSpace(line) + path := strings.TrimPrefix(line, "GET ") + if path == line { + return + } + req, _ := NewRequest("GET", path, nil) + req.Proto = "HTTP/0.9" + req.ProtoMajor = 0 + req.ProtoMinor = 9 + rw := &http09Writer{conn, make(Header)} + h.ServeHTTP(rw, req) +} + +type http09Writer struct { + io.Writer + h Header +} + +func (w http09Writer) Header() Header { return w.h } +func (w http09Writer) WriteHeader(int) {} // no headers diff --git a/src/pkg/net/http/pprof/pprof.go b/src/pkg/net/http/pprof/pprof.go index 06fcde144..0c7548e3e 100644 --- a/src/pkg/net/http/pprof/pprof.go +++ b/src/pkg/net/http/pprof/pprof.go @@ -14,6 +14,14 @@ // To use pprof, link this package into your program: // import _ "net/http/pprof" // +// If your application is not already running an http server, you +// need to start one. Add "net/http" and "log" to your imports and +// the following code to your main function: +// +// go func() { +// log.Println(http.ListenAndServe("localhost:6060", nil)) +// }() +// // Then use the pprof tool to look at the heap profile: // // go tool pprof http://localhost:6060/debug/pprof/heap @@ -22,9 +30,12 @@ // // go tool pprof http://localhost:6060/debug/pprof/profile // -// Or to view all available profiles: +// Or to look at the goroutine blocking profile: +// +// go tool pprof http://localhost:6060/debug/pprof/block // -// go tool pprof http://localhost:6060/debug/pprof/ +// To view all available profiles, open http://localhost:6060/debug/pprof/ +// in your browser. // // For a study of the facility in action, visit // @@ -161,7 +172,7 @@ func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // listing the available profiles. func Index(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/debug/pprof/") { - name := r.URL.Path[len("/debug/pprof/"):] + name := strings.TrimPrefix(r.URL.Path, "/debug/pprof/") if name != "" { handler(name).ServeHTTP(w, r) return diff --git a/src/pkg/net/http/proxy_test.go b/src/pkg/net/http/proxy_test.go index 5ecffafac..449ccaeea 100644 --- a/src/pkg/net/http/proxy_test.go +++ b/src/pkg/net/http/proxy_test.go @@ -25,13 +25,13 @@ var UseProxyTests = []struct { {"[::2]", true}, // not a loopback address {"barbaz.net", false}, // match as .barbaz.net - {"foobar.com", false}, // have a port but match + {"foobar.com", false}, // have a port but match {"foofoobar.com", true}, // not match as a part of foobar.com {"baz.com", true}, // not match as a part of barbaz.com {"localhost.net", true}, // not match as suffix of address {"local.localhost", true}, // not match as prefix as address {"barbarbaz.net", true}, // not match because NO_PROXY have a '.' - {"www.foobar.com", true}, // not match because NO_PROXY is not .foobar.com + {"www.foobar.com", false}, // match because NO_PROXY includes "foobar.com" } func TestUseProxy(t *testing.T) { diff --git a/src/pkg/net/http/range_test.go b/src/pkg/net/http/range_test.go index 5274a81fa..ef911af7b 100644 --- a/src/pkg/net/http/range_test.go +++ b/src/pkg/net/http/range_test.go @@ -14,15 +14,34 @@ var ParseRangeTests = []struct { r []httpRange }{ {"", 0, nil}, + {"", 1000, nil}, {"foo", 0, nil}, {"bytes=", 0, nil}, + {"bytes=7", 10, nil}, + {"bytes= 7 ", 10, nil}, + {"bytes=1-", 0, nil}, {"bytes=5-4", 10, nil}, {"bytes=0-2,5-4", 10, nil}, + {"bytes=2-5,4-3", 10, nil}, + {"bytes=--5,4--3", 10, nil}, + {"bytes=A-", 10, nil}, + {"bytes=A- ", 10, nil}, + {"bytes=A-Z", 10, nil}, + {"bytes= -Z", 10, nil}, + {"bytes=5-Z", 10, nil}, + {"bytes=Ran-dom, garbage", 10, nil}, + {"bytes=0x01-0x02", 10, nil}, + {"bytes= ", 10, nil}, + {"bytes= , , , ", 10, nil}, + {"bytes=0-9", 10, []httpRange{{0, 10}}}, {"bytes=0-", 10, []httpRange{{0, 10}}}, {"bytes=5-", 10, []httpRange{{5, 5}}}, {"bytes=0-20", 10, []httpRange{{0, 10}}}, {"bytes=15-,0-5", 10, nil}, + {"bytes=1-2,5-", 10, []httpRange{{1, 2}, {5, 5}}}, + {"bytes=-2 , 7-", 11, []httpRange{{9, 2}, {7, 4}}}, + {"bytes=0-0 ,2-2, 7-", 11, []httpRange{{0, 1}, {2, 1}, {7, 4}}}, {"bytes=-5", 10, []httpRange{{5, 5}}}, {"bytes=-15", 10, []httpRange{{0, 10}}}, {"bytes=0-499", 10000, []httpRange{{0, 500}}}, @@ -32,6 +51,9 @@ var ParseRangeTests = []struct { {"bytes=0-0,-1", 10000, []httpRange{{0, 1}, {9999, 1}}}, {"bytes=500-600,601-999", 10000, []httpRange{{500, 101}, {601, 399}}}, {"bytes=500-700,601-999", 10000, []httpRange{{500, 201}, {601, 399}}}, + + // Match Apache laxity: + {"bytes= 1 -2 , 4- 5, 7 - 8 , ,,", 11, []httpRange{{1, 2}, {4, 2}, {7, 2}}}, } func TestParseRange(t *testing.T) { diff --git a/src/pkg/net/http/readrequest_test.go b/src/pkg/net/http/readrequest_test.go index 2e03c658a..ffdd6a892 100644 --- a/src/pkg/net/http/readrequest_test.go +++ b/src/pkg/net/http/readrequest_test.go @@ -247,6 +247,54 @@ var reqTests = []reqTest{ noTrailer, noError, }, + + // SSDP Notify request. golang.org/issue/3692 + { + "NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n", + &Request{ + Method: "NOTIFY", + URL: &url.URL{ + Path: "*", + }, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: Header{ + "Server": []string{"foo"}, + }, + Close: false, + ContentLength: 0, + RequestURI: "*", + }, + + noBody, + noTrailer, + noError, + }, + + // OPTIONS request. Similar to golang.org/issue/3692 + { + "OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n", + &Request{ + Method: "OPTIONS", + URL: &url.URL{ + Path: "*", + }, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: Header{ + "Server": []string{"foo"}, + }, + Close: false, + ContentLength: 0, + RequestURI: "*", + }, + + noBody, + noTrailer, + noError, + }, } func TestReadRequest(t *testing.T) { diff --git a/src/pkg/net/http/request.go b/src/pkg/net/http/request.go index f5bc6eb91..217f35b48 100644 --- a/src/pkg/net/http/request.go +++ b/src/pkg/net/http/request.go @@ -19,6 +19,7 @@ import ( "mime/multipart" "net/textproto" "net/url" + "strconv" "strings" ) @@ -70,7 +71,13 @@ var reqWriteExcludeHeader = map[string]bool{ // or to be sent by a client. type Request struct { Method string // GET, POST, PUT, etc. - URL *url.URL + + // URL is created from the URI supplied on the Request-Line + // as stored in RequestURI. + // + // For most requests, fields other than Path and RawQuery + // will be empty. (See RFC 2616, Section 5.1.2) + URL *url.URL // The protocol version for incoming requests. // Outgoing requests always use HTTP/1.1. @@ -123,6 +130,7 @@ type Request struct { // The host on which the URL is sought. // Per RFC 2616, this is either the value of the Host: header // or the host name given in the URL itself. + // It may be of the form "host:port". Host string // Form contains the parsed form data, including both the URL @@ -131,6 +139,12 @@ type Request struct { // The HTTP client ignores Form and uses Body instead. Form url.Values + // PostForm contains the parsed form data from POST or PUT + // body parameters. + // This field is only available after ParseForm is called. + // The HTTP client ignores PostForm and uses Body instead. + PostForm url.Values + // MultipartForm is the parsed multipart form, including file uploads. // This field is only available after ParseMultipartForm is called. // The HTTP client ignores MultipartForm and uses Body instead. @@ -317,11 +331,20 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err } // TODO(bradfitz): escape at least newlines in ruri? - bw := bufio.NewWriter(w) - fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri) + // Wrap the writer in a bufio Writer if it's not already buffered. + // Don't always call NewWriter, as that forces a bytes.Buffer + // and other small bufio Writers to have a minimum 4k buffer + // size. + var bw *bufio.Writer + if _, ok := w.(io.ByteWriter); !ok { + bw = bufio.NewWriter(w) + w = bw + } + + fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri) // Header lines - fmt.Fprintf(bw, "Host: %s\r\n", host) + fmt.Fprintf(w, "Host: %s\r\n", host) // Use the defaultUserAgent unless the Header contains one, which // may be blank to not send the header. @@ -332,7 +355,7 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err } } if userAgent != "" { - fmt.Fprintf(bw, "User-Agent: %s\r\n", userAgent) + fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent) } // Process Body,ContentLength,Close,Trailer @@ -340,65 +363,61 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err if err != nil { return err } - err = tw.WriteHeader(bw) + err = tw.WriteHeader(w) if err != nil { return err } // TODO: split long values? (If so, should share code with Conn.Write) - err = req.Header.WriteSubset(bw, reqWriteExcludeHeader) + err = req.Header.WriteSubset(w, reqWriteExcludeHeader) if err != nil { return err } if extraHeaders != nil { - err = extraHeaders.Write(bw) + err = extraHeaders.Write(w) if err != nil { return err } } - io.WriteString(bw, "\r\n") + io.WriteString(w, "\r\n") // Write body and trailer - err = tw.WriteBody(bw) + err = tw.WriteBody(w) if err != nil { return err } - return bw.Flush() -} - -// Convert decimal at s[i:len(s)] to integer, -// returning value, string position where the digits stopped, -// and whether there was a valid number (digits, not too big). -func atoi(s string, i int) (n, i1 int, ok bool) { - const Big = 1000000 - if i >= len(s) || s[i] < '0' || s[i] > '9' { - return 0, 0, false - } - n = 0 - for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { - n = n*10 + int(s[i]-'0') - if n > Big { - return 0, 0, false - } + if bw != nil { + return bw.Flush() } - return n, i, true + return nil } // ParseHTTPVersion parses a HTTP version string. // "HTTP/1.0" returns (1, 0, true). func ParseHTTPVersion(vers string) (major, minor int, ok bool) { - if len(vers) < 5 || vers[0:5] != "HTTP/" { + const Big = 1000000 // arbitrary upper bound + switch vers { + case "HTTP/1.1": + return 1, 1, true + case "HTTP/1.0": + return 1, 0, true + } + if !strings.HasPrefix(vers, "HTTP/") { return 0, 0, false } - major, i, ok := atoi(vers, 5) - if !ok || i >= len(vers) || vers[i] != '.' { + dot := strings.Index(vers, ".") + if dot < 0 { return 0, 0, false } - minor, i, ok = atoi(vers, i+1) - if !ok || i != len(vers) { + major, err := strconv.Atoi(vers[5:dot]) + if err != nil || major < 0 || major > Big { + return 0, 0, false + } + minor, err = strconv.Atoi(vers[dot+1:]) + if err != nil || minor < 0 || minor > Big { return 0, 0, false } return major, minor, true @@ -426,10 +445,12 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) { } if body != nil { switch v := body.(type) { - case *strings.Reader: - req.ContentLength = int64(v.Len()) case *bytes.Buffer: req.ContentLength = int64(v.Len()) + case *bytes.Reader: + req.ContentLength = int64(v.Len()) + case *strings.Reader: + req.ContentLength = int64(v.Len()) } } @@ -513,9 +534,9 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) { // the same. In the second case, any Host line is ignored. req.Host = req.URL.Host if req.Host == "" { - req.Host = req.Header.Get("Host") + req.Host = req.Header.get("Host") } - req.Header.Del("Host") + delete(req.Header, "Host") fixPragmaCacheControl(req.Header) @@ -594,66 +615,97 @@ func (l *maxBytesReader) Close() error { return l.r.Close() } -// ParseForm parses the raw query from the URL. +func copyValues(dst, src url.Values) { + for k, vs := range src { + for _, value := range vs { + dst.Add(k, value) + } + } +} + +func parsePostForm(r *Request) (vs url.Values, err error) { + if r.Body == nil { + err = errors.New("missing form body") + return + } + ct := r.Header.Get("Content-Type") + ct, _, err = mime.ParseMediaType(ct) + switch { + case ct == "application/x-www-form-urlencoded": + var reader io.Reader = r.Body + maxFormSize := int64(1<<63 - 1) + if _, ok := r.Body.(*maxBytesReader); !ok { + maxFormSize = int64(10 << 20) // 10 MB is a lot of text. + reader = io.LimitReader(r.Body, maxFormSize+1) + } + b, e := ioutil.ReadAll(reader) + if e != nil { + if err == nil { + err = e + } + break + } + if int64(len(b)) > maxFormSize { + err = errors.New("http: POST too large") + return + } + vs, e = url.ParseQuery(string(b)) + if err == nil { + err = e + } + case ct == "multipart/form-data": + // handled by ParseMultipartForm (which is calling us, or should be) + // TODO(bradfitz): there are too many possible + // orders to call too many functions here. + // Clean this up and write more tests. + // request_test.go contains the start of this, + // in TestRequestMultipartCallOrder. + } + return +} + +// ParseForm parses the raw query from the URL and updates r.Form. +// +// For POST or PUT requests, it also parses the request body as a form and +// put the results into both r.PostForm and r.Form. +// POST and PUT body parameters take precedence over URL query string values +// in r.Form. // -// For POST or PUT requests, it also parses the request body as a form. // If the request Body's size has not already been limited by MaxBytesReader, // the size is capped at 10MB. // // ParseMultipartForm calls ParseForm automatically. // It is idempotent. -func (r *Request) ParseForm() (err error) { - if r.Form != nil { - return - } - if r.URL != nil { - r.Form, err = url.ParseQuery(r.URL.RawQuery) +func (r *Request) ParseForm() error { + var err error + if r.PostForm == nil { + if r.Method == "POST" || r.Method == "PUT" { + r.PostForm, err = parsePostForm(r) + } + if r.PostForm == nil { + r.PostForm = make(url.Values) + } } - if r.Method == "POST" || r.Method == "PUT" { - if r.Body == nil { - return errors.New("missing form body") + if r.Form == nil { + if len(r.PostForm) > 0 { + r.Form = make(url.Values) + copyValues(r.Form, r.PostForm) } - ct := r.Header.Get("Content-Type") - ct, _, err = mime.ParseMediaType(ct) - switch { - case ct == "application/x-www-form-urlencoded": - var reader io.Reader = r.Body - maxFormSize := int64(1<<63 - 1) - if _, ok := r.Body.(*maxBytesReader); !ok { - maxFormSize = int64(10 << 20) // 10 MB is a lot of text. - reader = io.LimitReader(r.Body, maxFormSize+1) - } - b, e := ioutil.ReadAll(reader) - if e != nil { - if err == nil { - err = e - } - break - } - if int64(len(b)) > maxFormSize { - return errors.New("http: POST too large") - } - var newValues url.Values - newValues, e = url.ParseQuery(string(b)) + var newValues url.Values + if r.URL != nil { + var e error + newValues, e = url.ParseQuery(r.URL.RawQuery) if err == nil { err = e } - if r.Form == nil { - r.Form = make(url.Values) - } - // Copy values into r.Form. TODO: make this smoother. - for k, vs := range newValues { - for _, value := range vs { - r.Form.Add(k, value) - } - } - case ct == "multipart/form-data": - // handled by ParseMultipartForm (which is calling us, or should be) - // TODO(bradfitz): there are too many possible - // orders to call too many functions here. - // Clean this up and write more tests. - // request_test.go contains the start of this, - // in TestRequestMultipartCallOrder. + } + if newValues == nil { + newValues = make(url.Values) + } + if r.Form == nil { + r.Form = newValues + } else { + copyValues(r.Form, newValues) } } return err @@ -699,7 +751,9 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error { } // FormValue returns the first value for the named component of the query. +// POST and PUT body parameters take precedence over URL query string values. // FormValue calls ParseMultipartForm and ParseForm if necessary. +// To access multiple values of the same key use ParseForm. func (r *Request) FormValue(key string) string { if r.Form == nil { r.ParseMultipartForm(defaultMaxMemory) @@ -710,6 +764,19 @@ func (r *Request) FormValue(key string) string { return "" } +// PostFormValue returns the first value for the named component of the POST +// or PUT request body. URL query parameters are ignored. +// PostFormValue calls ParseMultipartForm and ParseForm if necessary. +func (r *Request) PostFormValue(key string) string { + if r.PostForm == nil { + r.ParseMultipartForm(defaultMaxMemory) + } + if vs := r.PostForm[key]; len(vs) > 0 { + return vs[0] + } + return "" +} + // FormFile returns the first file for the provided form key. // FormFile calls ParseMultipartForm and ParseForm if necessary. func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) { @@ -732,12 +799,16 @@ func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, e } func (r *Request) expectsContinue() bool { - return strings.ToLower(r.Header.Get("Expect")) == "100-continue" + return hasToken(r.Header.get("Expect"), "100-continue") } func (r *Request) wantsHttp10KeepAlive() bool { if r.ProtoMajor != 1 || r.ProtoMinor != 0 { return false } - return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "keep-alive") + return hasToken(r.Header.get("Connection"), "keep-alive") +} + +func (r *Request) wantsClose() bool { + return hasToken(r.Header.get("Connection"), "close") } diff --git a/src/pkg/net/http/request_test.go b/src/pkg/net/http/request_test.go index 6e00b9bfd..00ad791de 100644 --- a/src/pkg/net/http/request_test.go +++ b/src/pkg/net/http/request_test.go @@ -30,8 +30,8 @@ func TestQuery(t *testing.T) { } func TestPostQuery(t *testing.T) { - req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x", - strings.NewReader("z=post&both=y")) + req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not", + strings.NewReader("z=post&both=y&prio=2&empty=")) req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") if q := req.FormValue("q"); q != "foo" { @@ -40,8 +40,23 @@ func TestPostQuery(t *testing.T) { if z := req.FormValue("z"); z != "post" { t.Errorf(`req.FormValue("z") = %q, want "post"`, z) } - if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"x", "y"}) { - t.Errorf(`req.FormValue("both") = %q, want ["x", "y"]`, both) + if bq, found := req.PostForm["q"]; found { + t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq) + } + if bz := req.PostFormValue("z"); bz != "post" { + t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz) + } + if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) { + t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs) + } + if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) { + t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both) + } + if prio := req.FormValue("prio"); prio != "2" { + t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio) + } + if empty := req.FormValue("empty"); empty != "" { + t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty) } } @@ -76,6 +91,23 @@ func TestParseFormUnknownContentType(t *testing.T) { } } +func TestParseFormInitializeOnError(t *testing.T) { + nilBody, _ := NewRequest("POST", "http://www.google.com/search?q=foo", nil) + tests := []*Request{ + nilBody, + {Method: "GET", URL: nil}, + } + for i, req := range tests { + err := req.ParseForm() + if req.Form == nil { + t.Errorf("%d. Form not initialized, error %v", i, err) + } + if req.PostForm == nil { + t.Errorf("%d. PostForm not initialized, error %v", i, err) + } + } +} + func TestMultipartReader(t *testing.T) { req := &Request{ Method: "POST", @@ -129,7 +161,7 @@ func TestSetBasicAuth(t *testing.T) { } func TestMultipartRequest(t *testing.T) { - // Test that we can read the values and files of a + // Test that we can read the values and files of a // multipart request with FormValue and FormFile, // and that ParseMultipartForm can be called multiple times. req := newTestMultipartRequest(t) @@ -196,6 +228,75 @@ func TestReadRequestErrors(t *testing.T) { } } +func TestNewRequestHost(t *testing.T) { + req, err := NewRequest("GET", "http://localhost:1234/", nil) + if err != nil { + t.Fatal(err) + } + if req.Host != "localhost:1234" { + t.Errorf("Host = %q; want localhost:1234", req.Host) + } +} + +func TestNewRequestContentLength(t *testing.T) { + readByte := func(r io.Reader) io.Reader { + var b [1]byte + r.Read(b[:]) + return r + } + tests := []struct { + r io.Reader + want int64 + }{ + {bytes.NewReader([]byte("123")), 3}, + {bytes.NewBuffer([]byte("1234")), 4}, + {strings.NewReader("12345"), 5}, + // Not detected: + {struct{ io.Reader }{strings.NewReader("xyz")}, 0}, + {io.NewSectionReader(strings.NewReader("x"), 0, 6), 0}, + {readByte(io.NewSectionReader(strings.NewReader("xy"), 0, 6)), 0}, + } + for _, tt := range tests { + req, err := NewRequest("POST", "http://localhost/", tt.r) + if err != nil { + t.Fatal(err) + } + if req.ContentLength != tt.want { + t.Errorf("ContentLength(%T) = %d; want %d", tt.r, req.ContentLength, tt.want) + } + } +} + +type logWrites struct { + t *testing.T + dst *[]string +} + +func (l logWrites) WriteByte(c byte) error { + l.t.Fatalf("unexpected WriteByte call") + return nil +} + +func (l logWrites) Write(p []byte) (n int, err error) { + *l.dst = append(*l.dst, string(p)) + return len(p), nil +} + +func TestRequestWriteBufferedWriter(t *testing.T) { + got := []string{} + req, _ := NewRequest("GET", "http://foo.com/", nil) + req.Write(logWrites{t, &got}) + want := []string{ + "GET / HTTP/1.1\r\n", + "Host: foo.com\r\n", + "User-Agent: Go http package\r\n", + "\r\n", + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Writes = %q\n Want = %q", got, want) + } +} + func testMissingFile(t *testing.T, req *Request) { f, fh, err := req.FormFile("missing") if f != nil { @@ -300,3 +401,81 @@ Content-Disposition: form-data; name="textb" ` + textbValue + ` --MyBoundary-- ` + +func benchmarkReadRequest(b *testing.B, request string) { + request = request + "\n" // final \n + request = strings.Replace(request, "\n", "\r\n", -1) // expand \n to \r\n + b.SetBytes(int64(len(request))) + r := bufio.NewReader(&infiniteReader{buf: []byte(request)}) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := ReadRequest(r) + if err != nil { + b.Fatalf("failed to read request: %v", err) + } + } +} + +// infiniteReader satisfies Read requests as if the contents of buf +// loop indefinitely. +type infiniteReader struct { + buf []byte + offset int +} + +func (r *infiniteReader) Read(b []byte) (int, error) { + n := copy(b, r.buf[r.offset:]) + r.offset = (r.offset + n) % len(r.buf) + return n, nil +} + +func BenchmarkReadRequestChrome(b *testing.B) { + // https://github.com/felixge/node-http-perf/blob/master/fixtures/get.http + benchmarkReadRequest(b, `GET / HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17 +Accept-Encoding: gzip,deflate,sdch +Accept-Language: en-US,en;q=0.8 +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 +Cookie: __utma=1.1978842379.1323102373.1323102373.1323102373.1; EPi:NumberOfVisits=1,2012-02-28T13:42:18; CrmSession=5b707226b9563e1bc69084d07a107c98; plushContainerWidth=100%25; plushNoTopMenu=0; hudson_auto_refresh=false +`) +} + +func BenchmarkReadRequestCurl(b *testing.B) { + // curl http://localhost:8080/ + benchmarkReadRequest(b, `GET / HTTP/1.1 +User-Agent: curl/7.27.0 +Host: localhost:8080 +Accept: */* +`) +} + +func BenchmarkReadRequestApachebench(b *testing.B) { + // ab -n 1 -c 1 http://localhost:8080/ + benchmarkReadRequest(b, `GET / HTTP/1.0 +Host: localhost:8080 +User-Agent: ApacheBench/2.3 +Accept: */* +`) +} + +func BenchmarkReadRequestSiege(b *testing.B) { + // siege -r 1 -c 1 http://localhost:8080/ + benchmarkReadRequest(b, `GET / HTTP/1.1 +Host: localhost:8080 +Accept: */* +Accept-Encoding: gzip +User-Agent: JoeDog/1.00 [en] (X11; I; Siege 2.70) +Connection: keep-alive +`) +} + +func BenchmarkReadRequestWrk(b *testing.B) { + // wrk -t 1 -r 1 -c 1 http://localhost:8080/ + benchmarkReadRequest(b, `GET / HTTP/1.1 +Host: localhost:8080 +`) +} diff --git a/src/pkg/net/http/requestwrite_test.go b/src/pkg/net/http/requestwrite_test.go index fc3186f0c..bc637f18b 100644 --- a/src/pkg/net/http/requestwrite_test.go +++ b/src/pkg/net/http/requestwrite_test.go @@ -328,6 +328,69 @@ var reqWriteTests = []reqWriteTest{ "User-Agent: Go http package\r\n" + "X-Foo: X-Bar\r\n\r\n", }, + + // If no Request.Host and no Request.URL.Host, we send + // an empty Host header, and don't use + // Request.Header["Host"]. This is just testing that + // we don't change Go 1.0 behavior. + { + Req: Request{ + Method: "GET", + Host: "", + URL: &url.URL{ + Scheme: "http", + Host: "", + Path: "/search", + }, + ProtoMajor: 1, + ProtoMinor: 1, + Header: Header{ + "Host": []string{"bad.example.com"}, + }, + }, + + WantWrite: "GET /search HTTP/1.1\r\n" + + "Host: \r\n" + + "User-Agent: Go http package\r\n\r\n", + }, + + // Opaque test #1 from golang.org/issue/4860 + { + Req: Request{ + Method: "GET", + URL: &url.URL{ + Scheme: "http", + Host: "www.google.com", + Opaque: "/%2F/%2F/", + }, + ProtoMajor: 1, + ProtoMinor: 1, + Header: Header{}, + }, + + WantWrite: "GET /%2F/%2F/ HTTP/1.1\r\n" + + "Host: www.google.com\r\n" + + "User-Agent: Go http package\r\n\r\n", + }, + + // Opaque test #2 from golang.org/issue/4860 + { + Req: Request{ + Method: "GET", + URL: &url.URL{ + Scheme: "http", + Host: "x.google.com", + Opaque: "//y.google.com/%2F/%2F/", + }, + ProtoMajor: 1, + ProtoMinor: 1, + Header: Header{}, + }, + + WantWrite: "GET http://y.google.com/%2F/%2F/ HTTP/1.1\r\n" + + "Host: x.google.com\r\n" + + "User-Agent: Go http package\r\n\r\n", + }, } func TestRequestWrite(t *testing.T) { diff --git a/src/pkg/net/http/response.go b/src/pkg/net/http/response.go index 945ecd8a4..391ebbf6d 100644 --- a/src/pkg/net/http/response.go +++ b/src/pkg/net/http/response.go @@ -49,7 +49,7 @@ type Response struct { Body io.ReadCloser // ContentLength records the length of the associated content. The - // value -1 indicates that the length is unknown. Unless RequestMethod + // value -1 indicates that the length is unknown. Unless Request.Method // is "HEAD", values >= 0 indicate that the given number of bytes may // be read from Body. ContentLength int64 @@ -107,7 +107,6 @@ func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err error) { resp = new(Response) resp.Request = req - resp.Request.Method = strings.ToUpper(resp.Request.Method) // Parse the first line of the response. line, err := tp.ReadLine() @@ -179,7 +178,7 @@ func (r *Response) ProtoAtLeast(major, minor int) bool { // StatusCode // ProtoMajor // ProtoMinor -// RequestMethod +// Request.Method // TransferEncoding // Trailer // Body @@ -188,11 +187,6 @@ func (r *Response) ProtoAtLeast(major, minor int) bool { // func (r *Response) Write(w io.Writer) error { - // RequestMethod should be upper-case - if r.Request != nil { - r.Request.Method = strings.ToUpper(r.Request.Method) - } - // Status line text := r.Status if text == "" { @@ -204,9 +198,7 @@ func (r *Response) Write(w io.Writer) error { } protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor) statusCode := strconv.Itoa(r.StatusCode) + " " - if strings.HasPrefix(text, statusCode) { - text = text[len(statusCode):] - } + text = strings.TrimPrefix(text, statusCode) io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n") // Process Body,ContentLength,Close,Trailer diff --git a/src/pkg/net/http/response_test.go b/src/pkg/net/http/response_test.go index 6eed4887d..2f5f77369 100644 --- a/src/pkg/net/http/response_test.go +++ b/src/pkg/net/http/response_test.go @@ -124,7 +124,7 @@ var respTests = []respTest{ // Chunked response without Content-Length. { - "HTTP/1.0 200 OK\r\n" + + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "0a\r\n" + @@ -137,12 +137,12 @@ var respTests = []respTest{ Response{ Status: "200 OK", StatusCode: 200, - Proto: "HTTP/1.0", + Proto: "HTTP/1.1", ProtoMajor: 1, - ProtoMinor: 0, + ProtoMinor: 1, Request: dummyReq("GET"), Header: Header{}, - Close: true, + Close: false, ContentLength: -1, TransferEncoding: []string{"chunked"}, }, @@ -152,24 +152,24 @@ var respTests = []respTest{ // Chunked response with Content-Length. { - "HTTP/1.0 200 OK\r\n" + + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Length: 10\r\n" + "\r\n" + "0a\r\n" + - "Body here\n" + + "Body here\n\r\n" + "0\r\n" + "\r\n", Response{ Status: "200 OK", StatusCode: 200, - Proto: "HTTP/1.0", + Proto: "HTTP/1.1", ProtoMajor: 1, - ProtoMinor: 0, + ProtoMinor: 1, Request: dummyReq("GET"), Header: Header{}, - Close: true, + Close: false, ContentLength: -1, // TODO(rsc): Fix? TransferEncoding: []string{"chunked"}, }, @@ -177,23 +177,88 @@ var respTests = []respTest{ "Body here\n", }, - // Chunked response in response to a HEAD request (the "chunked" should - // be ignored, as HEAD responses never have bodies) + // Chunked response in response to a HEAD request { - "HTTP/1.0 200 OK\r\n" + + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n", Response{ - Status: "200 OK", - StatusCode: 200, - Proto: "HTTP/1.0", - ProtoMajor: 1, - ProtoMinor: 0, - Request: dummyReq("HEAD"), - Header: Header{}, - Close: true, - ContentLength: 0, + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Request: dummyReq("HEAD"), + Header: Header{}, + TransferEncoding: []string{"chunked"}, + Close: false, + ContentLength: -1, + }, + + "", + }, + + // Content-Length in response to a HEAD request + { + "HTTP/1.0 200 OK\r\n" + + "Content-Length: 256\r\n" + + "\r\n", + + Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Request: dummyReq("HEAD"), + Header: Header{"Content-Length": {"256"}}, + TransferEncoding: nil, + Close: true, + ContentLength: 256, + }, + + "", + }, + + // Content-Length in response to a HEAD request with HTTP/1.1 + { + "HTTP/1.1 200 OK\r\n" + + "Content-Length: 256\r\n" + + "\r\n", + + Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Request: dummyReq("HEAD"), + Header: Header{"Content-Length": {"256"}}, + TransferEncoding: nil, + Close: false, + ContentLength: 256, + }, + + "", + }, + + // No Content-Length or Chunked in response to a HEAD request + { + "HTTP/1.0 200 OK\r\n" + + "\r\n", + + Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Request: dummyReq("HEAD"), + Header: Header{}, + TransferEncoding: nil, + Close: true, + ContentLength: -1, }, "", @@ -259,16 +324,37 @@ var respTests = []respTest{ "", }, + + // golang.org/issue/4767: don't special-case multipart/byteranges responses + { + `HTTP/1.1 206 Partial Content +Connection: close +Content-Type: multipart/byteranges; boundary=18a75608c8f47cef + +some body`, + Response{ + Status: "206 Partial Content", + StatusCode: 206, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Request: dummyReq("GET"), + Header: Header{ + "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"}, + }, + Close: true, + ContentLength: -1, + }, + + "some body", + }, } func TestReadResponse(t *testing.T) { - for i := range respTests { - tt := &respTests[i] - var braw bytes.Buffer - braw.WriteString(tt.Raw) - resp, err := ReadResponse(bufio.NewReader(&braw), tt.Resp.Request) + for i, tt := range respTests { + resp, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.Raw)), tt.Resp.Request) if err != nil { - t.Errorf("#%d: %s", i, err) + t.Errorf("#%d: %v", i, err) continue } rbody := resp.Body @@ -276,7 +362,11 @@ func TestReadResponse(t *testing.T) { diff(t, fmt.Sprintf("#%d Response", i), resp, &tt.Resp) var bout bytes.Buffer if rbody != nil { - io.Copy(&bout, rbody) + _, err = io.Copy(&bout, rbody) + if err != nil { + t.Errorf("#%d: %v", i, err) + continue + } rbody.Close() } body := bout.String() @@ -286,6 +376,22 @@ func TestReadResponse(t *testing.T) { } } +func TestWriteResponse(t *testing.T) { + for i, tt := range respTests { + resp, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.Raw)), tt.Resp.Request) + if err != nil { + t.Errorf("#%d: %v", i, err) + continue + } + bout := bytes.NewBuffer(nil) + err = resp.Write(bout) + if err != nil { + t.Errorf("#%d: %v", i, err) + continue + } + } +} + var readResponseCloseInMiddleTests = []struct { chunked, compressed bool }{ diff --git a/src/pkg/net/http/responsewrite_test.go b/src/pkg/net/http/responsewrite_test.go index f8e63acf4..5c10e2161 100644 --- a/src/pkg/net/http/responsewrite_test.go +++ b/src/pkg/net/http/responsewrite_test.go @@ -15,83 +15,83 @@ type respWriteTest struct { Raw string } -var respWriteTests = []respWriteTest{ - // HTTP/1.0, identity coding; no trailer - { - Response{ - StatusCode: 503, - ProtoMajor: 1, - ProtoMinor: 0, - Request: dummyReq("GET"), - Header: Header{}, - Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")), - ContentLength: 6, - }, +func TestResponseWrite(t *testing.T) { + respWriteTests := []respWriteTest{ + // HTTP/1.0, identity coding; no trailer + { + Response{ + StatusCode: 503, + ProtoMajor: 1, + ProtoMinor: 0, + Request: dummyReq("GET"), + Header: Header{}, + Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")), + ContentLength: 6, + }, - "HTTP/1.0 503 Service Unavailable\r\n" + - "Content-Length: 6\r\n\r\n" + - "abcdef", - }, - // Unchunked response without Content-Length. - { - Response{ - StatusCode: 200, - ProtoMajor: 1, - ProtoMinor: 0, - Request: dummyReq("GET"), - Header: Header{}, - Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")), - ContentLength: -1, + "HTTP/1.0 503 Service Unavailable\r\n" + + "Content-Length: 6\r\n\r\n" + + "abcdef", }, - "HTTP/1.0 200 OK\r\n" + - "\r\n" + - "abcdef", - }, - // HTTP/1.1, chunked coding; empty trailer; close - { - Response{ - StatusCode: 200, - ProtoMajor: 1, - ProtoMinor: 1, - Request: dummyReq("GET"), - Header: Header{}, - Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")), - ContentLength: 6, - TransferEncoding: []string{"chunked"}, - Close: true, + // Unchunked response without Content-Length. + { + Response{ + StatusCode: 200, + ProtoMajor: 1, + ProtoMinor: 0, + Request: dummyReq("GET"), + Header: Header{}, + Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")), + ContentLength: -1, + }, + "HTTP/1.0 200 OK\r\n" + + "\r\n" + + "abcdef", }, + // HTTP/1.1, chunked coding; empty trailer; close + { + Response{ + StatusCode: 200, + ProtoMajor: 1, + ProtoMinor: 1, + Request: dummyReq("GET"), + Header: Header{}, + Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")), + ContentLength: 6, + TransferEncoding: []string{"chunked"}, + Close: true, + }, - "HTTP/1.1 200 OK\r\n" + - "Connection: close\r\n" + - "Transfer-Encoding: chunked\r\n\r\n" + - "6\r\nabcdef\r\n0\r\n\r\n", - }, + "HTTP/1.1 200 OK\r\n" + + "Connection: close\r\n" + + "Transfer-Encoding: chunked\r\n\r\n" + + "6\r\nabcdef\r\n0\r\n\r\n", + }, - // Header value with a newline character (Issue 914). - // Also tests removal of leading and trailing whitespace. - { - Response{ - StatusCode: 204, - ProtoMajor: 1, - ProtoMinor: 1, - Request: dummyReq("GET"), - Header: Header{ - "Foo": []string{" Bar\nBaz "}, + // Header value with a newline character (Issue 914). + // Also tests removal of leading and trailing whitespace. + { + Response{ + StatusCode: 204, + ProtoMajor: 1, + ProtoMinor: 1, + Request: dummyReq("GET"), + Header: Header{ + "Foo": []string{" Bar\nBaz "}, + }, + Body: nil, + ContentLength: 0, + TransferEncoding: []string{"chunked"}, + Close: true, }, - Body: nil, - ContentLength: 0, - TransferEncoding: []string{"chunked"}, - Close: true, - }, - "HTTP/1.1 204 No Content\r\n" + - "Connection: close\r\n" + - "Foo: Bar Baz\r\n" + - "\r\n", - }, -} + "HTTP/1.1 204 No Content\r\n" + + "Connection: close\r\n" + + "Foo: Bar Baz\r\n" + + "\r\n", + }, + } -func TestResponseWrite(t *testing.T) { for i := range respWriteTests { tt := &respWriteTests[i] var braw bytes.Buffer diff --git a/src/pkg/net/http/serve_test.go b/src/pkg/net/http/serve_test.go index b6a6b4c77..3300fef59 100644 --- a/src/pkg/net/http/serve_test.go +++ b/src/pkg/net/http/serve_test.go @@ -20,8 +20,13 @@ import ( "net/http/httputil" "net/url" "os" + "os/exec" "reflect" + "runtime" + "strconv" "strings" + "sync" + "sync/atomic" "syscall" "testing" "time" @@ -62,6 +67,7 @@ func (a dummyAddr) String() string { type testConn struct { readBuf bytes.Buffer writeBuf bytes.Buffer + closec chan bool // if non-nil, send value to it on close } func (c *testConn) Read(b []byte) (int, error) { @@ -73,6 +79,10 @@ func (c *testConn) Write(b []byte) (int, error) { } func (c *testConn) Close() error { + select { + case c.closec <- true: + default: + } return nil } @@ -168,13 +178,18 @@ var vtests = []struct { {"http://someHost.com/someDir/apage", "someHost.com/someDir"}, {"http://otherHost.com/someDir/apage", "someDir"}, {"http://otherHost.com/aDir/apage", "Default"}, + // redirections for trees + {"http://localhost/someDir", "/someDir/"}, + {"http://someHost.com/someDir", "/someDir/"}, } func TestHostHandlers(t *testing.T) { + defer checkLeakedTransports(t) + mux := NewServeMux() for _, h := range handlers { - Handle(h.pattern, stringHandler(h.msg)) + mux.Handle(h.pattern, stringHandler(h.msg)) } - ts := httptest.NewServer(nil) + ts := httptest.NewServer(mux) defer ts.Close() conn, err := net.Dial("tcp", ts.Listener.Addr().String()) @@ -199,9 +214,19 @@ func TestHostHandlers(t *testing.T) { t.Errorf("reading response: %v", err) continue } - s := r.Header.Get("Result") - if s != vt.expected { - t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected) + switch r.StatusCode { + case StatusOK: + s := r.Header.Get("Result") + if s != vt.expected { + t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected) + } + case StatusMovedPermanently: + s := r.Header.Get("Location") + if s != vt.expected { + t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected) + } + default: + t.Errorf("Get(%q) unhandled status code %d", vt.url, r.StatusCode) } } } @@ -232,28 +257,22 @@ func TestMuxRedirectLeadingSlashes(t *testing.T) { } func TestServerTimeouts(t *testing.T) { - // TODO(bradfitz): convert this to use httptest.Server - l, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("listen error: %v", err) - } - addr, _ := l.Addr().(*net.TCPAddr) - + defer checkLeakedTransports(t) reqNum := 0 - handler := HandlerFunc(func(res ResponseWriter, req *Request) { + ts := httptest.NewUnstartedServer(HandlerFunc(func(res ResponseWriter, req *Request) { reqNum++ fmt.Fprintf(res, "req=%d", reqNum) - }) - - server := &Server{Handler: handler, ReadTimeout: 250 * time.Millisecond, WriteTimeout: 250 * time.Millisecond} - go server.Serve(l) - - url := fmt.Sprintf("http://%s/", addr) + })) + ts.Config.ReadTimeout = 250 * time.Millisecond + ts.Config.WriteTimeout = 250 * time.Millisecond + ts.Start() + defer ts.Close() // Hit the HTTP server successfully. tr := &Transport{DisableKeepAlives: true} // they interfere with this test + defer tr.CloseIdleConnections() c := &Client{Transport: tr} - r, err := c.Get(url) + r, err := c.Get(ts.URL) if err != nil { t.Fatalf("http Get #1: %v", err) } @@ -266,13 +285,13 @@ func TestServerTimeouts(t *testing.T) { // Slow client that should timeout. t1 := time.Now() - conn, err := net.Dial("tcp", addr.String()) + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) if err != nil { t.Fatalf("Dial: %v", err) } buf := make([]byte, 1) n, err := conn.Read(buf) - latency := time.Now().Sub(t1) + latency := time.Since(t1) if n != 0 || err != io.EOF { t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, io.EOF) } @@ -283,7 +302,7 @@ func TestServerTimeouts(t *testing.T) { // Hit the HTTP server successfully again, verifying that the // previous slow connection didn't run our handler. (that we // get "req=2", not "req=3") - r, err = Get(url) + r, err = Get(ts.URL) if err != nil { t.Fatalf("http Get #2: %v", err) } @@ -293,11 +312,87 @@ func TestServerTimeouts(t *testing.T) { t.Errorf("Get #2 got %q, want %q", string(got), expected) } - l.Close() + if !testing.Short() { + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer conn.Close() + go io.Copy(ioutil.Discard, conn) + for i := 0; i < 5; i++ { + _, err := conn.Write([]byte("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")) + if err != nil { + t.Fatalf("on write %d: %v", i, err) + } + time.Sleep(ts.Config.ReadTimeout / 2) + } + } +} + +// golang.org/issue/4741 -- setting only a write timeout that triggers +// shouldn't cause a handler to block forever on reads (next HTTP +// request) that will never happen. +func TestOnlyWriteTimeout(t *testing.T) { + defer checkLeakedTransports(t) + var conn net.Conn + var afterTimeoutErrc = make(chan error, 1) + ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, req *Request) { + buf := make([]byte, 512<<10) + _, err := w.Write(buf) + if err != nil { + t.Errorf("handler Write error: %v", err) + return + } + conn.SetWriteDeadline(time.Now().Add(-30 * time.Second)) + _, err = w.Write(buf) + afterTimeoutErrc <- err + })) + ts.Listener = trackLastConnListener{ts.Listener, &conn} + ts.Start() + defer ts.Close() + + tr := &Transport{DisableKeepAlives: false} + defer tr.CloseIdleConnections() + c := &Client{Transport: tr} + + errc := make(chan error) + go func() { + res, err := c.Get(ts.URL) + if err != nil { + errc <- err + return + } + _, err = io.Copy(ioutil.Discard, res.Body) + errc <- err + }() + select { + case err := <-errc: + if err == nil { + t.Errorf("expected an error from Get request") + } + case <-time.After(5 * time.Second): + t.Fatal("timeout waiting for Get error") + } + if err := <-afterTimeoutErrc; err == nil { + t.Error("expected write error after timeout") + } +} + +// trackLastConnListener tracks the last net.Conn that was accepted. +type trackLastConnListener struct { + net.Listener + last *net.Conn // destination } -// TestIdentityResponse verifies that a handler can unset +func (l trackLastConnListener) Accept() (c net.Conn, err error) { + c, err = l.Listener.Accept() + *l.last = c + return +} + +// TestIdentityResponse verifies that a handler can unset func TestIdentityResponse(t *testing.T) { + defer checkLeakedTransports(t) handler := HandlerFunc(func(rw ResponseWriter, req *Request) { rw.Header().Set("Content-Length", "3") rw.Header().Set("Transfer-Encoding", req.FormValue("te")) @@ -343,10 +438,12 @@ func TestIdentityResponse(t *testing.T) { // Verify that ErrContentLength is returned url := ts.URL + "/?overwrite=1" - _, err := Get(url) + res, err := Get(url) if err != nil { t.Fatalf("error with Get of %s: %v", url, err) } + res.Body.Close() + // Verify that the connection is closed when the declared Content-Length // is larger than what the handler wrote. conn, err := net.Dial("tcp", ts.Listener.Addr().String()) @@ -370,7 +467,8 @@ func TestIdentityResponse(t *testing.T) { }) } -func testTcpConnectionCloses(t *testing.T, req string, h Handler) { +func testTCPConnectionCloses(t *testing.T, req string, h Handler) { + defer checkLeakedTransports(t) s := httptest.NewServer(h) defer s.Close() @@ -386,17 +484,18 @@ func testTcpConnectionCloses(t *testing.T, req string, h Handler) { } r := bufio.NewReader(conn) - _, err = ReadResponse(r, &Request{Method: "GET"}) + res, err := ReadResponse(r, &Request{Method: "GET"}) if err != nil { t.Fatal("ReadResponse error:", err) } - success := make(chan bool) + didReadAll := make(chan bool, 1) go func() { select { case <-time.After(5 * time.Second): - t.Fatal("body not closed after 5s") - case <-success: + t.Error("body not closed after 5s") + return + case <-didReadAll: } }() @@ -404,32 +503,43 @@ func testTcpConnectionCloses(t *testing.T, req string, h Handler) { if err != nil { t.Fatal("read error:", err) } + didReadAll <- true - success <- true + if !res.Close { + t.Errorf("Response.Close = false; want true") + } } // TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive. func TestServeHTTP10Close(t *testing.T) { - testTcpConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + testTCPConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "testdata/file") })) } +// TestClientCanClose verifies that clients can also force a connection to close. +func TestClientCanClose(t *testing.T) { + testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nConnection: close\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + // Nothing. + })) +} + // TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close, // even for HTTP/1.1 requests. func TestHandlersCanSetConnectionClose11(t *testing.T) { - testTcpConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + testTCPConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Connection", "close") })) } func TestHandlersCanSetConnectionClose10(t *testing.T) { - testTcpConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + testTCPConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Connection", "close") })) } func TestSetsRemoteAddr(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { fmt.Fprintf(w, "%s", r.RemoteAddr) })) @@ -450,11 +560,13 @@ func TestSetsRemoteAddr(t *testing.T) { } func TestChunkedResponseHeaders(t *testing.T) { + defer checkLeakedTransports(t) log.SetOutput(ioutil.Discard) // is noisy otherwise defer log.SetOutput(os.Stderr) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Content-Length", "intentional gibberish") // we check that this is deleted + w.(Flusher).Flush() fmt.Fprintf(w, "I am a chunked response.") })) defer ts.Close() @@ -463,6 +575,7 @@ func TestChunkedResponseHeaders(t *testing.T) { if err != nil { t.Fatalf("Get error: %v", err) } + defer res.Body.Close() if g, e := res.ContentLength, int64(-1); g != e { t.Errorf("expected ContentLength of %d; got %d", e, g) } @@ -478,6 +591,7 @@ func TestChunkedResponseHeaders(t *testing.T) { // chunking in their response headers and aren't allowed to produce // output. func Test304Responses(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.WriteHeader(StatusNotModified) _, err := w.Write([]byte("illegal body")) @@ -507,6 +621,7 @@ func Test304Responses(t *testing.T) { // allowed to produce output, and don't set a Content-Type since // the real type of the body data cannot be inferred. func TestHeadResponses(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { _, err := w.Write([]byte("Ignored body")) if err != ErrBodyNotAllowed { @@ -541,6 +656,7 @@ func TestHeadResponses(t *testing.T) { } func TestTLSHandshakeTimeout(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {})) ts.Config.ReadTimeout = 250 * time.Millisecond ts.StartTLS() @@ -560,6 +676,7 @@ func TestTLSHandshakeTimeout(t *testing.T) { } func TestTLSServer(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.TLS != nil { w.Header().Set("X-TLS-Set", "true") @@ -642,6 +759,7 @@ var serverExpectTests = []serverExpectTest{ // Tests that the server responds to the "Expect" request header // correctly. func TestServerExpect(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { // Note using r.FormValue("readbody") because for POST // requests that would read from r.Body, which we only @@ -661,30 +779,51 @@ func TestServerExpect(t *testing.T) { t.Fatalf("Dial: %v", err) } defer conn.Close() - sendf := func(format string, args ...interface{}) { - _, err := fmt.Fprintf(conn, format, args...) - if err != nil { - t.Fatalf("On test %#v, error writing %q: %v", test, format, err) - } - } + + // Only send the body immediately if we're acting like an HTTP client + // that doesn't send 100-continue expectations. + writeBody := test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue" + go func() { - sendf("POST /?readbody=%v HTTP/1.1\r\n"+ + _, err := fmt.Fprintf(conn, "POST /?readbody=%v HTTP/1.1\r\n"+ "Connection: close\r\n"+ "Content-Length: %d\r\n"+ "Expect: %s\r\nHost: foo\r\n\r\n", test.readBody, test.contentLength, test.expectation) - if test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue" { + if err != nil { + t.Errorf("On test %#v, error writing request headers: %v", test, err) + return + } + if writeBody { body := strings.Repeat("A", test.contentLength) - sendf(body) + _, err = fmt.Fprint(conn, body) + if err != nil { + if !test.readBody { + // Server likely already hung up on us. + // See larger comment below. + t.Logf("On test %#v, acceptable error writing request body: %v", test, err) + return + } + t.Errorf("On test %#v, error writing request body: %v", test, err) + } } }() bufr := bufio.NewReader(conn) line, err := bufr.ReadString('\n') if err != nil { - t.Fatalf("ReadString: %v", err) + if writeBody && !test.readBody { + // This is an acceptable failure due to a possible TCP race: + // We were still writing data and the server hung up on us. A TCP + // implementation may send a RST if our request body data was known + // to be lost, which may trigger our reads to fail. + // See RFC 1122 page 88. + t.Logf("On test %#v, acceptable error from ReadString: %v", test, err) + return + } + t.Fatalf("On test %#v, ReadString: %v", test, err) } if !strings.Contains(line, test.expectedResponse) { - t.Errorf("for test %#v got first line=%q", test, line) + t.Errorf("On test %#v, got first line = %q; want %q", test, line, test.expectedResponse) } } @@ -714,6 +853,7 @@ func TestServerUnreadRequestBodyLittle(t *testing.T) { t.Errorf("on request, read buffer length is %d; expected about 100 KB", conn.readBuf.Len()) } rw.WriteHeader(200) + rw.(Flusher).Flush() if g, e := conn.readBuf.Len(), 0; g != e { t.Errorf("after WriteHeader, read buffer length is %d; want %d", g, e) } @@ -736,27 +876,28 @@ func TestServerUnreadRequestBodyLarge(t *testing.T) { "Content-Length: %d\r\n"+ "\r\n", len(body)))) conn.readBuf.Write([]byte(body)) - - done := make(chan bool) + conn.closec = make(chan bool, 1) ls := &oneConnListener{conn} go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) { - defer close(done) if conn.readBuf.Len() < len(body)/2 { t.Errorf("on request, read buffer length is %d; expected about 1MB", conn.readBuf.Len()) } rw.WriteHeader(200) + rw.(Flusher).Flush() if conn.readBuf.Len() < len(body)/2 { t.Errorf("post-WriteHeader, read buffer length is %d; expected about 1MB", conn.readBuf.Len()) } - if c := rw.Header().Get("Connection"); c != "close" { - t.Errorf(`Connection header = %q; want "close"`, c) - } })) - <-done + <-conn.closec + + if res := conn.writeBuf.String(); !strings.Contains(res, "Connection: close") { + t.Errorf("Expected a Connection: close header; got response: %s", res) + } } func TestTimeoutHandler(t *testing.T) { + defer checkLeakedTransports(t) sendHi := make(chan bool, 1) writeErrors := make(chan error, 1) sayHi := HandlerFunc(func(w ResponseWriter, r *Request) { @@ -831,6 +972,7 @@ func TestRedirectMunging(t *testing.T) { // the previous request's body, which is not optimal for zero-lengthed bodies, // as the client would then see http.ErrBodyReadAfterClose and not 0, io.EOF. func TestZeroLengthPostAndResponse(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { all, err := ioutil.ReadAll(r.Body) if err != nil { @@ -868,15 +1010,20 @@ func TestZeroLengthPostAndResponse(t *testing.T) { } } +func TestHandlerPanicNil(t *testing.T) { + testHandlerPanic(t, false, nil) +} + func TestHandlerPanic(t *testing.T) { - testHandlerPanic(t, false) + testHandlerPanic(t, false, "intentional death for testing") } func TestHandlerPanicWithHijack(t *testing.T) { - testHandlerPanic(t, true) + testHandlerPanic(t, true, "intentional death for testing") } -func testHandlerPanic(t *testing.T, withHijack bool) { +func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) { + defer checkLeakedTransports(t) // Unlike the other tests that set the log output to ioutil.Discard // to quiet the output, this test uses a pipe. The pipe serves three // purposes: @@ -896,6 +1043,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) { pr, pw := io.Pipe() log.SetOutput(pw) defer log.SetOutput(os.Stderr) + defer pw.Close() ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if withHijack { @@ -905,7 +1053,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) { } defer rwc.Close() } - panic("intentional death for testing") + panic(panicValue) })) defer ts.Close() @@ -917,8 +1065,8 @@ func testHandlerPanic(t *testing.T, withHijack bool) { buf := make([]byte, 4<<10) _, err := pr.Read(buf) pr.Close() - if err != nil { - t.Fatal(err) + if err != nil && err != io.EOF { + t.Error(err) } done <- true }() @@ -928,6 +1076,10 @@ func testHandlerPanic(t *testing.T, withHijack bool) { t.Logf("expected an error") } + if panicValue == nil { + return + } + select { case <-done: return @@ -937,6 +1089,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) { } func TestNoDate(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header()["Date"] = nil })) @@ -952,6 +1105,7 @@ func TestNoDate(t *testing.T) { } func TestStripPrefix(t *testing.T) { + defer checkLeakedTransports(t) h := HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("X-Path", r.URL.Path) }) @@ -965,6 +1119,7 @@ func TestStripPrefix(t *testing.T) { if g, e := res.Header.Get("X-Path"), "/bar"; g != e { t.Errorf("test 1: got %s, want %s", g, e) } + res.Body.Close() res, err = Get(ts.URL + "/bar") if err != nil { @@ -973,9 +1128,11 @@ func TestStripPrefix(t *testing.T) { if g, e := res.StatusCode, 404; g != e { t.Errorf("test 2: got status %v, want %v", g, e) } + res.Body.Close() } func TestRequestLimit(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { t.Fatalf("didn't expect to get request in Handler") })) @@ -992,6 +1149,7 @@ func TestRequestLimit(t *testing.T) { // we do support it (at least currently), so we expect a response below. t.Fatalf("Do: %v", err) } + defer res.Body.Close() if res.StatusCode != 413 { t.Fatalf("expected 413 response status; got: %d %s", res.StatusCode, res.Status) } @@ -1013,11 +1171,12 @@ type countReader struct { func (cr countReader) Read(p []byte) (n int, err error) { n, err = cr.r.Read(p) - *cr.n += int64(n) + atomic.AddInt64(cr.n, int64(n)) return } func TestRequestBodyLimit(t *testing.T) { + defer checkLeakedTransports(t) const limit = 1 << 20 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { r.Body = MaxBytesReader(w, r.Body, limit) @@ -1031,8 +1190,8 @@ func TestRequestBodyLimit(t *testing.T) { })) defer ts.Close() - nWritten := int64(0) - req, _ := NewRequest("POST", ts.URL, io.LimitReader(countReader{neverEnding('a'), &nWritten}, limit*200)) + nWritten := new(int64) + req, _ := NewRequest("POST", ts.URL, io.LimitReader(countReader{neverEnding('a'), nWritten}, limit*200)) // Send the POST, but don't care it succeeds or not. The // remote side is going to reply and then close the TCP @@ -1045,7 +1204,7 @@ func TestRequestBodyLimit(t *testing.T) { // the remote side hung up on us before we wrote too much. _, _ = DefaultClient.Do(req) - if nWritten > limit*100 { + if atomic.LoadInt64(nWritten) > limit*100 { t.Errorf("handler restricted the request body to %d bytes, but client managed to write %d", limit, nWritten) } @@ -1054,6 +1213,7 @@ func TestRequestBodyLimit(t *testing.T) { // TestClientWriteShutdown tests that if the client shuts down the write // side of their TCP connection, the server doesn't send a 400 Bad Request. func TestClientWriteShutdown(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {})) defer ts.Close() conn, err := net.Dial("tcp", ts.Listener.Addr().String()) @@ -1086,28 +1246,207 @@ func TestClientWriteShutdown(t *testing.T) { // Tests that chunked server responses that write 1 byte at a time are // buffered before chunk headers are added, not after chunk headers. func TestServerBufferedChunking(t *testing.T) { - if true { - t.Logf("Skipping known broken test; see Issue 2357") - return - } conn := new(testConn) conn.readBuf.Write([]byte("GET / HTTP/1.1\r\n\r\n")) - done := make(chan bool) + conn.closec = make(chan bool, 1) ls := &oneConnListener{conn} go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) { - defer close(done) - rw.Header().Set("Content-Type", "text/plain") // prevent sniffing, which buffers + rw.(Flusher).Flush() // force the Header to be sent, in chunking mode, not counting the length rw.Write([]byte{'x'}) rw.Write([]byte{'y'}) rw.Write([]byte{'z'}) })) - <-done + <-conn.closec if !bytes.HasSuffix(conn.writeBuf.Bytes(), []byte("\r\n\r\n3\r\nxyz\r\n0\r\n\r\n")) { t.Errorf("response didn't end with a single 3 byte 'xyz' chunk; got:\n%q", conn.writeBuf.Bytes()) } } +// Tests that the server flushes its response headers out when it's +// ignoring the response body and waits a bit before forcefully +// closing the TCP connection, causing the client to get a RST. +// See http://golang.org/issue/3595 +func TestServerGracefulClose(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + Error(w, "bye", StatusUnauthorized) + })) + defer ts.Close() + + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + const bodySize = 5 << 20 + req := []byte(fmt.Sprintf("POST / HTTP/1.1\r\nHost: foo.com\r\nContent-Length: %d\r\n\r\n", bodySize)) + for i := 0; i < bodySize; i++ { + req = append(req, 'x') + } + writeErr := make(chan error) + go func() { + _, err := conn.Write(req) + writeErr <- err + }() + br := bufio.NewReader(conn) + lineNum := 0 + for { + line, err := br.ReadString('\n') + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("ReadLine: %v", err) + } + lineNum++ + if lineNum == 1 && !strings.Contains(line, "401 Unauthorized") { + t.Errorf("Response line = %q; want a 401", line) + } + } + // Wait for write to finish. This is a broken pipe on both + // Darwin and Linux, but checking this isn't the point of + // the test. + <-writeErr +} + +func TestCaseSensitiveMethod(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + if r.Method != "get" { + t.Errorf(`Got method %q; want "get"`, r.Method) + } + })) + defer ts.Close() + req, _ := NewRequest("get", ts.URL, nil) + res, err := DefaultClient.Do(req) + if err != nil { + t.Error(err) + return + } + res.Body.Close() +} + +// TestContentLengthZero tests that for both an HTTP/1.0 and HTTP/1.1 +// request (both keep-alive), when a Handler never writes any +// response, the net/http package adds a "Content-Length: 0" response +// header. +func TestContentLengthZero(t *testing.T) { + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {})) + defer ts.Close() + + for _, version := range []string{"HTTP/1.0", "HTTP/1.1"} { + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("error dialing: %v", err) + } + _, err = fmt.Fprintf(conn, "GET / %v\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n", version) + if err != nil { + t.Fatalf("error writing: %v", err) + } + req, _ := NewRequest("GET", "/", nil) + res, err := ReadResponse(bufio.NewReader(conn), req) + if err != nil { + t.Fatalf("error reading response: %v", err) + } + if te := res.TransferEncoding; len(te) > 0 { + t.Errorf("For version %q, Transfer-Encoding = %q; want none", version, te) + } + if cl := res.ContentLength; cl != 0 { + t.Errorf("For version %q, Content-Length = %v; want 0", version, cl) + } + conn.Close() + } +} + +func TestCloseNotifier(t *testing.T) { + gotReq := make(chan bool, 1) + sawClose := make(chan bool, 1) + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + gotReq <- true + cc := rw.(CloseNotifier).CloseNotify() + <-cc + sawClose <- true + })) + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("error dialing: %v", err) + } + diec := make(chan bool) + go func() { + _, err = fmt.Fprintf(conn, "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n") + if err != nil { + t.Fatal(err) + } + <-diec + conn.Close() + }() +For: + for { + select { + case <-gotReq: + diec <- true + case <-sawClose: + break For + case <-time.After(5 * time.Second): + t.Fatal("timeout") + } + } + ts.Close() +} + +func TestOptions(t *testing.T) { + uric := make(chan string, 2) // only expect 1, but leave space for 2 + mux := NewServeMux() + mux.HandleFunc("/", func(w ResponseWriter, r *Request) { + uric <- r.RequestURI + }) + ts := httptest.NewServer(mux) + defer ts.Close() + + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + // An OPTIONS * request should succeed. + _, err = conn.Write([]byte("OPTIONS * HTTP/1.1\r\nHost: foo.com\r\n\r\n")) + if err != nil { + t.Fatal(err) + } + br := bufio.NewReader(conn) + res, err := ReadResponse(br, &Request{Method: "OPTIONS"}) + if err != nil { + t.Fatal(err) + } + if res.StatusCode != 200 { + t.Errorf("Got non-200 response to OPTIONS *: %#v", res) + } + + // A GET * request on a ServeMux should fail. + _, err = conn.Write([]byte("GET * HTTP/1.1\r\nHost: foo.com\r\n\r\n")) + if err != nil { + t.Fatal(err) + } + res, err = ReadResponse(br, &Request{Method: "GET"}) + if err != nil { + t.Fatal(err) + } + if res.StatusCode != 400 { + t.Errorf("Got non-400 response to GET *: %#v", res) + } + + res, err = Get(ts.URL + "/second") + if err != nil { + t.Fatal(err) + } + res.Body.Close() + if got := <-uric; got != "/second" { + t.Errorf("Handler saw request for %q; want /second", got) + } +} + // goTimeout runs f, failing t if f takes more than ns to complete. func goTimeout(t *testing.T, d time.Duration, f func()) { ch := make(chan bool, 2) @@ -1184,3 +1523,100 @@ func BenchmarkClientServer(b *testing.B) { b.StopTimer() } + +func BenchmarkClientServerParallel4(b *testing.B) { + benchmarkClientServerParallel(b, 4) +} + +func BenchmarkClientServerParallel64(b *testing.B) { + benchmarkClientServerParallel(b, 64) +} + +func benchmarkClientServerParallel(b *testing.B, conc int) { + b.StopTimer() + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { + fmt.Fprintf(rw, "Hello world.\n") + })) + defer ts.Close() + b.StartTimer() + + numProcs := runtime.GOMAXPROCS(-1) * conc + var wg sync.WaitGroup + wg.Add(numProcs) + n := int32(b.N) + for p := 0; p < numProcs; p++ { + go func() { + for atomic.AddInt32(&n, -1) >= 0 { + res, err := Get(ts.URL) + if err != nil { + b.Logf("Get: %v", err) + continue + } + all, err := ioutil.ReadAll(res.Body) + if err != nil { + b.Logf("ReadAll: %v", err) + continue + } + body := string(all) + if body != "Hello world.\n" { + panic("Got body: " + body) + } + } + wg.Done() + }() + } + wg.Wait() +} + +// A benchmark for profiling the server without the HTTP client code. +// The client code runs in a subprocess. +// +// For use like: +// $ go test -c +// $ ./http.test -test.run=XX -test.bench=BenchmarkServer -test.benchtime=15s -test.cpuprofile=http.prof +// $ go tool pprof http.test http.prof +// (pprof) web +func BenchmarkServer(b *testing.B) { + // Child process mode; + if url := os.Getenv("TEST_BENCH_SERVER_URL"); url != "" { + n, err := strconv.Atoi(os.Getenv("TEST_BENCH_CLIENT_N")) + if err != nil { + panic(err) + } + for i := 0; i < n; i++ { + res, err := Get(url) + if err != nil { + log.Panicf("Get: %v", err) + } + all, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Panicf("ReadAll: %v", err) + } + body := string(all) + if body != "Hello world.\n" { + log.Panicf("Got body: %q", body) + } + } + os.Exit(0) + return + } + + var res = []byte("Hello world.\n") + b.StopTimer() + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { + rw.Header().Set("Content-Type", "text/html; charset=utf-8") + rw.Write(res) + })) + defer ts.Close() + b.StartTimer() + + cmd := exec.Command(os.Args[0], "-test.run=XXXX", "-test.bench=BenchmarkServer") + cmd.Env = append([]string{ + fmt.Sprintf("TEST_BENCH_CLIENT_N=%d", b.N), + fmt.Sprintf("TEST_BENCH_SERVER_URL=%s", ts.URL), + }, os.Environ()...) + out, err := cmd.CombinedOutput() + if err != nil { + b.Errorf("Test failure: %v, with output: %s", err, out) + } +} diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go index 0572b4ae3..b6ab78228 100644 --- a/src/pkg/net/http/server.go +++ b/src/pkg/net/http/server.go @@ -11,7 +11,6 @@ package http import ( "bufio" - "bytes" "crypto/tls" "errors" "fmt" @@ -21,7 +20,7 @@ import ( "net" "net/url" "path" - "runtime/debug" + "runtime" "strconv" "strings" "sync" @@ -94,30 +93,188 @@ type Hijacker interface { Hijack() (net.Conn, *bufio.ReadWriter, error) } +// The CloseNotifier interface is implemented by ResponseWriters which +// allow detecting when the underlying connection has gone away. +// +// This mechanism can be used to cancel long operations on the server +// if the client has disconnected before the response is ready. +type CloseNotifier interface { + // CloseNotify returns a channel that receives a single value + // when the client connection has gone away. + CloseNotify() <-chan bool +} + // A conn represents the server side of an HTTP connection. type conn struct { remoteAddr string // network address of remote side server *Server // the Server on which the connection arrived rwc net.Conn // i/o connection - lr *io.LimitedReader // io.LimitReader(rwc) - buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->rwc - hijacked bool // connection has been hijacked by handler + sr switchReader // where the LimitReader reads from; usually the rwc + lr *io.LimitedReader // io.LimitReader(sr) + buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc tlsState *tls.ConnectionState // or nil when not using TLS - body []byte + + mu sync.Mutex // guards the following + clientGone bool // if client has disconnected mid-request + closeNotifyc chan bool // made lazily + hijackedv bool // connection has been hijacked by handler +} + +func (c *conn) hijacked() bool { + c.mu.Lock() + defer c.mu.Unlock() + return c.hijackedv +} + +func (c *conn) hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.hijackedv { + return nil, nil, ErrHijacked + } + if c.closeNotifyc != nil { + return nil, nil, errors.New("http: Hijack is incompatible with use of CloseNotifier") + } + c.hijackedv = true + rwc = c.rwc + buf = c.buf + c.rwc = nil + c.buf = nil + return +} + +func (c *conn) closeNotify() <-chan bool { + c.mu.Lock() + defer c.mu.Unlock() + if c.closeNotifyc == nil { + c.closeNotifyc = make(chan bool) + if c.hijackedv { + // to obey the function signature, even though + // it'll never receive a value. + return c.closeNotifyc + } + pr, pw := io.Pipe() + + readSource := c.sr.r + c.sr.Lock() + c.sr.r = pr + c.sr.Unlock() + go func() { + _, err := io.Copy(pw, readSource) + if err == nil { + err = io.EOF + } + pw.CloseWithError(err) + c.noteClientGone() + }() + } + return c.closeNotifyc +} + +func (c *conn) noteClientGone() { + c.mu.Lock() + defer c.mu.Unlock() + if c.closeNotifyc != nil && !c.clientGone { + c.closeNotifyc <- true + } + c.clientGone = true +} + +type switchReader struct { + sync.Mutex + r io.Reader +} + +func (sr *switchReader) Read(p []byte) (n int, err error) { + sr.Lock() + r := sr.r + sr.Unlock() + return r.Read(p) +} + +// This should be >= 512 bytes for DetectContentType, +// but otherwise it's somewhat arbitrary. +const bufferBeforeChunkingSize = 2048 + +// chunkWriter writes to a response's conn buffer, and is the writer +// wrapped by the response.bufw buffered writer. +// +// chunkWriter also is responsible for finalizing the Header, including +// conditionally setting the Content-Type and setting a Content-Length +// in cases where the handler's final output is smaller than the buffer +// size. It also conditionally adds chunk headers, when in chunking mode. +// +// See the comment above (*response).Write for the entire write flow. +type chunkWriter struct { + res *response + header Header // a deep copy of r.Header, once WriteHeader is called + wroteHeader bool // whether the header's been sent + + // set by the writeHeader method: + chunking bool // using chunked transfer encoding for reply body +} + +var crlf = []byte("\r\n") + +func (cw *chunkWriter) Write(p []byte) (n int, err error) { + if !cw.wroteHeader { + cw.writeHeader(p) + } + if cw.chunking { + _, err = fmt.Fprintf(cw.res.conn.buf, "%x\r\n", len(p)) + if err != nil { + cw.res.conn.rwc.Close() + return + } + } + n, err = cw.res.conn.buf.Write(p) + if cw.chunking && err == nil { + _, err = cw.res.conn.buf.Write(crlf) + } + if err != nil { + cw.res.conn.rwc.Close() + } + return +} + +func (cw *chunkWriter) flush() { + if !cw.wroteHeader { + cw.writeHeader(nil) + } + cw.res.conn.buf.Flush() +} + +func (cw *chunkWriter) close() { + if !cw.wroteHeader { + cw.writeHeader(nil) + } + if cw.chunking { + // zero EOF chunk, trailer key/value pairs (currently + // unsupported in Go's server), followed by a blank + // line. + io.WriteString(cw.res.conn.buf, "0\r\n\r\n") + } } // A response represents the server side of an HTTP response. type response struct { conn *conn req *Request // request for this response - chunking bool // using chunked transfer encoding for reply body - wroteHeader bool // reply header has been written + wroteHeader bool // reply header has been (logically) written wroteContinue bool // 100 Continue response was written - header Header // reply header parameters - written int64 // number of bytes written in body - contentLength int64 // explicitly-declared Content-Length; or -1 - status int // status code passed to WriteHeader - needSniff bool // need to sniff to find Content-Type + + w *bufio.Writer // buffers output in chunks to chunkWriter + cw *chunkWriter + + // handlerHeader is the Header that Handlers get access to, + // which may be retained and mutated even after WriteHeader. + // handlerHeader is copied into cw.header at WriteHeader + // time, and privately mutated thereafter. + handlerHeader Header + + written int64 // number of bytes written in body + contentLength int64 // explicitly-declared Content-Length; or -1 + status int // status code passed to WriteHeader // close connection after this reply. set on request and // updated after response from handler if there's a @@ -127,12 +284,14 @@ type response struct { // requestBodyLimitHit is set by requestTooLarge when // maxBytesReader hits its max size. It is checked in - // WriteHeader, to make sure we don't consume the the + // WriteHeader, to make sure we don't consume the // remaining request body to try to advance to the next HTTP - // request. Instead, when this is set, we stop doing + // request. Instead, when this is set, we stop reading // subsequent requests on this connection and stop reading // input from it. requestBodyLimitHit bool + + handlerDone bool // set true when the handler exits } // requestTooLarge is called by maxBytesReader when too much input has @@ -145,42 +304,68 @@ func (w *response) requestTooLarge() { } } +// needsSniff returns whether a Content-Type still needs to be sniffed. +func (w *response) needsSniff() bool { + return !w.cw.wroteHeader && w.handlerHeader.Get("Content-Type") == "" && w.written < sniffLen +} + type writerOnly struct { io.Writer } func (w *response) ReadFrom(src io.Reader) (n int64, err error) { - // Call WriteHeader before checking w.chunking if it hasn't - // been called yet, since WriteHeader is what sets w.chunking. if !w.wroteHeader { w.WriteHeader(StatusOK) } - if !w.chunking && w.bodyAllowed() && !w.needSniff { - w.Flush() + + if w.needsSniff() { + n0, err := io.Copy(writerOnly{w}, io.LimitReader(src, sniffLen)) + n += n0 + if err != nil { + return n, err + } + } + + w.w.Flush() // get rid of any previous writes + w.cw.flush() // make sure Header is written; flush data to rwc + + // Now that cw has been flushed, its chunking field is guaranteed initialized. + if !w.cw.chunking && w.bodyAllowed() { if rf, ok := w.conn.rwc.(io.ReaderFrom); ok { - n, err = rf.ReadFrom(src) - w.written += n - return + n0, err := rf.ReadFrom(src) + n += n0 + w.written += n0 + return n, err } } + // Fall back to default io.Copy implementation. // Use wrapper to hide w.ReadFrom from io.Copy. - return io.Copy(writerOnly{w}, src) + n0, err := io.Copy(writerOnly{w}, src) + n += n0 + return n, err } // noLimit is an effective infinite upper bound for io.LimitedReader const noLimit int64 = (1 << 63) - 1 +// debugServerConnections controls whether all server connections are wrapped +// with a verbose logging wrapper. +const debugServerConnections = false + // Create new connection from rwc. func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) { c = new(conn) c.remoteAddr = rwc.RemoteAddr().String() c.server = srv c.rwc = rwc - c.body = make([]byte, sniffLen) - c.lr = io.LimitReader(rwc, noLimit).(*io.LimitedReader) + if debugServerConnections { + c.rwc = newLoggingConn("server", c.rwc) + } + c.sr = switchReader{r: c.rwc} + c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader) br := bufio.NewReader(c.lr) - bw := bufio.NewWriter(rwc) + bw := bufio.NewWriter(c.rwc) c.buf = bufio.NewReadWriter(br, bw) return c, nil } @@ -207,9 +392,9 @@ type expectContinueReader struct { func (ecr *expectContinueReader) Read(p []byte) (n int, err error) { if ecr.closed { - return 0, errors.New("http: Read after Close on request Body") + return 0, ErrBodyReadAfterClose } - if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked { + if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() { ecr.resp.wroteContinue = true io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n") ecr.resp.conn.buf.Flush() @@ -232,9 +417,19 @@ var errTooLarge = errors.New("http: request too large") // Read next request from connection. func (c *conn) readRequest() (w *response, err error) { - if c.hijacked { + if c.hijacked() { return nil, ErrHijacked } + + if d := c.server.ReadTimeout; d != 0 { + c.rwc.SetReadDeadline(time.Now().Add(d)) + } + if d := c.server.WriteTimeout; d != 0 { + defer func() { + c.rwc.SetWriteDeadline(time.Now().Add(d)) + }() + } + c.lr.N = int64(c.server.maxHeaderBytes()) + 4096 /* bufio slop */ var req *Request if req, err = ReadRequest(c.buf.Reader); err != nil { @@ -248,17 +443,20 @@ func (c *conn) readRequest() (w *response, err error) { req.RemoteAddr = c.remoteAddr req.TLS = c.tlsState - w = new(response) - w.conn = c - w.req = req - w.header = make(Header) - w.contentLength = -1 - c.body = c.body[:0] + w = &response{ + conn: c, + req: req, + handlerHeader: make(Header), + contentLength: -1, + cw: new(chunkWriter), + } + w.cw.res = w + w.w = bufio.NewWriterSize(w.cw, bufferBeforeChunkingSize) return w, nil } func (w *response) Header() Header { - return w.header + return w.handlerHeader } // maxPostHandlerReadBytes is the max number of Request.Body bytes not @@ -273,7 +471,7 @@ func (w *response) Header() Header { const maxPostHandlerReadBytes = 256 << 10 func (w *response) WriteHeader(code int) { - if w.conn.hijacked { + if w.conn.hijacked() { log.Print("http: response.WriteHeader on hijacked connection") return } @@ -284,31 +482,68 @@ func (w *response) WriteHeader(code int) { w.wroteHeader = true w.status = code - // Check for a explicit (and valid) Content-Length header. - var hasCL bool - var contentLength int64 - if clenStr := w.header.Get("Content-Length"); clenStr != "" { - var err error - contentLength, err = strconv.ParseInt(clenStr, 10, 64) - if err == nil { - hasCL = true + w.cw.header = w.handlerHeader.clone() + + if cl := w.cw.header.get("Content-Length"); cl != "" { + v, err := strconv.ParseInt(cl, 10, 64) + if err == nil && v >= 0 { + w.contentLength = v } else { - log.Printf("http: invalid Content-Length of %q sent", clenStr) - w.header.Del("Content-Length") + log.Printf("http: invalid Content-Length of %q", cl) + w.cw.header.Del("Content-Length") + } + } +} + +// writeHeader finalizes the header sent to the client and writes it +// to cw.res.conn.buf. +// +// p is not written by writeHeader, but is the first chunk of the body +// that will be written. It is sniffed for a Content-Type if none is +// set explicitly. It's also used to set the Content-Length, if the +// total body size was small and the handler has already finished +// running. +func (cw *chunkWriter) writeHeader(p []byte) { + if cw.wroteHeader { + return + } + cw.wroteHeader = true + + w := cw.res + code := w.status + done := w.handlerDone + + // If the handler is done but never sent a Content-Length + // response header and this is our first (and last) write, set + // it, even to zero. This helps HTTP/1.0 clients keep their + // "keep-alive" connections alive. + if done && cw.header.get("Content-Length") == "" && w.req.Method != "HEAD" { + w.contentLength = int64(len(p)) + cw.header.Set("Content-Length", strconv.Itoa(len(p))) + } + + // If this was an HTTP/1.0 request with keep-alive and we sent a + // Content-Length back, we can make this a keep-alive response ... + if w.req.wantsHttp10KeepAlive() { + sentLength := cw.header.get("Content-Length") != "" + if sentLength && cw.header.get("Connection") == "keep-alive" { + w.closeAfterReply = false } } + // Check for a explicit (and valid) Content-Length header. + hasCL := w.contentLength != -1 + if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) { - _, connectionHeaderSet := w.header["Connection"] + _, connectionHeaderSet := cw.header["Connection"] if !connectionHeaderSet { - w.header.Set("Connection", "keep-alive") + cw.header.Set("Connection", "keep-alive") } - } else if !w.req.ProtoAtLeast(1, 1) { - // Client did not ask to keep connection alive. + } else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() { w.closeAfterReply = true } - if w.header.Get("Connection") == "close" { + if cw.header.get("Connection") == "close" { w.closeAfterReply = true } @@ -322,7 +557,7 @@ func (w *response) WriteHeader(code int) { n, _ := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1) if n >= maxPostHandlerReadBytes { w.requestTooLarge() - w.header.Set("Connection", "close") + cw.header.Set("Connection", "close") } else { w.req.Body.Close() } @@ -332,64 +567,67 @@ func (w *response) WriteHeader(code int) { if code == StatusNotModified { // Must not have body. for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} { - if w.header.Get(header) != "" { - // TODO: return an error if WriteHeader gets a return parameter - // or set a flag on w to make future Writes() write an error page? - // for now just log and drop the header. - log.Printf("http: StatusNotModified response with header %q defined", header) - w.header.Del(header) + // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers" + if cw.header.get(header) != "" { + cw.header.Del(header) } } } else { // If no content type, apply sniffing algorithm to body. - if w.header.Get("Content-Type") == "" && w.req.Method != "HEAD" { - w.needSniff = true + if cw.header.get("Content-Type") == "" && w.req.Method != "HEAD" { + cw.header.Set("Content-Type", DetectContentType(p)) } } - if _, ok := w.header["Date"]; !ok { - w.Header().Set("Date", time.Now().UTC().Format(TimeFormat)) + if _, ok := cw.header["Date"]; !ok { + cw.header.Set("Date", time.Now().UTC().Format(TimeFormat)) } - te := w.header.Get("Transfer-Encoding") + te := cw.header.get("Transfer-Encoding") hasTE := te != "" if hasCL && hasTE && te != "identity" { // TODO: return an error if WriteHeader gets a return parameter // For now just ignore the Content-Length. log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d", - te, contentLength) - w.header.Del("Content-Length") + te, w.contentLength) + cw.header.Del("Content-Length") hasCL = false } if w.req.Method == "HEAD" || code == StatusNotModified { // do nothing + } else if code == StatusNoContent { + cw.header.Del("Transfer-Encoding") } else if hasCL { - w.contentLength = contentLength - w.header.Del("Transfer-Encoding") + cw.header.Del("Transfer-Encoding") } else if w.req.ProtoAtLeast(1, 1) { // HTTP/1.1 or greater: use chunked transfer encoding // to avoid closing the connection at EOF. // TODO: this blows away any custom or stacked Transfer-Encoding they // might have set. Deal with that as need arises once we have a valid // use case. - w.chunking = true - w.header.Set("Transfer-Encoding", "chunked") + cw.chunking = true + cw.header.Set("Transfer-Encoding", "chunked") } else { // HTTP version < 1.1: cannot do chunked transfer // encoding and we don't know the Content-Length so // signal EOF by closing connection. w.closeAfterReply = true - w.header.Del("Transfer-Encoding") // in case already set + cw.header.Del("Transfer-Encoding") // in case already set } // Cannot use Content-Length with non-identity Transfer-Encoding. - if w.chunking { - w.header.Del("Content-Length") + if cw.chunking { + cw.header.Del("Content-Length") } if !w.req.ProtoAtLeast(1, 0) { return } + + if w.closeAfterReply && !hasToken(cw.header.get("Connection"), "close") { + cw.header.Set("Connection", "close") + } + proto := "HTTP/1.0" if w.req.ProtoAtLeast(1, 1) { proto = "HTTP/1.1" @@ -400,37 +638,8 @@ func (w *response) WriteHeader(code int) { text = "status code " + codestring } io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n") - w.header.Write(w.conn.buf) - - // If we need to sniff the body, leave the header open. - // Otherwise, end it here. - if !w.needSniff { - io.WriteString(w.conn.buf, "\r\n") - } -} - -// sniff uses the first block of written data, -// stored in w.conn.body, to decide the Content-Type -// for the HTTP body. -func (w *response) sniff() { - if !w.needSniff { - return - } - w.needSniff = false - - data := w.conn.body - fmt.Fprintf(w.conn.buf, "Content-Type: %s\r\n\r\n", DetectContentType(data)) - - if len(data) == 0 { - return - } - if w.chunking { - fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) - } - _, err := w.conn.buf.Write(data) - if w.chunking && err == nil { - io.WriteString(w.conn.buf, "\r\n") - } + cw.header.Write(w.conn.buf) + w.conn.buf.Write(crlf) } // bodyAllowed returns true if a Write is allowed for this response type. @@ -442,8 +651,40 @@ func (w *response) bodyAllowed() bool { return w.status != StatusNotModified && w.req.Method != "HEAD" } +// The Life Of A Write is like this: +// +// Handler starts. No header has been sent. The handler can either +// write a header, or just start writing. Writing before sending a header +// sends an implicity empty 200 OK header. +// +// If the handler didn't declare a Content-Length up front, we either +// go into chunking mode or, if the handler finishes running before +// the chunking buffer size, we compute a Content-Length and send that +// in the header instead. +// +// Likewise, if the handler didn't set a Content-Type, we sniff that +// from the initial chunk of output. +// +// The Writers are wired together like: +// +// 1. *response (the ResponseWriter) -> +// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes +// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type) +// and which writes the chunk headers, if needed. +// 4. conn.buf, a bufio.Writer of default (4kB) bytes +// 5. the rwc, the net.Conn. +// +// TODO(bradfitz): short-circuit some of the buffering when the +// initial header contains both a Content-Type and Content-Length. +// Also short-circuit in (1) when the header's been sent and not in +// chunking mode, writing directly to (4) instead, if (2) has no +// buffered data. More generally, we could short-circuit from (1) to +// (3) even in chunking mode if the write size from (1) is over some +// threshold and nothing is in (2). The answer might be mostly making +// bufferBeforeChunkingSize smaller and having bufio's fast-paths deal +// with this instead. func (w *response) Write(data []byte) (n int, err error) { - if w.conn.hijacked { + if w.conn.hijacked() { log.Print("http: response.Write on hijacked connection") return 0, ErrHijacked } @@ -461,73 +702,20 @@ func (w *response) Write(data []byte) (n int, err error) { if w.contentLength != -1 && w.written > w.contentLength { return 0, ErrContentLength } - - var m int - if w.needSniff { - // We need to sniff the beginning of the output to - // determine the content type. Accumulate the - // initial writes in w.conn.body. - // Cap m so that append won't allocate. - m = cap(w.conn.body) - len(w.conn.body) - if m > len(data) { - m = len(data) - } - w.conn.body = append(w.conn.body, data[:m]...) - data = data[m:] - if len(data) == 0 { - // Copied everything into the buffer. - // Wait for next write. - return m, nil - } - - // Filled the buffer; more data remains. - // Sniff the content (flushes the buffer) - // and then proceed with the remainder - // of the data as a normal Write. - // Calling sniff clears needSniff. - w.sniff() - } - - // TODO(rsc): if chunking happened after the buffering, - // then there would be fewer chunk headers. - // On the other hand, it would make hijacking more difficult. - if w.chunking { - fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt - } - n, err = w.conn.buf.Write(data) - if err == nil && w.chunking { - if n != len(data) { - err = io.ErrShortWrite - } - if err == nil { - io.WriteString(w.conn.buf, "\r\n") - } - } - - return m + n, err + return w.w.Write(data) } func (w *response) finishRequest() { - // If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length - // back, we can make this a keep-alive response ... - if w.req.wantsHttp10KeepAlive() { - sentLength := w.header.Get("Content-Length") != "" - if sentLength && w.header.Get("Connection") == "keep-alive" { - w.closeAfterReply = false - } - } + w.handlerDone = true + if !w.wroteHeader { w.WriteHeader(StatusOK) } - if w.needSniff { - w.sniff() - } - if w.chunking { - io.WriteString(w.conn.buf, "0\r\n") - // trailer key/value pairs, followed by blank line - io.WriteString(w.conn.buf, "\r\n") - } + + w.w.Flush() + w.cw.close() w.conn.buf.Flush() + // Close the body, unless we're about to close the whole TCP connection // anyway. if !w.closeAfterReply { @@ -537,7 +725,7 @@ func (w *response) finishRequest() { w.req.MultipartForm.RemoveAll() } - if w.contentLength != -1 && w.contentLength != w.written { + if w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written { // Did not write enough. Avoid getting out of sync. w.closeAfterReply = true } @@ -547,66 +735,114 @@ func (w *response) Flush() { if !w.wroteHeader { w.WriteHeader(StatusOK) } - w.sniff() - w.conn.buf.Flush() + w.w.Flush() + w.cw.flush() } -// Close the connection. -func (c *conn) close() { +func (c *conn) finalFlush() { if c.buf != nil { c.buf.Flush() c.buf = nil } +} + +// Close the connection. +func (c *conn) close() { + c.finalFlush() if c.rwc != nil { c.rwc.Close() c.rwc = nil } } +// rstAvoidanceDelay is the amount of time we sleep after closing the +// write side of a TCP connection before closing the entire socket. +// By sleeping, we increase the chances that the client sees our FIN +// and processes its final data before they process the subsequent RST +// from closing a connection with known unread data. +// This RST seems to occur mostly on BSD systems. (And Windows?) +// This timeout is somewhat arbitrary (~latency around the planet). +const rstAvoidanceDelay = 500 * time.Millisecond + +// closeWrite flushes any outstanding data and sends a FIN packet (if +// client is connected via TCP), signalling that we're done. We then +// pause for a bit, hoping the client processes it before `any +// subsequent RST. +// +// See http://golang.org/issue/3595 +func (c *conn) closeWriteAndWait() { + c.finalFlush() + if tcp, ok := c.rwc.(*net.TCPConn); ok { + tcp.CloseWrite() + } + time.Sleep(rstAvoidanceDelay) +} + +// validNPN returns whether the proto is not a blacklisted Next +// Protocol Negotiation protocol. Empty and built-in protocol types +// are blacklisted and can't be overridden with alternate +// implementations. +func validNPN(proto string) bool { + switch proto { + case "", "http/1.1", "http/1.0": + return false + } + return true +} + // Serve a new connection. func (c *conn) serve() { defer func() { - err := recover() - if err == nil { - return + if err := recover(); err != nil { + const size = 4096 + buf := make([]byte, size) + buf = buf[:runtime.Stack(buf, false)] + log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf) } - - var buf bytes.Buffer - fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err) - buf.Write(debug.Stack()) - log.Print(buf.String()) - - if c.rwc != nil { // may be nil if connection hijacked - c.rwc.Close() + if !c.hijacked() { + c.close() } }() if tlsConn, ok := c.rwc.(*tls.Conn); ok { + if d := c.server.ReadTimeout; d != 0 { + c.rwc.SetReadDeadline(time.Now().Add(d)) + } + if d := c.server.WriteTimeout; d != 0 { + c.rwc.SetWriteDeadline(time.Now().Add(d)) + } if err := tlsConn.Handshake(); err != nil { - c.close() return } c.tlsState = new(tls.ConnectionState) *c.tlsState = tlsConn.ConnectionState() + if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) { + if fn := c.server.TLSNextProto[proto]; fn != nil { + h := initNPNRequest{tlsConn, serverHandler{c.server}} + fn(c.server, tlsConn, h) + } + return + } } for { w, err := c.readRequest() if err != nil { - msg := "400 Bad Request" if err == errTooLarge { // Their HTTP client may or may not be // able to read this if we're // responding to them and hanging up // while they're still writing their // request. Undefined behavior. - msg = "413 Request Entity Too Large" + io.WriteString(c.rwc, "HTTP/1.1 413 Request Entity Too Large\r\n\r\n") + c.closeWriteAndWait() + break } else if err == io.EOF { break // Don't reply } else if neterr, ok := err.(net.Error); ok && neterr.Timeout() { break // Don't reply } - fmt.Fprintf(c.rwc, "HTTP/1.1 %s\r\n\r\n", msg) + io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\r\n\r\n") break } @@ -624,59 +860,59 @@ func (c *conn) serve() { break } req.Header.Del("Expect") - } else if req.Header.Get("Expect") != "" { - // TODO(bradfitz): let ServeHTTP handlers handle - // requests with non-standard expectation[s]? Seems - // theoretical at best, and doesn't fit into the - // current ServeHTTP model anyway. We'd need to - // make the ResponseWriter an optional - // "ExpectReplier" interface or something. - // - // For now we'll just obey RFC 2616 14.20 which says - // "If a server receives a request containing an - // Expect field that includes an expectation- - // extension that it does not support, it MUST - // respond with a 417 (Expectation Failed) status." - w.Header().Set("Connection", "close") - w.WriteHeader(StatusExpectationFailed) - w.finishRequest() + } else if req.Header.get("Expect") != "" { + w.sendExpectationFailed() break } - handler := c.server.Handler - if handler == nil { - handler = DefaultServeMux - } - // HTTP cannot have multiple simultaneous active requests.[*] // Until the server replies to this request, it can't read another, // so we might as well run the handler in this goroutine. // [*] Not strictly true: HTTP pipelining. We could let them all process // in parallel even if their responses need to be serialized. - handler.ServeHTTP(w, w.req) - if c.hijacked { + serverHandler{c.server}.ServeHTTP(w, w.req) + if c.hijacked() { return } w.finishRequest() if w.closeAfterReply { + if w.requestBodyLimitHit { + c.closeWriteAndWait() + } break } } - c.close() +} + +func (w *response) sendExpectationFailed() { + // TODO(bradfitz): let ServeHTTP handlers handle + // requests with non-standard expectation[s]? Seems + // theoretical at best, and doesn't fit into the + // current ServeHTTP model anyway. We'd need to + // make the ResponseWriter an optional + // "ExpectReplier" interface or something. + // + // For now we'll just obey RFC 2616 14.20 which says + // "If a server receives a request containing an + // Expect field that includes an expectation- + // extension that it does not support, it MUST + // respond with a 417 (Expectation Failed) status." + w.Header().Set("Connection", "close") + w.WriteHeader(StatusExpectationFailed) + w.finishRequest() } // Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter // and a Hijacker. func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { - if w.conn.hijacked { - return nil, nil, ErrHijacked + if w.wroteHeader { + w.cw.flush() } - w.conn.hijacked = true - rwc = w.conn.rwc - buf = w.conn.buf - w.conn.rwc = nil - w.conn.buf = nil - return + return w.conn.hijack() +} + +func (w *response) CloseNotify() <-chan bool { + return w.conn.closeNotify() } // The HandlerFunc type is an adapter to allow the use of @@ -817,13 +1053,13 @@ func RedirectHandler(url string, code int) Handler { // patterns and calls the handler for the pattern that // most closely matches the URL. // -// Patterns named fixed, rooted paths, like "/favicon.ico", +// Patterns name fixed, rooted paths, like "/favicon.ico", // or rooted subtrees, like "/images/" (note the trailing slash). // Longer patterns take precedence over shorter ones, so that // if there are handlers registered for both "/images/" // and "/images/thumbnails/", the latter handler will be // called for paths beginning "/images/thumbnails/" and the -// former will receiver requests for any other paths in the +// former will receive requests for any other paths in the // "/images/" subtree. // // Patterns may optionally begin with a host name, restricting matches to @@ -836,13 +1072,15 @@ func RedirectHandler(url string, code int) Handler { // redirecting any request containing . or .. elements to an // equivalent .- and ..-free URL. type ServeMux struct { - mu sync.RWMutex - m map[string]muxEntry + mu sync.RWMutex + m map[string]muxEntry + hosts bool // whether any patterns contain hostnames } type muxEntry struct { explicit bool h Handler + pattern string } // NewServeMux allocates and returns a new ServeMux. @@ -883,8 +1121,7 @@ func cleanPath(p string) string { // Find a handler on a handler map given a path string // Most-specific (longest) pattern wins -func (mux *ServeMux) match(path string) Handler { - var h Handler +func (mux *ServeMux) match(path string) (h Handler, pattern string) { var n = 0 for k, v := range mux.m { if !pathMatch(k, path) { @@ -893,37 +1130,64 @@ func (mux *ServeMux) match(path string) Handler { if h == nil || len(k) > n { n = len(k) h = v.h + pattern = v.pattern + } + } + return +} + +// Handler returns the handler to use for the given request, +// consulting r.Method, r.Host, and r.URL.Path. It always returns +// a non-nil handler. If the path is not in its canonical form, the +// handler will be an internally-generated handler that redirects +// to the canonical path. +// +// Handler also returns the registered pattern that matches the +// request or, in the case of internally-generated redirects, +// the pattern that will match after following the redirect. +// +// If there is no registered handler that applies to the request, +// Handler returns a ``page not found'' handler and an empty pattern. +func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { + if r.Method != "CONNECT" { + if p := cleanPath(r.URL.Path); p != r.URL.Path { + _, pattern = mux.handler(r.Host, p) + return RedirectHandler(p, StatusMovedPermanently), pattern } } - return h + + return mux.handler(r.Host, r.URL.Path) } -// handler returns the handler to use for the request r. -func (mux *ServeMux) handler(r *Request) Handler { +// handler is the main implementation of Handler. +// The path is known to be in canonical form, except for CONNECT methods. +func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { mux.mu.RLock() defer mux.mu.RUnlock() // Host-specific pattern takes precedence over generic ones - h := mux.match(r.Host + r.URL.Path) + if mux.hosts { + h, pattern = mux.match(host + path) + } if h == nil { - h = mux.match(r.URL.Path) + h, pattern = mux.match(path) } if h == nil { - h = NotFoundHandler() + h, pattern = NotFoundHandler(), "" } - return h + return } // ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { - // Clean path to canonical form and redirect. - if p := cleanPath(r.URL.Path); p != r.URL.Path { - w.Header().Set("Location", p) - w.WriteHeader(StatusMovedPermanently) + if r.RequestURI == "*" { + w.Header().Set("Connection", "close") + w.WriteHeader(StatusBadRequest) return } - mux.handler(r).ServeHTTP(w, r) + h, _ := mux.Handler(r) + h.ServeHTTP(w, r) } // Handle registers the handler for the given pattern. @@ -942,14 +1206,26 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) { panic("http: multiple registrations for " + pattern) } - mux.m[pattern] = muxEntry{explicit: true, h: handler} + mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} + + if pattern[0] != '/' { + mux.hosts = true + } // Helpful behavior: // If pattern is /tree/, insert an implicit permanent redirect for /tree. // It can be overridden by an explicit registration. n := len(pattern) if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { - mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(pattern, StatusMovedPermanently)} + // If pattern contains a host name, strip it and use remaining + // path for redirect. + path := pattern + if pattern[0] != '/' { + // In pattern, at least the last character is a '/', so + // strings.Index can't be -1. + path = pattern[strings.Index(pattern, "/"):] + } + mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern} } } @@ -971,7 +1247,7 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { } // Serve accepts incoming HTTP connections on the listener l, -// creating a new service thread for each. The service threads +// creating a new service goroutine for each. The service goroutines // read requests and then call handler to reply to them. // Handler is typically nil, in which case the DefaultServeMux is used. func Serve(l net.Listener, handler Handler) error { @@ -987,6 +1263,32 @@ type Server struct { WriteTimeout time.Duration // maximum duration before timing out write of the response MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0 TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS + + // TLSNextProto optionally specifies a function to take over + // ownership of the provided TLS connection when an NPN + // protocol upgrade has occured. The map key is the protocol + // name negotiated. The Handler argument should be used to + // handle HTTP requests and will initialize the Request's TLS + // and RemoteAddr if not already set. The connection is + // automatically closed when the function returns. + TLSNextProto map[string]func(*Server, *tls.Conn, Handler) +} + +// serverHandler delegates to either the server's Handler or +// DefaultServeMux and also handles "OPTIONS *" requests. +type serverHandler struct { + srv *Server +} + +func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { + handler := sh.srv.Handler + if handler == nil { + handler = DefaultServeMux + } + if req.RequestURI == "*" && req.Method == "OPTIONS" { + handler = globalOptionsHandler{} + } + handler.ServeHTTP(rw, req) } // ListenAndServe listens on the TCP network address srv.Addr and then @@ -1005,7 +1307,7 @@ func (srv *Server) ListenAndServe() error { } // Serve accepts incoming connections on the Listener l, creating a -// new service thread for each. The service threads read requests and +// new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. func (srv *Server) Serve(l net.Listener) error { defer l.Close() @@ -1029,12 +1331,6 @@ func (srv *Server) Serve(l net.Listener) error { return e } tempDelay = 0 - if srv.ReadTimeout != 0 { - rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout)) - } - if srv.WriteTimeout != 0 { - rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout)) - } c, err := srv.newConn(rw) if err != nil { continue @@ -1150,7 +1446,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error { // TimeoutHandler returns a Handler that runs h with the given time limit. // // The new Handler calls h.ServeHTTP to handle each request, but if a -// call runs for more than ns nanoseconds, the handler responds with +// call runs for longer than its time limit, the handler responds with // a 503 Service Unavailable error and the given message in its body. // (If msg is empty, a suitable default message will be sent.) // After such a timeout, writes by h to its ResponseWriter will return @@ -1180,7 +1476,7 @@ func (h *timeoutHandler) errorBody() string { } func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) { - done := make(chan bool) + done := make(chan bool, 1) tw := &timeoutWriter{w: w} go func() { h.handler.ServeHTTP(tw, r) @@ -1232,3 +1528,86 @@ func (tw *timeoutWriter) WriteHeader(code int) { tw.mu.Unlock() tw.w.WriteHeader(code) } + +// globalOptionsHandler responds to "OPTIONS *" requests. +type globalOptionsHandler struct{} + +func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) { + w.Header().Set("Content-Length", "0") + if r.ContentLength != 0 { + // Read up to 4KB of OPTIONS body (as mentioned in the + // spec as being reserved for future use), but anything + // over that is considered a waste of server resources + // (or an attack) and we abort and close the connection, + // courtesy of MaxBytesReader's EOF behavior. + mb := MaxBytesReader(w, r.Body, 4<<10) + io.Copy(ioutil.Discard, mb) + } +} + +// eofReader is a non-nil io.ReadCloser that always returns EOF. +var eofReader = ioutil.NopCloser(strings.NewReader("")) + +// initNPNRequest is an HTTP handler that initializes certain +// uninitialized fields in its *Request. Such partially-initialized +// Requests come from NPN protocol handlers. +type initNPNRequest struct { + c *tls.Conn + h serverHandler +} + +func (h initNPNRequest) ServeHTTP(rw ResponseWriter, req *Request) { + if req.TLS == nil { + req.TLS = &tls.ConnectionState{} + *req.TLS = h.c.ConnectionState() + } + if req.Body == nil { + req.Body = eofReader + } + if req.RemoteAddr == "" { + req.RemoteAddr = h.c.RemoteAddr().String() + } + h.h.ServeHTTP(rw, req) +} + +// loggingConn is used for debugging. +type loggingConn struct { + name string + net.Conn +} + +var ( + uniqNameMu sync.Mutex + uniqNameNext = make(map[string]int) +) + +func newLoggingConn(baseName string, c net.Conn) net.Conn { + uniqNameMu.Lock() + defer uniqNameMu.Unlock() + uniqNameNext[baseName]++ + return &loggingConn{ + name: fmt.Sprintf("%s-%d", baseName, uniqNameNext[baseName]), + Conn: c, + } +} + +func (c *loggingConn) Write(p []byte) (n int, err error) { + log.Printf("%s.Write(%d) = ....", c.name, len(p)) + n, err = c.Conn.Write(p) + log.Printf("%s.Write(%d) = %d, %v", c.name, len(p), n, err) + return +} + +func (c *loggingConn) Read(p []byte) (n int, err error) { + log.Printf("%s.Read(%d) = ....", c.name, len(p)) + n, err = c.Conn.Read(p) + log.Printf("%s.Read(%d) = %d, %v", c.name, len(p), n, err) + return +} + +func (c *loggingConn) Close() (err error) { + log.Printf("%s.Close() = ...", c.name) + err = c.Conn.Close() + log.Printf("%s.Close() = %v", c.name, err) + return +} diff --git a/src/pkg/net/http/server_test.go b/src/pkg/net/http/server_test.go new file mode 100644 index 000000000..8b4e8c6d6 --- /dev/null +++ b/src/pkg/net/http/server_test.go @@ -0,0 +1,95 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http + +import ( + "net/url" + "testing" +) + +var serveMuxRegister = []struct { + pattern string + h Handler +}{ + {"/dir/", serve(200)}, + {"/search", serve(201)}, + {"codesearch.google.com/search", serve(202)}, + {"codesearch.google.com/", serve(203)}, +} + +// serve returns a handler that sends a response with the given code. +func serve(code int) HandlerFunc { + return func(w ResponseWriter, r *Request) { + w.WriteHeader(code) + } +} + +var serveMuxTests = []struct { + method string + host string + path string + code int + pattern string +}{ + {"GET", "google.com", "/", 404, ""}, + {"GET", "google.com", "/dir", 301, "/dir/"}, + {"GET", "google.com", "/dir/", 200, "/dir/"}, + {"GET", "google.com", "/dir/file", 200, "/dir/"}, + {"GET", "google.com", "/search", 201, "/search"}, + {"GET", "google.com", "/search/", 404, ""}, + {"GET", "google.com", "/search/foo", 404, ""}, + {"GET", "codesearch.google.com", "/search", 202, "codesearch.google.com/search"}, + {"GET", "codesearch.google.com", "/search/", 203, "codesearch.google.com/"}, + {"GET", "codesearch.google.com", "/search/foo", 203, "codesearch.google.com/"}, + {"GET", "codesearch.google.com", "/", 203, "codesearch.google.com/"}, + {"GET", "images.google.com", "/search", 201, "/search"}, + {"GET", "images.google.com", "/search/", 404, ""}, + {"GET", "images.google.com", "/search/foo", 404, ""}, + {"GET", "google.com", "/../search", 301, "/search"}, + {"GET", "google.com", "/dir/..", 301, ""}, + {"GET", "google.com", "/dir/..", 301, ""}, + {"GET", "google.com", "/dir/./file", 301, "/dir/"}, + + // The /foo -> /foo/ redirect applies to CONNECT requests + // but the path canonicalization does not. + {"CONNECT", "google.com", "/dir", 301, "/dir/"}, + {"CONNECT", "google.com", "/../search", 404, ""}, + {"CONNECT", "google.com", "/dir/..", 200, "/dir/"}, + {"CONNECT", "google.com", "/dir/..", 200, "/dir/"}, + {"CONNECT", "google.com", "/dir/./file", 200, "/dir/"}, +} + +func TestServeMuxHandler(t *testing.T) { + mux := NewServeMux() + for _, e := range serveMuxRegister { + mux.Handle(e.pattern, e.h) + } + + for _, tt := range serveMuxTests { + r := &Request{ + Method: tt.method, + Host: tt.host, + URL: &url.URL{ + Path: tt.path, + }, + } + h, pattern := mux.Handler(r) + cs := &codeSaver{h: Header{}} + h.ServeHTTP(cs, r) + if pattern != tt.pattern || cs.code != tt.code { + t.Errorf("%s %s %s = %d, %q, want %d, %q", tt.method, tt.host, tt.path, cs.code, pattern, tt.code, tt.pattern) + } + } +} + +// A codeSaver is a ResponseWriter that saves the code passed to WriteHeader. +type codeSaver struct { + h Header + code int +} + +func (cs *codeSaver) Header() Header { return cs.h } +func (cs *codeSaver) Write(p []byte) (int, error) { return len(p), nil } +func (cs *codeSaver) WriteHeader(code int) { cs.code = code } diff --git a/src/pkg/net/http/transfer.go b/src/pkg/net/http/transfer.go index 9e9d84172..43c6023a3 100644 --- a/src/pkg/net/http/transfer.go +++ b/src/pkg/net/http/transfer.go @@ -87,10 +87,8 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) { // Sanitize Body,ContentLength,TransferEncoding if t.ResponseToHEAD { t.Body = nil - t.TransferEncoding = nil - // ContentLength is expected to hold Content-Length - if t.ContentLength < 0 { - return nil, ErrMissingContentLength + if chunked(t.TransferEncoding) { + t.ContentLength = -1 } } else { if !atLeastHTTP11 || t.Body == nil { @@ -122,9 +120,6 @@ func (t *transferWriter) shouldSendContentLength() bool { if t.ContentLength > 0 { return true } - if t.ResponseToHEAD { - return true - } // Many servers expect a Content-Length for these methods if t.Method == "POST" || t.Method == "PUT" { return true @@ -199,10 +194,11 @@ func (t *transferWriter) WriteBody(w io.Writer) (err error) { ncopy, err = io.Copy(w, t.Body) } else { ncopy, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength)) - nextra, err := io.Copy(ioutil.Discard, t.Body) if err != nil { return err } + var nextra int64 + nextra, err = io.Copy(ioutil.Discard, t.Body) ncopy += nextra } if err != nil { @@ -213,7 +209,7 @@ func (t *transferWriter) WriteBody(w io.Writer) (err error) { } } - if t.ContentLength != -1 && t.ContentLength != ncopy { + if !t.ResponseToHEAD && t.ContentLength != -1 && t.ContentLength != ncopy { return fmt.Errorf("http: Request.ContentLength=%d with Body length %d", t.ContentLength, ncopy) } @@ -294,10 +290,19 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { return err } - t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding) + realLength, err := fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding) if err != nil { return err } + if isResponse && t.RequestMethod == "HEAD" { + if n, err := parseContentLength(t.Header.get("Content-Length")); err != nil { + return err + } else { + t.ContentLength = n + } + } else { + t.ContentLength = realLength + } // Trailer t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding) @@ -310,7 +315,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { // See RFC2616, section 4.4. switch msg.(type) { case *Response: - if t.ContentLength == -1 && + if realLength == -1 && !chunked(t.TransferEncoding) && bodyAllowedForStatus(t.StatusCode) { // Unbounded body. @@ -322,12 +327,16 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { // or close connection when finished, since multipart is not supported yet switch { case chunked(t.TransferEncoding): - t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close} - case t.ContentLength >= 0: + if noBodyExpected(t.RequestMethod) { + t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close} + } else { + t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close} + } + case realLength >= 0: // TODO: limit the Content-Length. This is an easy DoS vector. - t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close} + t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close} default: - // t.ContentLength < 0, i.e. "Content-Length" not mentioned in header + // realLength < 0, i.e. "Content-Length" not mentioned in header if t.Close { // Close semantics (i.e. HTTP/1.0) t.Body = &body{Reader: r, closing: t.Close} @@ -371,12 +380,6 @@ func fixTransferEncoding(requestMethod string, header Header) ([]string, error) delete(header, "Transfer-Encoding") - // Head responses have no bodies, so the transfer encoding - // should be ignored. - if requestMethod == "HEAD" { - return nil, nil - } - encodings := strings.Split(raw[0], ",") te := make([]string, 0, len(encodings)) // TODO: Even though we only support "identity" and "chunked" @@ -432,11 +435,11 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header, } // Logic based on Content-Length - cl := strings.TrimSpace(header.Get("Content-Length")) + cl := strings.TrimSpace(header.get("Content-Length")) if cl != "" { - n, err := strconv.ParseInt(cl, 10, 64) - if err != nil || n < 0 { - return -1, &badStringError{"bad Content-Length", cl} + n, err := parseContentLength(cl) + if err != nil { + return -1, err } return n, nil } else { @@ -451,13 +454,6 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header, return 0, nil } - // Logic based on media type. The purpose of the following code is just - // to detect whether the unsupported "multipart/byteranges" is being - // used. A proper Content-Type parser is needed in the future. - if strings.Contains(strings.ToLower(header.Get("Content-Type")), "multipart/byteranges") { - return -1, ErrNotSupported - } - // Body-EOF logic based on other methods (like closing, or chunked coding) return -1, nil } @@ -469,14 +465,14 @@ func shouldClose(major, minor int, header Header) bool { if major < 1 { return true } else if major == 1 && minor == 0 { - if !strings.Contains(strings.ToLower(header.Get("Connection")), "keep-alive") { + if !strings.Contains(strings.ToLower(header.get("Connection")), "keep-alive") { return true } return false } else { // TODO: Should split on commas, toss surrounding white space, // and check each field. - if strings.ToLower(header.Get("Connection")) == "close" { + if strings.ToLower(header.get("Connection")) == "close" { header.Del("Connection") return true } @@ -486,7 +482,7 @@ func shouldClose(major, minor int, header Header) bool { // Parse the trailer header func fixTrailer(header Header, te []string) (Header, error) { - raw := header.Get("Trailer") + raw := header.get("Trailer") if raw == "" { return nil, nil } @@ -525,11 +521,11 @@ type body struct { res *response // response writer for server requests, else nil } -// ErrBodyReadAfterClose is returned when reading a Request Body after -// the body has been closed. This typically happens when the body is +// ErrBodyReadAfterClose is returned when reading a Request or Response +// Body after the body has been closed. This typically happens when the body is // read after an HTTP Handler calls WriteHeader or Write on its // ResponseWriter. -var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed request Body") +var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body") func (b *body) Read(p []byte) (n int, err error) { if b.closed { @@ -567,14 +563,22 @@ func seeUpcomingDoubleCRLF(r *bufio.Reader) bool { return false } +var errTrailerEOF = errors.New("http: unexpected EOF reading trailer") + func (b *body) readTrailer() error { // The common case, since nobody uses trailers. - buf, _ := b.r.Peek(2) + buf, err := b.r.Peek(2) if bytes.Equal(buf, singleCRLF) { b.r.ReadByte() b.r.ReadByte() return nil } + if len(buf) < 2 { + return errTrailerEOF + } + if err != nil { + return err + } // Make sure there's a header terminator coming up, to prevent // a DoS with an unbounded size Trailer. It's not easy to @@ -590,6 +594,9 @@ func (b *body) readTrailer() error { hdr, err := textproto.NewReader(b.r).ReadMIMEHeader() if err != nil { + if err == io.EOF { + return errTrailerEOF + } return err } switch rr := b.hdr.(type) { @@ -630,3 +637,18 @@ func (b *body) Close() error { } return nil } + +// parseContentLength trims whitespace from s and returns -1 if no value +// is set, or the value if it's >= 0. +func parseContentLength(cl string) (int64, error) { + cl = strings.TrimSpace(cl) + if cl == "" { + return -1, nil + } + n, err := strconv.ParseInt(cl, 10, 64) + if err != nil || n < 0 { + return 0, &badStringError{"bad Content-Length", cl} + } + return n, nil + +} diff --git a/src/pkg/net/http/transfer_test.go b/src/pkg/net/http/transfer_test.go new file mode 100644 index 000000000..8627a374c --- /dev/null +++ b/src/pkg/net/http/transfer_test.go @@ -0,0 +1,37 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http + +import ( + "bufio" + "strings" + "testing" +) + +func TestBodyReadBadTrailer(t *testing.T) { + b := &body{ + Reader: strings.NewReader("foobar"), + hdr: true, // force reading the trailer + r: bufio.NewReader(strings.NewReader("")), + } + buf := make([]byte, 7) + n, err := b.Read(buf[:3]) + got := string(buf[:n]) + if got != "foo" || err != nil { + t.Fatalf(`first Read = %d (%q), %v; want 3 ("foo")`, n, got, err) + } + + n, err = b.Read(buf[:]) + got = string(buf[:n]) + if got != "bar" || err != nil { + t.Fatalf(`second Read = %d (%q), %v; want 3 ("bar")`, n, got, err) + } + + n, err = b.Read(buf[:]) + got = string(buf[:n]) + if err == nil { + t.Errorf("final Read was successful (%q), expected error from trailer read", got) + } +} diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go index 6efe191eb..685d7d56c 100644 --- a/src/pkg/net/http/transport.go +++ b/src/pkg/net/http/transport.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // HTTP client implementation. See RFC 2616. -// +// // This is the low-level Transport implementation of RoundTripper. // The high-level interface is in client.go. @@ -24,13 +24,14 @@ import ( "os" "strings" "sync" + "time" ) // DefaultTransport is the default implementation of Transport and is -// used by DefaultClient. It establishes a new network connection for -// each call to Do and uses HTTP proxies as directed by the -// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy) -// environment variables. +// used by DefaultClient. It establishes network connections as needed +// and caches them for reuse by subsequent calls. It uses HTTP proxies +// as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and +// $no_proxy) environment variables. var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment} // DefaultMaxIdleConnsPerHost is the default value of Transport's @@ -41,8 +42,11 @@ const DefaultMaxIdleConnsPerHost = 2 // https, and http proxies (for either http or https with CONNECT). // Transport can also cache connections for future re-use. type Transport struct { - lk sync.Mutex + idleMu sync.Mutex idleConn map[string][]*persistConn + reqMu sync.Mutex + reqConn map[*Request]*persistConn + altMu sync.RWMutex altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper // TODO: tunable on global max cached connections @@ -68,9 +72,15 @@ type Transport struct { DisableCompression bool // MaxIdleConnsPerHost, if non-zero, controls the maximum idle - // (keep-alive) to keep to keep per-host. If zero, + // (keep-alive) to keep per-host. If zero, // DefaultMaxIdleConnsPerHost is used. MaxIdleConnsPerHost int + + // ResponseHeaderTimeout, if non-zero, specifies the amount of + // time to wait for a server's response headers after fully + // writing the request (including its body, if any). This + // time does not include the time to read the response body. + ResponseHeaderTimeout time.Duration } // ProxyFromEnvironment returns the URL of the proxy to use for a @@ -88,7 +98,7 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) { return nil, nil } proxyURL, err := url.Parse(proxy) - if err != nil || proxyURL.Scheme == "" { + if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") { if u, err := url.Parse("http://" + proxy); err == nil { proxyURL = u err = nil @@ -131,17 +141,20 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) { return nil, errors.New("http: nil Request.Header") } if req.URL.Scheme != "http" && req.URL.Scheme != "https" { - t.lk.Lock() + t.altMu.RLock() var rt RoundTripper if t.altProto != nil { rt = t.altProto[req.URL.Scheme] } - t.lk.Unlock() + t.altMu.RUnlock() if rt == nil { return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme} } return rt.RoundTrip(req) } + if req.URL.Host == "" { + return nil, errors.New("http: no Host in request URL") + } treq := &transportRequest{Request: req} cm, err := t.connectMethodForRequest(treq) if err != nil { @@ -170,8 +183,8 @@ func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { if scheme == "http" || scheme == "https" { panic("protocol " + scheme + " already registered") } - t.lk.Lock() - defer t.lk.Unlock() + t.altMu.Lock() + defer t.altMu.Unlock() if t.altProto == nil { t.altProto = make(map[string]RoundTripper) } @@ -186,17 +199,29 @@ func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { // a "keep-alive" state. It does not interrupt any connections currently // in use. func (t *Transport) CloseIdleConnections() { - t.lk.Lock() - defer t.lk.Unlock() - if t.idleConn == nil { + t.idleMu.Lock() + m := t.idleConn + t.idleConn = nil + t.idleMu.Unlock() + if m == nil { return } - for _, conns := range t.idleConn { + for _, conns := range m { for _, pconn := range conns { pconn.close() } } - t.idleConn = make(map[string][]*persistConn) +} + +// CancelRequest cancels an in-flight request by closing its +// connection. +func (t *Transport) CancelRequest(req *Request) { + t.reqMu.Lock() + pc := t.reqConn[req] + t.reqMu.Unlock() + if pc != nil { + pc.conn.Close() + } } // @@ -242,8 +267,6 @@ func (cm *connectMethod) proxyAuth() string { // If pconn is no longer needed or not in a good state, putIdleConn // returns false. func (t *Transport) putIdleConn(pconn *persistConn) bool { - t.lk.Lock() - defer t.lk.Unlock() if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 { pconn.close() return false @@ -256,21 +279,32 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool { if max == 0 { max = DefaultMaxIdleConnsPerHost } + t.idleMu.Lock() + if t.idleConn == nil { + t.idleConn = make(map[string][]*persistConn) + } if len(t.idleConn[key]) >= max { + t.idleMu.Unlock() pconn.close() return false } + for _, exist := range t.idleConn[key] { + if exist == pconn { + log.Fatalf("dup idle pconn %p in freelist", pconn) + } + } t.idleConn[key] = append(t.idleConn[key], pconn) + t.idleMu.Unlock() return true } func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) { - t.lk.Lock() - defer t.lk.Unlock() + key := cm.String() + t.idleMu.Lock() + defer t.idleMu.Unlock() if t.idleConn == nil { - t.idleConn = make(map[string][]*persistConn) + return nil } - key := cm.String() for { pconns, ok := t.idleConn[key] if !ok { @@ -289,7 +323,20 @@ func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) { return } } - return + panic("unreachable") +} + +func (t *Transport) setReqConn(r *Request, pc *persistConn) { + t.reqMu.Lock() + defer t.reqMu.Unlock() + if t.reqConn == nil { + t.reqConn = make(map[*Request]*persistConn) + } + if pc != nil { + t.reqConn[r] = pc + } else { + delete(t.reqConn, r) + } } func (t *Transport) dial(network, addr string) (c net.Conn, err error) { @@ -323,6 +370,8 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) { cacheKey: cm.String(), conn: conn, reqch: make(chan requestAndChan, 50), + writech: make(chan writeRequest, 50), + closech: make(chan struct{}), } switch { @@ -365,7 +414,18 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) { if cm.targetScheme == "https" { // Initiate TLS and check remote host name against certificate. - conn = tls.Client(conn, t.TLSClientConfig) + cfg := t.TLSClientConfig + if cfg == nil || cfg.ServerName == "" { + host := cm.tlsHost() + if cfg == nil { + cfg = &tls.Config{ServerName: host} + } else { + clone := *cfg // shallow clone + clone.ServerName = host + cfg = &clone + } + } + conn = tls.Client(conn, cfg) if err = conn.(*tls.Conn).Handshake(); err != nil { return nil, err } @@ -380,6 +440,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) { pconn.br = bufio.NewReader(pconn.conn) pconn.bw = bufio.NewWriter(pconn.conn) go pconn.readLoop() + go pconn.writeLoop() return pconn, nil } @@ -421,7 +482,15 @@ func useProxy(addr string) bool { if hasPort(p) { p = p[:strings.LastIndex(p, ":")] } - if addr == p || (p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:])) { + if addr == p { + return false + } + if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) { + // no_proxy ".foo.com" matches "bar.foo.com" or "foo.com" + return false + } + if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' { + // no_proxy "foo.com" matches "bar.foo.com" return false } } @@ -484,25 +553,28 @@ type persistConn struct { t *Transport cacheKey string // its connectMethod.String() conn net.Conn + closed bool // whether conn has been closed br *bufio.Reader // from conn bw *bufio.Writer // to conn - reqch chan requestAndChan // written by roundTrip(); read by readLoop() + reqch chan requestAndChan // written by roundTrip; read by readLoop + writech chan writeRequest // written by roundTrip; read by writeLoop + closech chan struct{} // broadcast close when readLoop (TCP connection) closes isProxy bool + lk sync.Mutex // guards following 3 fields + numExpectedResponses int + broken bool // an error has happened on this connection; marked broken so it's not reused. // mutateHeaderFunc is an optional func to modify extra // headers on each outbound request before it's written. (the // original Request given to RoundTrip is not modified) mutateHeaderFunc func(Header) - - lk sync.Mutex // guards numExpectedResponses and broken - numExpectedResponses int - broken bool // an error has happened on this connection; marked broken so it's not reused. } func (pc *persistConn) isBroken() bool { pc.lk.Lock() - defer pc.lk.Unlock() - return pc.broken + b := pc.broken + pc.lk.Unlock() + return b } var remoteSideClosedFunc func(error) bool // or nil to use default @@ -518,6 +590,7 @@ func remoteSideClosed(err error) bool { } func (pc *persistConn) readLoop() { + defer close(pc.closech) alive := true var lastbody io.ReadCloser // last response body, if any, read on this connection @@ -544,12 +617,16 @@ func (pc *persistConn) readLoop() { lastbody.Close() // assumed idempotent lastbody = nil } - resp, err := ReadResponse(pc.br, rc.req) + + var resp *Response + if err == nil { + resp, err = ReadResponse(pc.br, rc.req) + } + hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0 if err != nil { pc.close() } else { - hasBody := rc.req.Method != "HEAD" && resp.ContentLength != 0 if rc.addedGzip && hasBody && resp.Header.Get("Content-Encoding") == "gzip" { resp.Header.Del("Content-Encoding") resp.Header.Del("Content-Length") @@ -569,31 +646,37 @@ func (pc *persistConn) readLoop() { alive = false } - hasBody := resp != nil && resp.ContentLength != 0 var waitForBodyRead chan bool - if alive { - if hasBody { - lastbody = resp.Body - waitForBodyRead = make(chan bool) - resp.Body.(*bodyEOFSignal).fn = func() { - if !pc.t.putIdleConn(pc) { - alive = false - } - waitForBodyRead <- true + if hasBody { + lastbody = resp.Body + waitForBodyRead = make(chan bool, 1) + resp.Body.(*bodyEOFSignal).fn = func(err error) { + alive1 := alive + if err != nil { + alive1 = false } - } else { - // When there's no response body, we immediately - // reuse the TCP connection (putIdleConn), but - // we need to prevent ClientConn.Read from - // closing the Response.Body on the next - // loop, otherwise it might close the body - // before the client code has had a chance to - // read it (even though it'll just be 0, EOF). - lastbody = nil - - if !pc.t.putIdleConn(pc) { - alive = false + if alive1 && !pc.t.putIdleConn(pc) { + alive1 = false + } + if !alive1 || pc.isBroken() { + pc.close() } + waitForBodyRead <- alive1 + } + } + + if alive && !hasBody { + // When there's no response body, we immediately + // reuse the TCP connection (putIdleConn), but + // we need to prevent ClientConn.Read from + // closing the Response.Body on the next + // loop, otherwise it might close the body + // before the client code has had a chance to + // read it (even though it'll just be 0, EOF). + lastbody = nil + + if !pc.t.putIdleConn(pc) { + alive = false } } @@ -602,7 +685,35 @@ func (pc *persistConn) readLoop() { // Wait for the just-returned response body to be fully consumed // before we race and peek on the underlying bufio reader. if waitForBodyRead != nil { - <-waitForBodyRead + alive = <-waitForBodyRead + } + + pc.t.setReqConn(rc.req, nil) + + if !alive { + pc.close() + } + } +} + +func (pc *persistConn) writeLoop() { + for { + select { + case wr := <-pc.writech: + if pc.isBroken() { + wr.ch <- errors.New("http: can't write HTTP request on broken connection") + continue + } + err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra) + if err == nil { + err = pc.bw.Flush() + } + if err != nil { + pc.markBroken() + } + wr.ch <- err + case <-pc.closech: + return } } } @@ -622,9 +733,24 @@ type requestAndChan struct { addedGzip bool } +// A writeRequest is sent by the readLoop's goroutine to the +// writeLoop's goroutine to write a request while the read loop +// concurrently waits on both the write response and the server's +// reply. +type writeRequest struct { + req *transportRequest + ch chan<- error +} + func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) { - if pc.mutateHeaderFunc != nil { - pc.mutateHeaderFunc(req.extraHeaders()) + pc.t.setReqConn(req.Request, pc) + pc.lk.Lock() + pc.numExpectedResponses++ + headerFn := pc.mutateHeaderFunc + pc.lk.Unlock() + + if headerFn != nil { + headerFn(req.extraHeaders()) } // Ask for a compressed version if the caller didn't set their @@ -633,34 +759,84 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err // requested it. requestedGzip := false if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" { - // Request gzip only, not deflate. Deflate is ambiguous and + // Request gzip only, not deflate. Deflate is ambiguous and // not as universally supported anyway. // See: http://www.gzip.org/zlib/zlib_faq.html#faq38 requestedGzip = true req.extraHeaders().Set("Accept-Encoding", "gzip") } - pc.lk.Lock() - pc.numExpectedResponses++ - pc.lk.Unlock() + // Write the request concurrently with waiting for a response, + // in case the server decides to reply before reading our full + // request body. + writeErrCh := make(chan error, 1) + pc.writech <- writeRequest{req, writeErrCh} - err = req.Request.write(pc.bw, pc.isProxy, req.extra) - if err != nil { - pc.close() - return + resc := make(chan responseAndError, 1) + pc.reqch <- requestAndChan{req.Request, resc, requestedGzip} + + var re responseAndError + var pconnDeadCh = pc.closech + var failTicker <-chan time.Time + var respHeaderTimer <-chan time.Time +WaitResponse: + for { + select { + case err := <-writeErrCh: + if err != nil { + re = responseAndError{nil, err} + pc.close() + break WaitResponse + } + if d := pc.t.ResponseHeaderTimeout; d > 0 { + respHeaderTimer = time.After(d) + } + case <-pconnDeadCh: + // The persist connection is dead. This shouldn't + // usually happen (only with Connection: close responses + // with no response bodies), but if it does happen it + // means either a) the remote server hung up on us + // prematurely, or b) the readLoop sent us a response & + // closed its closech at roughly the same time, and we + // selected this case first, in which case a response + // might still be coming soon. + // + // We can't avoid the select race in b) by using a unbuffered + // resc channel instead, because then goroutines can + // leak if we exit due to other errors. + pconnDeadCh = nil // avoid spinning + failTicker = time.After(100 * time.Millisecond) // arbitrary time to wait for resc + case <-failTicker: + re = responseAndError{err: errors.New("net/http: transport closed before response was received")} + break WaitResponse + case <-respHeaderTimer: + pc.close() + re = responseAndError{err: errors.New("net/http: timeout awaiting response headers")} + break WaitResponse + case re = <-resc: + break WaitResponse + } } - pc.bw.Flush() - ch := make(chan responseAndError, 1) - pc.reqch <- requestAndChan{req.Request, ch, requestedGzip} - re := <-ch pc.lk.Lock() pc.numExpectedResponses-- pc.lk.Unlock() + if re.err != nil { + pc.t.setReqConn(req.Request, nil) + } return re.res, re.err } +// markBroken marks a connection as broken (so it's not reused). +// It differs from close in that it doesn't close the underlying +// connection for use when it's still being read. +func (pc *persistConn) markBroken() { + pc.lk.Lock() + defer pc.lk.Unlock() + pc.broken = true +} + func (pc *persistConn) close() { pc.lk.Lock() defer pc.lk.Unlock() @@ -669,7 +845,10 @@ func (pc *persistConn) close() { func (pc *persistConn) closeLocked() { pc.broken = true - pc.conn.Close() + if !pc.closed { + pc.conn.Close() + pc.closed = true + } pc.mutateHeaderFunc = nil } @@ -687,43 +866,62 @@ func canonicalAddr(url *url.URL) string { return addr } -func responseIsKeepAlive(res *Response) bool { - // TODO: implement. for now just always shutting down the connection. - return false -} - // bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most -// once, right before the final Read() or Close() call returns, but after -// EOF has been seen. +// once, right before its final (error-producing) Read or Close call +// returns. type bodyEOFSignal struct { - body io.ReadCloser - fn func() - isClosed bool + body io.ReadCloser + mu sync.Mutex // guards closed, rerr and fn + closed bool // whether Close has been called + rerr error // sticky Read error + fn func(error) // error will be nil on Read io.EOF } func (es *bodyEOFSignal) Read(p []byte) (n int, err error) { - n, err = es.body.Read(p) - if es.isClosed && n > 0 { - panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725") + es.mu.Lock() + closed, rerr := es.closed, es.rerr + es.mu.Unlock() + if closed { + return 0, errors.New("http: read on closed response body") + } + if rerr != nil { + return 0, rerr } - if err == io.EOF && es.fn != nil { - es.fn() - es.fn = nil + + n, err = es.body.Read(p) + if err != nil { + es.mu.Lock() + defer es.mu.Unlock() + if es.rerr == nil { + es.rerr = err + } + es.condfn(err) } return } -func (es *bodyEOFSignal) Close() (err error) { - if es.isClosed { +func (es *bodyEOFSignal) Close() error { + es.mu.Lock() + defer es.mu.Unlock() + if es.closed { return nil } - es.isClosed = true - err = es.body.Close() - if err == nil && es.fn != nil { - es.fn() - es.fn = nil + es.closed = true + err := es.body.Close() + es.condfn(err) + return err +} + +// caller must hold es.mu. +func (es *bodyEOFSignal) condfn(err error) { + if es.fn == nil { + return } - return + if err == io.EOF { + err = nil + } + es.fn(err) + es.fn = nil } type readFirstCloseBoth struct { diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go index a9e401de5..68010e68b 100644 --- a/src/pkg/net/http/transport_test.go +++ b/src/pkg/net/http/transport_test.go @@ -13,6 +13,7 @@ import ( "fmt" "io" "io/ioutil" + "net" . "net/http" "net/http/httptest" "net/url" @@ -20,6 +21,7 @@ import ( "runtime" "strconv" "strings" + "sync" "testing" "time" ) @@ -35,14 +37,78 @@ var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) { w.Write([]byte(r.RemoteAddr)) }) +// testCloseConn is a net.Conn tracked by a testConnSet. +type testCloseConn struct { + net.Conn + set *testConnSet +} + +func (c *testCloseConn) Close() error { + c.set.remove(c) + return c.Conn.Close() +} + +// testConnSet tracks a set of TCP connections and whether they've +// been closed. +type testConnSet struct { + t *testing.T + closed map[net.Conn]bool + list []net.Conn // in order created + mutex sync.Mutex +} + +func (tcs *testConnSet) insert(c net.Conn) { + tcs.mutex.Lock() + defer tcs.mutex.Unlock() + tcs.closed[c] = false + tcs.list = append(tcs.list, c) +} + +func (tcs *testConnSet) remove(c net.Conn) { + tcs.mutex.Lock() + defer tcs.mutex.Unlock() + tcs.closed[c] = true +} + +// some tests use this to manage raw tcp connections for later inspection +func makeTestDial(t *testing.T) (*testConnSet, func(n, addr string) (net.Conn, error)) { + connSet := &testConnSet{ + t: t, + closed: make(map[net.Conn]bool), + } + dial := func(n, addr string) (net.Conn, error) { + c, err := net.Dial(n, addr) + if err != nil { + return nil, err + } + tc := &testCloseConn{c, connSet} + connSet.insert(tc) + return tc, nil + } + return connSet, dial +} + +func (tcs *testConnSet) check(t *testing.T) { + tcs.mutex.Lock() + defer tcs.mutex.Unlock() + + for i, c := range tcs.list { + if !tcs.closed[c] { + t.Errorf("TCP connection #%d, %p (of %d total) was not closed", i+1, c, len(tcs.list)) + } + } +} + // Two subsequent requests and verify their response is the same. // The response from the server is our own IP:port func TestTransportKeepAlives(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(hostPortHandler) defer ts.Close() for _, disableKeepAlive := range []bool{false, true} { tr := &Transport{DisableKeepAlives: disableKeepAlive} + defer tr.CloseIdleConnections() c := &Client{Transport: tr} fetch := func(n int) string { @@ -69,11 +135,16 @@ func TestTransportKeepAlives(t *testing.T) { } func TestTransportConnectionCloseOnResponse(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(hostPortHandler) defer ts.Close() + connSet, testDial := makeTestDial(t) + for _, connectionClose := range []bool{false, true} { - tr := &Transport{} + tr := &Transport{ + Dial: testDial, + } c := &Client{Transport: tr} fetch := func(n int) string { @@ -92,8 +163,8 @@ func TestTransportConnectionCloseOnResponse(t *testing.T) { if err != nil { t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err) } - body, err := ioutil.ReadAll(res.Body) defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err) } @@ -107,15 +178,24 @@ func TestTransportConnectionCloseOnResponse(t *testing.T) { t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q", connectionClose, bodiesDiffer, body1, body2) } + + tr.CloseIdleConnections() } + + connSet.check(t) } func TestTransportConnectionCloseOnRequest(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(hostPortHandler) defer ts.Close() + connSet, testDial := makeTestDial(t) + for _, connectionClose := range []bool{false, true} { - tr := &Transport{} + tr := &Transport{ + Dial: testDial, + } c := &Client{Transport: tr} fetch := func(n int) string { @@ -149,10 +229,15 @@ func TestTransportConnectionCloseOnRequest(t *testing.T) { t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q", connectionClose, bodiesDiffer, body1, body2) } + + tr.CloseIdleConnections() } + + connSet.check(t) } func TestTransportIdleCacheKeys(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(hostPortHandler) defer ts.Close() @@ -185,6 +270,7 @@ func TestTransportIdleCacheKeys(t *testing.T) { } func TestTransportMaxPerHostIdleConns(t *testing.T) { + defer checkLeakedTransports(t) resch := make(chan string) gotReq := make(chan bool) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { @@ -201,7 +287,7 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) { c := &Client{Transport: tr} // Start 3 outstanding requests and wait for the server to get them. - // Their responses will hang until we we write to resch, though. + // Their responses will hang until we write to resch, though. donech := make(chan bool) doReq := func() { resp, err := c.Get(ts.URL) @@ -253,6 +339,7 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) { } func TestTransportServerClosingUnexpectedly(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(hostPortHandler) defer ts.Close() @@ -309,9 +396,9 @@ func TestTransportServerClosingUnexpectedly(t *testing.T) { // Test for http://golang.org/issue/2616 (appropriate issue number) // This fails pretty reliably with GOMAXPROCS=100 or something high. func TestStressSurpriseServerCloses(t *testing.T) { + defer checkLeakedTransports(t) if testing.Short() { - t.Logf("skipping test in short mode") - return + t.Skip("skipping test in short mode") } ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Content-Length", "5") @@ -365,6 +452,7 @@ func TestStressSurpriseServerCloses(t *testing.T) { // TestTransportHeadResponses verifies that we deal with Content-Lengths // with no bodies properly func TestTransportHeadResponses(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.Method != "HEAD" { panic("expected HEAD; got " + r.Method) @@ -384,7 +472,7 @@ func TestTransportHeadResponses(t *testing.T) { if e, g := "123", res.Header.Get("Content-Length"); e != g { t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g) } - if e, g := int64(0), res.ContentLength; e != g { + if e, g := int64(123), res.ContentLength; e != g { t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g) } } @@ -393,6 +481,7 @@ func TestTransportHeadResponses(t *testing.T) { // TestTransportHeadChunkedResponse verifies that we ignore chunked transfer-encoding // on responses to HEAD requests. func TestTransportHeadChunkedResponse(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.Method != "HEAD" { panic("expected HEAD; got " + r.Method) @@ -434,6 +523,7 @@ var roundTripTests = []struct { // Test that the modification made to the Request by the RoundTripper is cleaned up func TestRoundTripGzip(t *testing.T) { + defer checkLeakedTransports(t) const responseBody = "test response body" ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { accept := req.Header.Get("Accept-Encoding") @@ -490,6 +580,7 @@ func TestRoundTripGzip(t *testing.T) { } func TestTransportGzip(t *testing.T) { + defer checkLeakedTransports(t) const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" const nRandBytes = 1024 * 1024 ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { @@ -582,6 +673,7 @@ func TestTransportGzip(t *testing.T) { } func TestTransportProxy(t *testing.T) { + defer checkLeakedTransports(t) ch := make(chan string, 1) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ch <- "real server" @@ -610,6 +702,7 @@ func TestTransportProxy(t *testing.T) { // but checks that we don't recurse forever, and checks that // Content-Encoding is removed. func TestTransportGzipRecursive(t *testing.T) { + defer checkLeakedTransports(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Content-Encoding", "gzip") w.Write(rgz) @@ -636,6 +729,7 @@ func TestTransportGzipRecursive(t *testing.T) { // tests that persistent goroutine connections shut down when no longer desired. func TestTransportPersistConnLeak(t *testing.T) { + defer checkLeakedTransports(t) gotReqCh := make(chan bool) unblockCh := make(chan bool) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { @@ -698,8 +792,49 @@ func TestTransportPersistConnLeak(t *testing.T) { } } +// golang.org/issue/4531: Transport leaks goroutines when +// request.ContentLength is explicitly short +func TestTransportPersistConnLeakShortBody(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + })) + defer ts.Close() + + tr := &Transport{} + c := &Client{Transport: tr} + + n0 := runtime.NumGoroutine() + body := []byte("Hello") + for i := 0; i < 20; i++ { + req, err := NewRequest("POST", ts.URL, bytes.NewReader(body)) + if err != nil { + t.Fatal(err) + } + req.ContentLength = int64(len(body) - 2) // explicitly short + _, err = c.Do(req) + if err == nil { + t.Fatal("Expect an error from writing too long of a body.") + } + } + nhigh := runtime.NumGoroutine() + tr.CloseIdleConnections() + time.Sleep(50 * time.Millisecond) + runtime.GC() + nfinal := runtime.NumGoroutine() + + growth := nfinal - n0 + + // We expect 0 or 1 extra goroutine, empirically. Allow up to 5. + // Previously we were leaking one per numReq. + t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth) + if int(growth) > 5 { + t.Error("too many new goroutines") + } +} + // This used to crash; http://golang.org/issue/3266 func TestTransportIdleConnCrash(t *testing.T) { + defer checkLeakedTransports(t) tr := &Transport{} c := &Client{Transport: tr} @@ -724,6 +859,361 @@ func TestTransportIdleConnCrash(t *testing.T) { <-didreq } +// Test that the transport doesn't close the TCP connection early, +// before the response body has been read. This was a regression +// which sadly lacked a triggering test. The large response body made +// the old race easier to trigger. +func TestIssue3644(t *testing.T) { + defer checkLeakedTransports(t) + const numFoos = 5000 + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Connection", "close") + for i := 0; i < numFoos; i++ { + w.Write([]byte("foo ")) + } + })) + defer ts.Close() + tr := &Transport{} + c := &Client{Transport: tr} + res, err := c.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + bs, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if len(bs) != numFoos*len("foo ") { + t.Errorf("unexpected response length") + } +} + +// Test that a client receives a server's reply, even if the server doesn't read +// the entire request body. +func TestIssue3595(t *testing.T) { + defer checkLeakedTransports(t) + const deniedMsg = "sorry, denied." + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + Error(w, deniedMsg, StatusUnauthorized) + })) + defer ts.Close() + tr := &Transport{} + c := &Client{Transport: tr} + res, err := c.Post(ts.URL, "application/octet-stream", neverEnding('a')) + if err != nil { + t.Errorf("Post: %v", err) + return + } + got, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatalf("Body ReadAll: %v", err) + } + if !strings.Contains(string(got), deniedMsg) { + t.Errorf("Known bug: response %q does not contain %q", got, deniedMsg) + } +} + +// From http://golang.org/issue/4454 , +// "client fails to handle requests with no body and chunked encoding" +func TestChunkedNoContent(t *testing.T) { + defer checkLeakedTransports(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.WriteHeader(StatusNoContent) + })) + defer ts.Close() + + for _, closeBody := range []bool{true, false} { + c := &Client{Transport: &Transport{}} + const n = 4 + for i := 1; i <= n; i++ { + res, err := c.Get(ts.URL) + if err != nil { + t.Errorf("closingBody=%v, req %d/%d: %v", closeBody, i, n, err) + } else { + if closeBody { + res.Body.Close() + } + } + } + } +} + +func TestTransportConcurrency(t *testing.T) { + defer checkLeakedTransports(t) + const maxProcs = 16 + const numReqs = 500 + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + fmt.Fprintf(w, "%v", r.FormValue("echo")) + })) + defer ts.Close() + tr := &Transport{} + c := &Client{Transport: tr} + reqs := make(chan string) + defer close(reqs) + + var wg sync.WaitGroup + wg.Add(numReqs) + for i := 0; i < maxProcs*2; i++ { + go func() { + for req := range reqs { + res, err := c.Get(ts.URL + "/?echo=" + req) + if err != nil { + t.Errorf("error on req %s: %v", req, err) + wg.Done() + continue + } + all, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Errorf("read error on req %s: %v", req, err) + wg.Done() + continue + } + if string(all) != req { + t.Errorf("body of req %s = %q; want %q", req, all, req) + } + wg.Done() + res.Body.Close() + } + }() + } + for i := 0; i < numReqs; i++ { + reqs <- fmt.Sprintf("request-%d", i) + } + wg.Wait() +} + +func TestIssue4191_InfiniteGetTimeout(t *testing.T) { + defer checkLeakedTransports(t) + const debug = false + mux := NewServeMux() + mux.HandleFunc("/get", func(w ResponseWriter, r *Request) { + io.Copy(w, neverEnding('a')) + }) + ts := httptest.NewServer(mux) + timeout := 100 * time.Millisecond + + client := &Client{ + Transport: &Transport{ + Dial: func(n, addr string) (net.Conn, error) { + conn, err := net.Dial(n, addr) + if err != nil { + return nil, err + } + conn.SetDeadline(time.Now().Add(timeout)) + if debug { + conn = NewLoggingConn("client", conn) + } + return conn, nil + }, + DisableKeepAlives: true, + }, + } + + getFailed := false + nRuns := 5 + if testing.Short() { + nRuns = 1 + } + for i := 0; i < nRuns; i++ { + if debug { + println("run", i+1, "of", nRuns) + } + sres, err := client.Get(ts.URL + "/get") + if err != nil { + if !getFailed { + // Make the timeout longer, once. + getFailed = true + t.Logf("increasing timeout") + i-- + timeout *= 10 + continue + } + t.Errorf("Error issuing GET: %v", err) + break + } + _, err = io.Copy(ioutil.Discard, sres.Body) + if err == nil { + t.Errorf("Unexpected successful copy") + break + } + } + if debug { + println("tests complete; waiting for handlers to finish") + } + ts.Close() +} + +func TestIssue4191_InfiniteGetToPutTimeout(t *testing.T) { + defer checkLeakedTransports(t) + const debug = false + mux := NewServeMux() + mux.HandleFunc("/get", func(w ResponseWriter, r *Request) { + io.Copy(w, neverEnding('a')) + }) + mux.HandleFunc("/put", func(w ResponseWriter, r *Request) { + defer r.Body.Close() + io.Copy(ioutil.Discard, r.Body) + }) + ts := httptest.NewServer(mux) + timeout := 100 * time.Millisecond + + client := &Client{ + Transport: &Transport{ + Dial: func(n, addr string) (net.Conn, error) { + conn, err := net.Dial(n, addr) + if err != nil { + return nil, err + } + conn.SetDeadline(time.Now().Add(timeout)) + if debug { + conn = NewLoggingConn("client", conn) + } + return conn, nil + }, + DisableKeepAlives: true, + }, + } + + getFailed := false + nRuns := 5 + if testing.Short() { + nRuns = 1 + } + for i := 0; i < nRuns; i++ { + if debug { + println("run", i+1, "of", nRuns) + } + sres, err := client.Get(ts.URL + "/get") + if err != nil { + if !getFailed { + // Make the timeout longer, once. + getFailed = true + t.Logf("increasing timeout") + i-- + timeout *= 10 + continue + } + t.Errorf("Error issuing GET: %v", err) + break + } + req, _ := NewRequest("PUT", ts.URL+"/put", sres.Body) + _, err = client.Do(req) + if err == nil { + sres.Body.Close() + t.Errorf("Unexpected successful PUT") + break + } + sres.Body.Close() + } + if debug { + println("tests complete; waiting for handlers to finish") + } + ts.Close() +} + +func TestTransportResponseHeaderTimeout(t *testing.T) { + defer checkLeakedTransports(t) + if testing.Short() { + t.Skip("skipping timeout test in -short mode") + } + mux := NewServeMux() + mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {}) + mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) { + time.Sleep(2 * time.Second) + }) + ts := httptest.NewServer(mux) + defer ts.Close() + + tr := &Transport{ + ResponseHeaderTimeout: 500 * time.Millisecond, + } + defer tr.CloseIdleConnections() + c := &Client{Transport: tr} + + tests := []struct { + path string + want int + wantErr string + }{ + {path: "/fast", want: 200}, + {path: "/slow", wantErr: "timeout awaiting response headers"}, + {path: "/fast", want: 200}, + } + for i, tt := range tests { + res, err := c.Get(ts.URL + tt.path) + if err != nil { + if strings.Contains(err.Error(), tt.wantErr) { + continue + } + t.Errorf("%d. unexpected error: %v", i, err) + continue + } + if tt.wantErr != "" { + t.Errorf("%d. no error. expected error: %v", i, tt.wantErr) + continue + } + if res.StatusCode != tt.want { + t.Errorf("%d for path %q status = %d; want %d", i, tt.path, res.StatusCode, tt.want) + } + } +} + +func TestTransportCancelRequest(t *testing.T) { + defer checkLeakedTransports(t) + if testing.Short() { + t.Skip("skipping test in -short mode") + } + unblockc := make(chan bool) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + fmt.Fprintf(w, "Hello") + w.(Flusher).Flush() // send headers and some body + <-unblockc + })) + defer ts.Close() + defer close(unblockc) + + tr := &Transport{} + defer tr.CloseIdleConnections() + c := &Client{Transport: tr} + + req, _ := NewRequest("GET", ts.URL, nil) + res, err := c.Do(req) + if err != nil { + t.Fatal(err) + } + go func() { + time.Sleep(1 * time.Second) + tr.CancelRequest(req) + }() + t0 := time.Now() + body, err := ioutil.ReadAll(res.Body) + d := time.Since(t0) + + if err == nil { + t.Error("expected an error reading the body") + } + if string(body) != "Hello" { + t.Errorf("Body = %q; want Hello", body) + } + if d < 500*time.Millisecond { + t.Errorf("expected ~1 second delay; got %v", d) + } + // Verify no outstanding requests after readLoop/writeLoop + // goroutines shut down. + for tries := 3; tries > 0; tries-- { + n := tr.NumPendingRequestsForTesting() + if n == 0 { + break + } + time.Sleep(100 * time.Millisecond) + if tries == 1 { + t.Errorf("pending requests = %d; want 0", n) + } + } +} + type fooProto struct{} func (fooProto) RoundTrip(req *Request) (*Response, error) { @@ -737,6 +1227,7 @@ func (fooProto) RoundTrip(req *Request) (*Response, error) { } func TestTransportAltProto(t *testing.T) { + defer checkLeakedTransports(t) tr := &Transport{} c := &Client{Transport: tr} tr.RegisterProtocol("foo", fooProto{}) @@ -754,15 +1245,58 @@ func TestTransportAltProto(t *testing.T) { } } -var proxyFromEnvTests = []struct { +func TestTransportNoHost(t *testing.T) { + defer checkLeakedTransports(t) + tr := &Transport{} + _, err := tr.RoundTrip(&Request{ + Header: make(Header), + URL: &url.URL{ + Scheme: "http", + }, + }) + want := "http: no Host in request URL" + if got := fmt.Sprint(err); got != want { + t.Errorf("error = %v; want %q", err, want) + } +} + +type proxyFromEnvTest struct { + req string // URL to fetch; blank means "http://example.com" env string - wanturl string + noenv string + want string wanterr error -}{ - {"127.0.0.1:8080", "http://127.0.0.1:8080", nil}, - {"http://127.0.0.1:8080", "http://127.0.0.1:8080", nil}, - {"https://127.0.0.1:8080", "https://127.0.0.1:8080", nil}, - {"", "", nil}, +} + +func (t proxyFromEnvTest) String() string { + var buf bytes.Buffer + if t.env != "" { + fmt.Fprintf(&buf, "http_proxy=%q", t.env) + } + if t.noenv != "" { + fmt.Fprintf(&buf, " no_proxy=%q", t.noenv) + } + req := "http://example.com" + if t.req != "" { + req = t.req + } + fmt.Fprintf(&buf, " req=%q", req) + return strings.TrimSpace(buf.String()) +} + +var proxyFromEnvTests = []proxyFromEnvTest{ + {env: "127.0.0.1:8080", want: "http://127.0.0.1:8080"}, + {env: "cache.corp.example.com:1234", want: "http://cache.corp.example.com:1234"}, + {env: "cache.corp.example.com", want: "http://cache.corp.example.com"}, + {env: "https://cache.corp.example.com", want: "https://cache.corp.example.com"}, + {env: "http://127.0.0.1:8080", want: "http://127.0.0.1:8080"}, + {env: "https://127.0.0.1:8080", want: "https://127.0.0.1:8080"}, + {want: ""}, + {noenv: "example.com", req: "http://example.com/", env: "proxy", want: ""}, + {noenv: ".example.com", req: "http://example.com/", env: "proxy", want: ""}, + {noenv: "ample.com", req: "http://example.com/", env: "proxy", want: "http://proxy"}, + {noenv: "example.com", req: "http://foo.example.com/", env: "proxy", want: ""}, + {noenv: ".foo.com", req: "http://example.com/", env: "proxy", want: "http://proxy"}, } func TestProxyFromEnvironment(t *testing.T) { @@ -770,16 +1304,21 @@ func TestProxyFromEnvironment(t *testing.T) { os.Setenv("http_proxy", "") os.Setenv("NO_PROXY", "") os.Setenv("no_proxy", "") - for i, tt := range proxyFromEnvTests { + for _, tt := range proxyFromEnvTests { os.Setenv("HTTP_PROXY", tt.env) - req, _ := NewRequest("GET", "http://example.com", nil) + os.Setenv("NO_PROXY", tt.noenv) + reqURL := tt.req + if reqURL == "" { + reqURL = "http://example.com" + } + req, _ := NewRequest("GET", reqURL, nil) url, err := ProxyFromEnvironment(req) if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e { - t.Errorf("%d. got error = %q, want %q", i, g, e) + t.Errorf("%v: got error = %q, want %q", tt, g, e) continue } - if got := fmt.Sprintf("%s", url); got != tt.wanturl { - t.Errorf("%d. got URL = %q, want %q", i, url, tt.wanturl) + if got := fmt.Sprintf("%s", url); got != tt.want { + t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want) } } } diff --git a/src/pkg/net/http/z_last_test.go b/src/pkg/net/http/z_last_test.go new file mode 100644 index 000000000..44095a8d9 --- /dev/null +++ b/src/pkg/net/http/z_last_test.go @@ -0,0 +1,60 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http_test + +import ( + "net/http" + "runtime" + "strings" + "testing" + "time" +) + +// Verify the other tests didn't leave any goroutines running. +// This is in a file named z_last_test.go so it sorts at the end. +func TestGoroutinesRunning(t *testing.T) { + n := runtime.NumGoroutine() + t.Logf("num goroutines = %d", n) + if n > 20 { + // Currently 14 on Linux (blocked in epoll_wait, + // waiting for on fds that are closed?), but give some + // slop for now. + buf := make([]byte, 1<<20) + buf = buf[:runtime.Stack(buf, true)] + t.Errorf("Too many goroutines:\n%s", buf) + } +} + +func checkLeakedTransports(t *testing.T) { + http.DefaultTransport.(*http.Transport).CloseIdleConnections() + if testing.Short() { + return + } + buf := make([]byte, 1<<20) + var stacks string + var bad string + badSubstring := map[string]string{ + ").readLoop(": "a Transport", + ").writeLoop(": "a Transport", + "created by net/http/httptest.(*Server).Start": "an httptest.Server", + "timeoutHandler": "a TimeoutHandler", + } + for i := 0; i < 4; i++ { + bad = "" + stacks = string(buf[:runtime.Stack(buf, true)]) + for substr, what := range badSubstring { + if strings.Contains(stacks, substr) { + bad = what + } + } + if bad == "" { + return + } + // Bad stuff found, but goroutines might just still be + // shutting down, so give it some time. + time.Sleep(250 * time.Millisecond) + } + t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks) +} diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go index ee23570a9..0713e9cd6 100644 --- a/src/pkg/net/interface.go +++ b/src/pkg/net/interface.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Network interface identification - package net import "errors" @@ -66,7 +64,7 @@ func (ifi *Interface) Addrs() ([]Addr, error) { if ifi == nil { return nil, errInvalidInterface } - return interfaceAddrTable(ifi.Index) + return interfaceAddrTable(ifi) } // MulticastAddrs returns multicast, joined group addresses for @@ -75,7 +73,7 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) { if ifi == nil { return nil, errInvalidInterface } - return interfaceMulticastAddrTable(ifi.Index) + return interfaceMulticastAddrTable(ifi) } // Interfaces returns a list of the system's network interfaces. @@ -86,7 +84,7 @@ func Interfaces() ([]Interface, error) { // InterfaceAddrs returns a list of the system's network interface // addresses. func InterfaceAddrs() ([]Addr, error) { - return interfaceAddrTable(0) + return interfaceAddrTable(nil) } // InterfaceByIndex returns the interface specified by index. @@ -98,8 +96,14 @@ func InterfaceByIndex(index int) (*Interface, error) { if err != nil { return nil, err } + return interfaceByIndex(ift, index) +} + +func interfaceByIndex(ift []Interface, index int) (*Interface, error) { for _, ifi := range ift { - return &ifi, nil + if index == ifi.Index { + return &ifi, nil + } } return nil, errNoSuchInterface } diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go index 7f090d8d4..f58065a85 100644 --- a/src/pkg/net/interface_bsd.go +++ b/src/pkg/net/interface_bsd.go @@ -4,8 +4,6 @@ // +build darwin freebsd netbsd openbsd -// Network interface identification for BSD variants - package net import ( @@ -22,57 +20,60 @@ func interfaceTable(ifindex int) ([]Interface, error) { if err != nil { return nil, os.NewSyscallError("route rib", err) } - msgs, err := syscall.ParseRoutingMessage(tab) if err != nil { return nil, os.NewSyscallError("route message", err) } + return parseInterfaceTable(ifindex, msgs) +} +func parseInterfaceTable(ifindex int, msgs []syscall.RoutingMessage) ([]Interface, error) { var ift []Interface +loop: for _, m := range msgs { - switch v := m.(type) { + switch m := m.(type) { case *syscall.InterfaceMessage: - if ifindex == 0 || ifindex == int(v.Header.Index) { - ifi, err := newLink(v) + if ifindex == 0 || ifindex == int(m.Header.Index) { + ifi, err := newLink(m) if err != nil { return nil, err } - ift = append(ift, ifi...) + ift = append(ift, *ifi) + if ifindex == int(m.Header.Index) { + break loop + } } } } return ift, nil } -func newLink(m *syscall.InterfaceMessage) ([]Interface, error) { +func newLink(m *syscall.InterfaceMessage) (*Interface, error) { sas, err := syscall.ParseRoutingSockaddr(m) if err != nil { return nil, os.NewSyscallError("route sockaddr", err) } - - var ift []Interface - for _, s := range sas { - switch v := s.(type) { + ifi := &Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)} + for _, sa := range sas { + switch sa := sa.(type) { case *syscall.SockaddrDatalink: // NOTE: SockaddrDatalink.Data is minimum work area, // can be larger. - m.Data = m.Data[unsafe.Offsetof(v.Data):] - ifi := Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)} + m.Data = m.Data[unsafe.Offsetof(sa.Data):] var name [syscall.IFNAMSIZ]byte - for i := 0; i < int(v.Nlen); i++ { + for i := 0; i < int(sa.Nlen); i++ { name[i] = byte(m.Data[i]) } - ifi.Name = string(name[:v.Nlen]) + ifi.Name = string(name[:sa.Nlen]) ifi.MTU = int(m.Header.Data.Mtu) - addr := make([]byte, v.Alen) - for i := 0; i < int(v.Alen); i++ { - addr[i] = byte(m.Data[int(v.Nlen)+i]) + addr := make([]byte, sa.Alen) + for i := 0; i < int(sa.Alen); i++ { + addr[i] = byte(m.Data[int(sa.Nlen)+i]) } - ifi.HardwareAddr = addr[:v.Alen] - ift = append(ift, ifi) + ifi.HardwareAddr = addr[:sa.Alen] } } - return ift, nil + return ifi, nil } func linkFlags(rawFlags int32) Flags { @@ -95,68 +96,87 @@ func linkFlags(rawFlags int32) Flags { return f } -// If the ifindex is zero, interfaceAddrTable returns addresses -// for all network interfaces. Otherwise it returns addresses -// for a specific interface. -func interfaceAddrTable(ifindex int) ([]Addr, error) { - tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) +// If the ifi is nil, interfaceAddrTable returns addresses for all +// network interfaces. Otherwise it returns addresses for a specific +// interface. +func interfaceAddrTable(ifi *Interface) ([]Addr, error) { + index := 0 + if ifi != nil { + index = ifi.Index + } + tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, index) if err != nil { return nil, os.NewSyscallError("route rib", err) } - msgs, err := syscall.ParseRoutingMessage(tab) if err != nil { return nil, os.NewSyscallError("route message", err) } - + var ift []Interface + if index == 0 { + ift, err = parseInterfaceTable(index, msgs) + if err != nil { + return nil, err + } + } var ifat []Addr for _, m := range msgs { - switch v := m.(type) { + switch m := m.(type) { case *syscall.InterfaceAddrMessage: - if ifindex == 0 || ifindex == int(v.Header.Index) { - ifa, err := newAddr(v) + if index == 0 || index == int(m.Header.Index) { + if index == 0 { + var err error + ifi, err = interfaceByIndex(ift, int(m.Header.Index)) + if err != nil { + return nil, err + } + } + ifa, err := newAddr(ifi, m) if err != nil { return nil, err } - ifat = append(ifat, ifa) + if ifa != nil { + ifat = append(ifat, ifa) + } } } } return ifat, nil } -func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error) { +func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (Addr, error) { sas, err := syscall.ParseRoutingSockaddr(m) if err != nil { return nil, os.NewSyscallError("route sockaddr", err) } - ifa := &IPNet{} - for i, s := range sas { - switch v := s.(type) { + for i, sa := range sas { + switch sa := sa.(type) { case *syscall.SockaddrInet4: switch i { case 0: - ifa.Mask = IPv4Mask(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3]) + ifa.Mask = IPv4Mask(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]) case 1: - ifa.IP = IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3]) + ifa.IP = IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]) } case *syscall.SockaddrInet6: switch i { case 0: ifa.Mask = make(IPMask, IPv6len) - copy(ifa.Mask, v.Addr[:]) + copy(ifa.Mask, sa.Addr[:]) case 1: ifa.IP = make(IP, IPv6len) - copy(ifa.IP, v.Addr[:]) + copy(ifa.IP, sa.Addr[:]) // NOTE: KAME based IPv6 protcol stack usually embeds // the interface index in the interface-local or link- // local address as the kernel-internal form. if ifa.IP.IsLinkLocalUnicast() { - // remove embedded scope zone ID + ifa.Zone = ifi.Name ifa.IP[2], ifa.IP[3] = 0, 0 } } + default: // Sockaddrs contain syscall.SockaddrDatalink on NetBSD + return nil, nil } } return ifa, nil diff --git a/src/pkg/net/interface_bsd_test.go b/src/pkg/net/interface_bsd_test.go new file mode 100644 index 000000000..aa1141903 --- /dev/null +++ b/src/pkg/net/interface_bsd_test.go @@ -0,0 +1,52 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd netbsd openbsd + +package net + +import ( + "fmt" + "os/exec" +) + +func (ti *testInterface) setBroadcast(suffix int) error { + ti.name = fmt.Sprintf("vlan%d", suffix) + xname, err := exec.LookPath("ifconfig") + if err != nil { + return err + } + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "create"}, + }) + ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "destroy"}, + }) + return nil +} + +func (ti *testInterface) setPointToPoint(suffix int, local, remote string) error { + ti.name = fmt.Sprintf("gif%d", suffix) + ti.local = local + ti.remote = remote + xname, err := exec.LookPath("ifconfig") + if err != nil { + return err + } + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "create"}, + }) + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "inet", ti.local, ti.remote}, + }) + ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "destroy"}, + }) + return nil +} diff --git a/src/pkg/net/interface_darwin.go b/src/pkg/net/interface_darwin.go index 0b5fb5fb9..83e483ba2 100644 --- a/src/pkg/net/interface_darwin.go +++ b/src/pkg/net/interface_darwin.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Network interface identification for Darwin - package net import ( @@ -11,26 +9,23 @@ import ( "syscall" ) -// If the ifindex is zero, interfaceMulticastAddrTable returns -// addresses for all network interfaces. Otherwise it returns -// addresses for a specific interface. -func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { - tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifindex) +// interfaceMulticastAddrTable returns addresses for a specific +// interface. +func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { + tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifi.Index) if err != nil { return nil, os.NewSyscallError("route rib", err) } - msgs, err := syscall.ParseRoutingMessage(tab) if err != nil { return nil, os.NewSyscallError("route message", err) } - var ifmat []Addr for _, m := range msgs { - switch v := m.(type) { + switch m := m.(type) { case *syscall.InterfaceMulticastAddrMessage: - if ifindex == 0 || ifindex == int(v.Header.Index) { - ifma, err := newMulticastAddr(v) + if ifi.Index == int(m.Header.Index) { + ifma, err := newMulticastAddr(ifi, m) if err != nil { return nil, err } @@ -41,27 +36,25 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { return ifmat, nil } -func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) { +func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) { sas, err := syscall.ParseRoutingSockaddr(m) if err != nil { return nil, os.NewSyscallError("route sockaddr", err) } - var ifmat []Addr - for _, s := range sas { - switch v := s.(type) { + for _, sa := range sas { + switch sa := sa.(type) { case *syscall.SockaddrInet4: - ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])} + ifma := &IPAddr{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])} ifmat = append(ifmat, ifma.toAddr()) case *syscall.SockaddrInet6: ifma := &IPAddr{IP: make(IP, IPv6len)} - copy(ifma.IP, v.Addr[:]) + copy(ifma.IP, sa.Addr[:]) // NOTE: KAME based IPv6 protcol stack usually embeds // the interface index in the interface-local or link- // local address as the kernel-internal form. - if ifma.IP.IsInterfaceLocalMulticast() || - ifma.IP.IsLinkLocalMulticast() { - // remove embedded scope zone ID + if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() { + ifma.Zone = ifi.Name ifma.IP[2], ifma.IP[3] = 0, 0 } ifmat = append(ifmat, ifma.toAddr()) diff --git a/src/pkg/net/interface_freebsd.go b/src/pkg/net/interface_freebsd.go index 3cba28fc6..1bf5ae72b 100644 --- a/src/pkg/net/interface_freebsd.go +++ b/src/pkg/net/interface_freebsd.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Network interface identification for FreeBSD - package net import ( @@ -11,26 +9,23 @@ import ( "syscall" ) -// If the ifindex is zero, interfaceMulticastAddrTable returns -// addresses for all network interfaces. Otherwise it returns -// addresses for a specific interface. -func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { - tab, err := syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifindex) +// interfaceMulticastAddrTable returns addresses for a specific +// interface. +func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { + tab, err := syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifi.Index) if err != nil { return nil, os.NewSyscallError("route rib", err) } - msgs, err := syscall.ParseRoutingMessage(tab) if err != nil { return nil, os.NewSyscallError("route message", err) } - var ifmat []Addr for _, m := range msgs { - switch v := m.(type) { + switch m := m.(type) { case *syscall.InterfaceMulticastAddrMessage: - if ifindex == 0 || ifindex == int(v.Header.Index) { - ifma, err := newMulticastAddr(v) + if ifi.Index == int(m.Header.Index) { + ifma, err := newMulticastAddr(ifi, m) if err != nil { return nil, err } @@ -41,27 +36,25 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { return ifmat, nil } -func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) { +func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) { sas, err := syscall.ParseRoutingSockaddr(m) if err != nil { return nil, os.NewSyscallError("route sockaddr", err) } - var ifmat []Addr - for _, s := range sas { - switch v := s.(type) { + for _, sa := range sas { + switch sa := sa.(type) { case *syscall.SockaddrInet4: - ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])} + ifma := &IPAddr{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])} ifmat = append(ifmat, ifma.toAddr()) case *syscall.SockaddrInet6: ifma := &IPAddr{IP: make(IP, IPv6len)} - copy(ifma.IP, v.Addr[:]) + copy(ifma.IP, sa.Addr[:]) // NOTE: KAME based IPv6 protcol stack usually embeds // the interface index in the interface-local or link- // local address as the kernel-internal form. - if ifma.IP.IsInterfaceLocalMulticast() || - ifma.IP.IsLinkLocalMulticast() { - // remove embedded scope zone ID + if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() { + ifma.Zone = ifi.Name ifma.IP[2], ifma.IP[3] = 0, 0 } ifmat = append(ifmat, ifma.toAddr()) diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go index 825b20227..e66daef06 100644 --- a/src/pkg/net/interface_linux.go +++ b/src/pkg/net/interface_linux.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Network interface identification for Linux - package net import ( @@ -20,17 +18,16 @@ func interfaceTable(ifindex int) ([]Interface, error) { if err != nil { return nil, os.NewSyscallError("netlink rib", err) } - msgs, err := syscall.ParseNetlinkMessage(tab) if err != nil { return nil, os.NewSyscallError("netlink message", err) } - var ift []Interface +loop: for _, m := range msgs { switch m.Header.Type { case syscall.NLMSG_DONE: - goto done + break loop case syscall.RTM_NEWLINK: ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) if ifindex == 0 || ifindex == int(ifim.Index) { @@ -38,17 +35,18 @@ func interfaceTable(ifindex int) ([]Interface, error) { if err != nil { return nil, os.NewSyscallError("netlink routeattr", err) } - ifi := newLink(ifim, attrs) - ift = append(ift, ifi) + ift = append(ift, *newLink(ifim, attrs)) + if ifindex == int(ifim.Index) { + break loop + } } } } -done: return ift, nil } -func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) Interface { - ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)} +func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) *Interface { + ifi := &Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)} for _, a := range attrs { switch a.Attr.Type { case syscall.IFLA_ADDRESS: @@ -64,7 +62,7 @@ func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) Interfac case syscall.IFLA_IFNAME: ifi.Name = string(a.Value[:len(a.Value)-1]) case syscall.IFLA_MTU: - ifi.MTU = int(uint32(a.Value[3])<<24 | uint32(a.Value[2])<<16 | uint32(a.Value[1])<<8 | uint32(a.Value[0])) + ifi.MTU = int(*(*uint32)(unsafe.Pointer(&a.Value[:4][0]))) } } return ifi @@ -90,81 +88,87 @@ func linkFlags(rawFlags uint32) Flags { return f } -// If the ifindex is zero, interfaceAddrTable returns addresses -// for all network interfaces. Otherwise it returns addresses -// for a specific interface. -func interfaceAddrTable(ifindex int) ([]Addr, error) { +// If the ifi is nil, interfaceAddrTable returns addresses for all +// network interfaces. Otherwise it returns addresses for a specific +// interface. +func interfaceAddrTable(ifi *Interface) ([]Addr, error) { tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) if err != nil { return nil, os.NewSyscallError("netlink rib", err) } - msgs, err := syscall.ParseNetlinkMessage(tab) if err != nil { return nil, os.NewSyscallError("netlink message", err) } - - ifat, err := addrTable(msgs, ifindex) + var ift []Interface + if ifi == nil { + var err error + ift, err = interfaceTable(0) + if err != nil { + return nil, err + } + } + ifat, err := addrTable(ift, ifi, msgs) if err != nil { return nil, err } return ifat, nil } -func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) { +func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) { var ifat []Addr +loop: for _, m := range msgs { switch m.Header.Type { case syscall.NLMSG_DONE: - goto done + break loop case syscall.RTM_NEWADDR: ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) - if ifindex == 0 || ifindex == int(ifam.Index) { + if len(ift) != 0 || ifi.Index == int(ifam.Index) { + if len(ift) != 0 { + var err error + ifi, err = interfaceByIndex(ift, int(ifam.Index)) + if err != nil { + return nil, err + } + } attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { return nil, os.NewSyscallError("netlink routeattr", err) } - ifat = append(ifat, newAddr(attrs, int(ifam.Family), int(ifam.Prefixlen))) + ifa := newAddr(ifi, ifam, attrs) + if ifa != nil { + ifat = append(ifat, ifa) + } } } } -done: return ifat, nil } -func newAddr(attrs []syscall.NetlinkRouteAttr, family, pfxlen int) Addr { - ifa := &IPNet{} +func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) Addr { for _, a := range attrs { - switch a.Attr.Type { - case syscall.IFA_ADDRESS: - switch family { + if ifi.Flags&FlagPointToPoint != 0 && a.Attr.Type == syscall.IFA_LOCAL || + ifi.Flags&FlagPointToPoint == 0 && a.Attr.Type == syscall.IFA_ADDRESS { + switch ifam.Family { case syscall.AF_INET: - ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]) - ifa.Mask = CIDRMask(pfxlen, 8*IPv4len) + return &IPNet{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv4len)} case syscall.AF_INET6: - ifa.IP = make(IP, IPv6len) + ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)} copy(ifa.IP, a.Value[:]) - ifa.Mask = CIDRMask(pfxlen, 8*IPv6len) + if ifam.Scope == syscall.RT_SCOPE_HOST || ifam.Scope == syscall.RT_SCOPE_LINK { + ifa.Zone = ifi.Name + } + return ifa } } } - return ifa + return nil } -// If the ifindex is zero, interfaceMulticastAddrTable returns -// addresses for all network interfaces. Otherwise it returns -// addresses for a specific interface. -func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { - var ( - err error - ifi *Interface - ) - if ifindex > 0 { - ifi, err = InterfaceByIndex(ifindex) - if err != nil { - return nil, err - } - } +// interfaceMulticastAddrTable returns addresses for a specific +// interface. +func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi) ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi) return append(ifmat4, ifmat6...), nil @@ -176,7 +180,6 @@ func parseProcNetIGMP(path string, ifi *Interface) []Addr { return nil } defer fd.close() - var ( ifmat []Addr name string @@ -193,10 +196,14 @@ func parseProcNetIGMP(path string, ifi *Interface) []Addr { name = f[1] case len(f[0]) == 8: if ifi == nil || name == ifi.Name { + // The Linux kernel puts the IP + // address in /proc/net/igmp in native + // endianness. for i := 0; i+1 < len(f[0]); i += 2 { b[i/2], _ = xtoi2(f[0][i:i+2], 0) } - ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])} + i := *(*uint32)(unsafe.Pointer(&b[:4][0])) + ifma := IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))} ifmat = append(ifmat, ifma.toAddr()) } } @@ -210,7 +217,6 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr { return nil } defer fd.close() - var ifmat []Addr b := make([]byte, IPv6len) for l, ok := fd.readLine(); ok; l, ok = fd.readLine() { @@ -223,6 +229,9 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr { b[i/2], _ = xtoi2(f[2][i:i+2], 0) } ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}} + if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() { + ifma.Zone = ifi.Name + } ifmat = append(ifmat, ifma.toAddr()) } } diff --git a/src/pkg/net/interface_linux_test.go b/src/pkg/net/interface_linux_test.go index f14d1fe06..085d3de9d 100644 --- a/src/pkg/net/interface_linux_test.go +++ b/src/pkg/net/interface_linux_test.go @@ -4,7 +4,55 @@ package net -import "testing" +import ( + "fmt" + "os/exec" + "testing" +) + +func (ti *testInterface) setBroadcast(suffix int) error { + ti.name = fmt.Sprintf("gotest%d", suffix) + xname, err := exec.LookPath("ip") + if err != nil { + return err + } + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ip", "link", "add", ti.name, "type", "dummy"}, + }) + ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ip", "link", "delete", ti.name, "type", "dummy"}, + }) + return nil +} + +func (ti *testInterface) setPointToPoint(suffix int, local, remote string) error { + ti.name = fmt.Sprintf("gotest%d", suffix) + ti.local = local + ti.remote = remote + xname, err := exec.LookPath("ip") + if err != nil { + return err + } + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ip", "tunnel", "add", ti.name, "mode", "gre", "local", local, "remote", remote}, + }) + ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ip", "tunnel", "del", ti.name, "mode", "gre", "local", local, "remote", remote}, + }) + xname, err = exec.LookPath("ifconfig") + if err != nil { + return err + } + ti.setupCmds = append(ti.setupCmds, &exec.Cmd{ + Path: xname, + Args: []string{"ifconfig", ti.name, "inet", local, "dstaddr", remote}, + }) + return nil +} const ( numOfTestIPv4MCAddrs = 14 diff --git a/src/pkg/net/interface_netbsd.go b/src/pkg/net/interface_netbsd.go index 4150e9ad5..c9ce5a7ac 100644 --- a/src/pkg/net/interface_netbsd.go +++ b/src/pkg/net/interface_netbsd.go @@ -2,13 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Network interface identification for NetBSD - package net -// If the ifindex is zero, interfaceMulticastAddrTable returns -// addresses for all network interfaces. Otherwise it returns -// addresses for a specific interface. -func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { +// interfaceMulticastAddrTable returns addresses for a specific +// interface. +func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { + // TODO(mikio): Implement this like other platforms. return nil, nil } diff --git a/src/pkg/net/interface_openbsd.go b/src/pkg/net/interface_openbsd.go index d8adb4676..c9ce5a7ac 100644 --- a/src/pkg/net/interface_openbsd.go +++ b/src/pkg/net/interface_openbsd.go @@ -2,13 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Network interface identification for OpenBSD - package net -// If the ifindex is zero, interfaceMulticastAddrTable returns -// addresses for all network interfaces. Otherwise it returns -// addresses for a specific interface. -func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { +// interfaceMulticastAddrTable returns addresses for a specific +// interface. +func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { + // TODO(mikio): Implement this like other platforms. return nil, nil } diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go index d4d7ce9c7..a4eb731da 100644 --- a/src/pkg/net/interface_stub.go +++ b/src/pkg/net/interface_stub.go @@ -4,8 +4,6 @@ // +build plan9 -// Network interface identification - package net // If the ifindex is zero, interfaceTable returns mappings of all @@ -15,16 +13,15 @@ func interfaceTable(ifindex int) ([]Interface, error) { return nil, nil } -// If the ifindex is zero, interfaceAddrTable returns addresses -// for all network interfaces. Otherwise it returns addresses -// for a specific interface. -func interfaceAddrTable(ifindex int) ([]Addr, error) { +// If the ifi is nil, interfaceAddrTable returns addresses for all +// network interfaces. Otherwise it returns addresses for a specific +// interface. +func interfaceAddrTable(ifi *Interface) ([]Addr, error) { return nil, nil } -// If the ifindex is zero, interfaceMulticastAddrTable returns -// addresses for all network interfaces. Otherwise it returns -// addresses for a specific interface. -func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { +// interfaceMulticastAddrTable returns addresses for a specific +// interface. +func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { return nil, nil } diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go index 0a33bfdb5..7fb342818 100644 --- a/src/pkg/net/interface_test.go +++ b/src/pkg/net/interface_test.go @@ -5,18 +5,24 @@ package net import ( - "bytes" + "reflect" "testing" ) -func sameInterface(i, j *Interface) bool { - if i == nil || j == nil { - return false +// loopbackInterface returns an available logical network interface +// for loopback tests. It returns nil if no suitable interface is +// found. +func loopbackInterface() *Interface { + ift, err := Interfaces() + if err != nil { + return nil } - if i.Index == j.Index && i.Name == j.Name && bytes.Equal(i.HardwareAddr, j.HardwareAddr) { - return true + for _, ifi := range ift { + if ifi.Flags&FlagLoopback != 0 && ifi.Flags&FlagUp != 0 { + return &ifi + } } - return false + return nil } func TestInterfaces(t *testing.T) { @@ -24,24 +30,24 @@ func TestInterfaces(t *testing.T) { if err != nil { t.Fatalf("Interfaces failed: %v", err) } - t.Logf("table: len/cap = %v/%v\n", len(ift), cap(ift)) + t.Logf("table: len/cap = %v/%v", len(ift), cap(ift)) for _, ifi := range ift { ifxi, err := InterfaceByIndex(ifi.Index) if err != nil { - t.Fatalf("InterfaceByIndex(%q) failed: %v", ifi.Index, err) + t.Fatalf("InterfaceByIndex(%v) failed: %v", ifi.Index, err) } - if !sameInterface(ifxi, &ifi) { - t.Fatalf("InterfaceByIndex(%q) = %v, want %v", ifi.Index, *ifxi, ifi) + if !reflect.DeepEqual(ifxi, &ifi) { + t.Fatalf("InterfaceByIndex(%v) = %v, want %v", ifi.Index, ifxi, ifi) } ifxn, err := InterfaceByName(ifi.Name) if err != nil { t.Fatalf("InterfaceByName(%q) failed: %v", ifi.Name, err) } - if !sameInterface(ifxn, &ifi) { - t.Fatalf("InterfaceByName(%q) = %v, want %v", ifi.Name, *ifxn, ifi) + if !reflect.DeepEqual(ifxn, &ifi) { + t.Fatalf("InterfaceByName(%q) = %v, want %v", ifi.Name, ifxn, ifi) } - t.Logf("%q: flags %q, ifindex %v, mtu %v\n", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU) + t.Logf("%q: flags %q, ifindex %v, mtu %v", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU) t.Logf("\thardware address %q", ifi.HardwareAddr.String()) testInterfaceAddrs(t, &ifi) testInterfaceMulticastAddrs(t, &ifi) @@ -53,7 +59,7 @@ func TestInterfaceAddrs(t *testing.T) { if err != nil { t.Fatalf("InterfaceAddrs failed: %v", err) } - t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat)) + t.Logf("table: len/cap = %v/%v", len(ifat), cap(ifat)) testAddrs(t, ifat) } @@ -75,9 +81,13 @@ func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) { func testAddrs(t *testing.T, ifat []Addr) { for _, ifa := range ifat { - switch ifa.(type) { + switch v := ifa.(type) { case *IPAddr, *IPNet: - t.Logf("\tinterface address %q\n", ifa.String()) + if v == nil { + t.Errorf("\tunexpected value: %v", ifa) + } else { + t.Logf("\tinterface address %q", ifa.String()) + } default: t.Errorf("\tunexpected type: %T", ifa) } @@ -86,11 +96,79 @@ func testAddrs(t *testing.T, ifat []Addr) { func testMulticastAddrs(t *testing.T, ifmat []Addr) { for _, ifma := range ifmat { - switch ifma.(type) { + switch v := ifma.(type) { case *IPAddr: - t.Logf("\tjoined group address %q\n", ifma.String()) + if v == nil { + t.Errorf("\tunexpected value: %v", ifma) + } else { + t.Logf("\tjoined group address %q", ifma.String()) + } default: t.Errorf("\tunexpected type: %T", ifma) } } } + +func BenchmarkInterfaces(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := Interfaces(); err != nil { + b.Fatalf("Interfaces failed: %v", err) + } + } +} + +func BenchmarkInterfaceByIndex(b *testing.B) { + ifi := loopbackInterface() + if ifi == nil { + b.Skip("loopback interface not found") + } + for i := 0; i < b.N; i++ { + if _, err := InterfaceByIndex(ifi.Index); err != nil { + b.Fatalf("InterfaceByIndex failed: %v", err) + } + } +} + +func BenchmarkInterfaceByName(b *testing.B) { + ifi := loopbackInterface() + if ifi == nil { + b.Skip("loopback interface not found") + } + for i := 0; i < b.N; i++ { + if _, err := InterfaceByName(ifi.Name); err != nil { + b.Fatalf("InterfaceByName failed: %v", err) + } + } +} + +func BenchmarkInterfaceAddrs(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := InterfaceAddrs(); err != nil { + b.Fatalf("InterfaceAddrs failed: %v", err) + } + } +} + +func BenchmarkInterfacesAndAddrs(b *testing.B) { + ifi := loopbackInterface() + if ifi == nil { + b.Skip("loopback interface not found") + } + for i := 0; i < b.N; i++ { + if _, err := ifi.Addrs(); err != nil { + b.Fatalf("Interface.Addrs failed: %v", err) + } + } +} + +func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) { + ifi := loopbackInterface() + if ifi == nil { + b.Skip("loopback interface not found") + } + for i := 0; i < b.N; i++ { + if _, err := ifi.MulticastAddrs(); err != nil { + b.Fatalf("Interface.MulticastAddrs failed: %v", err) + } + } +} diff --git a/src/pkg/net/interface_unix_test.go b/src/pkg/net/interface_unix_test.go new file mode 100644 index 000000000..6dbd6e6e7 --- /dev/null +++ b/src/pkg/net/interface_unix_test.go @@ -0,0 +1,145 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package net + +import ( + "os" + "os/exec" + "runtime" + "testing" + "time" +) + +type testInterface struct { + name string + local string + remote string + setupCmds []*exec.Cmd + teardownCmds []*exec.Cmd +} + +func (ti *testInterface) setup() error { + for _, cmd := range ti.setupCmds { + if err := cmd.Run(); err != nil { + return err + } + } + return nil +} + +func (ti *testInterface) teardown() error { + for _, cmd := range ti.teardownCmds { + if err := cmd.Run(); err != nil { + return err + } + } + return nil +} + +func TestPointToPointInterface(t *testing.T) { + switch runtime.GOOS { + case "darwin": + t.Skipf("skipping read test on %q", runtime.GOOS) + } + if os.Getuid() != 0 { + t.Skip("skipping test; must be root") + } + + local, remote := "169.254.0.1", "169.254.0.254" + ip := ParseIP(remote) + for i := 0; i < 3; i++ { + ti := &testInterface{} + if err := ti.setPointToPoint(5963+i, local, remote); err != nil { + t.Skipf("test requries external command: %v", err) + } + if err := ti.setup(); err != nil { + t.Fatalf("testInterface.setup failed: %v", err) + } else { + time.Sleep(3 * time.Millisecond) + } + ift, err := Interfaces() + if err != nil { + ti.teardown() + t.Fatalf("Interfaces failed: %v", err) + } + for _, ifi := range ift { + if ti.name == ifi.Name { + ifat, err := ifi.Addrs() + if err != nil { + ti.teardown() + t.Fatalf("Interface.Addrs failed: %v", err) + } + for _, ifa := range ifat { + if ip.Equal(ifa.(*IPNet).IP) { + ti.teardown() + t.Fatalf("got %v; want %v", ip, local) + } + } + } + } + if err := ti.teardown(); err != nil { + t.Fatalf("testInterface.teardown failed: %v", err) + } else { + time.Sleep(3 * time.Millisecond) + } + } +} + +func TestInterfaceArrivalAndDeparture(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping test; must be root") + } + + for i := 0; i < 3; i++ { + ift1, err := Interfaces() + if err != nil { + t.Fatalf("Interfaces failed: %v", err) + } + ti := &testInterface{} + if err := ti.setBroadcast(5682 + i); err != nil { + t.Skipf("test requires external command: %v", err) + } + if err := ti.setup(); err != nil { + t.Fatalf("testInterface.setup failed: %v", err) + } else { + time.Sleep(3 * time.Millisecond) + } + ift2, err := Interfaces() + if err != nil { + ti.teardown() + t.Fatalf("Interfaces failed: %v", err) + } + if len(ift2) <= len(ift1) { + for _, ifi := range ift1 { + t.Logf("before: %v", ifi) + } + for _, ifi := range ift2 { + t.Logf("after: %v", ifi) + } + ti.teardown() + t.Fatalf("got %v; want gt %v", len(ift2), len(ift1)) + } + if err := ti.teardown(); err != nil { + t.Fatalf("testInterface.teardown failed: %v", err) + } else { + time.Sleep(3 * time.Millisecond) + } + ift3, err := Interfaces() + if err != nil { + t.Fatalf("Interfaces failed: %v", err) + } + if len(ift3) >= len(ift2) { + for _, ifi := range ift2 { + t.Logf("before: %v", ifi) + } + for _, ifi := range ift3 { + t.Logf("after: %v", ifi) + } + t.Fatalf("got %v; want lt %v", len(ift3), len(ift2)) + } + } +} diff --git a/src/pkg/net/interface_windows.go b/src/pkg/net/interface_windows.go index 4368b3306..0759dc255 100644 --- a/src/pkg/net/interface_windows.go +++ b/src/pkg/net/interface_windows.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Network interface identification for Windows - package net import ( @@ -25,6 +23,9 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) { b := make([]byte, 1000) l := uint32(len(b)) a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + // TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that + // contains IPv4 address list only. We should use another API + // for fetching IPv6 stuff from the kernel. err := syscall.GetAdaptersInfo(a, &l) if err == syscall.ERROR_BUFFER_OVERFLOW { b = make([]byte, l) @@ -38,7 +39,7 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) { } func getInterfaceList() ([]syscall.InterfaceInfo, error) { - s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) + s, err := sysSocket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if err != nil { return nil, os.NewSyscallError("Socket", err) } @@ -126,10 +127,10 @@ func interfaceTable(ifindex int) ([]Interface, error) { return ift, nil } -// If the ifindex is zero, interfaceAddrTable returns addresses -// for all network interfaces. Otherwise it returns addresses -// for a specific interface. -func interfaceAddrTable(ifindex int) ([]Addr, error) { +// If the ifi is nil, interfaceAddrTable returns addresses for all +// network interfaces. Otherwise it returns addresses for a specific +// interface. +func interfaceAddrTable(ifi *Interface) ([]Addr, error) { ai, err := getAdapterList() if err != nil { return nil, err @@ -138,11 +139,10 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) { var ifat []Addr for ; ai != nil; ai = ai.Next { index := ai.Index - if ifindex == 0 || ifindex == int(index) { + if ifi == nil || ifi.Index == int(index) { ipl := &ai.IpAddressList for ; ipl != nil; ipl = ipl.Next { - ifa := IPAddr{} - ifa.IP = parseIPv4(bytePtrToString(&ipl.IpAddress.String[0])) + ifa := IPAddr{IP: parseIPv4(bytePtrToString(&ipl.IpAddress.String[0]))} ifat = append(ifat, ifa.toAddr()) } } @@ -150,9 +150,9 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) { return ifat, nil } -// If the ifindex is zero, interfaceMulticastAddrTable returns -// addresses for all network interfaces. Otherwise it returns -// addresses for a specific interface. -func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { +// interfaceMulticastAddrTable returns addresses for a specific +// interface. +func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { + // TODO(mikio): Implement this like other platforms. return nil, nil } diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go index 979d7acd5..d588e3a42 100644 --- a/src/pkg/net/ip.go +++ b/src/pkg/net/ip.go @@ -7,7 +7,7 @@ // IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes. // An IPv4 address can be converted to an IPv6 address by // adding a canonical prefix (10 zeros, 2 0xFFs). -// This library accepts either size of byte array but always +// This library accepts either size of byte slice but always // returns 16-byte addresses. package net @@ -18,14 +18,14 @@ const ( IPv6len = 16 ) -// An IP is a single IP address, an array of bytes. +// An IP is a single IP address, a slice of bytes. // Functions in this package accept either 4-byte (IPv4) -// or 16-byte (IPv6) arrays as input. +// or 16-byte (IPv6) slices as input. // // Note that in this documentation, referring to an // IP address as an IPv4 address or an IPv6 address // is a semantic property of the address, not just the -// length of the byte array: a 16-byte array can still +// length of the byte slice: a 16-byte slice can still // be an IPv4 address. type IP []byte @@ -36,6 +36,7 @@ type IPMask []byte type IPNet struct { IP IP // network number Mask IPMask // network mask + Zone string // IPv6 scoped addressing zone } // IPv4 returns the IP address (in 16-byte form) of the @@ -645,5 +646,5 @@ func ParseCIDR(s string) (IP, *IPNet, error) { return nil, nil, &ParseError{"CIDR address", s} } m := CIDRMask(n, 8*iplen) - return ip, &IPNet{ip.Mask(m), m}, nil + return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil } diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go index df647ef73..f8b7f067f 100644 --- a/src/pkg/net/ip_test.go +++ b/src/pkg/net/ip_test.go @@ -114,23 +114,23 @@ var parsecidrtests = []struct { net *IPNet err error }{ - {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255)}, nil}, - {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil}, - {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil}, - {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255)}, nil}, - {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil}, - {"::1/128", ParseIP("::1"), &IPNet{ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil}, - {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil}, - {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil}, - {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil}, - {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil}, - {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::"))}, nil}, - {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::"))}, nil}, - {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::"))}, nil}, - {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil}, - {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil}, - {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, - {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, + {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 255)}, nil}, + {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IP: IPv4(0, 0, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil}, + {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil}, + {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IP: IPv4(135, 104, 0, 1), Mask: IPv4Mask(255, 255, 255, 255)}, nil}, + {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil}, + {"::1/128", ParseIP("::1"), &IPNet{IP: ParseIP("::1"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil}, + {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil}, + {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil}, + {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil}, + {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil}, + {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:8000::"))}, nil}, + {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff::"))}, nil}, + {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{IP: ParseIP("abcd:2344::"), Mask: IPMask(ParseIP("ffff:fffe::"))}, nil}, + {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil}, + {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil}, + {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, + {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, {"192.168.1.1/255.255.255.0", nil, nil, &ParseError{"CIDR address", "192.168.1.1/255.255.255.0"}}, {"192.168.1.1/35", nil, nil, &ParseError{"CIDR address", "192.168.1.1/35"}}, {"2001:db8::1/-1", nil, nil, &ParseError{"CIDR address", "2001:db8::1/-1"}}, @@ -154,14 +154,14 @@ var ipnetcontainstests = []struct { net *IPNet ok bool }{ - {IPv4(172, 16, 1, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(12, 32)}, true}, - {IPv4(172, 24, 0, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(13, 32)}, false}, - {IPv4(192, 168, 0, 3), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 0, 255, 252)}, true}, - {IPv4(192, 168, 0, 4), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 255, 0, 252)}, false}, - {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), CIDRMask(47, 128)}, true}, - {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:2::"), CIDRMask(47, 128)}, false}, - {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("ffff:0:ffff::"))}, true}, - {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("0:0:0:ffff::"))}, false}, + {IPv4(172, 16, 1, 1), &IPNet{IP: IPv4(172, 16, 0, 0), Mask: CIDRMask(12, 32)}, true}, + {IPv4(172, 24, 0, 1), &IPNet{IP: IPv4(172, 16, 0, 0), Mask: CIDRMask(13, 32)}, false}, + {IPv4(192, 168, 0, 3), &IPNet{IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(0, 0, 255, 252)}, true}, + {IPv4(192, 168, 0, 4), &IPNet{IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(0, 255, 0, 252)}, false}, + {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: CIDRMask(47, 128)}, true}, + {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:2::"), Mask: CIDRMask(47, 128)}, false}, + {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("ffff:0:ffff::"))}, true}, + {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("0:0:0:ffff::"))}, false}, } func TestIPNetContains(t *testing.T) { @@ -176,10 +176,10 @@ var ipnetstringtests = []struct { in *IPNet out string }{ - {&IPNet{IPv4(192, 168, 1, 0), CIDRMask(26, 32)}, "192.168.1.0/26"}, - {&IPNet{IPv4(192, 168, 1, 0), IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"}, - {&IPNet{ParseIP("2001:db8::"), CIDRMask(55, 128)}, "2001:db8::/55"}, - {&IPNet{ParseIP("2001:db8::"), IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"}, + {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: CIDRMask(26, 32)}, "192.168.1.0/26"}, + {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"}, + {&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"}, + {&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"}, } func TestIPNetString(t *testing.T) { @@ -233,27 +233,27 @@ var networknumberandmasktests = []struct { in IPNet out IPNet }{ - {IPNet{v4addr, v4mask}, IPNet{v4addr, v4mask}}, - {IPNet{v4addr, v4mappedv6mask}, IPNet{v4addr, v4mask}}, - {IPNet{v4mappedv6addr, v4mappedv6mask}, IPNet{v4addr, v4mask}}, - {IPNet{v4mappedv6addr, v6mask}, IPNet{v4addr, v4maskzero}}, - {IPNet{v4addr, v6mask}, IPNet{v4addr, v4maskzero}}, - {IPNet{v6addr, v6mask}, IPNet{v6addr, v6mask}}, - {IPNet{v6addr, v4mappedv6mask}, IPNet{v6addr, v4mappedv6mask}}, - {in: IPNet{v6addr, v4mask}}, - {in: IPNet{v4addr, badmask}}, - {in: IPNet{v4mappedv6addr, badmask}}, - {in: IPNet{v6addr, badmask}}, - {in: IPNet{badaddr, v4mask}}, - {in: IPNet{badaddr, v4mappedv6mask}}, - {in: IPNet{badaddr, v6mask}}, - {in: IPNet{badaddr, badmask}}, + {IPNet{IP: v4addr, Mask: v4mask}, IPNet{IP: v4addr, Mask: v4mask}}, + {IPNet{IP: v4addr, Mask: v4mappedv6mask}, IPNet{IP: v4addr, Mask: v4mask}}, + {IPNet{IP: v4mappedv6addr, Mask: v4mappedv6mask}, IPNet{IP: v4addr, Mask: v4mask}}, + {IPNet{IP: v4mappedv6addr, Mask: v6mask}, IPNet{IP: v4addr, Mask: v4maskzero}}, + {IPNet{IP: v4addr, Mask: v6mask}, IPNet{IP: v4addr, Mask: v4maskzero}}, + {IPNet{IP: v6addr, Mask: v6mask}, IPNet{IP: v6addr, Mask: v6mask}}, + {IPNet{IP: v6addr, Mask: v4mappedv6mask}, IPNet{IP: v6addr, Mask: v4mappedv6mask}}, + {in: IPNet{IP: v6addr, Mask: v4mask}}, + {in: IPNet{IP: v4addr, Mask: badmask}}, + {in: IPNet{IP: v4mappedv6addr, Mask: badmask}}, + {in: IPNet{IP: v6addr, Mask: badmask}}, + {in: IPNet{IP: badaddr, Mask: v4mask}}, + {in: IPNet{IP: badaddr, Mask: v4mappedv6mask}}, + {in: IPNet{IP: badaddr, Mask: v6mask}}, + {in: IPNet{IP: badaddr, Mask: badmask}}, } func TestNetworkNumberAndMask(t *testing.T) { for _, tt := range networknumberandmasktests { ip, m := networkNumberAndMask(&tt.in) - out := &IPNet{ip, m} + out := &IPNet{IP: ip, Mask: m} if !reflect.DeepEqual(&tt.out, out) { t.Errorf("networkNumberAndMask(%v) = %v; want %v", tt.in, out, &tt.out) } @@ -268,6 +268,29 @@ var splitjointests = []struct { {"www.google.com", "80", "www.google.com:80"}, {"127.0.0.1", "1234", "127.0.0.1:1234"}, {"::1", "80", "[::1]:80"}, + {"google.com", "https%foo", "google.com:https%foo"}, // Go 1.0 behavior + {"", "0", ":0"}, + {"127.0.0.1", "", "127.0.0.1:"}, // Go 1.0 behaviour + {"www.google.com", "", "www.google.com:"}, // Go 1.0 behaviour +} + +var splitfailuretests = []struct { + HostPort string + Err string +}{ + {"www.google.com", "missing port in address"}, + {"127.0.0.1", "missing port in address"}, + {"[::1]", "missing port in address"}, + {"::1", "too many colons in address"}, + + // Test cases that didn't fail in Go 1.0 + {"[foo:bar]", "missing port in address"}, + {"[foo:bar]baz", "missing port in address"}, + {"[foo]:[bar]:baz", "too many colons in address"}, + {"[foo]bar:baz", "missing port in address"}, + {"[foo]:[bar]baz", "unexpected '[' in address"}, + {"foo[bar]:baz", "unexpected '[' in address"}, + {"foo]bar:baz", "unexpected ']' in address"}, } func TestSplitHostPort(t *testing.T) { @@ -276,6 +299,16 @@ func TestSplitHostPort(t *testing.T) { t.Errorf("SplitHostPort(%q) = %q, %q, %v; want %q, %q, nil", tt.Join, host, port, err, tt.Host, tt.Port) } } + for _, tt := range splitfailuretests { + if _, _, err := SplitHostPort(tt.HostPort); err == nil { + t.Errorf("SplitHostPort(%q) should have failed", tt.HostPort) + } else { + e := err.(*AddrError) + if e.Err != tt.Err { + t.Errorf("SplitHostPort(%q) = _, _, %q; want %q", tt.HostPort, e.Err, tt.Err) + } + } + } } func TestJoinHostPort(t *testing.T) { diff --git a/src/pkg/net/ipraw_test.go b/src/pkg/net/ipraw_test.go index 613620272..65defc7ea 100644 --- a/src/pkg/net/ipraw_test.go +++ b/src/pkg/net/ipraw_test.go @@ -2,205 +2,340 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !plan9 + package net import ( "bytes" + "errors" "os" - "syscall" + "reflect" "testing" "time" ) -var icmpTests = []struct { +var resolveIPAddrTests = []struct { + net string + litAddr string + addr *IPAddr + err error +}{ + {"ip", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, + {"ip4", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, + {"ip4:icmp", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, + + {"ip", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, + {"ip6", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, + {"ip6:icmp", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, + + {"", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, // Go 1.0 behavior + {"", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, // Go 1.0 behavior + + {"l2tp", "127.0.0.1", nil, UnknownNetworkError("l2tp")}, + {"l2tp:gre", "127.0.0.1", nil, UnknownNetworkError("l2tp:gre")}, + {"tcp", "1.2.3.4:123", nil, UnknownNetworkError("tcp")}, +} + +func TestResolveIPAddr(t *testing.T) { + for _, tt := range resolveIPAddrTests { + addr, err := ResolveIPAddr(tt.net, tt.litAddr) + if err != tt.err { + t.Fatalf("ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + } + if !reflect.DeepEqual(addr, tt.addr) { + t.Fatalf("got %#v; expected %#v", addr, tt.addr) + } + } +} + +var icmpEchoTests = []struct { net string laddr string raddr string - ipv6 bool // test with underlying AF_INET6 socket }{ - {"ip4:icmp", "", "127.0.0.1", false}, - {"ip6:icmp", "", "::1", true}, + {"ip4:icmp", "0.0.0.0", "127.0.0.1"}, + {"ip6:ipv6-icmp", "::", "::1"}, } -func TestICMP(t *testing.T) { +func TestConnICMPEcho(t *testing.T) { if os.Getuid() != 0 { - t.Logf("test disabled; must be root") - return + t.Skip("skipping test; must be root") } - seqnum := 61455 - for _, tt := range icmpTests { - if tt.ipv6 && !supportsIPv6 { + for i, tt := range icmpEchoTests { + net, _, err := parseNetwork(tt.net) + if err != nil { + t.Fatalf("parseNetwork failed: %v", err) + } + if net == "ip6" && !supportsIPv6 { continue } - id := os.Getpid() & 0xffff - seqnum++ - echo := newICMPEchoRequest(tt.net, id, seqnum, 128, []byte("Go Go Gadget Ping!!!")) - exchangeICMPEcho(t, tt.net, tt.laddr, tt.raddr, echo) - } -} - -func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, echo []byte) { - c, err := ListenPacket(net, laddr) - if err != nil { - t.Errorf("ListenPacket(%q, %q) failed: %v", net, laddr, err) - return - } - c.SetDeadline(time.Now().Add(100 * time.Millisecond)) - defer c.Close() - - ra, err := ResolveIPAddr(net, raddr) - if err != nil { - t.Errorf("ResolveIPAddr(%q, %q) failed: %v", net, raddr, err) - return - } - waitForReady := make(chan bool) - go icmpEchoTransponder(t, net, raddr, waitForReady) - <-waitForReady - - _, err = c.WriteTo(echo, ra) - if err != nil { - t.Errorf("WriteTo failed: %v", err) - return - } + c, err := Dial(tt.net, tt.raddr) + if err != nil { + t.Fatalf("Dial failed: %v", err) + } + c.SetDeadline(time.Now().Add(100 * time.Millisecond)) + defer c.Close() - reply := make([]byte, 256) - for { - _, _, err := c.ReadFrom(reply) + typ := icmpv4EchoRequest + if net == "ip6" { + typ = icmpv6EchoRequest + } + xid, xseq := os.Getpid()&0xffff, i+1 + b, err := (&icmpMessage{ + Type: typ, Code: 0, + Body: &icmpEcho{ + ID: xid, Seq: xseq, + Data: bytes.Repeat([]byte("Go Go Gadget Ping!!!"), 3), + }, + }).Marshal() if err != nil { - t.Errorf("ReadFrom failed: %v", err) - return + t.Fatalf("icmpMessage.Marshal failed: %v", err) } - switch c.(*IPConn).fd.family { - case syscall.AF_INET: - if reply[0] != ICMP4_ECHO_REPLY { - continue + if _, err := c.Write(b); err != nil { + t.Fatalf("Conn.Write failed: %v", err) + } + var m *icmpMessage + for { + if _, err := c.Read(b); err != nil { + t.Fatalf("Conn.Read failed: %v", err) + } + if net == "ip4" { + b = ipv4Payload(b) } - case syscall.AF_INET6: - if reply[0] != ICMP6_ECHO_REPLY { + if m, err = parseICMPMessage(b); err != nil { + t.Fatalf("parseICMPMessage failed: %v", err) + } + switch m.Type { + case icmpv4EchoRequest, icmpv6EchoRequest: continue } + break } - xid, xseqnum := parseICMPEchoReply(echo) - rid, rseqnum := parseICMPEchoReply(reply) - if rid != xid || rseqnum != xseqnum { - t.Errorf("ID = %v, Seqnum = %v, want ID = %v, Seqnum = %v", rid, rseqnum, xid, xseqnum) - return + switch p := m.Body.(type) { + case *icmpEcho: + if p.ID != xid || p.Seq != xseq { + t.Fatalf("got id=%v, seqnum=%v; expected id=%v, seqnum=%v", p.ID, p.Seq, xid, xseq) + } + default: + t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, typ, 0) } - break } } -func icmpEchoTransponder(t *testing.T, net, raddr string, waitForReady chan bool) { - c, err := Dial(net, raddr) - if err != nil { - waitForReady <- true - t.Errorf("Dial(%q, %q) failed: %v", net, raddr, err) - return +func TestPacketConnICMPEcho(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping test; must be root") } - c.SetDeadline(time.Now().Add(100 * time.Millisecond)) - defer c.Close() - waitForReady <- true - echo := make([]byte, 256) - var nr int - for { - nr, err = c.Read(echo) + for i, tt := range icmpEchoTests { + net, _, err := parseNetwork(tt.net) if err != nil { - t.Errorf("Read failed: %v", err) - return + t.Fatalf("parseNetwork failed: %v", err) } - switch c.(*IPConn).fd.family { - case syscall.AF_INET: - if echo[0] != ICMP4_ECHO_REQUEST { - continue + if net == "ip6" && !supportsIPv6 { + continue + } + + c, err := ListenPacket(tt.net, tt.laddr) + if err != nil { + t.Fatalf("ListenPacket failed: %v", err) + } + c.SetDeadline(time.Now().Add(100 * time.Millisecond)) + defer c.Close() + + ra, err := ResolveIPAddr(tt.net, tt.raddr) + if err != nil { + t.Fatalf("ResolveIPAddr failed: %v", err) + } + typ := icmpv4EchoRequest + if net == "ip6" { + typ = icmpv6EchoRequest + } + xid, xseq := os.Getpid()&0xffff, i+1 + b, err := (&icmpMessage{ + Type: typ, Code: 0, + Body: &icmpEcho{ + ID: xid, Seq: xseq, + Data: bytes.Repeat([]byte("Go Go Gadget Ping!!!"), 3), + }, + }).Marshal() + if err != nil { + t.Fatalf("icmpMessage.Marshal failed: %v", err) + } + if _, err := c.WriteTo(b, ra); err != nil { + t.Fatalf("PacketConn.WriteTo failed: %v", err) + } + var m *icmpMessage + for { + if _, _, err := c.ReadFrom(b); err != nil { + t.Fatalf("PacketConn.ReadFrom failed: %v", err) } - case syscall.AF_INET6: - if echo[0] != ICMP6_ECHO_REQUEST { + // TODO: fix issue 3944 + //if net == "ip4" { + // b = ipv4Payload(b) + //} + if m, err = parseICMPMessage(b); err != nil { + t.Fatalf("parseICMPMessage failed: %v", err) + } + switch m.Type { + case icmpv4EchoRequest, icmpv6EchoRequest: continue } + break + } + switch p := m.Body.(type) { + case *icmpEcho: + if p.ID != xid || p.Seq != xseq { + t.Fatalf("got id=%v, seqnum=%v; expected id=%v, seqnum=%v", p.ID, p.Seq, xid, xseq) + } + default: + t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, typ, 0) } - break - } - - switch c.(*IPConn).fd.family { - case syscall.AF_INET: - echo[0] = ICMP4_ECHO_REPLY - case syscall.AF_INET6: - echo[0] = ICMP6_ECHO_REPLY } +} - _, err = c.Write(echo[:nr]) - if err != nil { - t.Errorf("Write failed: %v", err) - return +func ipv4Payload(b []byte) []byte { + if len(b) < 20 { + return b } + hdrlen := int(b[0]&0x0f) << 2 + return b[hdrlen:] } const ( - ICMP4_ECHO_REQUEST = 8 - ICMP4_ECHO_REPLY = 0 - ICMP6_ECHO_REQUEST = 128 - ICMP6_ECHO_REPLY = 129 + icmpv4EchoRequest = 8 + icmpv4EchoReply = 0 + icmpv6EchoRequest = 128 + icmpv6EchoReply = 129 ) -func newICMPEchoRequest(net string, id, seqnum, msglen int, filler []byte) []byte { - afnet, _, _ := parseDialNetwork(net) - switch afnet { - case "ip4": - return newICMPv4EchoRequest(id, seqnum, msglen, filler) - case "ip6": - return newICMPv6EchoRequest(id, seqnum, msglen, filler) - } - return nil +// icmpMessage represents an ICMP message. +type icmpMessage struct { + Type int // type + Code int // code + Checksum int // checksum + Body icmpMessageBody // body } -func newICMPv4EchoRequest(id, seqnum, msglen int, filler []byte) []byte { - b := newICMPInfoMessage(id, seqnum, msglen, filler) - b[0] = ICMP4_ECHO_REQUEST +// icmpMessageBody represents an ICMP message body. +type icmpMessageBody interface { + Len() int + Marshal() ([]byte, error) +} - // calculate ICMP checksum - cklen := len(b) +// Marshal returns the binary enconding of the ICMP echo request or +// reply message m. +func (m *icmpMessage) Marshal() ([]byte, error) { + b := []byte{byte(m.Type), byte(m.Code), 0, 0} + if m.Body != nil && m.Body.Len() != 0 { + mb, err := m.Body.Marshal() + if err != nil { + return nil, err + } + b = append(b, mb...) + } + switch m.Type { + case icmpv6EchoRequest, icmpv6EchoReply: + return b, nil + } + csumcv := len(b) - 1 // checksum coverage s := uint32(0) - for i := 0; i < cklen-1; i += 2 { + for i := 0; i < csumcv; i += 2 { s += uint32(b[i+1])<<8 | uint32(b[i]) } - if cklen&1 == 1 { - s += uint32(b[cklen-1]) + if csumcv&1 == 0 { + s += uint32(b[csumcv]) } - s = (s >> 16) + (s & 0xffff) - s = s + (s >> 16) - // place checksum back in header; using ^= avoids the - // assumption the checksum bytes are zero - b[2] ^= uint8(^s & 0xff) - b[3] ^= uint8(^s >> 8) + s = s>>16 + s&0xffff + s = s + s>>16 + // Place checksum back in header; using ^= avoids the + // assumption the checksum bytes are zero. + b[2] ^= byte(^s & 0xff) + b[3] ^= byte(^s >> 8) + return b, nil +} - return b +// parseICMPMessage parses b as an ICMP message. +func parseICMPMessage(b []byte) (*icmpMessage, error) { + msglen := len(b) + if msglen < 4 { + return nil, errors.New("message too short") + } + m := &icmpMessage{Type: int(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])} + if msglen > 4 { + var err error + switch m.Type { + case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply: + m.Body, err = parseICMPEcho(b[4:]) + if err != nil { + return nil, err + } + } + } + return m, nil +} + +// imcpEcho represenets an ICMP echo request or reply message body. +type icmpEcho struct { + ID int // identifier + Seq int // sequence number + Data []byte // data +} + +func (p *icmpEcho) Len() int { + if p == nil { + return 0 + } + return 4 + len(p.Data) } -func newICMPv6EchoRequest(id, seqnum, msglen int, filler []byte) []byte { - b := newICMPInfoMessage(id, seqnum, msglen, filler) - b[0] = ICMP6_ECHO_REQUEST - return b +// Marshal returns the binary enconding of the ICMP echo request or +// reply message body p. +func (p *icmpEcho) Marshal() ([]byte, error) { + b := make([]byte, 4+len(p.Data)) + b[0], b[1] = byte(p.ID>>8), byte(p.ID&0xff) + b[2], b[3] = byte(p.Seq>>8), byte(p.Seq&0xff) + copy(b[4:], p.Data) + return b, nil } -func newICMPInfoMessage(id, seqnum, msglen int, filler []byte) []byte { - b := make([]byte, msglen) - copy(b[8:], bytes.Repeat(filler, (msglen-8)/len(filler)+1)) - b[0] = 0 // type - b[1] = 0 // code - b[2] = 0 // checksum - b[3] = 0 // checksum - b[4] = uint8(id >> 8) // identifier - b[5] = uint8(id & 0xff) // identifier - b[6] = uint8(seqnum >> 8) // sequence number - b[7] = uint8(seqnum & 0xff) // sequence number - return b +// parseICMPEcho parses b as an ICMP echo request or reply message +// body. +func parseICMPEcho(b []byte) (*icmpEcho, error) { + bodylen := len(b) + p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])} + if bodylen > 4 { + p.Data = make([]byte, bodylen-4) + copy(p.Data, b[4:]) + } + return p, nil } -func parseICMPEchoReply(b []byte) (id, seqnum int) { - id = int(b[4])<<8 | int(b[5]) - seqnum = int(b[6])<<8 | int(b[7]) - return +var ipConnLocalNameTests = []struct { + net string + laddr *IPAddr +}{ + {"ip4:icmp", &IPAddr{IP: IPv4(127, 0, 0, 1)}}, + {"ip4:icmp", &IPAddr{}}, + {"ip4:icmp", nil}, +} + +func TestIPConnLocalName(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping test; must be root") + } + + for _, tt := range ipConnLocalNameTests { + c, err := ListenIP(tt.net, tt.laddr) + if err != nil { + t.Fatalf("ListenIP failed: %v", err) + } + defer c.Close() + if la := c.LocalAddr(); la == nil { + t.Fatal("IPConn.LocalAddr failed") + } + } } diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go index b23213ee1..daccba366 100644 --- a/src/pkg/net/iprawsock.go +++ b/src/pkg/net/iprawsock.go @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// (Raw) IP sockets +// Raw IP sockets package net -// IPAddr represents the address of a IP end point. +// IPAddr represents the address of an IP end point. type IPAddr struct { - IP IP + IP IP + Zone string // IPv6 scoped addressing zone } // Network returns the address's network name, "ip". @@ -21,45 +22,25 @@ func (a *IPAddr) String() string { return a.IP.String() } -// ResolveIPAddr parses addr as a IP address and resolves domain +// ResolveIPAddr parses addr as an IP address and resolves domain // names to numeric addresses on the network net, which must be -// "ip", "ip4" or "ip6". A literal IPv6 host address must be -// enclosed in square brackets, as in "[::]". +// "ip", "ip4" or "ip6". func ResolveIPAddr(net, addr string) (*IPAddr, error) { - ip, err := hostToIP(net, addr) + if net == "" { // a hint wildcard for Go 1.0 undocumented behavior + net = "ip" + } + afnet, _, err := parseNetwork(net) if err != nil { return nil, err } - return &IPAddr{ip}, nil -} - -// Convert "host" into IP address. -func hostToIP(net, host string) (ip IP, err error) { - var addr IP - // Try as an IP address. - addr = ParseIP(host) - if addr == nil { - filter := anyaddr - if net != "" && net[len(net)-1] == '4' { - filter = ipv4only - } - if net != "" && net[len(net)-1] == '6' { - filter = ipv6only - } - // Not an IP address. Try as a DNS name. - addrs, err1 := LookupHost(host) - if err1 != nil { - err = err1 - goto Error - } - addr = firstFavoriteAddr(filter, addrs) - if addr == nil { - // should not happen - err = &AddrError{"LookupHost returned no suitable address", addrs[0]} - goto Error - } + switch afnet { + case "ip", "ip4", "ip6": + default: + return nil, UnknownNetworkError(net) + } + a, err := resolveInternetAddr(afnet, addr, noDeadline) + if err != nil { + return nil, err } - return addr, nil -Error: - return nil, err + return a.(*IPAddr), nil } diff --git a/src/pkg/net/iprawsock_plan9.go b/src/pkg/net/iprawsock_plan9.go index 43719fc99..88e3b2c60 100644 --- a/src/pkg/net/iprawsock_plan9.go +++ b/src/pkg/net/iprawsock_plan9.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// (Raw) IP sockets stubs for Plan 9 +// Raw IP sockets for Plan 9 package net @@ -11,55 +11,13 @@ import ( "time" ) -// IPConn is the implementation of the Conn and PacketConn -// interfaces for IP network connections. -type IPConn bool - -// SetDeadline implements the Conn SetDeadline method. -func (c *IPConn) SetDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *IPConn) SetReadDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *IPConn) SetWriteDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the Conn Read method. -func (c *IPConn) Read(b []byte) (int, error) { - return 0, syscall.EPLAN9 -} - -// Write implements the Conn Write method. -func (c *IPConn) Write(b []byte) (int, error) { - return 0, syscall.EPLAN9 -} - -// Close closes the IP connection. -func (c *IPConn) Close() error { - return syscall.EPLAN9 -} - -// LocalAddr returns the local network address. -func (c *IPConn) LocalAddr() Addr { - return nil +// IPConn is the implementation of the Conn and PacketConn interfaces +// for IP network connections. +type IPConn struct { + conn } -// RemoteAddr returns the remote network address, a *IPAddr. -func (c *IPConn) RemoteAddr() Addr { - return nil -} - -// IP-specific methods. - -// ReadFromIP reads a IP packet from c, copying the payload into b. +// ReadFromIP reads an IP packet from c, copying the payload into b. // It returns the number of bytes copied into b and the return address // that was on the packet. // @@ -75,12 +33,21 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) { return 0, nil, syscall.EPLAN9 } -// WriteToIP writes a IP packet to addr via c, copying the payload from b. +// ReadMsgIP reads a packet from c, copying the payload into b and the +// associdated out-of-band data into oob. It returns the number of +// bytes copied into b, the number of bytes copied into oob, the flags +// that were set on the packet and the source address of the packet. +func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) { + return 0, 0, 0, nil, syscall.EPLAN9 +} + +// WriteToIP writes an IP packet to addr via c, copying the payload +// from b. // -// WriteToIP can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetDeadline and SetWriteDeadline. -// On packet-oriented connections, write timeouts are rare. +// WriteToIP can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetWriteDeadline. On packet-oriented connections, write timeouts +// are rare. func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) { return 0, syscall.EPLAN9 } @@ -90,16 +57,28 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) { return 0, syscall.EPLAN9 } -// DialIP connects to the remote address raddr on the network protocol netProto, -// which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name. +// WriteMsgIP writes a packet to addr via c, copying the payload from +// b and the associated out-of-band data from oob. It returns the +// number of payload and out-of-band bytes written. +func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) { + return 0, 0, syscall.EPLAN9 +} + +// DialIP connects to the remote address raddr on the network protocol +// netProto, which must be "ip", "ip4", or "ip6" followed by a colon +// and a protocol number or name. func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { + return dialIP(netProto, laddr, raddr, noDeadline) +} + +func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) { return nil, syscall.EPLAN9 } -// ListenIP listens for incoming IP packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send IP -// packets with per-packet addressing. +// ListenIP listens for incoming IP packets addressed to the local +// address laddr. The returned connection c's ReadFrom and WriteTo +// methods can be used to receive and send IP packets with per-packet +// addressing. func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) { return nil, syscall.EPLAN9 } diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go index 9fc7ecdb9..2ef4db19c 100644 --- a/src/pkg/net/iprawsock_posix.go +++ b/src/pkg/net/iprawsock_posix.go @@ -4,12 +4,11 @@ // +build darwin freebsd linux netbsd openbsd windows -// (Raw) IP sockets +// Raw IP sockets for POSIX package net import ( - "os" "syscall" "time" ) @@ -17,9 +16,9 @@ import ( func sockaddrToIP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { case *syscall.SockaddrInet4: - return &IPAddr{sa.Addr[0:]} + return &IPAddr{IP: sa.Addr[0:]} case *syscall.SockaddrInet6: - return &IPAddr{sa.Addr[0:]} + return &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))} } return nil } @@ -42,7 +41,7 @@ func (a *IPAddr) isWildcard() bool { } func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, 0) + return ipToSockaddr(family, a.IP, 0, a.Zone) } func (a *IPAddr) toAddr() sockaddr { @@ -55,98 +54,12 @@ func (a *IPAddr) toAddr() sockaddr { // IPConn is the implementation of the Conn and PacketConn // interfaces for IP network connections. type IPConn struct { - fd *netFD + conn } -func newIPConn(fd *netFD) *IPConn { return &IPConn{fd} } +func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} } -func (c *IPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the Conn Read method. -func (c *IPConn) Read(b []byte) (int, error) { - n, _, err := c.ReadFrom(b) - return n, err -} - -// Write implements the Conn Write method. -func (c *IPConn) Write(b []byte) (int, error) { - if !c.ok() { - return 0, syscall.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the IP connection. -func (c *IPConn) Close() error { - if !c.ok() { - return syscall.EINVAL - } - return c.fd.Close() -} - -// LocalAddr returns the local network address. -func (c *IPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *IPAddr. -func (c *IPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetDeadline implements the Conn SetDeadline method. -func (c *IPConn) SetDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setDeadline(c.fd, t) -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *IPConn) SetReadDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setReadDeadline(c.fd, t) -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *IPConn) SetWriteDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setWriteDeadline(c.fd, t) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *IPConn) SetReadBuffer(bytes int) error { - if !c.ok() { - return syscall.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *IPConn) SetWriteBuffer(bytes int) error { - if !c.ok() { - return syscall.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// IP-specific methods. - -// ReadFromIP reads a IP packet from c, copying the payload into b. +// ReadFromIP reads an IP packet from c, copying the payload into b. // It returns the number of bytes copied into b and the return address // that was on the packet. // @@ -163,14 +76,14 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) { n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { case *syscall.SockaddrInet4: - addr = &IPAddr{sa.Addr[0:]} + addr = &IPAddr{IP: sa.Addr[0:]} if len(b) >= IPv4len { // discard ipv4 header hsize := (int(b[0]) & 0xf) * 4 copy(b, b[hsize:]) n -= hsize } case *syscall.SockaddrInet6: - addr = &IPAddr{sa.Addr[0:]} + addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))} } return n, addr, err } @@ -180,11 +93,30 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL } - n, uaddr, err := c.ReadFromIP(b) - return n, uaddr.toAddr(), err + n, addr, err := c.ReadFromIP(b) + return n, addr.toAddr(), err +} + +// ReadMsgIP reads a packet from c, copying the payload into b and the +// associdated out-of-band data into oob. It returns the number of +// bytes copied into b, the number of bytes copied into oob, the flags +// that were set on the packet and the source address of the packet. +func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) { + if !c.ok() { + return 0, 0, 0, nil, syscall.EINVAL + } + var sa syscall.Sockaddr + n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob) + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + addr = &IPAddr{IP: sa.Addr[0:]} + case *syscall.SockaddrInet6: + addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))} + } + return } -// WriteToIP writes a IP packet to addr via c, copying the payload from b. +// WriteToIP writes an IP packet to addr via c, copying the payload from b. // // WriteToIP can be made to time out and return // an error with Timeout() == true after a fixed time limit; @@ -213,22 +145,40 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) { return c.WriteToIP(b, a) } +// WriteMsgIP writes a packet to addr via c, copying the payload from +// b and the associated out-of-band data from oob. It returns the +// number of payload and out-of-band bytes written. +func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) { + if !c.ok() { + return 0, 0, syscall.EINVAL + } + sa, err := addr.sockaddr(c.fd.family) + if err != nil { + return 0, 0, &OpError{"write", c.fd.net, addr, err} + } + return c.fd.WriteMsg(b, oob, sa) +} + // DialIP connects to the remote address raddr on the network protocol netProto, // which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name. func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { - net, proto, err := parseDialNetwork(netProto) + return dialIP(netProto, laddr, raddr, noDeadline) +} + +func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) { + net, proto, err := parseNetwork(netProto) if err != nil { return nil, err } switch net { case "ip", "ip4", "ip6": default: - return nil, UnknownNetworkError(net) + return nil, UnknownNetworkError(netProto) } if raddr == nil { return nil, &OpError{"dial", netProto, nil, errMissingAddress} } - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP) + fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP) if err != nil { return nil, err } @@ -240,23 +190,18 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { // and WriteTo methods can be used to receive and send IP // packets with per-packet addressing. func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) { - net, proto, err := parseDialNetwork(netProto) + net, proto, err := parseNetwork(netProto) if err != nil { return nil, err } switch net { case "ip", "ip4", "ip6": default: - return nil, UnknownNetworkError(net) + return nil, UnknownNetworkError(netProto) } - fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "listen", sockaddrToIP) + fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP) if err != nil { return nil, err } return newIPConn(fd), nil } - -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *IPConn) File() (f *os.File, err error) { return c.fd.dup() } diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go index bfbce18a4..1ef489289 100644 --- a/src/pkg/net/ipsock.go +++ b/src/pkg/net/ipsock.go @@ -2,11 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// IP sockets +// Internet protocol family sockets package net -var supportsIPv6, supportsIPv4map = probeIPv6Stack() +import "time" + +var supportsIPv6, supportsIPv4map bool + +func init() { + sysInit() + supportsIPv6, supportsIPv4map = probeIPv6Stack() +} func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { if filter == nil { @@ -65,25 +72,67 @@ func (e InvalidAddrError) Temporary() bool { return false } // "host:port" or "[host]:port" into host and port. // The latter form must be used when host contains a colon. func SplitHostPort(hostport string) (host, port string, err error) { + host, port, _, err = splitHostPort(hostport) + return +} + +func splitHostPort(hostport string) (host, port, zone string, err error) { + j, k := 0, 0 + // The port starts after the last colon. i := last(hostport, ':') if i < 0 { - err = &AddrError{"missing port in address", hostport} - return + goto missingPort } - host, port = hostport[0:i], hostport[i+1:] - - // Can put brackets around host ... - if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { - host = host[1 : len(host)-1] + if hostport[0] == '[' { + // Expect the first ']' just before the last ':'. + end := byteIndex(hostport, ']') + if end < 0 { + err = &AddrError{"missing ']' in address", hostport} + return + } + switch end + 1 { + case len(hostport): + // There can't be a ':' behind the ']' now. + goto missingPort + case i: + // The expected result. + default: + // Either ']' isn't followed by a colon, or it is + // followed by a colon that is not the last one. + if hostport[end+1] == ':' { + goto tooManyColons + } + goto missingPort + } + host = hostport[1:end] + j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions } else { - // ... but if there are no brackets, no colons. + host = hostport[:i] + if byteIndex(host, ':') >= 0 { - err = &AddrError{"too many colons in address", hostport} - return + goto tooManyColons } } + if byteIndex(hostport[j:], '[') >= 0 { + err = &AddrError{"unexpected '[' in address", hostport} + return + } + if byteIndex(hostport[k:], ']') >= 0 { + err = &AddrError{"unexpected ']' in address", hostport} + return + } + + port = hostport[i+1:] + return + +missingPort: + err = &AddrError{"missing port in address", hostport} + return + +tooManyColons: + err = &AddrError{"too many colons in address", hostport} return } @@ -97,49 +146,84 @@ func JoinHostPort(host, port string) string { return host + ":" + port } -// Convert "host:port" into IP address and port. -func hostPortToIP(net, hostport string) (ip IP, iport int, err error) { - host, port, err := SplitHostPort(hostport) - if err != nil { - return nil, 0, err - } - - var addr IP - if host != "" { - // Try as an IP address. - addr = ParseIP(host) - if addr == nil { - var filter func(IP) IP - if net != "" && net[len(net)-1] == '4' { - filter = ipv4only +func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) { + var ( + err error + host, port, zone string + portnum int + ) + switch net { + case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": + if addr != "" { + if host, port, zone, err = splitHostPort(addr); err != nil { + return nil, err } - if net != "" && net[len(net)-1] == '6' { - filter = ipv6only - } - // Not an IP address. Try as a DNS name. - addrs, err := LookupHost(host) - if err != nil { - return nil, 0, err - } - addr = firstFavoriteAddr(filter, addrs) - if addr == nil { - // should not happen - return nil, 0, &AddrError{"LookupHost returned no suitable address", addrs[0]} + if portnum, err = parsePort(net, port); err != nil { + return nil, err } } + case "ip", "ip4", "ip6": + if addr != "" { + host = addr + } + default: + return nil, UnknownNetworkError(net) } - - p, i, ok := dtoi(port, 0) - if !ok || i != len(port) { - p, err = LookupPort(net, port) - if err != nil { - return nil, 0, err + inetaddr := func(net string, ip IP, port int, zone string) Addr { + switch net { + case "tcp", "tcp4", "tcp6": + return &TCPAddr{IP: ip, Port: port, Zone: zone} + case "udp", "udp4", "udp6": + return &UDPAddr{IP: ip, Port: port, Zone: zone} + case "ip", "ip4", "ip6": + return &IPAddr{IP: ip, Zone: zone} } + return nil + } + if host == "" { + return inetaddr(net, nil, portnum, zone), nil } - if p < 0 || p > 0xFFFF { - return nil, 0, &AddrError{"invalid port", port} + // Try as an IP address. + if ip := ParseIP(host); ip != nil { + return inetaddr(net, ip, portnum, zone), nil } + var filter func(IP) IP + if net != "" && net[len(net)-1] == '4' { + filter = ipv4only + } + if net != "" && net[len(net)-1] == '6' { + filter = ipv6only + } + // Try as a DNS name. + addrs, err := lookupHostDeadline(host, deadline) + if err != nil { + return nil, err + } + ip := firstFavoriteAddr(filter, addrs) + if ip == nil { + // should not happen + return nil, &AddrError{"LookupHost returned no suitable address", addrs[0]} + } + return inetaddr(net, ip, portnum, zone), nil +} - return addr, p, nil +func zoneToString(zone int) string { + if zone == 0 { + return "" + } + if ifi, err := InterfaceByIndex(zone); err == nil { + return ifi.Name + } + return itod(uint(zone)) +} +func zoneToInt(zone string) int { + if zone == "" { + return 0 + } + if ifi, err := InterfaceByName(zone); err == nil { + return ifi.Index + } + n, _, _ := dtoi(zone, 0) + return n } diff --git a/src/pkg/net/ipsock_plan9.go b/src/pkg/net/ipsock_plan9.go index eab0bf3e8..c7d542dab 100644 --- a/src/pkg/net/ipsock_plan9.go +++ b/src/pkg/net/ipsock_plan9.go @@ -2,21 +2,22 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// IP sockets stubs for Plan 9 +// Internet protocol family sockets for Plan 9 package net import ( "errors" - "io" "os" "syscall" - "time" ) -// probeIPv6Stack returns two boolean values. If the first boolean value is -// true, kernel supports basic IPv6 functionality. If the second -// boolean value is true, kernel supports IPv6 IPv4-mapping. +// /sys/include/ape/sys/socket.h:/SOMAXCONN +var listenerBacklog = 5 + +// probeIPv6Stack returns two boolean values. If the first boolean +// value is true, kernel supports basic IPv6 functionality. If the +// second boolean value is true, kernel supports IPv6 IPv4-mapping. func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { return false, false } @@ -48,6 +49,7 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) { if err != nil { return } + defer f.Close() n, err := f.Read(buf[:]) if err != nil { return @@ -58,110 +60,15 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) { } switch proto { case "tcp": - addr = &TCPAddr{ip, port} + addr = &TCPAddr{IP: ip, Port: port} case "udp": - addr = &UDPAddr{ip, port} + addr = &UDPAddr{IP: ip, Port: port} default: return nil, errors.New("unknown protocol " + proto) } return addr, nil } -type plan9Conn struct { - proto, name, dir string - ctl, data *os.File - laddr, raddr Addr -} - -func newPlan9Conn(proto, name string, ctl *os.File, laddr, raddr Addr) *plan9Conn { - return &plan9Conn{proto, name, "/net/" + proto + "/" + name, ctl, nil, laddr, raddr} -} - -func (c *plan9Conn) ok() bool { return c != nil && c.ctl != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the Conn Read method. -func (c *plan9Conn) Read(b []byte) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - if c.data == nil { - c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) - if err != nil { - return 0, err - } - } - n, err = c.data.Read(b) - if c.proto == "udp" && err == io.EOF { - n = 0 - err = nil - } - return -} - -// Write implements the Conn Write method. -func (c *plan9Conn) Write(b []byte) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - if c.data == nil { - c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) - if err != nil { - return 0, err - } - } - return c.data.Write(b) -} - -// Close closes the connection. -func (c *plan9Conn) Close() error { - if !c.ok() { - return syscall.EINVAL - } - err := c.ctl.Close() - if err != nil { - return err - } - if c.data != nil { - err = c.data.Close() - } - c.ctl = nil - c.data = nil - return err -} - -// LocalAddr returns the local network address. -func (c *plan9Conn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.laddr -} - -// RemoteAddr returns the remote network address. -func (c *plan9Conn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.raddr -} - -// SetDeadline implements the Conn SetDeadline method. -func (c *plan9Conn) SetDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *plan9Conn) SetReadDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *plan9Conn) SetWriteDeadline(t time.Time) error { - return syscall.EPLAN9 -} - func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) { var ( ip IP @@ -192,98 +99,95 @@ func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, var buf [16]byte n, err := f.Read(buf[:]) if err != nil { + f.Close() return } return f, dest, proto, string(buf[:n]), nil } -func dialPlan9(net string, laddr, raddr Addr) (c *plan9Conn, err error) { +func netErr(e error) { + oe, ok := e.(*OpError) + if !ok { + return + } + if pe, ok := oe.Err.(*os.PathError); ok { + if _, ok = pe.Err.(syscall.ErrorString); ok { + oe.Err = pe.Err + } + } +} + +func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) { + defer func() { netErr(err) }() f, dest, proto, name, err := startPlan9(net, raddr) if err != nil { - return + return nil, &OpError{"dial", net, raddr, err} } _, err = f.WriteString("connect " + dest) if err != nil { - return + f.Close() + return nil, &OpError{"dial", f.Name(), raddr, err} } - laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") + data, err := os.OpenFile("/net/"+proto+"/"+name+"/data", os.O_RDWR, 0) if err != nil { - return + f.Close() + return nil, &OpError{"dial", net, raddr, err} } - raddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/remote") + laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") if err != nil { - return + data.Close() + f.Close() + return nil, &OpError{"dial", proto, raddr, err} } - return newPlan9Conn(proto, name, f, laddr, raddr), nil -} - -type plan9Listener struct { - proto, name, dir string - ctl *os.File - laddr Addr + return newFD(proto, name, f, data, laddr, raddr), nil } -func listenPlan9(net string, laddr Addr) (l *plan9Listener, err error) { +func listenPlan9(net string, laddr Addr) (fd *netFD, err error) { + defer func() { netErr(err) }() f, dest, proto, name, err := startPlan9(net, laddr) if err != nil { - return + return nil, &OpError{"listen", net, laddr, err} } _, err = f.WriteString("announce " + dest) if err != nil { - return + f.Close() + return nil, &OpError{"announce", proto, laddr, err} } laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local") if err != nil { - return + f.Close() + return nil, &OpError{Op: "listen", Net: net, Err: err} } - l = new(plan9Listener) - l.proto = proto - l.name = name - l.dir = "/net/" + proto + "/" + name - l.ctl = f - l.laddr = laddr - return l, nil + return newFD(proto, name, f, nil, laddr, nil), nil } -func (l *plan9Listener) plan9Conn() *plan9Conn { - return newPlan9Conn(l.proto, l.name, l.ctl, l.laddr, nil) +func (l *netFD) netFD() *netFD { + return newFD(l.proto, l.name, l.ctl, l.data, l.laddr, l.raddr) } -func (l *plan9Listener) acceptPlan9() (c *plan9Conn, err error) { +func (l *netFD) acceptPlan9() (fd *netFD, err error) { + defer func() { netErr(err) }() f, err := os.Open(l.dir + "/listen") if err != nil { - return + return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err} } var buf [16]byte n, err := f.Read(buf[:]) if err != nil { - return + f.Close() + return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err} } name := string(buf[:n]) - laddr, err := readPlan9Addr(l.proto, l.dir+"/local") - if err != nil { - return - } - raddr, err := readPlan9Addr(l.proto, l.dir+"/remote") + data, err := os.OpenFile("/net/"+l.proto+"/"+name+"/data", os.O_RDWR, 0) if err != nil { - return + f.Close() + return nil, &OpError{"accept", l.proto, l.laddr, err} } - return newPlan9Conn(l.proto, name, f, laddr, raddr), nil -} - -func (l *plan9Listener) Accept() (c Conn, err error) { - c1, err := l.acceptPlan9() + raddr, err := readPlan9Addr(l.proto, "/net/"+l.proto+"/"+name+"/remote") if err != nil { - return + data.Close() + f.Close() + return nil, &OpError{"accept", l.proto, l.laddr, err} } - return c1, nil + return newFD(l.proto, name, f, data, l.laddr, raddr), nil } - -func (l *plan9Listener) Close() error { - if l == nil || l.ctl == nil { - return syscall.EINVAL - } - return l.ctl.Close() -} - -func (l *plan9Listener) Addr() Addr { return l.laddr } diff --git a/src/pkg/net/ipsock_posix.go b/src/pkg/net/ipsock_posix.go index ed313195c..4c37616ec 100644 --- a/src/pkg/net/ipsock_posix.go +++ b/src/pkg/net/ipsock_posix.go @@ -4,9 +4,14 @@ // +build darwin freebsd linux netbsd openbsd windows +// Internet protocol family sockets for POSIX + package net -import "syscall" +import ( + "syscall" + "time" +) // Should we try to use the IPv4 socket interface if we're // only dealing with IPv4 sockets? As long as the host system @@ -97,10 +102,13 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family return syscall.AF_INET6, true } - if mode == "listen" && laddr.isWildcard() { + if mode == "listen" && (laddr == nil || laddr.isWildcard()) { if supportsIPv4map { return syscall.AF_INET6, false } + if laddr == nil { + return syscall.AF_INET, false + } return laddr.family(), false } @@ -122,7 +130,7 @@ type sockaddr interface { sockaddr(family int) (syscall.Sockaddr, error) } -func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { +func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { var la, ra syscall.Sockaddr family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode) if laddr != nil { @@ -135,7 +143,7 @@ func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode s goto Error } } - fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, toAddr) + fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, deadline, toAddr) if err != nil { goto Error } @@ -149,7 +157,7 @@ Error: return nil, &OpError{mode, net, addr, err} } -func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) { +func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) { switch family { case syscall.AF_INET: if len(ip) == 0 { @@ -158,12 +166,12 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) { if ip = ip.To4(); ip == nil { return nil, InvalidAddrError("non-IPv4 address") } - s := new(syscall.SockaddrInet4) + sa := new(syscall.SockaddrInet4) for i := 0; i < IPv4len; i++ { - s.Addr[i] = ip[i] + sa.Addr[i] = ip[i] } - s.Port = port - return s, nil + sa.Port = port + return sa, nil case syscall.AF_INET6: if len(ip) == 0 { ip = IPv6zero @@ -177,12 +185,13 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) { if ip = ip.To16(); ip == nil { return nil, InvalidAddrError("non-IPv6 address") } - s := new(syscall.SockaddrInet6) + sa := new(syscall.SockaddrInet6) for i := 0; i < IPv6len; i++ { - s.Addr[i] = ip[i] + sa.Addr[i] = ip[i] } - s.Port = port - return s, nil + sa.Port = port + sa.ZoneId = uint32(zoneToInt(zone)) + return sa, nil } return nil, InvalidAddrError("unexpected socket family") } diff --git a/src/pkg/net/lookup.go b/src/pkg/net/lookup.go new file mode 100644 index 000000000..bec93ec08 --- /dev/null +++ b/src/pkg/net/lookup.go @@ -0,0 +1,105 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "time" +) + +// LookupHost looks up the given host using the local resolver. +// It returns an array of that host's addresses. +func LookupHost(host string) (addrs []string, err error) { + return lookupHost(host) +} + +func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err error) { + if deadline.IsZero() { + return lookupHost(host) + } + + // TODO(bradfitz): consider pushing the deadline down into the + // name resolution functions. But that involves fixing it for + // the native Go resolver, cgo, Windows, etc. + // + // In the meantime, just use a goroutine. Most users affected + // by http://golang.org/issue/2631 are due to TCP connections + // to unresponsive hosts, not DNS. + timeout := deadline.Sub(time.Now()) + if timeout <= 0 { + err = errTimeout + return + } + t := time.NewTimer(timeout) + defer t.Stop() + type res struct { + addrs []string + err error + } + resc := make(chan res, 1) + go func() { + a, err := lookupHost(host) + resc <- res{a, err} + }() + select { + case <-t.C: + err = errTimeout + case r := <-resc: + addrs, err = r.addrs, r.err + } + return +} + +// LookupIP looks up host using the local resolver. +// It returns an array of that host's IPv4 and IPv6 addresses. +func LookupIP(host string) (addrs []IP, err error) { + return lookupIP(host) +} + +// LookupPort looks up the port for the given network and service. +func LookupPort(network, service string) (port int, err error) { + return lookupPort(network, service) +} + +// LookupCNAME returns the canonical DNS host for the given name. +// Callers that do not care about the canonical name can call +// LookupHost or LookupIP directly; both take care of resolving +// the canonical name as part of the lookup. +func LookupCNAME(name string) (cname string, err error) { + return lookupCNAME(name) +} + +// LookupSRV tries to resolve an SRV query of the given service, +// protocol, and domain name. The proto is "tcp" or "udp". +// The returned records are sorted by priority and randomized +// by weight within a priority. +// +// LookupSRV constructs the DNS name to look up following RFC 2782. +// That is, it looks up _service._proto.name. To accommodate services +// publishing SRV records under non-standard names, if both service +// and proto are empty strings, LookupSRV looks up name directly. +func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { + return lookupSRV(service, proto, name) +} + +// LookupMX returns the DNS MX records for the given domain name sorted by preference. +func LookupMX(name string) (mx []*MX, err error) { + return lookupMX(name) +} + +// LookupNS returns the DNS NS records for the given domain name. +func LookupNS(name string) (ns []*NS, err error) { + return lookupNS(name) +} + +// LookupTXT returns the DNS TXT records for the given domain name. +func LookupTXT(name string) (txt []string, err error) { + return lookupTXT(name) +} + +// LookupAddr performs a reverse lookup for the given address, returning a list +// of names mapping to that address. +func LookupAddr(addr string) (name []string, err error) { + return lookupAddr(addr) +} diff --git a/src/pkg/net/lookup_plan9.go b/src/pkg/net/lookup_plan9.go index 2c698304b..ae7cf7942 100644 --- a/src/pkg/net/lookup_plan9.go +++ b/src/pkg/net/lookup_plan9.go @@ -201,6 +201,21 @@ func lookupMX(name string) (mx []*MX, err error) { return } +func lookupNS(name string) (ns []*NS, err error) { + lines, err := queryDNS(name, "ns") + if err != nil { + return + } + for _, line := range lines { + f := getFields(line) + if len(f) < 4 { + continue + } + ns = append(ns, &NS{f[3]}) + } + return +} + func lookupTXT(name string) (txt []string, err error) { lines, err := queryDNS(name, "txt") if err != nil { diff --git a/src/pkg/net/lookup_test.go b/src/pkg/net/lookup_test.go index 3a61dfb29..3355e4694 100644 --- a/src/pkg/net/lookup_test.go +++ b/src/pkg/net/lookup_test.go @@ -9,6 +9,7 @@ package net import ( "flag" + "strings" "testing" ) @@ -16,8 +17,7 @@ var testExternal = flag.Bool("external", true, "allow use of external networks d func TestGoogleSRV(t *testing.T) { if testing.Short() || !*testExternal { - t.Logf("skipping test to avoid external network") - return + t.Skip("skipping test to avoid external network") } _, addrs, err := LookupSRV("xmpp-server", "tcp", "google.com") if err != nil { @@ -39,8 +39,7 @@ func TestGoogleSRV(t *testing.T) { func TestGmailMX(t *testing.T) { if testing.Short() || !*testExternal { - t.Logf("skipping test to avoid external network") - return + t.Skip("skipping test to avoid external network") } mx, err := LookupMX("gmail.com") if err != nil { @@ -51,10 +50,22 @@ func TestGmailMX(t *testing.T) { } } +func TestGmailNS(t *testing.T) { + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + ns, err := LookupNS("gmail.com") + if err != nil { + t.Errorf("failed: %s", err) + } + if len(ns) == 0 { + t.Errorf("no results") + } +} + func TestGmailTXT(t *testing.T) { if testing.Short() || !*testExternal { - t.Logf("skipping test to avoid external network") - return + t.Skip("skipping test to avoid external network") } txt, err := LookupTXT("gmail.com") if err != nil { @@ -67,8 +78,7 @@ func TestGmailTXT(t *testing.T) { func TestGoogleDNSAddr(t *testing.T) { if testing.Short() || !*testExternal { - t.Logf("skipping test to avoid external network") - return + t.Skip("skipping test to avoid external network") } names, err := LookupAddr("8.8.8.8") if err != nil { @@ -79,6 +89,16 @@ func TestGoogleDNSAddr(t *testing.T) { } } +func TestLookupIANACNAME(t *testing.T) { + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + cname, err := LookupCNAME("www.iana.org") + if !strings.HasSuffix(cname, ".icann.org.") || err != nil { + t.Errorf(`LookupCNAME("www.iana.org.") = %q, %v, want "*.icann.org.", nil`, cname, err) + } +} + var revAddrTests = []struct { Addr string Reverse string diff --git a/src/pkg/net/lookup_unix.go b/src/pkg/net/lookup_unix.go index d500a1240..fa98eed5f 100644 --- a/src/pkg/net/lookup_unix.go +++ b/src/pkg/net/lookup_unix.go @@ -119,6 +119,19 @@ func lookupMX(name string) (mx []*MX, err error) { return } +func lookupNS(name string) (ns []*NS, err error) { + _, records, err := lookup(name, dnsTypeNS) + if err != nil { + return + } + ns = make([]*NS, len(records)) + for i, r := range records { + r := r.(*dnsRR_NS) + ns[i] = &NS{r.Ns} + } + return +} + func lookupTXT(name string) (txt []string, err error) { _, records, err := lookup(name, dnsTypeTXT) if err != nil { diff --git a/src/pkg/net/lookup_windows.go b/src/pkg/net/lookup_windows.go index 99783e975..3b29724f2 100644 --- a/src/pkg/net/lookup_windows.go +++ b/src/pkg/net/lookup_windows.go @@ -6,21 +6,17 @@ package net import ( "os" - "sync" + "runtime" "syscall" "unsafe" ) var ( - protoentLock sync.Mutex - hostentLock sync.Mutex - serventLock sync.Mutex + lookupPort = oldLookupPort + lookupIP = oldLookupIP ) -// lookupProtocol looks up IP protocol name and returns correspondent protocol number. -func lookupProtocol(name string) (proto int, err error) { - protoentLock.Lock() - defer protoentLock.Unlock() +func getprotobyname(name string) (proto int, err error) { p, err := syscall.GetProtoByName(name) if err != nil { return 0, os.NewSyscallError("GetProtoByName", err) @@ -28,6 +24,25 @@ func lookupProtocol(name string) (proto int, err error) { return int(p.Proto), nil } +// lookupProtocol looks up IP protocol name and returns correspondent protocol number. +func lookupProtocol(name string) (proto int, err error) { + // GetProtoByName return value is stored in thread local storage. + // Start new os thread before the call to prevent races. + type result struct { + proto int + err error + } + ch := make(chan result) + go func() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + proto, err := getprotobyname(name) + ch <- result{proto: proto, err: err} + }() + r := <-ch + return r.proto, r.err +} + func lookupHost(name string) (addrs []string, err error) { ips, err := LookupIP(name) if err != nil { @@ -40,9 +55,7 @@ func lookupHost(name string) (addrs []string, err error) { return } -func lookupIP(name string) (addrs []IP, err error) { - hostentLock.Lock() - defer hostentLock.Unlock() +func gethostbyname(name string) (addrs []IP, err error) { h, err := syscall.GetHostByName(name) if err != nil { return nil, os.NewSyscallError("GetHostByName", err) @@ -56,20 +69,65 @@ func lookupIP(name string) (addrs []IP, err error) { } addrs = addrs[0:i] default: // TODO(vcc): Implement non IPv4 address lookups. - return nil, os.NewSyscallError("LookupHost", syscall.EWINDOWS) + return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS) } return addrs, nil } -func lookupPort(network, service string) (port int, err error) { +func oldLookupIP(name string) (addrs []IP, err error) { + // GetHostByName return value is stored in thread local storage. + // Start new os thread before the call to prevent races. + type result struct { + addrs []IP + err error + } + ch := make(chan result) + go func() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + addrs, err := gethostbyname(name) + ch <- result{addrs: addrs, err: err} + }() + r := <-ch + return r.addrs, r.err +} + +func newLookupIP(name string) (addrs []IP, err error) { + hints := syscall.AddrinfoW{ + Family: syscall.AF_UNSPEC, + Socktype: syscall.SOCK_STREAM, + Protocol: syscall.IPPROTO_IP, + } + var result *syscall.AddrinfoW + e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result) + if e != nil { + return nil, os.NewSyscallError("GetAddrInfoW", e) + } + defer syscall.FreeAddrInfoW(result) + addrs = make([]IP, 0, 5) + for ; result != nil; result = result.Next { + addr := unsafe.Pointer(result.Addr) + switch result.Family { + case syscall.AF_INET: + a := (*syscall.RawSockaddrInet4)(addr).Addr + addrs = append(addrs, IPv4(a[0], a[1], a[2], a[3])) + case syscall.AF_INET6: + a := (*syscall.RawSockaddrInet6)(addr).Addr + addrs = append(addrs, IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]}) + default: + return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS) + } + } + return addrs, nil +} + +func getservbyname(network, service string) (port int, err error) { switch network { case "tcp4", "tcp6": network = "tcp" case "udp4", "udp6": network = "udp" } - serventLock.Lock() - defer serventLock.Unlock() s, err := syscall.GetServByName(service, network) if err != nil { return 0, os.NewSyscallError("GetServByName", err) @@ -77,6 +135,58 @@ func lookupPort(network, service string) (port int, err error) { return int(syscall.Ntohs(s.Port)), nil } +func oldLookupPort(network, service string) (port int, err error) { + // GetServByName return value is stored in thread local storage. + // Start new os thread before the call to prevent races. + type result struct { + port int + err error + } + ch := make(chan result) + go func() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + port, err := getservbyname(network, service) + ch <- result{port: port, err: err} + }() + r := <-ch + return r.port, r.err +} + +func newLookupPort(network, service string) (port int, err error) { + var stype int32 + switch network { + case "tcp4", "tcp6": + stype = syscall.SOCK_STREAM + case "udp4", "udp6": + stype = syscall.SOCK_DGRAM + } + hints := syscall.AddrinfoW{ + Family: syscall.AF_UNSPEC, + Socktype: stype, + Protocol: syscall.IPPROTO_IP, + } + var result *syscall.AddrinfoW + e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result) + if e != nil { + return 0, os.NewSyscallError("GetAddrInfoW", e) + } + defer syscall.FreeAddrInfoW(result) + if result == nil { + return 0, os.NewSyscallError("LookupPort", syscall.EINVAL) + } + addr := unsafe.Pointer(result.Addr) + switch result.Family { + case syscall.AF_INET: + a := (*syscall.RawSockaddrInet4)(addr) + return int(syscall.Ntohs(a.Port)), nil + case syscall.AF_INET6: + a := (*syscall.RawSockaddrInet6)(addr) + return int(syscall.Ntohs(a.Port)), nil + } + return 0, os.NewSyscallError("LookupPort", syscall.EINVAL) +} + func lookupCNAME(name string) (cname string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil) @@ -129,6 +239,21 @@ func lookupMX(name string) (mx []*MX, err error) { return mx, nil } +func lookupNS(name string) (ns []*NS, err error) { + var r *syscall.DNSRecord + e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil) + if e != nil { + return nil, os.NewSyscallError("LookupNS", e) + } + defer syscall.DnsRecordListFree(r, 1) + ns = make([]*NS, 0, 10) + for p := r; p != nil && p.Type == syscall.DNS_TYPE_NS; p = p.Next { + v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0])) + ns = append(ns, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."}) + } + return ns, nil +} + func lookupTXT(name string) (txt []string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil) diff --git a/src/pkg/net/mail/message.go b/src/pkg/net/mail/message.go index b610ccf3f..96c796e78 100644 --- a/src/pkg/net/mail/message.go +++ b/src/pkg/net/mail/message.go @@ -47,7 +47,8 @@ type Message struct { } // ReadMessage reads a message from r. -// The headers are parsed, and the body of the message will be reading from r. +// The headers are parsed, and the body of the message will be available +// for reading from r. func ReadMessage(r io.Reader) (msg *Message, err error) { tp := textproto.NewReader(bufio.NewReader(r)) @@ -126,7 +127,7 @@ func (h Header) AddressList(key string) ([]*Address, error) { if hdr == "" { return nil, ErrHeaderNotPresent } - return newAddrParser(hdr).parseAddressList() + return ParseAddressList(hdr) } // Address represents a single mail address. @@ -137,6 +138,16 @@ type Address struct { Address string // user@domain } +// Parses a single RFC 5322 address, e.g. "Barry Gibbs " +func ParseAddress(address string) (*Address, error) { + return newAddrParser(address).parseAddress() +} + +// ParseAddressList parses the given string as a list of addresses. +func ParseAddressList(list string) ([]*Address, error) { + return newAddrParser(list).parseAddressList() +} + // String formats the address as a valid RFC 5322 address. // If the address's name contains non-ASCII characters // the name will be rendered according to RFC 2047. diff --git a/src/pkg/net/mail/message_test.go b/src/pkg/net/mail/message_test.go index fd17eb414..2e746f4a7 100644 --- a/src/pkg/net/mail/message_test.go +++ b/src/pkg/net/mail/message_test.go @@ -227,13 +227,24 @@ func TestAddressParsing(t *testing.T) { }, } for _, test := range tests { - addrs, err := newAddrParser(test.addrsStr).parseAddressList() + if len(test.exp) == 1 { + addr, err := ParseAddress(test.addrsStr) + if err != nil { + t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err) + continue + } + if !reflect.DeepEqual([]*Address{addr}, test.exp) { + t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp) + } + } + + addrs, err := ParseAddressList(test.addrsStr) if err != nil { - t.Errorf("Failed parsing %q: %v", test.addrsStr, err) + t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err) continue } if !reflect.DeepEqual(addrs, test.exp) { - t.Errorf("Parse of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp) + t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp) } } } diff --git a/src/pkg/net/multicast_posix_test.go b/src/pkg/net/multicast_posix_test.go new file mode 100644 index 000000000..ff1edaf83 --- /dev/null +++ b/src/pkg/net/multicast_posix_test.go @@ -0,0 +1,180 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9 + +package net + +import ( + "errors" + "os" + "runtime" + "testing" +) + +var multicastListenerTests = []struct { + net string + gaddr *UDPAddr + flags Flags + ipv6 bool // test with underlying AF_INET6 socket +}{ + // cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers + + {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, FlagUp | FlagLoopback, false}, + {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, 0, false}, + {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, 0, true}, + + {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, FlagUp | FlagLoopback, false}, + {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, 0, false}, + + {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, 0, true}, +} + +// TestMulticastListener tests both single and double listen to a test +// listener with same address family, same group address and same port. +func TestMulticastListener(t *testing.T) { + switch runtime.GOOS { + case "netbsd", "openbsd", "plan9", "solaris", "windows": + t.Skipf("skipping test on %q", runtime.GOOS) + case "linux": + if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" { + t.Skipf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH) + } + } + + for _, tt := range multicastListenerTests { + if tt.ipv6 && (!*testIPv6 || !supportsIPv6 || os.Getuid() != 0) { + continue + } + ifi, err := availMulticastInterface(t, tt.flags) + if err != nil { + continue + } + c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) + if err != nil { + t.Fatalf("First ListenMulticastUDP failed: %v", err) + } + checkMulticastListener(t, err, c1, tt.gaddr) + c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) + if err != nil { + t.Fatalf("Second ListenMulticastUDP failed: %v", err) + } + checkMulticastListener(t, err, c2, tt.gaddr) + c2.Close() + c1.Close() + } +} + +func TestSimpleMulticastListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + case "windows": + if testing.Short() || !*testExternal { + t.Skip("skipping test on windows to avoid firewall") + } + } + + for _, tt := range multicastListenerTests { + if tt.ipv6 { + continue + } + tt.flags = FlagUp | FlagMulticast // for windows testing + ifi, err := availMulticastInterface(t, tt.flags) + if err != nil { + continue + } + c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) + if err != nil { + t.Fatalf("First ListenMulticastUDP failed: %v", err) + } + checkSimpleMulticastListener(t, err, c1, tt.gaddr) + c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) + if err != nil { + t.Fatalf("Second ListenMulticastUDP failed: %v", err) + } + checkSimpleMulticastListener(t, err, c2, tt.gaddr) + c2.Close() + c1.Close() + } +} + +func checkMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) { + if !multicastRIBContains(t, gaddr.IP) { + t.Errorf("%q not found in RIB", gaddr.String()) + return + } + la := c.LocalAddr() + if la == nil { + t.Error("LocalAddr failed") + return + } + if a, ok := la.(*UDPAddr); !ok || a.Port == 0 { + t.Errorf("got %v; expected a proper address with non-zero port number", la) + return + } +} + +func checkSimpleMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) { + la := c.LocalAddr() + if la == nil { + t.Error("LocalAddr failed") + return + } + if a, ok := la.(*UDPAddr); !ok || a.Port == 0 { + t.Errorf("got %v; expected a proper address with non-zero port number", la) + return + } +} + +func availMulticastInterface(t *testing.T, flags Flags) (*Interface, error) { + var ifi *Interface + if flags != Flags(0) { + ift, err := Interfaces() + if err != nil { + t.Fatalf("Interfaces failed: %v", err) + } + for _, x := range ift { + if x.Flags&flags == flags { + ifi = &x + break + } + } + if ifi == nil { + return nil, errors.New("an appropriate multicast interface not found") + } + } + return ifi, nil +} + +func multicastRIBContains(t *testing.T, ip IP) bool { + ift, err := Interfaces() + if err != nil { + t.Fatalf("Interfaces failed: %v", err) + } + for _, ifi := range ift { + ifmat, err := ifi.MulticastAddrs() + if err != nil { + t.Fatalf("MulticastAddrs failed: %v", err) + } + for _, ifma := range ifmat { + if ifma.(*IPAddr).IP.Equal(ip) { + return true + } + } + } + return false +} diff --git a/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go deleted file mode 100644 index 67261b1ee..000000000 --- a/src/pkg/net/multicast_test.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package net - -import ( - "errors" - "os" - "runtime" - "syscall" - "testing" -) - -var multicastListenerTests = []struct { - net string - gaddr *UDPAddr - flags Flags - ipv6 bool // test with underlying AF_INET6 socket -}{ - // cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers - - {"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false}, - {"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false}, - {"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, 0, true}, - - {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false}, - {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false}, - - {"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, 0, true}, -} - -// TestMulticastListener tests both single and double listen to a test -// listener with same address family, same group address and same port. -func TestMulticastListener(t *testing.T) { - switch runtime.GOOS { - case "netbsd", "openbsd", "plan9", "windows": - t.Logf("skipping test on %q", runtime.GOOS) - return - case "linux": - if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" { - t.Logf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH) - return - } - } - - for _, tt := range multicastListenerTests { - if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) { - continue - } - ifi, err := availMulticastInterface(t, tt.flags) - if err != nil { - continue - } - c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) - if err != nil { - t.Fatalf("First ListenMulticastUDP failed: %v", err) - } - checkMulticastListener(t, err, c1, tt.gaddr) - c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) - if err != nil { - t.Fatalf("Second ListenMulticastUDP failed: %v", err) - } - checkMulticastListener(t, err, c2, tt.gaddr) - c2.Close() - switch c1.fd.family { - case syscall.AF_INET: - testIPv4MulticastSocketOptions(t, c1.fd, ifi) - case syscall.AF_INET6: - testIPv6MulticastSocketOptions(t, c1.fd, ifi) - } - c1.Close() - } -} - -func TestSimpleMulticastListener(t *testing.T) { - switch runtime.GOOS { - case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return - case "windows": - if testing.Short() || !*testExternal { - t.Logf("skipping test on windows to avoid firewall") - return - } - } - - for _, tt := range multicastListenerTests { - if tt.ipv6 { - continue - } - tt.flags = FlagUp | FlagMulticast // for windows testing - ifi, err := availMulticastInterface(t, tt.flags) - if err != nil { - continue - } - c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) - if err != nil { - t.Fatalf("First ListenMulticastUDP failed: %v", err) - } - checkSimpleMulticastListener(t, err, c1, tt.gaddr) - c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr) - if err != nil { - t.Fatalf("Second ListenMulticastUDP failed: %v", err) - } - checkSimpleMulticastListener(t, err, c2, tt.gaddr) - c2.Close() - c1.Close() - } -} - -func checkMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) { - if !multicastRIBContains(t, gaddr.IP) { - t.Fatalf("%q not found in RIB", gaddr.String()) - } - if c.LocalAddr().String() != gaddr.String() { - t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String()) - } -} - -func checkSimpleMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) { - if c.LocalAddr().String() != gaddr.String() { - t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String()) - } -} - -func availMulticastInterface(t *testing.T, flags Flags) (*Interface, error) { - var ifi *Interface - if flags != Flags(0) { - ift, err := Interfaces() - if err != nil { - t.Fatalf("Interfaces failed: %v", err) - } - for _, x := range ift { - if x.Flags&flags == flags { - ifi = &x - break - } - } - if ifi == nil { - return nil, errors.New("an appropriate multicast interface not found") - } - } - return ifi, nil -} - -func multicastRIBContains(t *testing.T, ip IP) bool { - ift, err := Interfaces() - if err != nil { - t.Fatalf("Interfaces failed: %v", err) - } - for _, ifi := range ift { - ifmat, err := ifi.MulticastAddrs() - if err != nil { - t.Fatalf("MulticastAddrs failed: %v", err) - } - for _, ifma := range ifmat { - if ifma.(*IPAddr).IP.Equal(ip) { - return true - } - } - } - return false -} - -func testIPv4MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) { - _, err := ipv4MulticastInterface(fd) - if err != nil { - t.Fatalf("ipv4MulticastInterface failed: %v", err) - } - if ifi != nil { - err = setIPv4MulticastInterface(fd, ifi) - if err != nil { - t.Fatalf("setIPv4MulticastInterface failed: %v", err) - } - } - _, err = ipv4MulticastTTL(fd) - if err != nil { - t.Fatalf("ipv4MulticastTTL failed: %v", err) - } - err = setIPv4MulticastTTL(fd, 1) - if err != nil { - t.Fatalf("setIPv4MulticastTTL failed: %v", err) - } - _, err = ipv4MulticastLoopback(fd) - if err != nil { - t.Fatalf("ipv4MulticastLoopback failed: %v", err) - } - err = setIPv4MulticastLoopback(fd, false) - if err != nil { - t.Fatalf("setIPv4MulticastLoopback failed: %v", err) - } -} - -func testIPv6MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) { - _, err := ipv6MulticastInterface(fd) - if err != nil { - t.Fatalf("ipv6MulticastInterface failed: %v", err) - } - if ifi != nil { - err = setIPv6MulticastInterface(fd, ifi) - if err != nil { - t.Fatalf("setIPv6MulticastInterface failed: %v", err) - } - } - _, err = ipv6MulticastHopLimit(fd) - if err != nil { - t.Fatalf("ipv6MulticastHopLimit failed: %v", err) - } - err = setIPv6MulticastHopLimit(fd, 1) - if err != nil { - t.Fatalf("setIPv6MulticastHopLimit failed: %v", err) - } - _, err = ipv6MulticastLoopback(fd) - if err != nil { - t.Fatalf("ipv6MulticastLoopback failed: %v", err) - } - err = setIPv6MulticastLoopback(fd, false) - if err != nil { - t.Fatalf("setIPv6MulticastLoopback failed: %v", err) - } -} diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go index 9ebcdbe99..72b2b646c 100644 --- a/src/pkg/net/net.go +++ b/src/pkg/net/net.go @@ -44,6 +44,10 @@ package net import ( "errors" + "io" + "os" + "sync" + "syscall" "time" ) @@ -103,6 +107,105 @@ type Conn interface { SetWriteDeadline(t time.Time) error } +type conn struct { + fd *netFD +} + +func (c *conn) ok() bool { return c != nil && c.fd != nil } + +// Implementation of the Conn interface. + +// Read implements the Conn Read method. +func (c *conn) Read(b []byte) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + return c.fd.Read(b) +} + +// Write implements the Conn Write method. +func (c *conn) Write(b []byte) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + return c.fd.Write(b) +} + +// Close closes the connection. +func (c *conn) Close() error { + if !c.ok() { + return syscall.EINVAL + } + return c.fd.Close() +} + +// LocalAddr returns the local network address. +func (c *conn) LocalAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.laddr +} + +// RemoteAddr returns the remote network address. +func (c *conn) RemoteAddr() Addr { + if !c.ok() { + return nil + } + return c.fd.raddr +} + +// SetDeadline implements the Conn SetDeadline method. +func (c *conn) SetDeadline(t time.Time) error { + if !c.ok() { + return syscall.EINVAL + } + return setDeadline(c.fd, t) +} + +// SetReadDeadline implements the Conn SetReadDeadline method. +func (c *conn) SetReadDeadline(t time.Time) error { + if !c.ok() { + return syscall.EINVAL + } + return setReadDeadline(c.fd, t) +} + +// SetWriteDeadline implements the Conn SetWriteDeadline method. +func (c *conn) SetWriteDeadline(t time.Time) error { + if !c.ok() { + return syscall.EINVAL + } + return setWriteDeadline(c.fd, t) +} + +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *conn) SetReadBuffer(bytes int) error { + if !c.ok() { + return syscall.EINVAL + } + return setReadBuffer(c.fd, bytes) +} + +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *conn) SetWriteBuffer(bytes int) error { + if !c.ok() { + return syscall.EINVAL + } + return setWriteBuffer(c.fd, bytes) +} + +// File sets the underlying os.File to blocking mode and returns a copy. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +// +// The returned os.File's file descriptor is different from the connection's. +// Attempting to change properties of the original using this duplicate +// may or may not have the desired effect. +func (c *conn) File() (f *os.File, err error) { return c.fd.dup() } + // An Error represents a network error. type Error interface { error @@ -173,11 +276,23 @@ type Listener interface { var errMissingAddress = errors.New("missing address") +// OpError is the error type usually returned by functions in the net +// package. It describes the operation, network type, and address of +// an error. type OpError struct { - Op string - Net string + // Op is the operation which caused the error, such as + // "read" or "write". + Op string + + // Net is the network type on which this error occurred, + // such as "tcp" or "udp6". + Net string + + // Addr is the network address on which this error occurred. Addr Addr - Err error + + // Err is the error that occurred during the operation. + Err error } func (e *OpError) Error() string { @@ -204,6 +319,8 @@ func (e *OpError) Temporary() bool { return ok && t.Temporary() } +var noDeadline = time.Time{} + type timeout interface { Timeout() bool } @@ -221,6 +338,8 @@ func (e *timeoutError) Temporary() bool { return true } var errTimeout error = &timeoutError{} +var errClosing = errors.New("use of closed network connection") + type AddrError struct { Err string Addr string @@ -262,3 +381,47 @@ func (e *DNSConfigError) Error() string { func (e *DNSConfigError) Timeout() bool { return false } func (e *DNSConfigError) Temporary() bool { return false } + +type writerOnly struct { + io.Writer +} + +// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't +// applicable. +func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) { + // Use wrapper to hide existing r.ReadFrom from io.Copy. + return io.Copy(writerOnly{w}, r) +} + +// deadline is an atomically-accessed number of nanoseconds since 1970 +// or 0, if no deadline is set. +type deadline struct { + sync.Mutex + val int64 +} + +func (d *deadline) expired() bool { + t := d.value() + return t > 0 && time.Now().UnixNano() >= t +} + +func (d *deadline) value() (v int64) { + d.Lock() + v = d.val + d.Unlock() + return +} + +func (d *deadline) set(v int64) { + d.Lock() + d.val = v + d.Unlock() +} + +func (d *deadline) setTime(t time.Time) { + if t.IsZero() { + d.set(0) + } else { + d.set(t.UnixNano()) + } +} diff --git a/src/pkg/net/net_test.go b/src/pkg/net/net_test.go index fd145e1d7..1a512a5b1 100644 --- a/src/pkg/net/net_test.go +++ b/src/pkg/net/net_test.go @@ -6,6 +6,8 @@ package net import ( "io" + "io/ioutil" + "os" "runtime" "testing" "time" @@ -13,18 +15,17 @@ import ( func TestShutdown(t *testing.T) { if runtime.GOOS == "plan9" { - t.Logf("skipping test on %q", runtime.GOOS) - return + t.Skipf("skipping test on %q", runtime.GOOS) } - l, err := Listen("tcp", "127.0.0.1:0") + ln, err := Listen("tcp", "127.0.0.1:0") if err != nil { - if l, err = Listen("tcp6", "[::1]:0"); err != nil { + if ln, err = Listen("tcp6", "[::1]:0"); err != nil { t.Fatalf("ListenTCP on :0: %v", err) } } go func() { - c, err := l.Accept() + c, err := ln.Accept() if err != nil { t.Fatalf("Accept: %v", err) } @@ -37,7 +38,7 @@ func TestShutdown(t *testing.T) { c.Close() }() - c, err := Dial("tcp", l.Addr().String()) + c, err := Dial("tcp", ln.Addr().String()) if err != nil { t.Fatalf("Dial: %v", err) } @@ -58,8 +59,61 @@ func TestShutdown(t *testing.T) { } } +func TestShutdownUnix(t *testing.T) { + switch runtime.GOOS { + case "windows", "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + f, err := ioutil.TempFile("", "go_net_unixtest") + if err != nil { + t.Fatalf("TempFile: %s", err) + } + f.Close() + tmpname := f.Name() + os.Remove(tmpname) + ln, err := Listen("unix", tmpname) + if err != nil { + t.Fatalf("ListenUnix on %s: %s", tmpname, err) + } + defer os.Remove(tmpname) + + go func() { + c, err := ln.Accept() + if err != nil { + t.Fatalf("Accept: %v", err) + } + var buf [10]byte + n, err := c.Read(buf[:]) + if n != 0 || err != io.EOF { + t.Fatalf("server Read = %d, %v; want 0, io.EOF", n, err) + } + c.Write([]byte("response")) + c.Close() + }() + + c, err := Dial("unix", tmpname) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer c.Close() + + err = c.(*UnixConn).CloseWrite() + if err != nil { + t.Fatalf("CloseWrite: %v", err) + } + var buf [10]byte + n, err := c.Read(buf[:]) + if err != nil { + t.Fatalf("client Read: %d, %v", n, err) + } + got := string(buf[:n]) + if got != "response" { + t.Errorf("read = %q, want \"response\"", got) + } +} + func TestTCPListenClose(t *testing.T) { - l, err := Listen("tcp", "127.0.0.1:0") + ln, err := Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("Listen failed: %v", err) } @@ -67,11 +121,12 @@ func TestTCPListenClose(t *testing.T) { done := make(chan bool, 1) go func() { time.Sleep(100 * time.Millisecond) - l.Close() + ln.Close() }() go func() { - _, err = l.Accept() + c, err := ln.Accept() if err == nil { + c.Close() t.Error("Accept succeeded") } else { t.Logf("Accept timeout error: %s (any error is fine)", err) @@ -86,7 +141,11 @@ func TestTCPListenClose(t *testing.T) { } func TestUDPListenClose(t *testing.T) { - l, err := ListenPacket("udp", "127.0.0.1:0") + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + ln, err := ListenPacket("udp", "127.0.0.1:0") if err != nil { t.Fatalf("Listen failed: %v", err) } @@ -95,10 +154,10 @@ func TestUDPListenClose(t *testing.T) { done := make(chan bool, 1) go func() { time.Sleep(100 * time.Millisecond) - l.Close() + ln.Close() }() go func() { - _, _, err = l.ReadFrom(buf) + _, _, err = ln.ReadFrom(buf) if err == nil { t.Error("ReadFrom succeeded") } else { @@ -112,3 +171,46 @@ func TestUDPListenClose(t *testing.T) { t.Fatal("timeout waiting for UDP close") } } + +func TestTCPClose(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + l, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer l.Close() + + read := func(r io.Reader) error { + var m [1]byte + _, err := r.Read(m[:]) + return err + } + + go func() { + c, err := Dial("tcp", l.Addr().String()) + if err != nil { + t.Fatal(err) + } + + go read(c) + + time.Sleep(10 * time.Millisecond) + c.Close() + }() + + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + defer c.Close() + + for err == nil { + err = read(c) + } + if err != nil && err != io.EOF { + t.Fatal(err) + } +} diff --git a/src/pkg/net/newpollserver.go b/src/pkg/net/newpollserver.go deleted file mode 100644 index d34bb511f..000000000 --- a/src/pkg/net/newpollserver.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd - -package net - -import ( - "os" - "syscall" -) - -func newPollServer() (s *pollServer, err error) { - s = new(pollServer) - s.cr = make(chan *netFD, 1) - s.cw = make(chan *netFD, 1) - if s.pr, s.pw, err = os.Pipe(); err != nil { - return nil, err - } - if err = syscall.SetNonblock(int(s.pr.Fd()), true); err != nil { - goto Errno - } - if err = syscall.SetNonblock(int(s.pw.Fd()), true); err != nil { - goto Errno - } - if s.poll, err = newpollster(); err != nil { - goto Error - } - if _, err = s.poll.AddFD(int(s.pr.Fd()), 'r', true); err != nil { - s.poll.Close() - goto Error - } - s.pending = make(map[int]*netFD) - go s.Run() - return s, nil - -Errno: - err = &os.PathError{ - Op: "setnonblock", - Path: s.pr.Name(), - Err: err, - } -Error: - s.pr.Close() - s.pw.Close() - return nil, err -} diff --git a/src/pkg/net/newpollserver_unix.go b/src/pkg/net/newpollserver_unix.go new file mode 100644 index 000000000..618b5b10b --- /dev/null +++ b/src/pkg/net/newpollserver_unix.go @@ -0,0 +1,46 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package net + +import ( + "os" + "syscall" +) + +func newPollServer() (s *pollServer, err error) { + s = new(pollServer) + if s.pr, s.pw, err = os.Pipe(); err != nil { + return nil, err + } + if err = syscall.SetNonblock(int(s.pr.Fd()), true); err != nil { + goto Errno + } + if err = syscall.SetNonblock(int(s.pw.Fd()), true); err != nil { + goto Errno + } + if s.poll, err = newpollster(); err != nil { + goto Error + } + if _, err = s.poll.AddFD(int(s.pr.Fd()), 'r', true); err != nil { + s.poll.Close() + goto Error + } + s.pending = make(map[int]*netFD) + go s.Run() + return s, nil + +Errno: + err = &os.PathError{ + Op: "setnonblock", + Path: s.pr.Name(), + Err: err, + } +Error: + s.pr.Close() + s.pw.Close() + return nil, err +} diff --git a/src/pkg/net/packetconn_test.go b/src/pkg/net/packetconn_test.go new file mode 100644 index 000000000..93c7a6472 --- /dev/null +++ b/src/pkg/net/packetconn_test.go @@ -0,0 +1,200 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements API tests across platforms and will never have a build +// tag. + +package net + +import ( + "os" + "runtime" + "strings" + "testing" + "time" +) + +var packetConnTests = []struct { + net string + addr1 string + addr2 string +}{ + {"udp", "127.0.0.1:0", "127.0.0.1:0"}, + {"ip:icmp", "127.0.0.1", "127.0.0.1"}, + {"unixgram", testUnixAddr(), testUnixAddr()}, +} + +func TestPacketConn(t *testing.T) { + closer := func(c PacketConn, net, addr1, addr2 string) { + c.Close() + switch net { + case "unixgram": + os.Remove(addr1) + os.Remove(addr2) + } + } + + for i, tt := range packetConnTests { + var wb []byte + netstr := strings.Split(tt.net, ":") + switch netstr[0] { + case "udp": + wb = []byte("UDP PACKETCONN TEST") + case "ip": + switch runtime.GOOS { + case "plan9": + continue + } + if os.Getuid() != 0 { + continue + } + var err error + wb, err = (&icmpMessage{ + Type: icmpv4EchoRequest, Code: 0, + Body: &icmpEcho{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("IP PACKETCONN TEST"), + }, + }).Marshal() + if err != nil { + t.Fatalf("icmpMessage.Marshal failed: %v", err) + } + case "unixgram": + switch runtime.GOOS { + case "plan9", "windows": + continue + } + wb = []byte("UNIXGRAM PACKETCONN TEST") + default: + continue + } + + c1, err := ListenPacket(tt.net, tt.addr1) + if err != nil { + t.Fatalf("ListenPacket failed: %v", err) + } + defer closer(c1, netstr[0], tt.addr1, tt.addr2) + c1.LocalAddr() + c1.SetDeadline(time.Now().Add(100 * time.Millisecond)) + c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)) + + c2, err := ListenPacket(tt.net, tt.addr2) + if err != nil { + t.Fatalf("ListenPacket failed: %v", err) + } + defer closer(c2, netstr[0], tt.addr1, tt.addr2) + c2.LocalAddr() + c2.SetDeadline(time.Now().Add(100 * time.Millisecond)) + c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)) + + if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil { + t.Fatalf("PacketConn.WriteTo failed: %v", err) + } + rb2 := make([]byte, 128) + if _, _, err := c2.ReadFrom(rb2); err != nil { + t.Fatalf("PacketConn.ReadFrom failed: %v", err) + } + if _, err := c2.WriteTo(wb, c1.LocalAddr()); err != nil { + t.Fatalf("PacketConn.WriteTo failed: %v", err) + } + rb1 := make([]byte, 128) + if _, _, err := c1.ReadFrom(rb1); err != nil { + t.Fatalf("PacketConn.ReadFrom failed: %v", err) + } + } +} + +func TestConnAndPacketConn(t *testing.T) { + closer := func(c PacketConn, net, addr1, addr2 string) { + c.Close() + switch net { + case "unixgram": + os.Remove(addr1) + os.Remove(addr2) + } + } + + for i, tt := range packetConnTests { + var wb []byte + netstr := strings.Split(tt.net, ":") + switch netstr[0] { + case "udp": + wb = []byte("UDP PACKETCONN TEST") + case "ip": + switch runtime.GOOS { + case "plan9": + continue + } + if os.Getuid() != 0 { + continue + } + var err error + wb, err = (&icmpMessage{ + Type: icmpv4EchoRequest, Code: 0, + Body: &icmpEcho{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("IP PACKETCONN TEST"), + }, + }).Marshal() + if err != nil { + t.Fatalf("icmpMessage.Marshal failed: %v", err) + } + case "unixgram": + switch runtime.GOOS { + case "plan9", "windows": + continue + } + wb = []byte("UNIXGRAM PACKETCONN TEST") + default: + continue + } + + c1, err := ListenPacket(tt.net, tt.addr1) + if err != nil { + t.Fatalf("ListenPacket failed: %v", err) + } + defer closer(c1, netstr[0], tt.addr1, tt.addr2) + c1.LocalAddr() + c1.SetDeadline(time.Now().Add(100 * time.Millisecond)) + c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)) + + c2, err := Dial(tt.net, c1.LocalAddr().String()) + if err != nil { + t.Fatalf("Dial failed: %v", err) + } + defer c2.Close() + c2.LocalAddr() + c2.RemoteAddr() + c2.SetDeadline(time.Now().Add(100 * time.Millisecond)) + c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)) + + if _, err := c2.Write(wb); err != nil { + t.Fatalf("Conn.Write failed: %v", err) + } + rb1 := make([]byte, 128) + if _, _, err := c1.ReadFrom(rb1); err != nil { + t.Fatalf("PacetConn.ReadFrom failed: %v", err) + } + var dst Addr + switch netstr[0] { + case "ip": + dst = &IPAddr{IP: IPv4(127, 0, 0, 1)} + case "unixgram": + continue + default: + dst = c2.LocalAddr() + } + if _, err := c1.WriteTo(wb, dst); err != nil { + t.Fatalf("PacketConn.WriteTo failed: %v", err) + } + rb2 := make([]byte, 128) + if _, err := c2.Read(rb2); err != nil { + t.Fatalf("Conn.Read failed: %v", err) + } + } +} diff --git a/src/pkg/net/parse_test.go b/src/pkg/net/parse_test.go index 30fda45df..9df0c534b 100644 --- a/src/pkg/net/parse_test.go +++ b/src/pkg/net/parse_test.go @@ -15,8 +15,7 @@ func TestReadLine(t *testing.T) { // /etc/services file does not exist on windows and Plan 9. switch runtime.GOOS { case "plan9", "windows": - t.Logf("skipping test on %q", runtime.GOOS) - return + t.Skipf("skipping test on %q", runtime.GOOS) } filename := "/etc/services" // a nice big file diff --git a/src/pkg/net/port.go b/src/pkg/net/port.go index 16780da11..c24f4ed5b 100644 --- a/src/pkg/net/port.go +++ b/src/pkg/net/port.go @@ -1,69 +1,24 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd - -// Read system port mappings from /etc/services +// Network service port manipulations package net -import "sync" - -var services map[string]map[string]int -var servicesError error -var onceReadServices sync.Once - -func readServices() { - services = make(map[string]map[string]int) - var file *file - if file, servicesError = open("/etc/services"); servicesError != nil { - return - } - for line, ok := file.readLine(); ok; line, ok = file.readLine() { - // "http 80/tcp www www-http # World Wide Web HTTP" - if i := byteIndex(line, '#'); i >= 0 { - line = line[0:i] - } - f := getFields(line) - if len(f) < 2 { - continue - } - portnet := f[1] // "tcp/80" - port, j, ok := dtoi(portnet, 0) - if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' { - continue - } - netw := portnet[j+1:] // "tcp" - m, ok1 := services[netw] - if !ok1 { - m = make(map[string]int) - services[netw] = m - } - for i := 0; i < len(f); i++ { - if i != 1 { // f[1] was port/net - m[f[i]] = port - } +// parsePort parses port as a network service port number for both +// TCP and UDP. +func parsePort(net, port string) (int, error) { + p, i, ok := dtoi(port, 0) + if !ok || i != len(port) { + var err error + p, err = LookupPort(net, port) + if err != nil { + return 0, err } } - file.close() -} - -// goLookupPort is the native Go implementation of LookupPort. -func goLookupPort(network, service string) (port int, err error) { - onceReadServices.Do(readServices) - - switch network { - case "tcp4", "tcp6": - network = "tcp" - case "udp4", "udp6": - network = "udp" - } - - if m, ok := services[network]; ok { - if port, ok = m[service]; ok { - return - } + if p < 0 || p > 0xFFFF { + return 0, &AddrError{"invalid port", port} } - return 0, &AddrError{"unknown port", network + "/" + service} + return p, nil } diff --git a/src/pkg/net/port_test.go b/src/pkg/net/port_test.go index 329b169f3..9e8968f35 100644 --- a/src/pkg/net/port_test.go +++ b/src/pkg/net/port_test.go @@ -46,7 +46,7 @@ func TestLookupPort(t *testing.T) { for i := 0; i < len(porttests); i++ { tt := porttests[i] if port, err := LookupPort(tt.netw, tt.name); port != tt.port || (err == nil) != tt.ok { - t.Errorf("LookupPort(%q, %q) = %v, %s; want %v", + t.Errorf("LookupPort(%q, %q) = %v, %v; want %v", tt.netw, tt.name, port, err, tt.port) } } diff --git a/src/pkg/net/port_unix.go b/src/pkg/net/port_unix.go new file mode 100644 index 000000000..16780da11 --- /dev/null +++ b/src/pkg/net/port_unix.go @@ -0,0 +1,69 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +// Read system port mappings from /etc/services + +package net + +import "sync" + +var services map[string]map[string]int +var servicesError error +var onceReadServices sync.Once + +func readServices() { + services = make(map[string]map[string]int) + var file *file + if file, servicesError = open("/etc/services"); servicesError != nil { + return + } + for line, ok := file.readLine(); ok; line, ok = file.readLine() { + // "http 80/tcp www www-http # World Wide Web HTTP" + if i := byteIndex(line, '#'); i >= 0 { + line = line[0:i] + } + f := getFields(line) + if len(f) < 2 { + continue + } + portnet := f[1] // "tcp/80" + port, j, ok := dtoi(portnet, 0) + if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' { + continue + } + netw := portnet[j+1:] // "tcp" + m, ok1 := services[netw] + if !ok1 { + m = make(map[string]int) + services[netw] = m + } + for i := 0; i < len(f); i++ { + if i != 1 { // f[1] was port/net + m[f[i]] = port + } + } + } + file.close() +} + +// goLookupPort is the native Go implementation of LookupPort. +func goLookupPort(network, service string) (port int, err error) { + onceReadServices.Do(readServices) + + switch network { + case "tcp4", "tcp6": + network = "tcp" + case "udp4", "udp6": + network = "udp" + } + + if m, ok := services[network]; ok { + if port, ok = m[service]; ok { + return + } + } + return 0, &AddrError{"unknown port", network + "/" + service} +} diff --git a/src/pkg/net/protoconn_test.go b/src/pkg/net/protoconn_test.go new file mode 100644 index 000000000..2fe7d1d1f --- /dev/null +++ b/src/pkg/net/protoconn_test.go @@ -0,0 +1,358 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements API tests across platforms and will never have a build +// tag. + +package net + +import ( + "io/ioutil" + "os" + "runtime" + "testing" + "time" +) + +// testUnixAddr uses ioutil.TempFile to get a name that is unique. +func testUnixAddr() string { + f, err := ioutil.TempFile("", "nettest") + if err != nil { + panic(err) + } + addr := f.Name() + f.Close() + os.Remove(addr) + return addr +} + +var condFatalf = func() func(*testing.T, string, ...interface{}) { + // A few APIs are not implemented yet on both Plan 9 and Windows. + switch runtime.GOOS { + case "plan9", "windows": + return (*testing.T).Logf + } + return (*testing.T).Fatalf +}() + +func TestTCPListenerSpecificMethods(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + la, err := ResolveTCPAddr("tcp4", "127.0.0.1:0") + if err != nil { + t.Fatalf("ResolveTCPAddr failed: %v", err) + } + ln, err := ListenTCP("tcp4", la) + if err != nil { + t.Fatalf("ListenTCP failed: %v", err) + } + defer ln.Close() + ln.Addr() + ln.SetDeadline(time.Now().Add(30 * time.Nanosecond)) + + if c, err := ln.Accept(); err != nil { + if !err.(Error).Timeout() { + t.Fatalf("TCPListener.Accept failed: %v", err) + } + } else { + c.Close() + } + if c, err := ln.AcceptTCP(); err != nil { + if !err.(Error).Timeout() { + t.Fatalf("TCPListener.AcceptTCP failed: %v", err) + } + } else { + c.Close() + } + + if f, err := ln.File(); err != nil { + condFatalf(t, "TCPListener.File failed: %v", err) + } else { + f.Close() + } +} + +func TestTCPConnSpecificMethods(t *testing.T) { + la, err := ResolveTCPAddr("tcp4", "127.0.0.1:0") + if err != nil { + t.Fatalf("ResolveTCPAddr failed: %v", err) + } + ln, err := ListenTCP("tcp4", la) + if err != nil { + t.Fatalf("ListenTCP failed: %v", err) + } + defer ln.Close() + ln.Addr() + + done := make(chan int) + go transponder(t, ln, done) + + ra, err := ResolveTCPAddr("tcp4", ln.Addr().String()) + if err != nil { + t.Fatalf("ResolveTCPAddr failed: %v", err) + } + c, err := DialTCP("tcp4", nil, ra) + if err != nil { + t.Fatalf("DialTCP failed: %v", err) + } + defer c.Close() + c.SetKeepAlive(false) + c.SetLinger(0) + c.SetNoDelay(false) + c.LocalAddr() + c.RemoteAddr() + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + + if _, err := c.Write([]byte("TCPCONN TEST")); err != nil { + t.Fatalf("TCPConn.Write failed: %v", err) + } + rb := make([]byte, 128) + if _, err := c.Read(rb); err != nil { + t.Fatalf("TCPConn.Read failed: %v", err) + } + + <-done +} + +func TestUDPConnSpecificMethods(t *testing.T) { + la, err := ResolveUDPAddr("udp4", "127.0.0.1:0") + if err != nil { + t.Fatalf("ResolveUDPAddr failed: %v", err) + } + c, err := ListenUDP("udp4", la) + if err != nil { + t.Fatalf("ListenUDP failed: %v", err) + } + defer c.Close() + c.LocalAddr() + c.RemoteAddr() + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + c.SetReadBuffer(2048) + c.SetWriteBuffer(2048) + + wb := []byte("UDPCONN TEST") + rb := make([]byte, 128) + if _, err := c.WriteToUDP(wb, c.LocalAddr().(*UDPAddr)); err != nil { + t.Fatalf("UDPConn.WriteToUDP failed: %v", err) + } + if _, _, err := c.ReadFromUDP(rb); err != nil { + t.Fatalf("UDPConn.ReadFromUDP failed: %v", err) + } + if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*UDPAddr)); err != nil { + condFatalf(t, "UDPConn.WriteMsgUDP failed: %v", err) + } + if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil { + condFatalf(t, "UDPConn.ReadMsgUDP failed: %v", err) + } + + if f, err := c.File(); err != nil { + condFatalf(t, "UDPConn.File failed: %v", err) + } else { + f.Close() + } +} + +func TestIPConnSpecificMethods(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping read test on %q", runtime.GOOS) + } + if os.Getuid() != 0 { + t.Skipf("skipping test; must be root") + } + + la, err := ResolveIPAddr("ip4", "127.0.0.1") + if err != nil { + t.Fatalf("ResolveIPAddr failed: %v", err) + } + c, err := ListenIP("ip4:icmp", la) + if err != nil { + t.Fatalf("ListenIP failed: %v", err) + } + defer c.Close() + c.LocalAddr() + c.RemoteAddr() + c.SetDeadline(time.Now().Add(someTimeout)) + c.SetReadDeadline(time.Now().Add(someTimeout)) + c.SetWriteDeadline(time.Now().Add(someTimeout)) + c.SetReadBuffer(2048) + c.SetWriteBuffer(2048) + + wb, err := (&icmpMessage{ + Type: icmpv4EchoRequest, Code: 0, + Body: &icmpEcho{ + ID: os.Getpid() & 0xffff, Seq: 1, + Data: []byte("IPCONN TEST "), + }, + }).Marshal() + if err != nil { + t.Fatalf("icmpMessage.Marshal failed: %v", err) + } + rb := make([]byte, 20+128) + if _, err := c.WriteToIP(wb, c.LocalAddr().(*IPAddr)); err != nil { + t.Fatalf("IPConn.WriteToIP failed: %v", err) + } + if _, _, err := c.ReadFromIP(rb); err != nil { + t.Fatalf("IPConn.ReadFromIP failed: %v", err) + } + if _, _, err := c.WriteMsgIP(wb, nil, c.LocalAddr().(*IPAddr)); err != nil { + condFatalf(t, "IPConn.WriteMsgIP failed: %v", err) + } + if _, _, _, _, err := c.ReadMsgIP(rb, nil); err != nil { + condFatalf(t, "IPConn.ReadMsgIP failed: %v", err) + } + + if f, err := c.File(); err != nil { + condFatalf(t, "IPConn.File failed: %v", err) + } else { + f.Close() + } +} + +func TestUnixListenerSpecificMethods(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("skipping read test on %q", runtime.GOOS) + } + + addr := testUnixAddr() + la, err := ResolveUnixAddr("unix", addr) + if err != nil { + t.Fatalf("ResolveUnixAddr failed: %v", err) + } + ln, err := ListenUnix("unix", la) + if err != nil { + t.Fatalf("ListenUnix failed: %v", err) + } + defer ln.Close() + defer os.Remove(addr) + ln.Addr() + ln.SetDeadline(time.Now().Add(30 * time.Nanosecond)) + + if c, err := ln.Accept(); err != nil { + if !err.(Error).Timeout() { + t.Fatalf("UnixListener.Accept failed: %v", err) + } + } else { + c.Close() + } + if c, err := ln.AcceptUnix(); err != nil { + if !err.(Error).Timeout() { + t.Fatalf("UnixListener.AcceptUnix failed: %v", err) + } + } else { + c.Close() + } + + if f, err := ln.File(); err != nil { + t.Fatalf("UnixListener.File failed: %v", err) + } else { + f.Close() + } +} + +func TestUnixConnSpecificMethods(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + addr1, addr2, addr3 := testUnixAddr(), testUnixAddr(), testUnixAddr() + + a1, err := ResolveUnixAddr("unixgram", addr1) + if err != nil { + t.Fatalf("ResolveUnixAddr failed: %v", err) + } + c1, err := DialUnix("unixgram", a1, nil) + if err != nil { + t.Fatalf("DialUnix failed: %v", err) + } + defer c1.Close() + defer os.Remove(addr1) + c1.LocalAddr() + c1.RemoteAddr() + c1.SetDeadline(time.Now().Add(someTimeout)) + c1.SetReadDeadline(time.Now().Add(someTimeout)) + c1.SetWriteDeadline(time.Now().Add(someTimeout)) + c1.SetReadBuffer(2048) + c1.SetWriteBuffer(2048) + + a2, err := ResolveUnixAddr("unixgram", addr2) + if err != nil { + t.Fatalf("ResolveUnixAddr failed: %v", err) + } + c2, err := DialUnix("unixgram", a2, nil) + if err != nil { + t.Fatalf("DialUnix failed: %v", err) + } + defer c2.Close() + defer os.Remove(addr2) + c2.LocalAddr() + c2.RemoteAddr() + c2.SetDeadline(time.Now().Add(someTimeout)) + c2.SetReadDeadline(time.Now().Add(someTimeout)) + c2.SetWriteDeadline(time.Now().Add(someTimeout)) + c2.SetReadBuffer(2048) + c2.SetWriteBuffer(2048) + + a3, err := ResolveUnixAddr("unixgram", addr3) + if err != nil { + t.Fatalf("ResolveUnixAddr failed: %v", err) + } + c3, err := ListenUnixgram("unixgram", a3) + if err != nil { + t.Fatalf("ListenUnixgram failed: %v", err) + } + defer c3.Close() + defer os.Remove(addr3) + c3.LocalAddr() + c3.RemoteAddr() + c3.SetDeadline(time.Now().Add(someTimeout)) + c3.SetReadDeadline(time.Now().Add(someTimeout)) + c3.SetWriteDeadline(time.Now().Add(someTimeout)) + c3.SetReadBuffer(2048) + c3.SetWriteBuffer(2048) + + wb := []byte("UNIXCONN TEST") + rb1 := make([]byte, 128) + rb2 := make([]byte, 128) + rb3 := make([]byte, 128) + if _, _, err := c1.WriteMsgUnix(wb, nil, a2); err != nil { + t.Fatalf("UnixConn.WriteMsgUnix failed: %v", err) + } + if _, _, _, _, err := c2.ReadMsgUnix(rb2, nil); err != nil { + t.Fatalf("UnixConn.ReadMsgUnix failed: %v", err) + } + if _, err := c2.WriteToUnix(wb, a1); err != nil { + t.Fatalf("UnixConn.WriteToUnix failed: %v", err) + } + if _, _, err := c1.ReadFromUnix(rb1); err != nil { + t.Fatalf("UnixConn.ReadFromUnix failed: %v", err) + } + if _, err := c3.WriteToUnix(wb, a1); err != nil { + t.Fatalf("UnixConn.WriteToUnix failed: %v", err) + } + if _, _, err := c1.ReadFromUnix(rb1); err != nil { + t.Fatalf("UnixConn.ReadFromUnix failed: %v", err) + } + if _, err := c2.WriteToUnix(wb, a3); err != nil { + t.Fatalf("UnixConn.WriteToUnix failed: %v", err) + } + if _, _, err := c3.ReadFromUnix(rb3); err != nil { + t.Fatalf("UnixConn.ReadFromUnix failed: %v", err) + } + + if f, err := c1.File(); err != nil { + t.Fatalf("UnixConn.File failed: %v", err) + } else { + f.Close() + } +} diff --git a/src/pkg/net/rpc/client.go b/src/pkg/net/rpc/client.go index db2da8e44..4b0c9c3bb 100644 --- a/src/pkg/net/rpc/client.go +++ b/src/pkg/net/rpc/client.go @@ -71,7 +71,7 @@ func (client *Client) send(call *Call) { // Register this call. client.mutex.Lock() - if client.shutdown { + if client.shutdown || client.closing { call.Error = ErrShutdown client.mutex.Unlock() call.done() @@ -88,10 +88,13 @@ func (client *Client) send(call *Call) { err := client.codec.WriteRequest(&client.request, call.Args) if err != nil { client.mutex.Lock() + call = client.pending[seq] delete(client.pending, seq) client.mutex.Unlock() - call.Error = err - call.done() + if call != nil { + call.Error = err + call.done() + } } } @@ -102,9 +105,6 @@ func (client *Client) input() { response = Response{} err = client.codec.ReadResponseHeader(&response) if err != nil { - if err == io.EOF && !client.closing { - err = io.ErrUnexpectedEOF - } break } seq := response.Seq @@ -113,12 +113,18 @@ func (client *Client) input() { delete(client.pending, seq) client.mutex.Unlock() - if response.Error == "" { - err = client.codec.ReadResponseBody(call.Reply) + switch { + case call == nil: + // We've got no pending call. That usually means that + // WriteRequest partially failed, and call was already + // removed; response is a server telling us about an + // error reading request body. We should still attempt + // to read error body, but there's no one to give it to. + err = client.codec.ReadResponseBody(nil) if err != nil { - call.Error = errors.New("reading body " + err.Error()) + err = errors.New("reading error body: " + err.Error()) } - } else { + case response.Error != "": // We've got an error response. Give this to the request; // any subsequent requests will get the ReadResponseBody // error if there is one. @@ -127,14 +133,27 @@ func (client *Client) input() { if err != nil { err = errors.New("reading error body: " + err.Error()) } + call.done() + default: + err = client.codec.ReadResponseBody(call.Reply) + if err != nil { + call.Error = errors.New("reading body " + err.Error()) + } + call.done() } - call.done() } // Terminate pending calls. client.sending.Lock() client.mutex.Lock() client.shutdown = true closing := client.closing + if err == io.EOF { + if closing { + err = ErrShutdown + } else { + err = io.ErrUnexpectedEOF + } + } for _, call := range client.pending { call.Error = err call.done() @@ -213,7 +232,7 @@ func DialHTTP(network, address string) (*Client, error) { return DialHTTPPath(network, address, DefaultRPCPath) } -// DialHTTPPath connects to an HTTP RPC server +// DialHTTPPath connects to an HTTP RPC server // at the specified network address and path. func DialHTTPPath(network, address, path string) (*Client, error) { var err error diff --git a/src/pkg/net/rpc/jsonrpc/all_test.go b/src/pkg/net/rpc/jsonrpc/all_test.go index e6c7441f0..3c7c4d48f 100644 --- a/src/pkg/net/rpc/jsonrpc/all_test.go +++ b/src/pkg/net/rpc/jsonrpc/all_test.go @@ -24,6 +24,12 @@ type Reply struct { type Arith int +type ArithAddResp struct { + Id interface{} `json:"id"` + Result Reply `json:"result"` + Error interface{} `json:"error"` +} + func (t *Arith) Add(args *Args, reply *Reply) error { reply.C = args.A + args.B return nil @@ -50,13 +56,39 @@ func init() { rpc.Register(new(Arith)) } -func TestServer(t *testing.T) { - type addResp struct { - Id interface{} `json:"id"` - Result Reply `json:"result"` - Error interface{} `json:"error"` +func TestServerNoParams(t *testing.T) { + cli, srv := net.Pipe() + defer cli.Close() + go ServeConn(srv) + dec := json.NewDecoder(cli) + + fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "123"}`) + var resp ArithAddResp + if err := dec.Decode(&resp); err != nil { + t.Fatalf("Decode after no params: %s", err) + } + if resp.Error == nil { + t.Fatalf("Expected error, got nil") + } +} + +func TestServerEmptyMessage(t *testing.T) { + cli, srv := net.Pipe() + defer cli.Close() + go ServeConn(srv) + dec := json.NewDecoder(cli) + + fmt.Fprintf(cli, "{}") + var resp ArithAddResp + if err := dec.Decode(&resp); err != nil { + t.Fatalf("Decode after empty: %s", err) } + if resp.Error == nil { + t.Fatalf("Expected error, got nil") + } +} +func TestServer(t *testing.T) { cli, srv := net.Pipe() defer cli.Close() go ServeConn(srv) @@ -65,7 +97,7 @@ func TestServer(t *testing.T) { // Send hand-coded requests to server, parse responses. for i := 0; i < 10; i++ { fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "\u%04d", "params": [{"A": %d, "B": %d}]}`, i, i, i+1) - var resp addResp + var resp ArithAddResp err := dec.Decode(&resp) if err != nil { t.Fatalf("Decode: %s", err) @@ -80,15 +112,6 @@ func TestServer(t *testing.T) { t.Fatalf("resp: bad result: %d+%d=%d", i, i+1, resp.Result.C) } } - - fmt.Fprintf(cli, "{}\n") - var resp addResp - if err := dec.Decode(&resp); err != nil { - t.Fatalf("Decode after empty: %s", err) - } - if resp.Error == nil { - t.Fatalf("Expected error, got nil") - } } func TestClient(t *testing.T) { @@ -108,7 +131,7 @@ func TestClient(t *testing.T) { t.Errorf("Add: expected no error but got string %q", err.Error()) } if reply.C != args.A+args.B { - t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B) + t.Errorf("Add: got %d expected %d", reply.C, args.A+args.B) } args = &Args{7, 8} @@ -118,7 +141,7 @@ func TestClient(t *testing.T) { t.Errorf("Mul: expected no error but got string %q", err.Error()) } if reply.C != args.A*args.B { - t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B) + t.Errorf("Mul: got %d expected %d", reply.C, args.A*args.B) } // Out of order. @@ -133,7 +156,7 @@ func TestClient(t *testing.T) { t.Errorf("Add: expected no error but got string %q", addCall.Error.Error()) } if addReply.C != args.A+args.B { - t.Errorf("Add: expected %d got %d", addReply.C, args.A+args.B) + t.Errorf("Add: got %d expected %d", addReply.C, args.A+args.B) } mulCall = <-mulCall.Done @@ -141,7 +164,7 @@ func TestClient(t *testing.T) { t.Errorf("Mul: expected no error but got string %q", mulCall.Error.Error()) } if mulReply.C != args.A*args.B { - t.Errorf("Mul: expected %d got %d", mulReply.C, args.A*args.B) + t.Errorf("Mul: got %d expected %d", mulReply.C, args.A*args.B) } // Error test diff --git a/src/pkg/net/rpc/jsonrpc/server.go b/src/pkg/net/rpc/jsonrpc/server.go index 4c54553a7..5bc05fd0a 100644 --- a/src/pkg/net/rpc/jsonrpc/server.go +++ b/src/pkg/net/rpc/jsonrpc/server.go @@ -12,6 +12,8 @@ import ( "sync" ) +var errMissingParams = errors.New("jsonrpc: request body missing params") + type serverCodec struct { dec *json.Decoder // for reading JSON values enc *json.Encoder // for writing JSON values @@ -50,12 +52,8 @@ type serverRequest struct { func (r *serverRequest) reset() { r.Method = "" - if r.Params != nil { - *r.Params = (*r.Params)[0:0] - } - if r.Id != nil { - *r.Id = (*r.Id)[0:0] - } + r.Params = nil + r.Id = nil } type serverResponse struct { @@ -88,6 +86,9 @@ func (c *serverCodec) ReadRequestBody(x interface{}) error { if x == nil { return nil } + if c.req.Params == nil { + return errMissingParams + } // JSON params is array value. // RPC params is struct. // Unmarshal into array containing struct for now. diff --git a/src/pkg/net/rpc/server.go b/src/pkg/net/rpc/server.go index 1680e2f0d..e71b6fb1a 100644 --- a/src/pkg/net/rpc/server.go +++ b/src/pkg/net/rpc/server.go @@ -24,12 +24,13 @@ where T, T1 and T2 can be marshaled by encoding/gob. These requirements apply even if a different codec is used. - (In future, these requirements may soften for custom codecs.) + (In the future, these requirements may soften for custom codecs.) The method's first argument represents the arguments provided by the caller; the second argument represents the result parameters to be returned to the caller. The method's return value, if non-nil, is passed back as a string that the client - sees as if created by errors.New. + sees as if created by errors.New. If an error is returned, the reply parameter + will not be sent back to the client. The server may handle requests on a single connection by calling ServeConn. More typically it will create a network listener and call Accept or, for an HTTP @@ -111,7 +112,7 @@ // Asynchronous call quotient := new(Quotient) - divCall := client.Go("Arith.Divide", args, "ient, nil) + divCall := client.Go("Arith.Divide", args, quotient, nil) replyCall := <-divCall.Done // will be equal to divCall // check errors, print, etc. @@ -181,7 +182,7 @@ type Response struct { // Server represents an RPC Server. type Server struct { - mu sync.Mutex // protects the serviceMap + mu sync.RWMutex // protects the serviceMap serviceMap map[string]*service reqLock sync.Mutex // protects freeReq freeReq *Request @@ -218,15 +219,15 @@ func isExportedOrBuiltinType(t reflect.Type) bool { // - exported method // - two arguments, both pointers to exported structs // - one return value, of type error -// It returns an error if the receiver is not an exported type or has no -// suitable methods. +// It returns an error if the receiver is not an exported type or has +// no methods or unsuitable methods. It also logs the error using package log. // The client accesses each method using a string of the form "Type.Method", // where Type is the receiver's concrete type. func (server *Server) Register(rcvr interface{}) error { return server.register(rcvr, "", false) } -// RegisterName is like Register but uses the provided name for the type +// RegisterName is like Register but uses the provided name for the type // instead of the receiver's concrete type. func (server *Server) RegisterName(name string, rcvr interface{}) error { return server.register(rcvr, name, true) @@ -260,8 +261,30 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro s.method = make(map[string]*methodType) // Install the methods - for m := 0; m < s.typ.NumMethod(); m++ { - method := s.typ.Method(m) + s.method = suitableMethods(s.typ, true) + + if len(s.method) == 0 { + str := "" + // To help the user, see if a pointer receiver would work. + method := suitableMethods(reflect.PtrTo(s.typ), false) + if len(method) != 0 { + str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)" + } else { + str = "rpc.Register: type " + sname + " has no exported methods of suitable type" + } + log.Print(str) + return errors.New(str) + } + server.serviceMap[s.name] = s + return nil +} + +// suitableMethods returns suitable Rpc methods of typ, it will report +// error using log if reportErr is true. +func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { + methods := make(map[string]*methodType) + for m := 0; m < typ.NumMethod(); m++ { + method := typ.Method(m) mtype := method.Type mname := method.Name // Method must be exported. @@ -270,46 +293,51 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro } // Method needs three ins: receiver, *args, *reply. if mtype.NumIn() != 3 { - log.Println("method", mname, "has wrong number of ins:", mtype.NumIn()) + if reportErr { + log.Println("method", mname, "has wrong number of ins:", mtype.NumIn()) + } continue } // First arg need not be a pointer. argType := mtype.In(1) if !isExportedOrBuiltinType(argType) { - log.Println(mname, "argument type not exported:", argType) + if reportErr { + log.Println(mname, "argument type not exported:", argType) + } continue } // Second arg must be a pointer. replyType := mtype.In(2) if replyType.Kind() != reflect.Ptr { - log.Println("method", mname, "reply type not a pointer:", replyType) + if reportErr { + log.Println("method", mname, "reply type not a pointer:", replyType) + } continue } // Reply type must be exported. if !isExportedOrBuiltinType(replyType) { - log.Println("method", mname, "reply type not exported:", replyType) + if reportErr { + log.Println("method", mname, "reply type not exported:", replyType) + } continue } // Method needs one out. if mtype.NumOut() != 1 { - log.Println("method", mname, "has wrong number of outs:", mtype.NumOut()) + if reportErr { + log.Println("method", mname, "has wrong number of outs:", mtype.NumOut()) + } continue } // The return type of the method must be error. if returnType := mtype.Out(0); returnType != typeOfError { - log.Println("method", mname, "returns", returnType.String(), "not error") + if reportErr { + log.Println("method", mname, "returns", returnType.String(), "not error") + } continue } - s.method[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType} + methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType} } - - if len(s.method) == 0 { - s := "rpc Register: type " + sname + " has no exported methods of suitable type" - log.Print(s) - return errors.New(s) - } - server.serviceMap[s.name] = s - return nil + return methods } // A value sent as a placeholder for the server's response value when the server @@ -538,9 +566,9 @@ func (server *Server) readRequestHeader(codec ServerCodec) (service *service, mt return } // Look up the request. - server.mu.Lock() + server.mu.RLock() service = server.serviceMap[serviceMethod[0]] - server.mu.Unlock() + server.mu.RUnlock() if service == nil { err = errors.New("rpc: can't find service " + req.ServiceMethod) return @@ -568,7 +596,7 @@ func (server *Server) Accept(lis net.Listener) { // Register publishes the receiver's methods in the DefaultServer. func Register(rcvr interface{}) error { return DefaultServer.Register(rcvr) } -// RegisterName is like Register but uses the provided name for the type +// RegisterName is like Register but uses the provided name for the type // instead of the receiver's concrete type. func RegisterName(name string, rcvr interface{}) error { return DefaultServer.RegisterName(name, rcvr) @@ -611,7 +639,7 @@ func ServeRequest(codec ServerCodec) error { } // Accept accepts connections on the listener and serves requests -// to DefaultServer for each incoming connection. +// to DefaultServer for each incoming connection. // Accept blocks; the caller typically invokes it in a go statement. func Accept(lis net.Listener) { DefaultServer.Accept(lis) } diff --git a/src/pkg/net/rpc/server_test.go b/src/pkg/net/rpc/server_test.go index 62c7b1e60..8a1530623 100644 --- a/src/pkg/net/rpc/server_test.go +++ b/src/pkg/net/rpc/server_test.go @@ -349,6 +349,7 @@ func testServeRequest(t *testing.T, server *Server) { type ReplyNotPointer int type ArgNotPublic int type ReplyNotPublic int +type NeedsPtrType int type local struct{} func (t *ReplyNotPointer) ReplyNotPointer(args *Args, reply Reply) error { @@ -363,19 +364,29 @@ func (t *ReplyNotPublic) ReplyNotPublic(args *Args, reply *local) error { return nil } +func (t *NeedsPtrType) NeedsPtrType(args *Args, reply *Reply) error { + return nil +} + // Check that registration handles lots of bad methods and a type with no suitable methods. func TestRegistrationError(t *testing.T) { err := Register(new(ReplyNotPointer)) if err == nil { - t.Errorf("expected error registering ReplyNotPointer") + t.Error("expected error registering ReplyNotPointer") } err = Register(new(ArgNotPublic)) if err == nil { - t.Errorf("expected error registering ArgNotPublic") + t.Error("expected error registering ArgNotPublic") } err = Register(new(ReplyNotPublic)) if err == nil { - t.Errorf("expected error registering ReplyNotPublic") + t.Error("expected error registering ReplyNotPublic") + } + err = Register(NeedsPtrType(0)) + if err == nil { + t.Error("expected error registering NeedsPtrType") + } else if !strings.Contains(err.Error(), "pointer") { + t.Error("expected hint when registering NeedsPtrType") } } @@ -434,7 +445,7 @@ func dialHTTP() (*Client, error) { return DialHTTP("tcp", httpServerAddr) } -func countMallocs(dial func() (*Client, error), t *testing.T) uint64 { +func countMallocs(dial func() (*Client, error), t *testing.T) float64 { once.Do(startServer) client, err := dial() if err != nil { @@ -442,11 +453,7 @@ func countMallocs(dial func() (*Client, error), t *testing.T) uint64 { } args := &Args{7, 8} reply := new(Reply) - memstats := new(runtime.MemStats) - runtime.ReadMemStats(memstats) - mallocs := 0 - memstats.Mallocs - const count = 100 - for i := 0; i < count; i++ { + return testing.AllocsPerRun(100, func() { err := client.Call("Arith.Add", args, reply) if err != nil { t.Errorf("Add: expected no error but got string %q", err.Error()) @@ -454,18 +461,15 @@ func countMallocs(dial func() (*Client, error), t *testing.T) uint64 { if reply.C != args.A+args.B { t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B) } - } - runtime.ReadMemStats(memstats) - mallocs += memstats.Mallocs - return mallocs / count + }) } func TestCountMallocs(t *testing.T) { - fmt.Printf("mallocs per rpc round trip: %d\n", countMallocs(dialDirect, t)) + fmt.Printf("mallocs per rpc round trip: %v\n", countMallocs(dialDirect, t)) } func TestCountMallocsOverHTTP(t *testing.T) { - fmt.Printf("mallocs per HTTP rpc round trip: %d\n", countMallocs(dialHTTP, t)) + fmt.Printf("mallocs per HTTP rpc round trip: %v\n", countMallocs(dialHTTP, t)) } type writeCrasher struct { @@ -499,6 +503,44 @@ func TestClientWriteError(t *testing.T) { w.done <- true } +func TestTCPClose(t *testing.T) { + once.Do(startServer) + + client, err := dialHTTP() + if err != nil { + t.Fatalf("dialing: %v", err) + } + defer client.Close() + + args := Args{17, 8} + var reply Reply + err = client.Call("Arith.Mul", args, &reply) + if err != nil { + t.Fatal("arith error:", err) + } + t.Logf("Arith: %d*%d=%d\n", args.A, args.B, reply) + if reply.C != args.A*args.B { + t.Errorf("Add: expected %d got %d", reply.C, args.A*args.B) + } +} + +func TestErrorAfterClientClose(t *testing.T) { + once.Do(startServer) + + client, err := dialHTTP() + if err != nil { + t.Fatalf("dialing: %v", err) + } + err = client.Close() + if err != nil { + t.Fatal("close error:", err) + } + err = client.Call("Arith.Add", &Args{7, 9}, new(Reply)) + if err != ErrShutdown { + t.Errorf("Forever: expected ErrShutdown got %v", err) + } +} + func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) { b.StopTimer() once.Do(startServer) diff --git a/src/pkg/net/sendfile_freebsd.go b/src/pkg/net/sendfile_freebsd.go new file mode 100644 index 000000000..8008bc3b5 --- /dev/null +++ b/src/pkg/net/sendfile_freebsd.go @@ -0,0 +1,105 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "io" + "os" + "syscall" +) + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// sendFile copies the contents of r to c using the sendfile +// system call to minimize copies. +// +// if handled == true, sendFile returns the number of bytes copied and any +// non-EOF error. +// +// if handled == false, sendFile performed no work. +func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { + // FreeBSD uses 0 as the "until EOF" value. If you pass in more bytes than the + // file contains, it will loop back to the beginning ad nauseum until it's sent + // exactly the number of bytes told to. As such, we need to know exactly how many + // bytes to send. + var remain int64 = 0 + + lr, ok := r.(*io.LimitedReader) + if ok { + remain, r = lr.N, lr.R + if remain <= 0 { + return 0, nil, true + } + } + f, ok := r.(*os.File) + if !ok { + return 0, nil, false + } + + if remain == 0 { + fi, err := f.Stat() + if err != nil { + return 0, err, false + } + + remain = fi.Size() + } + + // The other quirk with FreeBSD's sendfile implementation is that it doesn't + // use the current position of the file -- if you pass it offset 0, it starts + // from offset 0. There's no way to tell it "start from current position", so + // we have to manage that explicitly. + pos, err := f.Seek(0, os.SEEK_CUR) + if err != nil { + return 0, err, false + } + + c.wio.Lock() + defer c.wio.Unlock() + if err := c.incref(false); err != nil { + return 0, err, true + } + defer c.decref() + + dst := c.sysfd + src := int(f.Fd()) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + pos1 := pos + n, err1 := syscall.Sendfile(dst, src, &pos1, n) + if n > 0 { + pos += int64(n) + written += int64(n) + remain -= int64(n) + } + if n == 0 && err1 == nil { + break + } + if err1 == syscall.EAGAIN { + if err1 = c.pollServer.WaitWrite(c); err1 == nil { + continue + } + } + if err1 == syscall.EINTR { + continue + } + if err1 != nil { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile together) + err = &OpError{"sendfile", c.net, c.raddr, err1} + break + } + } + if lr != nil { + lr.N = remain + } + return written, err, written > 0 +} diff --git a/src/pkg/net/sendfile_linux.go b/src/pkg/net/sendfile_linux.go index a0d530362..3357e6538 100644 --- a/src/pkg/net/sendfile_linux.go +++ b/src/pkg/net/sendfile_linux.go @@ -58,8 +58,8 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { if n == 0 && err1 == nil { break } - if err1 == syscall.EAGAIN && c.wdeadline >= 0 { - if err1 = pollserver.WaitWrite(c); err1 == nil { + if err1 == syscall.EAGAIN { + if err1 = c.pollServer.WaitWrite(c); err1 == nil { continue } } diff --git a/src/pkg/net/sendfile_stub.go b/src/pkg/net/sendfile_stub.go index ff76ab9cf..3660849c1 100644 --- a/src/pkg/net/sendfile_stub.go +++ b/src/pkg/net/sendfile_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd netbsd openbsd +// +build darwin netbsd openbsd package net diff --git a/src/pkg/net/sendfile_windows.go b/src/pkg/net/sendfile_windows.go index f5a6d8804..2d64f2f5b 100644 --- a/src/pkg/net/sendfile_windows.go +++ b/src/pkg/net/sendfile_windows.go @@ -48,12 +48,12 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { return 0, nil, false } - c.wio.Lock() - defer c.wio.Unlock() if err := c.incref(false); err != nil { return 0, err, true } defer c.decref() + c.wio.Lock() + defer c.wio.Unlock() var o sendfileOp o.Init(c, 'w') diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go index 158b9477d..25c2be5a7 100644 --- a/src/pkg/net/server_test.go +++ b/src/pkg/net/server_test.go @@ -113,8 +113,7 @@ func TestStreamConnServer(t *testing.T) { case "tcp", "tcp4", "tcp6": _, port, err := SplitHostPort(taddr) if err != nil { - t.Errorf("SplitHostPort(%q) failed: %v", taddr, err) - return + t.Fatalf("SplitHostPort(%q) failed: %v", taddr, err) } taddr = tt.caddr + ":" + port } @@ -142,8 +141,7 @@ var seqpacketConnServerTests = []struct { func TestSeqpacketConnServer(t *testing.T) { if runtime.GOOS != "linux" { - t.Logf("skipping test on %q", runtime.GOOS) - return + t.Skipf("skipping test on %q", runtime.GOOS) } for _, tt := range seqpacketConnServerTests { @@ -170,11 +168,11 @@ func TestSeqpacketConnServer(t *testing.T) { } func runStreamConnServer(t *testing.T, net, laddr string, listening chan<- string, done chan<- int) { + defer close(done) l, err := Listen(net, laddr) if err != nil { t.Errorf("Listen(%q, %q) failed: %v", net, laddr, err) listening <- "" - done <- 1 return } defer l.Close() @@ -189,13 +187,14 @@ func runStreamConnServer(t *testing.T, net, laddr string, listening chan<- strin } rw.Write(buf[0:n]) } - done <- 1 + close(done) } run: for { c, err := l.Accept() if err != nil { + t.Logf("Accept failed: %v", err) continue run } echodone := make(chan int) @@ -204,14 +203,12 @@ run: c.Close() break run } - done <- 1 } func runStreamConnClient(t *testing.T, net, taddr string, isEmpty bool) { c, err := Dial(net, taddr) if err != nil { - t.Errorf("Dial(%q, %q) failed: %v", net, taddr, err) - return + t.Fatalf("Dial(%q, %q) failed: %v", net, taddr, err) } defer c.Close() c.SetReadDeadline(time.Now().Add(1 * time.Second)) @@ -221,14 +218,12 @@ func runStreamConnClient(t *testing.T, net, taddr string, isEmpty bool) { wb = []byte("StreamConnClient by Dial\n") } if n, err := c.Write(wb); err != nil || n != len(wb) { - t.Errorf("Write failed: %v, %v; want %v, ", n, err, len(wb)) - return + t.Fatalf("Write failed: %v, %v; want %v, ", n, err, len(wb)) } rb := make([]byte, 1024) if n, err := c.Read(rb[0:]); err != nil || n != len(wb) { - t.Errorf("Read failed: %v, %v; want %v, ", n, err, len(wb)) - return + t.Fatalf("Read failed: %v, %v; want %v, ", n, err, len(wb)) } // Send explicit ending for unixpacket. @@ -334,8 +329,7 @@ func TestDatagramPacketConnServer(t *testing.T) { case "udp", "udp4", "udp6": _, port, err := SplitHostPort(taddr) if err != nil { - t.Errorf("SplitHostPort(%q) failed: %v", taddr, err) - return + t.Fatalf("SplitHostPort(%q) failed: %v", taddr, err) } taddr = tt.caddr + ":" + port tt.caddr += ":0" @@ -398,14 +392,12 @@ func runDatagramConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool) case "udp", "udp4", "udp6": c, err = Dial(net, taddr) if err != nil { - t.Errorf("Dial(%q, %q) failed: %v", net, taddr, err) - return + t.Fatalf("Dial(%q, %q) failed: %v", net, taddr, err) } case "unixgram": c, err = DialUnix(net, &UnixAddr{laddr, net}, &UnixAddr{taddr, net}) if err != nil { - t.Errorf("DialUnix(%q, {%q, %q}) failed: %v", net, laddr, taddr, err) - return + t.Fatalf("DialUnix(%q, {%q, %q}) failed: %v", net, laddr, taddr, err) } } defer c.Close() @@ -416,14 +408,12 @@ func runDatagramConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool) wb = []byte("DatagramConnClient by Dial\n") } if n, err := c.Write(wb[0:]); err != nil || n != len(wb) { - t.Errorf("Write failed: %v, %v; want %v, ", n, err, len(wb)) - return + t.Fatalf("Write failed: %v, %v; want %v, ", n, err, len(wb)) } rb := make([]byte, 1024) if n, err := c.Read(rb[0:]); err != nil || n != len(wb) { - t.Errorf("Read failed: %v, %v; want %v, ", n, err, len(wb)) - return + t.Fatalf("Read failed: %v, %v; want %v, ", n, err, len(wb)) } } @@ -434,20 +424,17 @@ func runDatagramPacketConnClient(t *testing.T, net, laddr, taddr string, isEmpty case "udp", "udp4", "udp6": ra, err = ResolveUDPAddr(net, taddr) if err != nil { - t.Errorf("ResolveUDPAddr(%q, %q) failed: %v", net, taddr, err) - return + t.Fatalf("ResolveUDPAddr(%q, %q) failed: %v", net, taddr, err) } case "unixgram": ra, err = ResolveUnixAddr(net, taddr) if err != nil { - t.Errorf("ResolveUxixAddr(%q, %q) failed: %v", net, taddr, err) - return + t.Fatalf("ResolveUxixAddr(%q, %q) failed: %v", net, taddr, err) } } c, err := ListenPacket(net, laddr) if err != nil { - t.Errorf("ListenPacket(%q, %q) faild: %v", net, laddr, err) - return + t.Fatalf("ListenPacket(%q, %q) faild: %v", net, laddr, err) } defer c.Close() c.SetReadDeadline(time.Now().Add(1 * time.Second)) @@ -457,13 +444,11 @@ func runDatagramPacketConnClient(t *testing.T, net, laddr, taddr string, isEmpty wb = []byte("DatagramPacketConnClient by ListenPacket\n") } if n, err := c.WriteTo(wb[0:], ra); err != nil || n != len(wb) { - t.Errorf("WriteTo(%v) failed: %v, %v; want %v, ", ra, n, err, len(wb)) - return + t.Fatalf("WriteTo(%v) failed: %v, %v; want %v, ", ra, n, err, len(wb)) } rb := make([]byte, 1024) if n, _, err := c.ReadFrom(rb[0:]); err != nil || n != len(wb) { - t.Errorf("ReadFrom failed: %v, %v; want %v, ", n, err, len(wb)) - return + t.Fatalf("ReadFrom failed: %v, %v; want %v, ", n, err, len(wb)) } } diff --git a/src/pkg/net/smtp/smtp.go b/src/pkg/net/smtp/smtp.go index 59f6449f0..4b9177877 100644 --- a/src/pkg/net/smtp/smtp.go +++ b/src/pkg/net/smtp/smtp.go @@ -13,6 +13,7 @@ package smtp import ( "crypto/tls" "encoding/base64" + "errors" "io" "net" "net/textproto" @@ -33,7 +34,10 @@ type Client struct { // map of supported extensions ext map[string]string // supported auth mechanisms - auth []string + auth []string + localName string // the name to use in HELO/EHLO + didHello bool // whether we've said HELO/EHLO + helloError error // the error from the hello } // Dial returns a new Client connected to an SMTP server at addr. @@ -55,12 +59,33 @@ func NewClient(conn net.Conn, host string) (*Client, error) { text.Close() return nil, err } - c := &Client{Text: text, conn: conn, serverName: host} - err = c.ehlo() - if err != nil { - err = c.helo() + c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"} + return c, nil +} + +// hello runs a hello exchange if needed. +func (c *Client) hello() error { + if !c.didHello { + c.didHello = true + err := c.ehlo() + if err != nil { + c.helloError = c.helo() + } + } + return c.helloError +} + +// Hello sends a HELO or EHLO to the server as the given host name. +// Calling this method is only necessary if the client needs control +// over the host name used. The client will introduce itself as "localhost" +// automatically otherwise. If Hello is called, it must be called before +// any of the other methods. +func (c *Client) Hello(localName string) error { + if c.didHello { + return errors.New("smtp: Hello called after other methods") } - return c, err + c.localName = localName + return c.hello() } // cmd is a convenience function that sends a command and returns the response @@ -79,14 +104,14 @@ func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, s // server does not support ehlo. func (c *Client) helo() error { c.ext = nil - _, _, err := c.cmd(250, "HELO localhost") + _, _, err := c.cmd(250, "HELO %s", c.localName) return err } // ehlo sends the EHLO (extended hello) greeting to the server. It // should be the preferred greeting for servers that support it. func (c *Client) ehlo() error { - _, msg, err := c.cmd(250, "EHLO localhost") + _, msg, err := c.cmd(250, "EHLO %s", c.localName) if err != nil { return err } @@ -113,6 +138,9 @@ func (c *Client) ehlo() error { // StartTLS sends the STARTTLS command and encrypts all further communication. // Only servers that advertise the STARTTLS extension support this function. func (c *Client) StartTLS(config *tls.Config) error { + if err := c.hello(); err != nil { + return err + } _, _, err := c.cmd(220, "STARTTLS") if err != nil { return err @@ -128,6 +156,9 @@ func (c *Client) StartTLS(config *tls.Config) error { // does not necessarily indicate an invalid address. Many servers // will not verify addresses for security reasons. func (c *Client) Verify(addr string) error { + if err := c.hello(); err != nil { + return err + } _, _, err := c.cmd(250, "VRFY %s", addr) return err } @@ -136,6 +167,9 @@ func (c *Client) Verify(addr string) error { // A failed authentication closes the connection. // Only servers that advertise the AUTH extension support this function. func (c *Client) Auth(a Auth) error { + if err := c.hello(); err != nil { + return err + } encoding := base64.StdEncoding mech, resp, err := a.Start(&ServerInfo{c.serverName, c.tls, c.auth}) if err != nil { @@ -178,6 +212,9 @@ func (c *Client) Auth(a Auth) error { // parameter. // This initiates a mail transaction and is followed by one or more Rcpt calls. func (c *Client) Mail(from string) error { + if err := c.hello(); err != nil { + return err + } cmdStr := "MAIL FROM:<%s>" if c.ext != nil { if _, ok := c.ext["8BITMIME"]; ok { @@ -227,6 +264,9 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error { if err != nil { return err } + if err := c.hello(); err != nil { + return err + } if ok, _ := c.Extension("STARTTLS"); ok { if err = c.StartTLS(nil); err != nil { return err @@ -267,6 +307,9 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error { // Extension also returns a string that contains any parameters the // server specifies for the extension. func (c *Client) Extension(ext string) (bool, string) { + if err := c.hello(); err != nil { + return false, "" + } if c.ext == nil { return false, "" } @@ -278,12 +321,18 @@ func (c *Client) Extension(ext string) (bool, string) { // Reset sends the RSET command to the server, aborting the current mail // transaction. func (c *Client) Reset() error { + if err := c.hello(); err != nil { + return err + } _, _, err := c.cmd(250, "RSET") return err } // Quit sends the QUIT command and closes the connection to the server. func (c *Client) Quit() error { + if err := c.hello(); err != nil { + return err + } _, _, err := c.cmd(221, "QUIT") if err != nil { return err diff --git a/src/pkg/net/smtp/smtp_test.go b/src/pkg/net/smtp/smtp_test.go index c315d185c..8317428cb 100644 --- a/src/pkg/net/smtp/smtp_test.go +++ b/src/pkg/net/smtp/smtp_test.go @@ -69,14 +69,14 @@ func (f faker) SetReadDeadline(time.Time) error { return nil } func (f faker) SetWriteDeadline(time.Time) error { return nil } func TestBasic(t *testing.T) { - basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n") - basicClient = strings.Join(strings.Split(basicClient, "\n"), "\r\n") + server := strings.Join(strings.Split(basicServer, "\n"), "\r\n") + client := strings.Join(strings.Split(basicClient, "\n"), "\r\n") var cmdbuf bytes.Buffer bcmdbuf := bufio.NewWriter(&cmdbuf) var fake faker - fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(basicServer)), bcmdbuf) - c := &Client{Text: textproto.NewConn(fake)} + fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) + c := &Client{Text: textproto.NewConn(fake), localName: "localhost"} if err := c.helo(); err != nil { t.Fatalf("HELO failed: %s", err) @@ -88,6 +88,7 @@ func TestBasic(t *testing.T) { t.Fatalf("Second EHLO failed: %s", err) } + c.didHello = true if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" { t.Fatalf("Expected AUTH supported") } @@ -143,8 +144,8 @@ Goodbye.` bcmdbuf.Flush() actualcmds := cmdbuf.String() - if basicClient != actualcmds { - t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, basicClient) + if client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) } } @@ -187,8 +188,8 @@ QUIT ` func TestNewClient(t *testing.T) { - newClientServer = strings.Join(strings.Split(newClientServer, "\n"), "\r\n") - newClientClient = strings.Join(strings.Split(newClientClient, "\n"), "\r\n") + server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n") + client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n") var cmdbuf bytes.Buffer bcmdbuf := bufio.NewWriter(&cmdbuf) @@ -197,7 +198,7 @@ func TestNewClient(t *testing.T) { return cmdbuf.String() } var fake faker - fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClientServer)), bcmdbuf) + fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) c, err := NewClient(fake, "fake.host") if err != nil { t.Fatalf("NewClient: %v\n(after %v)", err, out()) @@ -213,8 +214,8 @@ func TestNewClient(t *testing.T) { } actualcmds := out() - if newClientClient != actualcmds { - t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClientClient) + if client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) } } @@ -231,13 +232,13 @@ QUIT ` func TestNewClient2(t *testing.T) { - newClient2Server = strings.Join(strings.Split(newClient2Server, "\n"), "\r\n") - newClient2Client = strings.Join(strings.Split(newClient2Client, "\n"), "\r\n") + server := strings.Join(strings.Split(newClient2Server, "\n"), "\r\n") + client := strings.Join(strings.Split(newClient2Client, "\n"), "\r\n") var cmdbuf bytes.Buffer bcmdbuf := bufio.NewWriter(&cmdbuf) var fake faker - fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClient2Server)), bcmdbuf) + fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) c, err := NewClient(fake, "fake.host") if err != nil { t.Fatalf("NewClient: %v", err) @@ -251,8 +252,8 @@ func TestNewClient2(t *testing.T) { bcmdbuf.Flush() actualcmds := cmdbuf.String() - if newClient2Client != actualcmds { - t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClient2Client) + if client != actualcmds { + t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) } } @@ -269,3 +270,199 @@ var newClient2Client = `EHLO localhost HELO localhost QUIT ` + +func TestHello(t *testing.T) { + + if len(helloServer) != len(helloClient) { + t.Fatalf("Hello server and client size mismatch") + } + + for i := 0; i < len(helloServer); i++ { + server := strings.Join(strings.Split(baseHelloServer+helloServer[i], "\n"), "\r\n") + client := strings.Join(strings.Split(baseHelloClient+helloClient[i], "\n"), "\r\n") + var cmdbuf bytes.Buffer + bcmdbuf := bufio.NewWriter(&cmdbuf) + var fake faker + fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) + c, err := NewClient(fake, "fake.host") + if err != nil { + t.Fatalf("NewClient: %v", err) + } + c.localName = "customhost" + err = nil + + switch i { + case 0: + err = c.Hello("customhost") + case 1: + err = c.StartTLS(nil) + if err.Error() == "502 Not implemented" { + err = nil + } + case 2: + err = c.Verify("test@example.com") + case 3: + c.tls = true + c.serverName = "smtp.google.com" + err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com")) + case 4: + err = c.Mail("test@example.com") + case 5: + ok, _ := c.Extension("feature") + if ok { + t.Errorf("Expected FEATURE not to be supported") + } + case 6: + err = c.Reset() + case 7: + err = c.Quit() + case 8: + err = c.Verify("test@example.com") + if err != nil { + err = c.Hello("customhost") + if err != nil { + t.Errorf("Want error, got none") + } + } + default: + t.Fatalf("Unhandled command") + } + + if err != nil { + t.Errorf("Command %d failed: %v", i, err) + } + + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + if client != actualcmds { + t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client) + } + } +} + +var baseHelloServer = `220 hello world +502 EH? +250-mx.google.com at your service +250 FEATURE +` + +var helloServer = []string{ + "", + "502 Not implemented\n", + "250 User is valid\n", + "235 Accepted\n", + "250 Sender ok\n", + "", + "250 Reset ok\n", + "221 Goodbye\n", + "250 Sender ok\n", +} + +var baseHelloClient = `EHLO customhost +HELO customhost +` + +var helloClient = []string{ + "", + "STARTTLS\n", + "VRFY test@example.com\n", + "AUTH PLAIN AHVzZXIAcGFzcw==\n", + "MAIL FROM:\n", + "", + "RSET\n", + "QUIT\n", + "VRFY test@example.com\n", +} + +func TestSendMail(t *testing.T) { + server := strings.Join(strings.Split(sendMailServer, "\n"), "\r\n") + client := strings.Join(strings.Split(sendMailClient, "\n"), "\r\n") + var cmdbuf bytes.Buffer + bcmdbuf := bufio.NewWriter(&cmdbuf) + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Unable to to create listener: %v", err) + } + defer l.Close() + + // prevent data race on bcmdbuf + var done = make(chan struct{}) + go func(data []string) { + + defer close(done) + + conn, err := l.Accept() + if err != nil { + t.Errorf("Accept error: %v", err) + return + } + defer conn.Close() + + tc := textproto.NewConn(conn) + for i := 0; i < len(data) && data[i] != ""; i++ { + tc.PrintfLine(data[i]) + for len(data[i]) >= 4 && data[i][3] == '-' { + i++ + tc.PrintfLine(data[i]) + } + if data[i] == "221 Goodbye" { + return + } + read := false + for !read || data[i] == "354 Go ahead" { + msg, err := tc.ReadLine() + bcmdbuf.Write([]byte(msg + "\r\n")) + read = true + if err != nil { + t.Errorf("Read error: %v", err) + return + } + if data[i] == "354 Go ahead" && msg == "." { + break + } + } + } + }(strings.Split(server, "\r\n")) + + err = SendMail(l.Addr().String(), nil, "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com +To: other@example.com +Subject: SendMail test + +SendMail is working for me. +`, "\n", "\r\n", -1))) + + if err != nil { + t.Errorf("%v", err) + } + + <-done + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + if client != actualcmds { + t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client) + } +} + +var sendMailServer = `220 hello world +502 EH? +250 mx.google.com at your service +250 Sender ok +250 Receiver ok +354 Go ahead +250 Data ok +221 Goodbye +` + +var sendMailClient = `EHLO localhost +HELO localhost +MAIL FROM: +RCPT TO: +DATA +From: test@example.com +To: other@example.com +Subject: SendMail test + +SendMail is working for me. +. +QUIT +` diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go deleted file mode 100644 index 3ae16054e..000000000 --- a/src/pkg/net/sock.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd windows - -// Sockets - -package net - -import ( - "io" - "syscall" -) - -var listenerBacklog = maxListenerBacklog() - -// Generic socket creation. -func socket(net string, f, t, p int, ipv6only bool, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { - // See ../syscall/exec.go for description of ForkLock. - syscall.ForkLock.RLock() - s, err := syscall.Socket(f, t, p) - if err != nil { - syscall.ForkLock.RUnlock() - return nil, err - } - syscall.CloseOnExec(s) - syscall.ForkLock.RUnlock() - - err = setDefaultSockopts(s, f, t, ipv6only) - if err != nil { - closesocket(s) - return nil, err - } - - var bla syscall.Sockaddr - if la != nil { - bla, err = listenerSockaddr(s, f, la, toAddr) - if err != nil { - closesocket(s) - return nil, err - } - err = syscall.Bind(s, bla) - if err != nil { - closesocket(s) - return nil, err - } - } - - if fd, err = newFD(s, f, t, net); err != nil { - closesocket(s) - return nil, err - } - - if ra != nil { - if err = fd.connect(ra); err != nil { - closesocket(s) - fd.Close() - return nil, err - } - fd.isConnected = true - } - - sa, _ := syscall.Getsockname(s) - var laddr Addr - if la != nil && bla != la { - laddr = toAddr(la) - } else { - laddr = toAddr(sa) - } - sa, _ = syscall.Getpeername(s) - raddr := toAddr(sa) - - fd.setAddr(laddr, raddr) - return fd, nil -} - -type writerOnly struct { - io.Writer -} - -// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't -// applicable. -func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) { - // Use wrapper to hide existing r.ReadFrom from io.Copy. - return io.Copy(writerOnly{w}, r) -} diff --git a/src/pkg/net/sock_bsd.go b/src/pkg/net/sock_bsd.go index 2607b04c7..3205f9404 100644 --- a/src/pkg/net/sock_bsd.go +++ b/src/pkg/net/sock_bsd.go @@ -4,8 +4,6 @@ // +build darwin freebsd netbsd openbsd -// Sockets for BSD variants - package net import ( @@ -31,32 +29,3 @@ func maxListenerBacklog() int { } return int(n) } - -func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) { - a := toAddr(la) - if a == nil { - return la, nil - } - switch v := a.(type) { - case *TCPAddr, *UnixAddr: - err := setDefaultListenerSockopts(s) - if err != nil { - return nil, err - } - case *UDPAddr: - if v.IP.IsMulticast() { - err := setDefaultMulticastSockopts(s) - if err != nil { - return nil, err - } - switch f { - case syscall.AF_INET: - v.IP = IPv4zero - case syscall.AF_INET6: - v.IP = IPv6unspecified - } - return v.sockaddr(f) - } - } - return la, nil -} diff --git a/src/pkg/net/sock_cloexec.go b/src/pkg/net/sock_cloexec.go new file mode 100644 index 000000000..12d0f3488 --- /dev/null +++ b/src/pkg/net/sock_cloexec.go @@ -0,0 +1,69 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements sysSocket and accept for platforms that +// provide a fast path for setting SetNonblock and CloseOnExec. + +// +build linux + +package net + +import "syscall" + +// Wrapper around the socket system call that marks the returned file +// descriptor as nonblocking and close-on-exec. +func sysSocket(f, t, p int) (int, error) { + s, err := syscall.Socket(f, t|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, p) + // The SOCK_NONBLOCK and SOCK_CLOEXEC flags were introduced in + // Linux 2.6.27. If we get an EINVAL error, fall back to + // using socket without them. + if err == nil || err != syscall.EINVAL { + return s, err + } + + // See ../syscall/exec_unix.go for description of ForkLock. + syscall.ForkLock.RLock() + s, err = syscall.Socket(f, t, p) + if err == nil { + syscall.CloseOnExec(s) + } + syscall.ForkLock.RUnlock() + if err != nil { + return -1, err + } + if err = syscall.SetNonblock(s, true); err != nil { + syscall.Close(s) + return -1, err + } + return s, nil +} + +// Wrapper around the accept system call that marks the returned file +// descriptor as nonblocking and close-on-exec. +func accept(fd int) (int, syscall.Sockaddr, error) { + nfd, sa, err := syscall.Accept4(fd, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC) + // The accept4 system call was introduced in Linux 2.6.28. If + // we get an ENOSYS error, fall back to using accept. + if err == nil || err != syscall.ENOSYS { + return nfd, sa, err + } + + // See ../syscall/exec_unix.go for description of ForkLock. + // It is probably okay to hold the lock across syscall.Accept + // because we have put fd.sysfd into non-blocking mode. + // However, a call to the File method will put it back into + // blocking mode. We can't take that risk, so no use of ForkLock here. + nfd, sa, err = syscall.Accept(fd) + if err == nil { + syscall.CloseOnExec(nfd) + } + if err != nil { + return -1, nil, err + } + if err = syscall.SetNonblock(nfd, true); err != nil { + syscall.Close(nfd) + return -1, nil, err + } + return nfd, sa, nil +} diff --git a/src/pkg/net/sock_linux.go b/src/pkg/net/sock_linux.go index e509d9397..8bbd74ddc 100644 --- a/src/pkg/net/sock_linux.go +++ b/src/pkg/net/sock_linux.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Sockets for Linux - package net import "syscall" @@ -25,32 +23,3 @@ func maxListenerBacklog() int { } return n } - -func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) { - a := toAddr(la) - if a == nil { - return la, nil - } - switch v := a.(type) { - case *TCPAddr, *UnixAddr: - err := setDefaultListenerSockopts(s) - if err != nil { - return nil, err - } - case *UDPAddr: - if v.IP.IsMulticast() { - err := setDefaultMulticastSockopts(s) - if err != nil { - return nil, err - } - switch f { - case syscall.AF_INET: - v.IP = IPv4zero - case syscall.AF_INET6: - v.IP = IPv6unspecified - } - return v.sockaddr(f) - } - } - return la, nil -} diff --git a/src/pkg/net/sock_posix.go b/src/pkg/net/sock_posix.go new file mode 100644 index 000000000..b50a892b1 --- /dev/null +++ b/src/pkg/net/sock_posix.go @@ -0,0 +1,67 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd windows + +package net + +import ( + "syscall" + "time" +) + +var listenerBacklog = maxListenerBacklog() + +// Generic POSIX socket creation. +func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { + s, err := sysSocket(f, t, p) + if err != nil { + return nil, err + } + + if err = setDefaultSockopts(s, f, t, ipv6only); err != nil { + closesocket(s) + return nil, err + } + + if ulsa != nil { + // We provide a socket that listens to a wildcard + // address with reusable UDP port when the given ulsa + // is an appropriate UDP multicast address prefix. + // This makes it possible for a single UDP listener + // to join multiple different group addresses, for + // multiple UDP listeners that listen on the same UDP + // port to join the same group address. + if ulsa, err = listenerSockaddr(s, f, ulsa, toAddr); err != nil { + closesocket(s) + return nil, err + } + if err = syscall.Bind(s, ulsa); err != nil { + closesocket(s) + return nil, err + } + } + + if fd, err = newFD(s, f, t, net); err != nil { + closesocket(s) + return nil, err + } + + if ursa != nil { + fd.wdeadline.setTime(deadline) + if err = fd.connect(ursa); err != nil { + closesocket(s) + return nil, err + } + fd.isConnected = true + fd.wdeadline.set(0) + } + + lsa, _ := syscall.Getsockname(s) + laddr := toAddr(lsa) + rsa, _ := syscall.Getpeername(s) + raddr := toAddr(rsa) + fd.setAddr(laddr, raddr) + return fd, nil +} diff --git a/src/pkg/net/sock_unix.go b/src/pkg/net/sock_unix.go new file mode 100644 index 000000000..b0d6d4900 --- /dev/null +++ b/src/pkg/net/sock_unix.go @@ -0,0 +1,36 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package net + +import "syscall" + +func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) { + a := toAddr(la) + if a == nil { + return la, nil + } + switch a := a.(type) { + case *TCPAddr, *UnixAddr: + if err := setDefaultListenerSockopts(s); err != nil { + return nil, err + } + case *UDPAddr: + if a.IP.IsMulticast() { + if err := setDefaultMulticastSockopts(s); err != nil { + return nil, err + } + switch f { + case syscall.AF_INET: + a.IP = IPv4zero + case syscall.AF_INET6: + a.IP = IPv6unspecified + } + return a.sockaddr(f) + } + } + return la, nil +} diff --git a/src/pkg/net/sock_windows.go b/src/pkg/net/sock_windows.go index cce6181c9..a77c48437 100644 --- a/src/pkg/net/sock_windows.go +++ b/src/pkg/net/sock_windows.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Sockets for Windows - package net import "syscall" @@ -18,26 +16,35 @@ func listenerSockaddr(s syscall.Handle, f int, la syscall.Sockaddr, toAddr func( if a == nil { return la, nil } - switch v := a.(type) { + switch a := a.(type) { case *TCPAddr, *UnixAddr: - err := setDefaultListenerSockopts(s) - if err != nil { + if err := setDefaultListenerSockopts(s); err != nil { return nil, err } case *UDPAddr: - if v.IP.IsMulticast() { - err := setDefaultMulticastSockopts(s) - if err != nil { + if a.IP.IsMulticast() { + if err := setDefaultMulticastSockopts(s); err != nil { return nil, err } switch f { case syscall.AF_INET: - v.IP = IPv4zero + a.IP = IPv4zero case syscall.AF_INET6: - v.IP = IPv6unspecified + a.IP = IPv6unspecified } - return v.sockaddr(f) + return a.sockaddr(f) } } return la, nil } + +func sysSocket(f, t, p int) (syscall.Handle, error) { + // See ../syscall/exec_unix.go for description of ForkLock. + syscall.ForkLock.RLock() + s, err := syscall.Socket(f, t, p) + if err == nil { + syscall.CloseOnExec(s) + } + syscall.ForkLock.RUnlock() + return s, err +} diff --git a/src/pkg/net/sockopt.go b/src/pkg/net/sockopt.go deleted file mode 100644 index 0cd19266f..000000000 --- a/src/pkg/net/sockopt.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd windows - -// Socket options - -package net - -import ( - "os" - "syscall" - "time" -) - -// Boolean to int. -func boolint(b bool) int { - if b { - return 1 - } - return 0 -} - -func ipv4AddrToInterface(ip IP) (*Interface, error) { - ift, err := Interfaces() - if err != nil { - return nil, err - } - for _, ifi := range ift { - ifat, err := ifi.Addrs() - if err != nil { - return nil, err - } - for _, ifa := range ifat { - switch v := ifa.(type) { - case *IPAddr: - if ip.Equal(v.IP) { - return &ifi, nil - } - case *IPNet: - if ip.Equal(v.IP) { - return &ifi, nil - } - } - } - } - if ip.Equal(IPv4zero) { - return nil, nil - } - return nil, errNoSuchInterface -} - -func interfaceToIPv4Addr(ifi *Interface) (IP, error) { - if ifi == nil { - return IPv4zero, nil - } - ifat, err := ifi.Addrs() - if err != nil { - return nil, err - } - for _, ifa := range ifat { - switch v := ifa.(type) { - case *IPAddr: - if v.IP.To4() != nil { - return v.IP, nil - } - case *IPNet: - if v.IP.To4() != nil { - return v.IP, nil - } - } - } - return nil, errNoSuchInterface -} - -func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error { - if ifi == nil { - return nil - } - ifat, err := ifi.Addrs() - if err != nil { - return err - } - for _, ifa := range ifat { - switch v := ifa.(type) { - case *IPAddr: - if a := v.IP.To4(); a != nil { - copy(mreq.Interface[:], a) - goto done - } - case *IPNet: - if a := v.IP.To4(); a != nil { - copy(mreq.Interface[:], a) - goto done - } - } - } -done: - if bytesEqual(mreq.Multiaddr[:], IPv4zero.To4()) { - return errNoSuchMulticastInterface - } - return nil -} - -func setReadBuffer(fd *netFD, bytes int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)) -} - -func setWriteBuffer(fd *netFD, bytes int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)) -} - -func setReadDeadline(fd *netFD, t time.Time) error { - if t.IsZero() { - fd.rdeadline = 0 - } else { - fd.rdeadline = t.UnixNano() - } - return nil -} - -func setWriteDeadline(fd *netFD, t time.Time) error { - if t.IsZero() { - fd.wdeadline = 0 - } else { - fd.wdeadline = t.UnixNano() - } - return nil -} - -func setDeadline(fd *netFD, t time.Time) error { - if err := setReadDeadline(fd, t); err != nil { - return err - } - return setWriteDeadline(fd, t) -} - -func setReuseAddr(fd *netFD, reuse bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse))) -} - -func setDontRoute(fd *netFD, dontroute bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute))) -} - -func setKeepAlive(fd *netFD, keepalive bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive))) -} - -func setNoDelay(fd *netFD, noDelay bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))) -} - -func setLinger(fd *netFD, sec int) error { - var l syscall.Linger - if sec >= 0 { - l.Onoff = 1 - l.Linger = int32(sec) - } else { - l.Onoff = 0 - l.Linger = 0 - } - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptLinger(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l)) -} diff --git a/src/pkg/net/sockopt_posix.go b/src/pkg/net/sockopt_posix.go new file mode 100644 index 000000000..fe371fe0c --- /dev/null +++ b/src/pkg/net/sockopt_posix.go @@ -0,0 +1,170 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd windows + +// Socket options + +package net + +import ( + "os" + "syscall" + "time" +) + +// Boolean to int. +func boolint(b bool) int { + if b { + return 1 + } + return 0 +} + +func ipv4AddrToInterface(ip IP) (*Interface, error) { + ift, err := Interfaces() + if err != nil { + return nil, err + } + for _, ifi := range ift { + ifat, err := ifi.Addrs() + if err != nil { + return nil, err + } + for _, ifa := range ifat { + switch v := ifa.(type) { + case *IPAddr: + if ip.Equal(v.IP) { + return &ifi, nil + } + case *IPNet: + if ip.Equal(v.IP) { + return &ifi, nil + } + } + } + } + if ip.Equal(IPv4zero) { + return nil, nil + } + return nil, errNoSuchInterface +} + +func interfaceToIPv4Addr(ifi *Interface) (IP, error) { + if ifi == nil { + return IPv4zero, nil + } + ifat, err := ifi.Addrs() + if err != nil { + return nil, err + } + for _, ifa := range ifat { + switch v := ifa.(type) { + case *IPAddr: + if v.IP.To4() != nil { + return v.IP, nil + } + case *IPNet: + if v.IP.To4() != nil { + return v.IP, nil + } + } + } + return nil, errNoSuchInterface +} + +func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error { + if ifi == nil { + return nil + } + ifat, err := ifi.Addrs() + if err != nil { + return err + } + for _, ifa := range ifat { + switch v := ifa.(type) { + case *IPAddr: + if a := v.IP.To4(); a != nil { + copy(mreq.Interface[:], a) + goto done + } + case *IPNet: + if a := v.IP.To4(); a != nil { + copy(mreq.Interface[:], a) + goto done + } + } + } +done: + if bytesEqual(mreq.Multiaddr[:], IPv4zero.To4()) { + return errNoSuchMulticastInterface + } + return nil +} + +func setReadBuffer(fd *netFD, bytes int) error { + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)) +} + +func setWriteBuffer(fd *netFD, bytes int) error { + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)) +} + +// TODO(dfc) these unused error returns could be removed + +func setReadDeadline(fd *netFD, t time.Time) error { + fd.rdeadline.setTime(t) + return nil +} + +func setWriteDeadline(fd *netFD, t time.Time) error { + fd.wdeadline.setTime(t) + return nil +} + +func setDeadline(fd *netFD, t time.Time) error { + setReadDeadline(fd, t) + setWriteDeadline(fd, t) + return nil +} + +func setKeepAlive(fd *netFD, keepalive bool) error { + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive))) +} + +func setNoDelay(fd *netFD, noDelay bool) error { + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))) +} + +func setLinger(fd *netFD, sec int) error { + var l syscall.Linger + if sec >= 0 { + l.Onoff = 1 + l.Linger = int32(sec) + } else { + l.Onoff = 0 + l.Linger = 0 + } + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + return os.NewSyscallError("setsockopt", syscall.SetsockoptLinger(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_LINGER, &l)) +} diff --git a/src/pkg/net/sockoptip.go b/src/pkg/net/sockoptip.go deleted file mode 100644 index 1fcad4018..000000000 --- a/src/pkg/net/sockoptip.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd windows - -// IP-level socket options - -package net - -import ( - "os" - "syscall" -) - -func ipv4TOS(fd *netFD) (int, error) { - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS) - if err != nil { - return 0, os.NewSyscallError("getsockopt", err) - } - return v, nil -} - -func setIPv4TOS(fd *netFD, v int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv4TTL(fd *netFD) (int, error) { - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL) - if err != nil { - return 0, os.NewSyscallError("getsockopt", err) - } - return v, nil -} - -func setIPv4TTL(fd *netFD, v int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { - mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}} - if err := setIPv4MreqToInterface(mreq, ifi); err != nil { - return err - } - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) -} - -func leaveIPv4Group(fd *netFD, ifi *Interface, ip IP) error { - mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}} - if err := setIPv4MreqToInterface(mreq, ifi); err != nil { - return err - } - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq)) -} - -func ipv6HopLimit(fd *netFD) (int, error) { - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS) - if err != nil { - return 0, os.NewSyscallError("getsockopt", err) - } - return v, nil -} - -func setIPv6HopLimit(fd *netFD, v int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv6MulticastInterface(fd *netFD) (*Interface, error) { - if err := fd.incref(false); err != nil { - return nil, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - if v == 0 { - return nil, nil - } - ifi, err := InterfaceByIndex(v) - if err != nil { - return nil, err - } - return ifi, nil -} - -func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { - var v int - if ifi != nil { - v = ifi.Index - } - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv6MulticastHopLimit(fd *netFD) (int, error) { - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS) - if err != nil { - return 0, os.NewSyscallError("getsockopt", err) - } - return v, nil -} - -func setIPv6MulticastHopLimit(fd *netFD, v int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv6MulticastLoopback(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv6MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { - mreq := &syscall.IPv6Mreq{} - copy(mreq.Multiaddr[:], ip) - if ifi != nil { - mreq.Interface = uint32(ifi.Index) - } - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)) -} - -func leaveIPv6Group(fd *netFD, ifi *Interface, ip IP) error { - mreq := &syscall.IPv6Mreq{} - copy(mreq.Multiaddr[:], ip) - if ifi != nil { - mreq.Interface = uint32(ifi.Index) - } - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq)) -} diff --git a/src/pkg/net/sockoptip_bsd.go b/src/pkg/net/sockoptip_bsd.go index 19e2b142e..263f85521 100644 --- a/src/pkg/net/sockoptip_bsd.go +++ b/src/pkg/net/sockoptip_bsd.go @@ -4,8 +4,6 @@ // +build darwin freebsd netbsd openbsd -// IP-level socket options for BSD variants - package net import ( @@ -13,48 +11,30 @@ import ( "syscall" ) -func ipv4MulticastTTL(fd *netFD) (int, error) { - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL) +func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { + ip, err := interfaceToIPv4Addr(ifi) if err != nil { - return 0, os.NewSyscallError("getsockopt", err) + return os.NewSyscallError("setsockopt", err) } - return int(v), nil -} - -func setIPv4MulticastTTL(fd *netFD, v int) error { + var a [4]byte + copy(a[:], ip.To4()) if err := fd.incref(false); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, byte(v)) + err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a) if err != nil { return os.NewSyscallError("setsockopt", err) } return nil } -func ipv6TrafficClass(fd *netFD) (int, error) { - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS) - if err != nil { - return 0, os.NewSyscallError("getsockopt", err) - } - return v, nil -} - -func setIPv6TrafficClass(fd *netFD, v int) error { +func setIPv4MulticastLoopback(fd *netFD, v bool) error { if err := fd.incref(false); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v) + err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v))) if err != nil { return os.NewSyscallError("setsockopt", err) } diff --git a/src/pkg/net/sockoptip_darwin.go b/src/pkg/net/sockoptip_darwin.go deleted file mode 100644 index 52b237c4b..000000000 --- a/src/pkg/net/sockoptip_darwin.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// IP-level socket options for Darwin - -package net - -import ( - "os" - "syscall" -) - -func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - if err := fd.incref(false); err != nil { - return nil, err - } - defer fd.decref() - a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3])) -} - -func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { - ip, err := interfaceToIPv4Addr(ifi) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - var x [4]byte - copy(x[:], ip.To4()) - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv4MulticastLoopback(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv4ReceiveInterface(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv4ReceiveInterface(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} diff --git a/src/pkg/net/sockoptip_freebsd.go b/src/pkg/net/sockoptip_freebsd.go deleted file mode 100644 index 4a3bc2e82..000000000 --- a/src/pkg/net/sockoptip_freebsd.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// IP-level socket options for FreeBSD - -package net - -import ( - "os" - "syscall" -) - -func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - if err := fd.incref(false); err != nil { - return nil, err - } - defer fd.decref() - mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - if int(mreq.Ifindex) == 0 { - return nil, nil - } - return InterfaceByIndex(int(mreq.Ifindex)) -} - -func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { - var v int32 - if ifi != nil { - v = int32(ifi.Index) - } - mreq := &syscall.IPMreqn{Ifindex: v} - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv4MulticastLoopback(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv4ReceiveInterface(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv4ReceiveInterface(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} diff --git a/src/pkg/net/sockoptip_linux.go b/src/pkg/net/sockoptip_linux.go index 169718f14..225fb0c4c 100644 --- a/src/pkg/net/sockoptip_linux.go +++ b/src/pkg/net/sockoptip_linux.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// IP-level socket options for Linux - package net import ( @@ -11,21 +9,6 @@ import ( "syscall" ) -func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - if err := fd.incref(false); err != nil { - return nil, err - } - defer fd.decref() - mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - if int(mreq.Ifindex) == 0 { - return nil, nil - } - return InterfaceByIndex(int(mreq.Ifindex)) -} - func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { var v int32 if ifi != nil { @@ -43,42 +26,6 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { return nil } -func ipv4MulticastTTL(fd *netFD) (int, error) { - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL) - if err != nil { - return -1, os.NewSyscallError("getsockopt", err) - } - return v, nil -} - -func setIPv4MulticastTTL(fd *netFD, v int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv4MulticastLoopback(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - func setIPv4MulticastLoopback(fd *netFD, v bool) error { if err := fd.incref(false); err != nil { return err @@ -90,51 +37,3 @@ func setIPv4MulticastLoopback(fd *netFD, v bool) error { } return nil } - -func ipv4ReceiveInterface(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv4ReceiveInterface(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv6TrafficClass(fd *netFD) (int, error) { - if err := fd.incref(false); err != nil { - return 0, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS) - if err != nil { - return 0, os.NewSyscallError("getsockopt", err) - } - return v, nil -} - -func setIPv6TrafficClass(fd *netFD, v int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} diff --git a/src/pkg/net/sockoptip_netbsd.go b/src/pkg/net/sockoptip_netbsd.go deleted file mode 100644 index 446d92aa3..000000000 --- a/src/pkg/net/sockoptip_netbsd.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// IP-level socket options for NetBSD - -package net - -import "syscall" - -func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - // TODO: Implement this - return nil, syscall.EAFNOSUPPORT -} - -func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { - // TODO: Implement this - return syscall.EAFNOSUPPORT -} - -func ipv4MulticastLoopback(fd *netFD) (bool, error) { - // TODO: Implement this - return false, syscall.EAFNOSUPPORT -} - -func setIPv4MulticastLoopback(fd *netFD, v bool) error { - // TODO: Implement this - return syscall.EAFNOSUPPORT -} - -func ipv4ReceiveInterface(fd *netFD) (bool, error) { - // TODO: Implement this - return false, syscall.EAFNOSUPPORT -} - -func setIPv4ReceiveInterface(fd *netFD, v bool) error { - // TODO: Implement this - return syscall.EAFNOSUPPORT -} diff --git a/src/pkg/net/sockoptip_openbsd.go b/src/pkg/net/sockoptip_openbsd.go deleted file mode 100644 index f3e42f1a9..000000000 --- a/src/pkg/net/sockoptip_openbsd.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// IP-level socket options for OpenBSD - -package net - -import ( - "os" - "syscall" -) - -func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - if err := fd.incref(false); err != nil { - return nil, err - } - defer fd.decref() - a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF) - if err != nil { - return nil, os.NewSyscallError("getsockopt", err) - } - return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3])) -} - -func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { - ip, err := interfaceToIPv4Addr(ifi) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - var x [4]byte - copy(x[:], ip.To4()) - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv4MulticastLoopback(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v))) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -func ipv4ReceiveInterface(fd *netFD) (bool, error) { - if err := fd.incref(false); err != nil { - return false, err - } - defer fd.decref() - v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF) - if err != nil { - return false, os.NewSyscallError("getsockopt", err) - } - return v == 1, nil -} - -func setIPv4ReceiveInterface(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} diff --git a/src/pkg/net/sockoptip_posix.go b/src/pkg/net/sockoptip_posix.go new file mode 100644 index 000000000..e4c56a0e4 --- /dev/null +++ b/src/pkg/net/sockoptip_posix.go @@ -0,0 +1,73 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd windows + +package net + +import ( + "os" + "syscall" +) + +func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { + mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}} + if err := setIPv4MreqToInterface(mreq, ifi); err != nil { + return err + } + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + err := syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } + return nil +} + +func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { + var v int + if ifi != nil { + v = ifi.Index + } + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } + return nil +} + +func setIPv6MulticastLoopback(fd *netFD, v bool) error { + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v)) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } + return nil +} + +func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { + mreq := &syscall.IPv6Mreq{} + copy(mreq.Multiaddr[:], ip) + if ifi != nil { + mreq.Interface = uint32(ifi.Index) + } + if err := fd.incref(false); err != nil { + return err + } + defer fd.decref() + err := syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq) + if err != nil { + return os.NewSyscallError("setsockopt", err) + } + return nil +} diff --git a/src/pkg/net/sockoptip_windows.go b/src/pkg/net/sockoptip_windows.go index b9db3334d..3e248441a 100644 --- a/src/pkg/net/sockoptip_windows.go +++ b/src/pkg/net/sockoptip_windows.go @@ -2,90 +2,41 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// IP-level socket options for Windows - package net import ( "os" "syscall" + "unsafe" ) -func ipv4MulticastInterface(fd *netFD) (*Interface, error) { - // TODO: Implement this - return nil, syscall.EWINDOWS -} - func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { ip, err := interfaceToIPv4Addr(ifi) if err != nil { return os.NewSyscallError("setsockopt", err) } - var x [4]byte - copy(x[:], ip.To4()) + var a [4]byte + copy(a[:], ip.To4()) if err := fd.incref(false); err != nil { return err } defer fd.decref() - err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x) + err = syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_IF), (*byte)(unsafe.Pointer(&a[0])), 4) if err != nil { return os.NewSyscallError("setsockopt", err) } return nil } -func ipv4MulticastTTL(fd *netFD) (int, error) { - // TODO: Implement this - return -1, syscall.EWINDOWS -} - -func setIPv4MulticastTTL(fd *netFD, v int) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil - -} - -func ipv4MulticastLoopback(fd *netFD) (bool, error) { - // TODO: Implement this - return false, syscall.EWINDOWS -} - func setIPv4MulticastLoopback(fd *netFD, v bool) error { if err := fd.incref(false); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) + vv := int32(boolint(v)) + err := syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&vv)), 4) if err != nil { return os.NewSyscallError("setsockopt", err) } return nil - -} - -func ipv4ReceiveInterface(fd *netFD) (bool, error) { - // TODO: Implement this - return false, syscall.EWINDOWS -} - -func setIPv4ReceiveInterface(fd *netFD, v bool) error { - // TODO: Implement this - return syscall.EWINDOWS -} - -func ipv6TrafficClass(fd *netFD) (int, error) { - // TODO: Implement this - return 0, syscall.EWINDOWS -} - -func setIPv6TrafficClass(fd *netFD, v int) error { - // TODO: Implement this - return syscall.EWINDOWS } diff --git a/src/pkg/net/sys_cloexec.go b/src/pkg/net/sys_cloexec.go new file mode 100644 index 000000000..17e874908 --- /dev/null +++ b/src/pkg/net/sys_cloexec.go @@ -0,0 +1,54 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements sysSocket and accept for platforms that do not +// provide a fast path for setting SetNonblock and CloseOnExec. + +// +build darwin freebsd netbsd openbsd + +package net + +import "syscall" + +// Wrapper around the socket system call that marks the returned file +// descriptor as nonblocking and close-on-exec. +func sysSocket(f, t, p int) (int, error) { + // See ../syscall/exec_unix.go for description of ForkLock. + syscall.ForkLock.RLock() + s, err := syscall.Socket(f, t, p) + if err == nil { + syscall.CloseOnExec(s) + } + syscall.ForkLock.RUnlock() + if err != nil { + return -1, err + } + if err = syscall.SetNonblock(s, true); err != nil { + syscall.Close(s) + return -1, err + } + return s, nil +} + +// Wrapper around the accept system call that marks the returned file +// descriptor as nonblocking and close-on-exec. +func accept(fd int) (int, syscall.Sockaddr, error) { + // See ../syscall/exec_unix.go for description of ForkLock. + // It is probably okay to hold the lock across syscall.Accept + // because we have put fd.sysfd into non-blocking mode. + // However, a call to the File method will put it back into + // blocking mode. We can't take that risk, so no use of ForkLock here. + nfd, sa, err := syscall.Accept(fd) + if err == nil { + syscall.CloseOnExec(nfd) + } + if err != nil { + return -1, nil, err + } + if err = syscall.SetNonblock(nfd, true); err != nil { + syscall.Close(nfd) + return -1, nil, err + } + return nfd, sa, nil +} diff --git a/src/pkg/net/tcp_test.go b/src/pkg/net/tcp_test.go new file mode 100644 index 000000000..6c4485a94 --- /dev/null +++ b/src/pkg/net/tcp_test.go @@ -0,0 +1,206 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "reflect" + "runtime" + "testing" + "time" +) + +func BenchmarkTCP4OneShot(b *testing.B) { + benchmarkTCP(b, false, false, "127.0.0.1:0") +} + +func BenchmarkTCP4OneShotTimeout(b *testing.B) { + benchmarkTCP(b, false, true, "127.0.0.1:0") +} + +func BenchmarkTCP4Persistent(b *testing.B) { + benchmarkTCP(b, true, false, "127.0.0.1:0") +} + +func BenchmarkTCP4PersistentTimeout(b *testing.B) { + benchmarkTCP(b, true, true, "127.0.0.1:0") +} + +func BenchmarkTCP6OneShot(b *testing.B) { + if !supportsIPv6 { + b.Skip("ipv6 is not supported") + } + benchmarkTCP(b, false, false, "[::1]:0") +} + +func BenchmarkTCP6OneShotTimeout(b *testing.B) { + if !supportsIPv6 { + b.Skip("ipv6 is not supported") + } + benchmarkTCP(b, false, true, "[::1]:0") +} + +func BenchmarkTCP6Persistent(b *testing.B) { + if !supportsIPv6 { + b.Skip("ipv6 is not supported") + } + benchmarkTCP(b, true, false, "[::1]:0") +} + +func BenchmarkTCP6PersistentTimeout(b *testing.B) { + if !supportsIPv6 { + b.Skip("ipv6 is not supported") + } + benchmarkTCP(b, true, true, "[::1]:0") +} + +func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) { + const msgLen = 512 + conns := b.N + numConcurrent := runtime.GOMAXPROCS(-1) * 16 + msgs := 1 + if persistent { + conns = numConcurrent + msgs = b.N / conns + if msgs == 0 { + msgs = 1 + } + if conns > b.N { + conns = b.N + } + } + sendMsg := func(c Conn, buf []byte) bool { + n, err := c.Write(buf) + if n != len(buf) || err != nil { + b.Logf("Write failed: %v", err) + return false + } + return true + } + recvMsg := func(c Conn, buf []byte) bool { + for read := 0; read != len(buf); { + n, err := c.Read(buf) + read += n + if err != nil { + b.Logf("Read failed: %v", err) + return false + } + } + return true + } + ln, err := Listen("tcp", laddr) + if err != nil { + b.Fatalf("Listen failed: %v", err) + } + defer ln.Close() + // Acceptor. + go func() { + for { + c, err := ln.Accept() + if err != nil { + break + } + // Server connection. + go func(c Conn) { + defer c.Close() + if timeout { + c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire. + } + var buf [msgLen]byte + for m := 0; m < msgs; m++ { + if !recvMsg(c, buf[:]) || !sendMsg(c, buf[:]) { + break + } + } + }(c) + } + }() + sem := make(chan bool, numConcurrent) + for i := 0; i < conns; i++ { + sem <- true + // Client connection. + go func() { + defer func() { + <-sem + }() + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + b.Logf("Dial failed: %v", err) + return + } + defer c.Close() + if timeout { + c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire. + } + var buf [msgLen]byte + for m := 0; m < msgs; m++ { + if !sendMsg(c, buf[:]) || !recvMsg(c, buf[:]) { + break + } + } + }() + } + for i := 0; i < cap(sem); i++ { + sem <- true + } +} + +var resolveTCPAddrTests = []struct { + net string + litAddr string + addr *TCPAddr + err error +}{ + {"tcp", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, + {"tcp4", "127.0.0.1:65535", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil}, + + {"tcp", "[::1]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1}, nil}, + {"tcp6", "[::1]:65534", &TCPAddr{IP: ParseIP("::1"), Port: 65534}, nil}, + + {"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior + {"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior + + {"http", "127.0.0.1:0", nil, UnknownNetworkError("http")}, +} + +func TestResolveTCPAddr(t *testing.T) { + for _, tt := range resolveTCPAddrTests { + addr, err := ResolveTCPAddr(tt.net, tt.litAddr) + if err != tt.err { + t.Fatalf("ResolveTCPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + } + if !reflect.DeepEqual(addr, tt.addr) { + t.Fatalf("got %#v; expected %#v", addr, tt.addr) + } + } +} + +var tcpListenerNameTests = []struct { + net string + laddr *TCPAddr +}{ + {"tcp4", &TCPAddr{IP: IPv4(127, 0, 0, 1)}}, + {"tcp4", &TCPAddr{}}, + {"tcp4", nil}, +} + +func TestTCPListenerName(t *testing.T) { + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + + for _, tt := range tcpListenerNameTests { + ln, err := ListenTCP(tt.net, tt.laddr) + if err != nil { + t.Errorf("ListenTCP failed: %v", err) + return + } + defer ln.Close() + la := ln.Addr() + if a, ok := la.(*TCPAddr); !ok || a.Port == 0 { + t.Errorf("got %v; expected a proper address with non-zero port number", la) + return + } + } +} diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go index 47fbf2919..d5158b22d 100644 --- a/src/pkg/net/tcpsock.go +++ b/src/pkg/net/tcpsock.go @@ -10,6 +10,7 @@ package net type TCPAddr struct { IP IP Port int + Zone string // IPv6 scoped addressing zone } // Network returns the address's network name, "tcp". @@ -28,9 +29,16 @@ func (a *TCPAddr) String() string { // "tcp4" or "tcp6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]:80". func ResolveTCPAddr(net, addr string) (*TCPAddr, error) { - ip, port, err := hostPortToIP(net, addr) + switch net { + case "tcp", "tcp4", "tcp6": + case "": // a hint wildcard for Go 1.0 undocumented behavior + net = "tcp" + default: + return nil, UnknownNetworkError(net) + } + a, err := resolveInternetAddr(net, addr, noDeadline) if err != nil { return nil, err } - return &TCPAddr{ip, port}, nil + return a.(*TCPAddr), nil } diff --git a/src/pkg/net/tcpsock_plan9.go b/src/pkg/net/tcpsock_plan9.go index 35f56966e..ed3664603 100644 --- a/src/pkg/net/tcpsock_plan9.go +++ b/src/pkg/net/tcpsock_plan9.go @@ -2,34 +2,30 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// TCP for Plan 9 +// TCP sockets for Plan 9 package net import ( + "io" + "os" "syscall" "time" ) -// TCPConn is an implementation of the Conn interface -// for TCP network connections. +// TCPConn is an implementation of the Conn interface for TCP network +// connections. type TCPConn struct { - plan9Conn + conn } -// SetDeadline implements the Conn SetDeadline method. -func (c *TCPConn) SetDeadline(t time.Time) error { - return syscall.EPLAN9 +func newTCPConn(fd *netFD) *TCPConn { + return &TCPConn{conn{fd}} } -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *TCPConn) SetReadDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *TCPConn) SetWriteDeadline(t time.Time) error { - return syscall.EPLAN9 +// ReadFrom implements the io.ReaderFrom ReadFrom method. +func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) { + return genericReadFrom(c, r) } // CloseRead shuts down the reading side of the TCP connection. @@ -38,7 +34,7 @@ func (c *TCPConn) CloseRead() error { if !c.ok() { return syscall.EINVAL } - return syscall.EPLAN9 + return c.fd.CloseRead() } // CloseWrite shuts down the writing side of the TCP connection. @@ -47,51 +43,142 @@ func (c *TCPConn) CloseWrite() error { if !c.ok() { return syscall.EINVAL } + return c.fd.CloseWrite() +} + +// SetLinger sets the behavior of Close() on a connection which still +// has data waiting to be sent or to be acknowledged. +// +// If sec < 0 (the default), Close returns immediately and the +// operating system finishes sending the data in the background. +// +// If sec == 0, Close returns immediately and the operating system +// discards any unsent or unacknowledged data. +// +// If sec > 0, Close blocks for at most sec seconds waiting for data +// to be sent and acknowledged. +func (c *TCPConn) SetLinger(sec int) error { + return syscall.EPLAN9 +} + +// SetKeepAlive sets whether the operating system should send +// keepalive messages on the connection. +func (c *TCPConn) SetKeepAlive(keepalive bool) error { + return syscall.EPLAN9 +} + +// SetNoDelay controls whether the operating system should delay +// packet transmission in hopes of sending fewer packets (Nagle's +// algorithm). The default is true (no delay), meaning that data is +// sent as soon as possible after a Write. +func (c *TCPConn) SetNoDelay(noDelay bool) error { return syscall.EPLAN9 } // DialTCP connects to the remote address raddr on the network net, -// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used -// as the local address for the connection. -func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) { +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is +// used as the local address for the connection. +func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { + return dialTCP(net, laddr, raddr, noDeadline) +} + +func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) { + if !deadline.IsZero() { + panic("net.dialTCP: deadline not implemented on Plan 9") + } switch net { case "tcp", "tcp4", "tcp6": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{"dial", net, raddr, UnknownNetworkError(net)} } if raddr == nil { return nil, &OpError{"dial", net, nil, errMissingAddress} } - c1, err := dialPlan9(net, laddr, raddr) + fd, err := dialPlan9(net, laddr, raddr) if err != nil { - return + return nil, err } - return &TCPConn{*c1}, nil + return newTCPConn(fd), nil } -// TCPListener is a TCP network listener. -// Clients should typically use variables of type Listener -// instead of assuming TCP. +// TCPListener is a TCP network listener. Clients should typically +// use variables of type Listener instead of assuming TCP. type TCPListener struct { - plan9Listener + fd *netFD +} + +// AcceptTCP accepts the next incoming call and returns the new +// connection and the remote address. +func (l *TCPListener) AcceptTCP() (*TCPConn, error) { + if l == nil || l.fd == nil || l.fd.ctl == nil { + return nil, syscall.EINVAL + } + fd, err := l.fd.acceptPlan9() + if err != nil { + return nil, err + } + return newTCPConn(fd), nil +} + +// Accept implements the Accept method in the Listener interface; it +// waits for the next call and returns a generic Conn. +func (l *TCPListener) Accept() (Conn, error) { + if l == nil || l.fd == nil || l.fd.ctl == nil { + return nil, syscall.EINVAL + } + c, err := l.AcceptTCP() + if err != nil { + return nil, err + } + return c, nil } -// ListenTCP announces on the TCP address laddr and returns a TCP listener. -// Net must be "tcp", "tcp4", or "tcp6". -// If laddr has a port of 0, it means to listen on some available port. -// The caller can use l.Addr() to retrieve the chosen address. -func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err error) { +// Close stops listening on the TCP address. +// Already Accepted connections are not closed. +func (l *TCPListener) Close() error { + if l == nil || l.fd == nil || l.fd.ctl == nil { + return syscall.EINVAL + } + if _, err := l.fd.ctl.WriteString("hangup"); err != nil { + l.fd.ctl.Close() + return &OpError{"close", l.fd.ctl.Name(), l.fd.laddr, err} + } + return l.fd.ctl.Close() +} + +// Addr returns the listener's network address, a *TCPAddr. +func (l *TCPListener) Addr() Addr { return l.fd.laddr } + +// SetDeadline sets the deadline associated with the listener. +// A zero time value disables the deadline. +func (l *TCPListener) SetDeadline(t time.Time) error { + if l == nil || l.fd == nil || l.fd.ctl == nil { + return syscall.EINVAL + } + return setDeadline(l.fd, t) +} + +// File returns a copy of the underlying os.File, set to blocking +// mode. It is the caller's responsibility to close f when finished. +// Closing l does not affect f, and closing f does not affect l. +func (l *TCPListener) File() (f *os.File, err error) { return l.dup() } + +// ListenTCP announces on the TCP address laddr and returns a TCP +// listener. Net must be "tcp", "tcp4", or "tcp6". If laddr has a +// port of 0, it means to listen on some available port. The caller +// can use l.Addr() to retrieve the chosen address. +func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) { switch net { case "tcp", "tcp4", "tcp6": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{"listen", net, laddr, UnknownNetworkError(net)} } if laddr == nil { - return nil, &OpError{"listen", net, nil, errMissingAddress} + laddr = &TCPAddr{} } - l1, err := listenPlan9(net, laddr) + fd, err := listenPlan9(net, laddr) if err != nil { - return + return nil, err } - return &TCPListener{*l1}, nil + return &TCPListener{fd}, nil } diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go index e6b1937fb..bd5a2a287 100644 --- a/src/pkg/net/tcpsock_posix.go +++ b/src/pkg/net/tcpsock_posix.go @@ -23,14 +23,9 @@ import ( func sockaddrToTCP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { case *syscall.SockaddrInet4: - return &TCPAddr{sa.Addr[0:], sa.Port} + return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port} case *syscall.SockaddrInet6: - return &TCPAddr{sa.Addr[0:], sa.Port} - default: - if sa != nil { - // Diagnose when we will turn a non-nil sockaddr into a nil. - panic("unexpected type in sockaddrToTCP") - } + return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} } return nil } @@ -53,7 +48,7 @@ func (a *TCPAddr) isWildcard() bool { } func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, a.Port) + return ipToSockaddr(family, a.IP, a.Port, a.Zone) } func (a *TCPAddr) toAddr() sockaddr { @@ -66,27 +61,15 @@ func (a *TCPAddr) toAddr() sockaddr { // TCPConn is an implementation of the Conn interface // for TCP network connections. type TCPConn struct { - fd *netFD + conn } func newTCPConn(fd *netFD) *TCPConn { - c := &TCPConn{fd} + c := &TCPConn{conn{fd}} c.SetNoDelay(true) return c } -func (c *TCPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the Conn Read method. -func (c *TCPConn) Read(b []byte) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - return c.fd.Read(b) -} - // ReadFrom implements the io.ReaderFrom ReadFrom method. func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) { if n, err, handled := sendFile(c.fd, r); handled { @@ -95,22 +78,6 @@ func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) { return genericReadFrom(c, r) } -// Write implements the Conn Write method. -func (c *TCPConn) Write(b []byte) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the TCP connection. -func (c *TCPConn) Close() error { - if !c.ok() { - return syscall.EINVAL - } - return c.fd.Close() -} - // CloseRead shuts down the reading side of the TCP connection. // Most callers should just use Close. func (c *TCPConn) CloseRead() error { @@ -129,64 +96,6 @@ func (c *TCPConn) CloseWrite() error { return c.fd.CloseWrite() } -// LocalAddr returns the local network address, a *TCPAddr. -func (c *TCPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *TCPAddr. -func (c *TCPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetDeadline implements the Conn SetDeadline method. -func (c *TCPConn) SetDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setDeadline(c.fd, t) -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *TCPConn) SetReadDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setReadDeadline(c.fd, t) -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *TCPConn) SetWriteDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setWriteDeadline(c.fd, t) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *TCPConn) SetReadBuffer(bytes int) error { - if !c.ok() { - return syscall.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *TCPConn) SetWriteBuffer(bytes int) error { - if !c.ok() { - return syscall.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - // SetLinger sets the behavior of Close() on a connection // which still has data waiting to be sent or to be acknowledged. // @@ -225,20 +134,23 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error { return setNoDelay(c.fd, noDelay) } -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *TCPConn) File() (f *os.File, err error) { return c.fd.dup() } - // DialTCP connects to the remote address raddr on the network net, // which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used // as the local address for the connection. func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { + switch net { + case "tcp", "tcp4", "tcp6": + default: + return nil, UnknownNetworkError(net) + } if raddr == nil { return nil, &OpError{"dial", net, nil, errMissingAddress} } + return dialTCP(net, laddr, raddr, noDeadline) +} - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) +func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) { + fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) // TCP has a rarely used mechanism called a 'simultaneous connection' in // which Dial("tcp", addr1, addr2) run on the machine at addr1 can @@ -257,9 +169,18 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { // use the result. See also: // http://golang.org/issue/2690 // http://stackoverflow.com/questions/4949858/ - for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ { - fd.Close() - fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + // + // The opposite can also happen: if we ask the kernel to pick an appropriate + // originating local address, sometimes it picks one that is already in use. + // So if the error is EADDRNOTAVAIL, we have to try again too, just for + // a different reason. + // + // The kernel socket code is no doubt enjoying watching us squirm. + for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ { + if err == nil { + fd.Close() + } + fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) } if err != nil { @@ -268,7 +189,12 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { return newTCPConn(fd), nil } -func selfConnect(fd *netFD) bool { +func selfConnect(fd *netFD, err error) bool { + // If the connect failed, we clearly didn't connect to ourselves. + if err != nil { + return false + } + // The socket constructor can return an fd with raddr nil under certain // unknown conditions. The errors in the calls there to Getpeername // are discarded, but we can't catch the problem there because those @@ -285,6 +211,11 @@ func selfConnect(fd *netFD) bool { return l.Port == r.Port && l.IP.Equal(r.IP) } +func spuriousENOTAVAIL(err error) bool { + e, ok := err.(*OpError) + return ok && e.Err == syscall.EADDRNOTAVAIL +} + // TCPListener is a TCP network listener. // Clients should typically use variables of type Listener // instead of assuming TCP. @@ -292,29 +223,10 @@ type TCPListener struct { fd *netFD } -// ListenTCP announces on the TCP address laddr and returns a TCP listener. -// Net must be "tcp", "tcp4", or "tcp6". -// If laddr has a port of 0, it means to listen on some available port. -// The caller can use l.Addr() to retrieve the chosen address. -func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) { - fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) - if err != nil { - return nil, err - } - err = syscall.Listen(fd.sysfd, listenerBacklog) - if err != nil { - closesocket(fd.sysfd) - return nil, &OpError{"listen", net, laddr, err} - } - l := new(TCPListener) - l.fd = fd - return l, nil -} - // AcceptTCP accepts the next incoming call and returns the new connection // and the remote address. func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) { - if l == nil || l.fd == nil || l.fd.sysfd < 0 { + if l == nil || l.fd == nil { return nil, syscall.EINVAL } fd, err := l.fd.accept(sockaddrToTCP) @@ -359,3 +271,28 @@ func (l *TCPListener) SetDeadline(t time.Time) error { // It is the caller's responsibility to close f when finished. // Closing l does not affect f, and closing f does not affect l. func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() } + +// ListenTCP announces on the TCP address laddr and returns a TCP listener. +// Net must be "tcp", "tcp4", or "tcp6". +// If laddr has a port of 0, it means to listen on some available port. +// The caller can use l.Addr() to retrieve the chosen address. +func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) { + switch net { + case "tcp", "tcp4", "tcp6": + default: + return nil, UnknownNetworkError(net) + } + if laddr == nil { + laddr = &TCPAddr{} + } + fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) + if err != nil { + return nil, err + } + err = syscall.Listen(fd.sysfd, listenerBacklog) + if err != nil { + closesocket(fd.sysfd) + return nil, &OpError{"listen", net, laddr, err} + } + return &TCPListener{fd}, nil +} diff --git a/src/pkg/net/textproto/reader.go b/src/pkg/net/textproto/reader.go index 125feb3e8..b61bea862 100644 --- a/src/pkg/net/textproto/reader.go +++ b/src/pkg/net/textproto/reader.go @@ -128,6 +128,17 @@ func (r *Reader) readContinuedLineSlice() ([]byte, error) { return line, nil } + // Optimistically assume that we have started to buffer the next line + // and it starts with an ASCII letter (the next header key), so we can + // avoid copying that buffered data around in memory and skipping over + // non-existent whitespace. + if r.R.Buffered() > 1 { + peek, err := r.R.Peek(1) + if err == nil && isASCIILetter(peek[0]) { + return trim(line), nil + } + } + // ReadByte or the next readLineSlice will flush the read buffer; // copy the slice into buf. r.buf = append(r.buf[:0], trim(line)...) @@ -445,23 +456,25 @@ func (r *Reader) ReadDotLines() ([]string, error) { // } // func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { - m := make(MIMEHeader) + m := make(MIMEHeader, 4) for { kv, err := r.readContinuedLineSlice() if len(kv) == 0 { return m, err } - // Key ends at first colon; must not have spaces. + // Key ends at first colon; should not have spaces but + // they appear in the wild, violating specs, so we + // remove them if present. i := bytes.IndexByte(kv, ':') if i < 0 { return m, ProtocolError("malformed MIME header line: " + string(kv)) } - key := string(kv[0:i]) - if strings.Index(key, " ") >= 0 { - key = strings.TrimRight(key, " ") + endKey := i + for endKey > 0 && kv[endKey-1] == ' ' { + endKey-- } - key = CanonicalMIMEHeaderKey(key) + key := canonicalMIMEHeaderKey(kv[:endKey]) // Skip initial spaces in value. i++ // skip colon @@ -484,41 +497,107 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { // letter and any letter following a hyphen to upper case; // the rest are converted to lowercase. For example, the // canonical key for "accept-encoding" is "Accept-Encoding". +// MIME header keys are assumed to be ASCII only. func CanonicalMIMEHeaderKey(s string) string { // Quick check for canonical encoding. - needUpper := true + upper := true for i := 0; i < len(s); i++ { c := s[i] - if needUpper && 'a' <= c && c <= 'z' { - goto MustRewrite + if upper && 'a' <= c && c <= 'z' { + return canonicalMIMEHeaderKey([]byte(s)) } - if !needUpper && 'A' <= c && c <= 'Z' { - goto MustRewrite + if !upper && 'A' <= c && c <= 'Z' { + return canonicalMIMEHeaderKey([]byte(s)) } - needUpper = c == '-' + upper = c == '-' } return s +} + +const toLower = 'a' - 'A' -MustRewrite: - // Canonicalize: first letter upper case - // and upper case after each dash. - // (Host, User-Agent, If-Modified-Since). - // MIME headers are ASCII only, so no Unicode issues. - a := []byte(s) +// canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is +// allowed to mutate the provided byte slice before returning the +// string. +func canonicalMIMEHeaderKey(a []byte) string { + // Look for it in commonHeaders , so that we can avoid an + // allocation by sharing the strings among all users + // of textproto. If we don't find it, a has been canonicalized + // so just return string(a). upper := true - for i, v := range a { - if v == ' ' { + lo := 0 + hi := len(commonHeaders) + for i := 0; i < len(a); i++ { + // Canonicalize: first letter upper case + // and upper case after each dash. + // (Host, User-Agent, If-Modified-Since). + // MIME headers are ASCII only, so no Unicode issues. + if a[i] == ' ' { a[i] = '-' upper = true continue } - if upper && 'a' <= v && v <= 'z' { - a[i] = v + 'A' - 'a' + c := a[i] + if upper && 'a' <= c && c <= 'z' { + c -= toLower + } else if !upper && 'A' <= c && c <= 'Z' { + c += toLower } - if !upper && 'A' <= v && v <= 'Z' { - a[i] = v + 'a' - 'A' + a[i] = c + upper = c == '-' // for next time + + if lo < hi { + for lo < hi && (len(commonHeaders[lo]) <= i || commonHeaders[lo][i] < c) { + lo++ + } + for hi > lo && commonHeaders[hi-1][i] > c { + hi-- + } } - upper = v == '-' + } + if lo < hi && len(commonHeaders[lo]) == len(a) { + return commonHeaders[lo] } return string(a) } + +var commonHeaders = []string{ + "Accept", + "Accept-Charset", + "Accept-Encoding", + "Accept-Language", + "Accept-Ranges", + "Cache-Control", + "Cc", + "Connection", + "Content-Id", + "Content-Language", + "Content-Length", + "Content-Transfer-Encoding", + "Content-Type", + "Date", + "Dkim-Signature", + "Etag", + "Expires", + "From", + "Host", + "If-Modified-Since", + "If-None-Match", + "In-Reply-To", + "Last-Modified", + "Location", + "Message-Id", + "Mime-Version", + "Pragma", + "Received", + "Return-Path", + "Server", + "Set-Cookie", + "Subject", + "To", + "User-Agent", + "Via", + "X-Forwarded-For", + "X-Imforwards", + "X-Powered-By", +} diff --git a/src/pkg/net/textproto/reader_test.go b/src/pkg/net/textproto/reader_test.go index 7c5d16227..26987f611 100644 --- a/src/pkg/net/textproto/reader_test.go +++ b/src/pkg/net/textproto/reader_test.go @@ -6,6 +6,7 @@ package textproto import ( "bufio" + "bytes" "io" "reflect" "strings" @@ -23,6 +24,7 @@ var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{ {"uSER-aGENT", "User-Agent"}, {"user-agent", "User-Agent"}, {"USER-AGENT", "User-Agent"}, + {"üser-agenT", "üser-Agent"}, // non-ASCII unchanged } func TestCanonicalMIMEHeaderKey(t *testing.T) { @@ -239,3 +241,95 @@ func TestRFC959Lines(t *testing.T) { } } } + +func TestCommonHeaders(t *testing.T) { + // need to disable the commonHeaders-based optimization + // during this check, or we'd not be testing anything + oldch := commonHeaders + commonHeaders = []string{} + defer func() { commonHeaders = oldch }() + + last := "" + for _, h := range oldch { + if last > h { + t.Errorf("%v is out of order", h) + } + if last == h { + t.Errorf("%v is duplicated", h) + } + if canon := CanonicalMIMEHeaderKey(h); h != canon { + t.Errorf("%v is not canonical", h) + } + last = h + } +} + +var clientHeaders = strings.Replace(`Host: golang.org +Connection: keep-alive +Cache-Control: max-age=0 +Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 +User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3 +Accept-Encoding: gzip,deflate,sdch +Accept-Language: en-US,en;q=0.8,fr-CH;q=0.6 +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 +COOKIE: __utma=000000000.0000000000.0000000000.0000000000.0000000000.00; __utmb=000000000.0.00.0000000000; __utmc=000000000; __utmz=000000000.0000000000.00.0.utmcsr=code.google.com|utmccn=(referral)|utmcmd=referral|utmcct=/p/go/issues/detail +Non-Interned: test + +`, "\n", "\r\n", -1) + +var serverHeaders = strings.Replace(`Content-Type: text/html; charset=utf-8 +Content-Encoding: gzip +Date: Thu, 27 Sep 2012 09:03:33 GMT +Server: Google Frontend +Cache-Control: private +Content-Length: 2298 +VIA: 1.1 proxy.example.com:80 (XXX/n.n.n-nnn) +Connection: Close +Non-Interned: test + +`, "\n", "\r\n", -1) + +func BenchmarkReadMIMEHeader(b *testing.B) { + var buf bytes.Buffer + br := bufio.NewReader(&buf) + r := NewReader(br) + for i := 0; i < b.N; i++ { + var want int + var find string + if (i & 1) == 1 { + buf.WriteString(clientHeaders) + want = 10 + find = "Cookie" + } else { + buf.WriteString(serverHeaders) + want = 9 + find = "Via" + } + h, err := r.ReadMIMEHeader() + if err != nil { + b.Fatal(err) + } + if len(h) != want { + b.Fatalf("wrong number of headers: got %d, want %d", len(h), want) + } + if _, ok := h[find]; !ok { + b.Fatalf("did not find key %s", find) + } + } +} + +func BenchmarkUncommon(b *testing.B) { + var buf bytes.Buffer + br := bufio.NewReader(&buf) + r := NewReader(br) + for i := 0; i < b.N; i++ { + buf.WriteString("uncommon-header-for-benchmark: foo\r\n\r\n") + h, err := r.ReadMIMEHeader() + if err != nil { + b.Fatal(err) + } + if _, ok := h["Uncommon-Header-For-Benchmark"]; !ok { + b.Fatal("Missing result header.") + } + } +} diff --git a/src/pkg/net/textproto/textproto.go b/src/pkg/net/textproto/textproto.go index ad5840cf7..eb6ced1c5 100644 --- a/src/pkg/net/textproto/textproto.go +++ b/src/pkg/net/textproto/textproto.go @@ -121,3 +121,34 @@ func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err error) { } return id, nil } + +// TrimString returns s without leading and trailing ASCII space. +func TrimString(s string) string { + for len(s) > 0 && isASCIISpace(s[0]) { + s = s[1:] + } + for len(s) > 0 && isASCIISpace(s[len(s)-1]) { + s = s[:len(s)-1] + } + return s +} + +// TrimBytes returns b without leading and trailing ASCII space. +func TrimBytes(b []byte) []byte { + for len(b) > 0 && isASCIISpace(b[0]) { + b = b[1:] + } + for len(b) > 0 && isASCIISpace(b[len(b)-1]) { + b = b[:len(b)-1] + } + return b +} + +func isASCIISpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' +} + +func isASCIILetter(b byte) bool { + b |= 0x20 // make lower case + return 'a' <= b && b <= 'z' +} diff --git a/src/pkg/net/timeout_test.go b/src/pkg/net/timeout_test.go index 672fb7241..0260efcc0 100644 --- a/src/pkg/net/timeout_test.go +++ b/src/pkg/net/timeout_test.go @@ -6,11 +6,187 @@ package net import ( "fmt" + "io" + "io/ioutil" "runtime" "testing" "time" ) +func isTimeout(err error) bool { + e, ok := err.(Error) + return ok && e.Timeout() +} + +type copyRes struct { + n int64 + err error + d time.Duration +} + +func TestAcceptTimeout(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + ln := newLocalListener(t).(*TCPListener) + defer ln.Close() + ln.SetDeadline(time.Now().Add(-1 * time.Second)) + if _, err := ln.Accept(); !isTimeout(err) { + t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) + } + if _, err := ln.Accept(); !isTimeout(err) { + t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) + } + ln.SetDeadline(time.Now().Add(100 * time.Millisecond)) + if _, err := ln.Accept(); !isTimeout(err) { + t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) + } + if _, err := ln.Accept(); !isTimeout(err) { + t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) + } + ln.SetDeadline(noDeadline) + errc := make(chan error) + go func() { + _, err := ln.Accept() + errc <- err + }() + time.Sleep(100 * time.Millisecond) + select { + case err := <-errc: + t.Fatalf("Expected Accept() to not return, but it returned with %v\n", err) + default: + } + ln.Close() + switch nerr := <-errc; err := nerr.(type) { + case *OpError: + if err.Err != errClosing { + t.Fatalf("Accept: expected err %v, got %v", errClosing, err) + } + default: + if err != errClosing { + t.Fatalf("Accept: expected err %v, got %v", errClosing, err) + } + } +} + +func TestReadTimeout(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + ln := newLocalListener(t) + defer ln.Close() + c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr)) + if err != nil { + t.Fatalf("Connect: %v", err) + } + defer c.Close() + c.SetDeadline(time.Now().Add(time.Hour)) + c.SetReadDeadline(time.Now().Add(-1 * time.Second)) + buf := make([]byte, 1) + if _, err = c.Read(buf); !isTimeout(err) { + t.Fatalf("Read: expected err %v, got %v", errTimeout, err) + } + if _, err = c.Read(buf); !isTimeout(err) { + t.Fatalf("Read: expected err %v, got %v", errTimeout, err) + } + c.SetDeadline(time.Now().Add(100 * time.Millisecond)) + if _, err = c.Read(buf); !isTimeout(err) { + t.Fatalf("Read: expected err %v, got %v", errTimeout, err) + } + if _, err = c.Read(buf); !isTimeout(err) { + t.Fatalf("Read: expected err %v, got %v", errTimeout, err) + } + c.SetReadDeadline(noDeadline) + c.SetWriteDeadline(time.Now().Add(-1 * time.Second)) + errc := make(chan error) + go func() { + _, err := c.Read(buf) + errc <- err + }() + time.Sleep(100 * time.Millisecond) + select { + case err := <-errc: + t.Fatalf("Expected Read() to not return, but it returned with %v\n", err) + default: + } + c.Close() + switch nerr := <-errc; err := nerr.(type) { + case *OpError: + if err.Err != errClosing { + t.Fatalf("Read: expected err %v, got %v", errClosing, err) + } + default: + if err != errClosing { + t.Fatalf("Read: expected err %v, got %v", errClosing, err) + } + } +} + +func TestWriteTimeout(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + ln := newLocalListener(t) + defer ln.Close() + c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr)) + if err != nil { + t.Fatalf("Connect: %v", err) + } + defer c.Close() + c.SetDeadline(time.Now().Add(time.Hour)) + c.SetWriteDeadline(time.Now().Add(-1 * time.Second)) + buf := make([]byte, 4096) + writeUntilTimeout := func() { + for { + _, err := c.Write(buf) + if err != nil { + if isTimeout(err) { + return + } + t.Fatalf("Write: expected err %v, got %v", errTimeout, err) + } + } + } + writeUntilTimeout() + c.SetDeadline(time.Now().Add(10 * time.Millisecond)) + writeUntilTimeout() + writeUntilTimeout() + c.SetWriteDeadline(noDeadline) + c.SetReadDeadline(time.Now().Add(-1 * time.Second)) + errc := make(chan error) + go func() { + for { + _, err := c.Write(buf) + if err != nil { + errc <- err + } + } + }() + time.Sleep(100 * time.Millisecond) + select { + case err := <-errc: + t.Fatalf("Expected Write() to not return, but it returned with %v\n", err) + default: + } + c.Close() + switch nerr := <-errc; err := nerr.(type) { + case *OpError: + if err.Err != errClosing { + t.Fatalf("Write: expected err %v, got %v", errClosing, err) + } + default: + if err != errClosing { + t.Fatalf("Write: expected err %v, got %v", errClosing, err) + } + } +} + func testTimeout(t *testing.T, net, addr string, readFrom bool) { c, err := Dial(net, addr) if err != nil { @@ -59,8 +235,7 @@ func testTimeout(t *testing.T, net, addr string, readFrom bool) { func TestTimeoutUDP(t *testing.T) { switch runtime.GOOS { case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return + t.Skipf("skipping test on %q", runtime.GOOS) } // set up a listener that won't talk back @@ -77,8 +252,7 @@ func TestTimeoutUDP(t *testing.T) { func TestTimeoutTCP(t *testing.T) { switch runtime.GOOS { case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return + t.Skipf("skipping test on %q", runtime.GOOS) } // set up a listener that won't talk back @@ -94,8 +268,7 @@ func TestTimeoutTCP(t *testing.T) { func TestDeadlineReset(t *testing.T) { switch runtime.GOOS { case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return + t.Skipf("skipping test on %q", runtime.GOOS) } ln, err := Listen("tcp", "127.0.0.1:0") if err != nil { @@ -104,7 +277,7 @@ func TestDeadlineReset(t *testing.T) { defer ln.Close() tl := ln.(*TCPListener) tl.SetDeadline(time.Now().Add(1 * time.Minute)) - tl.SetDeadline(time.Time{}) // reset it + tl.SetDeadline(noDeadline) // reset it errc := make(chan error, 1) go func() { _, err := ln.Accept() @@ -119,3 +292,356 @@ func TestDeadlineReset(t *testing.T) { t.Errorf("unexpected return from Accept; err=%v", err) } } + +func TestTimeoutAccept(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + ln, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + tl := ln.(*TCPListener) + tl.SetDeadline(time.Now().Add(100 * time.Millisecond)) + errc := make(chan error, 1) + go func() { + _, err := ln.Accept() + errc <- err + }() + select { + case <-time.After(1 * time.Second): + // Accept shouldn't block indefinitely + t.Errorf("Accept didn't return in an expected time") + case <-errc: + // Pass. + } +} + +func TestReadWriteDeadline(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + if !canCancelIO { + t.Skip("skipping test on this system") + } + const ( + readTimeout = 50 * time.Millisecond + writeTimeout = 250 * time.Millisecond + ) + checkTimeout := func(command string, start time.Time, should time.Duration) { + is := time.Now().Sub(start) + d := is - should + if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d { + t.Errorf("%s timeout test failed: is=%v should=%v\n", command, is, should) + } + } + + ln, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("ListenTCP on :0: %v", err) + } + defer ln.Close() + + lnquit := make(chan bool) + + go func() { + c, err := ln.Accept() + if err != nil { + t.Fatalf("Accept: %v", err) + } + defer c.Close() + lnquit <- true + }() + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer c.Close() + + start := time.Now() + err = c.SetReadDeadline(start.Add(readTimeout)) + if err != nil { + t.Fatalf("SetReadDeadline: %v", err) + } + err = c.SetWriteDeadline(start.Add(writeTimeout)) + if err != nil { + t.Fatalf("SetWriteDeadline: %v", err) + } + + quit := make(chan bool) + + go func() { + var buf [10]byte + _, err := c.Read(buf[:]) + if err == nil { + t.Errorf("Read should not succeed") + } + checkTimeout("Read", start, readTimeout) + quit <- true + }() + + go func() { + var buf [10000]byte + for { + _, err := c.Write(buf[:]) + if err != nil { + break + } + } + checkTimeout("Write", start, writeTimeout) + quit <- true + }() + + <-quit + <-quit + <-lnquit +} + +type neverEnding byte + +func (b neverEnding) Read(p []byte) (n int, err error) { + for i := range p { + p[i] = byte(b) + } + return len(p), nil +} + +func TestVariousDeadlines1Proc(t *testing.T) { + testVariousDeadlines(t, 1) +} + +func TestVariousDeadlines4Proc(t *testing.T) { + testVariousDeadlines(t, 4) +} + +func testVariousDeadlines(t *testing.T, maxProcs int) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) + ln := newLocalListener(t) + defer ln.Close() + acceptc := make(chan error, 1) + + // The server, with no timeouts of its own, sending bytes to clients + // as fast as it can. + servec := make(chan copyRes) + go func() { + for { + c, err := ln.Accept() + if err != nil { + acceptc <- err + return + } + go func() { + t0 := time.Now() + n, err := io.Copy(c, neverEnding('a')) + d := time.Since(t0) + c.Close() + servec <- copyRes{n, err, d} + }() + } + }() + + for _, timeout := range []time.Duration{ + 1 * time.Nanosecond, + 2 * time.Nanosecond, + 5 * time.Nanosecond, + 50 * time.Nanosecond, + 100 * time.Nanosecond, + 200 * time.Nanosecond, + 500 * time.Nanosecond, + 750 * time.Nanosecond, + 1 * time.Microsecond, + 5 * time.Microsecond, + 25 * time.Microsecond, + 250 * time.Microsecond, + 500 * time.Microsecond, + 1 * time.Millisecond, + 5 * time.Millisecond, + 100 * time.Millisecond, + 250 * time.Millisecond, + 500 * time.Millisecond, + 1 * time.Second, + } { + numRuns := 3 + if testing.Short() { + numRuns = 1 + if timeout > 500*time.Microsecond { + continue + } + } + for run := 0; run < numRuns; run++ { + name := fmt.Sprintf("%v run %d/%d", timeout, run+1, numRuns) + t.Log(name) + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + clientc := make(chan copyRes) + go func() { + t0 := time.Now() + c.SetDeadline(t0.Add(timeout)) + n, err := io.Copy(ioutil.Discard, c) + d := time.Since(t0) + c.Close() + clientc <- copyRes{n, err, d} + }() + + const tooLong = 2000 * time.Millisecond + select { + case res := <-clientc: + if isTimeout(res.err) { + t.Logf("for %v, good client timeout after %v, reading %d bytes", name, res.d, res.n) + } else { + t.Fatalf("for %v: client Copy = %d, %v (want timeout)", name, res.n, res.err) + } + case <-time.After(tooLong): + t.Fatalf("for %v: timeout (%v) waiting for client to timeout (%v) reading", name, tooLong, timeout) + } + + select { + case res := <-servec: + t.Logf("for %v: server in %v wrote %d, %v", name, res.d, res.n, res.err) + case err := <-acceptc: + t.Fatalf("for %v: server Accept = %v", name, err) + case <-time.After(tooLong): + t.Fatalf("for %v, timeout waiting for server to finish writing", name) + } + } + } +} + +// TestReadDeadlineDataAvailable tests that read deadlines work, even +// if there's data ready to be read. +func TestReadDeadlineDataAvailable(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + ln := newLocalListener(t) + defer ln.Close() + + servec := make(chan copyRes) + const msg = "data client shouldn't read, even though it it'll be waiting" + go func() { + c, err := ln.Accept() + if err != nil { + t.Fatalf("Accept: %v", err) + } + defer c.Close() + n, err := c.Write([]byte(msg)) + servec <- copyRes{n: int64(n), err: err} + }() + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer c.Close() + if res := <-servec; res.err != nil || res.n != int64(len(msg)) { + t.Fatalf("unexpected server Write: n=%d, err=%d; want n=%d, err=nil", res.n, res.err, len(msg)) + } + c.SetReadDeadline(time.Now().Add(-5 * time.Second)) // in the psat. + buf := make([]byte, len(msg)/2) + n, err := c.Read(buf) + if n > 0 || !isTimeout(err) { + t.Fatalf("client read = %d (%q) err=%v; want 0, timeout", n, buf[:n], err) + } +} + +// TestWriteDeadlineBufferAvailable tests that write deadlines work, even +// if there's buffer space available to write. +func TestWriteDeadlineBufferAvailable(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + ln := newLocalListener(t) + defer ln.Close() + + servec := make(chan copyRes) + go func() { + c, err := ln.Accept() + if err != nil { + t.Fatalf("Accept: %v", err) + } + defer c.Close() + c.SetWriteDeadline(time.Now().Add(-5 * time.Second)) // in the past + n, err := c.Write([]byte{'x'}) + servec <- copyRes{n: int64(n), err: err} + }() + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer c.Close() + res := <-servec + if res.n != 0 { + t.Errorf("Write = %d; want 0", res.n) + } + if !isTimeout(res.err) { + t.Errorf("Write error = %v; want timeout", res.err) + } +} + +// TestProlongTimeout tests concurrent deadline modification. +// Known to cause data races in the past. +func TestProlongTimeout(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + ln := newLocalListener(t) + defer ln.Close() + connected := make(chan bool) + go func() { + s, err := ln.Accept() + connected <- true + if err != nil { + t.Fatalf("ln.Accept: %v", err) + } + defer s.Close() + s.SetDeadline(time.Now().Add(time.Hour)) + go func() { + var buf [4096]byte + for { + _, err := s.Write(buf[:]) + if err != nil { + break + } + s.SetDeadline(time.Now().Add(time.Hour)) + } + }() + buf := make([]byte, 1) + for { + _, err := s.Read(buf) + if err != nil { + break + } + s.SetDeadline(time.Now().Add(time.Hour)) + } + }() + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("DialTCP: %v", err) + } + defer c.Close() + <-connected + for i := 0; i < 1024; i++ { + var buf [1]byte + c.Write(buf[:]) + } +} diff --git a/src/pkg/net/udp_test.go b/src/pkg/net/udp_test.go index f80d3b5a9..220422e13 100644 --- a/src/pkg/net/udp_test.go +++ b/src/pkg/net/udp_test.go @@ -5,15 +5,45 @@ package net import ( + "reflect" "runtime" "testing" ) +var resolveUDPAddrTests = []struct { + net string + litAddr string + addr *UDPAddr + err error +}{ + {"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, + {"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil}, + + {"udp", "[::1]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1}, nil}, + {"udp6", "[::1]:65534", &UDPAddr{IP: ParseIP("::1"), Port: 65534}, nil}, + + {"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior + {"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior + + {"sip", "127.0.0.1:0", nil, UnknownNetworkError("sip")}, +} + +func TestResolveUDPAddr(t *testing.T) { + for _, tt := range resolveUDPAddrTests { + addr, err := ResolveUDPAddr(tt.net, tt.litAddr) + if err != tt.err { + t.Fatalf("ResolveUDPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + } + if !reflect.DeepEqual(addr, tt.addr) { + t.Fatalf("got %#v; expected %#v", addr, tt.addr) + } + } +} + func TestWriteToUDP(t *testing.T) { switch runtime.GOOS { case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return + t.Skipf("skipping test on %q", runtime.GOOS) } l, err := ListenPacket("udp", "127.0.0.1:0") @@ -87,3 +117,32 @@ func testWriteToPacketConn(t *testing.T, raddr string) { t.Fatal("Write should fail") } } + +var udpConnLocalNameTests = []struct { + net string + laddr *UDPAddr +}{ + {"udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}}, + {"udp4", &UDPAddr{}}, + {"udp4", nil}, +} + +func TestUDPConnLocalName(t *testing.T) { + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + + for _, tt := range udpConnLocalNameTests { + c, err := ListenUDP(tt.net, tt.laddr) + if err != nil { + t.Errorf("ListenUDP failed: %v", err) + return + } + defer c.Close() + la := c.LocalAddr() + if a, ok := la.(*UDPAddr); !ok || a.Port == 0 { + t.Errorf("got %v; expected a proper address with non-zero port number", la) + return + } + } +} diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go index b3520cf09..6e5e90268 100644 --- a/src/pkg/net/udpsock.go +++ b/src/pkg/net/udpsock.go @@ -6,10 +6,15 @@ package net +import "errors" + +var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP") + // UDPAddr represents the address of a UDP end point. type UDPAddr struct { IP IP Port int + Zone string // IPv6 scoped addressing zone } // Network returns the address's network name, "udp". @@ -28,9 +33,16 @@ func (a *UDPAddr) String() string { // "udp4" or "udp6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]:80". func ResolveUDPAddr(net, addr string) (*UDPAddr, error) { - ip, port, err := hostPortToIP(net, addr) + switch net { + case "udp", "udp4", "udp6": + case "": // a hint wildcard for Go 1.0 undocumented behavior + net = "udp" + default: + return nil, UnknownNetworkError(net) + } + a, err := resolveInternetAddr(net, addr, noDeadline) if err != nil { return nil, err } - return &UDPAddr{ip, port}, nil + return a.(*UDPAddr), nil } diff --git a/src/pkg/net/udpsock_plan9.go b/src/pkg/net/udpsock_plan9.go index 4f298a42f..2a7e3d19c 100644 --- a/src/pkg/net/udpsock_plan9.go +++ b/src/pkg/net/udpsock_plan9.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// UDP for Plan 9 +// UDP sockets for Plan 9 package net @@ -16,44 +16,26 @@ import ( // UDPConn is the implementation of the Conn and PacketConn // interfaces for UDP network connections. type UDPConn struct { - plan9Conn + conn } -// SetDeadline implements the Conn SetDeadline method. -func (c *UDPConn) SetDeadline(t time.Time) error { - return syscall.EPLAN9 +func newUDPConn(fd *netFD) *UDPConn { + return &UDPConn{conn{fd}} } -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *UDPConn) SetReadDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *UDPConn) SetWriteDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// UDP-specific methods. - // ReadFromUDP reads a UDP packet from c, copying the payload into b. // It returns the number of bytes copied into b and the return address // that was on the packet. // -// ReadFromUDP can be made to time out and return an error with Timeout() == true -// after a fixed time limit; see SetDeadline and SetReadDeadline. +// ReadFromUDP can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetReadDeadline. func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { - if !c.ok() { + if !c.ok() || c.fd.data == nil { return 0, nil, syscall.EINVAL } - if c.data == nil { - c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) - if err != nil { - return 0, nil, err - } - } buf := make([]byte, udpHeaderSize+len(b)) - m, err := c.data.Read(buf) + m, err := c.fd.data.Read(buf) if err != nil { return } @@ -64,62 +46,80 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { h, buf := unmarshalUDPHeader(buf) n = copy(b, buf) - return n, &UDPAddr{h.raddr, int(h.rport)}, nil + return n, &UDPAddr{IP: h.raddr, Port: int(h.rport)}, nil } // ReadFrom implements the PacketConn ReadFrom method. -func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { +func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL } return c.ReadFromUDP(b) } -// WriteToUDP writes a UDP packet to addr via c, copying the payload from b. +// ReadMsgUDP reads a packet from c, copying the payload into b and +// the associdated out-of-band data into oob. It returns the number +// of bytes copied into b, the number of bytes copied into oob, the +// flags that were set on the packet and the source address of the +// packet. +func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { + return 0, 0, 0, nil, syscall.EPLAN9 +} + +// WriteToUDP writes a UDP packet to addr via c, copying the payload +// from b. // -// WriteToUDP can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetDeadline and SetWriteDeadline. -// On packet-oriented connections, write timeouts are rare. -func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) { - if !c.ok() { +// WriteToUDP can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetWriteDeadline. On packet-oriented connections, write timeouts +// are rare. +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { + if !c.ok() || c.fd.data == nil { return 0, syscall.EINVAL } - if c.data == nil { - c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0) - if err != nil { - return 0, err - } - } h := new(udpHeader) h.raddr = addr.IP.To16() - h.laddr = c.laddr.(*UDPAddr).IP.To16() + h.laddr = c.fd.laddr.(*UDPAddr).IP.To16() h.ifcaddr = IPv6zero // ignored (receive only) h.rport = uint16(addr.Port) - h.lport = uint16(c.laddr.(*UDPAddr).Port) + h.lport = uint16(c.fd.laddr.(*UDPAddr).Port) buf := make([]byte, udpHeaderSize+len(b)) i := copy(buf, h.Bytes()) copy(buf[i:], b) - return c.data.Write(buf) + return c.fd.data.Write(buf) } // WriteTo implements the PacketConn WriteTo method. -func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) { +func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { if !c.ok() { return 0, syscall.EINVAL } a, ok := addr.(*UDPAddr) if !ok { - return 0, &OpError{"write", c.dir, addr, syscall.EINVAL} + return 0, &OpError{"write", c.fd.dir, addr, syscall.EINVAL} } return c.WriteToUDP(b, a) } +// WriteMsgUDP writes a packet to addr via c, copying the payload from +// b and the associated out-of-band data from oob. It returns the +// number of payload and out-of-band bytes written. +func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { + return 0, 0, syscall.EPLAN9 +} + // DialUDP connects to the remote address raddr on the network net, -// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used -// as the local address for the connection. -func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) { +// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is +// used as the local address for the connection. +func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) { + return dialUDP(net, laddr, raddr, noDeadline) +} + +func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) { + if !deadline.IsZero() { + panic("net.dialUDP: deadline not implemented on Plan 9") + } switch net { case "udp", "udp4", "udp6": default: @@ -128,11 +128,11 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) { if raddr == nil { return nil, &OpError{"dial", net, nil, errMissingAddress} } - c1, err := dialPlan9(net, laddr, raddr) + fd, err := dialPlan9(net, laddr, raddr) if err != nil { - return + return nil, err } - return &UDPConn{*c1}, nil + return newUDPConn(fd), nil } const udpHeaderSize = 16*3 + 2*2 @@ -163,34 +163,38 @@ func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) { return h, b } -// ListenUDP listens for incoming UDP packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send UDP -// packets with per-packet addressing. -func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) { +// ListenUDP listens for incoming UDP packets addressed to the local +// address laddr. The returned connection c's ReadFrom and WriteTo +// methods can be used to receive and send UDP packets with per-packet +// addressing. +func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { switch net { case "udp", "udp4", "udp6": default: return nil, UnknownNetworkError(net) } if laddr == nil { - return nil, &OpError{"listen", net, nil, errMissingAddress} + laddr = &UDPAddr{} } l, err := listenPlan9(net, laddr) if err != nil { - return + return nil, err } _, err = l.ctl.WriteString("headers") if err != nil { - return + return nil, err + } + l.data, err = os.OpenFile(l.dir+"/data", os.O_RDWR, 0) + if err != nil { + return nil, err } - return &UDPConn{*l.plan9Conn()}, nil + return newUDPConn(l.netFD()), nil } // ListenMulticastUDP listens for incoming multicast UDP packets -// addressed to the group address gaddr on ifi, which specifies -// the interface to join. ListenMulticastUDP uses default -// multicast interface if ifi is nil. +// addressed to the group address gaddr on ifi, which specifies the +// interface to join. ListenMulticastUDP uses default multicast +// interface if ifi is nil. func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) { return nil, syscall.EPLAN9 } diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go index 9c6b6d393..385cd902e 100644 --- a/src/pkg/net/udpsock_posix.go +++ b/src/pkg/net/udpsock_posix.go @@ -4,25 +4,21 @@ // +build darwin freebsd linux netbsd openbsd windows -// UDP sockets +// UDP sockets for POSIX package net import ( - "errors" - "os" "syscall" "time" ) -var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP") - func sockaddrToUDP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { case *syscall.SockaddrInet4: - return &UDPAddr{sa.Addr[0:], sa.Port} + return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} case *syscall.SockaddrInet6: - return &UDPAddr{sa.Addr[0:], sa.Port} + return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} } return nil } @@ -45,7 +41,7 @@ func (a *UDPAddr) isWildcard() bool { } func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, a.Port) + return ipToSockaddr(family, a.IP, a.Port, a.Zone) } func (a *UDPAddr) toAddr() sockaddr { @@ -58,98 +54,10 @@ func (a *UDPAddr) toAddr() sockaddr { // UDPConn is the implementation of the Conn and PacketConn // interfaces for UDP network connections. type UDPConn struct { - fd *netFD -} - -func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{fd} } - -func (c *UDPConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the Conn Read method. -func (c *UDPConn) Read(b []byte) (int, error) { - if !c.ok() { - return 0, syscall.EINVAL - } - return c.fd.Read(b) -} - -// Write implements the Conn Write method. -func (c *UDPConn) Write(b []byte) (int, error) { - if !c.ok() { - return 0, syscall.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the UDP connection. -func (c *UDPConn) Close() error { - if !c.ok() { - return syscall.EINVAL - } - return c.fd.Close() -} - -// LocalAddr returns the local network address. -func (c *UDPConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr -} - -// RemoteAddr returns the remote network address, a *UDPAddr. -func (c *UDPConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} - -// SetDeadline implements the Conn SetDeadline method. -func (c *UDPConn) SetDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setDeadline(c.fd, t) -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *UDPConn) SetReadDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setReadDeadline(c.fd, t) -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *UDPConn) SetWriteDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setWriteDeadline(c.fd, t) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *UDPConn) SetReadBuffer(bytes int) error { - if !c.ok() { - return syscall.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *UDPConn) SetWriteBuffer(bytes int) error { - if !c.ok() { - return syscall.EINVAL - } - return setWriteBuffer(c.fd, bytes) + conn } -// UDP-specific methods. +func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} } // ReadFromUDP reads a UDP packet from c, copying the payload into b. // It returns the number of bytes copied into b and the return address @@ -164,9 +72,9 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { case *syscall.SockaddrInet4: - addr = &UDPAddr{sa.Addr[0:], sa.Port} + addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} case *syscall.SockaddrInet6: - addr = &UDPAddr{sa.Addr[0:], sa.Port} + addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} } return } @@ -176,8 +84,28 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL } - n, uaddr, err := c.ReadFromUDP(b) - return n, uaddr.toAddr(), err + n, addr, err := c.ReadFromUDP(b) + return n, addr.toAddr(), err +} + +// ReadMsgUDP reads a packet from c, copying the payload into b and +// the associdated out-of-band data into oob. It returns the number +// of bytes copied into b, the number of bytes copied into oob, the +// flags that were set on the packet and the source address of the +// packet. +func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { + if !c.ok() { + return 0, 0, 0, nil, syscall.EINVAL + } + var sa syscall.Sockaddr + n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob) + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} + case *syscall.SockaddrInet6: + addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} + } + return } // WriteToUDP writes a UDP packet to addr via c, copying the payload from b. @@ -212,15 +140,31 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { return c.WriteToUDP(b, a) } -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *UDPConn) File() (f *os.File, err error) { return c.fd.dup() } +// WriteMsgUDP writes a packet to addr via c, copying the payload from +// b and the associated out-of-band data from oob. It returns the +// number of payload and out-of-band bytes written. +func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { + if !c.ok() { + return 0, 0, syscall.EINVAL + } + if c.fd.isConnected { + return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected} + } + sa, err := addr.sockaddr(c.fd.family) + if err != nil { + return 0, 0, &OpError{"write", c.fd.net, addr, err} + } + return c.fd.WriteMsg(b, oob, sa) +} // DialUDP connects to the remote address raddr on the network net, // which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used // as the local address for the connection. func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) { + return dialUDP(net, laddr, raddr, noDeadline) +} + +func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) { switch net { case "udp", "udp4", "udp6": default: @@ -229,7 +173,7 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) { if raddr == nil { return nil, &OpError{"dial", net, nil, errMissingAddress} } - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) + fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) if err != nil { return nil, err } @@ -247,9 +191,9 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { return nil, UnknownNetworkError(net) } if laddr == nil { - return nil, &OpError{"listen", net, nil, errMissingAddress} + laddr = &UDPAddr{} } - fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) + fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) if err != nil { return nil, err } @@ -267,25 +211,22 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e return nil, UnknownNetworkError(net) } if gaddr == nil || gaddr.IP == nil { - return nil, &OpError{"listenmulticast", net, nil, errMissingAddress} + return nil, &OpError{"listen", net, nil, errMissingAddress} } - fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) + fd, err := internetSocket(net, gaddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) if err != nil { return nil, err } c := newUDPConn(fd) - ip4 := gaddr.IP.To4() - if ip4 != nil { - err := listenIPv4MulticastUDP(c, ifi, ip4) - if err != nil { + if ip4 := gaddr.IP.To4(); ip4 != nil { + if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil { c.Close() - return nil, err + return nil, &OpError{"listen", net, &IPAddr{IP: ip4}, err} } } else { - err := listenIPv6MulticastUDP(c, ifi, gaddr.IP) - if err != nil { + if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil { c.Close() - return nil, err + return nil, &OpError{"listen", net, &IPAddr{IP: gaddr.IP}, err} } } return c, nil @@ -293,17 +234,14 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error { if ifi != nil { - err := setIPv4MulticastInterface(c.fd, ifi) - if err != nil { + if err := setIPv4MulticastInterface(c.fd, ifi); err != nil { return err } } - err := setIPv4MulticastLoopback(c.fd, false) - if err != nil { + if err := setIPv4MulticastLoopback(c.fd, false); err != nil { return err } - err = joinIPv4GroupUDP(c, ifi, ip) - if err != nil { + if err := joinIPv4Group(c.fd, ifi, ip); err != nil { return err } return nil @@ -311,50 +249,15 @@ func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error { func listenIPv6MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error { if ifi != nil { - err := setIPv6MulticastInterface(c.fd, ifi) - if err != nil { + if err := setIPv6MulticastInterface(c.fd, ifi); err != nil { return err } } - err := setIPv6MulticastLoopback(c.fd, false) - if err != nil { + if err := setIPv6MulticastLoopback(c.fd, false); err != nil { return err } - err = joinIPv6GroupUDP(c, ifi, ip) - if err != nil { + if err := joinIPv6Group(c.fd, ifi, ip); err != nil { return err } return nil } - -func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { - err := joinIPv4Group(c.fd, ifi, ip) - if err != nil { - return &OpError{"joinipv4group", c.fd.net, &IPAddr{ip}, err} - } - return nil -} - -func leaveIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { - err := leaveIPv4Group(c.fd, ifi, ip) - if err != nil { - return &OpError{"leaveipv4group", c.fd.net, &IPAddr{ip}, err} - } - return nil -} - -func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { - err := joinIPv6Group(c.fd, ifi, ip) - if err != nil { - return &OpError{"joinipv6group", c.fd.net, &IPAddr{ip}, err} - } - return nil -} - -func leaveIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { - err := leaveIPv6Group(c.fd, ifi, ip) - if err != nil { - return &OpError{"leaveipv6group", c.fd.net, &IPAddr{ip}, err} - } - return nil -} diff --git a/src/pkg/net/unicast_posix_test.go b/src/pkg/net/unicast_posix_test.go new file mode 100644 index 000000000..a8855cab7 --- /dev/null +++ b/src/pkg/net/unicast_posix_test.go @@ -0,0 +1,516 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9 + +package net + +import ( + "runtime" + "syscall" + "testing" +) + +var listenerTests = []struct { + net string + laddr string + ipv6 bool // test with underlying AF_INET6 socket + wildcard bool // test with wildcard address +}{ + {net: "tcp", laddr: "", wildcard: true}, + {net: "tcp", laddr: "0.0.0.0", wildcard: true}, + {net: "tcp", laddr: "[::ffff:0.0.0.0]", wildcard: true}, + {net: "tcp", laddr: "[::]", ipv6: true, wildcard: true}, + + {net: "tcp", laddr: "127.0.0.1"}, + {net: "tcp", laddr: "[::ffff:127.0.0.1]"}, + {net: "tcp", laddr: "[::1]", ipv6: true}, + + {net: "tcp4", laddr: "", wildcard: true}, + {net: "tcp4", laddr: "0.0.0.0", wildcard: true}, + {net: "tcp4", laddr: "[::ffff:0.0.0.0]", wildcard: true}, + + {net: "tcp4", laddr: "127.0.0.1"}, + {net: "tcp4", laddr: "[::ffff:127.0.0.1]"}, + + {net: "tcp6", laddr: "", ipv6: true, wildcard: true}, + {net: "tcp6", laddr: "[::]", ipv6: true, wildcard: true}, + + {net: "tcp6", laddr: "[::1]", ipv6: true}, +} + +// TestTCPListener tests both single and double listen to a test +// listener with same address family, same listening address and +// same port. +func TestTCPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + for _, tt := range listenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + if tt.ipv6 && !supportsIPv6 { + continue + } + l1, port := usableListenPort(t, tt.net, tt.laddr) + checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) + l2, err := Listen(tt.net, tt.laddr+":"+port) + checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) + l1.Close() + } +} + +// TestUDPListener tests both single and double listen to a test +// listener with same address family, same listening address and +// same port. +func TestUDPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + toudpnet := func(net string) string { + switch net { + case "tcp": + return "udp" + case "tcp4": + return "udp4" + case "tcp6": + return "udp6" + } + return "" + } + + for _, tt := range listenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + if tt.ipv6 && !supportsIPv6 { + continue + } + tt.net = toudpnet(tt.net) + l1, port := usableListenPacketPort(t, tt.net, tt.laddr) + checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) + l2, err := ListenPacket(tt.net, tt.laddr+":"+port) + checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) + l1.Close() + } +} + +func TestSimpleTCPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + return + } + + for _, tt := range listenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + if tt.ipv6 { + continue + } + l1, port := usableListenPort(t, tt.net, tt.laddr) + checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) + l2, err := Listen(tt.net, tt.laddr+":"+port) + checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) + l1.Close() + } +} + +func TestSimpleUDPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + return + } + + toudpnet := func(net string) string { + switch net { + case "tcp": + return "udp" + case "tcp4": + return "udp4" + case "tcp6": + return "udp6" + } + return "" + } + + for _, tt := range listenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + if tt.ipv6 { + continue + } + tt.net = toudpnet(tt.net) + l1, port := usableListenPacketPort(t, tt.net, tt.laddr) + checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) + l2, err := ListenPacket(tt.net, tt.laddr+":"+port) + checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) + l1.Close() + } +} + +var dualStackListenerTests = []struct { + net1 string // first listener + laddr1 string + net2 string // second listener + laddr2 string + wildcard bool // test with wildcard address + xerr error // expected error value, nil or other +}{ + // Test cases and expected results for the attemping 2nd listen on the same port + // 1st listen 2nd listen darwin freebsd linux openbsd + // ------------------------------------------------------------------------------------ + // "tcp" "" "tcp" "" - - - - + // "tcp" "" "tcp" "0.0.0.0" - - - - + // "tcp" "0.0.0.0" "tcp" "" - - - - + // ------------------------------------------------------------------------------------ + // "tcp" "" "tcp" "[::]" - - - ok + // "tcp" "[::]" "tcp" "" - - - ok + // "tcp" "0.0.0.0" "tcp" "[::]" - - - ok + // "tcp" "[::]" "tcp" "0.0.0.0" - - - ok + // "tcp" "[::ffff:0.0.0.0]" "tcp" "[::]" - - - ok + // "tcp" "[::]" "tcp" "[::ffff:0.0.0.0]" - - - ok + // ------------------------------------------------------------------------------------ + // "tcp4" "" "tcp6" "" ok ok ok ok + // "tcp6" "" "tcp4" "" ok ok ok ok + // "tcp4" "0.0.0.0" "tcp6" "[::]" ok ok ok ok + // "tcp6" "[::]" "tcp4" "0.0.0.0" ok ok ok ok + // ------------------------------------------------------------------------------------ + // "tcp" "127.0.0.1" "tcp" "[::1]" ok ok ok ok + // "tcp" "[::1]" "tcp" "127.0.0.1" ok ok ok ok + // "tcp4" "127.0.0.1" "tcp6" "[::1]" ok ok ok ok + // "tcp6" "[::1]" "tcp4" "127.0.0.1" ok ok ok ok + // + // Platform default configurations: + // darwin, kernel version 11.3.0 + // net.inet6.ip6.v6only=0 (overridable by sysctl or IPV6_V6ONLY option) + // freebsd, kernel version 8.2 + // net.inet6.ip6.v6only=1 (overridable by sysctl or IPV6_V6ONLY option) + // linux, kernel version 3.0.0 + // net.ipv6.bindv6only=0 (overridable by sysctl or IPV6_V6ONLY option) + // openbsd, kernel version 5.0 + // net.inet6.ip6.v6only=1 (overriding is prohibited) + + {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, + + {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "[::ffff:0.0.0.0]", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, + {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "[::ffff:0.0.0.0]", wildcard: true, xerr: syscall.EADDRINUSE}, + + {net1: "tcp4", laddr1: "", net2: "tcp6", laddr2: "", wildcard: true}, + {net1: "tcp6", laddr1: "", net2: "tcp4", laddr2: "", wildcard: true}, + {net1: "tcp4", laddr1: "0.0.0.0", net2: "tcp6", laddr2: "[::]", wildcard: true}, + {net1: "tcp6", laddr1: "[::]", net2: "tcp4", laddr2: "0.0.0.0", wildcard: true}, + + {net1: "tcp", laddr1: "127.0.0.1", net2: "tcp", laddr2: "[::1]"}, + {net1: "tcp", laddr1: "[::1]", net2: "tcp", laddr2: "127.0.0.1"}, + {net1: "tcp4", laddr1: "127.0.0.1", net2: "tcp6", laddr2: "[::1]"}, + {net1: "tcp6", laddr1: "[::1]", net2: "tcp4", laddr2: "127.0.0.1"}, +} + +// TestDualStackTCPListener tests both single and double listen +// to a test listener with various address families, differnet +// listening address and same port. +func TestDualStackTCPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + if !supportsIPv6 { + return + } + + for _, tt := range dualStackListenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + switch runtime.GOOS { + case "openbsd": + if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) { + tt.xerr = nil + } + } + l1, port := usableListenPort(t, tt.net1, tt.laddr1) + laddr := tt.laddr1 + ":" + port + checkFirstListener(t, tt.net1, laddr, l1) + laddr = tt.laddr2 + ":" + port + l2, err := Listen(tt.net2, laddr) + checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2) + l1.Close() + } +} + +// TestDualStackUDPListener tests both single and double listen +// to a test listener with various address families, differnet +// listening address and same port. +func TestDualStackUDPListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + if !supportsIPv6 { + return + } + + toudpnet := func(net string) string { + switch net { + case "tcp": + return "udp" + case "tcp4": + return "udp4" + case "tcp6": + return "udp6" + } + return "" + } + + for _, tt := range dualStackListenerTests { + if tt.wildcard && (testing.Short() || !*testExternal) { + continue + } + tt.net1 = toudpnet(tt.net1) + tt.net2 = toudpnet(tt.net2) + switch runtime.GOOS { + case "openbsd": + if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) { + tt.xerr = nil + } + } + l1, port := usableListenPacketPort(t, tt.net1, tt.laddr1) + laddr := tt.laddr1 + ":" + port + checkFirstListener(t, tt.net1, laddr, l1) + laddr = tt.laddr2 + ":" + port + l2, err := ListenPacket(tt.net2, laddr) + checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2) + l1.Close() + } +} + +func usableListenPort(t *testing.T, net, laddr string) (l Listener, port string) { + var nladdr string + var err error + switch net { + default: + panic("usableListenPort net=" + net) + case "tcp", "tcp4", "tcp6": + l, err = Listen(net, laddr+":0") + if err != nil { + t.Fatalf("Probe Listen(%q, %q) failed: %v", net, laddr, err) + } + nladdr = l.(*TCPListener).Addr().String() + } + _, port, err = SplitHostPort(nladdr) + if err != nil { + t.Fatalf("SplitHostPort failed: %v", err) + } + return l, port +} + +func usableListenPacketPort(t *testing.T, net, laddr string) (l PacketConn, port string) { + var nladdr string + var err error + switch net { + default: + panic("usableListenPacketPort net=" + net) + case "udp", "udp4", "udp6": + l, err = ListenPacket(net, laddr+":0") + if err != nil { + t.Fatalf("Probe ListenPacket(%q, %q) failed: %v", net, laddr, err) + } + nladdr = l.(*UDPConn).LocalAddr().String() + } + _, port, err = SplitHostPort(nladdr) + if err != nil { + t.Fatalf("SplitHostPort failed: %v", err) + } + return l, port +} + +func differentWildcardAddr(i, j string) bool { + if (i == "" || i == "0.0.0.0" || i == "::ffff:0.0.0.0") && (j == "" || j == "0.0.0.0" || j == "::ffff:0.0.0.0") { + return false + } + if i == "[::]" && j == "[::]" { + return false + } + return true +} + +func checkFirstListener(t *testing.T, net, laddr string, l interface{}) { + switch net { + case "tcp": + fd := l.(*TCPListener).fd + checkDualStackAddrFamily(t, net, laddr, fd) + case "tcp4": + fd := l.(*TCPListener).fd + if fd.family != syscall.AF_INET { + t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET) + } + case "tcp6": + fd := l.(*TCPListener).fd + if fd.family != syscall.AF_INET6 { + t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) + } + case "udp": + fd := l.(*UDPConn).fd + checkDualStackAddrFamily(t, net, laddr, fd) + case "udp4": + fd := l.(*UDPConn).fd + if fd.family != syscall.AF_INET { + t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET) + } + case "udp6": + fd := l.(*UDPConn).fd + if fd.family != syscall.AF_INET6 { + t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) + } + default: + t.Fatalf("Unexpected network: %q", net) + } +} + +func checkSecondListener(t *testing.T, net, laddr string, err error, l interface{}) { + switch net { + case "tcp", "tcp4", "tcp6": + if err == nil { + l.(*TCPListener).Close() + t.Fatalf("Second Listen(%q, %q) should fail", net, laddr) + } + case "udp", "udp4", "udp6": + if err == nil { + l.(*UDPConn).Close() + t.Fatalf("Second ListenPacket(%q, %q) should fail", net, laddr) + } + default: + t.Fatalf("Unexpected network: %q", net) + } +} + +func checkDualStackSecondListener(t *testing.T, net, laddr string, xerr, err error, l interface{}) { + switch net { + case "tcp", "tcp4", "tcp6": + if xerr == nil && err != nil || xerr != nil && err == nil { + t.Fatalf("Second Listen(%q, %q) returns %v, expected %v", net, laddr, err, xerr) + } + l.(*TCPListener).Close() + case "udp", "udp4", "udp6": + if xerr == nil && err != nil || xerr != nil && err == nil { + t.Fatalf("Second ListenPacket(%q, %q) returns %v, expected %v", net, laddr, err, xerr) + } + l.(*UDPConn).Close() + default: + t.Fatalf("Unexpected network: %q", net) + } +} + +func checkDualStackAddrFamily(t *testing.T, net, laddr string, fd *netFD) { + switch a := fd.laddr.(type) { + case *TCPAddr: + // If a node under test supports both IPv6 capability + // and IPv6 IPv4-mapping capability, we can assume + // that the node listens on a wildcard address with an + // AF_INET6 socket. + if supportsIPv4map && fd.laddr.(*TCPAddr).isWildcard() { + if fd.family != syscall.AF_INET6 { + t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) + } + } else { + if fd.family != a.family() { + t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family()) + } + } + case *UDPAddr: + // If a node under test supports both IPv6 capability + // and IPv6 IPv4-mapping capability, we can assume + // that the node listens on a wildcard address with an + // AF_INET6 socket. + if supportsIPv4map && fd.laddr.(*UDPAddr).isWildcard() { + if fd.family != syscall.AF_INET6 { + t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) + } + } else { + if fd.family != a.family() { + t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family()) + } + } + default: + t.Fatalf("Unexpected protocol address type: %T", a) + } +} + +var prohibitionaryDialArgTests = []struct { + net string + addr string +}{ + {"tcp6", "127.0.0.1"}, + {"tcp6", "[::ffff:127.0.0.1]"}, +} + +func TestProhibitionaryDialArgs(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + // This test requires both IPv6 and IPv6 IPv4-mapping functionality. + if !supportsIPv4map || testing.Short() || !*testExternal { + return + } + + l, port := usableListenPort(t, "tcp", "[::]") + defer l.Close() + + for _, tt := range prohibitionaryDialArgTests { + c, err := Dial(tt.net, tt.addr+":"+port) + if err == nil { + c.Close() + t.Fatalf("Dial(%q, %q) should fail", tt.net, tt.addr) + } + } +} + +func TestWildWildcardListener(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + + defer func() { + if recover() != nil { + t.Fatalf("panicked") + } + }() + + if ln, err := Listen("tcp", ""); err == nil { + ln.Close() + } + if ln, err := ListenPacket("udp", ""); err == nil { + ln.Close() + } + if ln, err := ListenTCP("tcp", nil); err == nil { + ln.Close() + } + if ln, err := ListenUDP("udp", nil); err == nil { + ln.Close() + } + if ln, err := ListenIP("ip:icmp", nil); err == nil { + ln.Close() + } +} diff --git a/src/pkg/net/unicast_test.go b/src/pkg/net/unicast_test.go deleted file mode 100644 index e5dd013db..000000000 --- a/src/pkg/net/unicast_test.go +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package net - -import ( - "runtime" - "syscall" - "testing" -) - -var listenerTests = []struct { - net string - laddr string - ipv6 bool // test with underlying AF_INET6 socket - wildcard bool // test with wildcard address -}{ - {net: "tcp", laddr: "", wildcard: true}, - {net: "tcp", laddr: "0.0.0.0", wildcard: true}, - {net: "tcp", laddr: "[::ffff:0.0.0.0]", wildcard: true}, - {net: "tcp", laddr: "[::]", ipv6: true, wildcard: true}, - - {net: "tcp", laddr: "127.0.0.1"}, - {net: "tcp", laddr: "[::ffff:127.0.0.1]"}, - {net: "tcp", laddr: "[::1]", ipv6: true}, - - {net: "tcp4", laddr: "", wildcard: true}, - {net: "tcp4", laddr: "0.0.0.0", wildcard: true}, - {net: "tcp4", laddr: "[::ffff:0.0.0.0]", wildcard: true}, - - {net: "tcp4", laddr: "127.0.0.1"}, - {net: "tcp4", laddr: "[::ffff:127.0.0.1]"}, - - {net: "tcp6", laddr: "", ipv6: true, wildcard: true}, - {net: "tcp6", laddr: "[::]", ipv6: true, wildcard: true}, - - {net: "tcp6", laddr: "[::1]", ipv6: true}, -} - -// TestTCPListener tests both single and double listen to a test -// listener with same address family, same listening address and -// same port. -func TestTCPListener(t *testing.T) { - switch runtime.GOOS { - case "plan9", "windows": - t.Logf("skipping test on %q", runtime.GOOS) - return - } - - for _, tt := range listenerTests { - if tt.wildcard && (testing.Short() || !*testExternal) { - continue - } - if tt.ipv6 && !supportsIPv6 { - continue - } - l1, port := usableListenPort(t, tt.net, tt.laddr) - checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) - l2, err := Listen(tt.net, tt.laddr+":"+port) - checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) - fd := l1.(*TCPListener).fd - switch fd.family { - case syscall.AF_INET: - testIPv4UnicastSocketOptions(t, fd) - case syscall.AF_INET6: - testIPv6UnicastSocketOptions(t, fd) - } - l1.Close() - } -} - -// TestUDPListener tests both single and double listen to a test -// listener with same address family, same listening address and -// same port. -func TestUDPListener(t *testing.T) { - switch runtime.GOOS { - case "plan9", "windows": - t.Logf("skipping test on %q", runtime.GOOS) - return - } - - toudpnet := func(net string) string { - switch net { - case "tcp": - return "udp" - case "tcp4": - return "udp4" - case "tcp6": - return "udp6" - } - return "" - } - - for _, tt := range listenerTests { - if tt.wildcard && (testing.Short() || !*testExternal) { - continue - } - if tt.ipv6 && !supportsIPv6 { - continue - } - tt.net = toudpnet(tt.net) - l1, port := usableListenPacketPort(t, tt.net, tt.laddr) - checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) - l2, err := ListenPacket(tt.net, tt.laddr+":"+port) - checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) - fd := l1.(*UDPConn).fd - switch fd.family { - case syscall.AF_INET: - testIPv4UnicastSocketOptions(t, fd) - case syscall.AF_INET6: - testIPv6UnicastSocketOptions(t, fd) - } - l1.Close() - } -} - -func TestSimpleTCPListener(t *testing.T) { - switch runtime.GOOS { - case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return - } - - for _, tt := range listenerTests { - if tt.wildcard && (testing.Short() || !*testExternal) { - continue - } - if tt.ipv6 { - continue - } - l1, port := usableListenPort(t, tt.net, tt.laddr) - checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) - l2, err := Listen(tt.net, tt.laddr+":"+port) - checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) - l1.Close() - } -} - -func TestSimpleUDPListener(t *testing.T) { - switch runtime.GOOS { - case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return - } - - toudpnet := func(net string) string { - switch net { - case "tcp": - return "udp" - case "tcp4": - return "udp4" - case "tcp6": - return "udp6" - } - return "" - } - - for _, tt := range listenerTests { - if tt.wildcard && (testing.Short() || !*testExternal) { - continue - } - if tt.ipv6 { - continue - } - tt.net = toudpnet(tt.net) - l1, port := usableListenPacketPort(t, tt.net, tt.laddr) - checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) - l2, err := ListenPacket(tt.net, tt.laddr+":"+port) - checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) - l1.Close() - } -} - -var dualStackListenerTests = []struct { - net1 string // first listener - laddr1 string - net2 string // second listener - laddr2 string - wildcard bool // test with wildcard address - xerr error // expected error value, nil or other -}{ - // Test cases and expected results for the attemping 2nd listen on the same port - // 1st listen 2nd listen darwin freebsd linux openbsd - // ------------------------------------------------------------------------------------ - // "tcp" "" "tcp" "" - - - - - // "tcp" "" "tcp" "0.0.0.0" - - - - - // "tcp" "0.0.0.0" "tcp" "" - - - - - // ------------------------------------------------------------------------------------ - // "tcp" "" "tcp" "[::]" - - - ok - // "tcp" "[::]" "tcp" "" - - - ok - // "tcp" "0.0.0.0" "tcp" "[::]" - - - ok - // "tcp" "[::]" "tcp" "0.0.0.0" - - - ok - // "tcp" "[::ffff:0.0.0.0]" "tcp" "[::]" - - - ok - // "tcp" "[::]" "tcp" "[::ffff:0.0.0.0]" - - - ok - // ------------------------------------------------------------------------------------ - // "tcp4" "" "tcp6" "" ok ok ok ok - // "tcp6" "" "tcp4" "" ok ok ok ok - // "tcp4" "0.0.0.0" "tcp6" "[::]" ok ok ok ok - // "tcp6" "[::]" "tcp4" "0.0.0.0" ok ok ok ok - // ------------------------------------------------------------------------------------ - // "tcp" "127.0.0.1" "tcp" "[::1]" ok ok ok ok - // "tcp" "[::1]" "tcp" "127.0.0.1" ok ok ok ok - // "tcp4" "127.0.0.1" "tcp6" "[::1]" ok ok ok ok - // "tcp6" "[::1]" "tcp4" "127.0.0.1" ok ok ok ok - // - // Platform default configurations: - // darwin, kernel version 11.3.0 - // net.inet6.ip6.v6only=0 (overridable by sysctl or IPV6_V6ONLY option) - // freebsd, kernel version 8.2 - // net.inet6.ip6.v6only=1 (overridable by sysctl or IPV6_V6ONLY option) - // linux, kernel version 3.0.0 - // net.ipv6.bindv6only=0 (overridable by sysctl or IPV6_V6ONLY option) - // openbsd, kernel version 5.0 - // net.inet6.ip6.v6only=1 (overriding is prohibited) - - {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, - {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE}, - {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, - - {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, - {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, - {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, - {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE}, - {net1: "tcp", laddr1: "[::ffff:0.0.0.0]", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, - {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "[::ffff:0.0.0.0]", wildcard: true, xerr: syscall.EADDRINUSE}, - - {net1: "tcp4", laddr1: "", net2: "tcp6", laddr2: "", wildcard: true}, - {net1: "tcp6", laddr1: "", net2: "tcp4", laddr2: "", wildcard: true}, - {net1: "tcp4", laddr1: "0.0.0.0", net2: "tcp6", laddr2: "[::]", wildcard: true}, - {net1: "tcp6", laddr1: "[::]", net2: "tcp4", laddr2: "0.0.0.0", wildcard: true}, - - {net1: "tcp", laddr1: "127.0.0.1", net2: "tcp", laddr2: "[::1]"}, - {net1: "tcp", laddr1: "[::1]", net2: "tcp", laddr2: "127.0.0.1"}, - {net1: "tcp4", laddr1: "127.0.0.1", net2: "tcp6", laddr2: "[::1]"}, - {net1: "tcp6", laddr1: "[::1]", net2: "tcp4", laddr2: "127.0.0.1"}, -} - -// TestDualStackTCPListener tests both single and double listen -// to a test listener with various address families, differnet -// listening address and same port. -func TestDualStackTCPListener(t *testing.T) { - switch runtime.GOOS { - case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return - } - if !supportsIPv6 { - return - } - - for _, tt := range dualStackListenerTests { - if tt.wildcard && (testing.Short() || !*testExternal) { - continue - } - switch runtime.GOOS { - case "openbsd": - if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) { - tt.xerr = nil - } - } - l1, port := usableListenPort(t, tt.net1, tt.laddr1) - laddr := tt.laddr1 + ":" + port - checkFirstListener(t, tt.net1, laddr, l1) - laddr = tt.laddr2 + ":" + port - l2, err := Listen(tt.net2, laddr) - checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2) - l1.Close() - } -} - -// TestDualStackUDPListener tests both single and double listen -// to a test listener with various address families, differnet -// listening address and same port. -func TestDualStackUDPListener(t *testing.T) { - switch runtime.GOOS { - case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return - } - if !supportsIPv6 { - return - } - - toudpnet := func(net string) string { - switch net { - case "tcp": - return "udp" - case "tcp4": - return "udp4" - case "tcp6": - return "udp6" - } - return "" - } - - for _, tt := range dualStackListenerTests { - if tt.wildcard && (testing.Short() || !*testExternal) { - continue - } - tt.net1 = toudpnet(tt.net1) - tt.net2 = toudpnet(tt.net2) - switch runtime.GOOS { - case "openbsd": - if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) { - tt.xerr = nil - } - } - l1, port := usableListenPacketPort(t, tt.net1, tt.laddr1) - laddr := tt.laddr1 + ":" + port - checkFirstListener(t, tt.net1, laddr, l1) - laddr = tt.laddr2 + ":" + port - l2, err := ListenPacket(tt.net2, laddr) - checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2) - l1.Close() - } -} - -func usableListenPort(t *testing.T, net, laddr string) (l Listener, port string) { - var nladdr string - var err error - switch net { - default: - panic("usableListenPort net=" + net) - case "tcp", "tcp4", "tcp6": - l, err = Listen(net, laddr+":0") - if err != nil { - t.Fatalf("Probe Listen(%q, %q) failed: %v", net, laddr, err) - } - nladdr = l.(*TCPListener).Addr().String() - } - _, port, err = SplitHostPort(nladdr) - if err != nil { - t.Fatalf("SplitHostPort failed: %v", err) - } - return l, port -} - -func usableListenPacketPort(t *testing.T, net, laddr string) (l PacketConn, port string) { - var nladdr string - var err error - switch net { - default: - panic("usableListenPacketPort net=" + net) - case "udp", "udp4", "udp6": - l, err = ListenPacket(net, laddr+":0") - if err != nil { - t.Fatalf("Probe ListenPacket(%q, %q) failed: %v", net, laddr, err) - } - nladdr = l.(*UDPConn).LocalAddr().String() - } - _, port, err = SplitHostPort(nladdr) - if err != nil { - t.Fatalf("SplitHostPort failed: %v", err) - } - return l, port -} - -func differentWildcardAddr(i, j string) bool { - if (i == "" || i == "0.0.0.0" || i == "::ffff:0.0.0.0") && (j == "" || j == "0.0.0.0" || j == "::ffff:0.0.0.0") { - return false - } - if i == "[::]" && j == "[::]" { - return false - } - return true -} - -func checkFirstListener(t *testing.T, net, laddr string, l interface{}) { - switch net { - case "tcp": - fd := l.(*TCPListener).fd - checkDualStackAddrFamily(t, net, laddr, fd) - case "tcp4": - fd := l.(*TCPListener).fd - if fd.family != syscall.AF_INET { - t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET) - } - case "tcp6": - fd := l.(*TCPListener).fd - if fd.family != syscall.AF_INET6 { - t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) - } - case "udp": - fd := l.(*UDPConn).fd - checkDualStackAddrFamily(t, net, laddr, fd) - case "udp4": - fd := l.(*UDPConn).fd - if fd.family != syscall.AF_INET { - t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET) - } - case "udp6": - fd := l.(*UDPConn).fd - if fd.family != syscall.AF_INET6 { - t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) - } - default: - t.Fatalf("Unexpected network: %q", net) - } -} - -func checkSecondListener(t *testing.T, net, laddr string, err error, l interface{}) { - switch net { - case "tcp", "tcp4", "tcp6": - if err == nil { - l.(*TCPListener).Close() - t.Fatalf("Second Listen(%q, %q) should fail", net, laddr) - } - case "udp", "udp4", "udp6": - if err == nil { - l.(*UDPConn).Close() - t.Fatalf("Second ListenPacket(%q, %q) should fail", net, laddr) - } - default: - t.Fatalf("Unexpected network: %q", net) - } -} - -func checkDualStackSecondListener(t *testing.T, net, laddr string, xerr, err error, l interface{}) { - switch net { - case "tcp", "tcp4", "tcp6": - if xerr == nil && err != nil || xerr != nil && err == nil { - t.Fatalf("Second Listen(%q, %q) returns %v, expected %v", net, laddr, err, xerr) - } - l.(*TCPListener).Close() - case "udp", "udp4", "udp6": - if xerr == nil && err != nil || xerr != nil && err == nil { - t.Fatalf("Second ListenPacket(%q, %q) returns %v, expected %v", net, laddr, err, xerr) - } - l.(*UDPConn).Close() - default: - t.Fatalf("Unexpected network: %q", net) - } -} - -func checkDualStackAddrFamily(t *testing.T, net, laddr string, fd *netFD) { - switch a := fd.laddr.(type) { - case *TCPAddr: - // If a node under test supports both IPv6 capability - // and IPv6 IPv4-mapping capability, we can assume - // that the node listens on a wildcard address with an - // AF_INET6 socket. - if supportsIPv4map && fd.laddr.(*TCPAddr).isWildcard() { - if fd.family != syscall.AF_INET6 { - t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) - } - } else { - if fd.family != a.family() { - t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family()) - } - } - case *UDPAddr: - // If a node under test supports both IPv6 capability - // and IPv6 IPv4-mapping capability, we can assume - // that the node listens on a wildcard address with an - // AF_INET6 socket. - if supportsIPv4map && fd.laddr.(*UDPAddr).isWildcard() { - if fd.family != syscall.AF_INET6 { - t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) - } - } else { - if fd.family != a.family() { - t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family()) - } - } - default: - t.Fatalf("Unexpected protocol address type: %T", a) - } -} - -func testIPv4UnicastSocketOptions(t *testing.T, fd *netFD) { - _, err := ipv4TOS(fd) - if err != nil { - t.Fatalf("ipv4TOS failed: %v", err) - } - err = setIPv4TOS(fd, 1) - if err != nil { - t.Fatalf("setIPv4TOS failed: %v", err) - } - _, err = ipv4TTL(fd) - if err != nil { - t.Fatalf("ipv4TTL failed: %v", err) - } - err = setIPv4TTL(fd, 1) - if err != nil { - t.Fatalf("setIPv4TTL failed: %v", err) - } -} - -func testIPv6UnicastSocketOptions(t *testing.T, fd *netFD) { - _, err := ipv6TrafficClass(fd) - if err != nil { - t.Fatalf("ipv6TrafficClass failed: %v", err) - } - err = setIPv6TrafficClass(fd, 1) - if err != nil { - t.Fatalf("setIPv6TrafficClass failed: %v", err) - } - _, err = ipv6HopLimit(fd) - if err != nil { - t.Fatalf("ipv6HopLimit failed: %v", err) - } - err = setIPv6HopLimit(fd, 1) - if err != nil { - t.Fatalf("setIPv6HopLimit failed: %v", err) - } -} - -var prohibitionaryDialArgTests = []struct { - net string - addr string -}{ - {"tcp6", "127.0.0.1"}, - {"tcp6", "[::ffff:127.0.0.1]"}, -} - -func TestProhibitionaryDialArgs(t *testing.T) { - switch runtime.GOOS { - case "plan9": - t.Logf("skipping test on %q", runtime.GOOS) - return - } - // This test requires both IPv6 and IPv6 IPv4-mapping functionality. - if !supportsIPv4map || testing.Short() || !*testExternal { - return - } - - l, port := usableListenPort(t, "tcp", "[::]") - defer l.Close() - - for _, tt := range prohibitionaryDialArgTests { - c, err := Dial(tt.net, tt.addr+":"+port) - if err == nil { - c.Close() - t.Fatalf("Dial(%q, %q) should fail", tt.net, tt.addr) - } - } -} diff --git a/src/pkg/net/unix_test.go b/src/pkg/net/unix_test.go new file mode 100644 index 000000000..2eaabe86e --- /dev/null +++ b/src/pkg/net/unix_test.go @@ -0,0 +1,144 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !plan9,!windows + +package net + +import ( + "bytes" + "os" + "reflect" + "runtime" + "syscall" + "testing" + "time" +) + +func TestReadUnixgramWithUnnamedSocket(t *testing.T) { + addr := testUnixAddr() + la, err := ResolveUnixAddr("unixgram", addr) + if err != nil { + t.Fatalf("ResolveUnixAddr failed: %v", err) + } + c, err := ListenUnixgram("unixgram", la) + if err != nil { + t.Fatalf("ListenUnixgram failed: %v", err) + } + defer func() { + c.Close() + os.Remove(addr) + }() + + off := make(chan bool) + data := [5]byte{1, 2, 3, 4, 5} + + go func() { + defer func() { off <- true }() + s, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0) + if err != nil { + t.Errorf("syscall.Socket failed: %v", err) + return + } + defer syscall.Close(s) + rsa := &syscall.SockaddrUnix{Name: addr} + if err := syscall.Sendto(s, data[:], 0, rsa); err != nil { + t.Errorf("syscall.Sendto failed: %v", err) + return + } + }() + + <-off + b := make([]byte, 64) + c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + n, from, err := c.ReadFrom(b) + if err != nil { + t.Errorf("UnixConn.ReadFrom failed: %v", err) + return + } + if from != nil { + t.Errorf("neighbor address is %v", from) + } + if !bytes.Equal(b[:n], data[:]) { + t.Errorf("got %v, want %v", b[:n], data[:]) + return + } +} + +func TestReadUnixgramWithZeroBytesBuffer(t *testing.T) { + // issue 4352: Recvfrom failed with "address family not + // supported by protocol family" if zero-length buffer provided + + addr := testUnixAddr() + la, err := ResolveUnixAddr("unixgram", addr) + if err != nil { + t.Fatalf("ResolveUnixAddr failed: %v", err) + } + c, err := ListenUnixgram("unixgram", la) + if err != nil { + t.Fatalf("ListenUnixgram failed: %v", err) + } + defer func() { + c.Close() + os.Remove(addr) + }() + + off := make(chan bool) + go func() { + defer func() { off <- true }() + c, err := DialUnix("unixgram", nil, la) + if err != nil { + t.Errorf("DialUnix failed: %v", err) + return + } + defer c.Close() + if _, err := c.Write([]byte{1, 2, 3, 4, 5}); err != nil { + t.Errorf("UnixConn.Write failed: %v", err) + return + } + }() + + <-off + c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) + var peer Addr + if _, peer, err = c.ReadFrom(nil); err != nil { + t.Errorf("UnixConn.ReadFrom failed: %v", err) + return + } + if peer != nil { + t.Errorf("peer adddress is %v", peer) + } +} + +func TestUnixAutobind(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("skipping: autobind is linux only") + } + + laddr := &UnixAddr{Name: "", Net: "unixgram"} + c1, err := ListenUnixgram("unixgram", laddr) + if err != nil { + t.Fatalf("ListenUnixgram failed: %v", err) + } + defer c1.Close() + + // retrieve the autobind address + autoAddr := c1.LocalAddr().(*UnixAddr) + if len(autoAddr.Name) <= 1 { + t.Fatalf("Invalid autobind address: %v", autoAddr) + } + if autoAddr.Name[0] != '@' { + t.Fatalf("Invalid autobind address: %v", autoAddr) + } + + c2, err := DialUnix("unixgram", nil, autoAddr) + if err != nil { + t.Fatalf("DialUnix failed: %v", err) + } + defer c2.Close() + + if !reflect.DeepEqual(c1.LocalAddr(), c2.RemoteAddr()) { + t.Fatalf("Expected autobind address %v, got %v", c1.LocalAddr(), c2.RemoteAddr()) + } +} diff --git a/src/pkg/net/unixsock_plan9.go b/src/pkg/net/unixsock_plan9.go index 7b4ae6bd1..00a0be5b0 100644 --- a/src/pkg/net/unixsock_plan9.go +++ b/src/pkg/net/unixsock_plan9.go @@ -7,100 +7,135 @@ package net import ( + "os" "syscall" "time" ) -// UnixConn is an implementation of the Conn interface -// for connections to Unix domain sockets. -type UnixConn bool - -// Implementation of the Conn interface - see Conn for documentation. +// UnixConn is an implementation of the Conn interface for connections +// to Unix domain sockets. +type UnixConn struct { + conn +} -// Read implements the Conn Read method. -func (c *UnixConn) Read(b []byte) (n int, err error) { - return 0, syscall.EPLAN9 +// ReadFromUnix reads a packet from c, copying the payload into b. It +// returns the number of bytes copied into b and the source address of +// the packet. +// +// ReadFromUnix can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetReadDeadline. +func (c *UnixConn) ReadFromUnix(b []byte) (int, *UnixAddr, error) { + return 0, nil, syscall.EPLAN9 } -// Write implements the Conn Write method. -func (c *UnixConn) Write(b []byte) (n int, err error) { - return 0, syscall.EPLAN9 +// ReadFrom implements the PacketConn ReadFrom method. +func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) { + return 0, nil, syscall.EPLAN9 } -// Close closes the Unix domain connection. -func (c *UnixConn) Close() error { - return syscall.EPLAN9 +// ReadMsgUnix reads a packet from c, copying the payload into b and +// the associated out-of-band data into oob. It returns the number of +// bytes copied into b, the number of bytes copied into oob, the flags +// that were set on the packet, and the source address of the packet. +func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) { + return 0, 0, 0, nil, syscall.EPLAN9 } -// LocalAddr returns the local network address, a *UnixAddr. -// Unlike in other protocols, LocalAddr is usually nil for dialed connections. -func (c *UnixConn) LocalAddr() Addr { - return nil +// WriteToUnix writes a packet to addr via c, copying the payload from b. +// +// WriteToUnix can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetWriteDeadline. On packet-oriented connections, write timeouts +// are rare. +func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) { + return 0, syscall.EPLAN9 } -// RemoteAddr returns the remote network address, a *UnixAddr. -// Unlike in other protocols, RemoteAddr is usually nil for connections -// accepted by a listener. -func (c *UnixConn) RemoteAddr() Addr { - return nil +// WriteTo implements the PacketConn WriteTo method. +func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error) { + return 0, syscall.EPLAN9 } -// SetDeadline implements the Conn SetDeadline method. -func (c *UnixConn) SetDeadline(t time.Time) error { - return syscall.EPLAN9 +// WriteMsgUnix writes a packet to addr via c, copying the payload +// from b and the associated out-of-band data from oob. It returns +// the number of payload and out-of-band bytes written. +func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) { + return 0, 0, syscall.EPLAN9 } -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *UnixConn) SetReadDeadline(t time.Time) error { +// CloseRead shuts down the reading side of the Unix domain connection. +// Most callers should just use Close. +func (c *UnixConn) CloseRead() error { return syscall.EPLAN9 } -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *UnixConn) SetWriteDeadline(t time.Time) error { +// CloseWrite shuts down the writing side of the Unix domain connection. +// Most callers should just use Close. +func (c *UnixConn) CloseWrite() error { return syscall.EPLAN9 } -// ReadFrom implements the PacketConn ReadFrom method. -func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) { - err = syscall.EPLAN9 - return +// DialUnix connects to the remote address raddr on the network net, +// which must be "unix", "unixgram" or "unixpacket". If laddr is not +// nil, it is used as the local address for the connection. +func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) { + return dialUnix(net, laddr, raddr, noDeadline) } -// WriteTo implements the PacketConn WriteTo method. -func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) { - err = syscall.EPLAN9 - return +func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) { + return nil, syscall.EPLAN9 } -// DialUnix connects to the remote address raddr on the network net, -// which must be "unix" or "unixgram". If laddr is not nil, it is used -// as the local address for the connection. -func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err error) { +// UnixListener is a Unix domain socket listener. Clients should +// typically use variables of type Listener instead of assuming Unix +// domain sockets. +type UnixListener struct{} + +// ListenUnix announces on the Unix domain socket laddr and returns a +// Unix listener. The network net must be "unix" or "unixpacket". +func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) { return nil, syscall.EPLAN9 } -// UnixListener is a Unix domain socket listener. -// Clients should typically use variables of type Listener -// instead of assuming Unix domain sockets. -type UnixListener bool - -// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. -// Net must be "unix" (stream sockets). -func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err error) { +// AcceptUnix accepts the next incoming call and returns the new +// connection and the remote address. +func (l *UnixListener) AcceptUnix() (*UnixConn, error) { return nil, syscall.EPLAN9 } -// Accept implements the Accept method in the Listener interface; -// it waits for the next call and returns a generic Conn. -func (l *UnixListener) Accept() (c Conn, err error) { +// Accept implements the Accept method in the Listener interface; it +// waits for the next call and returns a generic Conn. +func (l *UnixListener) Accept() (Conn, error) { return nil, syscall.EPLAN9 } -// Close stops listening on the Unix address. -// Already accepted connections are not closed. +// Close stops listening on the Unix address. Already accepted +// connections are not closed. func (l *UnixListener) Close() error { return syscall.EPLAN9 } // Addr returns the listener's network address. func (l *UnixListener) Addr() Addr { return nil } + +// SetDeadline sets the deadline associated with the listener. +// A zero time value disables the deadline. +func (l *UnixListener) SetDeadline(t time.Time) error { + return syscall.EPLAN9 +} + +// File returns a copy of the underlying os.File, set to blocking +// mode. It is the caller's responsibility to close f when finished. +// Closing l does not affect f, and closing f does not affect l. +func (l *UnixListener) File() (*os.File, error) { + return nil, syscall.EPLAN9 +} + +// ListenUnixgram listens for incoming Unix datagram packets addressed +// to the local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send packets with +// per-packet addressing. The network net must be "unixgram". +func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) { + return nil, syscall.EPLAN9 +} diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go index 57d784c71..6d6ce3f5e 100644 --- a/src/pkg/net/unixsock_posix.go +++ b/src/pkg/net/unixsock_posix.go @@ -9,29 +9,27 @@ package net import ( + "errors" "os" "syscall" "time" ) -func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err error) { +func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (*netFD, error) { var sotype int switch net { - default: - return nil, UnknownNetworkError(net) case "unix": sotype = syscall.SOCK_STREAM case "unixgram": sotype = syscall.SOCK_DGRAM case "unixpacket": sotype = syscall.SOCK_SEQPACKET + default: + return nil, UnknownNetworkError(net) } var la, ra syscall.Sockaddr switch mode { - default: - panic("unixSocket mode " + mode) - case "dial": if laddr != nil { la = &syscall.SockaddrUnix{Name: laddr.Name} @@ -41,15 +39,10 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err } else if sotype != syscall.SOCK_DGRAM || laddr == nil { return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress} } - case "listen": - if laddr == nil { - return nil, &OpError{mode, net, nil, errMissingAddress} - } la = &syscall.SockaddrUnix{Name: laddr.Name} - if raddr != nil { - return nil, &OpError{Op: mode, Net: net, Addr: raddr, Err: &AddrError{Err: "unexpected remote address", Addr: raddr.String()}} - } + default: + return nil, errors.New("unknown mode: " + mode) } f := sockaddrToUnix @@ -59,15 +52,16 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err f = sockaddrToUnixpacket } - fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, f) + fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f) if err != nil { - goto Error + goto error } return fd, nil -Error: +error: addr := raddr - if mode == "listen" { + switch mode { + case "listen": addr = laddr } return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err} @@ -108,110 +102,21 @@ func sotypeToNet(sotype int) string { return "" } -// UnixConn is an implementation of the Conn interface -// for connections to Unix domain sockets. +// UnixConn is an implementation of the Conn interface for connections +// to Unix domain sockets. type UnixConn struct { - fd *netFD -} - -func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{fd} } - -func (c *UnixConn) ok() bool { return c != nil && c.fd != nil } - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the Conn Read method. -func (c *UnixConn) Read(b []byte) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - return c.fd.Read(b) -} - -// Write implements the Conn Write method. -func (c *UnixConn) Write(b []byte) (n int, err error) { - if !c.ok() { - return 0, syscall.EINVAL - } - return c.fd.Write(b) -} - -// Close closes the Unix domain connection. -func (c *UnixConn) Close() error { - if !c.ok() { - return syscall.EINVAL - } - return c.fd.Close() -} - -// LocalAddr returns the local network address, a *UnixAddr. -// Unlike in other protocols, LocalAddr is usually nil for dialed connections. -func (c *UnixConn) LocalAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.laddr + conn } -// RemoteAddr returns the remote network address, a *UnixAddr. -// Unlike in other protocols, RemoteAddr is usually nil for connections -// accepted by a listener. -func (c *UnixConn) RemoteAddr() Addr { - if !c.ok() { - return nil - } - return c.fd.raddr -} +func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} } -// SetDeadline implements the Conn SetDeadline method. -func (c *UnixConn) SetDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setDeadline(c.fd, t) -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *UnixConn) SetReadDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setReadDeadline(c.fd, t) -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *UnixConn) SetWriteDeadline(t time.Time) error { - if !c.ok() { - return syscall.EINVAL - } - return setWriteDeadline(c.fd, t) -} - -// SetReadBuffer sets the size of the operating system's -// receive buffer associated with the connection. -func (c *UnixConn) SetReadBuffer(bytes int) error { - if !c.ok() { - return syscall.EINVAL - } - return setReadBuffer(c.fd, bytes) -} - -// SetWriteBuffer sets the size of the operating system's -// transmit buffer associated with the connection. -func (c *UnixConn) SetWriteBuffer(bytes int) error { - if !c.ok() { - return syscall.EINVAL - } - return setWriteBuffer(c.fd, bytes) -} - -// ReadFromUnix reads a packet from c, copying the payload into b. -// It returns the number of bytes copied into b and the source address -// of the packet. +// ReadFromUnix reads a packet from c, copying the payload into b. It +// returns the number of bytes copied into b and the source address of +// the packet. // -// ReadFromUnix can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetDeadline and SetReadDeadline. +// ReadFromUnix can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetReadDeadline. func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) { if !c.ok() { return 0, nil, syscall.EINVAL @@ -219,26 +124,46 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) { n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)} + if sa.Name != "" { + addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)} + } } return } // ReadFrom implements the PacketConn ReadFrom method. -func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) { +func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL } - n, uaddr, err := c.ReadFromUnix(b) - return n, uaddr.toAddr(), err + n, addr, err := c.ReadFromUnix(b) + return n, addr.toAddr(), err +} + +// ReadMsgUnix reads a packet from c, copying the payload into b and +// the associated out-of-band data into oob. It returns the number of +// bytes copied into b, the number of bytes copied into oob, the flags +// that were set on the packet, and the source address of the packet. +func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) { + if !c.ok() { + return 0, 0, 0, nil, syscall.EINVAL + } + n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) + switch sa := sa.(type) { + case *syscall.SockaddrUnix: + if sa.Name != "" { + addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)} + } + } + return } // WriteToUnix writes a packet to addr via c, copying the payload from b. // -// WriteToUnix can be made to time out and return -// an error with Timeout() == true after a fixed time limit; -// see SetDeadline and SetWriteDeadline. -// On packet-oriented connections, write timeouts are rare. +// WriteToUnix can be made to time out and return an error with +// Timeout() == true after a fixed time limit; see SetDeadline and +// SetWriteDeadline. On packet-oriented connections, write timeouts +// are rare. func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) { if !c.ok() { return 0, syscall.EINVAL @@ -262,26 +187,9 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) { return c.WriteToUnix(b, a) } -// ReadMsgUnix reads a packet from c, copying the payload into b -// and the associated out-of-band data into oob. -// It returns the number of bytes copied into b, the number of -// bytes copied into oob, the flags that were set on the packet, -// and the source address of the packet. -func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) { - if !c.ok() { - return 0, 0, 0, nil, syscall.EINVAL - } - n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob) - switch sa := sa.(type) { - case *syscall.SockaddrUnix: - addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)} - } - return -} - -// WriteMsgUnix writes a packet to addr via c, copying the payload from b -// and the associated out-of-band data from oob. It returns the number -// of payload and out-of-band bytes written. +// WriteMsgUnix writes a packet to addr via c, copying the payload +// from b and the associated out-of-band data from oob. It returns +// the number of payload and out-of-band bytes written. func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) { if !c.ok() { return 0, 0, syscall.EINVAL @@ -296,40 +204,64 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err return c.fd.WriteMsg(b, oob, nil) } -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *UnixConn) File() (f *os.File, err error) { return c.fd.dup() } +// CloseRead shuts down the reading side of the Unix domain connection. +// Most callers should just use Close. +func (c *UnixConn) CloseRead() error { + if !c.ok() { + return syscall.EINVAL + } + return c.fd.CloseRead() +} + +// CloseWrite shuts down the writing side of the Unix domain connection. +// Most callers should just use Close. +func (c *UnixConn) CloseWrite() error { + if !c.ok() { + return syscall.EINVAL + } + return c.fd.CloseWrite() +} // DialUnix connects to the remote address raddr on the network net, -// which must be "unix" or "unixgram". If laddr is not nil, it is used -// as the local address for the connection. +// which must be "unix", "unixgram" or "unixpacket". If laddr is not +// nil, it is used as the local address for the connection. func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) { - fd, err := unixSocket(net, laddr, raddr, "dial") + return dialUnix(net, laddr, raddr, noDeadline) +} + +func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) { + switch net { + case "unix", "unixgram", "unixpacket": + default: + return nil, UnknownNetworkError(net) + } + fd, err := unixSocket(net, laddr, raddr, "dial", deadline) if err != nil { return nil, err } return newUnixConn(fd), nil } -// UnixListener is a Unix domain socket listener. -// Clients should typically use variables of type Listener -// instead of assuming Unix domain sockets. +// UnixListener is a Unix domain socket listener. Clients should +// typically use variables of type Listener instead of assuming Unix +// domain sockets. type UnixListener struct { fd *netFD path string } -// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. -// Net must be "unix" (stream sockets). +// ListenUnix announces on the Unix domain socket laddr and returns a +// Unix listener. The network net must be "unix" or "unixpacket". func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) { - if net != "unix" && net != "unixgram" && net != "unixpacket" { + switch net { + case "unix", "unixpacket": + default: return nil, UnknownNetworkError(net) } - if laddr != nil { - laddr = &UnixAddr{laddr.Name, net} // make our own copy + if laddr == nil { + return nil, &OpError{"listen", net, nil, errMissingAddress} } - fd, err := unixSocket(net, laddr, nil, "listen") + fd, err := unixSocket(net, laddr, nil, "listen", noDeadline) if err != nil { return nil, err } @@ -341,8 +273,8 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) { return &UnixListener{fd, laddr.Name}, nil } -// AcceptUnix accepts the next incoming call and returns the new connection -// and the remote address. +// AcceptUnix accepts the next incoming call and returns the new +// connection and the remote address. func (l *UnixListener) AcceptUnix() (*UnixConn, error) { if l == nil || l.fd == nil { return nil, syscall.EINVAL @@ -355,8 +287,8 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) { return c, nil } -// Accept implements the Accept method in the Listener interface; -// it waits for the next call and returns a generic Conn. +// Accept implements the Accept method in the Listener interface; it +// waits for the next call and returns a generic Conn. func (l *UnixListener) Accept() (c Conn, err error) { c1, err := l.AcceptUnix() if err != nil { @@ -365,8 +297,8 @@ func (l *UnixListener) Accept() (c Conn, err error) { return c1, nil } -// Close stops listening on the Unix address. -// Already accepted connections are not closed. +// Close stops listening on the Unix address. Already accepted +// connections are not closed. func (l *UnixListener) Close() error { if l == nil || l.fd == nil { return syscall.EINVAL @@ -385,9 +317,7 @@ func (l *UnixListener) Close() error { if l.path[0] != '@' { syscall.Unlink(l.path) } - err := l.fd.Close() - l.fd = nil - return err + return l.fd.Close() } // Addr returns the listener's network address. @@ -402,16 +332,16 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) { return setDeadline(l.fd, t) } -// File returns a copy of the underlying os.File, set to blocking mode. -// It is the caller's responsibility to close f when finished. +// File returns a copy of the underlying os.File, set to blocking +// mode. It is the caller's responsibility to close f when finished. // Closing l does not affect f, and closing f does not affect l. func (l *UnixListener) File() (f *os.File, err error) { return l.fd.dup() } -// ListenUnixgram listens for incoming Unix datagram packets addressed to the -// local address laddr. The returned connection c's ReadFrom -// and WriteTo methods can be used to receive and send UDP -// packets with per-packet addressing. The network net must be "unixgram". -func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) { +// ListenUnixgram listens for incoming Unix datagram packets addressed +// to the local address laddr. The returned connection c's ReadFrom +// and WriteTo methods can be used to receive and send packets with +// per-packet addressing. The network net must be "unixgram". +func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) { switch net { case "unixgram": default: @@ -420,9 +350,9 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) { if laddr == nil { return nil, &OpError{"listen", net, nil, errMissingAddress} } - fd, err := unixSocket(net, laddr, nil, "listen") + fd, err := unixSocket(net, laddr, nil, "listen", noDeadline) if err != nil { return nil, err } - return newUDPConn(fd), nil + return newUnixConn(fd), nil } diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go index 17bf0d3a3..a39964ea1 100644 --- a/src/pkg/net/url/url.go +++ b/src/pkg/net/url/url.go @@ -7,7 +7,9 @@ package url import ( + "bytes" "errors" + "sort" "strconv" "strings" ) @@ -218,11 +220,18 @@ func escape(s string, mode encoding) string { // // scheme:opaque[?query][#fragment] // +// Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/. +// A consequence is that it is impossible to tell which slashes in the Path were +// slashes in the raw URL and which were %2f. This distinction is rarely important, +// but when it is a client must use other routines to parse the raw URL or construct +// the parsed URL. For example, an HTTP server can consult req.RequestURI, and +// an HTTP client can use URL{Host: "example.com", Opaque: "//example.com/Go%2f"} +// instead of URL{Host: "example.com", Path: "/Go/"}. type URL struct { Scheme string Opaque string // encoded opaque data User *Userinfo // username and password information - Host string + Host string // host or host:port Path string RawQuery string // encoded query values, without '?' Fragment string // fragment for references, without '#' @@ -359,11 +368,17 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) { } url = new(URL) + if rawurl == "*" { + url.Path = "*" + return + } + // Split off possible leading "http:", "mailto:", etc. // Cannot contain escaped characters. if url.Scheme, rest, err = getscheme(rawurl); err != nil { goto Error } + url.Scheme = strings.ToLower(url.Scheme) rest, url.RawQuery = split(rest, '?', true) @@ -379,7 +394,7 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) { } } - if (url.Scheme != "" || !viaRequest) && strings.HasPrefix(rest, "//") && !strings.HasPrefix(rest, "///") { + if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") { var authority string authority, rest = split(rest[2:], '/', false) url.User, url.Host, err = parseAuthority(authority) @@ -427,30 +442,35 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) { // String reassembles the URL into a valid URL string. func (u *URL) String() string { - // TODO: Rewrite to use bytes.Buffer - result := "" + var buf bytes.Buffer if u.Scheme != "" { - result += u.Scheme + ":" + buf.WriteString(u.Scheme) + buf.WriteByte(':') } if u.Opaque != "" { - result += u.Opaque + buf.WriteString(u.Opaque) } else { - if u.Host != "" || u.User != nil { - result += "//" + if u.Scheme != "" || u.Host != "" || u.User != nil { + buf.WriteString("//") if u := u.User; u != nil { - result += u.String() + "@" + buf.WriteString(u.String()) + buf.WriteByte('@') + } + if h := u.Host; h != "" { + buf.WriteString(h) } - result += u.Host } - result += escape(u.Path, encodePath) + buf.WriteString(escape(u.Path, encodePath)) } if u.RawQuery != "" { - result += "?" + u.RawQuery + buf.WriteByte('?') + buf.WriteString(u.RawQuery) } if u.Fragment != "" { - result += "#" + escape(u.Fragment, encodeFragment) + buf.WriteByte('#') + buf.WriteString(escape(u.Fragment, encodeFragment)) } - return result + return buf.String() } // Values maps a string key to a list of values. @@ -519,12 +539,16 @@ func parseQuery(m Values, query string) (err error) { } key, err1 := QueryUnescape(key) if err1 != nil { - err = err1 + if err == nil { + err = err1 + } continue } value, err1 = QueryUnescape(value) if err1 != nil { - err = err1 + if err == nil { + err = err1 + } continue } m[key] = append(m[key], value) @@ -538,14 +562,24 @@ func (v Values) Encode() string { if v == nil { return "" } - parts := make([]string, 0, len(v)) // will be large enough for most uses - for k, vs := range v { + var buf bytes.Buffer + keys := make([]string, 0, len(v)) + for k := range v { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := v[k] prefix := QueryEscape(k) + "=" for _, v := range vs { - parts = append(parts, prefix+QueryEscape(v)) + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(prefix) + buf.WriteString(QueryEscape(v)) } } - return strings.Join(parts, "&") + return buf.String() } // resolvePath applies special path segments from refs and applies @@ -556,23 +590,33 @@ func resolvePath(basepath string, refpath string) string { if len(base) == 0 { base = []string{""} } + + rm := true for idx, ref := range refs { switch { case ref == ".": - base[len(base)-1] = "" + if idx == 0 { + base[len(base)-1] = "" + rm = true + } else { + rm = false + } case ref == "..": newLen := len(base) - 1 if newLen < 1 { newLen = 1 } base = base[0:newLen] - base[len(base)-1] = "" + if rm { + base[len(base)-1] = "" + } default: if idx == 0 || base[len(base)-1] == "" { base[len(base)-1] = ref } else { base = append(base, ref) } + rm = false } } return strings.Join(base, "/") @@ -650,6 +694,10 @@ func (u *URL) RequestURI() string { if result == "" { result = "/" } + } else { + if strings.HasPrefix(result, "//") { + result = u.Scheme + ":" + result + } } if u.RawQuery != "" { result += "?" + u.RawQuery diff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go index 75e8abe4e..4c4f406c2 100644 --- a/src/pkg/net/url/url_test.go +++ b/src/pkg/net/url/url_test.go @@ -7,6 +7,7 @@ package url import ( "fmt" "reflect" + "strings" "testing" ) @@ -121,14 +122,14 @@ var urltests = []URLTest{ }, "http:%2f%2fwww.google.com/?q=go+language", }, - // non-authority + // non-authority with path { "mailto:/webmaster@golang.org", &URL{ Scheme: "mailto", Path: "/webmaster@golang.org", }, - "", + "mailto:///webmaster@golang.org", // unfortunate compromise }, // non-authority { @@ -241,6 +242,24 @@ var urltests = []URLTest{ }, "http://www.google.com/?q=go+language#foo&bar", }, + { + "file:///home/adg/rabbits", + &URL{ + Scheme: "file", + Host: "", + Path: "/home/adg/rabbits", + }, + "file:///home/adg/rabbits", + }, + // case-insensitive scheme + { + "MaIlTo:webmaster@golang.org", + &URL{ + Scheme: "mailto", + Opaque: "webmaster@golang.org", + }, + "mailto:webmaster@golang.org", + }, } // more useful string for debugging than fmt's struct printer @@ -270,13 +289,37 @@ func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests [ } } +func BenchmarkString(b *testing.B) { + b.StopTimer() + b.ReportAllocs() + for _, tt := range urltests { + u, err := Parse(tt.in) + if err != nil { + b.Errorf("Parse(%q) returned error %s", tt.in, err) + continue + } + if tt.roundtrip == "" { + continue + } + b.StartTimer() + var g string + for i := 0; i < b.N; i++ { + g = u.String() + } + b.StopTimer() + if w := tt.roundtrip; g != w { + b.Errorf("Parse(%q).String() == %q, want %q", tt.in, g, w) + } + } +} + func TestParse(t *testing.T) { DoTest(t, Parse, "Parse", urltests) } const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path" -var parseRequestUrlTests = []struct { +var parseRequestURLTests = []struct { url string expectedValid bool }{ @@ -288,10 +331,11 @@ var parseRequestUrlTests = []struct { {"//not.a.user@%66%6f%6f.com/just/a/path/also", true}, {"foo.html", false}, {"../dir/", false}, + {"*", true}, } func TestParseRequestURI(t *testing.T) { - for _, test := range parseRequestUrlTests { + for _, test := range parseRequestURLTests { _, err := ParseRequestURI(test.url) valid := err == nil if valid != test.expectedValid { @@ -453,20 +497,24 @@ func TestEscape(t *testing.T) { //} type EncodeQueryTest struct { - m Values - expected string - expected1 string + m Values + expected string } var encodeQueryTests = []EncodeQueryTest{ - {nil, "", ""}, - {Values{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"}, - {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"}, + {nil, ""}, + {Values{"q": {"puppies"}, "oe": {"utf8"}}, "oe=utf8&q=puppies"}, + {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7"}, + {Values{ + "a": {"a1", "a2", "a3"}, + "b": {"b1", "b2", "b3"}, + "c": {"c1", "c2", "c3"}, + }, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3&c=c1&c=c2&c=c3"}, } func TestEncodeQuery(t *testing.T) { for _, tt := range encodeQueryTests { - if q := tt.m.Encode(); q != tt.expected && q != tt.expected1 { + if q := tt.m.Encode(); q != tt.expected { t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected) } } @@ -531,6 +579,15 @@ var resolveReferenceTests = []struct { {"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"}, {"http://foo.com/bar", "..", "http://foo.com/"}, {"http://foo.com/bar/baz", "./..", "http://foo.com/"}, + // ".." in the middle (issue 3560) + {"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"}, + {"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot"}, // "." and ".." in the base aren't special {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"}, @@ -741,6 +798,24 @@ var requritests = []RequestURITest{ }, "/a%20b", }, + // golang.org/issue/4860 variant 1 + { + &URL{ + Scheme: "http", + Host: "example.com", + Opaque: "/%2F/%2F/", + }, + "/%2F/%2F/", + }, + // golang.org/issue/4860 variant 2 + { + &URL{ + Scheme: "http", + Host: "example.com", + Opaque: "//other.example.com/%2F/%2F/", + }, + "http://other.example.com/%2F/%2F/", + }, { &URL{ Scheme: "http", @@ -775,3 +850,13 @@ func TestRequestURI(t *testing.T) { } } } + +func TestParseFailure(t *testing.T) { + // Test that the first parse error is returned. + const url = "%gh&%ij" + _, err := ParseQuery(url) + errStr := fmt.Sprint(err) + if !strings.Contains(errStr, "%gh") { + t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh") + } +} diff --git a/src/pkg/os/dir_plan9.go b/src/pkg/os/dir_plan9.go index 7fa4c7f44..8195c02a4 100644 --- a/src/pkg/os/dir_plan9.go +++ b/src/pkg/os/dir_plan9.go @@ -5,15 +5,11 @@ package os import ( - "errors" "io" "syscall" ) -var errShortStat = errors.New("short stat message") -var errBadStat = errors.New("bad stat message format") - -func (file *File) readdir(n int) (fi []FileInfo, err error) { +func (file *File) readdir(n int) ([]FileInfo, error) { // If this file has no dirinfo, create one. if file.dirinfo == nil { file.dirinfo = new(dirInfo) @@ -24,44 +20,47 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) { size = 100 n = -1 } - result := make([]FileInfo, 0, size) // Empty with room to grow. + fi := make([]FileInfo, 0, size) // Empty with room to grow. for n != 0 { - // Refill the buffer if necessary + // Refill the buffer if necessary. if d.bufp >= d.nbuf { - d.bufp = 0 - var e error - d.nbuf, e = file.Read(d.buf[:]) - if e != nil && e != io.EOF { - return result, &PathError{"readdir", file.name, e} - } - if e == io.EOF { - break + nb, err := file.Read(d.buf[:]) + + // Update the buffer state before checking for errors. + d.bufp, d.nbuf = 0, nb + + if err != nil { + if err == io.EOF { + break + } + return fi, &PathError{"readdir", file.name, err} } - if d.nbuf < syscall.STATFIXLEN { - return result, &PathError{"readdir", file.name, errShortStat} + if nb < syscall.STATFIXLEN { + return fi, &PathError{"readdir", file.name, syscall.ErrShortStat} } } - // Get a record from buffer - m, _ := gbit16(d.buf[d.bufp:]) - m += 2 + // Get a record from the buffer. + b := d.buf[d.bufp:] + m := int(uint16(b[0])|uint16(b[1])<<8) + 2 if m < syscall.STATFIXLEN { - return result, &PathError{"readdir", file.name, errShortStat} + return fi, &PathError{"readdir", file.name, syscall.ErrShortStat} } - dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)]) - if e != nil { - return result, &PathError{"readdir", file.name, e} + + dir, err := syscall.UnmarshalDir(b[:m]) + if err != nil { + return fi, &PathError{"readdir", file.name, err} } - result = append(result, fileInfoFromStat(dir)) + fi = append(fi, fileInfoFromStat(dir)) - d.bufp += int(m) + d.bufp += m n-- } - if n >= 0 && len(result) == 0 { - return result, io.EOF + if n >= 0 && len(fi) == 0 { + return fi, io.EOF } - return result, nil + return fi, nil } func (file *File) readdirnames(n int) (names []string, err error) { @@ -72,205 +71,3 @@ func (file *File) readdirnames(n int) (names []string, err error) { } return } - -type Dir struct { - // system-modified data - Type uint16 // server type - Dev uint32 // server subtype - // file data - Qid Qid // unique id from server - Mode uint32 // permissions - Atime uint32 // last read time - Mtime uint32 // last write time - Length uint64 // file length - Name string // last element of path - Uid string // owner name - Gid string // group name - Muid string // last modifier name -} - -type Qid struct { - Path uint64 // the file server's unique identification for the file - Vers uint32 // version number for given Path - Type uint8 // the type of the file (syscall.QTDIR for example) -} - -var nullDir = Dir{ - ^uint16(0), - ^uint32(0), - Qid{^uint64(0), ^uint32(0), ^uint8(0)}, - ^uint32(0), - ^uint32(0), - ^uint32(0), - ^uint64(0), - "", - "", - "", - "", -} - -// Null assigns members of d with special "don't care" values indicating -// they should not be written by syscall.Wstat. -func (d *Dir) Null() { - *d = nullDir -} - -// pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b. -func pdir(b []byte, d *Dir) []byte { - n := len(b) - b = pbit16(b, 0) // length, filled in later - b = pbit16(b, d.Type) - b = pbit32(b, d.Dev) - b = pqid(b, d.Qid) - b = pbit32(b, d.Mode) - b = pbit32(b, d.Atime) - b = pbit32(b, d.Mtime) - b = pbit64(b, d.Length) - b = pstring(b, d.Name) - b = pstring(b, d.Uid) - b = pstring(b, d.Gid) - b = pstring(b, d.Muid) - pbit16(b[0:n], uint16(len(b)-(n+2))) - return b -} - -// UnmarshalDir reads a 9P Stat message from a 9P protocol message stored in b, -// returning the corresponding Dir struct. -func UnmarshalDir(b []byte) (d *Dir, err error) { - n := uint16(0) - n, b = gbit16(b) - - if int(n) != len(b) { - return nil, errBadStat - } - - d = new(Dir) - d.Type, b = gbit16(b) - d.Dev, b = gbit32(b) - d.Qid, b = gqid(b) - d.Mode, b = gbit32(b) - d.Atime, b = gbit32(b) - d.Mtime, b = gbit32(b) - d.Length, b = gbit64(b) - d.Name, b = gstring(b) - d.Uid, b = gstring(b) - d.Gid, b = gstring(b) - d.Muid, b = gstring(b) - - if len(b) != 0 { - return nil, errBadStat - } - - return d, nil -} - -// gqid reads the qid part of a 9P Stat message from a 9P protocol message stored in b, -// returning the corresponding Qid struct and the remaining slice of b. -func gqid(b []byte) (Qid, []byte) { - var q Qid - q.Path, b = gbit64(b) - q.Vers, b = gbit32(b) - q.Type, b = gbit8(b) - return q, b -} - -// pqid appends a Qid struct q to a 9P message b. -func pqid(b []byte, q Qid) []byte { - b = pbit64(b, q.Path) - b = pbit32(b, q.Vers) - b = pbit8(b, q.Type) - return b -} - -// gbit8 reads a byte-sized numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit8(b []byte) (uint8, []byte) { - return uint8(b[0]), b[1:] -} - -// gbit16 reads a 16-bit numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit16(b []byte) (uint16, []byte) { - return uint16(b[0]) | uint16(b[1])<<8, b[2:] -} - -// gbit32 reads a 32-bit numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit32(b []byte) (uint32, []byte) { - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] -} - -// gbit64 reads a 64-bit numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit64(b []byte) (uint64, []byte) { - lo, b := gbit32(b) - hi, b := gbit32(b) - return uint64(hi)<<32 | uint64(lo), b -} - -// gstring reads a string from a 9P protocol message stored in b, -// returning the value as a Go string and the remaining slice of b. -func gstring(b []byte) (string, []byte) { - n, b := gbit16(b) - return string(b[0:n]), b[n:] -} - -// pbit8 appends a byte-sized numeric value x to a 9P message b. -func pbit8(b []byte, x uint8) []byte { - n := len(b) - if n+1 > cap(b) { - nb := make([]byte, n, 100+2*cap(b)) - copy(nb, b) - b = nb - } - b = b[0 : n+1] - b[n] = x - return b -} - -// pbit16 appends a 16-bit numeric value x to a 9P message b. -func pbit16(b []byte, x uint16) []byte { - n := len(b) - if n+2 > cap(b) { - nb := make([]byte, n, 100+2*cap(b)) - copy(nb, b) - b = nb - } - b = b[0 : n+2] - b[n] = byte(x) - b[n+1] = byte(x >> 8) - return b -} - -// pbit32 appends a 32-bit numeric value x to a 9P message b. -func pbit32(b []byte, x uint32) []byte { - n := len(b) - if n+4 > cap(b) { - nb := make([]byte, n, 100+2*cap(b)) - copy(nb, b) - b = nb - } - b = b[0 : n+4] - b[n] = byte(x) - b[n+1] = byte(x >> 8) - b[n+2] = byte(x >> 16) - b[n+3] = byte(x >> 24) - return b -} - -// pbit64 appends a 64-bit numeric value x to a 9P message b. -func pbit64(b []byte, x uint64) []byte { - b = pbit32(b, uint32(x)) - b = pbit32(b, uint32(x>>32)) - return b -} - -// pstring appends a Go string s to a 9P message b. -func pstring(b []byte, s string) []byte { - if len(s) >= 1<<16 { - panic(errors.New("string too long")) - } - b = pbit16(b, uint16(len(s))) - b = append(b, s...) - return b -} diff --git a/src/pkg/os/doc.go b/src/pkg/os/doc.go index 6a531e0d7..2cc17530c 100644 --- a/src/pkg/os/doc.go +++ b/src/pkg/os/doc.go @@ -79,6 +79,8 @@ func (p *ProcessState) Sys() interface{} { // SysUsage returns system-dependent resource usage information about // the exited process. Convert it to the appropriate underlying // type, such as *syscall.Rusage on Unix, to access its contents. +// (On Unix, *syscall.Rusage matches struct rusage as defined in the +// getrusage(2) manual page.) func (p *ProcessState) SysUsage() interface{} { return p.sysUsage() } @@ -89,7 +91,7 @@ func Hostname() (name string, err error) { } // Readdir reads the contents of the directory associated with file and -// returns an array of up to n FileInfo values, as would be returned +// returns a slice of up to n FileInfo values, as would be returned // by Lstat, in directory order. Subsequent calls on the same file will yield // further FileInfos. // diff --git a/src/pkg/os/env.go b/src/pkg/os/env.go index eb265f241..db7fc72b8 100644 --- a/src/pkg/os/env.go +++ b/src/pkg/os/env.go @@ -9,7 +9,7 @@ package os import "syscall" // Expand replaces ${var} or $var in the string based on the mapping function. -// Invocations of undefined variables are replaced with the empty string. +// For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv). func Expand(s string, mapping func(string) string) string { buf := make([]byte, 0, 2*len(s)) // ${} is all ASCII, so bytes are fine for this operation. diff --git a/src/pkg/os/env_unix_test.go b/src/pkg/os/env_unix_test.go new file mode 100644 index 000000000..7eb4dc0ff --- /dev/null +++ b/src/pkg/os/env_unix_test.go @@ -0,0 +1,30 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package os_test + +import ( + . "os" + "testing" +) + +var setenvEinvalTests = []struct { + k, v string +}{ + {"", ""}, // empty key + {"k=v", ""}, // '=' in key + {"\x00", ""}, // '\x00' in key + {"k", "\x00"}, // '\x00' in value +} + +func TestSetenvUnixEinval(t *testing.T) { + for _, tt := range setenvEinvalTests { + err := Setenv(tt.k, tt.v) + if err == nil { + t.Errorf(`Setenv(%q, %q) == nil, want error`, tt.k, tt.v) + } + } +} diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go index b88e49400..a7977ff19 100644 --- a/src/pkg/os/error.go +++ b/src/pkg/os/error.go @@ -43,7 +43,7 @@ func NewSyscallError(syscall string, err error) error { return &SyscallError{syscall, err} } -// IsExist returns whether the error is known to report that a file or directory +// IsExist returns whether the error is known to report that a file or directory // already exists. It is satisfied by ErrExist as well as some syscall errors. func IsExist(err error) bool { return isExist(err) diff --git a/src/pkg/os/error_plan9.go b/src/pkg/os/error_plan9.go index 3c9dfb0b1..85260c82a 100644 --- a/src/pkg/os/error_plan9.go +++ b/src/pkg/os/error_plan9.go @@ -5,21 +5,36 @@ package os func isExist(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return contains(err.Error(), " exists") } func isNotExist(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return contains(err.Error(), "does not exist") } func isPermission(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return contains(err.Error(), "permission denied") diff --git a/src/pkg/os/error_posix.go b/src/pkg/os/error_posix.go index 1685c1f21..81b626aec 100644 --- a/src/pkg/os/error_posix.go +++ b/src/pkg/os/error_posix.go @@ -9,21 +9,36 @@ package os import "syscall" func isExist(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return err == syscall.EEXIST || err == ErrExist } func isNotExist(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return err == syscall.ENOENT || err == ErrNotExist } func isPermission(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission diff --git a/src/pkg/os/error_test.go b/src/pkg/os/error_test.go index 42f846fa3..02ed2351c 100644 --- a/src/pkg/os/error_test.go +++ b/src/pkg/os/error_test.go @@ -79,3 +79,54 @@ func checkErrorPredicate(predName string, pred func(error) bool, err error) stri } return "" } + +var isExistTests = []struct { + err error + is bool + isnot bool +}{ + {&os.PathError{Err: os.ErrInvalid}, false, false}, + {&os.PathError{Err: os.ErrPermission}, false, false}, + {&os.PathError{Err: os.ErrExist}, true, false}, + {&os.PathError{Err: os.ErrNotExist}, false, true}, + {&os.LinkError{Err: os.ErrInvalid}, false, false}, + {&os.LinkError{Err: os.ErrPermission}, false, false}, + {&os.LinkError{Err: os.ErrExist}, true, false}, + {&os.LinkError{Err: os.ErrNotExist}, false, true}, + {nil, false, false}, +} + +func TestIsExist(t *testing.T) { + for _, tt := range isExistTests { + if is := os.IsExist(tt.err); is != tt.is { + t.Errorf("os.IsExist(%T %v) = %v, want %v", tt.err, tt.err, is, tt.is) + } + if isnot := os.IsNotExist(tt.err); isnot != tt.isnot { + t.Errorf("os.IsNotExist(%T %v) = %v, want %v", tt.err, tt.err, isnot, tt.isnot) + } + } +} + +func TestErrPathNUL(t *testing.T) { + f, err := ioutil.TempFile("", "_Go_ErrPathNUL\x00") + if err == nil { + f.Close() + t.Fatal("TempFile should have failed") + } + f, err = ioutil.TempFile("", "_Go_ErrPathNUL") + if err != nil { + t.Fatalf("open ErrPathNUL tempfile: %s", err) + } + defer os.Remove(f.Name()) + defer f.Close() + f2, err := os.OpenFile(f.Name(), os.O_RDWR, 0600) + if err != nil { + t.Fatalf("open ErrPathNUL: %s", err) + } + f2.Close() + f2, err = os.OpenFile(f.Name()+"\x00", os.O_RDWR, 0600) + if err == nil { + f2.Close() + t.Fatal("Open should have failed") + } +} diff --git a/src/pkg/os/error_windows.go b/src/pkg/os/error_windows.go index fbb0d4f3f..83db6c078 100644 --- a/src/pkg/os/error_windows.go +++ b/src/pkg/os/error_windows.go @@ -7,7 +7,12 @@ package os import "syscall" func isExist(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return err == syscall.ERROR_ALREADY_EXISTS || @@ -15,7 +20,12 @@ func isExist(err error) bool { } func isNotExist(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return err == syscall.ERROR_FILE_NOT_FOUND || @@ -23,7 +33,12 @@ func isNotExist(err error) bool { } func isPermission(err error) bool { - if pe, ok := err.(*PathError); ok { + switch pe := err.(type) { + case nil: + return false + case *PathError: + err = pe.Err + case *LinkError: err = pe.Err } return err == syscall.ERROR_ACCESS_DENIED || err == ErrPermission diff --git a/src/pkg/os/error_windows_test.go b/src/pkg/os/error_windows_test.go new file mode 100644 index 000000000..3e6504f8d --- /dev/null +++ b/src/pkg/os/error_windows_test.go @@ -0,0 +1,47 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os_test + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func TestErrIsExistAfterRename(t *testing.T) { + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("Create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "src") + dest := filepath.Join(dir, "dest") + + f, err := os.Create(src) + if err != nil { + t.Fatalf("Create file %v: %v", src, err) + } + f.Close() + err = os.Rename(src, dest) + if err != nil { + t.Fatalf("Rename %v to %v: %v", src, dest, err) + } + + f, err = os.Create(src) + if err != nil { + t.Fatalf("Create file %v: %v", src, err) + } + f.Close() + err = os.Rename(src, dest) + if err == nil { + t.Fatal("Rename should have failed") + } + if s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" { + t.Fatal(s) + return + } +} diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go index 531b87ca5..5aea3098b 100644 --- a/src/pkg/os/exec.go +++ b/src/pkg/os/exec.go @@ -6,6 +6,7 @@ package os import ( "runtime" + "sync/atomic" "syscall" ) @@ -13,7 +14,7 @@ import ( type Process struct { Pid int handle uintptr - done bool // process has been successfully waited on + isdone uint32 // process has been successfully waited on, non zero if true } func newProcess(pid int, handle uintptr) *Process { @@ -22,6 +23,14 @@ func newProcess(pid int, handle uintptr) *Process { return p } +func (p *Process) setDone() { + atomic.StoreUint32(&p.isdone, 1) +} + +func (p *Process) done() bool { + return atomic.LoadUint32(&p.isdone) > 0 +} + // ProcAttr holds the attributes that will be applied to a new process // started by StartProcess. type ProcAttr struct { @@ -54,14 +63,6 @@ type Signal interface { Signal() // to distinguish from other Stringers } -// The only signal values guaranteed to be present on all systems -// are Interrupt (send the process an interrupt) and -// Kill (force the process to exit). -var ( - Interrupt Signal = syscall.SIGINT - Kill Signal = syscall.SIGKILL -) - // Getpid returns the process id of the caller. func Getpid() int { return syscall.Getpid() } diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go index 9a8e18170..8368491b0 100644 --- a/src/pkg/os/exec/exec.go +++ b/src/pkg/os/exec/exec.go @@ -16,7 +16,7 @@ import ( "syscall" ) -// Error records the name of a binary that failed to be be executed +// Error records the name of a binary that failed to be executed // and the reason it failed. type Error struct { Name string @@ -37,7 +37,7 @@ type Cmd struct { // Args holds command line arguments, including the command as Args[0]. // If the Args field is empty or nil, Run uses {Path}. - // + // // In typical use, both Path and Args are set by calling Command. Args []string @@ -143,6 +143,9 @@ func (c *Cmd) argv() []string { func (c *Cmd) stdin() (f *os.File, err error) { if c.Stdin == nil { f, err = os.Open(os.DevNull) + if err != nil { + return + } c.closeAfterStart = append(c.closeAfterStart, f) return } @@ -182,6 +185,9 @@ func (c *Cmd) stderr() (f *os.File, err error) { func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) { if w == nil { f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) + if err != nil { + return + } c.closeAfterStart = append(c.closeAfterStart, f) return } diff --git a/src/pkg/os/exec/exec_test.go b/src/pkg/os/exec/exec_test.go index 52f4bce3a..611ac0267 100644 --- a/src/pkg/os/exec/exec_test.go +++ b/src/pkg/os/exec/exec_test.go @@ -14,6 +14,7 @@ import ( "net/http" "net/http/httptest" "os" + "path/filepath" "runtime" "strconv" "strings" @@ -83,10 +84,16 @@ func TestNoExistBinary(t *testing.T) { func TestExitStatus(t *testing.T) { // Test that exit values are returned correctly - err := helperCommand("exit", "42").Run() + cmd := helperCommand("exit", "42") + err := cmd.Run() + want := "exit status 42" + switch runtime.GOOS { + case "plan9": + want = fmt.Sprintf("exit status: '%s %d: 42'", filepath.Base(cmd.Path), cmd.ProcessState.Pid()) + } if werr, ok := err.(*ExitError); ok { - if s, e := werr.Error(), "exit status 42"; s != e { - t.Errorf("from exit 42 got exit %q, want %q", s, e) + if s := werr.Error(); s != want { + t.Errorf("from exit 42 got exit %q, want %q", s, want) } } else { t.Fatalf("expected *ExitError from exit 42; got %T: %v", err, err) @@ -144,18 +151,36 @@ func TestPipes(t *testing.T) { check("Wait", err) } +var testedAlreadyLeaked = false + +// basefds returns the number of expected file descriptors +// to be present in a process at start. +func basefds() uintptr { + n := os.Stderr.Fd() + 1 + + // Go runtime for 32-bit Plan 9 requires that /dev/bintime + // be kept open. + // See ../../runtime/time_plan9_386.c:/^runtime·nanotime + if runtime.GOOS == "plan9" && runtime.GOARCH == "386" { + n++ + } + return n +} + func TestExtraFiles(t *testing.T) { if runtime.GOOS == "windows" { - t.Logf("no operating system support; skipping") - return + t.Skip("no operating system support; skipping") } // Ensure that file descriptors have not already been leaked into // our environment. - for fd := os.Stderr.Fd() + 1; fd <= 101; fd++ { - err := os.NewFile(fd, "").Close() - if err == nil { - t.Logf("Something already leaked - closed fd %d", fd) + if !testedAlreadyLeaked { + testedAlreadyLeaked = true + for fd := basefds(); fd <= 101; fd++ { + err := os.NewFile(fd, "").Close() + if err == nil { + t.Logf("Something already leaked - closed fd %d", fd) + } } } @@ -167,6 +192,18 @@ func TestExtraFiles(t *testing.T) { } defer ln.Close() + // Make sure duplicated fds don't leak to the child. + f, err := ln.(*net.TCPListener).File() + if err != nil { + t.Fatal(err) + } + defer f.Close() + ln2, err := net.FileListener(f) + if err != nil { + t.Fatal(err) + } + defer ln2.Close() + // Force TLS root certs to be loaded (which might involve // cgo), to make sure none of that potential C code leaks fds. ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -193,13 +230,65 @@ func TestExtraFiles(t *testing.T) { } c := helperCommand("read3") + var stdout, stderr bytes.Buffer + c.Stdout = &stdout + c.Stderr = &stderr c.ExtraFiles = []*os.File{tf} - bs, err := c.CombinedOutput() + err = c.Run() if err != nil { - t.Fatalf("CombinedOutput: %v; output %q", err, bs) + t.Fatalf("Run: %v; stdout %q, stderr %q", err, stdout.Bytes(), stderr.Bytes()) } - if string(bs) != text { - t.Errorf("got %q; want %q", string(bs), text) + if stdout.String() != text { + t.Errorf("got stdout %q, stderr %q; want %q on stdout", stdout.String(), stderr.String(), text) + } +} + +func TestExtraFilesRace(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("no operating system support; skipping") + } + listen := func() net.Listener { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + return ln + } + listenerFile := func(ln net.Listener) *os.File { + f, err := ln.(*net.TCPListener).File() + if err != nil { + t.Fatal(err) + } + return f + } + runCommand := func(c *Cmd, out chan<- string) { + bout, err := c.CombinedOutput() + if err != nil { + out <- "ERROR:" + err.Error() + } else { + out <- string(bout) + } + } + + for i := 0; i < 10; i++ { + la := listen() + ca := helperCommand("describefiles") + ca.ExtraFiles = []*os.File{listenerFile(la)} + lb := listen() + cb := helperCommand("describefiles") + cb.ExtraFiles = []*os.File{listenerFile(lb)} + ares := make(chan string) + bres := make(chan string) + go runCommand(ca, ares) + go runCommand(cb, bres) + if got, want := <-ares, fmt.Sprintf("fd3: listener %s\n", la.Addr()); got != want { + t.Errorf("iteration %d, process A got:\n%s\nwant:\n%s\n", i, got, want) + } + if got, want := <-bres, fmt.Sprintf("fd3: listener %s\n", lb.Addr()); got != want { + t.Errorf("iteration %d, process B got:\n%s\nwant:\n%s\n", i, got, want) + } + la.Close() + lb.Close() } } @@ -287,10 +376,15 @@ func TestHelperProcess(*testing.T) { // TODO(bradfitz): broken? Sometimes. // http://golang.org/issue/2603 // Skip this additional part of the test for now. + case "netbsd": + // TODO(jsing): This currently fails on NetBSD due to + // the cloned file descriptors that result from opening + // /dev/urandom. + // http://golang.org/issue/3955 default: // Now verify that there are no other open fds. var files []*os.File - for wantfd := os.Stderr.Fd() + 2; wantfd <= 100; wantfd++ { + for wantfd := basefds() + 1; wantfd <= 100; wantfd++ { f, err := os.Open(os.Args[0]) if err != nil { fmt.Printf("error opening file with expected fd %d: %v", wantfd, err) @@ -314,10 +408,20 @@ func TestHelperProcess(*testing.T) { // what we do with fd3 as long as we refer to it; // closing it is the easy choice. fd3.Close() - os.Stderr.Write(bs) + os.Stdout.Write(bs) case "exit": n, _ := strconv.Atoi(args[0]) os.Exit(n) + case "describefiles": + for fd := uintptr(3); fd < 25; fd++ { + f := os.NewFile(fd, fmt.Sprintf("fd-%d", fd)) + ln, err := net.FileListener(f) + if err == nil { + fmt.Printf("fd%d: listener %s\n", fd, ln.Addr()) + ln.Close() + } + } + os.Exit(0) default: fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) os.Exit(2) diff --git a/src/pkg/os/exec/lp_plan9.go b/src/pkg/os/exec/lp_plan9.go index 0e229e03e..6846a35c8 100644 --- a/src/pkg/os/exec/lp_plan9.go +++ b/src/pkg/os/exec/lp_plan9.go @@ -8,7 +8,6 @@ import ( "errors" "os" "strings" - "syscall" ) // ErrNotFound is the error resulting if a path search failed to find an executable file. @@ -22,7 +21,7 @@ func findExecutable(file string) error { if m := d.Mode(); !m.IsDir() && m&0111 != 0 { return nil } - return syscall.EPERM + return os.ErrPermission } // LookPath searches for an executable binary named file diff --git a/src/pkg/os/exec/lp_unix.go b/src/pkg/os/exec/lp_unix.go index 216322199..1d1ec07da 100644 --- a/src/pkg/os/exec/lp_unix.go +++ b/src/pkg/os/exec/lp_unix.go @@ -42,6 +42,9 @@ func LookPath(file string) (string, error) { return "", &Error{file, err} } pathenv := os.Getenv("PATH") + if pathenv == "" { + return "", &Error{file, ErrNotFound} + } for _, dir := range strings.Split(pathenv, ":") { if dir == "" { // Unix shell semantics: path element "" means "." diff --git a/src/pkg/os/exec/lp_unix_test.go b/src/pkg/os/exec/lp_unix_test.go new file mode 100644 index 000000000..625d78486 --- /dev/null +++ b/src/pkg/os/exec/lp_unix_test.go @@ -0,0 +1,55 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +package exec + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestLookPathUnixEmptyPath(t *testing.T) { + tmp, err := ioutil.TempDir("", "TestLookPathUnixEmptyPath") + if err != nil { + t.Fatal("TempDir failed: ", err) + } + defer os.RemoveAll(tmp) + wd, err := os.Getwd() + if err != nil { + t.Fatal("Getwd failed: ", err) + } + err = os.Chdir(tmp) + if err != nil { + t.Fatal("Chdir failed: ", err) + } + defer os.Chdir(wd) + + f, err := os.OpenFile("exec_me", os.O_CREATE|os.O_EXCL, 0700) + if err != nil { + t.Fatal("OpenFile failed: ", err) + } + err = f.Close() + if err != nil { + t.Fatal("Close failed: ", err) + } + + pathenv := os.Getenv("PATH") + defer os.Setenv("PATH", pathenv) + + err = os.Setenv("PATH", "") + if err != nil { + t.Fatal("Setenv failed: ", err) + } + + path, err := LookPath("exec_me") + if err == nil { + t.Fatal("LookPath found exec_me in empty $PATH") + } + if path != "" { + t.Fatalf("LookPath path == %q when err != nil", path) + } +} diff --git a/src/pkg/os/exec/lp_windows.go b/src/pkg/os/exec/lp_windows.go index d8351d7e6..7c7289bce 100644 --- a/src/pkg/os/exec/lp_windows.go +++ b/src/pkg/os/exec/lp_windows.go @@ -72,7 +72,7 @@ func LookPath(file string) (f string, err error) { return } if pathenv := os.Getenv(`PATH`); pathenv != `` { - for _, dir := range strings.Split(pathenv, `;`) { + for _, dir := range splitList(pathenv) { if f, err = findExecutable(dir+`\`+file, exts); err == nil { return } @@ -80,3 +80,36 @@ func LookPath(file string) (f string, err error) { } return ``, &Error{file, ErrNotFound} } + +func splitList(path string) []string { + // The same implementation is used in SplitList in path/filepath; + // consider changing path/filepath when changing this. + + if path == "" { + return []string{} + } + + // Split path, respecting but preserving quotes. + list := []string{} + start := 0 + quo := false + for i := 0; i < len(path); i++ { + switch c := path[i]; { + case c == '"': + quo = !quo + case c == os.PathListSeparator && !quo: + list = append(list, path[start:i]) + start = i + 1 + } + } + list = append(list, path[start:]) + + // Remove quotes. + for i, s := range list { + if strings.Contains(s, `"`) { + list[i] = strings.Replace(s, `"`, ``, -1) + } + } + + return list +} diff --git a/src/pkg/os/exec_plan9.go b/src/pkg/os/exec_plan9.go index 41cc8c26f..2bd5b6888 100644 --- a/src/pkg/os/exec_plan9.go +++ b/src/pkg/os/exec_plan9.go @@ -11,6 +11,14 @@ import ( "time" ) +// The only signal values guaranteed to be present on all systems +// are Interrupt (send the process an interrupt) and Kill (force +// the process to exit). +var ( + Interrupt Signal = syscall.Note("interrupt") + Kill Signal = syscall.Note("kill") +) + func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) { sysattr := &syscall.ProcAttr{ Dir: attr.Dir, @@ -30,35 +38,35 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e return newProcess(pid, h), nil } -// Plan9Note implements the Signal interface on Plan 9. -type Plan9Note string - -func (note Plan9Note) String() string { - return string(note) +func (p *Process) writeProcFile(file string, data string) error { + f, e := OpenFile("/proc/"+itoa(p.Pid)+"/"+file, O_WRONLY, 0) + if e != nil { + return e + } + defer f.Close() + _, e = f.Write([]byte(data)) + return e } func (p *Process) signal(sig Signal) error { - if p.done { + if p.done() { return errors.New("os: process already finished") } - - f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0) - if e != nil { + if sig == Kill { + // Special-case the kill signal since it doesn't use /proc/$pid/note. + return p.Kill() + } + if e := p.writeProcFile("note", sig.String()); e != nil { return NewSyscallError("signal", e) } - defer f.Close() - _, e = f.Write([]byte(sig.String())) - return e + return nil } func (p *Process) kill() error { - f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0) - if e != nil { + if e := p.writeProcFile("ctl", "kill"); e != nil { return NewSyscallError("kill", e) } - defer f.Close() - _, e = f.Write([]byte("kill")) - return e + return nil } func (p *Process) wait() (ps *ProcessState, err error) { @@ -67,20 +75,12 @@ func (p *Process) wait() (ps *ProcessState, err error) { if p.Pid == -1 { return nil, ErrInvalid } - - for true { - err = syscall.Await(&waitmsg) - - if err != nil { - return nil, NewSyscallError("wait", err) - } - - if waitmsg.Pid == p.Pid { - p.done = true - break - } + err = syscall.WaitProcess(p.Pid, &waitmsg) + if err != nil { + return nil, NewSyscallError("wait", err) } + p.setDone() ps = &ProcessState{ pid: waitmsg.Pid, status: &waitmsg, diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go index 70351cfb3..f7b10f3c6 100644 --- a/src/pkg/os/exec_posix.go +++ b/src/pkg/os/exec_posix.go @@ -10,10 +10,19 @@ import ( "syscall" ) +// The only signal values guaranteed to be present on all systems +// are Interrupt (send the process an interrupt) and Kill (force +// the process to exit). +var ( + Interrupt Signal = syscall.SIGINT + Kill Signal = syscall.SIGKILL +) + func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) { - // Double-check existence of the directory we want + // If there is no SysProcAttr (ie. no Chroot or changed + // UID/GID), double-check existence of the directory we want // to chdir into. We can make the error clearer this way. - if attr != nil && attr.Dir != "" { + if attr != nil && attr.Sys == nil && attr.Dir != "" { if _, err := Stat(attr.Dir); err != nil { pe := err.(*PathError) pe.Op = "chdir" @@ -109,9 +118,9 @@ func (p *ProcessState) String() string { case status.Exited(): res = "exit status " + itod(status.ExitStatus()) case status.Signaled(): - res = "signal " + itod(int(status.Signal())) + res = "signal: " + status.Signal().String() case status.Stopped(): - res = "stop signal " + itod(int(status.StopSignal())) + res = "stop signal: " + status.StopSignal().String() if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 { res += " (trap " + itod(status.TrapCause()) + ")" } diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go index ecfe5353b..fa3ba8a19 100644 --- a/src/pkg/os/exec_unix.go +++ b/src/pkg/os/exec_unix.go @@ -24,7 +24,7 @@ func (p *Process) wait() (ps *ProcessState, err error) { return nil, NewSyscallError("wait", e) } if pid1 != 0 { - p.done = true + p.setDone() } ps = &ProcessState{ pid: pid1, @@ -35,7 +35,7 @@ func (p *Process) wait() (ps *ProcessState, err error) { } func (p *Process) signal(sig Signal) error { - if p.done { + if p.done() { return errors.New("os: process already finished") } s, ok := sig.(syscall.Signal) diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go index 5beca4a65..4aa2ade63 100644 --- a/src/pkg/os/exec_windows.go +++ b/src/pkg/os/exec_windows.go @@ -32,7 +32,7 @@ func (p *Process) wait() (ps *ProcessState, err error) { if e != nil { return nil, NewSyscallError("GetProcessTimes", e) } - p.done = true + p.setDone() // NOTE(brainman): It seems that sometimes process is not dead // when WaitForSingleObject returns. But we do not know any // other way to wait for it. Sleeping for a while seems to do @@ -43,7 +43,7 @@ func (p *Process) wait() (ps *ProcessState, err error) { } func (p *Process) signal(sig Signal) error { - if p.done { + if p.done() { return errors.New("os: process already finished") } if sig == Kill { diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go index 4acf35d67..32cac6d89 100644 --- a/src/pkg/os/file.go +++ b/src/pkg/os/file.go @@ -9,7 +9,7 @@ // if a call that takes a file name fails, such as Open or Stat, the error // will include the failing file name when printed and will be of type // *PathError, which may be unpacked for more information. -// +// // The os interface is intended to be uniform across all operating systems. // Features not generally available appear in the system-specific package syscall. // @@ -185,7 +185,7 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) { } // WriteString is like Write, but writes the contents of string s rather than -// an array of bytes. +// a slice of bytes. func (f *File) WriteString(s string) (ret int, err error) { if f == nil { return 0, ErrInvalid diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go index cb0e9ef92..d6d39a899 100644 --- a/src/pkg/os/file_plan9.go +++ b/src/pkg/os/file_plan9.go @@ -5,14 +5,11 @@ package os import ( - "errors" "runtime" "syscall" "time" ) -var ErrPlan9 = errors.New("unimplemented on Plan 9") - // File represents an open file descriptor. type File struct { *file @@ -107,7 +104,6 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { append = true } - syscall.ForkLock.RLock() if (create && trunc) || excl { fd, e = syscall.Create(name, flag, syscallMode(perm)) } else { @@ -120,7 +116,6 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { } } } - syscall.ForkLock.RUnlock() if e != nil { return nil, &PathError{"open", name, e} @@ -137,8 +132,8 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { // Close closes the File, rendering it unusable for I/O. // It returns an error, if any. -func (file *File) Close() error { - return file.file.close() +func (f *File) Close() error { + return f.file.close() } func (file *file) close() error { @@ -159,8 +154,8 @@ func (file *file) close() error { } // Stat returns the FileInfo structure describing file. -// It returns the FileInfo and an error, if any. -func (f *File) Stat() (FileInfo, error) { +// If there is an error, it will be of type *PathError. +func (f *File) Stat() (fi FileInfo, err error) { d, err := dirstat(f) if err != nil { return nil, err @@ -170,14 +165,20 @@ func (f *File) Stat() (FileInfo, error) { // Truncate changes the size of the file. // It does not change the I/O offset. +// If there is an error, it will be of type *PathError. func (f *File) Truncate(size int64) error { - var d Dir - d.Null() + var d syscall.Dir - d.Length = uint64(size) + d.Null() + d.Length = size - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { - return &PathError{"truncate", f.name, e} + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"truncate", f.name, err} + } + if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + return &PathError{"truncate", f.name, err} } return nil } @@ -187,7 +188,7 @@ const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | Mod // Chmod changes the mode of the file to mode. // If there is an error, it will be of type *PathError. func (f *File) Chmod(mode FileMode) error { - var d Dir + var d syscall.Dir odir, e := dirstat(f) if e != nil { @@ -195,8 +196,14 @@ func (f *File) Chmod(mode FileMode) error { } d.Null() d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { - return &PathError{"chmod", f.name, e} + + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"chmod", f.name, err} + } + if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + return &PathError{"chmod", f.name, err} } return nil } @@ -208,12 +215,16 @@ func (f *File) Sync() (err error) { if f == nil { return ErrInvalid } - - var d Dir + var d syscall.Dir d.Null() - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { - return NewSyscallError("fsync", e) + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return NewSyscallError("fsync", err) + } + if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + return NewSyscallError("fsync", err) } return nil } @@ -233,13 +244,23 @@ func (f *File) pread(b []byte, off int64) (n int, err error) { // write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. +// Since Plan 9 preserves message boundaries, never allow +// a zero-byte write. func (f *File) write(b []byte) (n int, err error) { + if len(b) == 0 { + return 0, nil + } return syscall.Write(f.fd, b) } // pwrite writes len(b) bytes to the File starting at byte offset off. // It returns the number of bytes written and an error, if any. +// Since Plan 9 preserves message boundaries, never allow +// a zero-byte write. func (f *File) pwrite(b []byte, off int64) (n int, err error) { + if len(b) == 0 { + return 0, nil + } return syscall.Pwrite(f.fd, b, off) } @@ -255,13 +276,18 @@ func (f *File) seek(offset int64, whence int) (ret int64, err error) { // If the file is a symbolic link, it changes the size of the link's target. // If there is an error, it will be of type *PathError. func Truncate(name string, size int64) error { - var d Dir - d.Null() + var d syscall.Dir - d.Length = uint64(size) + d.Null() + d.Length = size - if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { - return &PathError{"truncate", name, e} + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"truncate", name, err} + } + if err = syscall.Wstat(name, buf[:n]); err != nil { + return &PathError{"truncate", name, err} } return nil } @@ -277,21 +303,27 @@ func Remove(name string) error { // Rename renames a file. func Rename(oldname, newname string) error { - var d Dir - d.Null() + var d syscall.Dir + d.Null() d.Name = newname - if e := syscall.Wstat(oldname, pdir(nil, &d)); e != nil { - return &PathError{"rename", oldname, e} + buf := make([]byte, syscall.STATFIXLEN+len(d.Name)) + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"rename", oldname, err} + } + if err = syscall.Wstat(oldname, buf[:n]); err != nil { + return &PathError{"rename", oldname, err} } return nil } // Chmod changes the mode of the named file to mode. +// If the file is a symbolic link, it changes the mode of the link's target. // If there is an error, it will be of type *PathError. func Chmod(name string, mode FileMode) error { - var d Dir + var d syscall.Dir odir, e := dirstat(name) if e != nil { @@ -299,8 +331,14 @@ func Chmod(name string, mode FileMode) error { } d.Null() d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask - if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { - return &PathError{"chmod", name, e} + + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"chmod", name, err} + } + if err = syscall.Wstat(name, buf[:n]); err != nil { + return &PathError{"chmod", name, err} } return nil } @@ -310,19 +348,27 @@ func Chmod(name string, mode FileMode) error { // // The underlying filesystem may truncate or round the values to a // less precise time unit. +// If there is an error, it will be of type *PathError. func Chtimes(name string, atime time.Time, mtime time.Time) error { - var d Dir - d.Null() + var d syscall.Dir + d.Null() d.Atime = uint32(atime.Unix()) d.Mtime = uint32(mtime.Unix()) - if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { - return &PathError{"chtimes", name, e} + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"chtimes", name, err} + } + if err = syscall.Wstat(name, buf[:n]); err != nil { + return &PathError{"chtimes", name, err} } return nil } +// Pipe returns a connected pair of Files; reads from r return bytes +// written to w. It returns the files and an error, if any. func Pipe() (r *File, w *File, err error) { var p [2]int @@ -338,32 +384,42 @@ func Pipe() (r *File, w *File, err error) { // not supported on Plan 9 -// Link creates a hard link. +// Link creates newname as a hard link to the oldname file. // If there is an error, it will be of type *LinkError. func Link(oldname, newname string) error { - return &LinkError{"link", oldname, newname, ErrPlan9} + return &LinkError{"link", oldname, newname, syscall.EPLAN9} } // Symlink creates newname as a symbolic link to oldname. // If there is an error, it will be of type *LinkError. func Symlink(oldname, newname string) error { - return &LinkError{"symlink", oldname, newname, ErrPlan9} + return &LinkError{"symlink", oldname, newname, syscall.EPLAN9} } +// Readlink returns the destination of the named symbolic link. +// If there is an error, it will be of type *PathError. func Readlink(name string) (string, error) { - return "", ErrPlan9 + return "", &PathError{"readlink", name, syscall.EPLAN9} } +// Chown changes the numeric uid and gid of the named file. +// If the file is a symbolic link, it changes the uid and gid of the link's target. +// If there is an error, it will be of type *PathError. func Chown(name string, uid, gid int) error { - return ErrPlan9 + return &PathError{"chown", name, syscall.EPLAN9} } +// Lchown changes the numeric uid and gid of the named file. +// If the file is a symbolic link, it changes the uid and gid of the link itself. +// If there is an error, it will be of type *PathError. func Lchown(name string, uid, gid int) error { - return ErrPlan9 + return &PathError{"lchown", name, syscall.EPLAN9} } +// Chown changes the numeric uid and gid of the named file. +// If there is an error, it will be of type *PathError. func (f *File) Chown(uid, gid int) error { - return ErrPlan9 + return &PathError{"chown", f.name, syscall.EPLAN9} } // TempDir returns the default directory to use for temporary files. diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go index 073bd56a4..b979fed97 100644 --- a/src/pkg/os/file_posix.go +++ b/src/pkg/os/file_posix.go @@ -13,17 +13,6 @@ import ( func sigpipe() // implemented in package runtime -func epipecheck(file *File, e error) { - if e == syscall.EPIPE { - file.nepipe++ - if file.nepipe >= 10 { - sigpipe() - } - } else { - file.nepipe = 0 - } -} - // Link creates newname as a hard link to the oldname file. // If there is an error, it will be of type *LinkError. func Link(oldname, newname string) error { @@ -164,12 +153,10 @@ func (f *File) Sync() (err error) { // less precise time unit. // If there is an error, it will be of type *PathError. func Chtimes(name string, atime time.Time, mtime time.Time) error { - var utimes [2]syscall.Timeval - atime_ns := atime.Unix()*1e9 + int64(atime.Nanosecond()) - mtime_ns := mtime.Unix()*1e9 + int64(mtime.Nanosecond()) - utimes[0] = syscall.NsecToTimeval(atime_ns) - utimes[1] = syscall.NsecToTimeval(mtime_ns) - if e := syscall.Utimes(name, utimes[0:]); e != nil { + var utimes [2]syscall.Timespec + utimes[0] = syscall.NsecToTimespec(atime.UnixNano()) + utimes[1] = syscall.NsecToTimespec(mtime.UnixNano()) + if e := syscall.UtimesNano(name, utimes[0:]); e != nil { return &PathError{"chtimes", name, e} } return nil diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go index 6271c3189..4f59c94cb 100644 --- a/src/pkg/os/file_unix.go +++ b/src/pkg/os/file_unix.go @@ -8,6 +8,7 @@ package os import ( "runtime" + "sync/atomic" "syscall" ) @@ -24,7 +25,7 @@ type file struct { fd int name string dirinfo *dirInfo // nil unless directory being read - nepipe int // number of consecutive EPIPE in Write + nepipe int32 // number of consecutive EPIPE in Write } // Fd returns the integer Unix file descriptor referencing the open file. @@ -53,6 +54,16 @@ type dirInfo struct { bufp int // location of next record in buf. } +func epipecheck(file *File, e error) { + if e == syscall.EPIPE { + if atomic.AddInt32(&file.nepipe, 1) >= 10 { + sigpipe() + } + } else { + atomic.StoreInt32(&file.nepipe, 0) + } +} + // DevNull is the name of the operating system's ``null device.'' // On Unix-like systems, it is "/dev/null"; on Windows, "NUL". const DevNull = "/dev/null" @@ -263,25 +274,6 @@ func basename(name string) string { return name } -// Pipe returns a connected pair of Files; reads from r return bytes written to w. -// It returns the files and an error, if any. -func Pipe() (r *File, w *File, err error) { - var p [2]int - - // See ../syscall/exec.go for description of lock. - syscall.ForkLock.RLock() - e := syscall.Pipe(p[0:]) - if e != nil { - syscall.ForkLock.RUnlock() - return nil, nil, NewSyscallError("pipe", e) - } - syscall.CloseOnExec(p[0]) - syscall.CloseOnExec(p[1]) - syscall.ForkLock.RUnlock() - - return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil -} - // TempDir returns the default directory to use for temporary files. func TempDir() string { dir := Getenv("TMPDIR") diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go index 88fa77bb8..2eba7a475 100644 --- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -10,6 +10,8 @@ import ( "sync" "syscall" "unicode/utf16" + "unicode/utf8" + "unsafe" ) // File represents an open file descriptor. @@ -25,8 +27,12 @@ type file struct { fd syscall.Handle name string dirinfo *dirInfo // nil unless directory being read - nepipe int // number of consecutive EPIPE in Write l sync.Mutex // used to implement windows pread/pwrite + + // only for console io + isConsole bool + lastbits []byte // first few bytes of the last incomplete rune in last write + readbuf []rune // input console buffer } // Fd returns the Windows handle referencing the open file. @@ -37,15 +43,25 @@ func (file *File) Fd() uintptr { return uintptr(file.fd) } +// newFile returns a new File with the given file handle and name. +// Unlike NewFile, it does not check that h is syscall.InvalidHandle. +func newFile(h syscall.Handle, name string) *File { + f := &File{&file{fd: h, name: name}} + var m uint32 + if syscall.GetConsoleMode(f.fd, &m) == nil { + f.isConsole = true + } + runtime.SetFinalizer(f.file, (*file).close) + return f +} + // NewFile returns a new File with the given file descriptor and name. func NewFile(fd uintptr, name string) *File { h := syscall.Handle(fd) if h == syscall.InvalidHandle { return nil } - f := &File{&file{fd: h, name: name}} - runtime.SetFinalizer(f.file, (*file).close) - return f + return newFile(h, name) } // Auxiliary information if the File describes a directory @@ -53,6 +69,10 @@ type dirInfo struct { data syscall.Win32finddata needdata bool path string + isempty bool // set if FindFirstFile returns ERROR_FILE_NOT_FOUND +} + +func epipecheck(file *File, e error) { } const DevNull = "NUL" @@ -62,30 +82,45 @@ func (f *file) isdir() bool { return f != nil && f.dirinfo != nil } func openFile(name string, flag int, perm FileMode) (file *File, err error) { r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm)) if e != nil { - return nil, &PathError{"open", name, e} - } - - // There's a race here with fork/exec, which we are - // content to live with. See ../syscall/exec.go - if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported - syscall.CloseOnExec(r) + return nil, e } - return NewFile(uintptr(r), name), nil } func openDir(name string) (file *File, err error) { + maskp, e := syscall.UTF16PtrFromString(name + `\*`) + if e != nil { + return nil, e + } d := new(dirInfo) - r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+`\*`), &d.data) + r, e := syscall.FindFirstFile(maskp, &d.data) if e != nil { - return nil, &PathError{"open", name, e} + // FindFirstFile returns ERROR_FILE_NOT_FOUND when + // no matching files can be found. Then, if directory + // exists, we should proceed. + if e != syscall.ERROR_FILE_NOT_FOUND { + return nil, e + } + var fa syscall.Win32FileAttributeData + namep, e := syscall.UTF16PtrFromString(name) + if e != nil { + return nil, e + } + e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) + if e != nil { + return nil, e + } + if fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 { + return nil, e + } + d.isempty = true } d.path = name if !isAbs(d.path) { cwd, _ := Getwd() d.path = cwd + `\` + d.path } - f := NewFile(uintptr(r), name) + f := newFile(r, name) f.dirinfo = d return f, nil } @@ -112,7 +147,7 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { if e == nil { return r, nil } - return nil, e + return nil, &PathError{"open", name, e} } // Close closes the File, rendering it unusable for I/O. @@ -122,7 +157,14 @@ func (file *File) Close() error { } func (file *file) close() error { - if file == nil || file.fd == syscall.InvalidHandle { + if file == nil { + return syscall.EINVAL + } + if file.isdir() && file.dirinfo.isempty { + // "special" empty directories + return nil + } + if file.fd == syscall.InvalidHandle { return syscall.EINVAL } var e error @@ -143,12 +185,15 @@ func (file *file) close() error { } func (file *File) readdir(n int) (fi []FileInfo, err error) { - if file == nil || file.fd == syscall.InvalidHandle { + if file == nil { return nil, syscall.EINVAL } if !file.isdir() { return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR} } + if !file.dirinfo.isempty && file.fd == syscall.InvalidHandle { + return nil, syscall.EINVAL + } wantAll := n <= 0 size := n if wantAll { @@ -157,7 +202,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) { } fi = make([]FileInfo, 0, size) // Empty with room to grow. d := &file.dirinfo.data - for n != 0 { + for n != 0 && !file.dirinfo.isempty { if file.dirinfo.needdata { e := syscall.FindNextFile(syscall.Handle(file.fd), d) if e != nil { @@ -178,11 +223,16 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) { continue } f := &fileStat{ - name: name, - size: mkSize(d.FileSizeHigh, d.FileSizeLow), - modTime: mkModTime(d.LastWriteTime), - mode: mkMode(d.FileAttributes), - sys: mkSys(file.dirinfo.path+`\`+name, d.LastAccessTime, d.CreationTime), + name: name, + sys: syscall.Win32FileAttributeData{ + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: d.FileSizeHigh, + FileSizeLow: d.FileSizeLow, + }, + path: file.dirinfo.path + `\` + name, } n-- fi = append(fi, f) @@ -193,11 +243,48 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) { return fi, nil } +// readConsole reads utf16 charcters from console File, +// encodes them into utf8 and stores them in buffer b. +// It returns the number of utf8 bytes read and an error, if any. +func (f *File) readConsole(b []byte) (n int, err error) { + if len(b) == 0 { + return 0, nil + } + if len(f.readbuf) == 0 { + // get more input data from os + wchars := make([]uint16, len(b)) + var p *uint16 + if len(b) > 0 { + p = &wchars[0] + } + var nw uint32 + err := syscall.ReadConsole(f.fd, p, uint32(len(wchars)), &nw, nil) + if err != nil { + return 0, err + } + f.readbuf = utf16.Decode(wchars[:nw]) + } + for i, r := range f.readbuf { + if utf8.RuneLen(r) > len(b) { + f.readbuf = f.readbuf[i:] + return n, nil + } + nr := utf8.EncodeRune(b, r) + b = b[nr:] + n += nr + } + f.readbuf = nil + return n, nil +} + // read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. func (f *File) read(b []byte) (n int, err error) { f.l.Lock() defer f.l.Unlock() + if f.isConsole { + return f.readConsole(b) + } return syscall.Read(f.fd, b) } @@ -224,11 +311,57 @@ func (f *File) pread(b []byte, off int64) (n int, err error) { return int(done), nil } +// writeConsole writes len(b) bytes to the console File. +// It returns the number of bytes written and an error, if any. +func (f *File) writeConsole(b []byte) (n int, err error) { + n = len(b) + runes := make([]rune, 0, 256) + if len(f.lastbits) > 0 { + b = append(f.lastbits, b...) + f.lastbits = nil + + } + for len(b) >= utf8.UTFMax || utf8.FullRune(b) { + r, l := utf8.DecodeRune(b) + runes = append(runes, r) + b = b[l:] + } + if len(b) > 0 { + f.lastbits = make([]byte, len(b)) + copy(f.lastbits, b) + } + // syscall.WriteConsole seems to fail, if given large buffer. + // So limit the buffer to 16000 characters. This number was + // discovered by experimenting with syscall.WriteConsole. + const maxWrite = 16000 + for len(runes) > 0 { + m := len(runes) + if m > maxWrite { + m = maxWrite + } + chunk := runes[:m] + runes = runes[m:] + uint16s := utf16.Encode(chunk) + for len(uint16s) > 0 { + var written uint32 + err = syscall.WriteConsole(f.fd, &uint16s[0], uint32(len(uint16s)), &written, nil) + if err != nil { + return 0, nil + } + uint16s = uint16s[written:] + } + } + return n, nil +} + // write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. func (f *File) write(b []byte) (n int, err error) { f.l.Lock() defer f.l.Unlock() + if f.isConsole { + return f.writeConsole(b) + } return syscall.Write(f.fd, b) } @@ -282,11 +415,14 @@ func Truncate(name string, size int64) error { // Remove removes the named file or directory. // If there is an error, it will be of type *PathError. func Remove(name string) error { - p := &syscall.StringToUTF16(name)[0] + p, e := syscall.UTF16PtrFromString(name) + if e != nil { + return &PathError{"remove", name, e} + } // Go file interface forces us to know whether // name is a file or directory. Try both. - e := syscall.DeleteFile(p) + e = syscall.DeleteFile(p) if e == nil { return nil } diff --git a/src/pkg/os/getwd.go b/src/pkg/os/getwd.go index 81d8fed92..1b2212306 100644 --- a/src/pkg/os/getwd.go +++ b/src/pkg/os/getwd.go @@ -5,9 +5,15 @@ package os import ( + "sync" "syscall" ) +var getwdCache struct { + sync.Mutex + dir string +} + // Getwd returns a rooted path name corresponding to the // current directory. If the current directory can be // reached via multiple paths (due to symbolic links), @@ -35,6 +41,17 @@ func Getwd() (pwd string, err error) { } } + // Apply same kludge but to cached dir instead of $PWD. + getwdCache.Lock() + pwd = getwdCache.dir + getwdCache.Unlock() + if len(pwd) > 0 { + d, err := Stat(pwd) + if err == nil && SameFile(dot, d) { + return pwd, nil + } + } + // Root is a special case because it has no parent // and ends in a slash. root, err := Stat("/") @@ -88,5 +105,11 @@ func Getwd() (pwd string, err error) { // Set up for next round. dot = pd } + + // Save answer as hint to avoid the expensive path next time. + getwdCache.Lock() + getwdCache.dir = pwd + getwdCache.Unlock() + return pwd, nil } diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go index dec80cc09..29706015d 100644 --- a/src/pkg/os/os_test.go +++ b/src/pkg/os/os_test.go @@ -6,6 +6,7 @@ package os_test import ( "bytes" + "flag" "fmt" "io" "io/ioutil" @@ -40,7 +41,6 @@ var sysdir = func() (sd *sysDir) { sd = &sysDir{ Getenv("SystemRoot") + "\\system32\\drivers\\etc", []string{ - "hosts", "networks", "protocol", "services", @@ -69,10 +69,10 @@ var sysdir = func() (sd *sysDir) { func size(name string, t *testing.T) int64 { file, err := Open(name) - defer file.Close() if err != nil { t.Fatal("open failed:", err) } + defer file.Close() var buf [100]byte len := 0 for { @@ -134,10 +134,10 @@ func TestStat(t *testing.T) { func TestFstat(t *testing.T) { path := sfdir + "/" + sfname file, err1 := Open(path) - defer file.Close() if err1 != nil { t.Fatal("open failed:", err1) } + defer file.Close() dir, err2 := file.Stat() if err2 != nil { t.Fatal("fstat failed:", err2) @@ -189,10 +189,10 @@ func TestRead0(t *testing.T) { func testReaddirnames(dir string, contents []string, t *testing.T) { file, err := Open(dir) - defer file.Close() if err != nil { t.Fatalf("open %q failed: %v", dir, err) } + defer file.Close() s, err2 := file.Readdirnames(-1) if err2 != nil { t.Fatalf("readdirnames %q failed: %v", dir, err2) @@ -218,10 +218,10 @@ func testReaddirnames(dir string, contents []string, t *testing.T) { func testReaddir(dir string, contents []string, t *testing.T) { file, err := Open(dir) - defer file.Close() if err != nil { t.Fatalf("open %q failed: %v", dir, err) } + defer file.Close() s, err2 := file.Readdir(-1) if err2 != nil { t.Fatalf("readdir %q failed: %v", dir, err2) @@ -285,10 +285,10 @@ func TestReaddirnamesOneAtATime(t *testing.T) { dir = "/bin" } file, err := Open(dir) - defer file.Close() if err != nil { t.Fatalf("open %q failed: %v", dir, err) } + defer file.Close() all, err1 := file.Readdirnames(-1) if err1 != nil { t.Fatalf("readdirnames %q failed: %v", dir, err1) @@ -310,8 +310,7 @@ func TestReaddirnamesOneAtATime(t *testing.T) { func TestReaddirNValues(t *testing.T) { if testing.Short() { - t.Logf("test.short; skipping") - return + t.Skip("test.short; skipping") } dir, err := ioutil.TempDir("", "") if err != nil { @@ -535,8 +534,10 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) { var b bytes.Buffer io.Copy(&b, r) output := b.String() - // Accept /usr prefix because Solaris /bin is symlinked to /usr/bin. - if output != expect && output != "/usr"+expect { + + fi1, _ := Stat(strings.TrimSpace(output)) + fi2, _ := Stat(expect) + if !SameFile(fi1, fi2) { t.Errorf("exec %q returned %q wanted %q", strings.Join(append([]string{cmd}, args...), " "), output, expect) } @@ -544,15 +545,13 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) { } func TestStartProcess(t *testing.T) { - var dir, cmd, le string + var dir, cmd string var args []string if runtime.GOOS == "windows" { - le = "\r\n" cmd = Getenv("COMSPEC") dir = Getenv("SystemRoot") args = []string{"/c", "cd"} } else { - le = "\n" cmd = "/bin/pwd" dir = "/" args = []string{} @@ -560,9 +559,9 @@ func TestStartProcess(t *testing.T) { cmddir, cmdbase := filepath.Split(cmd) args = append([]string{cmdbase}, args...) // Test absolute executable path. - exec(t, dir, cmd, args, dir+le) + exec(t, dir, cmd, args, dir) // Test relative executable path. - exec(t, cmddir, cmdbase, args, filepath.Clean(cmddir)+le) + exec(t, cmddir, cmdbase, args, cmddir) } func checkMode(t *testing.T, path string, mode FileMode) { @@ -1066,3 +1065,52 @@ func TestDevNullFile(t *testing.T) { t.Fatalf("wrong file size have %d want 0", fi.Size()) } } + +var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output") + +func TestLargeWriteToConsole(t *testing.T) { + if !*testLargeWrite { + t.Skip("skipping console-flooding test; enable with -large_write") + } + b := make([]byte, 32000) + for i := range b { + b[i] = '.' + } + b[len(b)-1] = '\n' + n, err := Stdout.Write(b) + if err != nil { + t.Fatalf("Write to os.Stdout failed: %v", err) + } + if n != len(b) { + t.Errorf("Write to os.Stdout should return %d; got %d", len(b), n) + } + n, err = Stderr.Write(b) + if err != nil { + t.Fatalf("Write to os.Stderr failed: %v", err) + } + if n != len(b) { + t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n) + } +} + +func TestStatDirModeExec(t *testing.T) { + const mode = 0111 + + path, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("Failed to create temp directory: %v", err) + } + defer RemoveAll(path) + + if err := Chmod(path, 0777); err != nil { + t.Fatalf("Chmod %q 0777: %v", path, err) + } + + dir, err := Stat(path) + if err != nil { + t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) + } + if dir.Mode()&mode != mode { + t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode) + } +} diff --git a/src/pkg/os/path_plan9.go b/src/pkg/os/path_plan9.go index 3121b7bc7..64bad500a 100644 --- a/src/pkg/os/path_plan9.go +++ b/src/pkg/os/path_plan9.go @@ -5,8 +5,8 @@ package os const ( - PathSeparator = '/' // OS-specific path separator - PathListSeparator = 0 // OS-specific path list separator + PathSeparator = '/' // OS-specific path separator + PathListSeparator = '\000' // OS-specific path list separator ) // IsPathSeparator returns true if c is a directory separator character. diff --git a/src/pkg/os/path_test.go b/src/pkg/os/path_test.go index c1e3fb354..16c4120dc 100644 --- a/src/pkg/os/path_test.go +++ b/src/pkg/os/path_test.go @@ -5,6 +5,7 @@ package os_test import ( + "io/ioutil" . "os" "path/filepath" "runtime" @@ -167,24 +168,26 @@ func TestRemoveAll(t *testing.T) { func TestMkdirAllWithSymlink(t *testing.T) { if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { - t.Log("Skipping test: symlinks don't exist under Windows/Plan 9") - return + t.Skip("Skipping test: symlinks don't exist under Windows/Plan 9") } - tmpDir := TempDir() + tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-") + if err != nil { + t.Fatal(err) + } + defer RemoveAll(tmpDir) + dir := tmpDir + "/dir" - err := Mkdir(dir, 0755) + err = Mkdir(dir, 0755) if err != nil { t.Fatalf("Mkdir %s: %s", dir, err) } - defer RemoveAll(dir) link := tmpDir + "/link" err = Symlink("dir", link) if err != nil { t.Fatalf("Symlink %s: %s", link, err) } - defer RemoveAll(link) path := link + "/foo" err = MkdirAll(path, 0755) diff --git a/src/pkg/os/pipe_bsd.go b/src/pkg/os/pipe_bsd.go new file mode 100644 index 000000000..a2ce9a39f --- /dev/null +++ b/src/pkg/os/pipe_bsd.go @@ -0,0 +1,28 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd netbsd openbsd + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e := syscall.Pipe(p[0:]) + if e != nil { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + + return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil +} diff --git a/src/pkg/os/pipe_linux.go b/src/pkg/os/pipe_linux.go new file mode 100644 index 000000000..9bafad84f --- /dev/null +++ b/src/pkg/os/pipe_linux.go @@ -0,0 +1,33 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC) + // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it + // might not be implemented. + if e == syscall.ENOSYS { + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e = syscall.Pipe(p[0:]) + if e != nil { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + } else if e != nil { + return nil, nil, NewSyscallError("pipe2", e) + } + + return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil +} diff --git a/src/pkg/os/proc.go b/src/pkg/os/proc.go index 61545f445..38c436ec5 100644 --- a/src/pkg/os/proc.go +++ b/src/pkg/os/proc.go @@ -31,4 +31,6 @@ func Getgroups() ([]int, error) { // Exit causes the current program to exit with the given status code. // Conventionally, code zero indicates success, non-zero an error. +// The program terminates immediately; deferred functions are +// not run. func Exit(code int) { syscall.Exit(code) } diff --git a/src/pkg/os/signal/example_test.go b/src/pkg/os/signal/example_test.go new file mode 100644 index 000000000..600ed315d --- /dev/null +++ b/src/pkg/os/signal/example_test.go @@ -0,0 +1,19 @@ +package signal_test + +import ( + "fmt" + "os" + "os/signal" +) + +func ExampleNotify() { + // Set up channel on which to send signal notifications. + // We must use a buffered channel or risk missing the signal + // if we're not ready to receive when the signal is sent. + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, os.Kill) + + // Block until a signal is received. + s := <-c + fmt.Println("Got signal:", s) +} diff --git a/src/pkg/os/signal/signal_test.go b/src/pkg/os/signal/signal_test.go index 3494f8c34..509b273aa 100644 --- a/src/pkg/os/signal/signal_test.go +++ b/src/pkg/os/signal/signal_test.go @@ -8,6 +8,7 @@ package signal import ( "os" + "runtime" "syscall" "testing" "time" @@ -58,3 +59,43 @@ func TestSignal(t *testing.T) { // The first SIGHUP should be waiting for us on c. waitSig(t, c, syscall.SIGHUP) } + +func TestStress(t *testing.T) { + dur := 3 * time.Second + if testing.Short() { + dur = 100 * time.Millisecond + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) + done := make(chan bool) + finished := make(chan bool) + go func() { + sig := make(chan os.Signal, 1) + Notify(sig, syscall.SIGUSR1) + Loop: + for { + select { + case <-sig: + case <-done: + break Loop + } + } + finished <- true + }() + go func() { + Loop: + for { + select { + case <-done: + break Loop + default: + syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) + runtime.Gosched() + } + } + finished <- true + }() + time.Sleep(dur) + close(done) + <-finished + <-finished +} diff --git a/src/pkg/os/signal/signal_windows_test.go b/src/pkg/os/signal/signal_windows_test.go index 8d807ff7b..26712f35b 100644 --- a/src/pkg/os/signal/signal_windows_test.go +++ b/src/pkg/os/signal/signal_windows_test.go @@ -5,16 +5,16 @@ package signal import ( - "flag" + "bytes" "os" + "os/exec" + "path/filepath" "syscall" "testing" "time" ) -var runCtrlBreakTest = flag.Bool("run_ctlbrk_test", false, "force to run Ctrl+Break test") - -func sendCtrlBreak(t *testing.T) { +func sendCtrlBreak(t *testing.T, pid int) { d, e := syscall.LoadDLL("kernel32.dll") if e != nil { t.Fatalf("LoadDLL: %v\n", e) @@ -23,29 +23,74 @@ func sendCtrlBreak(t *testing.T) { if e != nil { t.Fatalf("FindProc: %v\n", e) } - r, _, e := p.Call(0, 0) + r, _, e := p.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid)) if r == 0 { t.Fatalf("GenerateConsoleCtrlEvent: %v\n", e) } } func TestCtrlBreak(t *testing.T) { - if !*runCtrlBreakTest { - t.Logf("test disabled; use -run_ctlbrk_test to enable") - return - } - go func() { - time.Sleep(1 * time.Second) - sendCtrlBreak(t) - }() + // create source file + const source = ` +package main + +import ( + "log" + "os" + "os/signal" + "time" +) + + +func main() { c := make(chan os.Signal, 10) - Notify(c) + signal.Notify(c) select { case s := <-c: if s != os.Interrupt { - t.Fatalf("Wrong signal received: got %q, want %q\n", s, os.Interrupt) + log.Fatalf("Wrong signal received: got %q, want %q\n", s, os.Interrupt) } case <-time.After(3 * time.Second): - t.Fatalf("Timeout waiting for Ctrl+Break\n") + log.Fatalf("Timeout waiting for Ctrl+Break\n") + } +} +` + name := filepath.Join(os.TempDir(), "ctlbreak") + src := name + ".go" + defer os.Remove(src) + f, err := os.Create(src) + if err != nil { + t.Fatalf("Failed to create %v: %v", src, err) + } + defer f.Close() + f.Write([]byte(source)) + + // compile it + exe := name + ".exe" + defer os.Remove(exe) + o, err := exec.Command("go", "build", "-o", exe, src).CombinedOutput() + if err != nil { + t.Fatalf("Failed to compile: %v\n%v", err, string(o)) + } + + // run it + cmd := exec.Command(exe) + var b bytes.Buffer + cmd.Stdout = &b + cmd.Stderr = &b + cmd.SysProcAttr = &syscall.SysProcAttr{ + CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, + } + err = cmd.Start() + if err != nil { + t.Fatalf("Start failed: %v", err) + } + go func() { + time.Sleep(1 * time.Second) + sendCtrlBreak(t, cmd.Process.Pid) + }() + err = cmd.Wait() + if err != nil { + t.Fatalf("Program exited with error: %v\n%v", err, string(b.Bytes())) } } diff --git a/src/pkg/os/stat_darwin.go b/src/pkg/os/stat_darwin.go index 2e5967d5c..0eea52201 100644 --- a/src/pkg/os/stat_darwin.go +++ b/src/pkg/os/stat_darwin.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_freebsd.go b/src/pkg/os/stat_freebsd.go index 6ba84f438..2ffb60fe2 100644 --- a/src/pkg/os/stat_freebsd.go +++ b/src/pkg/os/stat_freebsd.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_linux.go b/src/pkg/os/stat_linux.go index 00506b2b6..605c1d9b6 100644 --- a/src/pkg/os/stat_linux.go +++ b/src/pkg/os/stat_linux.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_netbsd.go b/src/pkg/os/stat_netbsd.go index 00506b2b6..2ffb60fe2 100644 --- a/src/pkg/os/stat_netbsd.go +++ b/src/pkg/os/stat_netbsd.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } @@ -19,7 +19,7 @@ func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo { fs := &fileStat{ name: basename(name), size: int64(st.Size), - modTime: timespecToTime(st.Mtim), + modTime: timespecToTime(st.Mtimespec), sys: st, } fs.mode = FileMode(st.Mode & 0777) @@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time { // For testing. func atime(fi FileInfo) time.Time { - return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim) + return timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec) } diff --git a/src/pkg/os/stat_openbsd.go b/src/pkg/os/stat_openbsd.go index 00506b2b6..605c1d9b6 100644 --- a/src/pkg/os/stat_openbsd.go +++ b/src/pkg/os/stat_openbsd.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_plan9.go b/src/pkg/os/stat_plan9.go index a7990a359..25c9a8c14 100644 --- a/src/pkg/os/stat_plan9.go +++ b/src/pkg/os/stat_plan9.go @@ -9,13 +9,13 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - a := sys1.(*Dir) - b := sys2.(*Dir) +func sameFile(fs1, fs2 *fileStat) bool { + a := fs1.sys.(*syscall.Dir) + b := fs2.sys.(*syscall.Dir) return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev } -func fileInfoFromStat(d *Dir) FileInfo { +func fileInfoFromStat(d *syscall.Dir) FileInfo { fs := &fileStat{ name: d.Name, size: int64(d.Length), @@ -38,8 +38,8 @@ func fileInfoFromStat(d *Dir) FileInfo { return fs } -// arg is an open *File or a path string. -func dirstat(arg interface{}) (d *Dir, err error) { +// arg is an open *File or a path string. +func dirstat(arg interface{}) (*syscall.Dir, error) { var name string // This is big enough for most stat messages @@ -50,41 +50,45 @@ func dirstat(arg interface{}) (d *Dir, err error) { buf := make([]byte, size) var n int + var err error switch a := arg.(type) { case *File: name = a.name n, err = syscall.Fstat(a.fd, buf) case string: name = a - n, err = syscall.Stat(name, buf) + n, err = syscall.Stat(a, buf) + default: + panic("phase error in dirstat") } if err != nil { return nil, &PathError{"stat", name, err} } if n < syscall.STATFIXLEN { - return nil, &PathError{"stat", name, errShortStat} + return nil, &PathError{"stat", name, syscall.ErrShortStat} } // Pull the real size out of the stat message. - s, _ := gbit16(buf) - size = int(s) + size = int(uint16(buf[0]) | uint16(buf[1])<<8) // If the stat message is larger than our buffer we will // go around the loop and allocate one that is big enough. - if size <= n { - d, err = UnmarshalDir(buf[:n]) - if err != nil { - return nil, &PathError{"stat", name, err} - } - return + if size > n { + continue } + + d, err := syscall.UnmarshalDir(buf[:n]) + if err != nil { + return nil, &PathError{"stat", name, err} + } + return d, nil } - return nil, &PathError{"stat", name, errBadStat} + return nil, &PathError{"stat", name, syscall.ErrBadStat} } -// Stat returns a FileInfo structure describing the named file. +// Stat returns a FileInfo describing the named file. // If there is an error, it will be of type *PathError. -func Stat(name string) (FileInfo, error) { +func Stat(name string) (fi FileInfo, err error) { d, err := dirstat(name) if err != nil { return nil, err @@ -92,15 +96,15 @@ func Stat(name string) (FileInfo, error) { return fileInfoFromStat(d), nil } -// Lstat returns the FileInfo structure describing the named file. -// If the file is a symbolic link (though Plan 9 does not have symbolic links), -// the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link. +// Lstat returns a FileInfo describing the named file. +// If the file is a symbolic link, the returned FileInfo +// describes the symbolic link. Lstat makes no attempt to follow the link. // If there is an error, it will be of type *PathError. -func Lstat(name string) (FileInfo, error) { +func Lstat(name string) (fi FileInfo, err error) { return Stat(name) } // For testing. func atime(fi FileInfo) time.Time { - return time.Unix(int64(fi.Sys().(*Dir).Atime), 0) + return time.Unix(int64(fi.Sys().(*syscall.Dir).Atime), 0) } diff --git a/src/pkg/os/stat_windows.go b/src/pkg/os/stat_windows.go index 75351c805..8394c2b32 100644 --- a/src/pkg/os/stat_windows.go +++ b/src/pkg/os/stat_windows.go @@ -5,9 +5,7 @@ package os import ( - "sync" "syscall" - "time" "unsafe" ) @@ -22,7 +20,7 @@ func (file *File) Stat() (fi FileInfo, err error) { return Stat(file.name) } if file.name == DevNull { - return statDevNull() + return &devNullStat, nil } var d syscall.ByHandleFileInformation e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &d) @@ -30,11 +28,18 @@ func (file *File) Stat() (fi FileInfo, err error) { return nil, &PathError{"GetFileInformationByHandle", file.name, e} } return &fileStat{ - name: basename(file.name), - size: mkSize(d.FileSizeHigh, d.FileSizeLow), - modTime: mkModTime(d.LastWriteTime), - mode: mkMode(d.FileAttributes), - sys: mkSysFromFI(&d), + name: basename(file.name), + sys: syscall.Win32FileAttributeData{ + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: d.FileSizeHigh, + FileSizeLow: d.FileSizeLow, + }, + vol: d.VolumeSerialNumber, + idxhi: d.FileIndexHigh, + idxlo: d.FileIndexLow, }, nil } @@ -45,25 +50,23 @@ func Stat(name string) (fi FileInfo, err error) { return nil, &PathError{"Stat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} } if name == DevNull { - return statDevNull() + return &devNullStat, nil + } + fs := &fileStat{name: basename(name)} + namep, e := syscall.UTF16PtrFromString(name) + if e != nil { + return nil, &PathError{"Stat", name, e} } - var d syscall.Win32FileAttributeData - e := syscall.GetFileAttributesEx(syscall.StringToUTF16Ptr(name), syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&d))) + e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys))) if e != nil { return nil, &PathError{"GetFileAttributesEx", name, e} } - path := name - if !isAbs(path) { + fs.path = name + if !isAbs(fs.path) { cwd, _ := Getwd() - path = cwd + `\` + path + fs.path = cwd + `\` + fs.path } - return &fileStat{ - name: basename(name), - size: mkSize(d.FileSizeHigh, d.FileSizeLow), - modTime: mkModTime(d.LastWriteTime), - mode: mkMode(d.FileAttributes), - sys: mkSys(path, d.LastAccessTime, d.CreationTime), - }, nil + return fs, nil } // Lstat returns the FileInfo structure describing the named file. @@ -75,22 +78,6 @@ func Lstat(name string) (fi FileInfo, err error) { return Stat(name) } -// statDevNull return FileInfo structure describing DevNull file ("NUL"). -// It creates invented data, since none of windows api will return -// that information. -func statDevNull() (fi FileInfo, err error) { - return &fileStat{ - name: DevNull, - mode: ModeDevice | ModeCharDevice | 0666, - sys: &winSys{ - // hopefully this will work for SameFile - vol: 0, - idxhi: 0, - idxlo: 0, - }, - }, nil -} - // basename removes trailing slashes and the leading // directory name and drive letter from path name. func basename(name string) string { @@ -168,91 +155,3 @@ func volumeName(path string) (v string) { } return "" } - -type winSys struct { - sync.Mutex - path string - atime, ctime syscall.Filetime - vol, idxhi, idxlo uint32 -} - -func mkSize(hi, lo uint32) int64 { - return int64(hi)<<32 + int64(lo) -} - -func mkModTime(mtime syscall.Filetime) time.Time { - return time.Unix(0, mtime.Nanoseconds()) -} - -func mkMode(fa uint32) (m FileMode) { - if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { - m |= ModeDir - } - if fa&syscall.FILE_ATTRIBUTE_READONLY != 0 { - m |= 0444 - } else { - m |= 0666 - } - return m -} - -func mkSys(path string, atime, ctime syscall.Filetime) *winSys { - return &winSys{ - path: path, - atime: atime, - ctime: ctime, - } -} - -func mkSysFromFI(i *syscall.ByHandleFileInformation) *winSys { - return &winSys{ - atime: i.LastAccessTime, - ctime: i.CreationTime, - vol: i.VolumeSerialNumber, - idxhi: i.FileIndexHigh, - idxlo: i.FileIndexLow, - } -} - -func (s *winSys) loadFileId() error { - if s.path == "" { - // already done - return nil - } - s.Lock() - defer s.Unlock() - h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(s.path), 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) - if e != nil { - return e - } - defer syscall.CloseHandle(h) - var i syscall.ByHandleFileInformation - e = syscall.GetFileInformationByHandle(syscall.Handle(h), &i) - if e != nil { - return e - } - s.path = "" - s.vol = i.VolumeSerialNumber - s.idxhi = i.FileIndexHigh - s.idxlo = i.FileIndexLow - return nil -} - -func sameFile(sys1, sys2 interface{}) bool { - s1 := sys1.(*winSys) - s2 := sys2.(*winSys) - e := s1.loadFileId() - if e != nil { - panic(e) - } - e = s2.loadFileId() - if e != nil { - panic(e) - } - return s1.vol == s2.vol && s1.idxhi == s2.idxhi && s1.idxlo == s2.idxlo -} - -// For testing. -func atime(fi FileInfo) time.Time { - return time.Unix(0, fi.Sys().(*winSys).atime.Nanoseconds()) -} diff --git a/src/pkg/os/types.go b/src/pkg/os/types.go index 0c95c9cec..473d431d4 100644 --- a/src/pkg/os/types.go +++ b/src/pkg/os/types.go @@ -12,7 +12,7 @@ import ( // Getpagesize returns the underlying system's memory page size. func Getpagesize() int { return syscall.Getpagesize() } -// A FileInfo describes a file and is returned by Stat and Lstat +// A FileInfo describes a file and is returned by Stat and Lstat. type FileInfo interface { Name() string // base name of the file Size() int64 // length in bytes for regular files; system-dependent for others @@ -88,26 +88,19 @@ func (m FileMode) IsDir() bool { return m&ModeDir != 0 } +// IsRegular reports whether m describes a regular file. +// That is, it tests that no mode type bits are set. +func (m FileMode) IsRegular() bool { + return m&ModeType == 0 +} + // Perm returns the Unix permission bits in m. func (m FileMode) Perm() FileMode { return m & ModePerm } -// A fileStat is the implementation of FileInfo returned by Stat and Lstat. -type fileStat struct { - name string - size int64 - mode FileMode - modTime time.Time - sys interface{} -} - -func (fs *fileStat) Name() string { return fs.name } -func (fs *fileStat) Size() int64 { return fs.size } -func (fs *fileStat) Mode() FileMode { return fs.mode } -func (fs *fileStat) ModTime() time.Time { return fs.modTime } -func (fs *fileStat) IsDir() bool { return fs.mode.IsDir() } -func (fs *fileStat) Sys() interface{} { return fs.sys } +func (fs *fileStat) Name() string { return fs.name } +func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() } // SameFile reports whether fi1 and fi2 describe the same file. // For example, on Unix this means that the device and inode fields @@ -121,5 +114,5 @@ func SameFile(fi1, fi2 FileInfo) bool { if !ok1 || !ok2 { return false } - return sameFile(fs1.sys, fs2.sys) + return sameFile(fs1, fs2) } diff --git a/src/pkg/os/types_notwin.go b/src/pkg/os/types_notwin.go new file mode 100644 index 000000000..ea1a07393 --- /dev/null +++ b/src/pkg/os/types_notwin.go @@ -0,0 +1,25 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +package os + +import ( + "time" +) + +// A fileStat is the implementation of FileInfo returned by Stat and Lstat. +type fileStat struct { + name string + size int64 + mode FileMode + modTime time.Time + sys interface{} +} + +func (fs *fileStat) Size() int64 { return fs.size } +func (fs *fileStat) Mode() FileMode { return fs.mode } +func (fs *fileStat) ModTime() time.Time { return fs.modTime } +func (fs *fileStat) Sys() interface{} { return fs.sys } diff --git a/src/pkg/os/types_windows.go b/src/pkg/os/types_windows.go new file mode 100644 index 000000000..38901681e --- /dev/null +++ b/src/pkg/os/types_windows.go @@ -0,0 +1,104 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import ( + "sync" + "syscall" + "time" +) + +// A fileStat is the implementation of FileInfo returned by Stat and Lstat. +type fileStat struct { + name string + sys syscall.Win32FileAttributeData + + // used to implement SameFile + sync.Mutex + path string + vol uint32 + idxhi uint32 + idxlo uint32 +} + +func (fs *fileStat) Size() int64 { + return int64(fs.sys.FileSizeHigh)<<32 + int64(fs.sys.FileSizeLow) +} + +func (fs *fileStat) Mode() (m FileMode) { + if fs == &devNullStat { + return ModeDevice | ModeCharDevice | 0666 + } + if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + m |= ModeDir | 0111 + } + if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 { + m |= 0444 + } else { + m |= 0666 + } + return m +} + +func (fs *fileStat) ModTime() time.Time { + return time.Unix(0, fs.sys.LastWriteTime.Nanoseconds()) +} + +// Sys returns syscall.Win32FileAttributeData for file fs. +func (fs *fileStat) Sys() interface{} { return &fs.sys } + +func (fs *fileStat) loadFileId() error { + fs.Lock() + defer fs.Unlock() + if fs.path == "" { + // already done + return nil + } + pathp, err := syscall.UTF16PtrFromString(fs.path) + if err != nil { + return err + } + h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) + if err != nil { + return err + } + defer syscall.CloseHandle(h) + var i syscall.ByHandleFileInformation + err = syscall.GetFileInformationByHandle(syscall.Handle(h), &i) + if err != nil { + return err + } + fs.path = "" + fs.vol = i.VolumeSerialNumber + fs.idxhi = i.FileIndexHigh + fs.idxlo = i.FileIndexLow + return nil +} + +// devNullStat is fileStat structure describing DevNull file ("NUL"). +var devNullStat = fileStat{ + name: DevNull, + // hopefully this will work for SameFile + vol: 0, + idxhi: 0, + idxlo: 0, +} + +func sameFile(fs1, fs2 *fileStat) bool { + e := fs1.loadFileId() + if e != nil { + return false + } + e = fs2.loadFileId() + if e != nil { + return false + } + return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo +} + +// For testing. +func atime(fi FileInfo) time.Time { + return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) +} diff --git a/src/pkg/os/user/lookup.go b/src/pkg/os/user/lookup.go new file mode 100644 index 000000000..09f00c7bd --- /dev/null +++ b/src/pkg/os/user/lookup.go @@ -0,0 +1,22 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package user + +// Current returns the current user. +func Current() (*User, error) { + return current() +} + +// Lookup looks up a user by username. If the user cannot be found, the +// returned error is of type UnknownUserError. +func Lookup(username string) (*User, error) { + return lookup(username) +} + +// LookupId looks up a user by userid. If the user cannot be found, the +// returned error is of type UnknownUserIdError. +func LookupId(uid string) (*User, error) { + return lookupId(uid) +} diff --git a/src/pkg/os/user/lookup_stubs.go b/src/pkg/os/user/lookup_stubs.go index 415f869f2..ad06907b5 100644 --- a/src/pkg/os/user/lookup_stubs.go +++ b/src/pkg/os/user/lookup_stubs.go @@ -15,14 +15,14 @@ func init() { implemented = false } -func Current() (*User, error) { +func current() (*User, error) { return nil, fmt.Errorf("user: Current not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) } -func Lookup(username string) (*User, error) { +func lookup(username string) (*User, error) { return nil, fmt.Errorf("user: Lookup not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) } -func LookupId(string) (*User, error) { +func lookupId(uid string) (*User, error) { return nil, fmt.Errorf("user: LookupId not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) } diff --git a/src/pkg/os/user/lookup_unix.go b/src/pkg/os/user/lookup_unix.go index 241957c33..609542263 100644 --- a/src/pkg/os/user/lookup_unix.go +++ b/src/pkg/os/user/lookup_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux +// +build darwin freebsd linux netbsd openbsd // +build cgo package user @@ -29,28 +29,23 @@ static int mygetpwuid_r(int uid, struct passwd *pwd, */ import "C" -// Current returns the current user. -func Current() (*User, error) { - return lookup(syscall.Getuid(), "", false) +func current() (*User, error) { + return lookupUnix(syscall.Getuid(), "", false) } -// Lookup looks up a user by username. If the user cannot be found, -// the returned error is of type UnknownUserError. -func Lookup(username string) (*User, error) { - return lookup(-1, username, true) +func lookup(username string) (*User, error) { + return lookupUnix(-1, username, true) } -// LookupId looks up a user by userid. If the user cannot be found, -// the returned error is of type UnknownUserIdError. -func LookupId(uid string) (*User, error) { +func lookupId(uid string) (*User, error) { i, e := strconv.Atoi(uid) if e != nil { return nil, e } - return lookup(i, "", false) + return lookupUnix(i, "", false) } -func lookup(uid int, username string, lookupByName bool) (*User, error) { +func lookupUnix(uid int, username string, lookupByName bool) (*User, error) { var pwd C.struct_passwd var result *C.struct_passwd diff --git a/src/pkg/os/user/lookup_windows.go b/src/pkg/os/user/lookup_windows.go index 993687115..a0a8a4ec1 100644 --- a/src/pkg/os/user/lookup_windows.go +++ b/src/pkg/os/user/lookup_windows.go @@ -16,13 +16,21 @@ func lookupFullName(domain, username, domainAndUser string) (string, error) { syscall.NameSamCompatible, syscall.NameDisplay, 50) if e != nil { // domain lookup failed, perhaps this pc is not part of domain - d := syscall.StringToUTF16Ptr(domain) - u := syscall.StringToUTF16Ptr(username) - var p *byte - e := syscall.NetUserGetInfo(d, u, 10, &p) + d, e := syscall.UTF16PtrFromString(domain) + if e != nil { + return "", e + } + u, e := syscall.UTF16PtrFromString(username) if e != nil { return "", e } + var p *byte + e = syscall.NetUserGetInfo(d, u, 10, &p) + if e != nil { + // path executed when a domain user is disconnected from the domain + // pretend username is fullname + return username, nil + } defer syscall.NetApiBufferFree(p) i := (*syscall.UserInfo10)(unsafe.Pointer(p)) if i.FullName == nil { @@ -60,8 +68,7 @@ func newUser(usid *syscall.SID, gid, dir string) (*User, error) { return u, nil } -// Current returns the current user. -func Current() (*User, error) { +func current() (*User, error) { t, e := syscall.OpenCurrentProcessToken() if e != nil { return nil, e @@ -95,8 +102,7 @@ func newUserFromSid(usid *syscall.SID) (*User, error) { return newUser(usid, gid, dir) } -// Lookup looks up a user by username. -func Lookup(username string) (*User, error) { +func lookup(username string) (*User, error) { sid, _, t, e := syscall.LookupSID("", username) if e != nil { return nil, e @@ -107,8 +113,7 @@ func Lookup(username string) (*User, error) { return newUserFromSid(sid) } -// LookupId looks up a user by userid. -func LookupId(uid string) (*User, error) { +func lookupId(uid string) (*User, error) { sid, e := syscall.StringToSid(uid) if e != nil { return nil, e diff --git a/src/pkg/os/user/user_test.go b/src/pkg/os/user/user_test.go index b812ebce7..444a9aacd 100644 --- a/src/pkg/os/user/user_test.go +++ b/src/pkg/os/user/user_test.go @@ -5,41 +5,34 @@ package user import ( - "os" "runtime" "testing" ) -func skip(t *testing.T) bool { +func check(t *testing.T) { if !implemented { - t.Logf("user: not implemented; skipping tests") - return true + t.Skip("user: not implemented; skipping tests") } - switch runtime.GOOS { case "linux", "freebsd", "darwin", "windows": - return false + // test supported + default: + t.Skipf("user: Lookup not implemented on %q; skipping test", runtime.GOOS) } - - t.Logf("user: Lookup not implemented on %s; skipping test", runtime.GOOS) - return true } func TestCurrent(t *testing.T) { - if skip(t) { - return - } + check(t) u, err := Current() if err != nil { t.Fatalf("Current: %v", err) } - fi, err := os.Stat(u.HomeDir) - if err != nil || !fi.IsDir() { - t.Errorf("expected a valid HomeDir; stat(%q): err=%v", u.HomeDir, err) + if u.HomeDir == "" { + t.Errorf("didn't get a HomeDir") } if u.Username == "" { - t.Fatalf("didn't get a username") + t.Errorf("didn't get a username") } } @@ -55,8 +48,7 @@ func compare(t *testing.T, want, got *User) { } // TODO(brainman): fix it once we know how. if runtime.GOOS == "windows" { - t.Log("skipping Gid and HomeDir comparisons") - return + t.Skip("skipping Gid and HomeDir comparisons") } if want.Gid != got.Gid { t.Errorf("got Gid=%q; want %q", got.Gid, want.Gid) @@ -67,9 +59,7 @@ func compare(t *testing.T, want, got *User) { } func TestLookup(t *testing.T) { - if skip(t) { - return - } + check(t) want, err := Current() if err != nil { @@ -83,9 +73,7 @@ func TestLookup(t *testing.T) { } func TestLookupId(t *testing.T) { - if skip(t) { - return - } + check(t) want, err := Current() if err != nil { diff --git a/src/pkg/path/filepath/example_unix_test.go b/src/pkg/path/filepath/example_unix_test.go new file mode 100644 index 000000000..f3fe076c3 --- /dev/null +++ b/src/pkg/path/filepath/example_unix_test.go @@ -0,0 +1,39 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows,!plan9 + +package filepath_test + +import ( + "fmt" + "path/filepath" +) + +func ExampleSplitList() { + fmt.Println("On Unix:", filepath.SplitList("/a/b/c:/usr/bin")) + // Output: + // On Unix: [/a/b/c /usr/bin] +} + +func ExampleRel() { + paths := []string{ + "/a/b/c", + "/b/c", + "./b/c", + } + base := "/a" + + fmt.Println("On Unix:") + for _, p := range paths { + rel, err := filepath.Rel(base, p) + fmt.Printf("%q: %q %v\n", p, rel, err) + } + + // Output: + // On Unix: + // "/a/b/c": "b/c" + // "/b/c": "../b/c" + // "./b/c": "" Rel: can't make b/c relative to /a +} diff --git a/src/pkg/path/filepath/match_test.go b/src/pkg/path/filepath/match_test.go index 7b0ea8017..f1bc60e35 100644 --- a/src/pkg/path/filepath/match_test.go +++ b/src/pkg/path/filepath/match_test.go @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package filepath_test +package filepath import ( - . "path/filepath" "runtime" "strings" "testing" diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go index 815021bd0..f8c7e4b2f 100644 --- a/src/pkg/path/filepath/path.go +++ b/src/pkg/path/filepath/path.go @@ -13,6 +13,45 @@ import ( "strings" ) +// A lazybuf is a lazily constructed path buffer. +// It supports append, reading previously appended bytes, +// and retrieving the final string. It does not allocate a buffer +// to hold the output until that output diverges from s. +type lazybuf struct { + path string + buf []byte + w int + volAndPath string + volLen int +} + +func (b *lazybuf) index(i int) byte { + if b.buf != nil { + return b.buf[i] + } + return b.path[i] +} + +func (b *lazybuf) append(c byte) { + if b.buf == nil { + if b.w < len(b.path) && b.path[b.w] == c { + b.w++ + return + } + b.buf = make([]byte, len(b.path)) + copy(b.buf, b.path[:b.w]) + } + b.buf[b.w] = c + b.w++ +} + +func (b *lazybuf) string() string { + if b.buf == nil { + return b.volAndPath[:b.volLen+b.w] + } + return b.volAndPath[:b.volLen] + string(b.buf[:b.w]) +} + const ( Separator = os.PathSeparator ListSeparator = os.PathListSeparator @@ -40,14 +79,15 @@ const ( // Getting Dot-Dot Right,'' // http://plan9.bell-labs.com/sys/doc/lexnames.html func Clean(path string) string { - vol := VolumeName(path) - path = path[len(vol):] + originalPath := path + volLen := volumeNameLen(path) + path = path[volLen:] if path == "" { - if len(vol) > 1 && vol[1] != ':' { + if volLen > 1 && originalPath[1] != ':' { // should be UNC - return FromSlash(vol) + return FromSlash(originalPath) } - return vol + "." + return originalPath + "." } rooted := os.IsPathSeparator(path[0]) @@ -57,11 +97,11 @@ func Clean(path string) string { // dotdot is index in buf where .. must stop, either because // it is the leading slash or it is a leading ../../.. prefix. n := len(path) - buf := []byte(path) - r, w, dotdot := 0, 0, 0 + out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen} + r, dotdot := 0, 0 if rooted { - buf[0] = Separator - r, w, dotdot = 1, 1, 1 + out.append(Separator) + r, dotdot = 1, 1 } for r < n { @@ -76,46 +116,40 @@ func Clean(path string) string { // .. element: remove to last separator r += 2 switch { - case w > dotdot: + case out.w > dotdot: // can backtrack - w-- - for w > dotdot && !os.IsPathSeparator(buf[w]) { - w-- + out.w-- + for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) { + out.w-- } case !rooted: // cannot backtrack, but not rooted, so append .. element. - if w > 0 { - buf[w] = Separator - w++ + if out.w > 0 { + out.append(Separator) } - buf[w] = '.' - w++ - buf[w] = '.' - w++ - dotdot = w + out.append('.') + out.append('.') + dotdot = out.w } default: // real path element. // add slash if needed - if rooted && w != 1 || !rooted && w != 0 { - buf[w] = Separator - w++ + if rooted && out.w != 1 || !rooted && out.w != 0 { + out.append(Separator) } // copy element for ; r < n && !os.IsPathSeparator(path[r]); r++ { - buf[w] = path[r] - w++ + out.append(path[r]) } } } // Turn empty string into "." - if w == 0 { - buf[w] = '.' - w++ + if out.w == 0 { + out.append('.') } - return FromSlash(vol + string(buf[0:w])) + return FromSlash(out.string()) } // ToSlash returns the result of replacing each separator character @@ -142,10 +176,7 @@ func FromSlash(path string) string { // usually found in PATH or GOPATH environment variables. // Unlike strings.Split, SplitList returns an empty slice when passed an empty string. func SplitList(path string) []string { - if path == "" { - return []string{} - } - return strings.Split(path, string(ListSeparator)) + return splitList(path) } // Split splits path immediately following the final Separator, @@ -291,13 +322,18 @@ func Rel(basepath, targpath string) (string, error) { var SkipDir = errors.New("skip this directory") // WalkFunc is the type of the function called for each file or directory -// visited by Walk. If there was a problem walking to the file or directory -// named by path, the incoming error will describe the problem and the -// function can decide how to handle that error (and Walk will not descend -// into that directory). If an error is returned, processing stops. The -// sole exception is that if path is a directory and the function returns the -// special value SkipDir, the contents of the directory are skipped -// and processing continues as usual on the next file. +// visited by Walk. The path argument contains the argument to Walk as a +// prefix; that is, if Walk is called with "dir", which is a directory +// containing the file "a", the walk function will be called with argument +// "dir/a". The info argument is the os.FileInfo for the named path. +// +// If there was a problem walking to the file or directory named by path, the +// incoming error will describe the problem and the function can decide how +// to handle that error (and Walk will not descend into that directory). If +// an error is returned, processing stops. The sole exception is that if path +// is a directory and the function returns the special value SkipDir, the +// contents of the directory are skipped and processing continues as usual on +// the next file. type WalkFunc func(path string, info os.FileInfo, err error) error // walk recursively descends path, calling w. @@ -335,6 +371,7 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error { // and directories are filtered by walkFn. The files are walked in lexical // order, which makes the output deterministic but means that for very // large directories Walk can be inefficient. +// Walk does not follow symbolic links. func Walk(root string, walkFn WalkFunc) error { info, err := os.Lstat(root) if err != nil { @@ -397,7 +434,8 @@ func Base(path string) string { } // Dir returns all but the last element of path, typically the path's directory. -// Trailing path separators are removed before processing. +// After dropping the final element, the path is Cleaned and trailing +// slashes are removed. // If the path is empty, Dir returns ".". // If the path consists entirely of separators, Dir returns a single separator. // The returned path does not end in a separator unless it is the root directory. @@ -417,3 +455,11 @@ func Dir(path string) string { } return vol + dir } + +// VolumeName returns leading volume name. +// Given "C:\foo\bar" it returns "C:" under windows. +// Given "\\host\share\foo" it returns "\\host\share". +// On other platforms it returns "". +func VolumeName(path string) (v string) { + return path[:volumeNameLen(path)] +} diff --git a/src/pkg/path/filepath/path_plan9.go b/src/pkg/path/filepath/path_plan9.go index 59a5812dd..12e85aae0 100644 --- a/src/pkg/path/filepath/path_plan9.go +++ b/src/pkg/path/filepath/path_plan9.go @@ -11,13 +11,20 @@ func IsAbs(path string) bool { return strings.HasPrefix(path, "/") || strings.HasPrefix(path, "#") } -// VolumeName returns the leading volume name on Windows. -// It returns "" elsewhere. -func VolumeName(path string) string { - return "" +// volumeNameLen returns length of the leading volume name on Windows. +// It returns 0 elsewhere. +func volumeNameLen(path string) int { + return 0 } // HasPrefix exists for historical compatibility and should not be used. func HasPrefix(p, prefix string) bool { return strings.HasPrefix(p, prefix) } + +func splitList(path string) []string { + if path == "" { + return []string{} + } + return strings.Split(path, string(ListSeparator)) +} diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go index 070905fd3..e768ad32f 100644 --- a/src/pkg/path/filepath/path_test.go +++ b/src/pkg/path/filepath/path_test.go @@ -20,7 +20,6 @@ type PathTest struct { var cleantests = []PathTest{ // Already clean - {"", "."}, {"abc", "abc"}, {"abc/def", "abc/def"}, {"a/b/c", "a/b/c"}, @@ -31,6 +30,9 @@ var cleantests = []PathTest{ {"/abc", "/abc"}, {"/", "/"}, + // Empty is current dir + {"", "."}, + // Remove trailing slash {"abc/", "abc"}, {"abc/def/", "abc/def"}, @@ -61,6 +63,7 @@ var cleantests = []PathTest{ {"abc/def/../../..", ".."}, {"/abc/def/../../..", "/"}, {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, + {"/../abc", "/abc"}, // Combinations {"abc/./../def", "def"}, @@ -99,6 +102,16 @@ func TestClean(t *testing.T) { if s := filepath.Clean(test.path); s != test.result { t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) } + if s := filepath.Clean(test.result); s != test.result { + t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result) + } + } + + for _, test := range tests { + allocs := testing.AllocsPerRun(100, func() { filepath.Clean(test.result) }) + if allocs > 0 { + t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs) + } } } @@ -135,10 +148,36 @@ var splitlisttests = []SplitListTest{ {string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}}, } +var winsplitlisttests = []SplitListTest{ + // quoted + {`"a"`, []string{`a`}}, + + // semicolon + {`";"`, []string{`;`}}, + {`"a;b"`, []string{`a;b`}}, + {`";";`, []string{`;`, ``}}, + {`;";"`, []string{``, `;`}}, + + // partially quoted + {`a";"b`, []string{`a;b`}}, + {`a; ""b`, []string{`a`, ` b`}}, + {`"a;b`, []string{`a;b`}}, + {`""a;b`, []string{`a`, `b`}}, + {`"""a;b`, []string{`a;b`}}, + {`""""a;b`, []string{`a`, `b`}}, + {`a";b`, []string{`a;b`}}, + {`a;b";c`, []string{`a`, `b;c`}}, + {`"a";b";c`, []string{`a`, `b;c`}}, +} + func TestSplitList(t *testing.T) { - for _, test := range splitlisttests { + tests := splitlisttests + if runtime.GOOS == "windows" { + tests = append(tests, winsplitlisttests...) + } + for _, test := range tests { if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) { - t.Errorf("SplitList(%q) = %s, want %s", test.list, l, test.result) + t.Errorf("SplitList(%#q) = %#q, want %#q", test.list, l, test.result) } } } @@ -684,10 +723,15 @@ func TestAbs(t *testing.T) { } defer os.RemoveAll(root) + wd, err := os.Getwd() + if err != nil { + t.Fatal("getwd failed: ", err) + } err = os.Chdir(root) if err != nil { t.Fatal("chdir failed: ", err) } + defer os.Chdir(wd) for _, dir := range absTestDirs { err = os.Mkdir(dir, 0777) @@ -871,7 +915,7 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) { } func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486 - root, err := filepath.EvalSymlinks(os.Getenv("GOROOT")) + root, err := filepath.EvalSymlinks(runtime.GOROOT()) if err != nil { t.Fatal(err) } diff --git a/src/pkg/path/filepath/path_unix.go b/src/pkg/path/filepath/path_unix.go index 305e30727..cff7b2c65 100644 --- a/src/pkg/path/filepath/path_unix.go +++ b/src/pkg/path/filepath/path_unix.go @@ -13,13 +13,20 @@ func IsAbs(path string) bool { return strings.HasPrefix(path, "/") } -// VolumeName returns the leading volume name on Windows. -// It returns "" elsewhere. -func VolumeName(path string) string { - return "" +// volumeNameLen returns length of the leading volume name on Windows. +// It returns 0 elsewhere. +func volumeNameLen(path string) int { + return 0 } // HasPrefix exists for historical compatibility and should not be used. func HasPrefix(p, prefix string) bool { return strings.HasPrefix(p, prefix) } + +func splitList(path string) []string { + if path == "" { + return []string{} + } + return strings.Split(path, string(ListSeparator)) +} diff --git a/src/pkg/path/filepath/path_windows.go b/src/pkg/path/filepath/path_windows.go index 3dcd03021..e99997257 100644 --- a/src/pkg/path/filepath/path_windows.go +++ b/src/pkg/path/filepath/path_windows.go @@ -14,29 +14,27 @@ func isSlash(c uint8) bool { // IsAbs returns true if the path is absolute. func IsAbs(path string) (b bool) { - v := VolumeName(path) - if v == "" { + l := volumeNameLen(path) + if l == 0 { return false } - path = path[len(v):] + path = path[l:] if path == "" { return false } return isSlash(path[0]) } -// VolumeName returns leading volume name. -// Given "C:\foo\bar" it returns "C:" under windows. -// Given "\\host\share\foo" it returns "\\host\share". -// On other platforms it returns "". -func VolumeName(path string) (v string) { +// volumeNameLen returns length of the leading volume name on Windows. +// It returns 0 elsewhere. +func volumeNameLen(path string) int { if len(path) < 2 { - return "" + return 0 } // with drive letter c := path[0] if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') { - return path[:2] + return 2 } // is it UNC if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && @@ -56,13 +54,13 @@ func VolumeName(path string) (v string) { break } } - return path[:n] + return n } break } } } - return "" + return 0 } // HasPrefix exists for historical compatibility and should not be used. @@ -72,3 +70,36 @@ func HasPrefix(p, prefix string) bool { } return strings.HasPrefix(strings.ToLower(p), strings.ToLower(prefix)) } + +func splitList(path string) []string { + // The same implementation is used in LookPath in os/exec; + // consider changing os/exec when changing this. + + if path == "" { + return []string{} + } + + // Split path, respecting but preserving quotes. + list := []string{} + start := 0 + quo := false + for i := 0; i < len(path); i++ { + switch c := path[i]; { + case c == '"': + quo = !quo + case c == ListSeparator && !quo: + list = append(list, path[start:i]) + start = i + 1 + } + } + list = append(list, path[start:]) + + // Remove quotes. + for i, s := range list { + if strings.Contains(s, `"`) { + list[i] = strings.Replace(s, `"`, ``, -1) + } + } + + return list +} diff --git a/src/pkg/path/filepath/path_windows_test.go b/src/pkg/path/filepath/path_windows_test.go new file mode 100644 index 000000000..d8926adde --- /dev/null +++ b/src/pkg/path/filepath/path_windows_test.go @@ -0,0 +1,89 @@ +package filepath_test + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "reflect" + "testing" +) + +func TestWinSplitListTestsAreValid(t *testing.T) { + comspec := os.Getenv("ComSpec") + if comspec == "" { + t.Fatal("%ComSpec% must be set") + } + + for ti, tt := range winsplitlisttests { + testWinSplitListTestIsValid(t, ti, tt, comspec) + } +} + +func testWinSplitListTestIsValid(t *testing.T, ti int, tt SplitListTest, + comspec string) { + + const ( + cmdfile = `printdir.cmd` + perm os.FileMode = 0700 + ) + + tmp, err := ioutil.TempDir("", "testWinSplitListTestIsValid") + if err != nil { + t.Fatalf("TempDir failed: %v", err) + } + defer os.RemoveAll(tmp) + + for i, d := range tt.result { + if d == "" { + continue + } + if cd := filepath.Clean(d); filepath.VolumeName(cd) != "" || + cd[0] == '\\' || cd == ".." || (len(cd) >= 3 && cd[0:3] == `..\`) { + t.Errorf("%d,%d: %#q refers outside working directory", ti, i, d) + return + } + dd := filepath.Join(tmp, d) + if _, err := os.Stat(dd); err == nil { + t.Errorf("%d,%d: %#q already exists", ti, i, d) + return + } + if err = os.MkdirAll(dd, perm); err != nil { + t.Errorf("%d,%d: MkdirAll(%#q) failed: %v", ti, i, dd, err) + return + } + fn, data := filepath.Join(dd, cmdfile), []byte("@echo "+d+"\r\n") + if err = ioutil.WriteFile(fn, data, perm); err != nil { + t.Errorf("%d,%d: WriteFile(%#q) failed: %v", ti, i, fn, err) + return + } + } + + for i, d := range tt.result { + if d == "" { + continue + } + exp := []byte(d + "\r\n") + cmd := &exec.Cmd{ + Path: comspec, + Args: []string{`/c`, cmdfile}, + Env: []string{`Path=` + tt.list}, + Dir: tmp, + } + out, err := cmd.CombinedOutput() + switch { + case err != nil: + t.Errorf("%d,%d: execution error %v\n%q", ti, i, err, out) + return + case !reflect.DeepEqual(out, exp): + t.Errorf("%d,%d: expected %#q, got %#q", ti, i, exp, out) + return + default: + // unshadow cmdfile in next directory + err = os.Remove(filepath.Join(tmp, d, cmdfile)) + if err != nil { + t.Fatalf("Remove test command failed: %v", err) + } + } + } +} diff --git a/src/pkg/path/filepath/symlink_windows.go b/src/pkg/path/filepath/symlink_windows.go index 1ee939928..9adc8a48a 100644 --- a/src/pkg/path/filepath/symlink_windows.go +++ b/src/pkg/path/filepath/symlink_windows.go @@ -9,7 +9,10 @@ import ( ) func toShort(path string) (string, error) { - p := syscall.StringToUTF16(path) + p, err := syscall.UTF16FromString(path) + if err != nil { + return "", err + } b := p // GetShortPathName says we can reuse buffer n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b))) if err != nil { @@ -26,7 +29,10 @@ func toShort(path string) (string, error) { } func toLong(path string) (string, error) { - p := syscall.StringToUTF16(path) + p, err := syscall.UTF16FromString(path) + if err != nil { + return "", err + } b := p // GetLongPathName says we can reuse buffer n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b))) if err != nil { diff --git a/src/pkg/path/path.go b/src/pkg/path/path.go index a7e041568..bdb85c6b9 100644 --- a/src/pkg/path/path.go +++ b/src/pkg/path/path.go @@ -10,6 +10,43 @@ import ( "strings" ) +// A lazybuf is a lazily constructed path buffer. +// It supports append, reading previously appended bytes, +// and retrieving the final string. It does not allocate a buffer +// to hold the output until that output diverges from s. +type lazybuf struct { + s string + buf []byte + w int +} + +func (b *lazybuf) index(i int) byte { + if b.buf != nil { + return b.buf[i] + } + return b.s[i] +} + +func (b *lazybuf) append(c byte) { + if b.buf == nil { + if b.w < len(b.s) && b.s[b.w] == c { + b.w++ + return + } + b.buf = make([]byte, len(b.s)) + copy(b.buf, b.s[:b.w]) + } + b.buf[b.w] = c + b.w++ +} + +func (b *lazybuf) string() string { + if b.buf == nil { + return b.s[:b.w] + } + return string(b.buf[:b.w]) +} + // Clean returns the shortest path name equivalent to path // by purely lexical processing. It applies the following rules // iteratively until no further processing can be done: @@ -42,10 +79,11 @@ func Clean(path string) string { // writing to buf; w is index of next byte to write. // dotdot is index in buf where .. must stop, either because // it is the leading slash or it is a leading ../../.. prefix. - buf := []byte(path) - r, w, dotdot := 0, 0, 0 + out := lazybuf{s: path} + r, dotdot := 0, 0 if rooted { - r, w, dotdot = 1, 1, 1 + out.append('/') + r, dotdot = 1, 1 } for r < n { @@ -60,46 +98,40 @@ func Clean(path string) string { // .. element: remove to last / r += 2 switch { - case w > dotdot: + case out.w > dotdot: // can backtrack - w-- - for w > dotdot && buf[w] != '/' { - w-- + out.w-- + for out.w > dotdot && out.index(out.w) != '/' { + out.w-- } case !rooted: // cannot backtrack, but not rooted, so append .. element. - if w > 0 { - buf[w] = '/' - w++ + if out.w > 0 { + out.append('/') } - buf[w] = '.' - w++ - buf[w] = '.' - w++ - dotdot = w + out.append('.') + out.append('.') + dotdot = out.w } default: // real path element. // add slash if needed - if rooted && w != 1 || !rooted && w != 0 { - buf[w] = '/' - w++ + if rooted && out.w != 1 || !rooted && out.w != 0 { + out.append('/') } // copy element for ; r < n && path[r] != '/'; r++ { - buf[w] = path[r] - w++ + out.append(path[r]) } } } // Turn empty string into "." - if w == 0 { - buf[w] = '.' - w++ + if out.w == 0 { + return "." } - return string(buf[0:w]) + return out.string() } // Split splits path immediately following the final slash. @@ -166,7 +198,8 @@ func IsAbs(path string) bool { } // Dir returns all but the last element of path, typically the path's directory. -// The path is Cleaned and trailing slashes are removed before processing. +// After dropping the final element using Split, the path is Cleaned and trailing +// slashes are removed. // If the path is empty, Dir returns ".". // If the path consists entirely of slashes followed by non-slash bytes, Dir // returns a single slash. In any other case, the returned path does not end in a diff --git a/src/pkg/path/path_test.go b/src/pkg/path/path_test.go index 77f080433..220ec1a0b 100644 --- a/src/pkg/path/path_test.go +++ b/src/pkg/path/path_test.go @@ -67,6 +67,16 @@ func TestClean(t *testing.T) { if s := Clean(test.path); s != test.result { t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) } + if s := Clean(test.result); s != test.result { + t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result) + } + } + + for _, test := range cleantests { + allocs := testing.AllocsPerRun(100, func() { Clean(test.result) }) + if allocs > 0 { + t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs) + } } } @@ -181,6 +191,7 @@ var dirtests = []PathTest{ {"x/", "x"}, {"abc", "."}, {"abc/def", "abc"}, + {"abc////def", "abc"}, {"a/b/.x", "a/b"}, {"a/b/c.", "a/b"}, {"a/b/c.x", "a/b"}, diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index e33140563..6f006db18 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -7,12 +7,15 @@ package reflect_test import ( "bytes" "encoding/base64" + "flag" "fmt" "io" + "math/rand" "os" . "reflect" - "runtime" + "sync" "testing" + "time" "unsafe" ) @@ -1053,26 +1056,393 @@ func TestChan(t *testing.T) { if l, m := cv.Len(), cv.Cap(); l != len(c) || m != cap(c) { t.Errorf("Len/Cap = %d/%d want %d/%d", l, m, len(c), cap(c)) } +} +// caseInfo describes a single case in a select test. +type caseInfo struct { + desc string + canSelect bool + recv Value + closed bool + helper func() + panic bool } +var allselect = flag.Bool("allselect", false, "exhaustive select test") + +func TestSelect(t *testing.T) { + selectWatch.once.Do(func() { go selectWatcher() }) + + var x exhaustive + nch := 0 + newop := func(n int, cap int) (ch, val Value) { + nch++ + if nch%101%2 == 1 { + c := make(chan int, cap) + ch = ValueOf(c) + val = ValueOf(n) + } else { + c := make(chan string, cap) + ch = ValueOf(c) + val = ValueOf(fmt.Sprint(n)) + } + return + } + + for n := 0; x.Next(); n++ { + if testing.Short() && n >= 1000 { + break + } + if n >= 100000 && !*allselect { + break + } + if n%100000 == 0 && testing.Verbose() { + println("TestSelect", n) + } + var cases []SelectCase + var info []caseInfo + + // Ready send. + if x.Maybe() { + ch, val := newop(len(cases), 1) + cases = append(cases, SelectCase{ + Dir: SelectSend, + Chan: ch, + Send: val, + }) + info = append(info, caseInfo{desc: "ready send", canSelect: true}) + } + + // Ready recv. + if x.Maybe() { + ch, val := newop(len(cases), 1) + ch.Send(val) + cases = append(cases, SelectCase{ + Dir: SelectRecv, + Chan: ch, + }) + info = append(info, caseInfo{desc: "ready recv", canSelect: true, recv: val}) + } + + // Blocking send. + if x.Maybe() { + ch, val := newop(len(cases), 0) + cases = append(cases, SelectCase{ + Dir: SelectSend, + Chan: ch, + Send: val, + }) + // Let it execute? + if x.Maybe() { + f := func() { ch.Recv() } + info = append(info, caseInfo{desc: "blocking send", helper: f}) + } else { + info = append(info, caseInfo{desc: "blocking send"}) + } + } + + // Blocking recv. + if x.Maybe() { + ch, val := newop(len(cases), 0) + cases = append(cases, SelectCase{ + Dir: SelectRecv, + Chan: ch, + }) + // Let it execute? + if x.Maybe() { + f := func() { ch.Send(val) } + info = append(info, caseInfo{desc: "blocking recv", recv: val, helper: f}) + } else { + info = append(info, caseInfo{desc: "blocking recv"}) + } + } + + // Zero Chan send. + if x.Maybe() { + // Maybe include value to send. + var val Value + if x.Maybe() { + val = ValueOf(100) + } + cases = append(cases, SelectCase{ + Dir: SelectSend, + Send: val, + }) + info = append(info, caseInfo{desc: "zero Chan send"}) + } + + // Zero Chan receive. + if x.Maybe() { + cases = append(cases, SelectCase{ + Dir: SelectRecv, + }) + info = append(info, caseInfo{desc: "zero Chan recv"}) + } + + // nil Chan send. + if x.Maybe() { + cases = append(cases, SelectCase{ + Dir: SelectSend, + Chan: ValueOf((chan int)(nil)), + Send: ValueOf(101), + }) + info = append(info, caseInfo{desc: "nil Chan send"}) + } + + // nil Chan recv. + if x.Maybe() { + cases = append(cases, SelectCase{ + Dir: SelectRecv, + Chan: ValueOf((chan int)(nil)), + }) + info = append(info, caseInfo{desc: "nil Chan recv"}) + } + + // closed Chan send. + if x.Maybe() { + ch := make(chan int) + close(ch) + cases = append(cases, SelectCase{ + Dir: SelectSend, + Chan: ValueOf(ch), + Send: ValueOf(101), + }) + info = append(info, caseInfo{desc: "closed Chan send", canSelect: true, panic: true}) + } + + // closed Chan recv. + if x.Maybe() { + ch, val := newop(len(cases), 0) + ch.Close() + val = Zero(val.Type()) + cases = append(cases, SelectCase{ + Dir: SelectRecv, + Chan: ch, + }) + info = append(info, caseInfo{desc: "closed Chan recv", canSelect: true, closed: true, recv: val}) + } + + var helper func() // goroutine to help the select complete + + // Add default? Must be last case here, but will permute. + // Add the default if the select would otherwise + // block forever, and maybe add it anyway. + numCanSelect := 0 + canProceed := false + canBlock := true + canPanic := false + helpers := []int{} + for i, c := range info { + if c.canSelect { + canProceed = true + canBlock = false + numCanSelect++ + if c.panic { + canPanic = true + } + } else if c.helper != nil { + canProceed = true + helpers = append(helpers, i) + } + } + if !canProceed || x.Maybe() { + cases = append(cases, SelectCase{ + Dir: SelectDefault, + }) + info = append(info, caseInfo{desc: "default", canSelect: canBlock}) + numCanSelect++ + } else if canBlock { + // Select needs to communicate with another goroutine. + cas := &info[helpers[x.Choose(len(helpers))]] + helper = cas.helper + cas.canSelect = true + numCanSelect++ + } + + // Permute cases and case info. + // Doing too much here makes the exhaustive loop + // too exhausting, so just do two swaps. + for loop := 0; loop < 2; loop++ { + i := x.Choose(len(cases)) + j := x.Choose(len(cases)) + cases[i], cases[j] = cases[j], cases[i] + info[i], info[j] = info[j], info[i] + } + + if helper != nil { + // We wait before kicking off a goroutine to satisfy a blocked select. + // The pause needs to be big enough to let the select block before + // we run the helper, but if we lose that race once in a while it's okay: the + // select will just proceed immediately. Not a big deal. + // For short tests we can grow [sic] the timeout a bit without fear of taking too long + pause := 10 * time.Microsecond + if testing.Short() { + pause = 100 * time.Microsecond + } + time.AfterFunc(pause, helper) + } + + // Run select. + i, recv, recvOK, panicErr := runSelect(cases, info) + if panicErr != nil && !canPanic { + t.Fatalf("%s\npanicked unexpectedly: %v", fmtSelect(info), panicErr) + } + if panicErr == nil && canPanic && numCanSelect == 1 { + t.Fatalf("%s\nselected #%d incorrectly (should panic)", fmtSelect(info), i) + } + if panicErr != nil { + continue + } + + cas := info[i] + if !cas.canSelect { + recvStr := "" + if recv.IsValid() { + recvStr = fmt.Sprintf(", received %v, %v", recv.Interface(), recvOK) + } + t.Fatalf("%s\nselected #%d incorrectly%s", fmtSelect(info), i, recvStr) + continue + } + if cas.panic { + t.Fatalf("%s\nselected #%d incorrectly (case should panic)", fmtSelect(info), i) + continue + } + + if cases[i].Dir == SelectRecv { + if !recv.IsValid() { + t.Fatalf("%s\nselected #%d but got %v, %v, want %v, %v", fmtSelect(info), i, recv, recvOK, cas.recv.Interface(), !cas.closed) + } + if !cas.recv.IsValid() { + t.Fatalf("%s\nselected #%d but internal error: missing recv value", fmtSelect(info), i) + } + if recv.Interface() != cas.recv.Interface() || recvOK != !cas.closed { + if recv.Interface() == cas.recv.Interface() && recvOK == !cas.closed { + t.Fatalf("%s\nselected #%d, got %#v, %v, and DeepEqual is broken on %T", fmtSelect(info), i, recv.Interface(), recvOK, recv.Interface()) + } + t.Fatalf("%s\nselected #%d but got %#v, %v, want %#v, %v", fmtSelect(info), i, recv.Interface(), recvOK, cas.recv.Interface(), !cas.closed) + } + } else { + if recv.IsValid() || recvOK { + t.Fatalf("%s\nselected #%d but got %v, %v, want %v, %v", fmtSelect(info), i, recv, recvOK, Value{}, false) + } + } + } +} + +// selectWatch and the selectWatcher are a watchdog mechanism for running Select. +// If the selectWatcher notices that the select has been blocked for >1 second, it prints +// an error describing the select and panics the entire test binary. +var selectWatch struct { + sync.Mutex + once sync.Once + now time.Time + info []caseInfo +} + +func selectWatcher() { + for { + time.Sleep(1 * time.Second) + selectWatch.Lock() + if selectWatch.info != nil && time.Since(selectWatch.now) > 1*time.Second { + fmt.Fprintf(os.Stderr, "TestSelect:\n%s blocked indefinitely\n", fmtSelect(selectWatch.info)) + panic("select stuck") + } + selectWatch.Unlock() + } +} + +// runSelect runs a single select test. +// It returns the values returned by Select but also returns +// a panic value if the Select panics. +func runSelect(cases []SelectCase, info []caseInfo) (chosen int, recv Value, recvOK bool, panicErr interface{}) { + defer func() { + panicErr = recover() + + selectWatch.Lock() + selectWatch.info = nil + selectWatch.Unlock() + }() + + selectWatch.Lock() + selectWatch.now = time.Now() + selectWatch.info = info + selectWatch.Unlock() + + chosen, recv, recvOK = Select(cases) + return +} + +// fmtSelect formats the information about a single select test. +func fmtSelect(info []caseInfo) string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "\nselect {\n") + for i, cas := range info { + fmt.Fprintf(&buf, "%d: %s", i, cas.desc) + if cas.recv.IsValid() { + fmt.Fprintf(&buf, " val=%#v", cas.recv.Interface()) + } + if cas.canSelect { + fmt.Fprintf(&buf, " canselect") + } + if cas.panic { + fmt.Fprintf(&buf, " panic") + } + fmt.Fprintf(&buf, "\n") + } + fmt.Fprintf(&buf, "}") + return buf.String() +} + +type two [2]uintptr + // Difficult test for function call because of // implicit padding between arguments. -func dummy(b byte, c int, d byte) (i byte, j int, k byte) { - return b, c, d +func dummy(b byte, c int, d byte, e two, f byte, g float32, h byte) (i byte, j int, k byte, l two, m byte, n float32, o byte) { + return b, c, d, e, f, g, h } func TestFunc(t *testing.T) { - ret := ValueOf(dummy).Call([]Value{ValueOf(byte(10)), ValueOf(20), ValueOf(byte(30))}) - if len(ret) != 3 { - t.Fatalf("Call returned %d values, want 3", len(ret)) + ret := ValueOf(dummy).Call([]Value{ + ValueOf(byte(10)), + ValueOf(20), + ValueOf(byte(30)), + ValueOf(two{40, 50}), + ValueOf(byte(60)), + ValueOf(float32(70)), + ValueOf(byte(80)), + }) + if len(ret) != 7 { + t.Fatalf("Call returned %d values, want 7", len(ret)) } i := byte(ret[0].Uint()) j := int(ret[1].Int()) k := byte(ret[2].Uint()) - if i != 10 || j != 20 || k != 30 { - t.Errorf("Call returned %d, %d, %d; want 10, 20, 30", i, j, k) + l := ret[3].Interface().(two) + m := byte(ret[4].Uint()) + n := float32(ret[5].Float()) + o := byte(ret[6].Uint()) + + if i != 10 || j != 20 || k != 30 || l != (two{40, 50}) || m != 60 || n != 70 || o != 80 { + t.Errorf("Call returned %d, %d, %d, %v, %d, %g, %d; want 10, 20, 30, [40, 50], 60, 70, 80", i, j, k, l, m, n, o) + } +} + +func TestMakeFunc(t *testing.T) { + f := dummy + fv := MakeFunc(TypeOf(f), func(in []Value) []Value { return in }) + ValueOf(&f).Elem().Set(fv) + + // Call g with small arguments so that there is + // something predictable (and different from the + // correct results) in those positions on the stack. + g := dummy + g(1, 2, 3, two{4, 5}, 6, 7, 8) + + // Call constructed function f. + i, j, k, l, m, n, o := f(10, 20, 30, two{40, 50}, 60, 70, 80) + if i != 10 || j != 20 || k != 30 || l != (two{40, 50}) || m != 60 || n != 70 || o != 80 { + t.Errorf("Call returned %d, %d, %d, %v, %d, %g, %d; want 10, 20, 30, [40, 50], 60, 70, 80", i, j, k, l, m, n, o) } } @@ -1123,7 +1493,7 @@ func TestMethod(t *testing.T) { } // Curried method of value. - tfunc := TypeOf(func(int) int(nil)) + tfunc := TypeOf((func(int) int)(nil)) v := ValueOf(p).Method(1) if tt := v.Type(); tt != tfunc { t.Errorf("Value Method Type is %s; want %s", tt, tfunc) @@ -1225,7 +1595,7 @@ func TestAnonymousFields(t *testing.T) { var t1 T1 type1 := TypeOf(t1) if field, ok = type1.FieldByName("int"); !ok { - t.Error("no field 'int'") + t.Fatal("no field 'int'") } if field.Index[0] != 1 { t.Error("field index should be 1; is", field.Index) @@ -1282,6 +1652,61 @@ type S4 struct { A int } +// The X in S6 and S7 annihilate, but they also block the X in S8.S9. +type S5 struct { + S6 + S7 + S8 +} + +type S6 struct { + X int +} + +type S7 S6 + +type S8 struct { + S9 +} + +type S9 struct { + X int + Y int +} + +// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. +type S10 struct { + S11 + S12 + S13 +} + +type S11 struct { + S6 +} + +type S12 struct { + S6 +} + +type S13 struct { + S8 +} + +// The X in S15.S11.S1 and S16.S11.S1 annihilate. +type S14 struct { + S15 + S16 +} + +type S15 struct { + S11 +} + +type S16 struct { + S11 +} + var fieldTests = []FTest{ {struct{}{}, "", nil, 0}, {struct{}{}, "Foo", nil, 0}, @@ -1303,6 +1728,11 @@ var fieldTests = []FTest{ {S3{E: 'e'}, "E", []int{3}, 'e'}, {S4{A: 'a'}, "A", []int{1}, 'a'}, {S4{}, "B", nil, 0}, + {S5{}, "X", nil, 0}, + {S5{}, "Y", []int{2, 0, 1}, 0}, + {S10{}, "X", nil, 0}, + {S10{}, "Y", []int{2, 0, 0, 1}, 0}, + {S14{}, "X", nil, 0}, } func TestFieldByIndex(t *testing.T) { @@ -1346,7 +1776,7 @@ func TestFieldByName(t *testing.T) { if test.index != nil { // Verify field depth and index. if len(f.Index) != len(test.index) { - t.Errorf("%s.%s depth %d; want %d", s.Name(), test.name, len(f.Index), len(test.index)) + t.Errorf("%s.%s depth %d; want %d: %v vs %v", s.Name(), test.name, len(f.Index), len(test.index), f.Index, test.index) } else { for i, x := range f.Index { if x != test.index[i] { @@ -1384,7 +1814,30 @@ func TestImportPath(t *testing.T) { path string }{ {TypeOf(&base64.Encoding{}).Elem(), "encoding/base64"}, + {TypeOf(int(0)), ""}, + {TypeOf(int8(0)), ""}, + {TypeOf(int16(0)), ""}, + {TypeOf(int32(0)), ""}, + {TypeOf(int64(0)), ""}, {TypeOf(uint(0)), ""}, + {TypeOf(uint8(0)), ""}, + {TypeOf(uint16(0)), ""}, + {TypeOf(uint32(0)), ""}, + {TypeOf(uint64(0)), ""}, + {TypeOf(uintptr(0)), ""}, + {TypeOf(float32(0)), ""}, + {TypeOf(float64(0)), ""}, + {TypeOf(complex64(0)), ""}, + {TypeOf(complex128(0)), ""}, + {TypeOf(byte(0)), ""}, + {TypeOf(rune(0)), ""}, + {TypeOf([]byte(nil)), ""}, + {TypeOf([]rune(nil)), ""}, + {TypeOf(string("")), ""}, + {TypeOf((*interface{})(nil)).Elem(), ""}, + {TypeOf((*byte)(nil)), ""}, + {TypeOf((*rune)(nil)), ""}, + {TypeOf((*int64)(nil)), ""}, {TypeOf(map[string]int{}), ""}, {TypeOf((*error)(nil)).Elem(), ""}, } @@ -1558,21 +2011,13 @@ func TestAddr(t *testing.T) { } func noAlloc(t *testing.T, n int, f func(int)) { - // once to prime everything - f(-1) - memstats := new(runtime.MemStats) - runtime.ReadMemStats(memstats) - oldmallocs := memstats.Mallocs - - for j := 0; j < n; j++ { - f(j) - } - // A few allocs may happen in the testing package when GOMAXPROCS > 1, so don't - // require zero mallocs. - runtime.ReadMemStats(memstats) - mallocs := memstats.Mallocs - oldmallocs - if mallocs > 5 { - t.Fatalf("%d mallocs after %d iterations", mallocs, n) + i := -1 + allocs := testing.AllocsPerRun(n, func() { + f(i) + i++ + }) + if allocs > 0 { + t.Errorf("%d iterations: got %v mallocs, want 0", n, allocs) } } @@ -1596,6 +2041,24 @@ func TestSmallNegativeInt(t *testing.T) { } } +func TestIndex(t *testing.T) { + xs := []byte{1, 2, 3, 4, 5, 6, 7, 8} + v := ValueOf(xs).Index(3).Interface().(byte) + if v != xs[3] { + t.Errorf("xs.Index(3) = %v; expected %v", v, xs[3]) + } + xa := [8]byte{10, 20, 30, 40, 50, 60, 70, 80} + v = ValueOf(xa).Index(2).Interface().(byte) + if v != xa[2] { + t.Errorf("xa.Index(2) = %v; expected %v", v, xa[2]) + } + s := "0123456789" + v = ValueOf(s).Index(3).Interface().(byte) + if v != s[3] { + t.Errorf("s.Index(3) = %v; expected %v", v, s[3]) + } +} + func TestSlice(t *testing.T) { xs := []int{1, 2, 3, 4, 5, 6, 7, 8} v := ValueOf(xs).Slice(3, 5).Interface().([]int) @@ -1608,7 +2071,6 @@ func TestSlice(t *testing.T) { if !DeepEqual(v[0:5], xs[3:]) { t.Errorf("xs.Slice(3, 5)[0:5] = %v", v[0:5]) } - xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80} v = ValueOf(&xa).Elem().Slice(2, 5).Interface().([]int) if len(v) != 3 { @@ -1620,6 +2082,11 @@ func TestSlice(t *testing.T) { if !DeepEqual(v[0:6], xa[2:]) { t.Errorf("xs.Slice(2, 5)[0:6] = %v", v[0:6]) } + s := "0123456789" + vs := ValueOf(s).Slice(3, 5).Interface().(string) + if vs != s[3:5] { + t.Errorf("s.Slice(3, 5) = %q; expected %q", vs, s[3:5]) + } } func TestVariadic(t *testing.T) { @@ -1760,3 +2227,761 @@ func TestAlias(t *testing.T) { t.Errorf("aliasing: old=%q new=%q, want hello, world", oldvalue, newvalue) } } + +var V = ValueOf + +func EmptyInterfaceV(x interface{}) Value { + return ValueOf(&x).Elem() +} + +func ReaderV(x io.Reader) Value { + return ValueOf(&x).Elem() +} + +func ReadWriterV(x io.ReadWriter) Value { + return ValueOf(&x).Elem() +} + +type Empty struct{} +type MyString string +type MyBytes []byte +type MyRunes []int32 +type MyFunc func() +type MyByte byte + +var convertTests = []struct { + in Value + out Value +}{ + // numbers + /* + Edit .+1,/\*\//-1>cat >/tmp/x.go && go run /tmp/x.go + + package main + + import "fmt" + + var numbers = []string{ + "int8", "uint8", "int16", "uint16", + "int32", "uint32", "int64", "uint64", + "int", "uint", "uintptr", + "float32", "float64", + } + + func main() { + // all pairs but in an unusual order, + // to emit all the int8, uint8 cases + // before n grows too big. + n := 1 + for i, f := range numbers { + for _, g := range numbers[i:] { + fmt.Printf("\t{V(%s(%d)), V(%s(%d))},\n", f, n, g, n) + n++ + if f != g { + fmt.Printf("\t{V(%s(%d)), V(%s(%d))},\n", g, n, f, n) + n++ + } + } + } + } + */ + {V(int8(1)), V(int8(1))}, + {V(int8(2)), V(uint8(2))}, + {V(uint8(3)), V(int8(3))}, + {V(int8(4)), V(int16(4))}, + {V(int16(5)), V(int8(5))}, + {V(int8(6)), V(uint16(6))}, + {V(uint16(7)), V(int8(7))}, + {V(int8(8)), V(int32(8))}, + {V(int32(9)), V(int8(9))}, + {V(int8(10)), V(uint32(10))}, + {V(uint32(11)), V(int8(11))}, + {V(int8(12)), V(int64(12))}, + {V(int64(13)), V(int8(13))}, + {V(int8(14)), V(uint64(14))}, + {V(uint64(15)), V(int8(15))}, + {V(int8(16)), V(int(16))}, + {V(int(17)), V(int8(17))}, + {V(int8(18)), V(uint(18))}, + {V(uint(19)), V(int8(19))}, + {V(int8(20)), V(uintptr(20))}, + {V(uintptr(21)), V(int8(21))}, + {V(int8(22)), V(float32(22))}, + {V(float32(23)), V(int8(23))}, + {V(int8(24)), V(float64(24))}, + {V(float64(25)), V(int8(25))}, + {V(uint8(26)), V(uint8(26))}, + {V(uint8(27)), V(int16(27))}, + {V(int16(28)), V(uint8(28))}, + {V(uint8(29)), V(uint16(29))}, + {V(uint16(30)), V(uint8(30))}, + {V(uint8(31)), V(int32(31))}, + {V(int32(32)), V(uint8(32))}, + {V(uint8(33)), V(uint32(33))}, + {V(uint32(34)), V(uint8(34))}, + {V(uint8(35)), V(int64(35))}, + {V(int64(36)), V(uint8(36))}, + {V(uint8(37)), V(uint64(37))}, + {V(uint64(38)), V(uint8(38))}, + {V(uint8(39)), V(int(39))}, + {V(int(40)), V(uint8(40))}, + {V(uint8(41)), V(uint(41))}, + {V(uint(42)), V(uint8(42))}, + {V(uint8(43)), V(uintptr(43))}, + {V(uintptr(44)), V(uint8(44))}, + {V(uint8(45)), V(float32(45))}, + {V(float32(46)), V(uint8(46))}, + {V(uint8(47)), V(float64(47))}, + {V(float64(48)), V(uint8(48))}, + {V(int16(49)), V(int16(49))}, + {V(int16(50)), V(uint16(50))}, + {V(uint16(51)), V(int16(51))}, + {V(int16(52)), V(int32(52))}, + {V(int32(53)), V(int16(53))}, + {V(int16(54)), V(uint32(54))}, + {V(uint32(55)), V(int16(55))}, + {V(int16(56)), V(int64(56))}, + {V(int64(57)), V(int16(57))}, + {V(int16(58)), V(uint64(58))}, + {V(uint64(59)), V(int16(59))}, + {V(int16(60)), V(int(60))}, + {V(int(61)), V(int16(61))}, + {V(int16(62)), V(uint(62))}, + {V(uint(63)), V(int16(63))}, + {V(int16(64)), V(uintptr(64))}, + {V(uintptr(65)), V(int16(65))}, + {V(int16(66)), V(float32(66))}, + {V(float32(67)), V(int16(67))}, + {V(int16(68)), V(float64(68))}, + {V(float64(69)), V(int16(69))}, + {V(uint16(70)), V(uint16(70))}, + {V(uint16(71)), V(int32(71))}, + {V(int32(72)), V(uint16(72))}, + {V(uint16(73)), V(uint32(73))}, + {V(uint32(74)), V(uint16(74))}, + {V(uint16(75)), V(int64(75))}, + {V(int64(76)), V(uint16(76))}, + {V(uint16(77)), V(uint64(77))}, + {V(uint64(78)), V(uint16(78))}, + {V(uint16(79)), V(int(79))}, + {V(int(80)), V(uint16(80))}, + {V(uint16(81)), V(uint(81))}, + {V(uint(82)), V(uint16(82))}, + {V(uint16(83)), V(uintptr(83))}, + {V(uintptr(84)), V(uint16(84))}, + {V(uint16(85)), V(float32(85))}, + {V(float32(86)), V(uint16(86))}, + {V(uint16(87)), V(float64(87))}, + {V(float64(88)), V(uint16(88))}, + {V(int32(89)), V(int32(89))}, + {V(int32(90)), V(uint32(90))}, + {V(uint32(91)), V(int32(91))}, + {V(int32(92)), V(int64(92))}, + {V(int64(93)), V(int32(93))}, + {V(int32(94)), V(uint64(94))}, + {V(uint64(95)), V(int32(95))}, + {V(int32(96)), V(int(96))}, + {V(int(97)), V(int32(97))}, + {V(int32(98)), V(uint(98))}, + {V(uint(99)), V(int32(99))}, + {V(int32(100)), V(uintptr(100))}, + {V(uintptr(101)), V(int32(101))}, + {V(int32(102)), V(float32(102))}, + {V(float32(103)), V(int32(103))}, + {V(int32(104)), V(float64(104))}, + {V(float64(105)), V(int32(105))}, + {V(uint32(106)), V(uint32(106))}, + {V(uint32(107)), V(int64(107))}, + {V(int64(108)), V(uint32(108))}, + {V(uint32(109)), V(uint64(109))}, + {V(uint64(110)), V(uint32(110))}, + {V(uint32(111)), V(int(111))}, + {V(int(112)), V(uint32(112))}, + {V(uint32(113)), V(uint(113))}, + {V(uint(114)), V(uint32(114))}, + {V(uint32(115)), V(uintptr(115))}, + {V(uintptr(116)), V(uint32(116))}, + {V(uint32(117)), V(float32(117))}, + {V(float32(118)), V(uint32(118))}, + {V(uint32(119)), V(float64(119))}, + {V(float64(120)), V(uint32(120))}, + {V(int64(121)), V(int64(121))}, + {V(int64(122)), V(uint64(122))}, + {V(uint64(123)), V(int64(123))}, + {V(int64(124)), V(int(124))}, + {V(int(125)), V(int64(125))}, + {V(int64(126)), V(uint(126))}, + {V(uint(127)), V(int64(127))}, + {V(int64(128)), V(uintptr(128))}, + {V(uintptr(129)), V(int64(129))}, + {V(int64(130)), V(float32(130))}, + {V(float32(131)), V(int64(131))}, + {V(int64(132)), V(float64(132))}, + {V(float64(133)), V(int64(133))}, + {V(uint64(134)), V(uint64(134))}, + {V(uint64(135)), V(int(135))}, + {V(int(136)), V(uint64(136))}, + {V(uint64(137)), V(uint(137))}, + {V(uint(138)), V(uint64(138))}, + {V(uint64(139)), V(uintptr(139))}, + {V(uintptr(140)), V(uint64(140))}, + {V(uint64(141)), V(float32(141))}, + {V(float32(142)), V(uint64(142))}, + {V(uint64(143)), V(float64(143))}, + {V(float64(144)), V(uint64(144))}, + {V(int(145)), V(int(145))}, + {V(int(146)), V(uint(146))}, + {V(uint(147)), V(int(147))}, + {V(int(148)), V(uintptr(148))}, + {V(uintptr(149)), V(int(149))}, + {V(int(150)), V(float32(150))}, + {V(float32(151)), V(int(151))}, + {V(int(152)), V(float64(152))}, + {V(float64(153)), V(int(153))}, + {V(uint(154)), V(uint(154))}, + {V(uint(155)), V(uintptr(155))}, + {V(uintptr(156)), V(uint(156))}, + {V(uint(157)), V(float32(157))}, + {V(float32(158)), V(uint(158))}, + {V(uint(159)), V(float64(159))}, + {V(float64(160)), V(uint(160))}, + {V(uintptr(161)), V(uintptr(161))}, + {V(uintptr(162)), V(float32(162))}, + {V(float32(163)), V(uintptr(163))}, + {V(uintptr(164)), V(float64(164))}, + {V(float64(165)), V(uintptr(165))}, + {V(float32(166)), V(float32(166))}, + {V(float32(167)), V(float64(167))}, + {V(float64(168)), V(float32(168))}, + {V(float64(169)), V(float64(169))}, + + // truncation + {V(float64(1.5)), V(int(1))}, + + // complex + {V(complex64(1i)), V(complex64(1i))}, + {V(complex64(2i)), V(complex128(2i))}, + {V(complex128(3i)), V(complex64(3i))}, + {V(complex128(4i)), V(complex128(4i))}, + + // string + {V(string("hello")), V(string("hello"))}, + {V(string("bytes1")), V([]byte("bytes1"))}, + {V([]byte("bytes2")), V(string("bytes2"))}, + {V([]byte("bytes3")), V([]byte("bytes3"))}, + {V(string("runes♝")), V([]rune("runes♝"))}, + {V([]rune("runes♕")), V(string("runes♕"))}, + {V([]rune("runes🙈🙉🙊")), V([]rune("runes🙈🙉🙊"))}, + {V(int('a')), V(string("a"))}, + {V(int8('a')), V(string("a"))}, + {V(int16('a')), V(string("a"))}, + {V(int32('a')), V(string("a"))}, + {V(int64('a')), V(string("a"))}, + {V(uint('a')), V(string("a"))}, + {V(uint8('a')), V(string("a"))}, + {V(uint16('a')), V(string("a"))}, + {V(uint32('a')), V(string("a"))}, + {V(uint64('a')), V(string("a"))}, + {V(uintptr('a')), V(string("a"))}, + {V(int(-1)), V(string("\uFFFD"))}, + {V(int8(-2)), V(string("\uFFFD"))}, + {V(int16(-3)), V(string("\uFFFD"))}, + {V(int32(-4)), V(string("\uFFFD"))}, + {V(int64(-5)), V(string("\uFFFD"))}, + {V(uint(0x110001)), V(string("\uFFFD"))}, + {V(uint32(0x110002)), V(string("\uFFFD"))}, + {V(uint64(0x110003)), V(string("\uFFFD"))}, + {V(uintptr(0x110004)), V(string("\uFFFD"))}, + + // named string + {V(MyString("hello")), V(string("hello"))}, + {V(string("hello")), V(MyString("hello"))}, + {V(string("hello")), V(string("hello"))}, + {V(MyString("hello")), V(MyString("hello"))}, + {V(MyString("bytes1")), V([]byte("bytes1"))}, + {V([]byte("bytes2")), V(MyString("bytes2"))}, + {V([]byte("bytes3")), V([]byte("bytes3"))}, + {V(MyString("runes♝")), V([]rune("runes♝"))}, + {V([]rune("runes♕")), V(MyString("runes♕"))}, + {V([]rune("runes🙈🙉🙊")), V([]rune("runes🙈🙉🙊"))}, + {V([]rune("runes🙈🙉🙊")), V(MyRunes("runes🙈🙉🙊"))}, + {V(MyRunes("runes🙈🙉🙊")), V([]rune("runes🙈🙉🙊"))}, + {V(int('a')), V(MyString("a"))}, + {V(int8('a')), V(MyString("a"))}, + {V(int16('a')), V(MyString("a"))}, + {V(int32('a')), V(MyString("a"))}, + {V(int64('a')), V(MyString("a"))}, + {V(uint('a')), V(MyString("a"))}, + {V(uint8('a')), V(MyString("a"))}, + {V(uint16('a')), V(MyString("a"))}, + {V(uint32('a')), V(MyString("a"))}, + {V(uint64('a')), V(MyString("a"))}, + {V(uintptr('a')), V(MyString("a"))}, + {V(int(-1)), V(MyString("\uFFFD"))}, + {V(int8(-2)), V(MyString("\uFFFD"))}, + {V(int16(-3)), V(MyString("\uFFFD"))}, + {V(int32(-4)), V(MyString("\uFFFD"))}, + {V(int64(-5)), V(MyString("\uFFFD"))}, + {V(uint(0x110001)), V(MyString("\uFFFD"))}, + {V(uint32(0x110002)), V(MyString("\uFFFD"))}, + {V(uint64(0x110003)), V(MyString("\uFFFD"))}, + {V(uintptr(0x110004)), V(MyString("\uFFFD"))}, + + // named []byte + {V(string("bytes1")), V(MyBytes("bytes1"))}, + {V(MyBytes("bytes2")), V(string("bytes2"))}, + {V(MyBytes("bytes3")), V(MyBytes("bytes3"))}, + {V(MyString("bytes1")), V(MyBytes("bytes1"))}, + {V(MyBytes("bytes2")), V(MyString("bytes2"))}, + + // named []rune + {V(string("runes♝")), V(MyRunes("runes♝"))}, + {V(MyRunes("runes♕")), V(string("runes♕"))}, + {V(MyRunes("runes🙈🙉🙊")), V(MyRunes("runes🙈🙉🙊"))}, + {V(MyString("runes♝")), V(MyRunes("runes♝"))}, + {V(MyRunes("runes♕")), V(MyString("runes♕"))}, + + // named types and equal underlying types + {V(new(int)), V(new(integer))}, + {V(new(integer)), V(new(int))}, + {V(Empty{}), V(struct{}{})}, + {V(new(Empty)), V(new(struct{}))}, + {V(struct{}{}), V(Empty{})}, + {V(new(struct{})), V(new(Empty))}, + {V(Empty{}), V(Empty{})}, + {V(MyBytes{}), V([]byte{})}, + {V([]byte{}), V(MyBytes{})}, + {V((func())(nil)), V(MyFunc(nil))}, + {V((MyFunc)(nil)), V((func())(nil))}, + + // can convert *byte and *MyByte + {V((*byte)(nil)), V((*MyByte)(nil))}, + {V((*MyByte)(nil)), V((*byte)(nil))}, + + // cannot convert mismatched array sizes + {V([2]byte{}), V([2]byte{})}, + {V([3]byte{}), V([3]byte{})}, + + // cannot convert other instances + {V((**byte)(nil)), V((**byte)(nil))}, + {V((**MyByte)(nil)), V((**MyByte)(nil))}, + {V((chan byte)(nil)), V((chan byte)(nil))}, + {V((chan MyByte)(nil)), V((chan MyByte)(nil))}, + {V(([]byte)(nil)), V(([]byte)(nil))}, + {V(([]MyByte)(nil)), V(([]MyByte)(nil))}, + {V((map[int]byte)(nil)), V((map[int]byte)(nil))}, + {V((map[int]MyByte)(nil)), V((map[int]MyByte)(nil))}, + {V((map[byte]int)(nil)), V((map[byte]int)(nil))}, + {V((map[MyByte]int)(nil)), V((map[MyByte]int)(nil))}, + {V([2]byte{}), V([2]byte{})}, + {V([2]MyByte{}), V([2]MyByte{})}, + + // other + {V((***int)(nil)), V((***int)(nil))}, + {V((***byte)(nil)), V((***byte)(nil))}, + {V((***int32)(nil)), V((***int32)(nil))}, + {V((***int64)(nil)), V((***int64)(nil))}, + {V((chan int)(nil)), V((<-chan int)(nil))}, + {V((chan int)(nil)), V((chan<- int)(nil))}, + {V((chan string)(nil)), V((<-chan string)(nil))}, + {V((chan string)(nil)), V((chan<- string)(nil))}, + {V((chan byte)(nil)), V((chan byte)(nil))}, + {V((chan MyByte)(nil)), V((chan MyByte)(nil))}, + {V((map[int]bool)(nil)), V((map[int]bool)(nil))}, + {V((map[int]byte)(nil)), V((map[int]byte)(nil))}, + {V((map[uint]bool)(nil)), V((map[uint]bool)(nil))}, + {V([]uint(nil)), V([]uint(nil))}, + {V([]int(nil)), V([]int(nil))}, + {V(new(interface{})), V(new(interface{}))}, + {V(new(io.Reader)), V(new(io.Reader))}, + {V(new(io.Writer)), V(new(io.Writer))}, + + // interfaces + {V(int(1)), EmptyInterfaceV(int(1))}, + {V(string("hello")), EmptyInterfaceV(string("hello"))}, + {V(new(bytes.Buffer)), ReaderV(new(bytes.Buffer))}, + {ReadWriterV(new(bytes.Buffer)), ReaderV(new(bytes.Buffer))}, + {V(new(bytes.Buffer)), ReadWriterV(new(bytes.Buffer))}, +} + +func TestConvert(t *testing.T) { + canConvert := map[[2]Type]bool{} + all := map[Type]bool{} + + for _, tt := range convertTests { + t1 := tt.in.Type() + if !t1.ConvertibleTo(t1) { + t.Errorf("(%s).ConvertibleTo(%s) = false, want true", t1, t1) + continue + } + + t2 := tt.out.Type() + if !t1.ConvertibleTo(t2) { + t.Errorf("(%s).ConvertibleTo(%s) = false, want true", t1, t2) + continue + } + + all[t1] = true + all[t2] = true + canConvert[[2]Type{t1, t2}] = true + + v1 := tt.in + vout1 := v1.Convert(t1) + out1 := vout1.Interface() + if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) { + t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t1, out1, out1, tt.in.Interface(), tt.in.Interface()) + } + + vout := v1.Convert(t2) + out := vout.Interface() + if vout.Type() != tt.out.Type() || !DeepEqual(out, tt.out.Interface()) { + t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t2, out, out, tt.out.Interface(), tt.out.Interface()) + } + + if IsRO(v1) { + t.Errorf("table entry %v is RO, should not be", v1) + } + if IsRO(vout1) { + t.Errorf("self-conversion output %v is RO, should not be", vout1) + } + if IsRO(vout) { + t.Errorf("conversion output %v is RO, should not be", vout) + } + if !IsRO(MakeRO(v1).Convert(t1)) { + t.Errorf("RO self-conversion output %v is not RO, should be", v1) + } + if !IsRO(MakeRO(v1).Convert(t2)) { + t.Errorf("RO conversion output %v is not RO, should be", v1) + } + } + + // Assume that of all the types we saw during the tests, + // if there wasn't an explicit entry for a conversion between + // a pair of types, then it's not to be allowed. This checks for + // things like 'int64' converting to '*int'. + for t1 := range all { + for t2 := range all { + expectOK := t1 == t2 || canConvert[[2]Type{t1, t2}] || t2.Kind() == Interface && t2.NumMethod() == 0 + if ok := t1.ConvertibleTo(t2); ok != expectOK { + t.Errorf("(%s).ConvertibleTo(%s) = %v, want %v", t1, t2, ok, expectOK) + } + } + } +} + +func TestOverflow(t *testing.T) { + if ovf := V(float64(0)).OverflowFloat(1e300); ovf { + t.Errorf("%v wrongly overflows float64", 1e300) + } + + maxFloat32 := float64((1<<24 - 1) << (127 - 23)) + if ovf := V(float32(0)).OverflowFloat(maxFloat32); ovf { + t.Errorf("%v wrongly overflows float32", maxFloat32) + } + ovfFloat32 := float64((1<<24-1)<<(127-23) + 1<<(127-52)) + if ovf := V(float32(0)).OverflowFloat(ovfFloat32); !ovf { + t.Errorf("%v should overflow float32", ovfFloat32) + } + if ovf := V(float32(0)).OverflowFloat(-ovfFloat32); !ovf { + t.Errorf("%v should overflow float32", -ovfFloat32) + } + + maxInt32 := int64(0x7fffffff) + if ovf := V(int32(0)).OverflowInt(maxInt32); ovf { + t.Errorf("%v wrongly overflows int32", maxInt32) + } + if ovf := V(int32(0)).OverflowInt(-1 << 31); ovf { + t.Errorf("%v wrongly overflows int32", -int64(1)<<31) + } + ovfInt32 := int64(1 << 31) + if ovf := V(int32(0)).OverflowInt(ovfInt32); !ovf { + t.Errorf("%v should overflow int32", ovfInt32) + } + + maxUint32 := uint64(0xffffffff) + if ovf := V(uint32(0)).OverflowUint(maxUint32); ovf { + t.Errorf("%v wrongly overflows uint32", maxUint32) + } + ovfUint32 := uint64(1 << 32) + if ovf := V(uint32(0)).OverflowUint(ovfUint32); !ovf { + t.Errorf("%v should overflow uint32", ovfUint32) + } +} + +func checkSameType(t *testing.T, x, y interface{}) { + if TypeOf(x) != TypeOf(y) { + t.Errorf("did not find preexisting type for %s (vs %s)", TypeOf(x), TypeOf(y)) + } +} + +func TestArrayOf(t *testing.T) { + // check construction and use of type not in binary + type T int + at := ArrayOf(10, TypeOf(T(1))) + v := New(at).Elem() + for i := 0; i < v.Len(); i++ { + v.Index(i).Set(ValueOf(T(i))) + } + s := fmt.Sprint(v.Interface()) + want := "[0 1 2 3 4 5 6 7 8 9]" + if s != want { + t.Errorf("constructed array = %s, want %s", s, want) + } + + // check that type already in binary is found + checkSameType(t, Zero(ArrayOf(5, TypeOf(T(1)))).Interface(), [5]T{}) +} + +func TestSliceOf(t *testing.T) { + // check construction and use of type not in binary + type T int + st := SliceOf(TypeOf(T(1))) + v := MakeSlice(st, 10, 10) + for i := 0; i < v.Len(); i++ { + v.Index(i).Set(ValueOf(T(i))) + } + s := fmt.Sprint(v.Interface()) + want := "[0 1 2 3 4 5 6 7 8 9]" + if s != want { + t.Errorf("constructed slice = %s, want %s", s, want) + } + + // check that type already in binary is found + type T1 int + checkSameType(t, Zero(SliceOf(TypeOf(T1(1)))).Interface(), []T1{}) +} + +func TestChanOf(t *testing.T) { + // check construction and use of type not in binary + type T string + ct := ChanOf(BothDir, TypeOf(T(""))) + v := MakeChan(ct, 2) + v.Send(ValueOf(T("hello"))) + v.Send(ValueOf(T("world"))) + + sv1, _ := v.Recv() + sv2, _ := v.Recv() + s1 := sv1.String() + s2 := sv2.String() + if s1 != "hello" || s2 != "world" { + t.Errorf("constructed chan: have %q, %q, want %q, %q", s1, s2, "hello", "world") + } + + // check that type already in binary is found + type T1 int + checkSameType(t, Zero(ChanOf(BothDir, TypeOf(T1(1)))).Interface(), (chan T1)(nil)) +} + +func TestMapOf(t *testing.T) { + // check construction and use of type not in binary + type K string + type V float64 + + v := MakeMap(MapOf(TypeOf(K("")), TypeOf(V(0)))) + v.SetMapIndex(ValueOf(K("a")), ValueOf(V(1))) + + s := fmt.Sprint(v.Interface()) + want := "map[a:1]" + if s != want { + t.Errorf("constructed map = %s, want %s", s, want) + } + + // check that type already in binary is found + checkSameType(t, Zero(MapOf(TypeOf(V(0)), TypeOf(K("")))).Interface(), map[V]K(nil)) +} + +type B1 struct { + X int + Y int + Z int +} + +func BenchmarkFieldByName1(b *testing.B) { + t := TypeOf(B1{}) + for i := 0; i < b.N; i++ { + t.FieldByName("Z") + } +} + +func BenchmarkFieldByName2(b *testing.B) { + t := TypeOf(S3{}) + for i := 0; i < b.N; i++ { + t.FieldByName("B") + } +} + +type R0 struct { + *R1 + *R2 + *R3 + *R4 +} + +type R1 struct { + *R5 + *R6 + *R7 + *R8 +} + +type R2 R1 +type R3 R1 +type R4 R1 + +type R5 struct { + *R9 + *R10 + *R11 + *R12 +} + +type R6 R5 +type R7 R5 +type R8 R5 + +type R9 struct { + *R13 + *R14 + *R15 + *R16 +} + +type R10 R9 +type R11 R9 +type R12 R9 + +type R13 struct { + *R17 + *R18 + *R19 + *R20 +} + +type R14 R13 +type R15 R13 +type R16 R13 + +type R17 struct { + *R21 + *R22 + *R23 + *R24 +} + +type R18 R17 +type R19 R17 +type R20 R17 + +type R21 struct { + X int +} + +type R22 R21 +type R23 R21 +type R24 R21 + +func TestEmbed(t *testing.T) { + typ := TypeOf(R0{}) + f, ok := typ.FieldByName("X") + if ok { + t.Fatalf(`FieldByName("X") should fail, returned %v`, f.Index) + } +} + +func BenchmarkFieldByName3(b *testing.B) { + t := TypeOf(R0{}) + for i := 0; i < b.N; i++ { + t.FieldByName("X") + } +} + +// An exhaustive is a mechanism for writing exhaustive or stochastic tests. +// The basic usage is: +// +// for x.Next() { +// ... code using x.Maybe() or x.Choice(n) to create test cases ... +// } +// +// Each iteration of the loop returns a different set of results, until all +// possible result sets have been explored. It is okay for different code paths +// to make different method call sequences on x, but there must be no +// other source of non-determinism in the call sequences. +// +// When faced with a new decision, x chooses randomly. Future explorations +// of that path will choose successive values for the result. Thus, stopping +// the loop after a fixed number of iterations gives somewhat stochastic +// testing. +// +// Example: +// +// for x.Next() { +// v := make([]bool, x.Choose(4)) +// for i := range v { +// v[i] = x.Maybe() +// } +// fmt.Println(v) +// } +// +// prints (in some order): +// +// [] +// [false] +// [true] +// [false false] +// [false true] +// ... +// [true true] +// [false false false] +// ... +// [true true true] +// [false false false false] +// ... +// [true true true true] +// +type exhaustive struct { + r *rand.Rand + pos int + last []choice +} + +type choice struct { + off int + n int + max int +} + +func (x *exhaustive) Next() bool { + if x.r == nil { + x.r = rand.New(rand.NewSource(time.Now().UnixNano())) + } + x.pos = 0 + if x.last == nil { + x.last = []choice{} + return true + } + for i := len(x.last) - 1; i >= 0; i-- { + c := &x.last[i] + if c.n+1 < c.max { + c.n++ + x.last = x.last[:i+1] + return true + } + } + return false +} + +func (x *exhaustive) Choose(max int) int { + if x.pos >= len(x.last) { + x.last = append(x.last, choice{x.r.Intn(max), 0, max}) + } + c := &x.last[x.pos] + x.pos++ + if c.max != max { + panic("inconsistent use of exhaustive tester") + } + return (c.n + c.off) % max +} + +func (x *exhaustive) Maybe() bool { + return x.Choose(2) == 1 +} diff --git a/src/pkg/reflect/asm_386.s b/src/pkg/reflect/asm_386.s new file mode 100644 index 000000000..27d3fa21d --- /dev/null +++ b/src/pkg/reflect/asm_386.s @@ -0,0 +1,13 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// makeFuncStub is the code half of the function returned by MakeFunc. +// See the comment on the declaration of makeFuncStub in value.go +// for more details. +TEXT ·makeFuncStub(SB),7,$8 + MOVL DX, 0(SP) + LEAL arg+0(FP), CX + MOVL CX, 4(SP) + CALL ·callReflect(SB) + RET diff --git a/src/pkg/reflect/asm_amd64.s b/src/pkg/reflect/asm_amd64.s new file mode 100644 index 000000000..d51d982a9 --- /dev/null +++ b/src/pkg/reflect/asm_amd64.s @@ -0,0 +1,13 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// makeFuncStub is the code half of the function returned by MakeFunc. +// See the comment on the declaration of makeFuncStub in value.go +// for more details. +TEXT ·makeFuncStub(SB),7,$16 + MOVQ DX, 0(SP) + LEAQ arg+0(FP), CX + MOVQ CX, 8(SP) + CALL ·callReflect(SB) + RET diff --git a/src/pkg/reflect/asm_arm.s b/src/pkg/reflect/asm_arm.s new file mode 100644 index 000000000..db487f8a5 --- /dev/null +++ b/src/pkg/reflect/asm_arm.s @@ -0,0 +1,13 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// makeFuncStub is jumped to by the code generated by MakeFunc. +// See the comment on the declaration of makeFuncStub in value.go +// for more details. +TEXT ·makeFuncStub(SB),7,$8 + MOVW R7, 4(R13) + MOVW $arg+0(FP), R1 + MOVW R1, 8(R13) + BL ·callReflect(SB) + RET diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go index c12e90f36..db047963e 100644 --- a/src/pkg/reflect/deepequal.go +++ b/src/pkg/reflect/deepequal.go @@ -122,9 +122,12 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool panic("Not reached") } -// DeepEqual tests for deep equality. It uses normal == equality where possible -// but will scan members of arrays, slices, maps, and fields of structs. It correctly -// handles recursive types. Functions are equal only if they are both nil. +// DeepEqual tests for deep equality. It uses normal == equality where +// possible but will scan elements of arrays, slices, maps, and fields of +// structs. In maps, keys are compared with == but elements use deep +// equality. DeepEqual correctly handles recursive types. Functions are equal +// only if they are both nil. +// An empty slice is not equal to a nil slice. func DeepEqual(a1, a2 interface{}) bool { if a1 == nil || a2 == nil { return a1 == a2 diff --git a/src/pkg/reflect/example_test.go b/src/pkg/reflect/example_test.go new file mode 100644 index 000000000..62455c00a --- /dev/null +++ b/src/pkg/reflect/example_test.go @@ -0,0 +1,52 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflect_test + +import ( + "fmt" + "reflect" +) + +func ExampleMakeFunc() { + // swap is the implementation passed to MakeFunc. + // It must work in terms of reflect.Values so that it is possible + // to write code without knowing beforehand what the types + // will be. + swap := func(in []reflect.Value) []reflect.Value { + return []reflect.Value{in[1], in[0]} + } + + // makeSwap expects fptr to be a pointer to a nil function. + // It sets that pointer to a new function created with MakeFunc. + // When the function is invoked, reflect turns the arguments + // into Values, calls swap, and then turns swap's result slice + // into the values returned by the new function. + makeSwap := func(fptr interface{}) { + // fptr is a pointer to a function. + // Obtain the function value itself (likely nil) as a reflect.Value + // so that we can query its type and then set the value. + fn := reflect.ValueOf(fptr).Elem() + + // Make a function of the right type. + v := reflect.MakeFunc(fn.Type(), swap) + + // Assign it to the value fn represents. + fn.Set(v) + } + + // Make and call a swap function for ints. + var intSwap func(int, int) (int, int) + makeSwap(&intSwap) + fmt.Println(intSwap(0, 1)) + + // Make and call a swap function for float64s. + var floatSwap func(float64, float64) (float64, float64) + makeSwap(&floatSwap) + fmt.Println(floatSwap(2.72, 3.14)) + + // Output: + // 1 0 + // 3.14 2.72 +} diff --git a/src/pkg/reflect/export_test.go b/src/pkg/reflect/export_test.go new file mode 100644 index 000000000..cd8cf2cf2 --- /dev/null +++ b/src/pkg/reflect/export_test.go @@ -0,0 +1,18 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflect + +// MakeRO returns a copy of v with the read-only flag set. +func MakeRO(v Value) Value { + v.flag |= flagRO + return v +} + +// IsRO reports whether v's read-only flag is set. +func IsRO(v Value) bool { + return v.flag&flagRO != 0 +} + +var ArrayOf = arrayOf diff --git a/src/pkg/reflect/makefunc.go b/src/pkg/reflect/makefunc.go new file mode 100644 index 000000000..024f938f1 --- /dev/null +++ b/src/pkg/reflect/makefunc.go @@ -0,0 +1,67 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// MakeFunc implementation. + +package reflect + +import ( + "unsafe" +) + +// makeFuncImpl is the closure value implementing the function +// returned by MakeFunc. +type makeFuncImpl struct { + code uintptr + typ *funcType + fn func([]Value) []Value +} + +// MakeFunc returns a new function of the given Type +// that wraps the function fn. When called, that new function +// does the following: +// +// - converts its arguments to a list of Values args. +// - runs results := fn(args). +// - returns the results as a slice of Values, one per formal result. +// +// The implementation fn can assume that the argument Value slice +// has the number and type of arguments given by typ. +// If typ describes a variadic function, the final Value is itself +// a slice representing the variadic arguments, as in the +// body of a variadic function. The result Value slice returned by fn +// must have the number and type of results given by typ. +// +// The Value.Call method allows the caller to invoke a typed function +// in terms of Values; in contrast, MakeFunc allows the caller to implement +// a typed function in terms of Values. +// +// The Examples section of the documentation includes an illustration +// of how to use MakeFunc to build a swap function for different types. +// +func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { + if typ.Kind() != Func { + panic("reflect: call of MakeFunc with non-Func type") + } + + t := typ.common() + ftyp := (*funcType)(unsafe.Pointer(t)) + + // indirect Go func value (dummy) to obtain + // actual code address. (A Go func is a pointer + // to a C function pointer. http://golang.org/s/go11func.) + dummy := makeFuncStub + code := **(**uintptr)(unsafe.Pointer(&dummy)) + + impl := &makeFuncImpl{code: code, typ: ftyp, fn: fn} + + return Value{t, unsafe.Pointer(impl), flag(Func) << flagKindShift} +} + +// makeFuncStub is an assembly function that is the code half of +// the function returned from MakeFunc. It expects a *callReflectFunc +// as its context register, and its job is to invoke callReflect(ctxt, frame) +// where ctxt is the context register and frame is a pointer to the first +// word in the passed-in argument frame. +func makeFuncStub() diff --git a/src/pkg/reflect/set_test.go b/src/pkg/reflect/set_test.go index 8135a4cd1..85dc55e68 100644 --- a/src/pkg/reflect/set_test.go +++ b/src/pkg/reflect/set_test.go @@ -81,11 +81,11 @@ func TestImplicitMapConversion(t *testing.T) { t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m) } if p := mv.MapIndex(ValueOf(b1)).Elem().Pointer(); p != uintptr(unsafe.Pointer(b2)) { - t.Errorf("#5 MapIndex(b1) = %p want %p", p, b2) + t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2) } } { - // convert channel direction + // convert channel direction m := make(map[<-chan int]chan int) mv := ValueOf(m) c1 := make(chan int) @@ -96,7 +96,7 @@ func TestImplicitMapConversion(t *testing.T) { t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m) } if p := mv.MapIndex(ValueOf(c1)).Pointer(); p != ValueOf(c2).Pointer() { - t.Errorf("#6 MapIndex(c1) = %p want %p", p, c2) + t.Errorf("#6 MapIndex(c1) = %#x want %p", p, c2) } } { @@ -115,7 +115,7 @@ func TestImplicitMapConversion(t *testing.T) { t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m) } if p := mv.MapIndex(ValueOf(b1)).Pointer(); p != uintptr(unsafe.Pointer(b2)) { - t.Errorf("#7 MapIndex(b1) = %p want %p", p, b2) + t.Errorf("#7 MapIndex(b1) = %#x want %p", p, b2) } } diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go index 060bde3af..94a7521a7 100644 --- a/src/pkg/reflect/type.go +++ b/src/pkg/reflect/type.go @@ -92,6 +92,9 @@ type Type interface { // AssignableTo returns true if a value of the type is assignable to type u. AssignableTo(u Type) bool + // ConvertibleTo returns true if a value of the type is convertible to type u. + ConvertibleTo(u Type) bool + // Methods applicable only to some types, depending on Kind. // The methods allowed for each kind are: // @@ -181,11 +184,16 @@ type Type interface { // It panics if i is not in the range [0, NumOut()). Out(i int) Type - runtimeType() *runtimeType - common() *commonType + common() *rtype uncommon() *uncommonType } +/* + * These data structures are known to the compiler (../../cmd/gc/reflect.c). + * A few are known to ../runtime/type.go to convey to debuggers. + * They are also known to ../runtime/type.h. + */ + // A Kind represents the specific kind of type that a Type represents. // The zero Kind is not a valid kind. type Kind uint @@ -220,41 +228,30 @@ const ( UnsafePointer ) -/* - * These data structures are known to the compiler (../../cmd/gc/reflect.c). - * A few are known to ../runtime/type.go to convey to debuggers. - */ - -// The compiler can only construct empty interface values at -// compile time; non-empty interface values get created -// during initialization. Type is an empty interface -// so that the compiler can lay out references as data. -// The underlying type is *reflect.ArrayType and so on. -type runtimeType interface{} - -// commonType is the common implementation of most values. +// rtype is the common implementation of most values. // It is embedded in other, public struct types, but always // with a unique tag like `reflect:"array"` or `reflect:"ptr"` // so that code cannot convert from, say, *arrayType to *ptrType. -type commonType struct { - size uintptr // size in bytes - hash uint32 // hash of type; avoids computation in hash tables - _ uint8 // unused/padding - align uint8 // alignment of variable with this type - fieldAlign uint8 // alignment of struct field with this type - kind uint8 // enumeration for C - alg *uintptr // algorithm table (../runtime/runtime.h:/Alg) - string *string // string form; unnecessary but undeniably useful - *uncommonType // (relatively) uncommon fields - ptrToThis *runtimeType // pointer to this type, if used in binary or has methods +type rtype struct { + size uintptr // size in bytes + hash uint32 // hash of type; avoids computation in hash tables + _ uint8 // unused/padding + align uint8 // alignment of variable with this type + fieldAlign uint8 // alignment of struct field with this type + kind uint8 // enumeration for C + alg *uintptr // algorithm table (../runtime/runtime.h:/Alg) + gc uintptr // garbage collection data + string *string // string form; unnecessary but undeniably useful + *uncommonType // (relatively) uncommon fields + ptrToThis *rtype // type for pointer to this type, if used in binary or has methods } // Method on non-interface type type method struct { name *string // name of method pkgPath *string // nil for exported Names; otherwise import path - mtyp *runtimeType // method type (without receiver) - typ *runtimeType // .(*FuncType) underneath (with receiver) + mtyp *rtype // method type (without receiver) + typ *rtype // .(*FuncType) underneath (with receiver) ifn unsafe.Pointer // fn used in interface call (one-word receiver) tfn unsafe.Pointer // fn used for normal method call } @@ -280,72 +277,72 @@ const ( // arrayType represents a fixed array type. type arrayType struct { - commonType `reflect:"array"` - elem *runtimeType // array element type - slice *runtimeType // slice type - len uintptr + rtype `reflect:"array"` + elem *rtype // array element type + slice *rtype // slice type + len uintptr } // chanType represents a channel type. type chanType struct { - commonType `reflect:"chan"` - elem *runtimeType // channel element type - dir uintptr // channel direction (ChanDir) + rtype `reflect:"chan"` + elem *rtype // channel element type + dir uintptr // channel direction (ChanDir) } // funcType represents a function type. type funcType struct { - commonType `reflect:"func"` - dotdotdot bool // last input parameter is ... - in []*runtimeType // input parameter types - out []*runtimeType // output parameter types + rtype `reflect:"func"` + dotdotdot bool // last input parameter is ... + in []*rtype // input parameter types + out []*rtype // output parameter types } // imethod represents a method on an interface type type imethod struct { - name *string // name of method - pkgPath *string // nil for exported Names; otherwise import path - typ *runtimeType // .(*FuncType) underneath + name *string // name of method + pkgPath *string // nil for exported Names; otherwise import path + typ *rtype // .(*FuncType) underneath } // interfaceType represents an interface type. type interfaceType struct { - commonType `reflect:"interface"` - methods []imethod // sorted by hash + rtype `reflect:"interface"` + methods []imethod // sorted by hash } // mapType represents a map type. type mapType struct { - commonType `reflect:"map"` - key *runtimeType // map key type - elem *runtimeType // map element (value) type + rtype `reflect:"map"` + key *rtype // map key type + elem *rtype // map element (value) type } // ptrType represents a pointer type. type ptrType struct { - commonType `reflect:"ptr"` - elem *runtimeType // pointer element (pointed at) type + rtype `reflect:"ptr"` + elem *rtype // pointer element (pointed at) type } // sliceType represents a slice type. type sliceType struct { - commonType `reflect:"slice"` - elem *runtimeType // slice element type + rtype `reflect:"slice"` + elem *rtype // slice element type } // Struct field type structField struct { - name *string // nil for embedded fields - pkgPath *string // nil for exported Names; otherwise import path - typ *runtimeType // type of field - tag *string // nil if no tag - offset uintptr // byte offset of field within struct + name *string // nil for embedded fields + pkgPath *string // nil for exported Names; otherwise import path + typ *rtype // type of field + tag *string // nil if no tag + offset uintptr // byte offset of field within struct } // structType represents a struct type. type structType struct { - commonType `reflect:"struct"` - fields []structField // sorted by offset + rtype `reflect:"struct"` + fields []structField // sorted by offset } /* @@ -359,7 +356,7 @@ type Method struct { // PkgPath is the package path that qualifies a lower case (unexported) // method name. It is empty for upper case (exported) method names. // The combination of PkgPath and Name uniquely identifies a method - // in a method set. + // in a method set. // See http://golang.org/ref/spec#Uniqueness_of_identifiers Name string PkgPath string @@ -428,18 +425,11 @@ func (t *uncommonType) Name() string { return *t.name } -func (t *commonType) toType() Type { - if t == nil { - return nil - } - return t -} - -func (t *commonType) String() string { return *t.string } +func (t *rtype) String() string { return *t.string } -func (t *commonType) Size() uintptr { return t.size } +func (t *rtype) Size() uintptr { return t.size } -func (t *commonType) Bits() int { +func (t *rtype) Bits() int { if t == nil { panic("reflect: Bits of nil Type") } @@ -450,13 +440,13 @@ func (t *commonType) Bits() int { return int(t.size) * 8 } -func (t *commonType) Align() int { return int(t.align) } +func (t *rtype) Align() int { return int(t.align) } -func (t *commonType) FieldAlign() int { return int(t.fieldAlign) } +func (t *rtype) FieldAlign() int { return int(t.fieldAlign) } -func (t *commonType) Kind() Kind { return Kind(t.kind & kindMask) } +func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) } -func (t *commonType) common() *commonType { return t } +func (t *rtype) common() *rtype { return t } func (t *uncommonType) Method(i int) (m Method) { if t == nil || i < 0 || i >= len(t.methods) { @@ -471,9 +461,9 @@ func (t *uncommonType) Method(i int) (m Method) { m.PkgPath = *p.pkgPath fl |= flagRO } - mt := toCommonType(p.typ) + mt := p.typ m.Type = mt - fn := p.tfn + fn := unsafe.Pointer(&p.tfn) m.Func = Value{mt, fn, fl} m.Index = i return @@ -502,8 +492,8 @@ func (t *uncommonType) MethodByName(name string) (m Method, ok bool) { // TODO(rsc): 6g supplies these, but they are not // as efficient as they could be: they have commonType -// as the receiver instead of *commonType. -func (t *commonType) NumMethod() int { +// as the receiver instead of *rtype. +func (t *rtype) NumMethod() int { if t.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(t)) return tt.NumMethod() @@ -511,7 +501,7 @@ func (t *commonType) NumMethod() int { return t.uncommonType.NumMethod() } -func (t *commonType) Method(i int) (m Method) { +func (t *rtype) Method(i int) (m Method) { if t.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(t)) return tt.Method(i) @@ -519,7 +509,7 @@ func (t *commonType) Method(i int) (m Method) { return t.uncommonType.Method(i) } -func (t *commonType) MethodByName(name string) (m Method, ok bool) { +func (t *rtype) MethodByName(name string) (m Method, ok bool) { if t.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(t)) return tt.MethodByName(name) @@ -527,15 +517,15 @@ func (t *commonType) MethodByName(name string) (m Method, ok bool) { return t.uncommonType.MethodByName(name) } -func (t *commonType) PkgPath() string { +func (t *rtype) PkgPath() string { return t.uncommonType.PkgPath() } -func (t *commonType) Name() string { +func (t *rtype) Name() string { return t.uncommonType.Name() } -func (t *commonType) ChanDir() ChanDir { +func (t *rtype) ChanDir() ChanDir { if t.Kind() != Chan { panic("reflect: ChanDir of non-chan type") } @@ -543,7 +533,7 @@ func (t *commonType) ChanDir() ChanDir { return ChanDir(tt.dir) } -func (t *commonType) IsVariadic() bool { +func (t *rtype) IsVariadic() bool { if t.Kind() != Func { panic("reflect: IsVariadic of non-func type") } @@ -551,7 +541,7 @@ func (t *commonType) IsVariadic() bool { return tt.dotdotdot } -func (t *commonType) Elem() Type { +func (t *rtype) Elem() Type { switch t.Kind() { case Array: tt := (*arrayType)(unsafe.Pointer(t)) @@ -572,7 +562,7 @@ func (t *commonType) Elem() Type { panic("reflect: Elem of invalid type") } -func (t *commonType) Field(i int) StructField { +func (t *rtype) Field(i int) StructField { if t.Kind() != Struct { panic("reflect: Field of non-struct type") } @@ -580,7 +570,7 @@ func (t *commonType) Field(i int) StructField { return tt.Field(i) } -func (t *commonType) FieldByIndex(index []int) StructField { +func (t *rtype) FieldByIndex(index []int) StructField { if t.Kind() != Struct { panic("reflect: FieldByIndex of non-struct type") } @@ -588,7 +578,7 @@ func (t *commonType) FieldByIndex(index []int) StructField { return tt.FieldByIndex(index) } -func (t *commonType) FieldByName(name string) (StructField, bool) { +func (t *rtype) FieldByName(name string) (StructField, bool) { if t.Kind() != Struct { panic("reflect: FieldByName of non-struct type") } @@ -596,7 +586,7 @@ func (t *commonType) FieldByName(name string) (StructField, bool) { return tt.FieldByName(name) } -func (t *commonType) FieldByNameFunc(match func(string) bool) (StructField, bool) { +func (t *rtype) FieldByNameFunc(match func(string) bool) (StructField, bool) { if t.Kind() != Struct { panic("reflect: FieldByNameFunc of non-struct type") } @@ -604,7 +594,7 @@ func (t *commonType) FieldByNameFunc(match func(string) bool) (StructField, bool return tt.FieldByNameFunc(match) } -func (t *commonType) In(i int) Type { +func (t *rtype) In(i int) Type { if t.Kind() != Func { panic("reflect: In of non-func type") } @@ -612,7 +602,7 @@ func (t *commonType) In(i int) Type { return toType(tt.in[i]) } -func (t *commonType) Key() Type { +func (t *rtype) Key() Type { if t.Kind() != Map { panic("reflect: Key of non-map type") } @@ -620,7 +610,7 @@ func (t *commonType) Key() Type { return toType(tt.key) } -func (t *commonType) Len() int { +func (t *rtype) Len() int { if t.Kind() != Array { panic("reflect: Len of non-array type") } @@ -628,7 +618,7 @@ func (t *commonType) Len() int { return int(tt.len) } -func (t *commonType) NumField() int { +func (t *rtype) NumField() int { if t.Kind() != Struct { panic("reflect: NumField of non-struct type") } @@ -636,7 +626,7 @@ func (t *commonType) NumField() int { return len(tt.fields) } -func (t *commonType) NumIn() int { +func (t *rtype) NumIn() int { if t.Kind() != Func { panic("reflect: NumIn of non-func type") } @@ -644,7 +634,7 @@ func (t *commonType) NumIn() int { return len(tt.in) } -func (t *commonType) NumOut() int { +func (t *rtype) NumOut() int { if t.Kind() != Func { panic("reflect: NumOut of non-func type") } @@ -652,7 +642,7 @@ func (t *commonType) NumOut() int { return len(tt.out) } -func (t *commonType) Out(i int) Type { +func (t *rtype) Out(i int) Type { if t.Kind() != Func { panic("reflect: Out of non-func type") } @@ -718,7 +708,7 @@ type StructField struct { Tag StructTag // field tag string Offset uintptr // offset within struct, in bytes Index []int // index sequence for Type.FieldByIndex - Anonymous bool // is an anonymous field + Anonymous bool // is an embedded field } // A StructTag is the tag string in a struct field. @@ -822,7 +812,7 @@ func (t *structType) Field(i int) (f StructField) { // FieldByIndex returns the nested field corresponding to index. func (t *structType) FieldByIndex(index []int) (f StructField) { - f.Type = Type(t.toType()) + f.Type = toType(&t.rtype) for i, x := range index { if i > 0 { ft := f.Type @@ -836,107 +826,143 @@ func (t *structType) FieldByIndex(index []int) (f StructField) { return } -const inf = 1 << 30 // infinity - no struct has that many nesting levels - -func (t *structType) fieldByNameFunc(match func(string) bool, mark map[*structType]bool, depth int) (ff StructField, fd int) { - fd = inf // field depth +// A fieldScan represents an item on the fieldByNameFunc scan work list. +type fieldScan struct { + typ *structType + index []int +} - if mark[t] { - // Struct already seen. - return - } - mark[t] = true - - var fi int // field index - n := 0 // number of matching fields at depth fd -L: - for i := range t.fields { - f := t.Field(i) - d := inf - switch { - case match(f.Name): - // Matching top-level field. - d = depth - case f.Anonymous: - ft := f.Type - if ft.Kind() == Ptr { - ft = ft.Elem() +// FieldByNameFunc returns the struct field with a name that satisfies the +// match function and a boolean to indicate if the field was found. +func (t *structType) FieldByNameFunc(match func(string) bool) (result StructField, ok bool) { + // This uses the same condition that the Go language does: there must be a unique instance + // of the match at a given depth level. If there are multiple instances of a match at the + // same depth, they annihilate each other and inhibit any possible match at a lower level. + // The algorithm is breadth first search, one depth level at a time. + + // The current and next slices are work queues: + // current lists the fields to visit on this depth level, + // and next lists the fields on the next lower level. + current := []fieldScan{} + next := []fieldScan{{typ: t}} + + // nextCount records the number of times an embedded type has been + // encountered and considered for queueing in the 'next' slice. + // We only queue the first one, but we increment the count on each. + // If a struct type T can be reached more than once at a given depth level, + // then it annihilates itself and need not be considered at all when we + // process that next depth level. + var nextCount map[*structType]int + + // visited records the structs that have been considered already. + // Embedded pointer fields can create cycles in the graph of + // reachable embedded types; visited avoids following those cycles. + // It also avoids duplicated effort: if we didn't find the field in an + // embedded type T at level 2, we won't find it in one at level 4 either. + visited := map[*structType]bool{} + + for len(next) > 0 { + current, next = next, current[:0] + count := nextCount + nextCount = nil + + // Process all the fields at this depth, now listed in 'current'. + // The loop queues embedded fields found in 'next', for processing during the next + // iteration. The multiplicity of the 'current' field counts is recorded + // in 'count'; the multiplicity of the 'next' field counts is recorded in 'nextCount'. + for _, scan := range current { + t := scan.typ + if visited[t] { + // We've looked through this type before, at a higher level. + // That higher level would shadow the lower level we're now at, + // so this one can't be useful to us. Ignore it. + continue } - switch { - case match(ft.Name()): - // Matching anonymous top-level field. - d = depth - case fd > depth: - // No top-level field yet; look inside nested structs. - if ft.Kind() == Struct { - st := (*structType)(unsafe.Pointer(ft.(*commonType))) - f, d = st.fieldByNameFunc(match, mark, depth+1) + visited[t] = true + for i := range t.fields { + f := &t.fields[i] + // Find name and type for field f. + var fname string + var ntyp *rtype + if f.name != nil { + fname = *f.name + } else { + // Anonymous field of type T or *T. + // Name taken from type. + ntyp = f.typ + if ntyp.Kind() == Ptr { + ntyp = ntyp.Elem().common() + } + fname = ntyp.Name() } - } - } - switch { - case d < fd: - // Found field at shallower depth. - ff, fi, fd = f, i, d - n = 1 - case d == fd: - // More than one matching field at the same depth (or d, fd == inf). - // Same as no field found at this depth. - n++ - if d == depth { - // Impossible to find a field at lower depth. - break L - } - } - } + // Does it match? + if match(fname) { + // Potential match + if count[t] > 1 || ok { + // Name appeared multiple times at this level: annihilate. + return StructField{}, false + } + result = t.Field(i) + result.Index = nil + result.Index = append(result.Index, scan.index...) + result.Index = append(result.Index, i) + ok = true + continue + } - if n == 1 { - // Found matching field. - if depth >= len(ff.Index) { - ff.Index = make([]int, depth+1) + // Queue embedded struct fields for processing with next level, + // but only if we haven't seen a match yet at this level and only + // if the embedded types haven't already been queued. + if ok || ntyp == nil || ntyp.Kind() != Struct { + continue + } + styp := (*structType)(unsafe.Pointer(ntyp)) + if nextCount[styp] > 0 { + nextCount[styp] = 2 // exact multiple doesn't matter + continue + } + if nextCount == nil { + nextCount = map[*structType]int{} + } + nextCount[styp] = 1 + if count[t] > 1 { + nextCount[styp] = 2 // exact multiple doesn't matter + } + var index []int + index = append(index, scan.index...) + index = append(index, i) + next = append(next, fieldScan{styp, index}) + } } - if len(ff.Index) > 1 { - ff.Index[depth] = fi + if ok { + break } - } else { - // None or more than one matching field found. - fd = inf } - - delete(mark, t) return } // FieldByName returns the struct field with the given name // and a boolean to indicate if the field was found. func (t *structType) FieldByName(name string) (f StructField, present bool) { - return t.FieldByNameFunc(func(s string) bool { return s == name }) -} - -// FieldByNameFunc returns the struct field with a name that satisfies the -// match function and a boolean to indicate if the field was found. -func (t *structType) FieldByNameFunc(match func(string) bool) (f StructField, present bool) { - if ff, fd := t.fieldByNameFunc(match, make(map[*structType]bool), 0); fd < inf { - ff.Index = ff.Index[0 : fd+1] - f, present = ff, true - } - return -} - -// Convert runtime type to reflect type. -func toCommonType(p *runtimeType) *commonType { - if p == nil { - return nil + // Quick check for top-level name, or struct without anonymous fields. + hasAnon := false + if name != "" { + for i := range t.fields { + tf := &t.fields[i] + if tf.name == nil { + hasAnon = true + continue + } + if *tf.name == name { + return t.Field(i), true + } + } } - return (*p).(*commonType) -} - -func toType(p *runtimeType) Type { - if p == nil { - return nil + if !hasAnon { + return } - return (*p).(*commonType) + return t.FieldByNameFunc(func(s string) bool { return s == name }) } // TypeOf returns the reflection Type of the value in the interface{}. @@ -949,28 +975,18 @@ func TypeOf(i interface{}) Type { // ptrMap is the cache for PtrTo. var ptrMap struct { sync.RWMutex - m map[*commonType]*ptrType -} - -func (t *commonType) runtimeType() *runtimeType { - // The runtimeType always precedes the commonType in memory. - // Adjust pointer to find it. - var rt struct { - i runtimeType - ct commonType - } - return (*runtimeType)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) - unsafe.Offsetof(rt.ct))) + m map[*rtype]*ptrType } // PtrTo returns the pointer type with element t. // For example, if t represents type Foo, PtrTo(t) represents *Foo. func PtrTo(t Type) Type { - return t.(*commonType).ptrTo() + return t.(*rtype).ptrTo() } -func (ct *commonType) ptrTo() *commonType { - if p := ct.ptrToThis; p != nil { - return toCommonType(p) +func (t *rtype) ptrTo() *rtype { + if p := t.ptrToThis; p != nil { + return p } // Otherwise, synthesize one. @@ -980,36 +996,31 @@ func (ct *commonType) ptrTo() *commonType { // the type structures in read-only memory. ptrMap.RLock() if m := ptrMap.m; m != nil { - if p := m[ct]; p != nil { + if p := m[t]; p != nil { ptrMap.RUnlock() - return &p.commonType + return &p.rtype } } ptrMap.RUnlock() ptrMap.Lock() if ptrMap.m == nil { - ptrMap.m = make(map[*commonType]*ptrType) + ptrMap.m = make(map[*rtype]*ptrType) } - p := ptrMap.m[ct] + p := ptrMap.m[t] if p != nil { // some other goroutine won the race and created it ptrMap.Unlock() - return &p.commonType + return &p.rtype } - var rt struct { - i runtimeType - ptrType - } - rt.i = &rt.commonType - - // initialize p using *byte's ptrType as a prototype. - p = &rt.ptrType - var ibyte interface{} = (*byte)(nil) - bp := (*ptrType)(unsafe.Pointer((**(**runtimeType)(unsafe.Pointer(&ibyte))).(*commonType))) - *p = *bp + // Create a new ptrType starting with the description + // of an *unsafe.Pointer. + p = new(ptrType) + var iptr interface{} = (*unsafe.Pointer)(nil) + prototype := *(**ptrType)(unsafe.Pointer(&iptr)) + *p = *prototype - s := "*" + *ct.string + s := "*" + *t.string p.string = &s // For the type structures linked into the binary, the @@ -1017,37 +1028,53 @@ func (ct *commonType) ptrTo() *commonType { // Create a good hash for the new string by using // the FNV-1 hash's mixing function to combine the // old hash and the new "*". - p.hash = ct.hash*16777619 ^ '*' + p.hash = fnv1(t.hash, '*') p.uncommonType = nil p.ptrToThis = nil - p.elem = (*runtimeType)(unsafe.Pointer(uintptr(unsafe.Pointer(ct)) - unsafe.Offsetof(rt.ptrType))) + p.elem = t - ptrMap.m[ct] = p + ptrMap.m[t] = p ptrMap.Unlock() - return &p.commonType + return &p.rtype +} + +// fnv1 incorporates the list of bytes into the hash x using the FNV-1 hash function. +func fnv1(x uint32, list ...byte) uint32 { + for _, b := range list { + x = x*16777619 ^ uint32(b) + } + return x } -func (t *commonType) Implements(u Type) bool { +func (t *rtype) Implements(u Type) bool { if u == nil { panic("reflect: nil type passed to Type.Implements") } if u.Kind() != Interface { panic("reflect: non-interface type passed to Type.Implements") } - return implements(u.(*commonType), t) + return implements(u.(*rtype), t) } -func (t *commonType) AssignableTo(u Type) bool { +func (t *rtype) AssignableTo(u Type) bool { if u == nil { panic("reflect: nil type passed to Type.AssignableTo") } - uu := u.(*commonType) + uu := u.(*rtype) return directlyAssignable(uu, t) || implements(uu, t) } +func (t *rtype) ConvertibleTo(u Type) bool { + if u == nil { + panic("reflect: nil type passed to Type.ConvertibleTo") + } + uu := u.(*rtype) + return convertOp(uu, t) != nil +} + // implements returns true if the type V implements the interface type T. -func implements(T, V *commonType) bool { +func implements(T, V *rtype) bool { if T.Kind() != Interface { return false } @@ -1105,7 +1132,7 @@ func implements(T, V *commonType) bool { // http://golang.org/doc/go_spec.html#Assignability // Ignoring the interface rules (implemented elsewhere) // and the ideal constant rules (no ideal constants at run time). -func directlyAssignable(T, V *commonType) bool { +func directlyAssignable(T, V *rtype) bool { // x's type V is identical to T? if T == V { return true @@ -1117,10 +1144,28 @@ func directlyAssignable(T, V *commonType) bool { return false } - // x's type T and V have identical underlying types. - // Since at least one is unnamed, only the composite types - // need to be considered. - switch T.Kind() { + // x's type T and V must have identical underlying types. + return haveIdenticalUnderlyingType(T, V) +} + +func haveIdenticalUnderlyingType(T, V *rtype) bool { + if T == V { + return true + } + + kind := T.Kind() + if kind != V.Kind() { + return false + } + + // Non-composite types of equal kind have same underlying type + // (the predefined instance of the type). + if Bool <= kind && kind <= Complex128 || kind == String || kind == UnsafePointer { + return true + } + + // Composite types. + switch kind { case Array: return T.Elem() == V.Elem() && T.Len() == V.Len() @@ -1178,8 +1223,19 @@ func directlyAssignable(T, V *commonType) bool { for i := range t.fields { tf := &t.fields[i] vf := &v.fields[i] - if tf.name != vf.name || tf.pkgPath != vf.pkgPath || - tf.typ != vf.typ || tf.tag != vf.tag || tf.offset != vf.offset { + if tf.name != vf.name && (tf.name == nil || vf.name == nil || *tf.name != *vf.name) { + return false + } + if tf.pkgPath != vf.pkgPath && (tf.pkgPath == nil || vf.pkgPath == nil || *tf.pkgPath != *vf.pkgPath) { + return false + } + if tf.typ != vf.typ { + return false + } + if tf.tag != vf.tag && (tf.tag == nil || vf.tag == nil || *tf.tag != *vf.tag) { + return false + } + if tf.offset != vf.offset { return false } } @@ -1188,3 +1244,295 @@ func directlyAssignable(T, V *commonType) bool { return false } + +// typelinks is implemented in package runtime. +// It retuns a slice of all the 'typelink' information in the binary, +// which is to say a slice of known types, sorted by string. +// Note that strings are not unique identifiers for types: +// there can be more than one with a given string. +// Only types we might want to look up are included: +// channels, maps, slices, and arrays. +func typelinks() []*rtype + +// typesByString returns the subslice of typelinks() whose elements have +// the given string representation. +// It may be empty (no known types with that string) or may have +// multiple elements (multiple types with that string). +func typesByString(s string) []*rtype { + typ := typelinks() + + // We are looking for the first index i where the string becomes >= s. + // This is a copy of sort.Search, with f(h) replaced by (*typ[h].string >= s). + i, j := 0, len(typ) + for i < j { + h := i + (j-i)/2 // avoid overflow when computing h + // i ≤ h < j + if !(*typ[h].string >= s) { + i = h + 1 // preserves f(i-1) == false + } else { + j = h // preserves f(j) == true + } + } + // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. + + // Having found the first, linear scan forward to find the last. + // We could do a second binary search, but the caller is going + // to do a linear scan anyway. + j = i + for j < len(typ) && *typ[j].string == s { + j++ + } + + // This slice will be empty if the string is not found. + return typ[i:j] +} + +// The lookupCache caches ChanOf, MapOf, and SliceOf lookups. +var lookupCache struct { + sync.RWMutex + m map[cacheKey]*rtype +} + +// A cacheKey is the key for use in the lookupCache. +// Four values describe any of the types we are looking for: +// type kind, one or two subtypes, and an extra integer. +type cacheKey struct { + kind Kind + t1 *rtype + t2 *rtype + extra uintptr +} + +// cacheGet looks for a type under the key k in the lookupCache. +// If it finds one, it returns that type. +// If not, it returns nil with the cache locked. +// The caller is expected to use cachePut to unlock the cache. +func cacheGet(k cacheKey) Type { + lookupCache.RLock() + t := lookupCache.m[k] + lookupCache.RUnlock() + if t != nil { + return t + } + + lookupCache.Lock() + t = lookupCache.m[k] + if t != nil { + lookupCache.Unlock() + return t + } + + if lookupCache.m == nil { + lookupCache.m = make(map[cacheKey]*rtype) + } + + return nil +} + +// cachePut stores the given type in the cache, unlocks the cache, +// and returns the type. It is expected that the cache is locked +// because cacheGet returned nil. +func cachePut(k cacheKey, t *rtype) Type { + lookupCache.m[k] = t + lookupCache.Unlock() + return t +} + +// ChanOf returns the channel type with the given direction and element type. +// For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int. +// +// The gc runtime imposes a limit of 64 kB on channel element types. +// If t's size is equal to or exceeds this limit, ChanOf panics. +func ChanOf(dir ChanDir, t Type) Type { + typ := t.(*rtype) + + // Look in cache. + ckey := cacheKey{Chan, typ, nil, uintptr(dir)} + if ch := cacheGet(ckey); ch != nil { + return ch + } + + // This restriction is imposed by the gc compiler and the runtime. + if typ.size >= 1<<16 { + lookupCache.Unlock() + panic("reflect.ChanOf: element size too large") + } + + // Look in known types. + // TODO: Precedence when constructing string. + var s string + switch dir { + default: + lookupCache.Unlock() + panic("reflect.ChanOf: invalid dir") + case SendDir: + s = "chan<- " + *typ.string + case RecvDir: + s = "<-chan " + *typ.string + case BothDir: + s = "chan " + *typ.string + } + for _, tt := range typesByString(s) { + ch := (*chanType)(unsafe.Pointer(tt)) + if ch.elem == typ && ch.dir == uintptr(dir) { + return cachePut(ckey, tt) + } + } + + // Make a channel type. + var ichan interface{} = (chan unsafe.Pointer)(nil) + prototype := *(**chanType)(unsafe.Pointer(&ichan)) + ch := new(chanType) + *ch = *prototype + ch.string = &s + ch.hash = fnv1(typ.hash, 'c', byte(dir)) + ch.elem = typ + ch.uncommonType = nil + ch.ptrToThis = nil + + return cachePut(ckey, &ch.rtype) +} + +// MapOf returns the map type with the given key and element types. +// For example, if k represents int and e represents string, +// MapOf(k, e) represents map[int]string. +// +// If the key type is not a valid map key type (that is, if it does +// not implement Go's == operator), MapOf panics. TODO(rsc). +func MapOf(key, elem Type) Type { + ktyp := key.(*rtype) + etyp := elem.(*rtype) + + // TODO: Check for invalid key types. + + // Look in cache. + ckey := cacheKey{Map, ktyp, etyp, 0} + if mt := cacheGet(ckey); mt != nil { + return mt + } + + // Look in known types. + s := "map[" + *ktyp.string + "]" + *etyp.string + for _, tt := range typesByString(s) { + mt := (*mapType)(unsafe.Pointer(tt)) + if mt.key == ktyp && mt.elem == etyp { + return cachePut(ckey, tt) + } + } + + // Make a map type. + var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil) + prototype := *(**mapType)(unsafe.Pointer(&imap)) + mt := new(mapType) + *mt = *prototype + mt.string = &s + mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash)) + mt.key = ktyp + mt.elem = etyp + mt.uncommonType = nil + mt.ptrToThis = nil + + return cachePut(ckey, &mt.rtype) +} + +// SliceOf returns the slice type with element type t. +// For example, if t represents int, SliceOf(t) represents []int. +func SliceOf(t Type) Type { + typ := t.(*rtype) + + // Look in cache. + ckey := cacheKey{Slice, typ, nil, 0} + if slice := cacheGet(ckey); slice != nil { + return slice + } + + // Look in known types. + s := "[]" + *typ.string + for _, tt := range typesByString(s) { + slice := (*sliceType)(unsafe.Pointer(tt)) + if slice.elem == typ { + return cachePut(ckey, tt) + } + } + + // Make a slice type. + var islice interface{} = ([]unsafe.Pointer)(nil) + prototype := *(**sliceType)(unsafe.Pointer(&islice)) + slice := new(sliceType) + *slice = *prototype + slice.string = &s + slice.hash = fnv1(typ.hash, '[') + slice.elem = typ + slice.uncommonType = nil + slice.ptrToThis = nil + + return cachePut(ckey, &slice.rtype) +} + +// ArrayOf returns the array type with the given count and element type. +// For example, if t represents int, ArrayOf(5, t) represents [5]int. +// +// If the resulting type would be larger than the available address space, +// ArrayOf panics. +// +// TODO(rsc): Unexported for now. Export once the alg field is set correctly +// for the type. This may require significant work. +func arrayOf(count int, elem Type) Type { + typ := elem.(*rtype) + slice := SliceOf(elem) + + // Look in cache. + ckey := cacheKey{Array, typ, nil, uintptr(count)} + if slice := cacheGet(ckey); slice != nil { + return slice + } + + // Look in known types. + s := "[" + strconv.Itoa(count) + "]" + *typ.string + for _, tt := range typesByString(s) { + slice := (*sliceType)(unsafe.Pointer(tt)) + if slice.elem == typ { + return cachePut(ckey, tt) + } + } + + // Make an array type. + var iarray interface{} = [1]unsafe.Pointer{} + prototype := *(**arrayType)(unsafe.Pointer(&iarray)) + array := new(arrayType) + *array = *prototype + array.string = &s + array.hash = fnv1(typ.hash, '[') + for n := uint32(count); n > 0; n >>= 8 { + array.hash = fnv1(array.hash, byte(n)) + } + array.hash = fnv1(array.hash, ']') + array.elem = typ + max := ^uintptr(0) / typ.size + if uintptr(count) > max { + panic("reflect.ArrayOf: array size would exceed virtual address space") + } + array.size = typ.size * uintptr(count) + array.align = typ.align + array.fieldAlign = typ.fieldAlign + // TODO: array.alg + // TODO: array.gc + array.uncommonType = nil + array.ptrToThis = nil + array.len = uintptr(count) + array.slice = slice.(*rtype) + + return cachePut(ckey, &array.rtype) +} + +// toType converts from a *rtype to a Type that can be returned +// to the client of package reflect. In gc, the only concern is that +// a nil *rtype must be replaced by a nil Type, but in gccgo this +// function takes care of ensuring that multiple *rtype for the same +// type are coalesced into a single Type. +func toType(t *rtype) Type { + if t == nil { + return nil + } + return t +} diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 79476ad22..c87812c46 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -60,7 +60,7 @@ func memmove(adst, asrc unsafe.Pointer, n uintptr) { // direct operations. type Value struct { // typ holds the type of the value represented by a Value. - typ *commonType + typ *rtype // val holds the 1-word representation of the value. // If flag's flagIndir bit is set, then val is a pointer to the data. @@ -211,7 +211,7 @@ func storeIword(p unsafe.Pointer, w iword, n uintptr) { // emptyInterface is the header for an interface{} value. type emptyInterface struct { - typ *runtimeType + typ *rtype word iword } @@ -219,8 +219,8 @@ type emptyInterface struct { type nonEmptyInterface struct { // see ../runtime/iface.c:/Itab itab *struct { - ityp *runtimeType // static interface type - typ *runtimeType // dynamic concrete type + ityp *rtype // static interface type + typ *rtype // dynamic concrete type link unsafe.Pointer bad int32 unused int32 @@ -302,6 +302,17 @@ func (v Value) Bytes() []byte { return *(*[]byte)(v.val) } +// runes returns v's underlying value. +// It panics if v's underlying value is not a slice of runes (int32s). +func (v Value) runes() []rune { + v.mustBe(Slice) + if v.typ.Elem().Kind() != Int32 { + panic("reflect.Value.Bytes of non-rune slice") + } + // Slice is always bigger than a word; assume flagIndir. + return *(*[]rune)(v.val) +} + // CanAddr returns true if the value's address can be obtained with Addr. // Such values are called addressable. A value is addressable if it is // an element of a slice, an element of an addressable array, @@ -335,7 +346,7 @@ func (v Value) Call(in []Value) []Value { } // CallSlice calls the variadic function v with the input arguments in, -// assigning the slice in[len(in)-1] to v's final variadic argument. +// assigning the slice in[len(in)-1] to v's final variadic argument. // For example, if len(in) == 3, v.Call(in) represents the Go call v(in[0], in[1], in[2]...). // Call panics if v's Kind is not Func or if v is not variadic. // It returns the output results as Values. @@ -365,12 +376,12 @@ func (v Value) call(method string, in []Value) []Value { if m.pkgPath != nil { panic(method + " of unexported method") } - t = toCommonType(m.typ) + t = m.typ iface := (*nonEmptyInterface)(v.val) if iface.itab == nil { panic(method + " of method on nil interface value") } - fn = iface.itab.fun[i] + fn = unsafe.Pointer(&iface.itab.fun[i]) rcvr = iface.word } else { ut := v.typ.uncommon() @@ -381,8 +392,8 @@ func (v Value) call(method string, in []Value) []Value { if m.pkgPath != nil { panic(method + " of unexported method") } - fn = m.ifn - t = toCommonType(m.mtyp) + fn = unsafe.Pointer(&m.ifn) + t = m.mtyp rcvr = v.iword() } } else if v.flag&flagIndir != 0 { @@ -490,9 +501,9 @@ func (v Value) call(method string, in []Value) []Value { // TODO(rsc): revisit when reference counting happens. // The values are holding up the in references for us, // but something must be done for the out references. - // For now make everything look like a pointer by pretending - // to allocate a []*int. - args := make([]*int, size/ptrSize) + // For now make everything look like a pointer by allocating + // a []unsafe.Pointer. + args := make([]unsafe.Pointer, size/ptrSize) ptr := uintptr(unsafe.Pointer(&args[0])) off := uintptr(0) if v.flag&flagMethod != 0 { @@ -502,7 +513,7 @@ func (v Value) call(method string, in []Value) []Value { } for i, v := range in { v.mustBeExported() - targ := t.In(i).(*commonType) + targ := t.In(i).(*rtype) a := uintptr(targ.align) off = (off + a - 1) &^ (a - 1) n := targ.size @@ -536,6 +547,85 @@ func (v Value) call(method string, in []Value) []Value { return ret } +// callReflect is the call implementation used by a function +// returned by MakeFunc. In many ways it is the opposite of the +// method Value.call above. The method above converts a call using Values +// into a call of a function with a concrete argument frame, while +// callReflect converts a call of a function with a concrete argument +// frame into a call using Values. +// It is in this file so that it can be next to the call method above. +// The remainder of the MakeFunc implementation is in makefunc.go. +func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) { + ftyp := ctxt.typ + f := ctxt.fn + + // Copy argument frame into Values. + ptr := frame + off := uintptr(0) + in := make([]Value, 0, len(ftyp.in)) + for _, arg := range ftyp.in { + typ := arg + off += -off & uintptr(typ.align-1) + v := Value{typ, nil, flag(typ.Kind()) << flagKindShift} + if typ.size <= ptrSize { + // value fits in word. + v.val = unsafe.Pointer(loadIword(unsafe.Pointer(uintptr(ptr)+off), typ.size)) + } else { + // value does not fit in word. + // Must make a copy, because f might keep a reference to it, + // and we cannot let f keep a reference to the stack frame + // after this function returns, not even a read-only reference. + v.val = unsafe_New(typ) + memmove(v.val, unsafe.Pointer(uintptr(ptr)+off), typ.size) + v.flag |= flagIndir + } + in = append(in, v) + off += typ.size + } + + // Call underlying function. + out := f(in) + if len(out) != len(ftyp.out) { + panic("reflect: wrong return count from function created by MakeFunc") + } + + // Copy results back into argument frame. + if len(ftyp.out) > 0 { + off += -off & (ptrSize - 1) + for i, arg := range ftyp.out { + typ := arg + v := out[i] + if v.typ != typ { + panic("reflect: function created by MakeFunc using " + funcName(f) + + " returned wrong type: have " + + out[i].typ.String() + " for " + typ.String()) + } + if v.flag&flagRO != 0 { + panic("reflect: function created by MakeFunc using " + funcName(f) + + " returned value obtained from unexported field") + } + off += -off & uintptr(typ.align-1) + addr := unsafe.Pointer(uintptr(ptr) + off) + if v.flag&flagIndir == 0 { + storeIword(addr, iword(v.val), typ.size) + } else { + memmove(addr, v.val, typ.size) + } + off += typ.size + } + } +} + +// funcName returns the name of f, for use in error messages. +func funcName(f func([]Value) []Value) string { + pc := *(*uintptr)(unsafe.Pointer(&f)) + rf := runtime.FuncForPC(pc) + if rf != nil { + return rf.Name() + } + return "closure" +} + // Cap returns v's capacity. // It panics if v's Kind is not Array, Chan, or Slice. func (v Value) Cap() int { @@ -586,7 +676,7 @@ func (v Value) Elem() Value { switch k { case Interface: var ( - typ *commonType + typ *rtype val unsafe.Pointer ) if v.typ.NumMethod() == 0 { @@ -595,7 +685,7 @@ func (v Value) Elem() Value { // nil interface value return Value{} } - typ = toCommonType(eface.typ) + typ = eface.typ val = unsafe.Pointer(eface.word) } else { iface := (*nonEmptyInterface)(v.val) @@ -603,7 +693,7 @@ func (v Value) Elem() Value { // nil interface value return Value{} } - typ = toCommonType(iface.itab.typ) + typ = iface.itab.typ val = unsafe.Pointer(iface.word) } fl := v.flag & flagRO @@ -623,7 +713,7 @@ func (v Value) Elem() Value { return Value{} } tt := (*ptrType)(unsafe.Pointer(v.typ)) - typ := toCommonType(tt.elem) + typ := tt.elem fl := v.flag&flagRO | flagIndir | flagAddr fl |= flag(typ.Kind() << flagKindShift) return Value{typ, val, fl} @@ -640,7 +730,7 @@ func (v Value) Field(i int) Value { panic("reflect: Field index out of range") } field := &tt.fields[i] - typ := toCommonType(field.typ) + typ := field.typ // Inherit permission bits from v. fl := v.flag & (flagRO | flagIndir | flagAddr) @@ -723,8 +813,10 @@ func (v Value) Float() float64 { panic(&ValueError{"reflect.Value.Float", k}) } +var uint8Type = TypeOf(uint8(0)).(*rtype) + // Index returns v's i'th element. -// It panics if v's Kind is not Array or Slice or i is out of range. +// It panics if v's Kind is not Array, Slice, or String or i is out of range. func (v Value) Index(i int) Value { k := v.kind() switch k { @@ -733,7 +825,7 @@ func (v Value) Index(i int) Value { if i < 0 || i > int(tt.len) { panic("reflect: array index out of range") } - typ := toCommonType(tt.elem) + typ := tt.elem fl := v.flag & (flagRO | flagIndir | flagAddr) // bits same as overall array fl |= flag(typ.Kind()) << flagKindShift offset := uintptr(i) * typ.size @@ -761,10 +853,19 @@ func (v Value) Index(i int) Value { panic("reflect: slice index out of range") } tt := (*sliceType)(unsafe.Pointer(v.typ)) - typ := toCommonType(tt.elem) + typ := tt.elem fl |= flag(typ.Kind()) << flagKindShift val := unsafe.Pointer(s.Data + uintptr(i)*typ.size) return Value{typ, val, fl} + + case String: + fl := v.flag&flagRO | flag(Uint8<= s.Len { + panic("reflect: string index out of range") + } + val := *(*byte)(unsafe.Pointer(s.Data + uintptr(i))) + return Value{uint8Type, unsafe.Pointer(uintptr(val)), fl} } panic(&ValueError{"reflect.Value.Index", k}) } @@ -826,7 +927,7 @@ func valueInterface(v Value, safe bool) interface{} { if safe && v.flag&flagRO != 0 { // Do not allow access to unexported values via Interface, - // because they might be pointers that should not be + // because they might be pointers that should not be // writable or methods or function that should not be callable. panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") } @@ -846,7 +947,7 @@ func valueInterface(v Value, safe bool) interface{} { // Non-interface value. var eface emptyInterface - eface.typ = v.typ.runtimeType() + eface.typ = v.typ eface.word = v.iword() if v.flag&flagIndir != 0 && v.typ.size > ptrSize { @@ -919,9 +1020,9 @@ func (v Value) Len() int { tt := (*arrayType)(unsafe.Pointer(v.typ)) return int(tt.len) case Chan: - return int(chanlen(v.iword())) + return chanlen(v.iword()) case Map: - return int(maplen(v.iword())) + return maplen(v.iword()) case Slice: // Slice is bigger than a word; assume flagIndir. return (*SliceHeader)(v.val).Len @@ -947,13 +1048,13 @@ func (v Value) MapIndex(key Value) Value { // considered unexported. This is consistent with the // behavior for structs, which allow read but not write // of unexported fields. - key = key.assignTo("reflect.Value.MapIndex", toCommonType(tt.key), nil) + key = key.assignTo("reflect.Value.MapIndex", tt.key, nil) - word, ok := mapaccess(v.typ.runtimeType(), v.iword(), key.iword()) + word, ok := mapaccess(v.typ, v.iword(), key.iword()) if !ok { return Value{} } - typ := toCommonType(tt.elem) + typ := tt.elem fl := (v.flag | key.flag) & flagRO if typ.size > ptrSize { fl |= flagIndir @@ -969,7 +1070,7 @@ func (v Value) MapIndex(key Value) Value { func (v Value) MapKeys() []Value { v.mustBe(Map) tt := (*mapType)(unsafe.Pointer(v.typ)) - keyType := toCommonType(tt.key) + keyType := tt.key fl := v.flag & flagRO fl |= flag(keyType.Kind()) << flagKindShift @@ -978,11 +1079,11 @@ func (v Value) MapKeys() []Value { } m := v.iword() - mlen := int32(0) + mlen := int(0) if m != nil { mlen = maplen(m) } - it := mapiterinit(v.typ.runtimeType(), m) + it := mapiterinit(v.typ, m) a := make([]Value, mlen) var i int for i = 0; i < len(a); i++ { @@ -1081,7 +1182,7 @@ func overflowFloat32(x float64) bool { if x < 0 { x = -x } - return math.MaxFloat32 <= x && x <= math.MaxFloat64 + return math.MaxFloat32 < x && x <= math.MaxFloat64 } // OverflowInt returns true if the int64 x cannot be represented by v's type. @@ -1115,18 +1216,35 @@ func (v Value) OverflowUint(x uint64) bool { // code using reflect cannot obtain unsafe.Pointers // without importing the unsafe package explicitly. // It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer. +// +// If v's Kind is Func, the returned pointer is an underlying +// code pointer, but not necessarily enough to identify a +// single function uniquely. The only guarantee is that the +// result is zero if and only if v is a nil func Value. func (v Value) Pointer() uintptr { k := v.kind() switch k { - case Chan, Func, Map, Ptr, UnsafePointer: - if k == Func && v.flag&flagMethod != 0 { + case Chan, Map, Ptr, UnsafePointer: + p := v.val + if v.flag&flagIndir != 0 { + p = *(*unsafe.Pointer)(p) + } + return uintptr(p) + case Func: + if v.flag&flagMethod != 0 { panic("reflect.Value.Pointer of method Value") } p := v.val if v.flag&flagIndir != 0 { p = *(*unsafe.Pointer)(p) } + // Non-nil func value points at data block. + // First word of data block is actual code. + if p != nil { + p = *(*unsafe.Pointer)(p) + } return uintptr(p) + case Slice: return (*SliceHeader)(v.val).Data } @@ -1151,9 +1269,9 @@ func (v Value) recv(nb bool) (val Value, ok bool) { if ChanDir(tt.dir)&RecvDir == 0 { panic("recv on send-only channel") } - word, selected, ok := chanrecv(v.typ.runtimeType(), v.iword(), nb) + word, selected, ok := chanrecv(v.typ, v.iword(), nb) if selected { - typ := toCommonType(tt.elem) + typ := tt.elem fl := flag(typ.Kind()) << flagKindShift if typ.size > ptrSize { fl |= flagIndir @@ -1180,8 +1298,8 @@ func (v Value) send(x Value, nb bool) (selected bool) { panic("send on recv-only channel") } x.mustBeExported() - x = x.assignTo("reflect.Value.Send", toCommonType(tt.elem), nil) - return chansend(v.typ.runtimeType(), v.iword(), x.iword(), nb) + x = x.assignTo("reflect.Value.Send", tt.elem, nil) + return chansend(v.typ, v.iword(), x.iword(), nb) } // Set assigns x to the value v. @@ -1221,6 +1339,17 @@ func (v Value) SetBytes(x []byte) { *(*[]byte)(v.val) = x } +// setRunes sets v's underlying value. +// It panics if v's underlying value is not a slice of runes (int32s). +func (v Value) setRunes(x []rune) { + v.mustBeAssignable() + v.mustBe(Slice) + if v.typ.Elem().Kind() != Int32 { + panic("reflect.Value.setRunes of non-rune slice") + } + *(*[]rune)(v.val) = x +} + // SetComplex sets v's underlying value to x. // It panics if v's Kind is not Complex64 or Complex128, or if CanSet() is false. func (v Value) SetComplex(x complex128) { @@ -1292,12 +1421,12 @@ func (v Value) SetMapIndex(key, val Value) { v.mustBeExported() key.mustBeExported() tt := (*mapType)(unsafe.Pointer(v.typ)) - key = key.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.key), nil) + key = key.assignTo("reflect.Value.SetMapIndex", tt.key, nil) if val.typ != nil { val.mustBeExported() - val = val.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.elem), nil) + val = val.assignTo("reflect.Value.SetMapIndex", tt.elem, nil) } - mapassign(v.typ.runtimeType(), v.iword(), key.iword(), val.iword(), val.typ != nil) + mapassign(v.typ, v.iword(), key.iword(), val.iword(), val.typ != nil) } // SetUint sets v's underlying value to x. @@ -1339,7 +1468,7 @@ func (v Value) SetString(x string) { } // Slice returns a slice of v. -// It panics if v's Kind is not Array or Slice. +// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array. func (v Value) Slice(beg, end int) Value { var ( cap int @@ -1349,31 +1478,44 @@ func (v Value) Slice(beg, end int) Value { switch k := v.kind(); k { default: panic(&ValueError{"reflect.Value.Slice", k}) + case Array: if v.flag&flagAddr == 0 { panic("reflect.Value.Slice: slice of unaddressable array") } tt := (*arrayType)(unsafe.Pointer(v.typ)) cap = int(tt.len) - typ = (*sliceType)(unsafe.Pointer(toCommonType(tt.slice))) + typ = (*sliceType)(unsafe.Pointer(tt.slice)) base = v.val + case Slice: typ = (*sliceType)(unsafe.Pointer(v.typ)) s := (*SliceHeader)(v.val) base = unsafe.Pointer(s.Data) cap = s.Cap + case String: + s := (*StringHeader)(v.val) + if beg < 0 || end < beg || end > s.Len { + panic("reflect.Value.Slice: string slice index out of bounds") + } + var x string + val := (*StringHeader)(unsafe.Pointer(&x)) + val.Data = s.Data + uintptr(beg) + val.Len = end - beg + return Value{v.typ, unsafe.Pointer(&x), v.flag} } + if beg < 0 || end < beg || end > cap { panic("reflect.Value.Slice: slice index out of bounds") } // Declare slice so that gc can see the base pointer in it. - var x []byte + var x []unsafe.Pointer // Reinterpret as *SliceHeader to edit. s := (*SliceHeader)(unsafe.Pointer(&x)) - s.Data = uintptr(base) + uintptr(beg)*toCommonType(typ.elem).Size() + s.Data = uintptr(base) + uintptr(beg)*typ.elem.Size() s.Len = end - beg s.Cap = cap - beg @@ -1439,7 +1581,7 @@ func (v Value) Type() Type { panic("reflect: broken Value") } m := &tt.methods[i] - return toCommonType(m.typ) + return m.typ } // Method on concrete type. ut := v.typ.uncommon() @@ -1447,7 +1589,7 @@ func (v Value) Type() Type { panic("reflect: broken Value") } m := &ut.methods[i] - return toCommonType(m.mtyp) + return m.mtyp } // Uint returns v's underlying value, as a uint64. @@ -1618,13 +1760,148 @@ func Copy(dst, src Value) int { return n } +// A runtimeSelect is a single case passed to rselect. +// This must match ../runtime/chan.c:/runtimeSelect +type runtimeSelect struct { + dir uintptr // 0, SendDir, or RecvDir + typ *rtype // channel type + ch iword // interface word for channel + val iword // interface word for value (for SendDir) +} + +// rselect runs a select. It returns the index of the chosen case, +// and if the case was a receive, the interface word of the received +// value and the conventional OK bool to indicate whether the receive +// corresponds to a sent value. +func rselect([]runtimeSelect) (chosen int, recv iword, recvOK bool) + +// A SelectDir describes the communication direction of a select case. +type SelectDir int + +// NOTE: These values must match ../runtime/chan.c:/SelectDir. + +const ( + _ SelectDir = iota + SelectSend // case Chan <- Send + SelectRecv // case <-Chan: + SelectDefault // default +) + +// A SelectCase describes a single case in a select operation. +// The kind of case depends on Dir, the communication direction. +// +// If Dir is SelectDefault, the case represents a default case. +// Chan and Send must be zero Values. +// +// If Dir is SelectSend, the case represents a send operation. +// Normally Chan's underlying value must be a channel, and Send's underlying value must be +// assignable to the channel's element type. As a special case, if Chan is a zero Value, +// then the case is ignored, and the field Send will also be ignored and may be either zero +// or non-zero. +// +// If Dir is SelectRecv, the case represents a receive operation. +// Normally Chan's underlying value must be a channel and Send must be a zero Value. +// If Chan is a zero Value, then the case is ignored, but Send must still be a zero Value. +// When a receive operation is selected, the received Value is returned by Select. +// +type SelectCase struct { + Dir SelectDir // direction of case + Chan Value // channel to use (for send or receive) + Send Value // value to send (for send) +} + +// Select executes a select operation described by the list of cases. +// Like the Go select statement, it blocks until at least one of the cases +// can proceed, makes a uniform pseudo-random choice, +// and then executes that case. It returns the index of the chosen case +// and, if that case was a receive operation, the value received and a +// boolean indicating whether the value corresponds to a send on the channel +// (as opposed to a zero value received because the channel is closed). +func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) { + // NOTE: Do not trust that caller is not modifying cases data underfoot. + // The range is safe because the caller cannot modify our copy of the len + // and each iteration makes its own copy of the value c. + runcases := make([]runtimeSelect, len(cases)) + haveDefault := false + for i, c := range cases { + rc := &runcases[i] + rc.dir = uintptr(c.Dir) + switch c.Dir { + default: + panic("reflect.Select: invalid Dir") + + case SelectDefault: // default + if haveDefault { + panic("reflect.Select: multiple default cases") + } + haveDefault = true + if c.Chan.IsValid() { + panic("reflect.Select: default case has Chan value") + } + if c.Send.IsValid() { + panic("reflect.Select: default case has Send value") + } + + case SelectSend: + ch := c.Chan + if !ch.IsValid() { + break + } + ch.mustBe(Chan) + ch.mustBeExported() + tt := (*chanType)(unsafe.Pointer(ch.typ)) + if ChanDir(tt.dir)&SendDir == 0 { + panic("reflect.Select: SendDir case using recv-only channel") + } + rc.ch = ch.iword() + rc.typ = &tt.rtype + v := c.Send + if !v.IsValid() { + panic("reflect.Select: SendDir case missing Send value") + } + v.mustBeExported() + v = v.assignTo("reflect.Select", tt.elem, nil) + rc.val = v.iword() + + case SelectRecv: + if c.Send.IsValid() { + panic("reflect.Select: RecvDir case has Send value") + } + ch := c.Chan + if !ch.IsValid() { + break + } + ch.mustBe(Chan) + ch.mustBeExported() + tt := (*chanType)(unsafe.Pointer(ch.typ)) + rc.typ = &tt.rtype + if ChanDir(tt.dir)&RecvDir == 0 { + panic("reflect.Select: RecvDir case using send-only channel") + } + rc.ch = ch.iword() + } + } + + chosen, word, recvOK := rselect(runcases) + if runcases[chosen].dir == uintptr(SelectRecv) { + tt := (*chanType)(unsafe.Pointer(runcases[chosen].typ)) + typ := tt.elem + fl := flag(typ.Kind()) << flagKindShift + if typ.size > ptrSize { + fl |= flagIndir + } + recv = Value{typ, unsafe.Pointer(word), fl} + } + return chosen, recv, recvOK +} + /* * constructors */ // implemented in package runtime -func unsafe_New(Type) unsafe.Pointer -func unsafe_NewArray(Type, int) unsafe.Pointer +func unsafe_New(*rtype) unsafe.Pointer +func unsafe_NewArray(*rtype, int) unsafe.Pointer // MakeSlice creates a new zero-initialized slice value // for the specified slice type, length, and capacity. @@ -1643,11 +1920,11 @@ func MakeSlice(typ Type, len, cap int) Value { } // Declare slice so that gc can see the base pointer in it. - var x []byte + var x []unsafe.Pointer // Reinterpret as *SliceHeader to edit. s := (*SliceHeader)(unsafe.Pointer(&x)) - s.Data = uintptr(unsafe_NewArray(typ.Elem(), cap)) + s.Data = uintptr(unsafe_NewArray(typ.Elem().(*rtype), cap)) s.Len = len s.Cap = cap @@ -1665,7 +1942,7 @@ func MakeChan(typ Type, buffer int) Value { if typ.ChanDir() != BothDir { panic("reflect.MakeChan: unidirectional channel type") } - ch := makechan(typ.runtimeType(), uint32(buffer)) + ch := makechan(typ.(*rtype), uint64(buffer)) return Value{typ.common(), unsafe.Pointer(ch), flag(Chan) << flagKindShift} } @@ -1674,7 +1951,7 @@ func MakeMap(typ Type) Value { if typ.Kind() != Map { panic("reflect.MakeMap of non-map type") } - m := makemap(typ.runtimeType()) + m := makemap(typ.(*rtype)) return Value{typ.common(), unsafe.Pointer(m), flag(Map) << flagKindShift} } @@ -1705,7 +1982,7 @@ func ValueOf(i interface{}) Value { // For an interface value with the noAddr bit set, // the representation is identical to an empty interface. eface := *(*emptyInterface)(unsafe.Pointer(&i)) - typ := toCommonType(eface.typ) + typ := eface.typ fl := flag(typ.Kind()) << flagKindShift if typ.size > ptrSize { fl |= flagIndir @@ -1713,10 +1990,11 @@ func ValueOf(i interface{}) Value { return Value{typ, unsafe.Pointer(eface.word), fl} } -// Zero returns a Value representing a zero value for the specified type. +// Zero returns a Value representing the zero value for the specified type. // The result is different from the zero value of the Value struct, // which represents no value at all. // For example, Zero(TypeOf(42)) returns a Value with Kind Int and value 0. +// The returned value is neither addressable nor settable. func Zero(typ Type) Value { if typ == nil { panic("reflect: Zero(nil)") @@ -1726,7 +2004,7 @@ func Zero(typ Type) Value { if t.size <= ptrSize { return Value{t, nil, fl} } - return Value{t, unsafe_New(typ), fl | flagIndir} + return Value{t, unsafe_New(typ.(*rtype)), fl | flagIndir} } // New returns a Value representing a pointer to a new zero value @@ -1735,7 +2013,7 @@ func New(typ Type) Value { if typ == nil { panic("reflect: New(nil)") } - ptr := unsafe_New(typ) + ptr := unsafe_New(typ.(*rtype)) fl := flag(Ptr) << flagKindShift return Value{typ.common().ptrTo(), ptr, fl} } @@ -1750,7 +2028,7 @@ func NewAt(typ Type, p unsafe.Pointer) Value { // assignTo returns a value v that can be assigned directly to typ. // It panics if v is not assignable to typ. // For a conversion to an interface type, target is a suggested scratch space to use. -func (v Value) assignTo(context string, dst *commonType, target *interface{}) Value { +func (v Value) assignTo(context string, dst *rtype, target *interface{}) Value { if v.flag&flagMethod != 0 { panic(context + ": cannot assign method value to type " + dst.String()) } @@ -1772,7 +2050,7 @@ func (v Value) assignTo(context string, dst *commonType, target *interface{}) Va if dst.NumMethod() == 0 { *target = x } else { - ifaceE2I(dst.runtimeType(), x, unsafe.Pointer(target)) + ifaceE2I(dst, x, unsafe.Pointer(target)) } return Value{dst, unsafe.Pointer(target), flagIndir | flag(Interface)< ptrSize { + // Assume ptrSize >= 4, so this must be uint64. + ptr := unsafe_New(typ) + *(*uint64)(unsafe.Pointer(ptr)) = bits + return Value{typ, ptr, f | flag(typ.Kind())< ptrSize { + // Assume ptrSize >= 4, so this must be float64. + ptr := unsafe_New(typ) + *(*float64)(unsafe.Pointer(ptr)) = v + return Value{typ, ptr, f | flag(typ.Kind())< ptrSize { + ptr := unsafe_New(typ) + switch typ.size { + case 8: + *(*complex64)(unsafe.Pointer(ptr)) = complex64(v) + case 16: + *(*complex128)(unsafe.Pointer(ptr)) = v + } + return Value{typ, ptr, f | flag(typ.Kind())< [u]intXX +func cvtInt(v Value, t Type) Value { + return makeInt(v.flag&flagRO, uint64(v.Int()), t) +} + +// convertOp: uintXX -> [u]intXX +func cvtUint(v Value, t Type) Value { + return makeInt(v.flag&flagRO, v.Uint(), t) +} + +// convertOp: floatXX -> intXX +func cvtFloatInt(v Value, t Type) Value { + return makeInt(v.flag&flagRO, uint64(int64(v.Float())), t) +} + +// convertOp: floatXX -> uintXX +func cvtFloatUint(v Value, t Type) Value { + return makeInt(v.flag&flagRO, uint64(v.Float()), t) +} + +// convertOp: intXX -> floatXX +func cvtIntFloat(v Value, t Type) Value { + return makeFloat(v.flag&flagRO, float64(v.Int()), t) +} + +// convertOp: uintXX -> floatXX +func cvtUintFloat(v Value, t Type) Value { + return makeFloat(v.flag&flagRO, float64(v.Uint()), t) +} + +// convertOp: floatXX -> floatXX +func cvtFloat(v Value, t Type) Value { + return makeFloat(v.flag&flagRO, v.Float(), t) +} + +// convertOp: complexXX -> complexXX +func cvtComplex(v Value, t Type) Value { + return makeComplex(v.flag&flagRO, v.Complex(), t) +} + +// convertOp: intXX -> string +func cvtIntString(v Value, t Type) Value { + return makeString(v.flag&flagRO, string(v.Int()), t) +} + +// convertOp: uintXX -> string +func cvtUintString(v Value, t Type) Value { + return makeString(v.flag&flagRO, string(v.Uint()), t) +} + +// convertOp: []byte -> string +func cvtBytesString(v Value, t Type) Value { + return makeString(v.flag&flagRO, string(v.Bytes()), t) +} + +// convertOp: string -> []byte +func cvtStringBytes(v Value, t Type) Value { + return makeBytes(v.flag&flagRO, []byte(v.String()), t) +} + +// convertOp: []rune -> string +func cvtRunesString(v Value, t Type) Value { + return makeString(v.flag&flagRO, string(v.runes()), t) +} + +// convertOp: string -> []rune +func cvtStringRunes(v Value, t Type) Value { + return makeRunes(v.flag&flagRO, []rune(v.String()), t) +} + +// convertOp: direct copy +func cvtDirect(v Value, typ Type) Value { + f := v.flag + t := typ.common() + val := v.val + if f&flagAddr != 0 { + // indirect, mutable word - make a copy + ptr := unsafe_New(t) + memmove(ptr, val, t.size) + val = ptr + f &^= flagAddr + } + return Value{t, val, v.flag&flagRO | f} +} + +// convertOp: concrete -> interface +func cvtT2I(v Value, typ Type) Value { + target := new(interface{}) + x := valueInterface(v, false) + if typ.NumMethod() == 0 { + *target = x + } else { + ifaceE2I(typ.(*rtype), x, unsafe.Pointer(target)) + } + return Value{typ.common(), unsafe.Pointer(target), v.flag&flagRO | flagIndir | flag(Interface)< interface +func cvtI2I(v Value, typ Type) Value { + if v.IsNil() { + ret := Zero(typ) + ret.flag |= v.flag & flagRO + return ret + } + return cvtT2I(v.Elem(), typ) +} + // implemented in ../pkg/runtime -func chancap(ch iword) int32 +func chancap(ch iword) int func chanclose(ch iword) -func chanlen(ch iword) int32 -func chanrecv(t *runtimeType, ch iword, nb bool) (val iword, selected, received bool) -func chansend(t *runtimeType, ch iword, val iword, nb bool) bool - -func makechan(typ *runtimeType, size uint32) (ch iword) -func makemap(t *runtimeType) (m iword) -func mapaccess(t *runtimeType, m iword, key iword) (val iword, ok bool) -func mapassign(t *runtimeType, m iword, key, val iword, ok bool) -func mapiterinit(t *runtimeType, m iword) *byte +func chanlen(ch iword) int +func chanrecv(t *rtype, ch iword, nb bool) (val iword, selected, received bool) +func chansend(t *rtype, ch iword, val iword, nb bool) bool + +func makechan(typ *rtype, size uint64) (ch iword) +func makemap(t *rtype) (m iword) +func mapaccess(t *rtype, m iword, key iword) (val iword, ok bool) +func mapassign(t *rtype, m iword, key, val iword, ok bool) +func mapiterinit(t *rtype, m iword) *byte func mapiterkey(it *byte) (key iword, ok bool) func mapiternext(it *byte) -func maplen(m iword) int32 +func maplen(m iword) int func call(fn, arg unsafe.Pointer, n uint32) -func ifaceE2I(t *runtimeType, src interface{}, dst unsafe.Pointer) +func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) // Dummy annotation marking that the value x escapes, // for use in cases where the reflect code is so clever that diff --git a/src/pkg/regexp/all_test.go b/src/pkg/regexp/all_test.go index f7b41a674..9c4d64f58 100644 --- a/src/pkg/regexp/all_test.go +++ b/src/pkg/regexp/all_test.go @@ -5,6 +5,7 @@ package regexp import ( + "reflect" "strings" "testing" ) @@ -29,53 +30,52 @@ var good_re = []string{ `\!\\`, } -/* type stringError struct { re string - err error + err string } var bad_re = []stringError{ - {`*`, ErrBareClosure}, - {`+`, ErrBareClosure}, - {`?`, ErrBareClosure}, - {`(abc`, ErrUnmatchedLpar}, - {`abc)`, ErrUnmatchedRpar}, - {`x[a-z`, ErrUnmatchedLbkt}, - {`abc]`, ErrUnmatchedRbkt}, - {`[z-a]`, ErrBadRange}, - {`abc\`, ErrExtraneousBackslash}, - {`a**`, ErrBadClosure}, - {`a*+`, ErrBadClosure}, - {`a??`, ErrBadClosure}, - {`\x`, ErrBadBackslash}, -} -*/ - -func compileTest(t *testing.T, expr string, error error) *Regexp { + {`*`, "missing argument to repetition operator: `*`"}, + {`+`, "missing argument to repetition operator: `+`"}, + {`?`, "missing argument to repetition operator: `?`"}, + {`(abc`, "missing closing ): `(abc`"}, + {`abc)`, "unexpected ): `abc)`"}, + {`x[a-z`, "missing closing ]: `[a-z`"}, + {`[z-a]`, "invalid character class range: `z-a`"}, + {`abc\`, "trailing backslash at end of expression"}, + {`a**`, "invalid nested repetition operator: `**`"}, + {`a*+`, "invalid nested repetition operator: `*+`"}, + {`\x`, "invalid escape sequence: `\\x`"}, +} + +func compileTest(t *testing.T, expr string, error string) *Regexp { re, err := Compile(expr) - if err != error { + if error == "" && err != nil { t.Error("compiling `", expr, "`; unexpected error: ", err.Error()) } + if error != "" && err == nil { + t.Error("compiling `", expr, "`; missing error") + } else if error != "" && !strings.Contains(err.Error(), error) { + t.Error("compiling `", expr, "`; wrong error: ", err.Error(), "; want ", error) + } return re } func TestGoodCompile(t *testing.T) { for i := 0; i < len(good_re); i++ { - compileTest(t, good_re[i], nil) + compileTest(t, good_re[i], "") } } -/* func TestBadCompile(t *testing.T) { for i := 0; i < len(bad_re); i++ { compileTest(t, bad_re[i].re, bad_re[i].err) } } -*/ func matchTest(t *testing.T, test *FindTest) { - re := compileTest(t, test.pat, nil) + re := compileTest(t, test.pat, "") if re == nil { return } @@ -196,6 +196,10 @@ var replaceTests = []ReplaceTest{ {"a+", "${oops", "aaa", "${oops"}, {"a+", "$$", "aaa", "$"}, {"a+", "$", "aaa", "$"}, + + // Substitution when subexpression isn't found + {"(x)?", "$1", "123", "123"}, + {"abc", "$1", "123", "123"}, } var replaceLiteralTests = []ReplaceTest{ @@ -416,6 +420,59 @@ func TestSubexp(t *testing.T) { } } +var splitTests = []struct { + s string + r string + n int + out []string +}{ + {"foo:and:bar", ":", -1, []string{"foo", "and", "bar"}}, + {"foo:and:bar", ":", 1, []string{"foo:and:bar"}}, + {"foo:and:bar", ":", 2, []string{"foo", "and:bar"}}, + {"foo:and:bar", "foo", -1, []string{"", ":and:bar"}}, + {"foo:and:bar", "bar", -1, []string{"foo:and:", ""}}, + {"foo:and:bar", "baz", -1, []string{"foo:and:bar"}}, + {"baabaab", "a", -1, []string{"b", "", "b", "", "b"}}, + {"baabaab", "a*", -1, []string{"b", "b", "b"}}, + {"baabaab", "ba*", -1, []string{"", "", "", ""}}, + {"foobar", "f*b*", -1, []string{"", "o", "o", "a", "r"}}, + {"foobar", "f+.*b+", -1, []string{"", "ar"}}, + {"foobooboar", "o{2}", -1, []string{"f", "b", "boar"}}, + {"a,b,c,d,e,f", ",", 3, []string{"a", "b", "c,d,e,f"}}, + {"a,b,c,d,e,f", ",", 0, nil}, + {",", ",", -1, []string{"", ""}}, + {",,,", ",", -1, []string{"", "", "", ""}}, + {"", ",", -1, []string{""}}, + {"", ".*", -1, []string{""}}, + {"", ".+", -1, []string{""}}, + {"", "", -1, []string{}}, + {"foobar", "", -1, []string{"f", "o", "o", "b", "a", "r"}}, + {"abaabaccadaaae", "a*", 5, []string{"", "b", "b", "c", "cadaaae"}}, + {":x:y:z:", ":", -1, []string{"", "x", "y", "z", ""}}, +} + +func TestSplit(t *testing.T) { + for i, test := range splitTests { + re, err := Compile(test.r) + if err != nil { + t.Errorf("#%d: %q: compile error: %s", i, test.r, err.Error()) + continue + } + + split := re.Split(test.s, test.n) + if !reflect.DeepEqual(split, test.out) { + t.Errorf("#%d: %q: got %q; want %q", i, test.r, split, test.out) + } + + if QuoteMeta(test.r) == test.r { + strsplit := strings.SplitN(test.s, test.r, test.n) + if !reflect.DeepEqual(split, strsplit) { + t.Errorf("#%d: Split(%q, %q, %d): regexp vs strings mismatch\nregexp=%q\nstrings=%q", i, test.s, test.r, test.n, split, strsplit) + } + } + } +} + func BenchmarkLiteral(b *testing.B) { x := strings.Repeat("x", 50) + "y" b.StopTimer() diff --git a/src/pkg/regexp/example_test.go b/src/pkg/regexp/example_test.go new file mode 100644 index 000000000..b0ad9d340 --- /dev/null +++ b/src/pkg/regexp/example_test.go @@ -0,0 +1,144 @@ +package regexp_test + +import ( + "fmt" + "regexp" +) + +func Example() { + // Compile the expression once, usually at init time. + // Use raw strings to avoid having to quote the backslashes. + var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`) + + fmt.Println(validID.MatchString("adam[23]")) + fmt.Println(validID.MatchString("eve[7]")) + fmt.Println(validID.MatchString("Job[48]")) + fmt.Println(validID.MatchString("snakey")) + // Output: + // true + // true + // false + // false +} + +func ExampleMatchString() { + matched, err := regexp.MatchString("foo.*", "seafood") + fmt.Println(matched, err) + matched, err = regexp.MatchString("bar.*", "seafood") + fmt.Println(matched, err) + matched, err = regexp.MatchString("a(b", "seafood") + fmt.Println(matched, err) + // Output: + // true + // false + // false error parsing regexp: missing closing ): `a(b` +} + +func ExampleRegexp_FindString() { + re := regexp.MustCompile("fo.?") + fmt.Printf("%q\n", re.FindString("seafood")) + fmt.Printf("%q\n", re.FindString("meat")) + // Output: + // "foo" + // "" +} + +func ExampleRegexp_FindStringIndex() { + re := regexp.MustCompile("ab?") + fmt.Println(re.FindStringIndex("tablett")) + fmt.Println(re.FindStringIndex("foo") == nil) + // Output: + // [1 3] + // true +} + +func ExampleRegexp_FindStringSubmatch() { + re := regexp.MustCompile("a(x*)b(y|z)c") + fmt.Printf("%q\n", re.FindStringSubmatch("-axxxbyc-")) + fmt.Printf("%q\n", re.FindStringSubmatch("-abzc-")) + // Output: + // ["axxxbyc" "xxx" "y"] + // ["abzc" "" "z"] +} + +func ExampleRegexp_FindAllString() { + re := regexp.MustCompile("a.") + fmt.Println(re.FindAllString("paranormal", -1)) + fmt.Println(re.FindAllString("paranormal", 2)) + fmt.Println(re.FindAllString("graal", -1)) + fmt.Println(re.FindAllString("none", -1)) + // Output: + // [ar an al] + // [ar an] + // [aa] + // [] +} + +func ExampleRegexp_FindAllStringSubmatch() { + re := regexp.MustCompile("a(x*)b") + fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-", -1)) + fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-", -1)) + fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-axb-", -1)) + fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-ab-", -1)) + // Output: + // [["ab" ""]] + // [["axxb" "xx"]] + // [["ab" ""] ["axb" "x"]] + // [["axxb" "xx"] ["ab" ""]] +} + +func ExampleRegexp_FindAllStringSubmatchIndex() { + re := regexp.MustCompile("a(x*)b") + // Indices: + // 01234567 012345678 + // -ab-axb- -axxb-ab- + fmt.Println(re.FindAllStringSubmatchIndex("-ab-", -1)) + fmt.Println(re.FindAllStringSubmatchIndex("-axxb-", -1)) + fmt.Println(re.FindAllStringSubmatchIndex("-ab-axb-", -1)) + fmt.Println(re.FindAllStringSubmatchIndex("-axxb-ab-", -1)) + fmt.Println(re.FindAllStringSubmatchIndex("-foo-", -1)) + // Output: + // [[1 3 2 2]] + // [[1 5 2 4]] + // [[1 3 2 2] [4 7 5 6]] + // [[1 5 2 4] [6 8 7 7]] + // [] +} + +func ExampleRegexp_ReplaceAllLiteralString() { + re := regexp.MustCompile("a(x*)b") + fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "T")) + fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "$1")) + fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "${1}")) + // Output: + // -T-T- + // -$1-$1- + // -${1}-${1}- +} + +func ExampleRegexp_ReplaceAllString() { + re := regexp.MustCompile("a(x*)b") + fmt.Println(re.ReplaceAllString("-ab-axxb-", "T")) + fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1")) + fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1W")) + fmt.Println(re.ReplaceAllString("-ab-axxb-", "${1}W")) + // Output: + // -T-T- + // --xx- + // --- + // -W-xxW- +} + +func ExampleRegexp_SubexpNames() { + re := regexp.MustCompile("(?P[a-zA-Z]+) (?P[a-zA-Z]+)") + fmt.Println(re.MatchString("Alan Turing")) + fmt.Printf("%q\n", re.SubexpNames()) + reversed := fmt.Sprintf("${%s} ${%s}", re.SubexpNames()[2], re.SubexpNames()[1]) + fmt.Println(reversed) + fmt.Println(re.ReplaceAllString("Alan Turing", reversed)) + // Output: + // true + // ["" "first" "last"] + // ${last} ${first} + // Turing Alan +} diff --git a/src/pkg/regexp/exec_test.go b/src/pkg/regexp/exec_test.go index e668574a5..9dfaed713 100644 --- a/src/pkg/regexp/exec_test.go +++ b/src/pkg/regexp/exec_test.go @@ -69,8 +69,7 @@ func TestRE2Search(t *testing.T) { func TestRE2Exhaustive(t *testing.T) { if testing.Short() { - t.Log("skipping TestRE2Exhaustive during short test") - return + t.Skip("skipping TestRE2Exhaustive during short test") } testRE2(t, "testdata/re2-exhaustive.txt.bz2") } @@ -90,7 +89,7 @@ func testRE2(t *testing.T, file string) { txt = f } lineno := 0 - r := bufio.NewReader(txt) + scanner := bufio.NewScanner(txt) var ( str []string input []string @@ -100,16 +99,8 @@ func testRE2(t *testing.T, file string) { nfail int ncase int ) - for { - line, err := r.ReadString('\n') - if err != nil { - if err == io.EOF { - break - } - t.Fatalf("%s:%d: %v", file, lineno, err) - } - line = line[:len(line)-1] // chop \n - lineno++ + for lineno := 1; scanner.Scan(); lineno++ { + line := scanner.Text() switch { case line == "": t.Fatalf("%s:%d: unexpected blank line", file, lineno) @@ -205,6 +196,9 @@ func testRE2(t *testing.T, file string) { t.Fatalf("%s:%d: out of sync: %s\n", file, lineno, line) } } + if err := scanner.Err(); err != nil { + t.Fatalf("%s:%d: %v", file, lineno, err) + } if len(input) != 0 { t.Fatalf("%s:%d: out of sync: have %d strings left at EOF", file, lineno, len(input)) } @@ -405,14 +399,14 @@ Reading: // implementation. If the first character is not [BEASKLP] then the // specification is a global control line. One or more of [BEASKLP] may be // specified; the test will be repeated for each mode. - // + // // B basic BRE (grep, ed, sed) // E REG_EXTENDED ERE (egrep) // A REG_AUGMENTED ARE (egrep with negation) // S REG_SHELL SRE (sh glob) // K REG_SHELL|REG_AUGMENTED KRE (ksh glob) // L REG_LITERAL LRE (fgrep) - // + // // a REG_LEFT|REG_RIGHT implicit ^...$ // b REG_NOTBOL lhs does not match ^ // c REG_COMMENT ignore space and #...\n @@ -442,23 +436,23 @@ Reading: // $ expand C \c escapes in fields 2 and 3 // / field 2 is a regsubcomp() expression // = field 3 is a regdecomp() expression - // + // // Field 1 control lines: - // + // // C set LC_COLLATE and LC_CTYPE to locale in field 2 - // + // // ?test ... output field 5 if passed and != EXPECTED, silent otherwise // &test ... output field 5 if current and previous passed // |test ... output field 5 if current passed and previous failed // ; ... output field 2 if previous failed // {test ... skip if failed until } // } end of skip - // + // // : comment comment copied as output NOTE // :comment:test :comment: ignored // N[OTE] comment comment copied as output NOTE // T[EST] comment comment - // + // // number use number for nmatch (20 by default) flag := field[0] switch flag[0] { @@ -501,7 +495,7 @@ Reading: // Field 2: the regular expression pattern; SAME uses the pattern from // the previous specification. - // + // if field[1] == "SAME" { field[1] = lastRegexp } @@ -707,3 +701,17 @@ func BenchmarkMatchHard_1K(b *testing.B) { benchmark(b, hard, 1<<10) } func BenchmarkMatchHard_32K(b *testing.B) { benchmark(b, hard, 32<<10) } func BenchmarkMatchHard_1M(b *testing.B) { benchmark(b, hard, 1<<20) } func BenchmarkMatchHard_32M(b *testing.B) { benchmark(b, hard, 32<<20) } + +func TestLongest(t *testing.T) { + re, err := Compile(`a(|b)`) + if err != nil { + t.Fatal(err) + } + if g, w := re.FindString("ab"), "a"; g != w { + t.Errorf("first match was %q, want %q", g, w) + } + re.Longest() + if g, w := re.FindString("ab"), "ab"; g != w { + t.Errorf("longest match was %q, want %q", g, w) + } +} diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go index 87e6b1c61..3aa16dec6 100644 --- a/src/pkg/regexp/regexp.go +++ b/src/pkg/regexp/regexp.go @@ -130,6 +130,14 @@ func CompilePOSIX(expr string) (*Regexp, error) { return compile(expr, syntax.POSIX, true) } +// Longest makes future searches prefer the leftmost-longest match. +// That is, when matching against text, the regexp returns a match that +// begins as early as possible in the input (leftmost), and among those +// it chooses a match that is as long as possible. +func (re *Regexp) Longest() { + re.longest = true +} + func compile(expr string, mode syntax.Flags, longest bool) (*Regexp, error) { re, err := syntax.Parse(expr, mode) if err != nil { @@ -441,7 +449,7 @@ func (re *Regexp) ReplaceAllLiteralString(src, repl string) string { } // ReplaceAllStringFunc returns a copy of src in which all matches of the -// Regexp have been replaced by the return value of of function repl applied +// Regexp have been replaced by the return value of function repl applied // to the matched substring. The replacement returned by repl is substituted // directly, without using Expand. func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string { @@ -539,7 +547,7 @@ func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte { } // ReplaceAllFunc returns a copy of src in which all matches of the -// Regexp have been replaced by the return value of of function repl applied +// Regexp have been replaced by the return value of function repl applied // to the matched byte slice. The replacement returned by repl is substituted // directly, without using Expand. func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte { @@ -686,8 +694,9 @@ func (re *Regexp) FindStringIndex(s string) (loc []int) { // FindReaderIndex returns a two-element slice of integers defining the // location of the leftmost match of the regular expression in text read from -// the RuneReader. The match itself is at s[loc[0]:loc[1]]. A return -// value of nil indicates no match. +// the RuneReader. The match text was found in the input stream at +// byte offset loc[0] through loc[1]-1. +// A return value of nil indicates no match. func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) { a := re.doExecute(r, nil, "", 0, 2) if a == nil { @@ -719,7 +728,7 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte { // append, Expand replaces variables in the template with corresponding // matches drawn from src. The match slice should have been returned by // FindSubmatchIndex. -// +// // In the template, a variable is denoted by a substring of the form // $name or ${name}, where name is a non-empty sequence of letters, // digits, and underscores. A purely numeric name like $1 refers to @@ -727,10 +736,10 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte { // capturing parentheses named with the (?P...) syntax. A // reference to an out of range or unmatched index or a name that is not // present in the regular expression is replaced with an empty slice. -// +// // In the $name form, name is taken to be as long as possible: $1x is // equivalent to ${1x}, not ${1}x, and, $10 is equivalent to ${10}, not ${1}0. -// +// // To insert a literal $ in the output, use $$ in the template. func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte { return re.expand(dst, string(template), src, "", match) @@ -766,7 +775,7 @@ func (re *Regexp) expand(dst []byte, template string, bsrc []byte, src string, m } template = rest if num >= 0 { - if 2*num+1 < len(match) { + if 2*num+1 < len(match) && match[2*num] >= 0 { if bsrc != nil { dst = append(dst, bsrc[match[2*num]:match[2*num+1]]...) } else { @@ -1047,3 +1056,52 @@ func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int { } return result } + +// Split slices s into substrings separated by the expression and returns a slice of +// the substrings between those expression matches. +// +// The slice returned by this method consists of all the substrings of s +// not contained in the slice returned by FindAllString. When called on an expression +// that contains no metacharacters, it is equivalent to strings.SplitN. +// +// Example: +// s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5) +// // s: ["", "b", "b", "c", "cadaaae"] +// +// The count determines the number of substrings to return: +// n > 0: at most n substrings; the last substring will be the unsplit remainder. +// n == 0: the result is nil (zero substrings) +// n < 0: all substrings +func (re *Regexp) Split(s string, n int) []string { + + if n == 0 { + return nil + } + + if len(re.expr) > 0 && len(s) == 0 { + return []string{""} + } + + matches := re.FindAllStringIndex(s, n) + strings := make([]string, 0, len(matches)) + + beg := 0 + end := 0 + for _, match := range matches { + if n > 0 && len(strings) >= n-1 { + break + } + + end = match[0] + if match[1] != 0 { + strings = append(strings, s[beg:end]) + } + beg = match[1] + } + + if end != len(s) { + strings = append(strings, s[beg:]) + } + + return strings +} diff --git a/src/pkg/regexp/syntax/compile.go b/src/pkg/regexp/syntax/compile.go index 41955bfc2..95f6f1569 100644 --- a/src/pkg/regexp/syntax/compile.go +++ b/src/pkg/regexp/syntax/compile.go @@ -10,10 +10,10 @@ import "unicode" // Because the pointers haven't been filled in yet, we can reuse their storage // to hold the list. It's kind of sleazy, but works well in practice. // See http://swtch.com/~rsc/regexp/regexp1.html for inspiration. -// +// // These aren't really pointers: they're integers, so we can reinterpret them // this way without using package unsafe. A value l denotes -// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1). +// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1). // l == 0 denotes the empty list, okay because we start every program // with a fail instruction, so we'll never want to point at its output link. type patchList uint32 diff --git a/src/pkg/regexp/syntax/doc.go b/src/pkg/regexp/syntax/doc.go new file mode 100644 index 000000000..843a6f6a4 --- /dev/null +++ b/src/pkg/regexp/syntax/doc.go @@ -0,0 +1,127 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// DO NOT EDIT. This file is generated by mksyntaxgo from the RE2 distribution. + +/* +Package syntax parses regular expressions into parse trees and compiles +parse trees into programs. Most clients of regular expressions will use the +facilities of package regexp (such as Compile and Match) instead of this package. + +Syntax + +The regular expression syntax understood by this package when parsing with the Perl flag is as follows. +Parts of the syntax can be disabled by passing alternate flags to Parse. + + +Single characters: + . any character, possibly including newline (flag s=true) + [xyz] character class + [^xyz] negated character class + \d Perl character class + \D negated Perl character class + [:alpha:] ASCII character class + [:^alpha:] negated ASCII character class + \pN Unicode character class (one-letter name) + \p{Greek} Unicode character class + \PN negated Unicode character class (one-letter name) + \P{Greek} negated Unicode character class + +Composites: + xy x followed by y + x|y x or y (prefer x) + +Repetitions: + x* zero or more x, prefer more + x+ one or more x, prefer more + x? zero or one x, prefer one + x{n,m} n or n+1 or ... or m x, prefer more + x{n,} n or more x, prefer more + x{n} exactly n x + x*? zero or more x, prefer fewer + x+? one or more x, prefer fewer + x?? zero or one x, prefer zero + x{n,m}? n or n+1 or ... or m x, prefer fewer + x{n,}? n or more x, prefer fewer + x{n}? exactly n x + +Grouping: + (re) numbered capturing group + (?Pre) named & numbered capturing group + (?:re) non-capturing group + (?flags) set flags within current group; non-capturing + (?flags:re) set flags during re; non-capturing + + Flag syntax is xyz (set) or -xyz (clear) or xy-z (set xy, clear z). The flags are: + + i case-insensitive (default false) + m multi-line mode: ^ and $ match begin/end line in addition to begin/end text (default false) + s let . match \n (default false) + U ungreedy: swap meaning of x* and x*?, x+ and x+?, etc (default false) + +Empty strings: + ^ at beginning of text or line (flag m=true) + $ at end of text (like \z not \Z) or line (flag m=true) + \A at beginning of text + \b at word boundary (\w on one side and \W, \A, or \z on the other) + \B not a word boundary + \z at end of text + +Escape sequences: + \a bell (== \007) + \f form feed (== \014) + \t horizontal tab (== \011) + \n newline (== \012) + \r carriage return (== \015) + \v vertical tab character (== \013) + \* literal *, for any punctuation character * + \123 octal character code (up to three digits) + \x7F hex character code (exactly two digits) + \x{10FFFF} hex character code + \Q...\E literal text ... even if ... has punctuation + +Character class elements: + x single character + A-Z character range (inclusive) + \d Perl character class + [:foo:] ASCII character class foo + \p{Foo} Unicode character class Foo + \pF Unicode character class F (one-letter name) + +Named character classes as character class elements: + [\d] digits (== \d) + [^\d] not digits (== \D) + [\D] not digits (== \D) + [^\D] not not digits (== \d) + [[:name:]] named ASCII class inside character class (== [:name:]) + [^[:name:]] named ASCII class inside negated character class (== [:^name:]) + [\p{Name}] named Unicode property inside character class (== \p{Name}) + [^\p{Name}] named Unicode property inside negated character class (== \P{Name}) + +Perl character classes: + \d digits (== [0-9]) + \D not digits (== [^0-9]) + \s whitespace (== [\t\n\f\r ]) + \S not whitespace (== [^\t\n\f\r ]) + \w word characters (== [0-9A-Za-z_]) + \W not word characters (== [^0-9A-Za-z_]) + +ASCII character classes: + [:alnum:] alphanumeric (== [0-9A-Za-z]) + [:alpha:] alphabetic (== [A-Za-z]) + [:ascii:] ASCII (== [\x00-\x7F]) + [:blank:] blank (== [\t ]) + [:cntrl:] control (== [\x00-\x1F\x7F]) + [:digit:] digits (== [0-9]) + [:graph:] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]) + [:lower:] lower case (== [a-z]) + [:print:] printable (== [ -~] == [ [:graph:]]) + [:punct:] punctuation (== [!-/:-@[-`{-~]) + [:space:] whitespace (== [\t\n\v\f\r ]) + [:upper:] upper case (== [A-Z]) + [:word:] word characters (== [0-9A-Za-z_]) + [:xdigit:] hex digit (== [0-9A-Fa-f]) + +*/ +package syntax diff --git a/src/pkg/regexp/syntax/parse.go b/src/pkg/regexp/syntax/parse.go index 4924e9453..30e0e8b7f 100644 --- a/src/pkg/regexp/syntax/parse.go +++ b/src/pkg/regexp/syntax/parse.go @@ -2,10 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package syntax parses regular expressions into parse trees and compiles -// parse trees into programs. Most clients of regular expressions will use -// the facilities of package regexp (such as Compile and Match) instead of -// this package. package syntax import ( @@ -46,11 +42,9 @@ const ( ErrMissingParen ErrorCode = "missing closing )" ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator" ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression" + ErrUnexpectedParen ErrorCode = "unexpected )" ) -// TODO: Export for Go 1.1. -const errUnexpectedParen ErrorCode = "unexpected )" - func (e ErrorCode) String() string { return string(e) } @@ -470,7 +464,7 @@ func (p *parser) factor(sub []*Regexp, flags Flags) []*Regexp { // Construct factored form: prefix(suffix1|suffix2|...) prefix := first for j := start; j < i; j++ { - reuse := j != start // prefix came from sub[start] + reuse := j != start // prefix came from sub[start] sub[j] = p.removeLeadingRegexp(sub[j], reuse) } suffix := p.collapse(sub[start:i], OpAlternate) // recurse @@ -1171,13 +1165,13 @@ func (p *parser) parseRightParen() error { n := len(p.stack) if n < 2 { - return &Error{errUnexpectedParen, p.wholeRegexp} + return &Error{ErrUnexpectedParen, p.wholeRegexp} } re1 := p.stack[n-1] re2 := p.stack[n-2] p.stack = p.stack[:n-2] if re2.Op != opLeftParen { - return &Error{errUnexpectedParen, p.wholeRegexp} + return &Error{ErrUnexpectedParen, p.wholeRegexp} } // Restore flags at time of paren. p.flags = re2.Flags diff --git a/src/pkg/runtime/alg.c b/src/pkg/runtime/alg.c index 36973eba3..ad85b43ae 100644 --- a/src/pkg/runtime/alg.c +++ b/src/pkg/runtime/alg.c @@ -19,13 +19,13 @@ runtime·memhash(uintptr *h, uintptr s, void *a) uintptr hash; b = a; - hash = M0; + hash = M0 ^ *h; while(s > 0) { hash = (hash ^ *b) * M1; b++; s--; } - *h = (*h ^ hash) * M1; + *h = hash; } void @@ -316,7 +316,7 @@ runtime·strhash(uintptr *h, uintptr s, void *a) void runtime·strequal(bool *eq, uintptr s, void *a, void *b) { - int32 alen; + intgo alen; USED(s); alen = ((String*)a)->len; @@ -324,6 +324,10 @@ runtime·strequal(bool *eq, uintptr s, void *a, void *b) *eq = false; return; } + if(((String*)a)->str == ((String*)b)->str) { + *eq = true; + return; + } runtime·memequal(eq, alen, ((String*)a)->str, ((String*)b)->str); } @@ -351,7 +355,7 @@ void runtime·interhash(uintptr *h, uintptr s, void *a) { USED(s); - *h = (*h ^ runtime·ifacehash(*(Iface*)a)) * M1; + *h = runtime·ifacehash(*(Iface*)a, *h ^ M0) * M1; } void @@ -385,7 +389,7 @@ void runtime·nilinterhash(uintptr *h, uintptr s, void *a) { USED(s); - *h = (*h ^ runtime·efacehash(*(Eface*)a)) * M1; + *h = runtime·efacehash(*(Eface*)a, *h ^ M0) * M1; } void @@ -469,10 +473,11 @@ void runtime·equal(Type *t, ...) { byte *x, *y; - bool *ret; + uintptr ret; x = (byte*)(&t+1); y = x + t->size; - ret = (bool*)(y + t->size); - t->alg->equal(ret, t->size, x, y); + ret = (uintptr)(y + t->size); + ret = ROUND(ret, Structrnd); + t->alg->equal((bool*)ret, t->size, x, y); } diff --git a/src/pkg/runtime/append_test.go b/src/pkg/runtime/append_test.go index b8552224e..36390181e 100644 --- a/src/pkg/runtime/append_test.go +++ b/src/pkg/runtime/append_test.go @@ -19,6 +19,67 @@ func BenchmarkAppend(b *testing.B) { } } +func benchmarkAppendBytes(b *testing.B, length int) { + b.StopTimer() + x := make([]byte, 0, N) + y := make([]byte, length) + b.StartTimer() + for i := 0; i < b.N; i++ { + x = x[0:0] + x = append(x, y...) + } +} + +func BenchmarkAppend1Byte(b *testing.B) { + benchmarkAppendBytes(b, 1) +} + +func BenchmarkAppend4Bytes(b *testing.B) { + benchmarkAppendBytes(b, 4) +} + +func BenchmarkAppend8Bytes(b *testing.B) { + benchmarkAppendBytes(b, 8) +} + +func BenchmarkAppend16Bytes(b *testing.B) { + benchmarkAppendBytes(b, 16) +} + +func BenchmarkAppend32Bytes(b *testing.B) { + benchmarkAppendBytes(b, 32) +} + +func benchmarkAppendStr(b *testing.B, str string) { + b.StopTimer() + x := make([]byte, 0, N) + b.StartTimer() + for i := 0; i < b.N; i++ { + x = x[0:0] + x = append(x, str...) + } +} + +func BenchmarkAppendStr1Byte(b *testing.B) { + benchmarkAppendStr(b, "1") +} + +func BenchmarkAppendStr4Bytes(b *testing.B) { + benchmarkAppendStr(b, "1234") +} + +func BenchmarkAppendStr8Bytes(b *testing.B) { + benchmarkAppendStr(b, "12345678") +} + +func BenchmarkAppendStr16Bytes(b *testing.B) { + benchmarkAppendStr(b, "1234567890123456") +} + +func BenchmarkAppendStr32Bytes(b *testing.B) { + benchmarkAppendStr(b, "12345678901234567890123456789012") +} + func BenchmarkAppendSpecialCase(b *testing.B) { b.StopTimer() x := make([]int, 0, N) @@ -50,3 +111,13 @@ func TestSideEffectOrder(t *testing.T) { t.Error("append failed: ", x[0], x[1]) } } + +func TestAppendOverlap(t *testing.T) { + x := []byte("1234") + x = append(x[1:], x...) // p > q in runtime·appendslice. + got := string(x) + want := "2341234" + if got != want { + t.Errorf("overlap failed: got %q want %q", got, want) + } +} diff --git a/src/pkg/runtime/arch_386.h b/src/pkg/runtime/arch_386.h index a0798f99e..4df795f71 100644 --- a/src/pkg/runtime/arch_386.h +++ b/src/pkg/runtime/arch_386.h @@ -1,4 +1,6 @@ enum { thechar = '8', - CacheLineSize = 64 + BigEndian = 0, + CacheLineSize = 64, + appendCrossover = 16 }; diff --git a/src/pkg/runtime/arch_amd64.h b/src/pkg/runtime/arch_amd64.h index dd1cfc18d..e83dc9105 100644 --- a/src/pkg/runtime/arch_amd64.h +++ b/src/pkg/runtime/arch_amd64.h @@ -1,4 +1,6 @@ enum { thechar = '6', - CacheLineSize = 64 + BigEndian = 0, + CacheLineSize = 64, + appendCrossover = 16 }; diff --git a/src/pkg/runtime/arch_arm.h b/src/pkg/runtime/arch_arm.h index c1a7a0f37..f6af58514 100644 --- a/src/pkg/runtime/arch_arm.h +++ b/src/pkg/runtime/arch_arm.h @@ -1,4 +1,6 @@ enum { thechar = '5', - CacheLineSize = 32 + BigEndian = 0, + CacheLineSize = 32, + appendCrossover = 16 }; diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s index 21bd293ab..96f04e0ae 100644 --- a/src/pkg/runtime/asm_386.s +++ b/src/pkg/runtime/asm_386.s @@ -14,22 +14,22 @@ TEXT _rt0_386(SB),7,$0 MOVL BX, 124(SP) // set default stack bounds. - // initcgo may update stackguard. + // _cgo_init may update stackguard. MOVL $runtime·g0(SB), BP LEAL (-64*1024+104)(SP), BX MOVL BX, g_stackguard(BP) MOVL SP, g_stackbase(BP) - // if there is an initcgo, call it to let it + // if there is an _cgo_init, call it to let it // initialize and to set up GS. if not, // we set up GS ourselves. - MOVL initcgo(SB), AX + MOVL _cgo_init(SB), AX TESTL AX, AX JZ needtls PUSHL BP CALL AX POPL BP - // skip runtime·ldt0setup(SB) and tls test after initcgo for non-windows + // skip runtime·ldt0setup(SB) and tls test after _cgo_init for non-windows CMPL runtime·iswindows(SB), $0 JEQ ok needtls: @@ -75,7 +75,7 @@ ok: CALL runtime·schedinit(SB) // create a new goroutine to start program - PUSHL $runtime·main(SB) // entry + PUSHL $runtime·main·f(SB) // entry PUSHL $0 // arg size CALL runtime·newproc(SB) POPL AX @@ -87,6 +87,9 @@ ok: INT $3 RET +DATA runtime·main·f+0(SB)/4,$runtime·main(SB) +GLOBL runtime·main·f(SB),8,$4 + TEXT runtime·breakpoint(SB),7,$0 INT $3 RET @@ -131,22 +134,40 @@ TEXT runtime·gogo(SB), 7, $0 MOVL gobuf_pc(BX), BX JMP BX -// void gogocall(Gobuf*, void (*fn)(void)) +// void gogocall(Gobuf*, void (*fn)(void), uintptr r0) // restore state from Gobuf but then call fn. // (call fn, returning to state in Gobuf) TEXT runtime·gogocall(SB), 7, $0 + MOVL 12(SP), DX // context MOVL 8(SP), AX // fn MOVL 4(SP), BX // gobuf - MOVL gobuf_g(BX), DX + MOVL gobuf_g(BX), DI get_tls(CX) - MOVL DX, g(CX) - MOVL 0(DX), CX // make sure g != nil + MOVL DI, g(CX) + MOVL 0(DI), CX // make sure g != nil MOVL gobuf_sp(BX), SP // restore SP MOVL gobuf_pc(BX), BX PUSHL BX JMP AX POPL BX // not reached +// void gogocallfn(Gobuf*, FuncVal*) +// restore state from Gobuf but then call fn. +// (call fn, returning to state in Gobuf) +TEXT runtime·gogocallfn(SB), 7, $0 + MOVL 8(SP), DX // fn + MOVL 4(SP), BX // gobuf + MOVL gobuf_g(BX), DI + get_tls(CX) + MOVL DI, g(CX) + MOVL 0(DI), CX // make sure g != nil + MOVL gobuf_sp(BX), SP // restore SP + MOVL gobuf_pc(BX), BX + PUSHL BX + MOVL 0(DX), BX + JMP BX + POPL BX // not reached + // void mcall(void (*fn)(G*)) // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) @@ -189,11 +210,13 @@ TEXT runtime·morestack(SB),7,$0 CMPL g(CX), SI JNE 2(PC) INT $3 + + MOVL DX, m_cret(BX) - // frame size in DX + // frame size in DI // arg size in AX // Save in m. - MOVL DX, m_moreframesize(BX) + MOVL DI, m_moreframesize(BX) MOVL AX, m_moreargsize(BX) // Called from f. @@ -299,6 +322,33 @@ TEXT runtime·cas(SB), 7, $0 MOVL $1, AX RET +// bool runtime·cas64(uint64 *val, uint64 *old, uint64 new) +// Atomically: +// if(*val == *old){ +// *val = new; +// return 1; +// } else { +// *old = *val +// return 0; +// } +TEXT runtime·cas64(SB), 7, $0 + MOVL 4(SP), BP + MOVL 8(SP), SI + MOVL 0(SI), AX + MOVL 4(SI), DX + MOVL 12(SP), BX + MOVL 16(SP), CX + LOCK + CMPXCHG8B 0(BP) + JNZ cas64_fail + MOVL $1, AX + RET +cas64_fail: + MOVL AX, 0(SI) + MOVL DX, 4(SI) + MOVL $0, AX + RET + // bool casp(void **p, void *old, void *new) // Atomically: // if(*p == old){ @@ -357,17 +407,49 @@ TEXT runtime·atomicstore(SB), 7, $0 XCHGL AX, 0(BX) RET +// uint64 atomicload64(uint64 volatile* addr); +// so actually +// void atomicload64(uint64 *res, uint64 volatile *addr); +TEXT runtime·atomicload64(SB), 7, $0 + MOVL 4(SP), BX + MOVL 8(SP), AX + // MOVQ (%EAX), %MM0 + BYTE $0x0f; BYTE $0x6f; BYTE $0x00 + // MOVQ %MM0, 0(%EBX) + BYTE $0x0f; BYTE $0x7f; BYTE $0x03 + // EMMS + BYTE $0x0F; BYTE $0x77 + RET + +// void runtime·atomicstore64(uint64 volatile* addr, uint64 v); +TEXT runtime·atomicstore64(SB), 7, $0 + MOVL 4(SP), AX + // MOVQ and EMMS were introduced on the Pentium MMX. + // MOVQ 0x8(%ESP), %MM0 + BYTE $0x0f; BYTE $0x6f; BYTE $0x44; BYTE $0x24; BYTE $0x08 + // MOVQ %MM0, (%EAX) + BYTE $0x0f; BYTE $0x7f; BYTE $0x00 + // EMMS + BYTE $0x0F; BYTE $0x77 + // This is essentially a no-op, but it provides required memory fencing. + // It can be replaced with MFENCE, but MFENCE was introduced only on the Pentium4 (SSE2). + MOVL $0, AX + LOCK + XADDL AX, (SP) + RET + // void jmpdefer(fn, sp); // called from deferreturn. // 1. pop the caller // 2. sub 5 bytes from the callers return // 3. jmp to the argument TEXT runtime·jmpdefer(SB), 7, $0 - MOVL 4(SP), AX // fn + MOVL 4(SP), DX // fn MOVL 8(SP), BX // caller sp LEAL -4(BX), SP // caller sp after CALL SUBL $5, (SP) // return to CALL again - JMP AX // but first run the deferred function + MOVL 0(DX), BX + JMP BX // but first run the deferred function // Dummy function to use in saved gobuf.PC, // to match SP pointing at a return address. @@ -416,23 +498,49 @@ TEXT runtime·asmcgocall(SB),7,$0 RET // cgocallback(void (*fn)(void*), void *frame, uintptr framesize) -// See cgocall.c for more details. +// Turn the fn into a Go func (by taking its address) and call +// cgocallback_gofunc. TEXT runtime·cgocallback(SB),7,$12 - MOVL fn+0(FP), AX - MOVL frame+4(FP), BX - MOVL framesize+8(FP), DX + LEAL fn+0(FP), AX + MOVL AX, 0(SP) + MOVL frame+4(FP), AX + MOVL AX, 4(SP) + MOVL framesize+8(FP), AX + MOVL AX, 8(SP) + MOVL $runtime·cgocallback_gofunc(SB), AX + CALL AX + RET - // Save current m->g0->sched.sp on stack and then set it to SP. +// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) +// See cgocall.c for more details. +TEXT runtime·cgocallback_gofunc(SB),7,$12 + // If m is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call through AX. get_tls(CX) +#ifdef GOOS_windows + CMPL CX, $0 + JNE 3(PC) + PUSHL $0 + JMP needm +#endif MOVL m(CX), BP - - // If m is nil, it is almost certainly because we have been called - // on a thread that Go did not create. We're going to crash as - // soon as we try to use m; instead, try to print a nice error and exit. + PUSHL BP CMPL BP, $0 - JNE 2(PC) - CALL runtime·badcallback(SB) + JNE havem +needm: + MOVL $runtime·needm(SB), AX + CALL AX + get_tls(CX) + MOVL m(CX), BP +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. MOVL m_g0(BP), SI PUSHL (g_sched+gobuf_sp)(SI) MOVL SP, (g_sched+gobuf_sp)(SI) @@ -451,6 +559,10 @@ TEXT runtime·cgocallback(SB),7,$12 // a frame size of 12, the same amount that we use below), // so that the traceback will seamlessly trace back into // the earlier calls. + MOVL fn+0(FP), AX + MOVL frame+4(FP), BX + MOVL framesize+8(FP), DX + MOVL m_curg(BP), SI MOVL SI, g(CX) MOVL (g_sched+gobuf_sp)(SI), DI // prepare stack as DI @@ -488,10 +600,38 @@ TEXT runtime·cgocallback(SB),7,$12 MOVL SI, g(CX) MOVL (g_sched+gobuf_sp)(SI), SP POPL (g_sched+gobuf_sp)(SI) + + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + POPL BP + CMPL BP, $0 + JNE 3(PC) + MOVL $runtime·dropm(SB), AX + CALL AX // Done! RET +// void setmg(M*, G*); set m and g. for use by needm. +TEXT runtime·setmg(SB), 7, $0 +#ifdef GOOS_windows + MOVL mm+0(FP), AX + CMPL AX, $0 + JNE settls + MOVL $0, 0x14(FS) + RET +settls: + LEAL m_tls(AX), AX + MOVL AX, 0x14(FS) +#endif + MOVL mm+0(FP), AX + get_tls(CX) + MOVL mm+0(FP), AX + MOVL AX, m(CX) + MOVL gg+4(FP), BX + MOVL BX, g(CX) + RET + // check that SP is in range [g->stackbase, g->stackguard) TEXT runtime·stackcheck(SB), 7, $0 get_tls(CX) diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s index d41ab96d0..987958498 100644 --- a/src/pkg/runtime/asm_amd64.s +++ b/src/pkg/runtime/asm_amd64.s @@ -14,14 +14,14 @@ TEXT _rt0_amd64(SB),7,$-8 MOVQ BX, 24(SP) // create istack out of the given (operating system) stack. - // initcgo may update stackguard. + // _cgo_init may update stackguard. MOVQ $runtime·g0(SB), DI LEAQ (-64*1024+104)(SP), BX MOVQ BX, g_stackguard(DI) MOVQ SP, g_stackbase(DI) - // if there is an initcgo, call it. - MOVQ initcgo(SB), AX + // if there is an _cgo_init, call it. + MOVQ _cgo_init(SB), AX TESTQ AX, AX JZ needtls // g0 already in DI @@ -31,6 +31,10 @@ TEXT _rt0_amd64(SB),7,$-8 JEQ ok needtls: + // skip TLS setup on Plan 9 + CMPL runtime·isplan9(SB), $1 + JEQ ok + LEAQ runtime·tls0(SB), DI CALL runtime·settls(SB) @@ -64,7 +68,7 @@ ok: CALL runtime·schedinit(SB) // create a new goroutine to start program - PUSHQ $runtime·main(SB) // entry + PUSHQ $runtime·main·f(SB) // entry PUSHQ $0 // arg size CALL runtime·newproc(SB) POPQ AX @@ -76,6 +80,9 @@ ok: MOVL $0xf1, 0xf1 // crash RET +DATA runtime·main·f+0(SB)/8,$runtime·main(SB) +GLOBL runtime·main·f(SB),8,$8 + TEXT runtime·breakpoint(SB),7,$0 BYTE $0xcc RET @@ -114,22 +121,40 @@ TEXT runtime·gogo(SB), 7, $0 MOVQ gobuf_pc(BX), BX JMP BX -// void gogocall(Gobuf*, void (*fn)(void)) +// void gogocall(Gobuf*, void (*fn)(void), uintptr r0) // restore state from Gobuf but then call fn. // (call fn, returning to state in Gobuf) TEXT runtime·gogocall(SB), 7, $0 + MOVQ 24(SP), DX // context MOVQ 16(SP), AX // fn MOVQ 8(SP), BX // gobuf - MOVQ gobuf_g(BX), DX + MOVQ gobuf_g(BX), DI get_tls(CX) - MOVQ DX, g(CX) - MOVQ 0(DX), CX // make sure g != nil + MOVQ DI, g(CX) + MOVQ 0(DI), CX // make sure g != nil MOVQ gobuf_sp(BX), SP // restore SP MOVQ gobuf_pc(BX), BX PUSHQ BX JMP AX POPQ BX // not reached +// void gogocallfn(Gobuf*, FuncVal*) +// restore state from Gobuf but then call fn. +// (call fn, returning to state in Gobuf) +TEXT runtime·gogocallfn(SB), 7, $0 + MOVQ 16(SP), DX // fn + MOVQ 8(SP), BX // gobuf + MOVQ gobuf_g(BX), AX + get_tls(CX) + MOVQ AX, g(CX) + MOVQ 0(AX), CX // make sure g != nil + MOVQ gobuf_sp(BX), SP // restore SP + MOVQ gobuf_pc(BX), BX + PUSHQ BX + MOVQ 0(DX), BX + JMP BX + POPQ BX // not reached + // void mcall(void (*fn)(G*)) // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) @@ -171,6 +196,8 @@ TEXT runtime·morestack(SB),7,$0 CMPQ g(CX), SI JNE 2(PC) INT $3 + + MOVQ DX, m_cret(BX) // Called from f. // Set m->morebuf to f's caller. @@ -344,6 +371,30 @@ TEXT runtime·cas(SB), 7, $0 MOVL $1, AX RET +// bool runtime·cas64(uint64 *val, uint64 *old, uint64 new) +// Atomically: +// if(*val == *old){ +// *val = new; +// return 1; +// } else { +// *old = *val +// return 0; +// } +TEXT runtime·cas64(SB), 7, $0 + MOVQ 8(SP), BX + MOVQ 16(SP), BP + MOVQ 0(BP), AX + MOVQ 24(SP), CX + LOCK + CMPXCHGQ CX, 0(BX) + JNZ cas64_fail + MOVL $1, AX + RET +cas64_fail: + MOVQ AX, 0(BP) + MOVL $0, AX + RET + // bool casp(void **val, void *old, void *new) // Atomically: // if(*val == old){ @@ -376,6 +427,15 @@ TEXT runtime·xadd(SB), 7, $0 ADDL CX, AX RET +TEXT runtime·xadd64(SB), 7, $0 + MOVQ 8(SP), BX + MOVQ 16(SP), AX + MOVQ AX, CX + LOCK + XADDQ AX, 0(BX) + ADDQ CX, AX + RET + TEXT runtime·xchg(SB), 7, $0 MOVQ 8(SP), BX MOVL 16(SP), AX @@ -402,17 +462,24 @@ TEXT runtime·atomicstore(SB), 7, $0 XCHGL AX, 0(BX) RET +TEXT runtime·atomicstore64(SB), 7, $0 + MOVQ 8(SP), BX + MOVQ 16(SP), AX + XCHGQ AX, 0(BX) + RET + // void jmpdefer(fn, sp); // called from deferreturn. // 1. pop the caller // 2. sub 5 bytes from the callers return // 3. jmp to the argument TEXT runtime·jmpdefer(SB), 7, $0 - MOVQ 8(SP), AX // fn + MOVQ 8(SP), DX // fn MOVQ 16(SP), BX // caller sp LEAQ -8(BX), SP // caller sp after CALL SUBQ $5, (SP) // return to CALL again - JMP AX // but first run the deferred function + MOVQ 0(DX), BX + JMP BX // but first run the deferred function // Dummy function to use in saved gobuf.PC, // to match SP pointing at a return address. @@ -446,39 +513,67 @@ TEXT runtime·asmcgocall(SB),7,$0 MOVQ (g_sched+gobuf_sp)(SI), SP // Now on a scheduling stack (a pthread-created stack). - SUBQ $48, SP + // Make sure we have enough room for 4 stack-backed fast-call + // registers as per windows amd64 calling convention. + SUBQ $64, SP ANDQ $~15, SP // alignment for gcc ABI - MOVQ DI, 32(SP) // save g - MOVQ DX, 24(SP) // save SP + MOVQ DI, 48(SP) // save g + MOVQ DX, 40(SP) // save SP MOVQ BX, DI // DI = first argument in AMD64 ABI MOVQ BX, CX // CX = first argument in Win64 CALL AX // Restore registers, g, stack pointer. get_tls(CX) - MOVQ 32(SP), DI + MOVQ 48(SP), DI MOVQ DI, g(CX) - MOVQ 24(SP), SP + MOVQ 40(SP), SP RET // cgocallback(void (*fn)(void*), void *frame, uintptr framesize) -// See cgocall.c for more details. +// Turn the fn into a Go func (by taking its address) and call +// cgocallback_gofunc. TEXT runtime·cgocallback(SB),7,$24 - MOVQ fn+0(FP), AX - MOVQ frame+8(FP), BX - MOVQ framesize+16(FP), DX + LEAQ fn+0(FP), AX + MOVQ AX, 0(SP) + MOVQ frame+8(FP), AX + MOVQ AX, 8(SP) + MOVQ framesize+16(FP), AX + MOVQ AX, 16(SP) + MOVQ $runtime·cgocallback_gofunc(SB), AX + CALL AX + RET - // Save current m->g0->sched.sp on stack and then set it to SP. +// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) +// See cgocall.c for more details. +TEXT runtime·cgocallback_gofunc(SB),7,$24 + // If m is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call through AX. get_tls(CX) +#ifdef GOOS_windows + CMPQ CX, $0 + JNE 3(PC) + PUSHQ $0 + JMP needm +#endif MOVQ m(CX), BP - - // If m is nil, it is almost certainly because we have been called - // on a thread that Go did not create. We're going to crash as - // soon as we try to use m; instead, try to print a nice error and exit. + PUSHQ BP CMPQ BP, $0 - JNE 2(PC) - CALL runtime·badcallback(SB) + JNE havem +needm: + MOVQ $runtime·needm(SB), AX + CALL AX + get_tls(CX) + MOVQ m(CX), BP +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. MOVQ m_g0(BP), SI PUSHQ (g_sched+gobuf_sp)(SI) MOVQ SP, (g_sched+gobuf_sp)(SI) @@ -497,6 +592,10 @@ TEXT runtime·cgocallback(SB),7,$24 // a frame size of 24, the same amount that we use below), // so that the traceback will seamlessly trace back into // the earlier calls. + MOVQ fn+0(FP), AX + MOVQ frame+8(FP), BX + MOVQ framesize+16(FP), DX + MOVQ m_curg(BP), SI MOVQ SI, g(CX) MOVQ (g_sched+gobuf_sp)(SI), DI // prepare stack as DI @@ -534,10 +633,37 @@ TEXT runtime·cgocallback(SB),7,$24 MOVQ SI, g(CX) MOVQ (g_sched+gobuf_sp)(SI), SP POPQ (g_sched+gobuf_sp)(SI) + + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + POPQ BP + CMPQ BP, $0 + JNE 3(PC) + MOVQ $runtime·dropm(SB), AX + CALL AX // Done! RET +// void setmg(M*, G*); set m and g. for use by needm. +TEXT runtime·setmg(SB), 7, $0 + MOVQ mm+0(FP), AX +#ifdef GOOS_windows + CMPQ AX, $0 + JNE settls + MOVQ $0, 0x28(GS) + RET +settls: + LEAQ m_tls(AX), AX + MOVQ AX, 0x28(GS) +#endif + get_tls(CX) + MOVQ mm+0(FP), AX + MOVQ AX, m(CX) + MOVQ gg+8(FP), BX + MOVQ BX, g(CX) + RET + // check that SP is in range [g->stackbase, g->stackguard) TEXT runtime·stackcheck(SB), 7, $0 get_tls(CX) diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s index 423fda7a0..45b53541b 100644 --- a/src/pkg/runtime/asm_arm.s +++ b/src/pkg/runtime/asm_arm.s @@ -31,6 +31,13 @@ TEXT _rt0_arm(SB),7,$-4 MOVW R13, g_stackbase(g) BL runtime·emptyfunc(SB) // fault if stack check is wrong + // if there is an _cgo_init, call it. + MOVW _cgo_init(SB), R2 + CMP $0, R2 + MOVW.NE g, R0 // first argument of _cgo_init is g + BL.NE (R2) // will clobber R0-R3 + + BL runtime·checkgoarm(SB) BL runtime·check(SB) // saved argc, argv @@ -43,7 +50,7 @@ TEXT _rt0_arm(SB),7,$-4 BL runtime·schedinit(SB) // create a new goroutine to start program - MOVW $runtime·main(SB), R0 + MOVW $runtime·main·f(SB), R0 MOVW.W R0, -4(R13) MOVW $8, R0 MOVW.W R0, -4(R13) @@ -58,24 +65,25 @@ TEXT _rt0_arm(SB),7,$-4 MOVW $1234, R0 MOVW $1000, R1 MOVW R0, (R1) // fail hard - B runtime·_dep_dummy(SB) // Never reached - -// TODO(kaib): remove these once i actually understand how the linker removes symbols -// pull in dummy dependencies -TEXT runtime·_dep_dummy(SB),7,$0 - BL _div(SB) - BL _divu(SB) - BL _mod(SB) - BL _modu(SB) - BL _modu(SB) - BL _sfloat(SB) + +DATA runtime·main·f+0(SB)/4,$runtime·main(SB) +GLOBL runtime·main·f(SB),8,$4 TEXT runtime·breakpoint(SB),7,$0 - // no breakpoint yet; let program exit + // gdb won't skip this breakpoint instruction automatically, + // so you must manually "set $pc+=4" to skip it and continue. + WORD $0xe1200071 // BKPT 0x0001 RET +GLOBL runtime·goarm(SB), $4 TEXT runtime·asminit(SB),7,$0 - // No per-thread init. + // disable runfast (flush-to-zero) mode of vfp if runtime.goarm > 5 + MOVW runtime·goarm(SB), R11 + CMP $5, R11 + BLE 4(PC) + WORD $0xeef1ba10 // vmrs r11, fpscr + BIC $(1<<24), R11 + WORD $0xeee1ba10 // vmsr fpscr, r11 RET /* @@ -95,26 +103,49 @@ TEXT runtime·gosave(SB), 7, $-4 // restore state from Gobuf; longjmp TEXT runtime·gogo(SB), 7, $-4 MOVW 0(FP), R1 // gobuf - MOVW 4(FP), R0 // return 2nd arg MOVW gobuf_g(R1), g MOVW 0(g), R2 // make sure g != nil + MOVW _cgo_save_gm(SB), R2 + CMP $0, R2 // if in Cgo, we have to save g and m + BL.NE (R2) // this call will clobber R0 + MOVW 4(FP), R0 // return 2nd arg MOVW gobuf_sp(R1), SP // restore SP MOVW gobuf_pc(R1), PC -// void gogocall(Gobuf*, void (*fn)(void)) +// void gogocall(Gobuf*, void (*fn)(void), uintptr r7) // restore state from Gobuf but then call fn. // (call fn, returning to state in Gobuf) // using frame size $-4 means do not save LR on stack. TEXT runtime·gogocall(SB), 7, $-4 - MOVW 0(FP), R0 // gobuf + MOVW 0(FP), R3 // gobuf MOVW 4(FP), R1 // fn - MOVW 8(FP), R2 // fp offset - MOVW gobuf_g(R0), g - MOVW 0(g), R3 // make sure g != nil - MOVW gobuf_sp(R0), SP // restore SP - MOVW gobuf_pc(R0), LR + MOVW gobuf_g(R3), g + MOVW 0(g), R0 // make sure g != nil + MOVW _cgo_save_gm(SB), R0 + CMP $0, R0 // if in Cgo, we have to save g and m + BL.NE (R0) // this call will clobber R0 + MOVW 8(FP), R7 // context + MOVW gobuf_sp(R3), SP // restore SP + MOVW gobuf_pc(R3), LR MOVW R1, PC +// void gogocallfn(Gobuf*, FuncVal*) +// restore state from Gobuf but then call fn. +// (call fn, returning to state in Gobuf) +// using frame size $-4 means do not save LR on stack. +TEXT runtime·gogocallfn(SB), 7, $-4 + MOVW 0(FP), R3 // gobuf + MOVW 4(FP), R1 // fn + MOVW gobuf_g(R3), g + MOVW 0(g), R0 // make sure g != nil + MOVW _cgo_save_gm(SB), R0 + CMP $0, R0 // if in Cgo, we have to save g and m + BL.NE (R0) // this call will clobber R0 + MOVW gobuf_sp(R3), SP // restore SP + MOVW gobuf_pc(R3), LR + MOVW R1, R7 + MOVW 0(R1), PC + // void mcall(void (*fn)(G*)) // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) @@ -157,6 +188,7 @@ TEXT runtime·morestack(SB),7,$-4 BL.EQ runtime·abort(SB) // Save in m. + MOVW R7, m_cret(m) // function context MOVW R1, m_moreframesize(m) MOVW R2, m_moreargsize(m) @@ -228,28 +260,175 @@ TEXT runtime·lessstack(SB), 7, $-4 TEXT runtime·jmpdefer(SB), 7, $0 MOVW 0(SP), LR MOVW $-4(LR), LR // BL deferreturn - MOVW fn+0(FP), R0 + MOVW fn+0(FP), R7 MOVW argp+4(FP), SP MOVW $-4(SP), SP // SP is 4 below argp, due to saved LR - B (R0) + MOVW 0(R7), R1 + B (R1) + +// Dummy function to use in saved gobuf.PC, +// to match SP pointing at a return address. +// The gobuf.PC is unused by the contortions here +// but setting it to return will make the traceback code work. +TEXT return<>(SB),7,$0 + RET +// asmcgocall(void(*fn)(void*), void *arg) +// Call fn(arg) on the scheduler stack, +// aligned appropriately for the gcc ABI. +// See cgocall.c for more details. TEXT runtime·asmcgocall(SB),7,$0 - B runtime·cgounimpl(SB) + MOVW fn+0(FP), R1 + MOVW arg+4(FP), R0 + MOVW R13, R2 + MOVW g, R5 + + // Figure out if we need to switch to m->g0 stack. + // We get called to create new OS threads too, and those + // come in on the m->g0 stack already. + MOVW m_g0(m), R3 + CMP R3, g + BEQ 7(PC) + MOVW R13, (g_sched + gobuf_sp)(g) + MOVW $return<>(SB), R4 + MOVW R4, (g_sched+gobuf_pc)(g) + MOVW g, (g_sched+gobuf_g)(g) + MOVW R3, g + MOVW (g_sched+gobuf_sp)(g), R13 + + // Now on a scheduling stack (a pthread-created stack). + SUB $24, R13 + BIC $0x7, R13 // alignment for gcc ABI + MOVW R5, 20(R13) // save old g + MOVW R2, 16(R13) // save old SP + // R0 already contains the first argument + BL (R1) + + // Restore registers, g, stack pointer. + MOVW 20(R13), g + MOVW 16(R13), R13 + RET -TEXT runtime·cgocallback(SB),7,$0 - B runtime·cgounimpl(SB) +// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) +// Turn the fn into a Go func (by taking its address) and call +// cgocallback_gofunc. +TEXT runtime·cgocallback(SB),7,$12 + MOVW $fn+0(FP), R0 + MOVW R0, 4(R13) + MOVW frame+4(FP), R0 + MOVW R0, 8(R13) + MOVW framesize+8(FP), R0 + MOVW R0, 12(R13) + MOVW $runtime·cgocallback_gofunc(SB), R0 + BL (R0) + RET + +// cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize) +// See cgocall.c for more details. +TEXT runtime·cgocallback_gofunc(SB),7,$16 + // Load m and g from thread-local storage. + MOVW _cgo_load_gm(SB), R0 + CMP $0, R0 + BL.NE (R0) + + // If m is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call. + MOVW m, savedm-16(SP) + CMP $0, m + B.NE havem + MOVW $runtime·needm(SB), R0 + BL (R0) + +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. + MOVW fn+0(FP), R0 + MOVW frame+4(FP), R1 + MOVW framesize+8(FP), R2 + + MOVW m_g0(m), R3 + MOVW (g_sched+gobuf_sp)(R3), R4 + MOVW.W R4, -4(R13) + MOVW R13, (g_sched+gobuf_sp)(R3) + + // Switch to m->curg stack and call runtime.cgocallbackg + // with the three arguments. Because we are taking over + // the execution of m->curg but *not* resuming what had + // been running, we need to save that information (m->curg->gobuf) + // so that we can restore it when we're done. + // We can restore m->curg->gobuf.sp easily, because calling + // runtime.cgocallbackg leaves SP unchanged upon return. + // To save m->curg->gobuf.pc, we push it onto the stack. + // This has the added benefit that it looks to the traceback + // routine like cgocallbackg is going to return to that + // PC (because we defined cgocallbackg to have + // a frame size of 16, the same amount that we use below), + // so that the traceback will seamlessly trace back into + // the earlier calls. + + // Save current m->g0->sched.sp on stack and then set it to SP. + MOVW m_curg(m), g + MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4 + + // Push gobuf.pc + MOVW (g_sched+gobuf_pc)(g), R5 + SUB $4, R4 + MOVW R5, 0(R4) + + // Push arguments to cgocallbackg. + // Frame size here must match the frame size above + // to trick traceback routines into doing the right thing. + SUB $16, R4 + MOVW R0, 4(R4) + MOVW R1, 8(R4) + MOVW R2, 12(R4) + + // Switch stack and make the call. + MOVW R4, R13 + BL runtime·cgocallbackg(SB) + + // Restore g->gobuf (== m->curg->gobuf) from saved values. + MOVW 16(R13), R5 + MOVW R5, (g_sched+gobuf_pc)(g) + ADD $(16+4), R13 // SP clobbered! It is ok! + MOVW R13, (g_sched+gobuf_sp)(g) + + // Switch back to m->g0's stack and restore m->g0->sched.sp. + // (Unlike m->curg, the g0 goroutine never uses sched.pc, + // so we do not have to restore it.) + MOVW m_g0(m), g + MOVW (g_sched+gobuf_sp)(g), R13 + // POP R6 + MOVW 0(R13), R6 + ADD $4, R13 + MOVW R6, (g_sched+gobuf_sp)(g) + + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + MOVW savedm-16(SP), R6 + CMP $0, R6 + B.NE 3(PC) + MOVW $runtime·dropm(SB), R0 + BL (R0) + + // Done! + RET + +// void setmg(M*, G*); set m and g. for use by needm. +TEXT runtime·setmg(SB), 7, $-4 + MOVW mm+0(FP), m + MOVW gg+4(FP), g + + // Save m and g to thread-local storage. + MOVW _cgo_save_gm(SB), R0 + CMP $0, R0 + BL.NE (R0) -TEXT runtime·memclr(SB),7,$20 - MOVW 0(FP), R0 - MOVW $0, R1 // c = 0 - MOVW R1, -16(SP) - MOVW 4(FP), R1 // n - MOVW R1, -12(SP) - MOVW m, -8(SP) // Save m and g - MOVW g, -4(SP) - BL runtime·memset(SB) - MOVW -8(SP), m // Restore m and g, memset clobbers them - MOVW -4(SP), g RET TEXT runtime·getcallerpc(SB),7,$-4 @@ -269,16 +448,6 @@ TEXT runtime·getcallersp(SB),7,$-4 TEXT runtime·emptyfunc(SB),0,$0 RET -// int64 runtime·cputicks(), so really -// void runtime·cputicks(int64 *ticks) -// stubbed: return int64(0) -TEXT runtime·cputicks(SB),7,$0 - MOVW 0(FP), R1 - MOVW $0, R0 - MOVW R0, 0(R1) - MOVW R0, 4(R1) - RET - TEXT runtime·abort(SB),7,$-4 MOVW $0, R0 MOVW (R0), R1 diff --git a/src/pkg/runtime/atomic_386.c b/src/pkg/runtime/atomic_386.c index a4f2a114f..79b7cbf96 100644 --- a/src/pkg/runtime/atomic_386.c +++ b/src/pkg/runtime/atomic_386.c @@ -17,3 +17,16 @@ runtime·atomicloadp(void* volatile* addr) { return *addr; } + +#pragma textflag 7 +uint64 +runtime·xadd64(uint64 volatile* addr, int64 v) +{ + uint64 old; + + old = *addr; + while(!runtime·cas64(addr, &old, old+v)) { + // nothing + } + return old+v; +} diff --git a/src/pkg/runtime/atomic_amd64.c b/src/pkg/runtime/atomic_amd64.c index a4f2a114f..e92d8ec21 100644 --- a/src/pkg/runtime/atomic_amd64.c +++ b/src/pkg/runtime/atomic_amd64.c @@ -11,6 +11,13 @@ runtime·atomicload(uint32 volatile* addr) return *addr; } +#pragma textflag 7 +uint64 +runtime·atomicload64(uint64 volatile* addr) +{ + return *addr; +} + #pragma textflag 7 void* runtime·atomicloadp(void* volatile* addr) diff --git a/src/pkg/runtime/atomic_arm.c b/src/pkg/runtime/atomic_arm.c index 52e4059ae..0b54840cc 100644 --- a/src/pkg/runtime/atomic_arm.c +++ b/src/pkg/runtime/atomic_arm.c @@ -3,6 +3,14 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "arch_GOARCH.h" + +static union { + Lock l; + byte pad [CacheLineSize]; +} locktab[57]; + +#define LOCK(addr) (&locktab[((uintptr)(addr)>>3)%nelem(locktab)].l) // Atomic add and return new value. #pragma textflag 7 @@ -80,4 +88,56 @@ runtime·atomicstore(uint32 volatile* addr, uint32 v) if(runtime·cas(addr, old, v)) return; } -} \ No newline at end of file +} + +#pragma textflag 7 +bool +runtime·cas64(uint64 volatile *addr, uint64 *old, uint64 new) +{ + bool res; + + runtime·lock(LOCK(addr)); + if(*addr == *old) { + *addr = new; + res = true; + } else { + *old = *addr; + res = false; + } + runtime·unlock(LOCK(addr)); + return res; +} + +#pragma textflag 7 +uint64 +runtime·xadd64(uint64 volatile *addr, int64 delta) +{ + uint64 res; + + runtime·lock(LOCK(addr)); + res = *addr + delta; + *addr = res; + runtime·unlock(LOCK(addr)); + return res; +} + +#pragma textflag 7 +uint64 +runtime·atomicload64(uint64 volatile *addr) +{ + uint64 res; + + runtime·lock(LOCK(addr)); + res = *addr; + runtime·unlock(LOCK(addr)); + return res; +} + +#pragma textflag 7 +void +runtime·atomicstore64(uint64 volatile *addr, uint64 v) +{ + runtime·lock(LOCK(addr)); + *addr = v; + runtime·unlock(LOCK(addr)); +} diff --git a/src/pkg/runtime/callback_windows_386.c b/src/pkg/runtime/callback_windows_386.c index fcd292fbc..880588da6 100644 --- a/src/pkg/runtime/callback_windows_386.c +++ b/src/pkg/runtime/callback_windows_386.c @@ -4,6 +4,7 @@ #include "runtime.h" #include "type.h" +#include "typekind.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" @@ -79,7 +80,7 @@ runtime·compilecallback(Eface fn, bool cleanstack) // MOVL fn, AX *p++ = 0xb8; - *(uint32*)p = (uint32)fn.data; + *(uint32*)p = (uint32)(fn.data); p += 4; // MOVL argsize, DX diff --git a/src/pkg/runtime/callback_windows_amd64.c b/src/pkg/runtime/callback_windows_amd64.c index 99d7cb9e3..1a4779291 100644 --- a/src/pkg/runtime/callback_windows_amd64.c +++ b/src/pkg/runtime/callback_windows_amd64.c @@ -4,6 +4,7 @@ #include "runtime.h" #include "type.h" +#include "typekind.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" @@ -77,7 +78,7 @@ runtime·compilecallback(Eface fn, bool /*cleanstack*/) // MOVQ fn, AX *p++ = 0x48; *p++ = 0xb8; - *(uint64*)p = (uint64)fn.data; + *(uint64*)p = (uint64)(fn.data); p += 8; // PUSH AX *p++ = 0x50; diff --git a/src/pkg/runtime/cgo/asm_386.s b/src/pkg/runtime/cgo/asm_386.s new file mode 100644 index 000000000..7faaa4097 --- /dev/null +++ b/src/pkg/runtime/cgo/asm_386.s @@ -0,0 +1,29 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * void crosscall2(void (*fn)(void*, int32), void*, int32) + * Save registers and call fn with two arguments. + */ +TEXT crosscall2(SB),7,$0 + PUSHL BP + MOVL SP, BP + PUSHL BX + PUSHL SI + PUSHL DI + + SUBL $8, SP + MOVL 16(BP), AX + MOVL AX, 4(SP) + MOVL 12(BP), AX + MOVL AX, 0(SP) + MOVL 8(BP), AX + CALL AX + ADDL $8, SP + + POPL DI + POPL SI + POPL BX + POPL BP + RET diff --git a/src/pkg/runtime/cgo/asm_amd64.s b/src/pkg/runtime/cgo/asm_amd64.s new file mode 100644 index 000000000..53f7148a2 --- /dev/null +++ b/src/pkg/runtime/cgo/asm_amd64.s @@ -0,0 +1,45 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * void crosscall2(void (*fn)(void*, int32), void*, int32) + * Save registers and call fn with two arguments. + */ +TEXT crosscall2(SB),7,$0 + SUBQ $0x58, SP /* keeps stack pointer 32-byte aligned */ + MOVQ BX, 0x10(SP) + MOVQ BP, 0x18(SP) + MOVQ R12, 0x20(SP) + MOVQ R13, 0x28(SP) + MOVQ R14, 0x30(SP) + MOVQ R15, 0x38(SP) + +#ifdef GOOS_windows + // Win64 save RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 + MOVQ DI, 0x40(SP) + MOVQ SI, 0x48(SP) + + MOVQ DX, 0(SP) /* arg */ + MOVQ R8, 8(SP) /* argsize (includes padding) */ + + CALL CX /* fn */ + + MOVQ 0x40(SP), DI + MOVQ 0x48(SP), SI +#else + MOVQ SI, 0(SP) /* arg */ + MOVQ DX, 8(SP) /* argsize (includes padding) */ + + CALL DI /* fn */ +#endif + + MOVQ 0x10(SP), BX + MOVQ 0x18(SP), BP + MOVQ 0x20(SP), R12 + MOVQ 0x28(SP), R13 + MOVQ 0x30(SP), R14 + MOVQ 0x38(SP), R15 + + ADDQ $0x58, SP + RET diff --git a/src/pkg/runtime/cgo/asm_arm.s b/src/pkg/runtime/cgo/asm_arm.s new file mode 100644 index 000000000..a6ea0dc07 --- /dev/null +++ b/src/pkg/runtime/cgo/asm_arm.s @@ -0,0 +1,23 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * void crosscall2(void (*fn)(void*, int32), void*, int32) + * Save registers and call fn with two arguments. + */ +TEXT crosscall2(SB),7,$-4 + /* + * We still need to save all callee save register as before, and then + * push 2 args for fn (R1 and R2). + * Also note that at procedure entry in 5c/5g world, 4(R13) will be the + * first arg, so we must push another dummy reg (R0) for 0(R13). + * Additionally, cgo_tls_set_gm will clobber R0, so we need to save R0 + * nevertheless. + */ + MOVM.WP [R0, R1, R2, R4, R5, R6, R7, R8, R9, R10, R11, R12, R14], (R13) + MOVW _cgo_load_gm(SB), R0 + BL (R0) + MOVW PC, R14 + MOVW 0(R13), PC + MOVM.IAW (R13), [R0, R1, R2, R4, R5, R6, R7, R8, R9, R10, R11, R12, PC] diff --git a/src/pkg/runtime/cgo/callbacks.c b/src/pkg/runtime/cgo/callbacks.c index f36fb3fd7..51bd529ec 100644 --- a/src/pkg/runtime/cgo/callbacks.c +++ b/src/pkg/runtime/cgo/callbacks.c @@ -33,7 +33,13 @@ static void _cgo_allocate_internal(uintptr len, byte *ret) { + CgoMal *c; + ret = runtime·mal(len); + c = runtime·mal(sizeof(*c)); + c->next = m->cgomal; + c->alloc = ret; + m->cgomal = c; FLUSH(&ret); } @@ -71,3 +77,19 @@ _cgo_panic(void *a, int32 n) { runtime·cgocallback((void(*)(void))_cgo_panic_internal, a, n); } + +#pragma cgo_import_static x_cgo_init +extern void x_cgo_init(G*); +void (*_cgo_init)(G*) = x_cgo_init; + +#pragma cgo_import_static x_cgo_malloc +extern void x_cgo_malloc(void*); +void (*_cgo_malloc)(void*) = x_cgo_malloc; + +#pragma cgo_import_static x_cgo_free +extern void x_cgo_free(void*); +void (*_cgo_free)(void*) = x_cgo_free; + +#pragma cgo_import_static x_cgo_thread_start +extern void x_cgo_thread_start(void*); +void (*_cgo_thread_start)(void*) = x_cgo_thread_start; diff --git a/src/pkg/runtime/cgo/cgo.go b/src/pkg/runtime/cgo/cgo.go index 414f3da36..e0d538668 100644 --- a/src/pkg/runtime/cgo/cgo.go +++ b/src/pkg/runtime/cgo/cgo.go @@ -18,6 +18,8 @@ package cgo #cgo openbsd LDFLAGS: -lpthread #cgo windows LDFLAGS: -lm -mthreads +#cgo CFLAGS: -Wall -Werror + */ import "C" diff --git a/src/pkg/runtime/cgo/cgo_arm.c b/src/pkg/runtime/cgo/cgo_arm.c new file mode 100644 index 000000000..d23f53e77 --- /dev/null +++ b/src/pkg/runtime/cgo/cgo_arm.c @@ -0,0 +1,12 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#pragma cgo_import_static x_cgo_load_gm +extern void x_cgo_load_gm(void); +void (*_cgo_load_gm)(void) = x_cgo_load_gm; + +#pragma cgo_import_static x_cgo_save_gm +extern void x_cgo_save_gm(void); +void (*_cgo_save_gm)(void) = x_cgo_save_gm; + diff --git a/src/pkg/runtime/cgo/gcc_386.S b/src/pkg/runtime/cgo/gcc_386.S index 9abab7ebd..94ba5842f 100644 --- a/src/pkg/runtime/cgo/gcc_386.S +++ b/src/pkg/runtime/cgo/gcc_386.S @@ -35,31 +35,6 @@ EXT(crosscall_386): popl %ebp ret -/* - * void crosscall2(void (*fn)(void*, int32), void*, int32) - * - * Save registers and call fn with two arguments. - */ -.globl EXT(crosscall2) -EXT(crosscall2): - pushl %ebp - movl %esp, %ebp - pushl %ebx - pushl %esi - pushl %edi - - pushl 16(%ebp) - pushl 12(%ebp) - mov 8(%ebp), %eax - call *%eax - addl $8,%esp - - popl %edi - popl %esi - popl %ebx - popl %ebp - ret - .globl EXT(__stack_chk_fail_local) EXT(__stack_chk_fail_local): 1: diff --git a/src/pkg/runtime/cgo/gcc_amd64.S b/src/pkg/runtime/cgo/gcc_amd64.S index 706ee6b58..81b270195 100644 --- a/src/pkg/runtime/cgo/gcc_amd64.S +++ b/src/pkg/runtime/cgo/gcc_amd64.S @@ -19,9 +19,6 @@ * are callee-save so they must be saved explicitly. * The standard x86-64 ABI passes the three arguments m, g, fn * in %rdi, %rsi, %rdx. - * - * Also need to set %r15 to g and %r14 to m (see ../pkg/runtime/mkasmh.sh) - * during the call. */ .globl EXT(crosscall_amd64) EXT(crosscall_amd64): @@ -45,48 +42,3 @@ EXT(crosscall_amd64): popq %rbp popq %rbx ret - -/* - * void crosscall2(void (*fn)(void*, int32), void *arg, int32 argsize) - * - * Save registers and call fn with two arguments. fn is a Go function - * which takes parameters on the stack rather than in registers. - */ -.globl EXT(crosscall2) -EXT(crosscall2): - subq $0x58, %rsp /* keeps stack pointer 32-byte aligned */ - movq %rbx, 0x10(%rsp) - movq %rbp, 0x18(%rsp) - movq %r12, 0x20(%rsp) - movq %r13, 0x28(%rsp) - movq %r14, 0x30(%rsp) - movq %r15, 0x38(%rsp) - -#if defined(_WIN64) - // Win64 save RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 - movq %rdi, 0x40(%rsp) - movq %rsi, 0x48(%rsp) - - movq %rdx, 0(%rsp) /* arg */ - movq %r8, 8(%rsp) /* argsize (includes padding) */ - - call *%rcx /* fn */ -#else - movq %rsi, 0(%rsp) /* arg */ - movq %rdx, 8(%rsp) /* argsize (includes padding) */ - - call *%rdi /* fn */ -#endif - - movq 0x10(%rsp), %rbx - movq 0x18(%rsp), %rbp - movq 0x20(%rsp), %r12 - movq 0x28(%rsp), %r13 - movq 0x30(%rsp), %r14 - movq 0x38(%rsp), %r15 -#if defined(__WIN64) - movq 0x40(%rsp), %rdi - movq 0x48(%rsp), %rsi -#endif - addq $0x58, %rsp - ret diff --git a/src/pkg/runtime/cgo/gcc_arm.S b/src/pkg/runtime/cgo/gcc_arm.S index 32d862984..809fcb9a0 100644 --- a/src/pkg/runtime/cgo/gcc_arm.S +++ b/src/pkg/runtime/cgo/gcc_arm.S @@ -1 +1,36 @@ -/* unimplemented */ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * Apple still insists on underscore prefixes for C function names. + */ +#if defined(__APPLE__) +#define EXT(s) _##s +#else +#define EXT(s) s +#endif + +/* + * void crosscall_arm2(void (*fn)(void), void *g, void *m) + * + * Calling into the 5c tool chain, where all registers are caller save. + * Called from standard ARM EABI, where r4-r11 are callee-save, so they + * must be saved explicitly. + */ +.globl EXT(crosscall_arm2) +EXT(crosscall_arm2): + push {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr} + mov r10, r1 // g + mov r9, r2 // m + mov r3, r0 // save r0, cgo_tls_set_gm will clobber it + bl EXT(x_cgo_save_gm) // save current g and m into TLS variable + mov lr, pc + mov pc, r3 + pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc} + +.globl EXT(__stack_chk_fail_local) +EXT(__stack_chk_fail_local): +1: + b 1b + diff --git a/src/pkg/runtime/cgo/gcc_darwin_386.c b/src/pkg/runtime/cgo/gcc_darwin_386.c index 2c30c666f..ad9fb5abf 100644 --- a/src/pkg/runtime/cgo/gcc_darwin_386.c +++ b/src/pkg/runtime/cgo/gcc_darwin_386.c @@ -101,8 +101,8 @@ inittls(void) pthread_key_delete(tofree[i]); } -static void -xinitcgo(G *g) +void +x_cgo_init(G *g) { pthread_attr_t attr; size_t size; @@ -115,10 +115,9 @@ xinitcgo(G *g) inittls(); } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) { pthread_attr_t attr; sigset_t ign, oset; @@ -127,14 +126,14 @@ libcgo_sys_thread_start(ThreadStart *ts) int err; sigfillset(&ign); - sigprocmask(SIG_SETMASK, &ign, &oset); + pthread_sigmask(SIG_SETMASK, &ign, &oset); pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); ts->g->stackguard = size; err = pthread_create(&p, &attr, threadentry, ts); - sigprocmask(SIG_SETMASK, &oset, nil); + pthread_sigmask(SIG_SETMASK, &oset, nil); if (err != 0) { fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); @@ -153,7 +152,7 @@ threadentry(void *v) ts.g->stackbase = (uintptr)&ts; /* - * libcgo_sys_thread_start set stackguard to stack size; + * _cgo_sys_thread_start set stackguard to stack size; * change to actual guard pointer. */ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; diff --git a/src/pkg/runtime/cgo/gcc_darwin_amd64.c b/src/pkg/runtime/cgo/gcc_darwin_amd64.c index 89dc7a4e8..65d381633 100644 --- a/src/pkg/runtime/cgo/gcc_darwin_amd64.c +++ b/src/pkg/runtime/cgo/gcc_darwin_amd64.c @@ -72,7 +72,7 @@ inittls(void) } void -xinitcgo(G *g) +x_cgo_init(G *g) { pthread_attr_t attr; size_t size; @@ -85,10 +85,9 @@ xinitcgo(G *g) inittls(); } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) { pthread_attr_t attr; sigset_t ign, oset; @@ -97,14 +96,14 @@ libcgo_sys_thread_start(ThreadStart *ts) int err; sigfillset(&ign); - sigprocmask(SIG_SETMASK, &ign, &oset); + pthread_sigmask(SIG_SETMASK, &ign, &oset); pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); ts->g->stackguard = size; err = pthread_create(&p, &attr, threadentry, ts); - sigprocmask(SIG_SETMASK, &oset, nil); + pthread_sigmask(SIG_SETMASK, &oset, nil); if (err != 0) { fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); @@ -123,7 +122,7 @@ threadentry(void *v) ts.g->stackbase = (uintptr)&ts; /* - * libcgo_sys_thread_start set stackguard to stack size; + * _cgo_sys_thread_start set stackguard to stack size; * change to actual guard pointer. */ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; diff --git a/src/pkg/runtime/cgo/gcc_freebsd_386.c b/src/pkg/runtime/cgo/gcc_freebsd_386.c index 2c97e2a33..7c62a1bc4 100644 --- a/src/pkg/runtime/cgo/gcc_freebsd_386.c +++ b/src/pkg/runtime/cgo/gcc_freebsd_386.c @@ -6,12 +6,13 @@ #include #include #include +#include #include "libcgo.h" static void* threadentry(void*); -static void -xinitcgo(G *g) +void +x_cgo_init(G *g) { pthread_attr_t attr; size_t size; @@ -22,10 +23,9 @@ xinitcgo(G *g) pthread_attr_destroy(&attr); } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) { pthread_attr_t attr; sigset_t ign, oset; @@ -60,7 +60,7 @@ threadentry(void *v) ts.g->stackbase = (uintptr)&ts; /* - * libcgo_sys_thread_start set stackguard to stack size; + * _cgo_sys_thread_start set stackguard to stack size; * change to actual guard pointer. */ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; diff --git a/src/pkg/runtime/cgo/gcc_freebsd_amd64.c b/src/pkg/runtime/cgo/gcc_freebsd_amd64.c index 3beb4d7bb..6be8bd251 100644 --- a/src/pkg/runtime/cgo/gcc_freebsd_amd64.c +++ b/src/pkg/runtime/cgo/gcc_freebsd_amd64.c @@ -6,12 +6,13 @@ #include #include #include +#include #include "libcgo.h" static void* threadentry(void*); -static void -xinitcgo(G *g) +void +x_cgo_init(G *g) { pthread_attr_t attr; size_t size; @@ -22,10 +23,9 @@ xinitcgo(G *g) pthread_attr_destroy(&attr); } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) { pthread_attr_t attr; sigset_t ign, oset; @@ -61,7 +61,7 @@ threadentry(void *v) ts.g->stackbase = (uintptr)&ts; /* - * libcgo_sys_thread_start set stackguard to stack size; + * _cgo_sys_thread_start set stackguard to stack size; * change to actual guard pointer. */ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; diff --git a/src/pkg/runtime/cgo/gcc_freebsd_arm.c b/src/pkg/runtime/cgo/gcc_freebsd_arm.c new file mode 100644 index 000000000..3bcb0b270 --- /dev/null +++ b/src/pkg/runtime/cgo/gcc_freebsd_arm.c @@ -0,0 +1,114 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include "libcgo.h" + +static void *threadentry(void*); + +// We have to resort to TLS variable to save g(R10) and +// m(R9). One reason is that external code might trigger +// SIGSEGV, and our runtime.sigtramp don't even know we +// are in external code, and will continue to use R10/R9, +// this might as well result in another SIGSEGV. +// Note: all three functions will clobber R0, and the last +// two can be called from 5c ABI code. +void __aeabi_read_tp(void) __attribute__((naked)); +void x_cgo_save_gm(void) __attribute__((naked)); +void x_cgo_load_gm(void) __attribute__((naked)); + +void +__aeabi_read_tp(void) +{ + // read @ 0xffff1000 + __asm__ __volatile__ ( + "ldr r0, =0xffff1000\n\t" + "ldr r0, [r0]\n\t" + "mov pc, lr\n\t" + ); +} + +// g (R10) at 8(TP), m (R9) at 12(TP) +void +x_cgo_load_gm(void) +{ + __asm__ __volatile__ ( + "push {lr}\n\t" + "bl __aeabi_read_tp\n\t" + "ldr r10, [r0, #8]\n\t" + "ldr r9, [r0, #12]\n\t" + "pop {pc}\n\t" + ); +} + +void +x_cgo_save_gm(void) +{ + __asm__ __volatile__ ( + "push {lr}\n\t" + "bl __aeabi_read_tp\n\t" + "str r10, [r0, #8]\n\t" + "str r9, [r0, #12]\n\t" + "pop {pc}\n\t" + ); +} + +void +x_cgo_init(G *g) +{ + pthread_attr_t attr; + size_t size; + x_cgo_save_gm(); // save g and m for the initial thread + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stackguard = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + pthread_t p; + size_t size; + int err; + + // Not sure why the memset is necessary here, + // but without it, we get a bogus stack size + // out of pthread_attr_getstacksize. C'est la Linux. + memset(&attr, 0, sizeof attr); + pthread_attr_init(&attr); + size = 0; + pthread_attr_getstacksize(&attr, &size); + ts->g->stackguard = size; + err = pthread_create(&p, &attr, threadentry, ts); + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +extern void crosscall_arm2(void (*fn)(void), void *g, void *m); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + ts.g->stackbase = (uintptr)&ts; + + /* + * _cgo_sys_thread_start set stackguard to stack size; + * change to actual guard pointer. + */ + ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2; + + crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m); + return nil; +} diff --git a/src/pkg/runtime/cgo/gcc_linux_386.c b/src/pkg/runtime/cgo/gcc_linux_386.c index 7d84acc11..9357a63f7 100644 --- a/src/pkg/runtime/cgo/gcc_linux_386.c +++ b/src/pkg/runtime/cgo/gcc_linux_386.c @@ -9,8 +9,8 @@ static void *threadentry(void*); -static void -xinitcgo(G *g) +void +x_cgo_init(G *g) { pthread_attr_t attr; size_t size; @@ -21,10 +21,9 @@ xinitcgo(G *g) pthread_attr_destroy(&attr); } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) { pthread_attr_t attr; sigset_t ign, oset; @@ -64,7 +63,7 @@ threadentry(void *v) ts.g->stackbase = (uintptr)&ts; /* - * libcgo_sys_thread_start set stackguard to stack size; + * _cgo_sys_thread_start set stackguard to stack size; * change to actual guard pointer. */ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; diff --git a/src/pkg/runtime/cgo/gcc_linux_amd64.c b/src/pkg/runtime/cgo/gcc_linux_amd64.c index 28cbf78c5..bc76117d3 100644 --- a/src/pkg/runtime/cgo/gcc_linux_amd64.c +++ b/src/pkg/runtime/cgo/gcc_linux_amd64.c @@ -10,7 +10,7 @@ static void* threadentry(void*); void -xinitcgo(G* g) +x_cgo_init(G* g) { pthread_attr_t attr; size_t size; @@ -21,10 +21,9 @@ xinitcgo(G* g) pthread_attr_destroy(&attr); } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) { pthread_attr_t attr; sigset_t ign, oset; @@ -59,7 +58,7 @@ threadentry(void *v) ts.g->stackbase = (uintptr)&ts; /* - * libcgo_sys_thread_start set stackguard to stack size; + * _cgo_sys_thread_start set stackguard to stack size; * change to actual guard pointer. */ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; diff --git a/src/pkg/runtime/cgo/gcc_linux_arm.c b/src/pkg/runtime/cgo/gcc_linux_arm.c index 8397c75bb..46a1126ad 100644 --- a/src/pkg/runtime/cgo/gcc_linux_arm.c +++ b/src/pkg/runtime/cgo/gcc_linux_arm.c @@ -2,19 +2,113 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include +#include #include "libcgo.h" -static void -xinitcgo(G *g) +static void *threadentry(void*); + +// We have to resort to TLS variable to save g(R10) and +// m(R9). One reason is that external code might trigger +// SIGSEGV, and our runtime.sigtramp don't even know we +// are in external code, and will continue to use R10/R9, +// this might as well result in another SIGSEGV. +// Note: all three functions will clobber R0, and the last +// two can be called from 5c ABI code. +void __aeabi_read_tp(void) __attribute__((naked)); +void x_cgo_save_gm(void) __attribute__((naked)); +void x_cgo_load_gm(void) __attribute__((naked)); + +void +__aeabi_read_tp(void) +{ + // b __kuser_get_tls @ 0xffff0fe0 + __asm__ __volatile__ ( + "mvn r0, #0xf000\n\t" + "sub pc, r0, #31\n\t" + "nop\n\tnop\n\t" + ); +} + +// g (R10) at 8(TP), m (R9) at 12(TP) +void +x_cgo_load_gm(void) +{ + __asm__ __volatile__ ( + "push {lr}\n\t" + "bl __aeabi_read_tp\n\t" + "ldr r10, [r0, #8]\n\t" + "ldr r9, [r0, #12]\n\t" + "pop {pc}\n\t" + ); +} + +void +x_cgo_save_gm(void) +{ + __asm__ __volatile__ ( + "push {lr}\n\t" + "bl __aeabi_read_tp\n\t" + "str r10, [r0, #8]\n\t" + "str r9, [r0, #12]\n\t" + "pop {pc}\n\t" + ); +} + +void +x_cgo_init(G *g) { - // unimplemented + pthread_attr_t attr; + size_t size; + x_cgo_save_gm(); // save g and m for the initial thread + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stackguard = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + pthread_t p; + size_t size; + int err; + + // Not sure why the memset is necessary here, + // but without it, we get a bogus stack size + // out of pthread_attr_getstacksize. C'est la Linux. + memset(&attr, 0, sizeof attr); + pthread_attr_init(&attr); + size = 0; + pthread_attr_getstacksize(&attr, &size); + ts->g->stackguard = size; + err = pthread_create(&p, &attr, threadentry, ts); + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +extern void crosscall_arm2(void (*fn)(void), void *g, void *m); +static void* +threadentry(void *v) { - // unimplemented - *(int*)0 = 0; + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + ts.g->stackbase = (uintptr)&ts; + + /* + * _cgo_sys_thread_start set stackguard to stack size; + * change to actual guard pointer. + */ + ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2; + + crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m); + return nil; } diff --git a/src/pkg/runtime/cgo/gcc_netbsd_386.c b/src/pkg/runtime/cgo/gcc_netbsd_386.c new file mode 100644 index 000000000..09b271df4 --- /dev/null +++ b/src/pkg/runtime/cgo/gcc_netbsd_386.c @@ -0,0 +1,80 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include "libcgo.h" + +static void* threadentry(void*); + +void +x_cgo_init(G *g) +{ + pthread_attr_t attr; + size_t size; + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stackguard = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + sigprocmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + ts->g->stackguard = size; + err = pthread_create(&p, &attr, threadentry, ts); + + sigprocmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + ts.g->stackbase = (uintptr)&ts; + + /* + * _cgo_sys_thread_start set stackguard to stack size; + * change to actual guard pointer. + */ + ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; + + /* + * Set specific keys. On NetBSD/ELF, the thread local storage + * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes + * for the two words g and m at %gs:-8 and %gs:-4. + */ + asm volatile ( + "movl %0, %%gs:-8\n" // MOVL g, -8(GS) + "movl %1, %%gs:-4\n" // MOVL m, -4(GS) + :: "r"(ts.g), "r"(ts.m) + ); + + crosscall_386(ts.fn); + return nil; +} diff --git a/src/pkg/runtime/cgo/gcc_netbsd_amd64.c b/src/pkg/runtime/cgo/gcc_netbsd_amd64.c new file mode 100644 index 000000000..080c59ba4 --- /dev/null +++ b/src/pkg/runtime/cgo/gcc_netbsd_amd64.c @@ -0,0 +1,80 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include "libcgo.h" + +static void* threadentry(void*); + +void +x_cgo_init(G *g) +{ + pthread_attr_t attr; + size_t size; + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stackguard = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + sigprocmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + + ts->g->stackguard = size; + err = pthread_create(&p, &attr, threadentry, ts); + + sigprocmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + ts.g->stackbase = (uintptr)&ts; + + /* + * _cgo_sys_thread_start set stackguard to stack size; + * change to actual guard pointer. + */ + ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; + + /* + * Set specific keys. On NetBSD/ELF, the thread local storage + * is just before %fs:0. Our dynamic 6.out's reserve 16 bytes + * for the two words g and m at %fs:-16 and %fs:-8. + */ + asm volatile ( + "movq %0, %%fs:-16\n" // MOVL g, -16(FS) + "movq %1, %%fs:-8\n" // MOVL m, -8(FS) + :: "r"(ts.g), "r"(ts.m) + ); + crosscall_amd64(ts.fn); + return nil; +} diff --git a/src/pkg/runtime/cgo/gcc_netbsd_arm.c b/src/pkg/runtime/cgo/gcc_netbsd_arm.c new file mode 100644 index 000000000..d93b531e7 --- /dev/null +++ b/src/pkg/runtime/cgo/gcc_netbsd_arm.c @@ -0,0 +1,122 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include "libcgo.h" + +static void *threadentry(void*); + +// We have to resort to TLS variable to save g(R10) and +// m(R9). One reason is that external code might trigger +// SIGSEGV, and our runtime.sigtramp don't even know we +// are in external code, and will continue to use R10/R9, +// this might as well result in another SIGSEGV. +// Note: all three functions will clobber R0, and the last +// two can be called from 5c ABI code. +void __aeabi_read_tp(void) __attribute__((naked)); +void x_cgo_save_gm(void) __attribute__((naked)); +void x_cgo_load_gm(void) __attribute__((naked)); + +void +__aeabi_read_tp(void) +{ + // this function is only allowed to clobber r0 + __asm__ __volatile__ ( + "mrc p15, 0, r0, c13, c0, 3\n\t" + "cmp r0, #0\n\t" + "movne pc, lr\n\t" + "push {r1,r2,r3,r12}\n\t" + "svc 0x00a0013c\n\t" // _lwp_getprivate + "pop {r1,r2,r3,r12}\n\t" + "mov pc, lr\n\t" + ); +} + +// g (R10) at 8(TP), m (R9) at 12(TP) +void +x_cgo_load_gm(void) +{ + __asm__ __volatile__ ( + "push {lr}\n\t" + "bl __aeabi_read_tp\n\t" + "ldr r10, [r0, #8]\n\t" + "ldr r9, [r0, #12]\n\t" + "pop {pc}\n\t" + ); +} + +void +x_cgo_save_gm(void) +{ + __asm__ __volatile__ ( + "push {lr}\n\t" + "bl __aeabi_read_tp\n\t" + "str r10, [r0, #8]\n\t" + "str r9, [r0, #12]\n\t" + "pop {pc}\n\t" + ); +} + +void +x_cgo_init(G *g) +{ + pthread_attr_t attr; + size_t size; + x_cgo_save_gm(); // save g and m for the initial thread + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stackguard = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + sigprocmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + ts->g->stackguard = size; + err = pthread_create(&p, &attr, threadentry, ts); + + sigprocmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +extern void crosscall_arm2(void (*fn)(void), void *g, void *m); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + ts.g->stackbase = (uintptr)&ts; + + /* + * _cgo_sys_thread_start set stackguard to stack size; + * change to actual guard pointer. + */ + ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2; + + crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m); + return nil; +} diff --git a/src/pkg/runtime/cgo/gcc_openbsd_386.c b/src/pkg/runtime/cgo/gcc_openbsd_386.c new file mode 100644 index 000000000..86c1365ad --- /dev/null +++ b/src/pkg/runtime/cgo/gcc_openbsd_386.c @@ -0,0 +1,169 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include "libcgo.h" + +static void* threadentry(void*); + +// TCB_SIZE is sizeof(struct thread_control_block), +// as defined in /usr/src/lib/librthread/tcb.h +#define TCB_SIZE (4 * sizeof(void *)) +#define TLS_SIZE (2 * sizeof(void *)) + +void *__get_tcb(void); +void __set_tcb(void *); + +static int (*sys_pthread_create)(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); + +struct thread_args { + void *(*func)(void *); + void *arg; +}; + +static void +tcb_fixup(int mainthread) +{ + void *newtcb, *oldtcb; + + // The OpenBSD ld.so(1) does not currently support PT_TLS. As a result, + // we need to allocate our own TLS space while preserving the existing + // TCB that has been setup via librthread. + + newtcb = malloc(TCB_SIZE + TLS_SIZE); + if(newtcb == NULL) + abort(); + + // The signal trampoline expects the TLS slots to be zeroed. + bzero(newtcb, TLS_SIZE); + + oldtcb = __get_tcb(); + bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE); + __set_tcb(newtcb + TLS_SIZE); + + // The main thread TCB is a static allocation - do not try to free it. + if(!mainthread) + free(oldtcb); +} + +static void * +thread_start_wrapper(void *arg) +{ + struct thread_args args = *(struct thread_args *)arg; + + free(arg); + tcb_fixup(0); + + return args.func(args.arg); +} + +int +pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) +{ + struct thread_args *p; + + p = malloc(sizeof(*p)); + if(p == NULL) { + errno = ENOMEM; + return -1; + } + p->func = start_routine; + p->arg = arg; + + return sys_pthread_create(thread, attr, thread_start_wrapper, p); +} + +void +x_cgo_init(G *g) +{ + pthread_attr_t attr; + size_t size; + void *handle; + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stackguard = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); + + // Locate symbol for the system pthread_create function. + handle = dlopen("libpthread.so", RTLD_LAZY); + if(handle == NULL) { + fprintf(stderr, "dlopen: failed to load libpthread: %s\n", dlerror()); + abort(); + } + sys_pthread_create = dlsym(handle, "pthread_create"); + if(sys_pthread_create == NULL) { + fprintf(stderr, "dlsym: failed to find pthread_create: %s\n", dlerror()); + abort(); + } + dlclose(handle); + + tcb_fixup(1); +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + sigprocmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + ts->g->stackguard = size; + err = sys_pthread_create(&p, &attr, threadentry, ts); + + sigprocmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +static void* +threadentry(void *v) +{ + ThreadStart ts; + + tcb_fixup(0); + + ts = *(ThreadStart*)v; + free(v); + + ts.g->stackbase = (uintptr)&ts; + + /* + * _cgo_sys_thread_start set stackguard to stack size; + * change to actual guard pointer. + */ + ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; + + /* + * Set specific keys. On OpenBSD/ELF, the thread local storage + * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes + * for the two words g and m at %gs:-8 and %gs:-4. + */ + asm volatile ( + "movl %0, %%gs:-8\n" // MOVL g, -8(GS) + "movl %1, %%gs:-4\n" // MOVL m, -4(GS) + :: "r"(ts.g), "r"(ts.m) + ); + + crosscall_386(ts.fn); + return nil; +} diff --git a/src/pkg/runtime/cgo/gcc_openbsd_amd64.c b/src/pkg/runtime/cgo/gcc_openbsd_amd64.c new file mode 100644 index 000000000..d3a5e36b0 --- /dev/null +++ b/src/pkg/runtime/cgo/gcc_openbsd_amd64.c @@ -0,0 +1,169 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include "libcgo.h" + +static void* threadentry(void*); + +// TCB_SIZE is sizeof(struct thread_control_block), +// as defined in /usr/src/lib/librthread/tcb.h +#define TCB_SIZE (4 * sizeof(void *)) +#define TLS_SIZE (2 * sizeof(void *)) + +void *__get_tcb(void); +void __set_tcb(void *); + +static int (*sys_pthread_create)(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); + +struct thread_args { + void *(*func)(void *); + void *arg; +}; + +static void +tcb_fixup(int mainthread) +{ + void *newtcb, *oldtcb; + + // The OpenBSD ld.so(1) does not currently support PT_TLS. As a result, + // we need to allocate our own TLS space while preserving the existing + // TCB that has been setup via librthread. + + newtcb = malloc(TCB_SIZE + TLS_SIZE); + if(newtcb == NULL) + abort(); + + // The signal trampoline expects the TLS slots to be zeroed. + bzero(newtcb, TLS_SIZE); + + oldtcb = __get_tcb(); + bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE); + __set_tcb(newtcb + TLS_SIZE); + + // The main thread TCB is a static allocation - do not try to free it. + if(!mainthread) + free(oldtcb); +} + +static void * +thread_start_wrapper(void *arg) +{ + struct thread_args args = *(struct thread_args *)arg; + + free(arg); + tcb_fixup(0); + + return args.func(args.arg); +} + +int +pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) +{ + struct thread_args *p; + + p = malloc(sizeof(*p)); + if(p == NULL) { + errno = ENOMEM; + return -1; + } + p->func = start_routine; + p->arg = arg; + + return sys_pthread_create(thread, attr, thread_start_wrapper, p); +} + +void +x_cgo_init(G *g) +{ + pthread_attr_t attr; + size_t size; + void *handle; + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stackguard = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); + + // Locate symbol for the system pthread_create function. + handle = dlopen("libpthread.so", RTLD_LAZY); + if(handle == NULL) { + fprintf(stderr, "dlopen: failed to load libpthread: %s\n", dlerror()); + abort(); + } + sys_pthread_create = dlsym(handle, "pthread_create"); + if(sys_pthread_create == NULL) { + fprintf(stderr, "dlsym: failed to find pthread_create: %s\n", dlerror()); + abort(); + } + dlclose(handle); + + tcb_fixup(1); +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + sigprocmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + + ts->g->stackguard = size; + err = sys_pthread_create(&p, &attr, threadentry, ts); + + sigprocmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +static void* +threadentry(void *v) +{ + ThreadStart ts; + + tcb_fixup(0); + + ts = *(ThreadStart*)v; + free(v); + + ts.g->stackbase = (uintptr)&ts; + + /* + * _cgo_sys_thread_start set stackguard to stack size; + * change to actual guard pointer. + */ + ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; + + /* + * Set specific keys. On OpenBSD/ELF, the thread local storage + * is just before %fs:0. Our dynamic 6.out's reserve 16 bytes + * for the two words g and m at %fs:-16 and %fs:-8. + */ + asm volatile ( + "movq %0, %%fs:-16\n" // MOVL g, -16(FS) + "movq %1, %%fs:-8\n" // MOVL m, -8(FS) + :: "r"(ts.g), "r"(ts.m) + ); + crosscall_amd64(ts.fn); + return nil; +} diff --git a/src/pkg/runtime/cgo/gcc_setenv.c b/src/pkg/runtime/cgo/gcc_setenv.c index 7da4ad915..a0938166d 100644 --- a/src/pkg/runtime/cgo/gcc_setenv.c +++ b/src/pkg/runtime/cgo/gcc_setenv.c @@ -1,4 +1,4 @@ -// Copyright 20111 The Go Authors. All rights reserved. +// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -9,10 +9,8 @@ #include /* Stub for calling setenv */ -static void -xlibcgo_setenv(char **arg) +void +x_cgo_setenv(char **arg) { setenv(arg[0], arg[1], 1); } - -void (*libcgo_setenv)(char**) = xlibcgo_setenv; diff --git a/src/pkg/runtime/cgo/gcc_util.c b/src/pkg/runtime/cgo/gcc_util.c index e06b6f64d..20913d736 100644 --- a/src/pkg/runtime/cgo/gcc_util.c +++ b/src/pkg/runtime/cgo/gcc_util.c @@ -5,7 +5,7 @@ #include "libcgo.h" /* Stub for calling malloc from Go */ -static void +void x_cgo_malloc(void *p) { struct a { @@ -16,10 +16,8 @@ x_cgo_malloc(void *p) a->ret = malloc(a->n); } -void (*_cgo_malloc)(void*) = x_cgo_malloc; - /* Stub for calling free from Go */ -static void +void x_cgo_free(void *p) { struct a { @@ -29,11 +27,9 @@ x_cgo_free(void *p) free(a->arg); } -void (*_cgo_free)(void*) = x_cgo_free; - /* Stub for creating a new thread */ -static void -xlibcgo_thread_start(ThreadStart *arg) +void +x_cgo_thread_start(ThreadStart *arg) { ThreadStart *ts; @@ -45,7 +41,5 @@ xlibcgo_thread_start(ThreadStart *arg) } *ts = *arg; - libcgo_sys_thread_start(ts); /* OS-dependent half */ + _cgo_sys_thread_start(ts); /* OS-dependent half */ } - -void (*libcgo_thread_start)(ThreadStart*) = xlibcgo_thread_start; diff --git a/src/pkg/runtime/cgo/gcc_windows_386.c b/src/pkg/runtime/cgo/gcc_windows_386.c index 2b940d362..02eab12e5 100644 --- a/src/pkg/runtime/cgo/gcc_windows_386.c +++ b/src/pkg/runtime/cgo/gcc_windows_386.c @@ -4,31 +4,31 @@ #define WIN32_LEAN_AND_MEAN #include +#include #include "libcgo.h" -static void *threadentry(void*); +static void threadentry(void*); /* 1MB is default stack size for 32-bit Windows. Allocation granularity on Windows is typically 64 KB. The constant is also hardcoded in cmd/ld/pe.c (keep synchronized). */ #define STACKSIZE (1*1024*1024) -static void -xinitcgo(G *g) +void +x_cgo_init(G *g) { int tmp; g->stackguard = (uintptr)&tmp - STACKSIZE + 8*1024; } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) { _beginthread(threadentry, 0, ts); } -static void* +static void threadentry(void *v) { ThreadStart ts; @@ -55,5 +55,4 @@ threadentry(void *v) crosscall_386(ts.fn); LocalFree(tls0); - return nil; } diff --git a/src/pkg/runtime/cgo/gcc_windows_amd64.c b/src/pkg/runtime/cgo/gcc_windows_amd64.c index 0d2f5d233..f7695a1cc 100644 --- a/src/pkg/runtime/cgo/gcc_windows_amd64.c +++ b/src/pkg/runtime/cgo/gcc_windows_amd64.c @@ -4,31 +4,31 @@ #define WIN64_LEAN_AND_MEAN #include +#include #include "libcgo.h" -static void *threadentry(void*); +static void threadentry(void*); /* 2MB is default stack size for 64-bit Windows. Allocation granularity on Windows is typically 64 KB. The constant is also hardcoded in cmd/ld/pe.c (keep synchronized). */ #define STACKSIZE (2*1024*1024) -static void -xinitcgo(G *g) +void +x_cgo_init(G *g) { int tmp; g->stackguard = (uintptr)&tmp - STACKSIZE + 8*1024; } -void (*initcgo)(G*) = xinitcgo; void -libcgo_sys_thread_start(ThreadStart *ts) +_cgo_sys_thread_start(ThreadStart *ts) { _beginthread(threadentry, 0, ts); } -static void* +static void threadentry(void *v) { ThreadStart ts; @@ -53,5 +53,4 @@ threadentry(void *v) ); crosscall_amd64(ts.fn); - return nil; } diff --git a/src/pkg/runtime/cgo/libcgo.h b/src/pkg/runtime/cgo/libcgo.h index c31d19d76..41a371c27 100644 --- a/src/pkg/runtime/cgo/libcgo.h +++ b/src/pkg/runtime/cgo/libcgo.h @@ -26,7 +26,7 @@ struct G }; /* - * Arguments to the libcgo_thread_start call. + * Arguments to the _cgo_thread_start call. * Also known to ../pkg/runtime/runtime.h. */ typedef struct ThreadStart ThreadStart; @@ -40,14 +40,14 @@ struct ThreadStart /* * Called by 5c/6c/8c world. * Makes a local copy of the ThreadStart and - * calls libcgo_sys_thread_start(ts). + * calls _cgo_sys_thread_start(ts). */ -extern void (*libcgo_thread_start)(ThreadStart *ts); +extern void (*_cgo_thread_start)(ThreadStart *ts); /* * Creates the new operating system thread (OS, arch dependent). */ -void libcgo_sys_thread_start(ThreadStart *ts); +void _cgo_sys_thread_start(ThreadStart *ts); /* * Call fn in the 6c world. diff --git a/src/pkg/runtime/cgo/netbsd.c b/src/pkg/runtime/cgo/netbsd.c new file mode 100644 index 000000000..b6403f686 --- /dev/null +++ b/src/pkg/runtime/cgo/netbsd.c @@ -0,0 +1,13 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Supply environ and __progname, because we don't +// link against the standard NetBSD crt0.o and the +// libc dynamic library needs them. + +char *environ[1]; +char *__progname; + +#pragma dynexport environ environ +#pragma dynexport __progname __progname diff --git a/src/pkg/runtime/cgo/openbsd.c b/src/pkg/runtime/cgo/openbsd.c new file mode 100644 index 000000000..84e9f9eff --- /dev/null +++ b/src/pkg/runtime/cgo/openbsd.c @@ -0,0 +1,21 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Supply environ, __progname and __guard_local, because +// we don't link against the standard OpenBSD crt0.o and +// the libc dynamic library needs them. + +char *environ[1]; +char *__progname; +long __guard_local; + +#pragma dynexport environ environ +#pragma dynexport __progname __progname + +// This is normally marked as hidden and placed in the +// .openbsd.randomdata section. +#pragma dynexport __guard_local __guard_local + +// We override pthread_create to support PT_TLS. +#pragma dynexport pthread_create pthread_create diff --git a/src/pkg/runtime/cgo/setenv.c b/src/pkg/runtime/cgo/setenv.c new file mode 100644 index 000000000..4c47cdb00 --- /dev/null +++ b/src/pkg/runtime/cgo/setenv.c @@ -0,0 +1,10 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd + +#pragma cgo_import_static x_cgo_setenv + +void x_cgo_setenv(char**); +void (*_cgo_setenv)(char**) = x_cgo_setenv; diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c index 7a26538ec..590bf9b67 100644 --- a/src/pkg/runtime/cgocall.c +++ b/src/pkg/runtime/cgocall.c @@ -6,6 +6,7 @@ #include "arch_GOARCH.h" #include "stack.h" #include "cgocall.h" +#include "race.h" // Cgo call and callback support. // @@ -41,7 +42,7 @@ // know about packages). The gcc-compiled C function f calls GoF. // // GoF calls crosscall2(_cgoexp_GoF, frame, framesize). Crosscall2 -// (in cgo/$GOOS.S, a gcc-compiled assembly file) is a two-argument +// (in cgo/gcc_$GOARCH.S, a gcc-compiled assembly file) is a two-argument // adapter from the gcc function call ABI to the 6c function call ABI. // It is called from gcc to call 6c functions. In this case it calls // _cgoexp_GoF(frame, framesize), still running on m->g0's stack @@ -82,43 +83,55 @@ // _cgoexp_GoF immediately returns to crosscall2, which restores the // callee-save registers for gcc and returns to GoF, which returns to f. -void *initcgo; /* filled in by dynamic linker when Cgo is available */ +void *_cgo_init; /* filled in by dynamic linker when Cgo is available */ +static int64 cgosync; /* represents possible synchronization in C code */ + +// These two are only used by the architecture where TLS based storage isn't +// the default for g and m (e.g., ARM) +void *_cgo_load_gm; /* filled in by dynamic linker when Cgo is available */ +void *_cgo_save_gm; /* filled in by dynamic linker when Cgo is available */ -static void unlockm(void); static void unwindm(void); // Call from Go to C. +static FuncVal unlockOSThread = { runtime·unlockOSThread }; + void runtime·cgocall(void (*fn)(void*), void *arg) { Defer d; + if(m->racecall) { + runtime·asmcgocall(fn, arg); + return; + } + if(!runtime·iscgo && !Windows) runtime·throw("cgocall unavailable"); if(fn == 0) runtime·throw("cgocall nil"); + if(raceenabled) + runtime·racereleasemerge(&cgosync); + m->ncgocall++; /* * Lock g to m to ensure we stay on the same stack if we do a - * cgo callback. + * cgo callback. Add entry to defer stack in case of panic. */ - d.nofree = false; - if(m->lockedg == nil) { - m->lockedg = g; - g->lockedm = m; - - // Add entry to defer stack in case of panic. - d.fn = (byte*)unlockm; - d.siz = 0; - d.link = g->defer; - d.argp = (void*)-1; // unused because unlockm never recovers - d.nofree = true; - g->defer = &d; - } + runtime·lockOSThread(); + d.fn = &unlockOSThread; + d.siz = 0; + d.link = g->defer; + d.argp = (void*)-1; // unused because unlockm never recovers + d.special = true; + d.free = false; + g->defer = &d; + + m->ncgo++; /* * Announce we are entering a system call @@ -135,29 +148,31 @@ runtime·cgocall(void (*fn)(void*), void *arg) runtime·asmcgocall(fn, arg); runtime·exitsyscall(); - if(d.nofree) { - if(g->defer != &d || d.fn != (byte*)unlockm) - runtime·throw("runtime: bad defer entry in cgocallback"); - g->defer = d.link; - unlockm(); + m->ncgo--; + if(m->ncgo == 0) { + // We are going back to Go and are not in a recursive + // call. Let the GC collect any memory allocated via + // _cgo_allocate that is no longer referenced. + m->cgomal = nil; } -} -static void -unlockm(void) -{ - m->lockedg = nil; - g->lockedm = nil; + if(g->defer != &d || d.fn != &unlockOSThread) + runtime·throw("runtime: bad defer entry in cgocallback"); + g->defer = d.link; + runtime·unlockOSThread(); + + if(raceenabled) + runtime·raceacquire(&cgosync); } void runtime·NumCgoCall(int64 ret) { - M *m; + M *mp; ret = 0; - for(m=runtime·atomicloadp(&runtime·allm); m; m=m->alllink) - ret += m->ncgocall; + for(mp=runtime·atomicloadp(&runtime·allm); mp; mp=mp->alllink) + ret += mp->ncgocall; FLUSH(&ret); } @@ -188,31 +203,50 @@ runtime·cfree(void *p) // Call from C back to Go. +static FuncVal unwindmf = {unwindm}; + void -runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize) +runtime·cgocallbackg(FuncVal *fn, void *arg, uintptr argsize) { Defer d; + if(m->racecall) { + reflect·call(fn, arg, argsize); + return; + } + if(g != m->curg) runtime·throw("runtime: bad g in cgocallback"); runtime·exitsyscall(); // coming out of cgo call + if(m->needextram) { + m->needextram = 0; + runtime·newextram(); + } + // Add entry to defer stack in case of panic. - d.fn = (byte*)unwindm; + d.fn = &unwindmf; d.siz = 0; d.link = g->defer; d.argp = (void*)-1; // unused because unwindm never recovers - d.nofree = true; + d.special = true; + d.free = false; g->defer = &d; + if(raceenabled) + runtime·raceacquire(&cgosync); + // Invoke callback. - reflect·call((byte*)fn, arg, argsize); + reflect·call(fn, arg, argsize); + + if(raceenabled) + runtime·racereleasemerge(&cgosync); // Pop defer. // Do not unwind m->g0->sched.sp. // Our caller, cgocallback, will do that. - if(g->defer != &d || d.fn != (byte*)unwindm) + if(g->defer != &d || d.fn != &unwindmf) runtime·throw("runtime: bad defer entry in cgocallback"); g->defer = d.link; @@ -229,7 +263,8 @@ unwindm(void) runtime·throw("runtime: unwindm not implemented"); case '8': case '6': - m->g0->sched.sp = *(void**)m->g0->sched.sp; + case '5': + m->g0->sched.sp = *(uintptr*)m->g0->sched.sp; break; } } diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c index ef27144ef..32995c6dd 100644 --- a/src/pkg/runtime/chan.c +++ b/src/pkg/runtime/chan.c @@ -3,7 +3,10 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "arch_GOARCH.h" #include "type.h" +#include "race.h" +#include "malloc.h" #define MAXALIGN 7 #define NOSELGEN 1 @@ -20,6 +23,7 @@ struct SudoG G* g; // g and selgen constitute uint32 selgen; // a weak pointer to g SudoG* link; + int64 releasetime; byte* elem; // data element }; @@ -29,21 +33,25 @@ struct WaitQ SudoG* last; }; +// The garbage collector is assuming that Hchan can only contain pointers into the stack +// and cannot contain pointers into the heap. struct Hchan { - uint32 qcount; // total data in the q - uint32 dataqsiz; // size of the circular q + uintgo qcount; // total data in the q + uintgo dataqsiz; // size of the circular q uint16 elemsize; bool closed; uint8 elemalign; Alg* elemalg; // interface for element type - uint32 sendx; // send index - uint32 recvx; // receive index + uintgo sendx; // send index + uintgo recvx; // receive index WaitQ recvq; // list of recv waiters WaitQ sendq; // list of send waiters Lock; }; +uint32 runtime·Hchansize = sizeof(Hchan); + // Buffer follows Hchan immediately in memory. // chanbuf(c, i) is pointer to the i'th slot in the buffer. #define chanbuf(c, i) ((byte*)((c)+1)+(uintptr)(c)->elemsize*(i)) @@ -79,17 +87,22 @@ static void dequeueg(WaitQ*); static SudoG* dequeue(WaitQ*); static void enqueue(WaitQ*, SudoG*); static void destroychan(Hchan*); +static void racesync(Hchan*, SudoG*); Hchan* runtime·makechan_c(ChanType *t, int64 hint) { Hchan *c; - int32 n; + uintptr n; Type *elem; - + elem = t->elem; - if(hint < 0 || (int32)hint != hint || (elem->size > 0 && hint > ((uintptr)-1) / elem->size)) + // compiler checks this but be safe. + if(elem->size >= (1<<16)) + runtime·throw("makechan: invalid channel element type"); + + if(hint < 0 || (intgo)hint != hint || (elem->size > 0 && hint > MaxMem / elem->size)) runtime·panicstring("makechan: size out of range"); // calculate rounded size of Hchan @@ -103,18 +116,19 @@ runtime·makechan_c(ChanType *t, int64 hint) c->elemalg = elem->alg; c->elemalign = elem->align; c->dataqsiz = hint; + runtime·settype(c, (uintptr)t | TypeInfo_Chan); if(debug) - runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; elemalign=%d; dataqsiz=%d\n", - c, (int64)elem->size, elem->alg, elem->align, c->dataqsiz); + runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; elemalign=%d; dataqsiz=%D\n", + c, (int64)elem->size, elem->alg, elem->align, (int64)c->dataqsiz); return c; } // For reflect -// func makechan(typ *ChanType, size uint32) (chan) +// func makechan(typ *ChanType, size uint64) (chan) void -reflect·makechan(ChanType *t, uint32 size, Hchan *c) +reflect·makechan(ChanType *t, uint64 size, Hchan *c) { c = runtime·makechan_c(t, size); FLUSH(&c); @@ -143,11 +157,12 @@ runtime·makechan(ChanType *t, int64 hint, Hchan *ret) * the operation; we'll see that it's now closed. */ void -runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres) +runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc) { SudoG *sg; SudoG mysg; G* gp; + int64 t0; if(c == nil) { USED(t); @@ -155,9 +170,7 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres) *pres = false; return; } - g->status = Gwaiting; - g->waitreason = "chan send (nil chan)"; - runtime·gosched(); + runtime·park(nil, nil, "chan send (nil chan)"); return; // not reached } @@ -170,7 +183,17 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres) runtime·prints("\n"); } + t0 = 0; + mysg.releasetime = 0; + if(runtime·blockprofilerate > 0) { + t0 = runtime·cputicks(); + mysg.releasetime = -1; + } + runtime·lock(c); + // TODO(dvyukov): add similar instrumentation to select. + if(raceenabled) + runtime·racereadpc(c, pc, runtime·chansend); if(c->closed) goto closed; @@ -179,12 +202,16 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres) sg = dequeue(&c->recvq); if(sg != nil) { + if(raceenabled) + racesync(c, sg); runtime·unlock(c); - + gp = sg->g; gp->param = sg; if(sg->elem != nil) c->elemalg->copy(c->elemsize, sg->elem, ep); + if(sg->releasetime) + sg->releasetime = runtime·cputicks(); runtime·ready(gp); if(pres != nil) @@ -202,11 +229,8 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres) mysg.g = g; mysg.selgen = NOSELGEN; g->param = nil; - g->status = Gwaiting; - g->waitreason = "chan send"; enqueue(&c->sendq, &mysg); - runtime·unlock(c); - runtime·gosched(); + runtime·park(runtime·unlock, c, "chan send"); if(g->param == nil) { runtime·lock(c); @@ -215,6 +239,9 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres) goto closed; } + if(mysg.releasetime > 0) + runtime·blockevent(mysg.releasetime - t0, 2); + return; asynch: @@ -230,15 +257,16 @@ asynch: mysg.g = g; mysg.elem = nil; mysg.selgen = NOSELGEN; - g->status = Gwaiting; - g->waitreason = "chan send"; enqueue(&c->sendq, &mysg); - runtime·unlock(c); - runtime·gosched(); + runtime·park(runtime·unlock, c, "chan send"); runtime·lock(c); goto asynch; } + + if(raceenabled) + runtime·racerelease(chanbuf(c, c->sendx)); + c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), ep); if(++c->sendx == c->dataqsiz) c->sendx = 0; @@ -248,11 +276,15 @@ asynch: if(sg != nil) { gp = sg->g; runtime·unlock(c); + if(sg->releasetime) + sg->releasetime = runtime·cputicks(); runtime·ready(gp); } else runtime·unlock(c); if(pres != nil) *pres = true; + if(mysg.releasetime > 0) + runtime·blockevent(mysg.releasetime - t0, 2); return; closed: @@ -267,6 +299,7 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive SudoG *sg; SudoG mysg; G *gp; + int64 t0; if(runtime·gcwaiting) runtime·gosched(); @@ -280,12 +313,17 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive *selected = false; return; } - g->status = Gwaiting; - g->waitreason = "chan receive (nil chan)"; - runtime·gosched(); + runtime·park(nil, nil, "chan receive (nil chan)"); return; // not reached } + t0 = 0; + mysg.releasetime = 0; + if(runtime·blockprofilerate > 0) { + t0 = runtime·cputicks(); + mysg.releasetime = -1; + } + runtime·lock(c); if(c->dataqsiz > 0) goto asynch; @@ -295,12 +333,16 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive sg = dequeue(&c->sendq); if(sg != nil) { + if(raceenabled) + racesync(c, sg); runtime·unlock(c); if(ep != nil) c->elemalg->copy(c->elemsize, ep, sg->elem); gp = sg->g; gp->param = sg; + if(sg->releasetime) + sg->releasetime = runtime·cputicks(); runtime·ready(gp); if(selected != nil) @@ -320,11 +362,8 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive mysg.g = g; mysg.selgen = NOSELGEN; g->param = nil; - g->status = Gwaiting; - g->waitreason = "chan receive"; enqueue(&c->recvq, &mysg); - runtime·unlock(c); - runtime·gosched(); + runtime·park(runtime·unlock, c, "chan receive"); if(g->param == nil) { runtime·lock(c); @@ -335,6 +374,8 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive if(received != nil) *received = true; + if(mysg.releasetime > 0) + runtime·blockevent(mysg.releasetime - t0, 2); return; asynch: @@ -352,15 +393,16 @@ asynch: mysg.g = g; mysg.elem = nil; mysg.selgen = NOSELGEN; - g->status = Gwaiting; - g->waitreason = "chan receive"; enqueue(&c->recvq, &mysg); - runtime·unlock(c); - runtime·gosched(); + runtime·park(runtime·unlock, c, "chan receive"); runtime·lock(c); goto asynch; } + + if(raceenabled) + runtime·raceacquire(chanbuf(c, c->recvx)); + if(ep != nil) c->elemalg->copy(c->elemsize, ep, chanbuf(c, c->recvx)); c->elemalg->copy(c->elemsize, chanbuf(c, c->recvx), nil); @@ -372,6 +414,8 @@ asynch: if(sg != nil) { gp = sg->g; runtime·unlock(c); + if(sg->releasetime) + sg->releasetime = runtime·cputicks(); runtime·ready(gp); } else runtime·unlock(c); @@ -380,6 +424,8 @@ asynch: *selected = true; if(received != nil) *received = true; + if(mysg.releasetime > 0) + runtime·blockevent(mysg.releasetime - t0, 2); return; closed: @@ -389,7 +435,11 @@ closed: *selected = true; if(received != nil) *received = false; + if(raceenabled) + runtime·raceacquire(c); runtime·unlock(c); + if(mysg.releasetime > 0) + runtime·blockevent(mysg.releasetime - t0, 2); } // chansend1(hchan *chan any, elem any); @@ -397,7 +447,7 @@ closed: void runtime·chansend1(ChanType *t, Hchan* c, ...) { - runtime·chansend(t, c, (byte*)(&c+1), nil); + runtime·chansend(t, c, (byte*)(&c+1), nil, runtime·getcallerpc(&t)); } // chanrecv1(hchan *chan any) (elem any); @@ -446,8 +496,8 @@ runtime·selectnbsend(ChanType *t, Hchan *c, ...) byte *ae, *ap; ae = (byte*)(&c + 1); - ap = ae + runtime·rnd(t->elem->size, Structrnd); - runtime·chansend(t, c, ae, ap); + ap = ae + ROUND(t->elem->size, Structrnd); + runtime·chansend(t, c, ae, ap, runtime·getcallerpc(&t)); } // func selectnbrecv(elem *any, c chan any) bool @@ -474,7 +524,7 @@ void runtime·selectnbrecv(ChanType *t, byte *v, Hchan *c, bool selected) { runtime·chanrecv(t, c, v, &selected, nil); -} +} // func selectnbrecv2(elem *any, ok *bool, c chan any) bool // @@ -500,7 +550,7 @@ void runtime·selectnbrecv2(ChanType *t, byte *v, bool *received, Hchan *c, bool selected) { runtime·chanrecv(t, c, v, &selected, received); -} +} // For reflect: // func chansend(c chan, val iword, nb bool) (selected bool) @@ -509,12 +559,13 @@ runtime·selectnbrecv2(ChanType *t, byte *v, bool *received, Hchan *c, bool sele // // The "uintptr selected" is really "bool selected" but saying // uintptr gets us the right alignment for the output parameter block. +#pragma textflag 7 void reflect·chansend(ChanType *t, Hchan *c, uintptr val, bool nb, uintptr selected) { bool *sp; byte *vp; - + if(nb) { selected = false; sp = (bool*)&selected; @@ -527,7 +578,7 @@ reflect·chansend(ChanType *t, Hchan *c, uintptr val, bool nb, uintptr selected) vp = (byte*)&val; else vp = (byte*)val; - runtime·chansend(t, c, vp, sp); + runtime·chansend(t, c, vp, sp, runtime·getcallerpc(&t)); } // For reflect: @@ -571,7 +622,7 @@ runtime·newselect(int32 size, ...) int32 o; Select **selp; - o = runtime·rnd(sizeof(size), Structrnd); + o = ROUND(sizeof(size), Structrnd); selp = (Select**)((byte*)&size + o); newselect(size, selp); } @@ -619,7 +670,7 @@ runtime·selectsend(Select *sel, Hchan *c, void *elem, bool selected) // nil cases do not compete if(c == nil) return; - + selectsend(sel, c, runtime·getcallerpc(&sel), elem, (byte*)&selected - (byte*)&sel); } @@ -628,7 +679,7 @@ selectsend(Select *sel, Hchan *c, void *pc, void *elem, int32 so) { int32 i; Scase *cas; - + i = sel->ncase; if(i >= sel->tcase) runtime·throw("selectsend: too many cases"); @@ -774,9 +825,7 @@ selunlock(Select *sel) void runtime·block(void) { - g->status = Gwaiting; // forever - g->waitreason = "select (no cases)"; - runtime·gosched(); + runtime·park(nil, nil, "select (no cases)"); // forever } static void* selectgo(Select**); @@ -796,7 +845,7 @@ static void* selectgo(Select **selp) { Select *sel; - uint32 o, i, j; + uint32 o, i, j, k; Scase *cas, *dfl; Hchan *c; SudoG *sg; @@ -830,12 +879,42 @@ selectgo(Select **selp) } // sort the cases by Hchan address to get the locking order. + // simple heap sort, to guarantee n log n time and constant stack footprint. for(i=0; incase; i++) { - c = sel->scase[i].chan; - for(j=i; j>0 && sel->lockorder[j-1] >= c; j--) - sel->lockorder[j] = sel->lockorder[j-1]; + j = i; + c = sel->scase[j].chan; + while(j > 0 && sel->lockorder[k=(j-1)/2] < c) { + sel->lockorder[j] = sel->lockorder[k]; + j = k; + } + sel->lockorder[j] = c; + } + for(i=sel->ncase; i-->0; ) { + c = sel->lockorder[i]; + sel->lockorder[i] = sel->lockorder[0]; + j = 0; + for(;;) { + k = j*2+1; + if(k >= i) + break; + if(k+1 < i && sel->lockorder[k] < sel->lockorder[k+1]) + k++; + if(c < sel->lockorder[k]) { + sel->lockorder[j] = sel->lockorder[k]; + j = k; + continue; + } + break; + } sel->lockorder[j] = c; } + /* + for(i=0; i+1ncase; i++) + if(sel->lockorder[i] > sel->lockorder[i+1]) { + runtime·printf("i=%d %p %p\n", i, sel->lockorder[i], sel->lockorder[i+1]); + runtime·throw("select: broken sort"); + } + */ sellock(sel); loop: @@ -899,7 +978,7 @@ loop: case CaseRecv: enqueue(&c->recvq, sg); break; - + case CaseSend: enqueue(&c->sendq, sg); break; @@ -907,10 +986,7 @@ loop: } g->param = nil; - g->status = Gwaiting; - g->waitreason = "select"; - selunlock(sel); - runtime·gosched(); + runtime·park((void(*)(Lock*))selunlock, (Lock*)sel, "select"); sellock(sel); sg = g->param; @@ -951,6 +1027,8 @@ loop: asyncrecv: // can receive from buffer + if(raceenabled) + runtime·raceacquire(chanbuf(c, c->recvx)); if(cas->receivedp != nil) *cas->receivedp = true; if(cas->sg.elem != nil) @@ -971,6 +1049,8 @@ asyncrecv: asyncsend: // can send to buffer + if(raceenabled) + runtime·racerelease(chanbuf(c, c->sendx)); c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), cas->sg.elem); if(++c->sendx == c->dataqsiz) c->sendx = 0; @@ -987,6 +1067,8 @@ asyncsend: syncrecv: // can receive from sleeping sender (sg) + if(raceenabled) + racesync(c, sg); selunlock(sel); if(debug) runtime·printf("syncrecv: sel=%p c=%p o=%d\n", sel, c, o); @@ -1006,10 +1088,14 @@ rclose: *cas->receivedp = false; if(cas->sg.elem != nil) c->elemalg->copy(c->elemsize, cas->sg.elem, nil); + if(raceenabled) + runtime·raceacquire(c); goto retc; syncsend: // can send to sleeping receiver (sg) + if(raceenabled) + racesync(c, sg); selunlock(sel); if(debug) runtime·printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o); @@ -1020,11 +1106,17 @@ syncsend: runtime·ready(gp); retc: - // return to pc corresponding to chosen case + // return pc corresponding to chosen case. + // Set boolean passed during select creation + // (at offset selp + cas->so) to true. + // If cas->so == 0, this is a reflect-driven select and we + // don't need to update the boolean. pc = cas->pc; - as = (byte*)selp + cas->so; + if(cas->so > 0) { + as = (byte*)selp + cas->so; + *as = true; + } runtime·free(sel); - *as = true; return pc; sclose: @@ -1034,7 +1126,89 @@ sclose: return nil; // not reached } +// This struct must match ../reflect/value.go:/runtimeSelect. +typedef struct runtimeSelect runtimeSelect; +struct runtimeSelect +{ + uintptr dir; + ChanType *typ; + Hchan *ch; + uintptr val; +}; + +// This enum must match ../reflect/value.go:/SelectDir. +enum SelectDir { + SelectSend = 1, + SelectRecv, + SelectDefault, +}; + +// func rselect(cases []runtimeSelect) (chosen int, word uintptr, recvOK bool) +void +reflect·rselect(Slice cases, intgo chosen, uintptr word, bool recvOK) +{ + int32 i; + Select *sel; + runtimeSelect* rcase, *rc; + void *elem; + void *recvptr; + uintptr maxsize; + + chosen = -1; + word = 0; + recvOK = false; + + maxsize = 0; + rcase = (runtimeSelect*)cases.array; + for(i=0; idir == SelectRecv && rc->ch != nil && maxsize < rc->typ->elem->size) + maxsize = rc->typ->elem->size; + } + + recvptr = nil; + if(maxsize > sizeof(void*)) + recvptr = runtime·mal(maxsize); + + newselect(cases.len, &sel); + for(i=0; idir) { + case SelectDefault: + selectdefault(sel, (void*)i, 0); + break; + case SelectSend: + if(rc->ch == nil) + break; + if(rc->typ->elem->size > sizeof(void*)) + elem = (void*)rc->val; + else + elem = (void*)&rc->val; + selectsend(sel, rc->ch, (void*)i, elem, 0); + break; + case SelectRecv: + if(rc->ch == nil) + break; + if(rc->typ->elem->size > sizeof(void*)) + elem = recvptr; + else + elem = &word; + selectrecv(sel, rc->ch, (void*)i, elem, &recvOK, 0); + break; + } + } + + chosen = (intgo)(uintptr)selectgo(&sel); + if(rcase[chosen].dir == SelectRecv && rcase[chosen].typ->elem->size > sizeof(void*)) + word = (uintptr)recvptr; + + FLUSH(&chosen); + FLUSH(&word); + FLUSH(&recvOK); +} + // closechan(sel *byte); +#pragma textflag 7 void runtime·closechan(Hchan *c) { @@ -1053,6 +1227,11 @@ runtime·closechan(Hchan *c) runtime·panicstring("close of closed channel"); } + if(raceenabled) { + runtime·racewritepc(c, runtime·getcallerpc(&c), runtime·closechan); + runtime·racerelease(c); + } + c->closed = true; // release all readers @@ -1087,9 +1266,9 @@ reflect·chanclose(Hchan *c) } // For reflect -// func chanlen(c chan) (len int32) +// func chanlen(c chan) (len int) void -reflect·chanlen(Hchan *c, int32 len) +reflect·chanlen(Hchan *c, intgo len) { if(c == nil) len = 0; @@ -1099,9 +1278,9 @@ reflect·chanlen(Hchan *c, int32 len) } // For reflect -// func chancap(c chan) (cap int32) +// func chancap(c chan) int void -reflect·chancap(Hchan *c, int32 cap) +reflect·chancap(Hchan *c, intgo cap) { if(c == nil) cap = 0; @@ -1160,3 +1339,12 @@ enqueue(WaitQ *q, SudoG *sgp) q->last->link = sgp; q->last = sgp; } + +static void +racesync(Hchan *c, SudoG *sg) +{ + runtime·racerelease(chanbuf(c, 0)); + runtime·raceacquireg(sg->g, chanbuf(c, 0)); + runtime·racereleaseg(sg->g, chanbuf(c, 0)); + runtime·raceacquire(chanbuf(c, 0)); +} diff --git a/src/pkg/runtime/closure_386.c b/src/pkg/runtime/closure_386.c deleted file mode 100644 index b4d867711..000000000 --- a/src/pkg/runtime/closure_386.c +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "runtime.h" - -#pragma textflag 7 -// func closure(siz int32, -// fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy, -// arg0, arg1, arg2 *ptr) (func(xxx) yyy) -void -runtime·closure(int32 siz, byte *fn, byte *arg0) -{ - byte *p, *q, **ret; - int32 i, n; - int32 pcrel; - - if(siz < 0 || siz%4 != 0) - runtime·throw("bad closure size"); - - ret = (byte**)((byte*)&arg0 + siz); - - if(siz > 100) { - // TODO(rsc): implement stack growth preamble? - runtime·throw("closure too big"); - } - - // compute size of new fn. - // must match code laid out below. - n = 6+5+2+1; // SUBL MOVL MOVL CLD - if(siz <= 4*4) - n += 1*siz/4; // MOVSL MOVSL... - else - n += 6+2; // MOVL REP MOVSL - n += 5; // CALL - n += 6+1; // ADDL RET - - // store args aligned after code, so gc can find them. - n += siz; - if(n%4) - n += 4 - n%4; - - p = runtime·mal(n); - *ret = p; - q = p + n - siz; - - if(siz > 0) { - runtime·memmove(q, (byte*)&arg0, siz); - - // SUBL $siz, SP - *p++ = 0x81; - *p++ = 0xec; - *(uint32*)p = siz; - p += 4; - - // MOVL $q, SI - *p++ = 0xbe; - *(byte**)p = q; - p += 4; - - // MOVL SP, DI - *p++ = 0x89; - *p++ = 0xe7; - - // CLD - *p++ = 0xfc; - - if(siz <= 4*4) { - for(i=0; i q) - runtime·throw("bad math in sys.closure"); -} diff --git a/src/pkg/runtime/closure_amd64.c b/src/pkg/runtime/closure_amd64.c deleted file mode 100644 index 481b4a888..000000000 --- a/src/pkg/runtime/closure_amd64.c +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "runtime.h" - -#pragma textflag 7 -// func closure(siz int32, -// fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy, -// arg0, arg1, arg2 *ptr) (func(xxx) yyy) -void -runtime·closure(int32 siz, byte *fn, byte *arg0) -{ - byte *p, *q, **ret; - int32 i, n; - int64 pcrel; - - if(siz < 0 || siz%8 != 0) - runtime·throw("bad closure size"); - - ret = (byte**)((byte*)&arg0 + siz); - - if(siz > 100) { - // TODO(rsc): implement stack growth preamble? - runtime·throw("closure too big"); - } - - // compute size of new fn. - // must match code laid out below. - n = 7+10+3; // SUBQ MOVQ MOVQ - if(siz <= 4*8) - n += 2*siz/8; // MOVSQ MOVSQ... - else - n += 7+3; // MOVQ REP MOVSQ - n += 12; // CALL worst case; sometimes only 5 - n += 7+1; // ADDQ RET - - // store args aligned after code, so gc can find them. - n += siz; - if(n%8) - n += 8 - n%8; - - p = runtime·mal(n); - *ret = p; - q = p + n - siz; - - if(siz > 0) { - runtime·memmove(q, (byte*)&arg0, siz); - - // SUBQ $siz, SP - *p++ = 0x48; - *p++ = 0x81; - *p++ = 0xec; - *(uint32*)p = siz; - p += 4; - - // MOVQ $q, SI - *p++ = 0x48; - *p++ = 0xbe; - *(byte**)p = q; - p += 8; - - // MOVQ SP, DI - *p++ = 0x48; - *p++ = 0x89; - *p++ = 0xe7; - - if(siz <= 4*8) { - for(i=0; i q) - runtime·throw("bad math in sys.closure"); -} - - diff --git a/src/pkg/runtime/closure_arm.c b/src/pkg/runtime/closure_arm.c deleted file mode 100644 index 119e91b61..000000000 --- a/src/pkg/runtime/closure_arm.c +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "runtime.h" - -/* - There are two bits of magic: - - The signature of the compiler generated function uses two stack frames - as arguments (callerpc separates these frames) - - size determines how many arguments runtime.closure actually has - starting at arg0. - - Example closure with 3 captured variables: - func closure(siz int32, - fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy, - arg0, arg1, arg2 *ptr) (func(xxx) yyy) - - Code generated: - src R0 - dst R1 - end R3 - tmp R4 - frame = siz+4 - -//skip loop for 0 size closures - MOVW.W R14,-frame(R13) - - MOVW $vars(PC), R0 - MOVW $4(SP), R1 - MOVW $siz(R0), R3 -loop: MOVW.P 4(R0), R4 - MOVW.P R4, 4(R1) - CMP R0, R3 - BNE loop - - MOVW 8(PC), R0 - BL (R0) // 2 words - MOVW.P frame(R13),R15 -fptr: WORD *fn -vars: WORD arg0 - WORD arg1 - WORD arg2 -*/ - -extern void runtime·cacheflush(byte* start, byte* end); - -#pragma textflag 7 -void -runtime·closure(int32 siz, byte *fn, byte *arg0) -{ - byte *p, *q, **ret; - uint32 *pc; - int32 n; - - if(siz < 0 || siz%4 != 0) - runtime·throw("bad closure size"); - - ret = (byte**)((byte*)&arg0 + siz); - - if(siz > 100) { - // TODO(kaib): implement stack growth preamble? - runtime·throw("closure too big"); - } - - // size of new fn. - // must match code laid out below. - if (siz > 0) - n = 6 * 4 + 7 * 4; - else - n = 6 * 4; - - // store args aligned after code, so gc can find them. - n += siz; - - p = runtime·mal(n); - *ret = p; - q = p + n - siz; - - pc = (uint32*)p; - - // MOVW.W R14,-frame(R13) - *pc++ = 0xe52de000 | (siz + 4); - - if(siz > 0) { - runtime·memmove(q, (byte*)&arg0, siz); - - // MOVW $vars(PC), R0 - *pc = 0xe28f0000 | (int32)(q - (byte*)pc - 8); - pc++; - - // MOVW $4(SP), R1 - *pc++ = 0xe28d1004; - - // MOVW $siz(R0), R3 - *pc++ = 0xe2803000 | siz; - - // MOVW.P 4(R0), R4 - *pc++ = 0xe4904004; - // MOVW.P R4, 4(R1) - *pc++ = 0xe4814004; - // CMP R0, R3 - *pc++ = 0xe1530000; - // BNE loop - *pc++ = 0x1afffffb; - } - - // MOVW fptr(PC), R0 - *pc = 0xe59f0008 | (int32)((q - 4) -(byte*) pc - 8); - pc++; - - // BL (R0) - *pc++ = 0xe28fe000; - *pc++ = 0xe280f000; - - // MOVW.P frame(R13),R15 - *pc++ = 0xe49df000 | (siz + 4); - - // WORD *fn - *pc++ = (uint32)fn; - - p = (byte*)pc; - - if(p > q) - runtime·throw("bad math in sys.closure"); - - runtime·cacheflush(*ret, q+siz); -} - diff --git a/src/pkg/runtime/complex.c b/src/pkg/runtime/complex.c index eeb943940..395e70fe3 100644 --- a/src/pkg/runtime/complex.c +++ b/src/pkg/runtime/complex.c @@ -13,28 +13,30 @@ runtime·complex128div(Complex128 n, Complex128 d, Complex128 q) float64 a, b, ratio, denom; // Special cases as in C99. - ninf = runtime·isInf(n.real, 0) || runtime·isInf(n.imag, 0); - dinf = runtime·isInf(d.real, 0) || runtime·isInf(d.imag, 0); + ninf = n.real == runtime·posinf || n.real == runtime·neginf || + n.imag == runtime·posinf || n.imag == runtime·neginf; + dinf = d.real == runtime·posinf || d.real == runtime·neginf || + d.imag == runtime·posinf || d.imag == runtime·neginf; - nnan = !ninf && (runtime·isNaN(n.real) || runtime·isNaN(n.imag)); - dnan = !dinf && (runtime·isNaN(d.real) || runtime·isNaN(d.imag)); + nnan = !ninf && (ISNAN(n.real) || ISNAN(n.imag)); + dnan = !dinf && (ISNAN(d.real) || ISNAN(d.imag)); if(nnan || dnan) { - q.real = runtime·NaN(); - q.imag = runtime·NaN(); - } else if(ninf && !dinf && !dnan) { - q.real = runtime·Inf(0); - q.imag = runtime·Inf(0); - } else if(!ninf && !nnan && dinf) { + q.real = runtime·nan; + q.imag = runtime·nan; + } else if(ninf && !dinf) { + q.real = runtime·posinf; + q.imag = runtime·posinf; + } else if(!ninf && dinf) { q.real = 0; q.imag = 0; } else if(d.real == 0 && d.imag == 0) { if(n.real == 0 && n.imag == 0) { - q.real = runtime·NaN(); - q.imag = runtime·NaN(); + q.real = runtime·nan; + q.imag = runtime·nan; } else { - q.real = runtime·Inf(0); - q.imag = runtime·Inf(0); + q.real = runtime·posinf; + q.imag = runtime·posinf; } } else { // Standard complex arithmetic, factored to avoid unnecessary overflow. diff --git a/src/pkg/runtime/complex_test.go b/src/pkg/runtime/complex_test.go new file mode 100644 index 000000000..f41e6a357 --- /dev/null +++ b/src/pkg/runtime/complex_test.go @@ -0,0 +1,67 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime_test + +import ( + "math/cmplx" + "testing" +) + +var result complex128 + +func BenchmarkComplex128DivNormal(b *testing.B) { + d := 15 + 2i + n := 32 + 3i + res := 0i + for i := 0; i < b.N; i++ { + n += 0.1i + res += n / d + } + result = res +} + +func BenchmarkComplex128DivNisNaN(b *testing.B) { + d := cmplx.NaN() + n := 32 + 3i + res := 0i + for i := 0; i < b.N; i++ { + n += 0.1i + res += n / d + } + result = res +} + +func BenchmarkComplex128DivDisNaN(b *testing.B) { + d := 15 + 2i + n := cmplx.NaN() + res := 0i + for i := 0; i < b.N; i++ { + d += 0.1i + res += n / d + } + result = res +} + +func BenchmarkComplex128DivNisInf(b *testing.B) { + d := 15 + 2i + n := cmplx.Inf() + res := 0i + for i := 0; i < b.N; i++ { + d += 0.1i + res += n / d + } + result = res +} + +func BenchmarkComplex128DivDisInf(b *testing.B) { + d := cmplx.Inf() + n := 32 + 3i + res := 0i + for i := 0; i < b.N; i++ { + n += 0.1i + res += n / d + } + result = res +} diff --git a/src/pkg/runtime/cpuprof.c b/src/pkg/runtime/cpuprof.c index 05fa0cf61..9a0606a22 100644 --- a/src/pkg/runtime/cpuprof.c +++ b/src/pkg/runtime/cpuprof.c @@ -99,6 +99,7 @@ struct Profile { uint32 wtoggle; bool wholding; // holding & need to release a log half bool flushing; // flushing hash table - profile is over + bool eod_sent; // special end-of-data record sent; => flushing }; static Lock lk; @@ -109,16 +110,20 @@ static void add(Profile*, uintptr*, int32); static bool evict(Profile*, Entry*); static bool flushlog(Profile*); +static uintptr eod[3] = {0, 1, 0}; + // LostProfileData is a no-op function used in profiles // to mark the number of profiling stack traces that were // discarded due to slow data writers. -static void LostProfileData(void) { +static void +LostProfileData(void) +{ } // SetCPUProfileRate sets the CPU profiling rate. // The user documentation is in debug.go. void -runtime·SetCPUProfileRate(int32 hz) +runtime·SetCPUProfileRate(intgo hz) { uintptr *p; uintptr n; @@ -163,6 +168,7 @@ runtime·SetCPUProfileRate(int32 hz) prof->wholding = false; prof->wtoggle = 0; prof->flushing = false; + prof->eod_sent = false; runtime·noteclear(&prof->wait); runtime·setcpuprofilerate(tick, hz); @@ -356,7 +362,7 @@ getprofile(Profile *p) return ret; // Wait for new log. - runtime·entersyscall(); + runtime·entersyscallblock(); runtime·notesleep(&p->wait); runtime·exitsyscall(); runtime·noteclear(&p->wait); @@ -409,6 +415,16 @@ breakflush: } // Made it through the table without finding anything to log. + if(!p->eod_sent) { + // We may not have space to append this to the partial log buf, + // so we always return a new slice for the end-of-data marker. + p->eod_sent = true; + ret.array = (byte*)eod; + ret.len = sizeof eod; + ret.cap = ret.len; + return ret; + } + // Finally done. Clean up and return nil. p->flushing = false; if(!runtime·cas(&p->handoff, p->handoff, 0)) diff --git a/src/pkg/runtime/crash_cgo_test.go b/src/pkg/runtime/crash_cgo_test.go new file mode 100644 index 000000000..8ccea8f37 --- /dev/null +++ b/src/pkg/runtime/crash_cgo_test.go @@ -0,0 +1,88 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build cgo + +package runtime_test + +import ( + "testing" +) + +func TestCgoCrashHandler(t *testing.T) { + testCrashHandler(t, true) +} + +func TestCgoSignalDeadlock(t *testing.T) { + got := executeTest(t, cgoSignalDeadlockSource, nil) + want := "OK\n" + if got != want { + t.Fatalf("expected %q, but got %q", want, got) + } +} + +const cgoSignalDeadlockSource = ` +package main + +import "C" + +import ( + "fmt" + "runtime" + "time" +) + +func main() { + runtime.GOMAXPROCS(100) + ping := make(chan bool) + go func() { + for i := 0; ; i++ { + runtime.Gosched() + select { + case done := <-ping: + if done { + ping <- true + return + } + ping <- true + default: + } + func() { + defer func() { + recover() + }() + var s *string + *s = "" + }() + } + }() + time.Sleep(time.Millisecond) + for i := 0; i < 64; i++ { + go func() { + runtime.LockOSThread() + select {} + }() + go func() { + runtime.LockOSThread() + select {} + }() + time.Sleep(time.Millisecond) + ping <- false + select { + case <-ping: + case <-time.After(time.Second): + fmt.Printf("HANG\n") + return + } + } + ping <- true + select { + case <-ping: + case <-time.After(time.Second): + fmt.Printf("HANG\n") + return + } + fmt.Printf("OK\n") +} +` diff --git a/src/pkg/runtime/crash_test.go b/src/pkg/runtime/crash_test.go new file mode 100644 index 000000000..5f84cb5a2 --- /dev/null +++ b/src/pkg/runtime/crash_test.go @@ -0,0 +1,177 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime_test + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "text/template" +) + +func executeTest(t *testing.T, templ string, data interface{}) string { + checkStaleRuntime(t) + + st := template.Must(template.New("crashSource").Parse(templ)) + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "main.go") + f, err := os.Create(src) + if err != nil { + t.Fatalf("failed to create %v: %v", src, err) + } + err = st.Execute(f, data) + if err != nil { + f.Close() + t.Fatalf("failed to execute template: %v", err) + } + f.Close() + + got, _ := exec.Command("go", "run", src).CombinedOutput() + return string(got) +} + +func checkStaleRuntime(t *testing.T) { + // 'go run' uses the installed copy of runtime.a, which may be out of date. + out, err := exec.Command("go", "list", "-f", "{{.Stale}}", "runtime").CombinedOutput() + if err != nil { + t.Fatalf("failed to execute 'go list': %v\n%v", err, string(out)) + } + if string(out) != "false\n" { + t.Fatalf("Stale runtime.a. Run 'go install runtime'.") + } +} + +func testCrashHandler(t *testing.T, cgo bool) { + type crashTest struct { + Cgo bool + } + got := executeTest(t, crashSource, &crashTest{Cgo: cgo}) + want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" + if got != want { + t.Fatalf("expected %q, but got %q", want, got) + } +} + +func TestCrashHandler(t *testing.T) { + testCrashHandler(t, false) +} + +func testDeadlock(t *testing.T, source string) { + got := executeTest(t, source, nil) + want := "fatal error: all goroutines are asleep - deadlock!\n" + if !strings.HasPrefix(got, want) { + t.Fatalf("expected %q, but got %q", want, got) + } +} + +func TestSimpleDeadlock(t *testing.T) { + testDeadlock(t, simpleDeadlockSource) +} + +func TestInitDeadlock(t *testing.T) { + testDeadlock(t, initDeadlockSource) +} + +func TestLockedDeadlock(t *testing.T) { + testDeadlock(t, lockedDeadlockSource) +} + +func TestLockedDeadlock2(t *testing.T) { + testDeadlock(t, lockedDeadlockSource2) +} + +const crashSource = ` +package main + +import ( + "fmt" + "runtime" +) + +{{if .Cgo}} +import "C" +{{end}} + +func test(name string) { + defer func() { + if x := recover(); x != nil { + fmt.Printf(" recovered") + } + fmt.Printf(" done\n") + }() + fmt.Printf("%s:", name) + var s *string + _ = *s + fmt.Print("SHOULD NOT BE HERE") +} + +func testInNewThread(name string) { + c := make(chan bool) + go func() { + runtime.LockOSThread() + test(name) + c <- true + }() + <-c +} + +func main() { + runtime.LockOSThread() + test("main") + testInNewThread("new-thread") + testInNewThread("second-new-thread") + test("main-again") +} +` + +const simpleDeadlockSource = ` +package main +func main() { + select {} +} +` + +const initDeadlockSource = ` +package main +func init() { + select {} +} +func main() { +} +` + +const lockedDeadlockSource = ` +package main +import "runtime" +func main() { + runtime.LockOSThread() + select {} +} +` + +const lockedDeadlockSource2 = ` +package main +import ( + "runtime" + "time" +) +func main() { + go func() { + runtime.LockOSThread() + select {} + }() + time.Sleep(time.Millisecond) + select {} +} +` diff --git a/src/pkg/runtime/debug.go b/src/pkg/runtime/debug.go index b802fc63f..d82afb08e 100644 --- a/src/pkg/runtime/debug.go +++ b/src/pkg/runtime/debug.go @@ -4,7 +4,7 @@ package runtime -// Breakpoint() executes a breakpoint trap. +// Breakpoint executes a breakpoint trap. func Breakpoint() // LockOSThread wires the calling goroutine to its current operating system thread. @@ -125,6 +125,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) // blocking until data is available. If profiling is turned off and all the profile // data accumulated while it was on has been returned, CPUProfile returns nil. // The caller must save the returned data before calling CPUProfile again. +// // Most clients should use the runtime/pprof package or // the testing package's -test.cpuprofile flag instead of calling // CPUProfile directly. @@ -133,11 +134,37 @@ func CPUProfile() []byte // SetCPUProfileRate sets the CPU profiling rate to hz samples per second. // If hz <= 0, SetCPUProfileRate turns off profiling. // If the profiler is on, the rate cannot be changed without first turning it off. +// // Most clients should use the runtime/pprof package or // the testing package's -test.cpuprofile flag instead of calling // SetCPUProfileRate directly. func SetCPUProfileRate(hz int) +// SetBlockProfileRate controls the fraction of goroutine blocking events +// that are reported in the blocking profile. The profiler aims to sample +// an average of one blocking event per rate nanoseconds spent blocked. +// +// To include every blocking event in the profile, pass rate = 1. +// To turn off profiling entirely, pass rate <= 0. +func SetBlockProfileRate(rate int) + +// BlockProfileRecord describes blocking events originated +// at a particular call sequence (stack trace). +type BlockProfileRecord struct { + Count int64 + Cycles int64 + StackRecord +} + +// BlockProfile returns n, the number of records in the current blocking profile. +// If len(p) >= n, BlockProfile copies the profile into p and returns n, true. +// If len(p) < n, BlockProfile does not change p and returns n, false. +// +// Most clients should use the runtime/pprof package or +// the testing package's -test.blockprofile flag instead +// of calling BlockProfile directly. +func BlockProfile(p []BlockProfileRecord) (n int, ok bool) + // Stack formats a stack trace of the calling goroutine into buf // and returns the number of bytes written to buf. // If all is true, Stack formats stack traces of all other goroutines diff --git a/src/pkg/runtime/debug/debug.c b/src/pkg/runtime/debug/debug.c new file mode 100644 index 000000000..a7292c477 --- /dev/null +++ b/src/pkg/runtime/debug/debug.c @@ -0,0 +1,9 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Nothing to see here. +// This file exists so that the go command knows that parts of the +// package are implemented in C, so that it does not instruct the +// Go compiler to complain about extern declarations. +// The actual implementations are in package runtime. diff --git a/src/pkg/runtime/debug/garbage.go b/src/pkg/runtime/debug/garbage.go new file mode 100644 index 000000000..8f3026426 --- /dev/null +++ b/src/pkg/runtime/debug/garbage.go @@ -0,0 +1,101 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package debug + +import ( + "runtime" + "sort" + "time" +) + +// GCStats collect information about recent garbage collections. +type GCStats struct { + LastGC time.Time // time of last collection + NumGC int64 // number of garbage collections + PauseTotal time.Duration // total pause for all collections + Pause []time.Duration // pause history, most recent first + PauseQuantiles []time.Duration +} + +// Implemented in package runtime. +func readGCStats(*[]time.Duration) +func enableGC(bool) bool +func setGCPercent(int) int +func freeOSMemory() + +// ReadGCStats reads statistics about garbage collection into stats. +// The number of entries in the pause history is system-dependent; +// stats.Pause slice will be reused if large enough, reallocated otherwise. +// ReadGCStats may use the full capacity of the stats.Pause slice. +// If stats.PauseQuantiles is non-empty, ReadGCStats fills it with quantiles +// summarizing the distribution of pause time. For example, if +// len(stats.PauseQuantiles) is 5, it will be filled with the minimum, +// 25%, 50%, 75%, and maximum pause times. +func ReadGCStats(stats *GCStats) { + // Create a buffer with space for at least two copies of the + // pause history tracked by the runtime. One will be returned + // to the caller and the other will be used as a temporary buffer + // for computing quantiles. + const maxPause = len(((*runtime.MemStats)(nil)).PauseNs) + if cap(stats.Pause) < 2*maxPause { + stats.Pause = make([]time.Duration, 2*maxPause) + } + + // readGCStats fills in the pause history (up to maxPause entries) + // and then three more: Unix ns time of last GC, number of GC, + // and total pause time in nanoseconds. Here we depend on the + // fact that time.Duration's native unit is nanoseconds, so the + // pauses and the total pause time do not need any conversion. + readGCStats(&stats.Pause) + n := len(stats.Pause) - 3 + stats.LastGC = time.Unix(0, int64(stats.Pause[n])) + stats.NumGC = int64(stats.Pause[n+1]) + stats.PauseTotal = stats.Pause[n+2] + stats.Pause = stats.Pause[:n] + + if len(stats.PauseQuantiles) > 0 { + if n == 0 { + for i := range stats.PauseQuantiles { + stats.PauseQuantiles[i] = 0 + } + } else { + // There's room for a second copy of the data in stats.Pause. + // See the allocation at the top of the function. + sorted := stats.Pause[n : n+n] + copy(sorted, stats.Pause) + sort.Sort(byDuration(sorted)) + nq := len(stats.PauseQuantiles) - 1 + for i := 0; i < nq; i++ { + stats.PauseQuantiles[i] = sorted[len(sorted)*i/nq] + } + stats.PauseQuantiles[nq] = sorted[len(sorted)-1] + } + } +} + +type byDuration []time.Duration + +func (x byDuration) Len() int { return len(x) } +func (x byDuration) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byDuration) Less(i, j int) bool { return x[i] < x[j] } + +// SetGCPercent sets the garbage collection target percentage: +// a collection is triggered when the ratio of freshly allocated data +// to live data remaining after the previous collection reaches this percentage. +// SetGCPercent returns the previous setting. +// The initial setting is the value of the GOGC environment variable +// at startup, or 100 if the variable is not set. +// A negative percentage disables garbage collection. +func SetGCPercent(percent int) int { + return setGCPercent(percent) +} + +// FreeOSMemory forces a garbage collection followed by an +// attempt to return as much memory to the operating system +// as possible. (Even if this is not called, the runtime gradually +// returns memory to the operating system in a background task.) +func FreeOSMemory() { + freeOSMemory() +} diff --git a/src/pkg/runtime/debug/garbage_test.go b/src/pkg/runtime/debug/garbage_test.go new file mode 100644 index 000000000..b93cfee56 --- /dev/null +++ b/src/pkg/runtime/debug/garbage_test.go @@ -0,0 +1,100 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package debug + +import ( + "runtime" + "testing" + "time" +) + +func TestReadGCStats(t *testing.T) { + var stats GCStats + var mstats runtime.MemStats + var min, max time.Duration + + // First ReadGCStats will allocate, second should not, + // especially if we follow up with an explicit garbage collection. + stats.PauseQuantiles = make([]time.Duration, 10) + ReadGCStats(&stats) + runtime.GC() + + // Assume these will return same data: no GC during ReadGCStats. + ReadGCStats(&stats) + runtime.ReadMemStats(&mstats) + + if stats.NumGC != int64(mstats.NumGC) { + t.Errorf("stats.NumGC = %d, but mstats.NumGC = %d", stats.NumGC, mstats.NumGC) + } + if stats.PauseTotal != time.Duration(mstats.PauseTotalNs) { + t.Errorf("stats.PauseTotal = %d, but mstats.PauseTotalNs = %d", stats.PauseTotal, mstats.PauseTotalNs) + } + if stats.LastGC.UnixNano() != int64(mstats.LastGC) { + t.Errorf("stats.LastGC.UnixNano = %d, but mstats.LastGC = %d", stats.LastGC.UnixNano(), mstats.LastGC) + } + n := int(mstats.NumGC) + if n > len(mstats.PauseNs) { + n = len(mstats.PauseNs) + } + if len(stats.Pause) != n { + t.Errorf("len(stats.Pause) = %d, want %d", len(stats.Pause), n) + } else { + off := (int(mstats.NumGC) + len(mstats.PauseNs) - 1) % len(mstats.PauseNs) + for i := 0; i < n; i++ { + dt := stats.Pause[i] + if dt != time.Duration(mstats.PauseNs[off]) { + t.Errorf("stats.Pause[%d] = %d, want %d", i, dt, mstats.PauseNs[off]) + } + if max < dt { + max = dt + } + if min > dt || i == 0 { + min = dt + } + off = (off + len(mstats.PauseNs) - 1) % len(mstats.PauseNs) + } + } + + q := stats.PauseQuantiles + nq := len(q) + if q[0] != min || q[nq-1] != max { + t.Errorf("stats.PauseQuantiles = [%d, ..., %d], want [%d, ..., %d]", q[0], q[nq-1], min, max) + } + + for i := 0; i < nq-1; i++ { + if q[i] > q[i+1] { + t.Errorf("stats.PauseQuantiles[%d]=%d > stats.PauseQuantiles[%d]=%d", i, q[i], i+1, q[i+1]) + } + } +} + +var big = make([]byte, 1<<20) + +func TestFreeOSMemory(t *testing.T) { + var ms1, ms2 runtime.MemStats + + if big == nil { + t.Skip("test is not reliable when run multiple times") + } + big = nil + runtime.GC() + runtime.ReadMemStats(&ms1) + FreeOSMemory() + runtime.ReadMemStats(&ms2) + if ms1.HeapReleased >= ms2.HeapReleased { + t.Errorf("released before=%d; released after=%d; did not go up", ms1.HeapReleased, ms2.HeapReleased) + } +} + +func TestSetGCPercent(t *testing.T) { + // Test that the variable is being set and returned correctly. + // Assume the percentage itself is implemented fine during GC, + // which is harder to test. + old := SetGCPercent(123) + new := SetGCPercent(old) + if new != 123 { + t.Errorf("SetGCPercent(123); SetGCPercent(x) = %d, want 123", new) + } +} diff --git a/src/pkg/runtime/debug/stack.go b/src/pkg/runtime/debug/stack.go index a533a5c3b..2896b2141 100644 --- a/src/pkg/runtime/debug/stack.go +++ b/src/pkg/runtime/debug/stack.go @@ -29,6 +29,8 @@ func PrintStack() { // For each routine, it includes the source line information and PC value, // then attempts to discover, for Go functions, the calling function or // method and the text of the line containing the invocation. +// +// This function is deprecated. Use package runtime's Stack instead. func Stack() []byte { return stack() } diff --git a/src/pkg/runtime/debug/stack_test.go b/src/pkg/runtime/debug/stack_test.go index f33f5072b..bbd662618 100644 --- a/src/pkg/runtime/debug/stack_test.go +++ b/src/pkg/runtime/debug/stack_test.go @@ -36,7 +36,7 @@ func (t T) method() []byte { func TestStack(t *testing.T) { b := T(0).method() lines := strings.Split(string(b), "\n") - if len(lines) <= 6 { + if len(lines) < 6 { t.Fatal("too few lines") } n := 0 diff --git a/src/pkg/runtime/defs_freebsd.go b/src/pkg/runtime/defs_freebsd.go index 306e32197..084022715 100644 --- a/src/pkg/runtime/defs_freebsd.go +++ b/src/pkg/runtime/defs_freebsd.go @@ -7,8 +7,9 @@ /* Input to cgo. -GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h -GOARCH=386 cgo -cdefs defs.go >386/defs.h +GOARCH=amd64 go tool cgo -cdefs defs_freebsd.go >defs_freebsd_amd64.h +GOARCH=386 go tool cgo -cdefs defs_freebsd.go >defs_freebsd_386.h +GOARCH=arm go tool cgo -cdefs defs_freebsd.go >defs_freebsd_arm.h */ package runtime @@ -38,6 +39,8 @@ const ( MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED + MADV_FREE = C.MADV_FREE + SA_SIGINFO = C.SA_SIGINFO SA_RESTART = C.SA_RESTART SA_ONSTACK = C.SA_ONSTACK @@ -104,7 +107,6 @@ type Rtprio C.struct_rtprio type ThrParam C.struct_thr_param type Sigaltstack C.struct_sigaltstack type Sigset C.struct___sigset -type Sigval C.union_sigval type StackT C.stack_t type Siginfo C.siginfo_t diff --git a/src/pkg/runtime/defs_freebsd_386.h b/src/pkg/runtime/defs_freebsd_386.h index 29fcb8b57..d00c852c6 100644 --- a/src/pkg/runtime/defs_freebsd_386.h +++ b/src/pkg/runtime/defs_freebsd_386.h @@ -1,193 +1,190 @@ -// godefs -f -m32 defs.c +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_freebsd.go -// MACHINE GENERATED - DO NOT EDIT. -// Constants enum { - PROT_NONE = 0, - PROT_READ = 0x1, - PROT_WRITE = 0x2, - PROT_EXEC = 0x4, - MAP_ANON = 0x1000, - MAP_PRIVATE = 0x2, - MAP_FIXED = 0x10, - SA_SIGINFO = 0x40, - SA_RESTART = 0x2, - SA_ONSTACK = 0x1, - UMTX_OP_WAIT = 0x2, - UMTX_OP_WAKE = 0x3, - EINTR = 0x4, - SIGHUP = 0x1, - SIGINT = 0x2, - SIGQUIT = 0x3, - SIGILL = 0x4, - SIGTRAP = 0x5, - SIGABRT = 0x6, - SIGEMT = 0x7, - SIGFPE = 0x8, - SIGKILL = 0x9, - SIGBUS = 0xa, - SIGSEGV = 0xb, - SIGSYS = 0xc, - SIGPIPE = 0xd, - SIGALRM = 0xe, - SIGTERM = 0xf, - SIGURG = 0x10, - SIGSTOP = 0x11, - SIGTSTP = 0x12, - SIGCONT = 0x13, - SIGCHLD = 0x14, - SIGTTIN = 0x15, - SIGTTOU = 0x16, - SIGIO = 0x17, - SIGXCPU = 0x18, - SIGXFSZ = 0x19, - SIGVTALRM = 0x1a, - SIGPROF = 0x1b, - SIGWINCH = 0x1c, - SIGINFO = 0x1d, - SIGUSR1 = 0x1e, - SIGUSR2 = 0x1f, - FPE_INTDIV = 0x2, - FPE_INTOVF = 0x1, - FPE_FLTDIV = 0x3, - FPE_FLTOVF = 0x4, - FPE_FLTUND = 0x5, - FPE_FLTRES = 0x6, - FPE_FLTINV = 0x7, - FPE_FLTSUB = 0x8, - BUS_ADRALN = 0x1, - BUS_ADRERR = 0x2, - BUS_OBJERR = 0x3, - SEGV_MAPERR = 0x1, - SEGV_ACCERR = 0x2, - ITIMER_REAL = 0, - ITIMER_VIRTUAL = 0x1, - ITIMER_PROF = 0x2, + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, + + MADV_FREE = 0x5, + + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + + UMTX_OP_WAIT = 0x2, + UMTX_OP_WAKE = 0x3, + + EINTR = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGEMT = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGBUS = 0xa, + SIGSEGV = 0xb, + SIGSYS = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGTERM = 0xf, + SIGURG = 0x10, + SIGSTOP = 0x11, + SIGTSTP = 0x12, + SIGCONT = 0x13, + SIGCHLD = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGIO = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGINFO = 0x1d, + SIGUSR1 = 0x1e, + SIGUSR2 = 0x1f, + + FPE_INTDIV = 0x2, + FPE_INTOVF = 0x1, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, + + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, }; -// Types +typedef struct Rtprio Rtprio; +typedef struct ThrParam ThrParam; +typedef struct Sigaltstack Sigaltstack; +typedef struct Sigset Sigset; +typedef struct StackT StackT; +typedef struct Siginfo Siginfo; +typedef struct Mcontext Mcontext; +typedef struct Ucontext Ucontext; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Itimerval Itimerval; + #pragma pack on -typedef struct Rtprio Rtprio; struct Rtprio { - uint16 type; - uint16 prio; + uint16 type; + uint16 prio; }; - -typedef struct ThrParam ThrParam; struct ThrParam { - void *start_func; - void *arg; - int8 *stack_base; - uint32 stack_size; - int8 *tls_base; - uint32 tls_size; - int32 *child_tid; - int32 *parent_tid; - int32 flags; - Rtprio *rtp; - void* spare[3]; + void *start_func; + byte *arg; + int8 *stack_base; + uint32 stack_size; + int8 *tls_base; + uint32 tls_size; + int32 *child_tid; + int32 *parent_tid; + int32 flags; + Rtprio *rtp; + void *spare[3]; }; - -typedef struct Sigaltstack Sigaltstack; struct Sigaltstack { - int8 *ss_sp; - uint32 ss_size; - int32 ss_flags; + int8 *ss_sp; + uint32 ss_size; + int32 ss_flags; }; - -typedef struct Sigset Sigset; struct Sigset { - uint32 __bits[4]; -}; - -typedef union Sigval Sigval; -union Sigval { - int32 sival_int; - void *sival_ptr; - int32 sigval_int; - void *sigval_ptr; + uint32 __bits[4]; }; - -typedef struct StackT StackT; struct StackT { - int8 *ss_sp; - uint32 ss_size; - int32 ss_flags; + int8 *ss_sp; + uint32 ss_size; + int32 ss_flags; }; -typedef struct Siginfo Siginfo; struct Siginfo { - int32 si_signo; - int32 si_errno; - int32 si_code; - int32 si_pid; - uint32 si_uid; - int32 si_status; - void *si_addr; - Sigval si_value; - byte _reason[32]; + int32 si_signo; + int32 si_errno; + int32 si_code; + int32 si_pid; + uint32 si_uid; + int32 si_status; + byte *si_addr; + byte si_value[4]; + byte _reason[32]; }; -typedef struct Mcontext Mcontext; struct Mcontext { - int32 mc_onstack; - int32 mc_gs; - int32 mc_fs; - int32 mc_es; - int32 mc_ds; - int32 mc_edi; - int32 mc_esi; - int32 mc_ebp; - int32 mc_isp; - int32 mc_ebx; - int32 mc_edx; - int32 mc_ecx; - int32 mc_eax; - int32 mc_trapno; - int32 mc_err; - int32 mc_eip; - int32 mc_cs; - int32 mc_eflags; - int32 mc_esp; - int32 mc_ss; - int32 mc_len; - int32 mc_fpformat; - int32 mc_ownedfp; - int32 mc_spare1[1]; - int32 mc_fpstate[128]; - int32 mc_fsbase; - int32 mc_gsbase; - int32 mc_spare2[6]; + int32 mc_onstack; + int32 mc_gs; + int32 mc_fs; + int32 mc_es; + int32 mc_ds; + int32 mc_edi; + int32 mc_esi; + int32 mc_ebp; + int32 mc_isp; + int32 mc_ebx; + int32 mc_edx; + int32 mc_ecx; + int32 mc_eax; + int32 mc_trapno; + int32 mc_err; + int32 mc_eip; + int32 mc_cs; + int32 mc_eflags; + int32 mc_esp; + int32 mc_ss; + int32 mc_len; + int32 mc_fpformat; + int32 mc_ownedfp; + int32 mc_spare1[1]; + int32 mc_fpstate[128]; + int32 mc_fsbase; + int32 mc_gsbase; + int32 mc_spare2[6]; }; - -typedef struct Ucontext Ucontext; struct Ucontext { - Sigset uc_sigmask; - Mcontext uc_mcontext; - Ucontext *uc_link; - StackT uc_stack; - int32 uc_flags; - int32 __spare__[4]; - byte pad_godefs_0[12]; + Sigset uc_sigmask; + Mcontext uc_mcontext; + Ucontext *uc_link; + StackT uc_stack; + int32 uc_flags; + int32 __spare__[4]; + byte Pad_cgo_0[12]; }; -typedef struct Timespec Timespec; struct Timespec { - int32 tv_sec; - int32 tv_nsec; + int32 tv_sec; + int32 tv_nsec; }; - -typedef struct Timeval Timeval; struct Timeval { - int32 tv_sec; - int32 tv_usec; + int32 tv_sec; + int32 tv_usec; }; - -typedef struct Itimerval Itimerval; struct Itimerval { - Timeval it_interval; - Timeval it_value; + Timeval it_interval; + Timeval it_value; }; + + #pragma pack off diff --git a/src/pkg/runtime/defs_freebsd_amd64.h b/src/pkg/runtime/defs_freebsd_amd64.h index 8a222dca4..6348c0482 100644 --- a/src/pkg/runtime/defs_freebsd_amd64.h +++ b/src/pkg/runtime/defs_freebsd_amd64.h @@ -1,204 +1,201 @@ -// godefs -f -m64 defs.c +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_freebsd.go -// MACHINE GENERATED - DO NOT EDIT. -// Constants enum { - PROT_NONE = 0, - PROT_READ = 0x1, - PROT_WRITE = 0x2, - PROT_EXEC = 0x4, - MAP_ANON = 0x1000, - MAP_PRIVATE = 0x2, - MAP_FIXED = 0x10, - SA_SIGINFO = 0x40, - SA_RESTART = 0x2, - SA_ONSTACK = 0x1, - UMTX_OP_WAIT = 0x2, - UMTX_OP_WAKE = 0x3, - EINTR = 0x4, - SIGHUP = 0x1, - SIGINT = 0x2, - SIGQUIT = 0x3, - SIGILL = 0x4, - SIGTRAP = 0x5, - SIGABRT = 0x6, - SIGEMT = 0x7, - SIGFPE = 0x8, - SIGKILL = 0x9, - SIGBUS = 0xa, - SIGSEGV = 0xb, - SIGSYS = 0xc, - SIGPIPE = 0xd, - SIGALRM = 0xe, - SIGTERM = 0xf, - SIGURG = 0x10, - SIGSTOP = 0x11, - SIGTSTP = 0x12, - SIGCONT = 0x13, - SIGCHLD = 0x14, - SIGTTIN = 0x15, - SIGTTOU = 0x16, - SIGIO = 0x17, - SIGXCPU = 0x18, - SIGXFSZ = 0x19, - SIGVTALRM = 0x1a, - SIGPROF = 0x1b, - SIGWINCH = 0x1c, - SIGINFO = 0x1d, - SIGUSR1 = 0x1e, - SIGUSR2 = 0x1f, - FPE_INTDIV = 0x2, - FPE_INTOVF = 0x1, - FPE_FLTDIV = 0x3, - FPE_FLTOVF = 0x4, - FPE_FLTUND = 0x5, - FPE_FLTRES = 0x6, - FPE_FLTINV = 0x7, - FPE_FLTSUB = 0x8, - BUS_ADRALN = 0x1, - BUS_ADRERR = 0x2, - BUS_OBJERR = 0x3, - SEGV_MAPERR = 0x1, - SEGV_ACCERR = 0x2, - ITIMER_REAL = 0, - ITIMER_VIRTUAL = 0x1, - ITIMER_PROF = 0x2, + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, + + MADV_FREE = 0x5, + + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + + UMTX_OP_WAIT = 0x2, + UMTX_OP_WAKE = 0x3, + + EINTR = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGEMT = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGBUS = 0xa, + SIGSEGV = 0xb, + SIGSYS = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGTERM = 0xf, + SIGURG = 0x10, + SIGSTOP = 0x11, + SIGTSTP = 0x12, + SIGCONT = 0x13, + SIGCHLD = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGIO = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGINFO = 0x1d, + SIGUSR1 = 0x1e, + SIGUSR2 = 0x1f, + + FPE_INTDIV = 0x2, + FPE_INTOVF = 0x1, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, + + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, }; -// Types +typedef struct Rtprio Rtprio; +typedef struct ThrParam ThrParam; +typedef struct Sigaltstack Sigaltstack; +typedef struct Sigset Sigset; +typedef struct StackT StackT; +typedef struct Siginfo Siginfo; +typedef struct Mcontext Mcontext; +typedef struct Ucontext Ucontext; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Itimerval Itimerval; + #pragma pack on -typedef struct Rtprio Rtprio; struct Rtprio { - uint16 type; - uint16 prio; + uint16 type; + uint16 prio; }; - -typedef struct ThrParam ThrParam; struct ThrParam { - void *start_func; - void *arg; - int8 *stack_base; - uint64 stack_size; - int8 *tls_base; - uint64 tls_size; - int64 *child_tid; - int64 *parent_tid; - int32 flags; - byte pad_godefs_0[4]; - Rtprio *rtp; - void* spare[3]; + void *start_func; + byte *arg; + int8 *stack_base; + uint64 stack_size; + int8 *tls_base; + uint64 tls_size; + int64 *child_tid; + int64 *parent_tid; + int32 flags; + byte Pad_cgo_0[4]; + Rtprio *rtp; + void *spare[3]; }; - -typedef struct Sigaltstack Sigaltstack; struct Sigaltstack { - int8 *ss_sp; - uint64 ss_size; - int32 ss_flags; - byte pad_godefs_0[4]; + int8 *ss_sp; + uint64 ss_size; + int32 ss_flags; + byte Pad_cgo_0[4]; }; - -typedef struct Sigset Sigset; struct Sigset { - uint32 __bits[4]; -}; - -typedef union Sigval Sigval; -union Sigval { - int32 sival_int; - void *sival_ptr; - int32 sigval_int; - void *sigval_ptr; + uint32 __bits[4]; }; - -typedef struct StackT StackT; struct StackT { - int8 *ss_sp; - uint64 ss_size; - int32 ss_flags; - byte pad_godefs_0[4]; + int8 *ss_sp; + uint64 ss_size; + int32 ss_flags; + byte Pad_cgo_0[4]; }; -typedef struct Siginfo Siginfo; struct Siginfo { - int32 si_signo; - int32 si_errno; - int32 si_code; - int32 si_pid; - uint32 si_uid; - int32 si_status; - void *si_addr; - Sigval si_value; - byte _reason[40]; + int32 si_signo; + int32 si_errno; + int32 si_code; + int32 si_pid; + uint32 si_uid; + int32 si_status; + byte *si_addr; + byte si_value[8]; + byte _reason[40]; }; -typedef struct Mcontext Mcontext; struct Mcontext { - int64 mc_onstack; - int64 mc_rdi; - int64 mc_rsi; - int64 mc_rdx; - int64 mc_rcx; - int64 mc_r8; - int64 mc_r9; - int64 mc_rax; - int64 mc_rbx; - int64 mc_rbp; - int64 mc_r10; - int64 mc_r11; - int64 mc_r12; - int64 mc_r13; - int64 mc_r14; - int64 mc_r15; - uint32 mc_trapno; - uint16 mc_fs; - uint16 mc_gs; - int64 mc_addr; - uint32 mc_flags; - uint16 mc_es; - uint16 mc_ds; - int64 mc_err; - int64 mc_rip; - int64 mc_cs; - int64 mc_rflags; - int64 mc_rsp; - int64 mc_ss; - int64 mc_len; - int64 mc_fpformat; - int64 mc_ownedfp; - int64 mc_fpstate[64]; - int64 mc_fsbase; - int64 mc_gsbase; - int64 mc_spare[6]; + int64 mc_onstack; + int64 mc_rdi; + int64 mc_rsi; + int64 mc_rdx; + int64 mc_rcx; + int64 mc_r8; + int64 mc_r9; + int64 mc_rax; + int64 mc_rbx; + int64 mc_rbp; + int64 mc_r10; + int64 mc_r11; + int64 mc_r12; + int64 mc_r13; + int64 mc_r14; + int64 mc_r15; + uint32 mc_trapno; + uint16 mc_fs; + uint16 mc_gs; + int64 mc_addr; + uint32 mc_flags; + uint16 mc_es; + uint16 mc_ds; + int64 mc_err; + int64 mc_rip; + int64 mc_cs; + int64 mc_rflags; + int64 mc_rsp; + int64 mc_ss; + int64 mc_len; + int64 mc_fpformat; + int64 mc_ownedfp; + int64 mc_fpstate[64]; + int64 mc_fsbase; + int64 mc_gsbase; + int64 mc_spare[6]; }; - -typedef struct Ucontext Ucontext; struct Ucontext { - Sigset uc_sigmask; - Mcontext uc_mcontext; - Ucontext *uc_link; - StackT uc_stack; - int32 uc_flags; - int32 __spare__[4]; - byte pad_godefs_0[12]; + Sigset uc_sigmask; + Mcontext uc_mcontext; + Ucontext *uc_link; + StackT uc_stack; + int32 uc_flags; + int32 __spare__[4]; + byte Pad_cgo_0[12]; }; -typedef struct Timespec Timespec; struct Timespec { - int64 tv_sec; - int64 tv_nsec; + int64 tv_sec; + int64 tv_nsec; }; - -typedef struct Timeval Timeval; struct Timeval { - int64 tv_sec; - int64 tv_usec; + int64 tv_sec; + int64 tv_usec; }; - -typedef struct Itimerval Itimerval; struct Itimerval { - Timeval it_interval; - Timeval it_value; + Timeval it_interval; + Timeval it_value; }; + + #pragma pack off diff --git a/src/pkg/runtime/defs_freebsd_arm.h b/src/pkg/runtime/defs_freebsd_arm.h new file mode 100644 index 000000000..a744b808f --- /dev/null +++ b/src/pkg/runtime/defs_freebsd_arm.h @@ -0,0 +1,163 @@ +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_freebsd.go + + +enum { + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, + + MADV_FREE = 0x5, + + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + + UMTX_OP_WAIT = 0x2, + UMTX_OP_WAKE = 0x3, + + EINTR = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGEMT = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGBUS = 0xa, + SIGSEGV = 0xb, + SIGSYS = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGTERM = 0xf, + SIGURG = 0x10, + SIGSTOP = 0x11, + SIGTSTP = 0x12, + SIGCONT = 0x13, + SIGCHLD = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGIO = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGINFO = 0x1d, + SIGUSR1 = 0x1e, + SIGUSR2 = 0x1f, + + FPE_INTDIV = 0x2, + FPE_INTOVF = 0x1, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, + + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, +}; + +typedef struct Rtprio Rtprio; +typedef struct ThrParam ThrParam; +typedef struct Sigaltstack Sigaltstack; +typedef struct Sigset Sigset; +typedef struct StackT StackT; +typedef struct Siginfo Siginfo; +typedef struct Mcontext Mcontext; +typedef struct Ucontext Ucontext; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Itimerval Itimerval; + +#pragma pack on + +struct Rtprio { + uint16 type; + uint16 prio; +}; +struct ThrParam { + void *start_func; + byte *arg; + uint8 *stack_base; + uint32 stack_size; + uint8 *tls_base; + uint32 tls_size; + int32 *child_tid; + int32 *parent_tid; + int32 flags; + Rtprio *rtp; + void *spare[3]; +}; +struct Sigaltstack { + uint8 *ss_sp; + uint32 ss_size; + int32 ss_flags; +}; +struct Sigset { + uint32 __bits[4]; +}; +struct StackT { + uint8 *ss_sp; + uint32 ss_size; + int32 ss_flags; +}; + +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + int32 si_pid; + uint32 si_uid; + int32 si_status; + byte *si_addr; + byte si_value[4]; + byte _reason[32]; +}; + +struct Mcontext { + uint32 __gregs[17]; + byte __fpu[140]; +}; +struct Ucontext { + Sigset uc_sigmask; + Mcontext uc_mcontext; + Ucontext *uc_link; + StackT uc_stack; + int32 uc_flags; + int32 __spare__[4]; +}; + +struct Timespec { + int64 tv_sec; + int32 tv_nsec; +}; +struct Timeval { + int64 tv_sec; + int32 tv_usec; +}; +struct Itimerval { + Timeval it_interval; + Timeval it_value; +}; + + +#pragma pack off diff --git a/src/pkg/runtime/defs_linux_386.h b/src/pkg/runtime/defs_linux_386.h index 02760f987..e257a6f85 100644 --- a/src/pkg/runtime/defs_linux_386.h +++ b/src/pkg/runtime/defs_linux_386.h @@ -132,7 +132,7 @@ struct Sigaction { void *k_sa_handler; uint32 sa_flags; void *sa_restorer; - uint32 sa_mask; + uint64 sa_mask; }; struct Siginfo { int32 si_signo; diff --git a/src/pkg/runtime/defs_linux_arm.h b/src/pkg/runtime/defs_linux_arm.h index 9e5c83a07..f72ec3d1b 100644 --- a/src/pkg/runtime/defs_linux_arm.h +++ b/src/pkg/runtime/defs_linux_arm.h @@ -143,6 +143,6 @@ struct Sigaction { void *sa_handler; uint32 sa_flags; void *sa_restorer; - uint32 sa_mask; + uint64 sa_mask; }; #pragma pack off diff --git a/src/pkg/runtime/defs_netbsd.go b/src/pkg/runtime/defs_netbsd.go index 47c30cf10..53e061041 100644 --- a/src/pkg/runtime/defs_netbsd.go +++ b/src/pkg/runtime/defs_netbsd.go @@ -7,18 +7,21 @@ /* Input to cgo. -GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h -GOARCH=386 cgo -cdefs defs.go >386/defs.h +GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h +GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h */ +// +godefs map __fpregset_t [644]byte + package runtime /* #include #include +#include #include +#include #include -#include #include #include */ @@ -34,6 +37,8 @@ const ( MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED + MADV_FREE = C.MADV_FREE + SA_SIGINFO = C.SA_SIGINFO SA_RESTART = C.SA_RESTART SA_ONSTACK = C.SA_ONSTACK @@ -95,8 +100,7 @@ const ( type Sigaltstack C.struct_sigaltstack type Sigset C.sigset_t -type Siginfo C.siginfo_t -type Sigval C.union_sigval +type Siginfo C.struct__ksiginfo type StackT C.stack_t @@ -104,8 +108,5 @@ type Timespec C.struct_timespec type Timeval C.struct_timeval type Itimerval C.struct_itimerval -// This is a hack to avoid pulling in machine/fpu.h. -type sfxsave64 struct{} -type usavefpu struct{} - -type Sigcontext C.struct_sigcontext +type McontextT C.mcontext_t +type UcontextT C.ucontext_t diff --git a/src/pkg/runtime/defs_netbsd_386.go b/src/pkg/runtime/defs_netbsd_386.go new file mode 100644 index 000000000..e9e36608e --- /dev/null +++ b/src/pkg/runtime/defs_netbsd_386.go @@ -0,0 +1,42 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +Input to cgo. + +GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h +GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h +*/ + +package runtime + +/* +#include +#include +*/ +import "C" + +const ( + REG_GS = C._REG_GS + REG_FS = C._REG_FS + REG_ES = C._REG_ES + REG_DS = C._REG_DS + REG_EDI = C._REG_EDI + REG_ESI = C._REG_ESI + REG_EBP = C._REG_EBP + REG_ESP = C._REG_ESP + REG_EBX = C._REG_EBX + REG_EDX = C._REG_EDX + REG_ECX = C._REG_ECX + REG_EAX = C._REG_EAX + REG_TRAPNO = C._REG_TRAPNO + REG_ERR = C._REG_ERR + REG_EIP = C._REG_EIP + REG_CS = C._REG_CS + REG_EFL = C._REG_EFL + REG_UESP = C._REG_UESP + REG_SS = C._REG_SS +) diff --git a/src/pkg/runtime/defs_netbsd_386.h b/src/pkg/runtime/defs_netbsd_386.h index aff87fb3b..04c380e3f 100644 --- a/src/pkg/runtime/defs_netbsd_386.h +++ b/src/pkg/runtime/defs_netbsd_386.h @@ -1,146 +1,163 @@ -// godefs -f -m32 defs.c +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_netbsd.go defs_netbsd_386.go -// MACHINE GENERATED - DO NOT EDIT. -// Constants enum { - PROT_NONE = 0, - PROT_READ = 0x1, - PROT_WRITE = 0x2, - PROT_EXEC = 0x4, - MAP_ANON = 0x1000, - MAP_PRIVATE = 0x2, - MAP_FIXED = 0x10, - SA_SIGINFO = 0x40, - SA_RESTART = 0x2, - SA_ONSTACK = 0x1, - EINTR = 0x4, - SIGHUP = 0x1, - SIGINT = 0x2, - SIGQUIT = 0x3, - SIGILL = 0x4, - SIGTRAP = 0x5, - SIGABRT = 0x6, - SIGEMT = 0x7, - SIGFPE = 0x8, - SIGKILL = 0x9, - SIGBUS = 0xa, - SIGSEGV = 0xb, - SIGSYS = 0xc, - SIGPIPE = 0xd, - SIGALRM = 0xe, - SIGTERM = 0xf, - SIGURG = 0x10, - SIGSTOP = 0x11, - SIGTSTP = 0x12, - SIGCONT = 0x13, - SIGCHLD = 0x14, - SIGTTIN = 0x15, - SIGTTOU = 0x16, - SIGIO = 0x17, - SIGXCPU = 0x18, - SIGXFSZ = 0x19, - SIGVTALRM = 0x1a, - SIGPROF = 0x1b, - SIGWINCH = 0x1c, - SIGINFO = 0x1d, - SIGUSR1 = 0x1e, - SIGUSR2 = 0x1f, - FPE_INTDIV = 0x1, - FPE_INTOVF = 0x2, - FPE_FLTDIV = 0x3, - FPE_FLTOVF = 0x4, - FPE_FLTUND = 0x5, - FPE_FLTRES = 0x6, - FPE_FLTINV = 0x7, - FPE_FLTSUB = 0x8, - BUS_ADRALN = 0x1, - BUS_ADRERR = 0x2, - BUS_OBJERR = 0x3, - SEGV_MAPERR = 0x1, - SEGV_ACCERR = 0x2, - ITIMER_REAL = 0, - ITIMER_VIRTUAL = 0x1, - ITIMER_PROF = 0x2, + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, + + MADV_FREE = 0x6, + + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + + EINTR = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGEMT = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGBUS = 0xa, + SIGSEGV = 0xb, + SIGSYS = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGTERM = 0xf, + SIGURG = 0x10, + SIGSTOP = 0x11, + SIGTSTP = 0x12, + SIGCONT = 0x13, + SIGCHLD = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGIO = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGINFO = 0x1d, + SIGUSR1 = 0x1e, + SIGUSR2 = 0x1f, + + FPE_INTDIV = 0x1, + FPE_INTOVF = 0x2, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, + + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, }; -// Types +typedef struct Sigaltstack Sigaltstack; +typedef struct Sigset Sigset; +typedef struct Siginfo Siginfo; +typedef struct StackT StackT; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Itimerval Itimerval; +typedef struct McontextT McontextT; +typedef struct UcontextT UcontextT; + #pragma pack on -typedef struct Sigaltstack Sigaltstack; struct Sigaltstack { - void *ss_sp; - uint32 ss_size; - int32 ss_flags; + byte *ss_sp; + uint32 ss_size; + int32 ss_flags; }; - -typedef uint32 Sigset; - -typedef struct Siginfo Siginfo; -struct Siginfo { - int32 si_signo; - int32 si_code; - int32 si_errno; - byte _data[116]; +struct Sigset { + uint32 __bits[4]; }; - -typedef union Sigval Sigval; -union Sigval { - int32 sival_int; - void *sival_ptr; +struct Siginfo { + int32 _signo; + int32 _code; + int32 _errno; + byte _reason[20]; }; -typedef struct StackT StackT; struct StackT { - void *ss_sp; - uint32 ss_size; - int32 ss_flags; + byte *ss_sp; + uint32 ss_size; + int32 ss_flags; }; -typedef struct Timespec Timespec; struct Timespec { - int32 tv_sec; - int32 tv_nsec; + int64 tv_sec; + int32 tv_nsec; }; - -typedef struct Timeval Timeval; struct Timeval { - int32 tv_sec; - int32 tv_usec; + int64 tv_sec; + int32 tv_usec; }; - -typedef struct Itimerval Itimerval; struct Itimerval { - Timeval it_interval; - Timeval it_value; + Timeval it_interval; + Timeval it_value; }; -typedef void sfxsave64; - -typedef void usavefpu; - -typedef struct Sigcontext Sigcontext; -struct Sigcontext { - int32 sc_gs; - int32 sc_fs; - int32 sc_es; - int32 sc_ds; - int32 sc_edi; - int32 sc_esi; - int32 sc_ebp; - int32 sc_ebx; - int32 sc_edx; - int32 sc_ecx; - int32 sc_eax; - int32 sc_eip; - int32 sc_cs; - int32 sc_eflags; - int32 sc_esp; - int32 sc_ss; - int32 sc_onstack; - int32 sc_mask; - int32 sc_trapno; - int32 sc_err; - usavefpu *sc_fpstate; +struct McontextT { + int32 __gregs[19]; + byte __fpregs[644]; + int32 _mc_tlsbase; +}; +struct UcontextT { + uint32 uc_flags; + UcontextT *uc_link; + Sigset uc_sigmask; + StackT uc_stack; + McontextT uc_mcontext; + int32 __uc_pad[4]; }; + + #pragma pack off +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_netbsd.go defs_netbsd_386.go + + +enum { + REG_GS = 0x0, + REG_FS = 0x1, + REG_ES = 0x2, + REG_DS = 0x3, + REG_EDI = 0x4, + REG_ESI = 0x5, + REG_EBP = 0x6, + REG_ESP = 0x7, + REG_EBX = 0x8, + REG_EDX = 0x9, + REG_ECX = 0xa, + REG_EAX = 0xb, + REG_TRAPNO = 0xc, + REG_ERR = 0xd, + REG_EIP = 0xe, + REG_CS = 0xf, + REG_EFL = 0x10, + REG_UESP = 0x11, + REG_SS = 0x12, +}; + diff --git a/src/pkg/runtime/defs_netbsd_amd64.go b/src/pkg/runtime/defs_netbsd_amd64.go new file mode 100644 index 000000000..68f586b2f --- /dev/null +++ b/src/pkg/runtime/defs_netbsd_amd64.go @@ -0,0 +1,49 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +Input to cgo. + +GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h +GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h +*/ + +package runtime + +/* +#include +#include +*/ +import "C" + +const ( + REG_RDI = C._REG_RDI + REG_RSI = C._REG_RSI + REG_RDX = C._REG_RDX + REG_RCX = C._REG_RCX + REG_R8 = C._REG_R8 + REG_R9 = C._REG_R9 + REG_R10 = C._REG_R10 + REG_R11 = C._REG_R11 + REG_R12 = C._REG_R12 + REG_R13 = C._REG_R13 + REG_R14 = C._REG_R14 + REG_R15 = C._REG_R15 + REG_RBP = C._REG_RBP + REG_RBX = C._REG_RBX + REG_RAX = C._REG_RAX + REG_GS = C._REG_GS + REG_FS = C._REG_FS + REG_ES = C._REG_ES + REG_DS = C._REG_DS + REG_TRAPNO = C._REG_TRAPNO + REG_ERR = C._REG_ERR + REG_RIP = C._REG_RIP + REG_CS = C._REG_CS + REG_RFLAGS = C._REG_RFLAGS + REG_RSP = C._REG_RSP + REG_SS = C._REG_SS +) diff --git a/src/pkg/runtime/defs_netbsd_amd64.h b/src/pkg/runtime/defs_netbsd_amd64.h index 27bf4b9d6..3d3f576d3 100644 --- a/src/pkg/runtime/defs_netbsd_amd64.h +++ b/src/pkg/runtime/defs_netbsd_amd64.h @@ -1,158 +1,174 @@ -// godefs -f -m64 defs.c +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go -// MACHINE GENERATED - DO NOT EDIT. -// Constants enum { - PROT_NONE = 0, - PROT_READ = 0x1, - PROT_WRITE = 0x2, - PROT_EXEC = 0x4, - MAP_ANON = 0x1000, - MAP_PRIVATE = 0x2, - MAP_FIXED = 0x10, - SA_SIGINFO = 0x40, - SA_RESTART = 0x2, - SA_ONSTACK = 0x1, - EINTR = 0x4, - SIGHUP = 0x1, - SIGINT = 0x2, - SIGQUIT = 0x3, - SIGILL = 0x4, - SIGTRAP = 0x5, - SIGABRT = 0x6, - SIGEMT = 0x7, - SIGFPE = 0x8, - SIGKILL = 0x9, - SIGBUS = 0xa, - SIGSEGV = 0xb, - SIGSYS = 0xc, - SIGPIPE = 0xd, - SIGALRM = 0xe, - SIGTERM = 0xf, - SIGURG = 0x10, - SIGSTOP = 0x11, - SIGTSTP = 0x12, - SIGCONT = 0x13, - SIGCHLD = 0x14, - SIGTTIN = 0x15, - SIGTTOU = 0x16, - SIGIO = 0x17, - SIGXCPU = 0x18, - SIGXFSZ = 0x19, - SIGVTALRM = 0x1a, - SIGPROF = 0x1b, - SIGWINCH = 0x1c, - SIGINFO = 0x1d, - SIGUSR1 = 0x1e, - SIGUSR2 = 0x1f, - FPE_INTDIV = 0x1, - FPE_INTOVF = 0x2, - FPE_FLTDIV = 0x3, - FPE_FLTOVF = 0x4, - FPE_FLTUND = 0x5, - FPE_FLTRES = 0x6, - FPE_FLTINV = 0x7, - FPE_FLTSUB = 0x8, - BUS_ADRALN = 0x1, - BUS_ADRERR = 0x2, - BUS_OBJERR = 0x3, - SEGV_MAPERR = 0x1, - SEGV_ACCERR = 0x2, - ITIMER_REAL = 0, - ITIMER_VIRTUAL = 0x1, - ITIMER_PROF = 0x2, + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, + + MADV_FREE = 0x6, + + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + + EINTR = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGEMT = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGBUS = 0xa, + SIGSEGV = 0xb, + SIGSYS = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGTERM = 0xf, + SIGURG = 0x10, + SIGSTOP = 0x11, + SIGTSTP = 0x12, + SIGCONT = 0x13, + SIGCHLD = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGIO = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGINFO = 0x1d, + SIGUSR1 = 0x1e, + SIGUSR2 = 0x1f, + + FPE_INTDIV = 0x1, + FPE_INTOVF = 0x2, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, + + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, }; -// Types +typedef struct Sigaltstack Sigaltstack; +typedef struct Sigset Sigset; +typedef struct Siginfo Siginfo; +typedef struct StackT StackT; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Itimerval Itimerval; +typedef struct McontextT McontextT; +typedef struct UcontextT UcontextT; + #pragma pack on -typedef struct Sigaltstack Sigaltstack; struct Sigaltstack { - void *ss_sp; - uint64 ss_size; - int32 ss_flags; - byte pad_godefs_0[4]; + byte *ss_sp; + uint64 ss_size; + int32 ss_flags; + byte Pad_cgo_0[4]; }; - -typedef uint32 Sigset; - -typedef struct Siginfo Siginfo; -struct Siginfo { - int32 si_signo; - int32 si_code; - int32 si_errno; - byte pad_godefs_0[4]; - byte _data[120]; +struct Sigset { + uint32 __bits[4]; }; - -typedef union Sigval Sigval; -union Sigval { - int32 sival_int; - void *sival_ptr; +struct Siginfo { + int32 _signo; + int32 _code; + int32 _errno; + int32 _pad; + byte _reason[24]; }; -typedef struct StackT StackT; struct StackT { - void *ss_sp; - uint64 ss_size; - int32 ss_flags; - byte pad_godefs_0[4]; + byte *ss_sp; + uint64 ss_size; + int32 ss_flags; + byte Pad_cgo_0[4]; }; -typedef struct Timespec Timespec; struct Timespec { - int32 tv_sec; - byte pad_godefs_0[4]; - int64 tv_nsec; + int64 tv_sec; + int64 tv_nsec; }; - -typedef struct Timeval Timeval; struct Timeval { - int64 tv_sec; - int64 tv_usec; + int64 tv_sec; + int32 tv_usec; + byte Pad_cgo_0[4]; }; - -typedef struct Itimerval Itimerval; struct Itimerval { - Timeval it_interval; - Timeval it_value; + Timeval it_interval; + Timeval it_value; }; -typedef void sfxsave64; - -typedef void usavefpu; - -typedef struct Sigcontext Sigcontext; -struct Sigcontext { - int64 sc_rdi; - int64 sc_rsi; - int64 sc_rdx; - int64 sc_rcx; - int64 sc_r8; - int64 sc_r9; - int64 sc_r10; - int64 sc_r11; - int64 sc_r12; - int64 sc_r13; - int64 sc_r14; - int64 sc_r15; - int64 sc_rbp; - int64 sc_rbx; - int64 sc_rax; - int64 sc_gs; - int64 sc_fs; - int64 sc_es; - int64 sc_ds; - int64 sc_trapno; - int64 sc_err; - int64 sc_rip; - int64 sc_cs; - int64 sc_rflags; - int64 sc_rsp; - int64 sc_ss; - sfxsave64 *sc_fpstate; - int32 sc_onstack; - int32 sc_mask; +struct McontextT { + uint64 __gregs[26]; + uint64 _mc_tlsbase; + int8 __fpregs[512]; +}; +struct UcontextT { + uint32 uc_flags; + byte Pad_cgo_0[4]; + UcontextT *uc_link; + Sigset uc_sigmask; + StackT uc_stack; + McontextT uc_mcontext; }; + + #pragma pack off +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go + + +enum { + REG_RDI = 0x0, + REG_RSI = 0x1, + REG_RDX = 0x2, + REG_RCX = 0x3, + REG_R8 = 0x4, + REG_R9 = 0x5, + REG_R10 = 0x6, + REG_R11 = 0x7, + REG_R12 = 0x8, + REG_R13 = 0x9, + REG_R14 = 0xa, + REG_R15 = 0xb, + REG_RBP = 0xc, + REG_RBX = 0xd, + REG_RAX = 0xe, + REG_GS = 0xf, + REG_FS = 0x10, + REG_ES = 0x11, + REG_DS = 0x12, + REG_TRAPNO = 0x13, + REG_ERR = 0x14, + REG_RIP = 0x15, + REG_CS = 0x16, + REG_RFLAGS = 0x17, + REG_RSP = 0x18, + REG_SS = 0x19, +}; + diff --git a/src/pkg/runtime/defs_netbsd_arm.h b/src/pkg/runtime/defs_netbsd_arm.h new file mode 100644 index 000000000..f67475c76 --- /dev/null +++ b/src/pkg/runtime/defs_netbsd_arm.h @@ -0,0 +1,140 @@ +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_netbsd.go + + +enum { + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, + + MADV_FREE = 0x6, + + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + + EINTR = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGEMT = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGBUS = 0xa, + SIGSEGV = 0xb, + SIGSYS = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGTERM = 0xf, + SIGURG = 0x10, + SIGSTOP = 0x11, + SIGTSTP = 0x12, + SIGCONT = 0x13, + SIGCHLD = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGIO = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGINFO = 0x1d, + SIGUSR1 = 0x1e, + SIGUSR2 = 0x1f, + + FPE_INTDIV = 0x1, + FPE_INTOVF = 0x2, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, + + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, +}; + +typedef struct Sigaltstack Sigaltstack; +typedef struct Sigset Sigset; +typedef struct Siginfo Siginfo; +typedef struct StackT StackT; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Itimerval Itimerval; +typedef struct McontextT McontextT; +typedef struct UcontextT UcontextT; + +#pragma pack on + +struct Sigaltstack { + byte *ss_sp; + uint32 ss_size; + int32 ss_flags; +}; +struct Sigset { + uint32 __bits[4]; +}; +struct Siginfo { + int32 _signo; + int32 _code; + int32 _errno; + byte _reason[20]; +}; + +struct StackT { + byte *ss_sp; + uint32 ss_size; + int32 ss_flags; +}; + +struct Timespec { + int64 tv_sec; + int32 tv_nsec; +}; +struct Timeval { + int64 tv_sec; + int32 tv_usec; +}; +struct Itimerval { + Timeval it_interval; + Timeval it_value; +}; + +struct McontextT { + uint32 __gregs[17]; +#ifdef __ARM_EABI__ + byte __fpu[4+8*32+4]; +#else + byte __fpu[4+4*33+4]; +#endif + uint32 _mc_tlsbase; +}; +struct UcontextT { + uint32 uc_flags; + UcontextT *uc_link; + Sigset uc_sigmask; + StackT uc_stack; + McontextT uc_mcontext; + int32 __uc_pad[2]; +}; + +#pragma pack off diff --git a/src/pkg/runtime/defs_openbsd.go b/src/pkg/runtime/defs_openbsd.go index 47c30cf10..ff94b9405 100644 --- a/src/pkg/runtime/defs_openbsd.go +++ b/src/pkg/runtime/defs_openbsd.go @@ -7,8 +7,8 @@ /* Input to cgo. -GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h -GOARCH=386 cgo -cdefs defs.go >386/defs.h +GOARCH=amd64 go tool cgo -cdefs defs_openbsd.go >defs_openbsd_amd64.h +GOARCH=386 go tool cgo -cdefs defs_openbsd.go >defs_openbsd_386.h */ package runtime @@ -34,6 +34,8 @@ const ( MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED + MADV_FREE = C.MADV_FREE + SA_SIGINFO = C.SA_SIGINFO SA_RESTART = C.SA_RESTART SA_ONSTACK = C.SA_ONSTACK @@ -93,9 +95,12 @@ const ( ITIMER_PROF = C.ITIMER_PROF ) +type Tfork C.struct___tfork + type Sigaltstack C.struct_sigaltstack -type Sigset C.sigset_t +type Sigcontext C.struct_sigcontext type Siginfo C.siginfo_t +type Sigset C.sigset_t type Sigval C.union_sigval type StackT C.stack_t @@ -103,9 +108,3 @@ type StackT C.stack_t type Timespec C.struct_timespec type Timeval C.struct_timeval type Itimerval C.struct_itimerval - -// This is a hack to avoid pulling in machine/fpu.h. -type sfxsave64 struct{} -type usavefpu struct{} - -type Sigcontext C.struct_sigcontext diff --git a/src/pkg/runtime/defs_openbsd_386.h b/src/pkg/runtime/defs_openbsd_386.h index aff87fb3b..323bb084a 100644 --- a/src/pkg/runtime/defs_openbsd_386.h +++ b/src/pkg/runtime/defs_openbsd_386.h @@ -1,146 +1,150 @@ -// godefs -f -m32 defs.c +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_openbsd.go -// MACHINE GENERATED - DO NOT EDIT. -// Constants enum { - PROT_NONE = 0, - PROT_READ = 0x1, - PROT_WRITE = 0x2, - PROT_EXEC = 0x4, - MAP_ANON = 0x1000, - MAP_PRIVATE = 0x2, - MAP_FIXED = 0x10, - SA_SIGINFO = 0x40, - SA_RESTART = 0x2, - SA_ONSTACK = 0x1, - EINTR = 0x4, - SIGHUP = 0x1, - SIGINT = 0x2, - SIGQUIT = 0x3, - SIGILL = 0x4, - SIGTRAP = 0x5, - SIGABRT = 0x6, - SIGEMT = 0x7, - SIGFPE = 0x8, - SIGKILL = 0x9, - SIGBUS = 0xa, - SIGSEGV = 0xb, - SIGSYS = 0xc, - SIGPIPE = 0xd, - SIGALRM = 0xe, - SIGTERM = 0xf, - SIGURG = 0x10, - SIGSTOP = 0x11, - SIGTSTP = 0x12, - SIGCONT = 0x13, - SIGCHLD = 0x14, - SIGTTIN = 0x15, - SIGTTOU = 0x16, - SIGIO = 0x17, - SIGXCPU = 0x18, - SIGXFSZ = 0x19, - SIGVTALRM = 0x1a, - SIGPROF = 0x1b, - SIGWINCH = 0x1c, - SIGINFO = 0x1d, - SIGUSR1 = 0x1e, - SIGUSR2 = 0x1f, - FPE_INTDIV = 0x1, - FPE_INTOVF = 0x2, - FPE_FLTDIV = 0x3, - FPE_FLTOVF = 0x4, - FPE_FLTUND = 0x5, - FPE_FLTRES = 0x6, - FPE_FLTINV = 0x7, - FPE_FLTSUB = 0x8, - BUS_ADRALN = 0x1, - BUS_ADRERR = 0x2, - BUS_OBJERR = 0x3, - SEGV_MAPERR = 0x1, - SEGV_ACCERR = 0x2, - ITIMER_REAL = 0, - ITIMER_VIRTUAL = 0x1, - ITIMER_PROF = 0x2, -}; + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, -// Types -#pragma pack on + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, -typedef struct Sigaltstack Sigaltstack; -struct Sigaltstack { - void *ss_sp; - uint32 ss_size; - int32 ss_flags; -}; + MADV_FREE = 0x6, + + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + + EINTR = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGEMT = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGBUS = 0xa, + SIGSEGV = 0xb, + SIGSYS = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGTERM = 0xf, + SIGURG = 0x10, + SIGSTOP = 0x11, + SIGTSTP = 0x12, + SIGCONT = 0x13, + SIGCHLD = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGIO = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGINFO = 0x1d, + SIGUSR1 = 0x1e, + SIGUSR2 = 0x1f, + + FPE_INTDIV = 0x1, + FPE_INTOVF = 0x2, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, -typedef uint32 Sigset; + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, +}; + +typedef struct Tfork Tfork; +typedef struct Sigaltstack Sigaltstack; +typedef struct Sigcontext Sigcontext; typedef struct Siginfo Siginfo; -struct Siginfo { - int32 si_signo; - int32 si_code; - int32 si_errno; - byte _data[116]; +typedef struct StackT StackT; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Itimerval Itimerval; + +#pragma pack on + +struct Tfork { + byte *tf_tcb; + int32 *tf_tid; + byte *tf_stack; }; -typedef union Sigval Sigval; -union Sigval { - int32 sival_int; - void *sival_ptr; +struct Sigaltstack { + byte *ss_sp; + uint32 ss_size; + int32 ss_flags; }; +struct Sigcontext { + int32 sc_gs; + int32 sc_fs; + int32 sc_es; + int32 sc_ds; + int32 sc_edi; + int32 sc_esi; + int32 sc_ebp; + int32 sc_ebx; + int32 sc_edx; + int32 sc_ecx; + int32 sc_eax; + int32 sc_eip; + int32 sc_cs; + int32 sc_eflags; + int32 sc_esp; + int32 sc_ss; + int32 sc_onstack; + int32 sc_mask; + int32 sc_trapno; + int32 sc_err; + void *sc_fpstate; +}; +struct Siginfo { + int32 si_signo; + int32 si_code; + int32 si_errno; + byte _data[116]; +}; +typedef uint32 Sigset; +typedef byte Sigval[4]; -typedef struct StackT StackT; struct StackT { - void *ss_sp; - uint32 ss_size; - int32 ss_flags; + byte *ss_sp; + uint32 ss_size; + int32 ss_flags; }; -typedef struct Timespec Timespec; struct Timespec { - int32 tv_sec; - int32 tv_nsec; + int32 tv_sec; + int32 tv_nsec; }; - -typedef struct Timeval Timeval; struct Timeval { - int32 tv_sec; - int32 tv_usec; + int32 tv_sec; + int32 tv_usec; }; - -typedef struct Itimerval Itimerval; struct Itimerval { - Timeval it_interval; - Timeval it_value; + Timeval it_interval; + Timeval it_value; }; -typedef void sfxsave64; -typedef void usavefpu; - -typedef struct Sigcontext Sigcontext; -struct Sigcontext { - int32 sc_gs; - int32 sc_fs; - int32 sc_es; - int32 sc_ds; - int32 sc_edi; - int32 sc_esi; - int32 sc_ebp; - int32 sc_ebx; - int32 sc_edx; - int32 sc_ecx; - int32 sc_eax; - int32 sc_eip; - int32 sc_cs; - int32 sc_eflags; - int32 sc_esp; - int32 sc_ss; - int32 sc_onstack; - int32 sc_mask; - int32 sc_trapno; - int32 sc_err; - usavefpu *sc_fpstate; -}; #pragma pack off diff --git a/src/pkg/runtime/defs_openbsd_amd64.h b/src/pkg/runtime/defs_openbsd_amd64.h index 27bf4b9d6..429cc99f0 100644 --- a/src/pkg/runtime/defs_openbsd_amd64.h +++ b/src/pkg/runtime/defs_openbsd_amd64.h @@ -1,158 +1,162 @@ -// godefs -f -m64 defs.c +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_openbsd.go -// MACHINE GENERATED - DO NOT EDIT. -// Constants enum { - PROT_NONE = 0, - PROT_READ = 0x1, - PROT_WRITE = 0x2, - PROT_EXEC = 0x4, - MAP_ANON = 0x1000, - MAP_PRIVATE = 0x2, - MAP_FIXED = 0x10, - SA_SIGINFO = 0x40, - SA_RESTART = 0x2, - SA_ONSTACK = 0x1, - EINTR = 0x4, - SIGHUP = 0x1, - SIGINT = 0x2, - SIGQUIT = 0x3, - SIGILL = 0x4, - SIGTRAP = 0x5, - SIGABRT = 0x6, - SIGEMT = 0x7, - SIGFPE = 0x8, - SIGKILL = 0x9, - SIGBUS = 0xa, - SIGSEGV = 0xb, - SIGSYS = 0xc, - SIGPIPE = 0xd, - SIGALRM = 0xe, - SIGTERM = 0xf, - SIGURG = 0x10, - SIGSTOP = 0x11, - SIGTSTP = 0x12, - SIGCONT = 0x13, - SIGCHLD = 0x14, - SIGTTIN = 0x15, - SIGTTOU = 0x16, - SIGIO = 0x17, - SIGXCPU = 0x18, - SIGXFSZ = 0x19, - SIGVTALRM = 0x1a, - SIGPROF = 0x1b, - SIGWINCH = 0x1c, - SIGINFO = 0x1d, - SIGUSR1 = 0x1e, - SIGUSR2 = 0x1f, - FPE_INTDIV = 0x1, - FPE_INTOVF = 0x2, - FPE_FLTDIV = 0x3, - FPE_FLTOVF = 0x4, - FPE_FLTUND = 0x5, - FPE_FLTRES = 0x6, - FPE_FLTINV = 0x7, - FPE_FLTSUB = 0x8, - BUS_ADRALN = 0x1, - BUS_ADRERR = 0x2, - BUS_OBJERR = 0x3, - SEGV_MAPERR = 0x1, - SEGV_ACCERR = 0x2, - ITIMER_REAL = 0, - ITIMER_VIRTUAL = 0x1, - ITIMER_PROF = 0x2, -}; + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, -// Types -#pragma pack on + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, -typedef struct Sigaltstack Sigaltstack; -struct Sigaltstack { - void *ss_sp; - uint64 ss_size; - int32 ss_flags; - byte pad_godefs_0[4]; -}; + MADV_FREE = 0x6, + + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + + EINTR = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGEMT = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGBUS = 0xa, + SIGSEGV = 0xb, + SIGSYS = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGTERM = 0xf, + SIGURG = 0x10, + SIGSTOP = 0x11, + SIGTSTP = 0x12, + SIGCONT = 0x13, + SIGCHLD = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGIO = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGINFO = 0x1d, + SIGUSR1 = 0x1e, + SIGUSR2 = 0x1f, + + FPE_INTDIV = 0x1, + FPE_INTOVF = 0x2, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, -typedef uint32 Sigset; + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, +}; + +typedef struct Tfork Tfork; +typedef struct Sigaltstack Sigaltstack; +typedef struct Sigcontext Sigcontext; typedef struct Siginfo Siginfo; -struct Siginfo { - int32 si_signo; - int32 si_code; - int32 si_errno; - byte pad_godefs_0[4]; - byte _data[120]; +typedef struct StackT StackT; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Itimerval Itimerval; + +#pragma pack on + +struct Tfork { + byte *tf_tcb; + int32 *tf_tid; + byte *tf_stack; }; -typedef union Sigval Sigval; -union Sigval { - int32 sival_int; - void *sival_ptr; +struct Sigaltstack { + byte *ss_sp; + uint64 ss_size; + int32 ss_flags; + byte Pad_cgo_0[4]; }; +struct Sigcontext { + int64 sc_rdi; + int64 sc_rsi; + int64 sc_rdx; + int64 sc_rcx; + int64 sc_r8; + int64 sc_r9; + int64 sc_r10; + int64 sc_r11; + int64 sc_r12; + int64 sc_r13; + int64 sc_r14; + int64 sc_r15; + int64 sc_rbp; + int64 sc_rbx; + int64 sc_rax; + int64 sc_gs; + int64 sc_fs; + int64 sc_es; + int64 sc_ds; + int64 sc_trapno; + int64 sc_err; + int64 sc_rip; + int64 sc_cs; + int64 sc_rflags; + int64 sc_rsp; + int64 sc_ss; + void *sc_fpstate; + int32 sc_onstack; + int32 sc_mask; +}; +struct Siginfo { + int32 si_signo; + int32 si_code; + int32 si_errno; + byte Pad_cgo_0[4]; + byte _data[120]; +}; +typedef uint32 Sigset; +typedef byte Sigval[8]; -typedef struct StackT StackT; struct StackT { - void *ss_sp; - uint64 ss_size; - int32 ss_flags; - byte pad_godefs_0[4]; + byte *ss_sp; + uint64 ss_size; + int32 ss_flags; + byte Pad_cgo_0[4]; }; -typedef struct Timespec Timespec; struct Timespec { - int32 tv_sec; - byte pad_godefs_0[4]; - int64 tv_nsec; + int32 tv_sec; + byte Pad_cgo_0[4]; + int64 tv_nsec; }; - -typedef struct Timeval Timeval; struct Timeval { - int64 tv_sec; - int64 tv_usec; + int64 tv_sec; + int64 tv_usec; }; - -typedef struct Itimerval Itimerval; struct Itimerval { - Timeval it_interval; - Timeval it_value; + Timeval it_interval; + Timeval it_value; }; -typedef void sfxsave64; -typedef void usavefpu; - -typedef struct Sigcontext Sigcontext; -struct Sigcontext { - int64 sc_rdi; - int64 sc_rsi; - int64 sc_rdx; - int64 sc_rcx; - int64 sc_r8; - int64 sc_r9; - int64 sc_r10; - int64 sc_r11; - int64 sc_r12; - int64 sc_r13; - int64 sc_r14; - int64 sc_r15; - int64 sc_rbp; - int64 sc_rbx; - int64 sc_rax; - int64 sc_gs; - int64 sc_fs; - int64 sc_es; - int64 sc_ds; - int64 sc_trapno; - int64 sc_err; - int64 sc_rip; - int64 sc_cs; - int64 sc_rflags; - int64 sc_rsp; - int64 sc_ss; - sfxsave64 *sc_fpstate; - int32 sc_onstack; - int32 sc_mask; -}; #pragma pack off diff --git a/src/pkg/runtime/defs_plan9_386.h b/src/pkg/runtime/defs_plan9_386.h index 58fd9d94d..bde299dee 100644 --- a/src/pkg/runtime/defs_plan9_386.h +++ b/src/pkg/runtime/defs_plan9_386.h @@ -1,2 +1,29 @@ -// nothing to see here -#define tos_pid 48 +#define PAGESIZE 0x1000 + +typedef struct Ureg Ureg; + +struct Ureg +{ + uint32 di; /* general registers */ + uint32 si; /* ... */ + uint32 bp; /* ... */ + uint32 nsp; + uint32 bx; /* ... */ + uint32 dx; /* ... */ + uint32 cx; /* ... */ + uint32 ax; /* ... */ + uint32 gs; /* data segments */ + uint32 fs; /* ... */ + uint32 es; /* ... */ + uint32 ds; /* ... */ + uint32 trap; /* trap type */ + uint32 ecode; /* error code (or zero) */ + uint32 pc; /* pc */ + uint32 cs; /* old context */ + uint32 flags; /* old flags */ + union { + uint32 usp; + uint32 sp; + }; + uint32 ss; /* old stack segment */ +}; diff --git a/src/pkg/runtime/defs_plan9_amd64.h b/src/pkg/runtime/defs_plan9_amd64.h new file mode 100644 index 000000000..d8fec67eb --- /dev/null +++ b/src/pkg/runtime/defs_plan9_amd64.h @@ -0,0 +1,34 @@ +#define PAGESIZE 0x200000ULL + +typedef struct Ureg Ureg; + +struct Ureg { + uint64 ax; + uint64 bx; + uint64 cx; + uint64 dx; + uint64 si; + uint64 di; + uint64 bp; + uint64 r8; + uint64 r9; + uint64 r10; + uint64 r11; + uint64 r12; + uint64 r13; + uint64 r14; + uint64 r15; + + uint16 ds; + uint16 es; + uint16 fs; + uint16 gs; + + uint64 type; + uint64 error; /* error code (or zero) */ + uint64 ip; /* pc */ + uint64 cs; /* old context */ + uint64 flags; /* old flags */ + uint64 sp; /* sp */ + uint64 ss; /* old stack segment */ +}; diff --git a/src/pkg/runtime/env_plan9.c b/src/pkg/runtime/env_plan9.c new file mode 100644 index 000000000..848d73303 --- /dev/null +++ b/src/pkg/runtime/env_plan9.c @@ -0,0 +1,33 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "os_GOOS.h" + +byte* +runtime·getenv(int8 *s) +{ + int32 fd, len, n, r; + byte file[128]; + byte *p; + + len = runtime·findnull((byte*)s); + if(len > sizeof file-6) + return nil; + + runtime·memclr(file, sizeof file); + runtime·memmove((void*)file, (void*)"/env/", 5); + runtime·memmove((void*)(file+5), (void*)s, len); + + fd = runtime·open(file, OREAD); + if(fd < 0) + return nil; + n = runtime·seek(fd, 0, 2); + p = runtime·malloc(n+1); + r = runtime·pread(fd, p, n, 0); + runtime·close(fd); + if(r < 0) + return nil; + return p; +} diff --git a/src/pkg/runtime/env_posix.c b/src/pkg/runtime/env_posix.c new file mode 100644 index 000000000..8333811fb --- /dev/null +++ b/src/pkg/runtime/env_posix.c @@ -0,0 +1,61 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux netbsd openbsd windows + +#include "runtime.h" + +Slice syscall·envs; + +byte* +runtime·getenv(int8 *s) +{ + int32 i, j, len; + byte *v, *bs; + String* envv; + int32 envc; + + bs = (byte*)s; + len = runtime·findnull(bs); + envv = (String*)syscall·envs.array; + envc = syscall·envs.len; + for(i=0; i 0) - return x == uvinf; - return x == uvneginf; -} - -float64 -runtime·NaN(void) -{ - return runtime·float64frombits(uvnan); -} - -bool -runtime·isNaN(float64 f) -{ - uint64 x; - - x = runtime·float64tobits(f); - return ((uint32)(x>>52) & 0x7FF) == 0x7FF && !runtime·isInf(f, 0); -} - -float64 -runtime·Inf(int32 sign) -{ - if(sign >= 0) - return runtime·float64frombits(uvinf); - else - return runtime·float64frombits(uvneginf); -} - -enum -{ - MASK = 0x7ffL, - SHIFT = 64-11-1, - BIAS = 1022L, -}; - -float64 -runtime·frexp(float64 d, int32 *ep) -{ - uint64 x; - - if(d == 0) { - *ep = 0; - return 0; - } - x = runtime·float64tobits(d); - *ep = (int32)((x >> SHIFT) & MASK) - BIAS; - x &= ~((uint64)MASK << SHIFT); - x |= (uint64)BIAS << SHIFT; - return runtime·float64frombits(x); -} - -float64 -runtime·ldexp(float64 d, int32 e) -{ - uint64 x; - - if(d == 0) - return 0; - x = runtime·float64tobits(d); - e += (int32)(x >> SHIFT) & MASK; - if(e <= 0) - return 0; /* underflow */ - if(e >= MASK){ /* overflow */ - if(d < 0) - return runtime·Inf(-1); - return runtime·Inf(1); - } - x &= ~((uint64)MASK << SHIFT); - x |= (uint64)e << SHIFT; - return runtime·float64frombits(x); -} - -float64 -runtime·modf(float64 d, float64 *ip) -{ - float64 dd; - uint64 x; - int32 e; - - if(d < 1) { - if(d < 0) { - d = runtime·modf(-d, ip); - *ip = -*ip; - return -d; - } - *ip = 0; - return d; - } - - x = runtime·float64tobits(d); - e = (int32)((x >> SHIFT) & MASK) - BIAS; - - /* - * Keep the top 11+e bits; clear the rest. - */ - if(e <= 64-11) - x &= ~(((uint64)1 << (64LL-11LL-e))-1); - dd = runtime·float64frombits(x); - *ip = dd; - return d - dd; -} - +// used as float64 via runtime· names +uint64 ·nan = 0x7FF8000000000001ULL; +uint64 ·posinf = 0x7FF0000000000000ULL; +uint64 ·neginf = 0xFFF0000000000000ULL; diff --git a/src/pkg/runtime/gc_test.go b/src/pkg/runtime/gc_test.go index 65894a6fd..e1e1b1d01 100644 --- a/src/pkg/runtime/gc_test.go +++ b/src/pkg/runtime/gc_test.go @@ -5,37 +5,80 @@ package runtime_test import ( + "os" "runtime" "testing" ) func TestGcSys(t *testing.T) { + if os.Getenv("GOGC") == "off" { + t.Fatalf("GOGC=off in environment; test cannot pass") + } + data := struct{ Short bool }{testing.Short()} + got := executeTest(t, testGCSysSource, &data) + want := "OK\n" + if got != want { + t.Fatalf("expected %q, but got %q", want, got) + } +} + +const testGCSysSource = ` +package main + +import ( + "fmt" + "runtime" +) + +func main() { + runtime.GOMAXPROCS(1) memstats := new(runtime.MemStats) runtime.GC() runtime.ReadMemStats(memstats) sys := memstats.Sys + runtime.MemProfileRate = 0 // disable profiler + itercount := 1000000 - if testing.Short() { - itercount = 100000 - } +{{if .Short}} + itercount = 100000 +{{end}} for i := 0; i < itercount; i++ { workthegc() } // Should only be using a few MB. + // We allocated 100 MB or (if not short) 1 GB. runtime.ReadMemStats(memstats) if sys > memstats.Sys { sys = 0 } else { sys = memstats.Sys - sys } - t.Logf("used %d extra bytes", sys) - if sys > 4<<20 { - t.Fatalf("using too much memory: %d bytes", sys) + if sys > 16<<20 { + fmt.Printf("using too much memory: %d bytes\n", sys) + return } + fmt.Printf("OK\n") } func workthegc() []byte { return make([]byte, 1029) } +` + +func TestGcDeepNesting(t *testing.T) { + type T [2][2][2][2][2][2][2][2][2][2]*int + a := new(T) + + // Prevent the compiler from applying escape analysis. + // This makes sure new(T) is allocated on heap, not on the stack. + t.Logf("%p", a) + + a[0][0][0][0][0][0][0][0][0][0] = new(int) + *a[0][0][0][0][0][0][0][0][0][0] = 13 + runtime.GC() + if *a[0][0][0][0][0][0][0][0][0][0] != 13 { + t.Fail() + } +} diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c index 63ed4e2a3..37111daa9 100644 --- a/src/pkg/runtime/hashmap.c +++ b/src/pkg/runtime/hashmap.c @@ -3,8 +3,11 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "arch_GOARCH.h" +#include "malloc.h" #include "hashmap.h" #include "type.h" +#include "race.h" /* Hmap flag values */ #define IndirectVal (1<<0) /* storing pointers to values */ @@ -13,7 +16,7 @@ #define CanFreeKey (1<<3) /* okay to free pointers to keys */ struct Hmap { /* a hash table; initialize with hash_init() */ - uint32 count; /* elements in table - must be first */ + uintgo count; /* elements in table - must be first */ uint8 datasize; /* amount of data to store in entry */ uint8 flag; uint8 valoff; /* offset of value in key+value data block */ @@ -78,7 +81,7 @@ hash_subtable_new (Hmap *h, int32 power, int32 used) max_probes = 1 << power; } bytes += limit_bytes - elemsize; - st = malloc (offsetof (struct hash_subtable, entry[0]) + bytes); + st = runtime·mallocgc(offsetof (struct hash_subtable, entry[0]) + bytes, UseSpanType ? FlagNoPointers : 0, 1, 1); st->power = power; st->used = used; st->datasize = h->datasize; @@ -115,7 +118,7 @@ hash_init (Hmap *h, int32 datasize, int64 hint) if(datasize < sizeof (void *)) datasize = sizeof (void *); - datasize = runtime·rnd(datasize, sizeof (void *)); + datasize = ROUND(datasize, sizeof (void *)); init_sizes (hint, &init_power); h->datasize = datasize; assert (h->datasize == datasize); @@ -273,7 +276,7 @@ hash_lookup (MapType *t, Hmap *h, void *data, void **pres) struct hash_entry *end_e; void *key; bool eq; - + hash = h->hash0; (*t->key->alg->hash) (&hash, t->key->size, data); hash &= ~HASH_MASK; @@ -416,8 +419,12 @@ hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_ *pres = ins_e->data; return (1); } - assert (e_hash != hash || (flags & HASH_REHASH) == 0); - hash += (e_hash == hash); /* adjust hash if it collides */ + if (e_hash == hash) { /* adjust hash if it collides */ + assert ((flags & HASH_REHASH) == 0); + hash++; + if ((hash & HASH_MASK) == HASH_SUBHASH) + runtime·throw("runtime: map hash collision overflow"); + } ins_e = HASH_OFFSET (ins_e, elemsize); ins_i++; if (e_hash <= hash) { /* set e to insertion point */ @@ -462,7 +469,7 @@ hash_insert (MapType *t, Hmap *h, void *data, void **pres) { uintptr hash; int32 rc; - + hash = h->hash0; (*t->key->alg->hash) (&hash, t->key->size, data); rc = hash_insert_internal (t, &h->st, 0, hash, h, data, pres); @@ -618,7 +625,7 @@ hash_iter_init (MapType *t, Hmap *h, struct hash_iter *it) it->subtable_state[0].e = h->st->entry; it->subtable_state[0].start = h->st->entry; it->subtable_state[0].last = h->st->last; - + // fastrand1 returns 31 useful bits. // We don't care about not having a bottom bit but we // do want top bits. @@ -700,6 +707,82 @@ hash_visit (Hmap *h, void (*data_visit) (void *arg, int32 level, void *data), vo hash_visit_internal (h->st, 0, 0, data_visit, arg); } +// Initialize the iterator. +// Returns false if Hmap contains no pointers (in which case the iterator is not initialized). +bool +hash_gciter_init (Hmap *h, struct hash_gciter *it) +{ + // GC during map initialization + if(h->st == nil) + return false; + + it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + it->flag = h->flag; + it->valoff = h->valoff; + it->i = 0; + it->st = h->st; + it->subtable_state[it->i].e = h->st->entry; + it->subtable_state[it->i].last = h->st->last; + return true; +} + +// Returns true and fills *data with subtable/key/value data, +// or returns false if the iterator has terminated. +bool +hash_gciter_next (struct hash_gciter *it, struct hash_gciter_data *data) +{ + struct hash_entry *e; + struct hash_gciter_sub *sub; + + data->st = nil; + data->key_data = nil; + data->val_data = nil; + + // pointer to the first-level table + if(it->st != nil) { + data->st = it->st; + it->st = nil; + return true; + } + +popped: + sub = &it->subtable_state[it->i]; + e = sub->e; + while (e <= sub->last) { + if ((e->hash & HASH_MASK) == HASH_SUBHASH) { + struct hash_subtable *st = *(struct hash_subtable **)e->data; + data->st = st; + sub->e = HASH_OFFSET (e, it->elemsize); + + // push + it->i++; + assert (it->i < nelem(it->subtable_state)); + sub++; + sub->e = st->entry; + sub->last = st->last; + + return true; + } + if(e->hash != HASH_NIL) { + void *key_data = e->data; + void *val_data = (byte*)e->data + it->valoff; + data->key_data = key_data; + data->val_data = val_data; + data->indirectkey = (it->flag & IndirectKey) != 0; + data->indirectval = (it->flag & IndirectVal) != 0; + sub->e = HASH_OFFSET (e, it->elemsize); + return true; + } + e = HASH_OFFSET (e, it->elemsize); + } + if(it->i != 0) { + // pop + it->i--; + goto popped; + } + return false; +} + // /// interfaces to go runtime // @@ -724,14 +807,13 @@ hash_keyptr(Hmap *h, void *p) static int32 debug = 0; -// makemap(typ *Type, hint uint32) (hmap *map[any]any); Hmap* runtime·makemap_c(MapType *typ, int64 hint) { Hmap *h; Type *key, *val; uintptr ksize, vsize; - + key = typ->key; val = typ->elem; @@ -744,8 +826,15 @@ runtime·makemap_c(MapType *typ, int64 hint) h = runtime·mal(sizeof(*h)); h->flag |= CanFreeTable; /* until reflect gets involved, free is okay */ - ksize = runtime·rnd(key->size, sizeof(void*)); - vsize = runtime·rnd(val->size, sizeof(void*)); + if(UseSpanType) { + if(false) { + runtime·printf("makemap %S: %p\n", *typ->string, h); + } + runtime·settype(h, (uintptr)typ | TypeInfo_Map); + } + + ksize = ROUND(key->size, sizeof(void*)); + vsize = ROUND(val->size, sizeof(void*)); if(ksize > MaxData || vsize > MaxData || ksize+vsize > MaxData) { // Either key is too big, or value is, or combined they are. // Prefer to keep the key if possible, because we look at @@ -828,8 +917,11 @@ runtime·mapaccess1(MapType *t, Hmap *h, ...) byte *ak, *av; bool pres; + if(raceenabled && h != nil) + runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapaccess1); + ak = (byte*)(&h + 1); - av = ak + runtime·rnd(t->key->size, Structrnd); + av = ak + ROUND(t->key->size, Structrnd); runtime·mapaccess(t, h, ak, av, &pres); @@ -853,8 +945,11 @@ runtime·mapaccess2(MapType *t, Hmap *h, ...) { byte *ak, *av, *ap; + if(raceenabled && h != nil) + runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapaccess2); + ak = (byte*)(&h + 1); - av = ak + runtime·rnd(t->key->size, Structrnd); + av = ak + ROUND(t->key->size, Structrnd); ap = av + t->elem->size; runtime·mapaccess(t, h, ak, av, ap); @@ -881,6 +976,9 @@ reflect·mapaccess(MapType *t, Hmap *h, uintptr key, uintptr val, bool pres) { byte *ak, *av; + if(raceenabled && h != nil) + runtime·racereadpc(h, runtime·getcallerpc(&t), reflect·mapaccess); + if(t->key->size <= sizeof(key)) ak = (byte*)&key; else @@ -951,8 +1049,10 @@ runtime·mapassign1(MapType *t, Hmap *h, ...) if(h == nil) runtime·panicstring("assignment to entry in nil map"); + if(raceenabled) + runtime·racewritepc(h, runtime·getcallerpc(&t), runtime·mapassign1); ak = (byte*)(&h + 1); - av = ak + runtime·rnd(t->key->size, t->elem->align); + av = ak + ROUND(t->key->size, t->elem->align); runtime·mapassign(t, h, ak, av); } @@ -965,8 +1065,10 @@ runtime·mapdelete(MapType *t, Hmap *h, ...) byte *ak; if(h == nil) - runtime·panicstring("deletion of entry in nil map"); + return; + if(raceenabled) + runtime·racewritepc(h, runtime·getcallerpc(&t), runtime·mapdelete); ak = (byte*)(&h + 1); runtime·mapassign(t, h, ak, nil); @@ -990,6 +1092,8 @@ reflect·mapassign(MapType *t, Hmap *h, uintptr key, uintptr val, bool pres) if(h == nil) runtime·panicstring("assignment to entry in nil map"); + if(raceenabled) + runtime·racewritepc(h, runtime·getcallerpc(&t), reflect·mapassign); if(t->key->size <= sizeof(key)) ak = (byte*)&key; else @@ -1011,6 +1115,8 @@ runtime·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) it->data = nil; return; } + if(raceenabled) + runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapiterinit); hash_iter_init(t, h, it); it->data = hash_next(it); if(debug) { @@ -1054,6 +1160,8 @@ reflect·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) void runtime·mapiternext(struct hash_iter *it) { + if(raceenabled) + runtime·racereadpc(it->h, runtime·getcallerpc(&it), runtime·mapiternext); if(runtime·gcwaiting) runtime·gosched(); @@ -1148,15 +1256,18 @@ reflect·mapiterkey(struct hash_iter *it, uintptr key, bool ok) } // For reflect: -// func maplen(h map) (len int32) +// func maplen(h map) (len int) // Like len(m) in the actual language, we treat the nil map as length 0. void -reflect·maplen(Hmap *h, int32 len) +reflect·maplen(Hmap *h, intgo len) { if(h == nil) len = 0; - else + else { len = h->count; + if(raceenabled) + runtime·racereadpc(h, runtime·getcallerpc(&h), reflect·maplen); + } FLUSH(&len); } @@ -1171,7 +1282,7 @@ runtime·mapiter2(struct hash_iter *it, ...) t = it->t; ak = (byte*)(&it + 1); - av = ak + runtime·rnd(t->key->size, t->elem->align); + av = ak + ROUND(t->key->size, t->elem->align); res = it->data; if(res == nil) diff --git a/src/pkg/runtime/hashmap.h b/src/pkg/runtime/hashmap.h index 4c10cf6ef..9b82f299e 100644 --- a/src/pkg/runtime/hashmap.h +++ b/src/pkg/runtime/hashmap.h @@ -63,7 +63,6 @@ } */ -#define malloc runtime·mal #define memset(a,b,c) runtime·memclr((byte*)(a), (uint32)(c)) #define memcpy(a,b,c) runtime·memmove((byte*)(a),(byte*)(b),(uint32)(c)) #define assert(a) if(!(a)) runtime·throw("hashmap assert") @@ -143,7 +142,7 @@ struct hash_iter { Remove all sub-tables associated with *h. This undoes the effects of hash_init(). If other memory pointed to by user data must be freed, the caller is - responsible for doiing do by iterating over *h first; see + responsible for doing so by iterating over *h first; see hash_iter_init()/hash_next(). */ // void hash_destroy (struct hash *h); @@ -152,7 +151,7 @@ struct hash_iter { /* Initialize *it from *h. */ // void hash_iter_init (struct hash *h, struct hash_iter *it); -/* Return the next used entry in the table which which *it was initialized. */ +/* Return the next used entry in the table with which *it was initialized. */ // void *hash_next (struct hash_iter *it); /*---- test interface ----*/ @@ -160,3 +159,27 @@ struct hash_iter { whether used or not. "level" is the subtable level, 0 means first level. */ /* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */ // void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg); + +/* Used by the garbage collector */ +struct hash_gciter +{ + int32 elemsize; + uint8 flag; + uint8 valoff; + uint32 i; /* stack pointer in subtable_state */ + struct hash_subtable *st; + struct hash_gciter_sub { + struct hash_entry *e; /* pointer into subtable */ + struct hash_entry *last; /* last entry in subtable */ + } subtable_state[4]; +}; +struct hash_gciter_data +{ + struct hash_subtable *st; /* subtable pointer, or nil */ + uint8 *key_data; /* key data, or nil */ + uint8 *val_data; /* value data, or nil */ + bool indirectkey; /* storing pointers to keys */ + bool indirectval; /* storing pointers to values */ +}; +bool hash_gciter_init (struct Hmap *h, struct hash_gciter *it); +bool hash_gciter_next (struct hash_gciter *it, struct hash_gciter_data *data); diff --git a/src/pkg/runtime/iface.c b/src/pkg/runtime/iface.c index 2b60c4f23..370edffb8 100644 --- a/src/pkg/runtime/iface.c +++ b/src/pkg/runtime/iface.c @@ -5,6 +5,7 @@ #include "runtime.h" #include "arch_GOARCH.h" #include "type.h" +#include "typekind.h" #include "malloc.h" void @@ -19,19 +20,6 @@ runtime·printeface(Eface e) runtime·printf("(%p,%p)", e.type, e.data); } -/* - * layout of Itab known to compilers - */ -struct Itab -{ - InterfaceType* inter; - Type* type; - Itab* link; - int32 bad; - int32 unused; - void (*fun[])(void); -}; - static Itab* hash[1009]; static Lock ifacelock; @@ -182,19 +170,37 @@ copyout(Type *t, void **src, void *dst) alg->copy(size, dst, *src); } -// func convT2I(typ *byte, typ2 *byte, elem any) (ret any) #pragma textflag 7 void -runtime·convT2I(Type *t, InterfaceType *inter, ...) +runtime·typ2Itab(Type *t, InterfaceType *inter, Itab **cache, Itab *ret) +{ + Itab *tab; + + tab = itab(inter, t, 0); + runtime·atomicstorep(cache, tab); + ret = tab; + FLUSH(&ret); +} + +// func convT2I(typ *byte, typ2 *byte, cache **byte, elem any) (ret any) +#pragma textflag 7 +void +runtime·convT2I(Type *t, InterfaceType *inter, Itab **cache, ...) { byte *elem; Iface *ret; + Itab *tab; int32 wid; - elem = (byte*)(&inter+1); + elem = (byte*)(&cache+1); wid = t->size; - ret = (Iface*)(elem + runtime·rnd(wid, Structrnd)); - ret->tab = itab(inter, t, 0); + ret = (Iface*)(elem + ROUND(wid, Structrnd)); + tab = runtime·atomicloadp(cache); + if(!tab) { + tab = itab(inter, t, 0); + runtime·atomicstorep(cache, tab); + } + ret->tab = tab; copyin(t, elem, &ret->data); } @@ -209,7 +215,7 @@ runtime·convT2E(Type *t, ...) elem = (byte*)(&t+1); wid = t->size; - ret = (Eface*)(elem + runtime·rnd(wid, Structrnd)); + ret = (Eface*)(elem + ROUND(wid, Structrnd)); ret->type = t; copyin(t, elem, &ret->data); } @@ -272,6 +278,13 @@ runtime·assertI2T2(Type *t, Iface i, ...) copyout(t, &i.data, ret); } +void +runtime·assertI2TOK(Type *t, Iface i, bool ok) +{ + ok = i.tab!=nil && i.tab->type==t; + FLUSH(&ok); +} + static void assertE2Tret(Type *t, Eface e, byte *ret); // func ifaceE2T(typ *byte, iface any) (ret any) @@ -328,6 +341,13 @@ runtime·assertE2T2(Type *t, Eface e, ...) copyout(t, &e.data, ret); } +void +runtime·assertE2TOK(Type *t, Eface e, bool ok) +{ + ok = t==e.type; + FLUSH(&ok); +} + // func convI2E(elem any) (ret any) void runtime·convI2E(Iface i, Eface ret) @@ -387,7 +407,7 @@ void runtime·convI2I(InterfaceType* inter, Iface i, Iface ret) { Itab *tab; - + ret.data = i.data; if((tab = i.tab) == nil) ret.tab = nil; @@ -526,10 +546,10 @@ runtime·assertE2E2(InterfaceType* inter, Eface e, Eface ret, bool ok) } static uintptr -ifacehash1(void *data, Type *t) +ifacehash1(void *data, Type *t, uintptr h) { Alg *alg; - uintptr size, h; + uintptr size; Eface err; if(t == nil) @@ -543,7 +563,6 @@ ifacehash1(void *data, Type *t) runtime·newErrorString(runtime·catstring(runtime·gostringnocopy((byte*)"hash of unhashable type "), *t->string), &err); runtime·panic(err); } - h = 0; if(size <= sizeof(data)) alg->hash(&h, size, &data); else @@ -552,17 +571,17 @@ ifacehash1(void *data, Type *t) } uintptr -runtime·ifacehash(Iface a) +runtime·ifacehash(Iface a, uintptr h) { if(a.tab == nil) - return 0; - return ifacehash1(a.data, a.tab->type); + return h; + return ifacehash1(a.data, a.tab->type, h); } uintptr -runtime·efacehash(Eface a) +runtime·efacehash(Eface a, uintptr h) { - return ifacehash1(a.data, a.type); + return ifacehash1(a.data, a.type, h); } static bool @@ -666,39 +685,54 @@ reflect·unsafe_Typeof(Eface e, Eface ret) } void -reflect·unsafe_New(Eface typ, void *ret) +reflect·unsafe_New(Type *t, void *ret) { - Type *t; + uint32 flag; - // Reflect library has reinterpreted typ - // as its own kind of type structure. - // We know that the pointer to the original - // type structure sits before the data pointer. - t = (Type*)((Eface*)typ.data-1); + flag = t->kind&KindNoPointers ? FlagNoPointers : 0; + ret = runtime·mallocgc(t->size, flag, 1, 1); + + if(UseSpanType && !flag) { + if(false) { + runtime·printf("unsafe_New %S: %p\n", *t->string, ret); + } + runtime·settype(ret, (uintptr)t | TypeInfo_SingleObject); + } - if(t->kind&KindNoPointers) - ret = runtime·mallocgc(t->size, FlagNoPointers, 1, 1); - else - ret = runtime·mal(t->size); FLUSH(&ret); } void -reflect·unsafe_NewArray(Eface typ, uint32 n, void *ret) +reflect·unsafe_NewArray(Type *t, intgo n, void *ret) { uint64 size; - Type *t; - // Reflect library has reinterpreted typ - // as its own kind of type structure. - // We know that the pointer to the original - // type structure sits before the data pointer. - t = (Type*)((Eface*)typ.data-1); - size = n*t->size; - if(t->kind&KindNoPointers) + if(size == 0) + ret = (byte*)&runtime·zerobase; + else if(t->kind&KindNoPointers) ret = runtime·mallocgc(size, FlagNoPointers, 1, 1); - else - ret = runtime·mal(size); + else { + ret = runtime·mallocgc(size, 0, 1, 1); + + if(UseSpanType) { + if(false) { + runtime·printf("unsafe_NewArray [%D]%S: %p\n", (int64)n, *t->string, ret); + } + runtime·settype(ret, (uintptr)t | TypeInfo_Array); + } + } + + FLUSH(&ret); +} + +void +reflect·typelinks(Slice ret) +{ + extern Type *typelink[], *etypelink[]; + static int32 first = 1; + ret.array = (byte*)typelink; + ret.len = etypelink - typelink; + ret.cap = ret.len; FLUSH(&ret); } diff --git a/src/pkg/runtime/iface_test.go b/src/pkg/runtime/iface_test.go new file mode 100644 index 000000000..bca0ea0ee --- /dev/null +++ b/src/pkg/runtime/iface_test.go @@ -0,0 +1,138 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime_test + +import ( + "testing" +) + +type I1 interface { + Method1() +} + +type I2 interface { + Method1() + Method2() +} + +type TS uint16 +type TM uintptr +type TL [2]uintptr + +func (TS) Method1() {} +func (TS) Method2() {} +func (TM) Method1() {} +func (TM) Method2() {} +func (TL) Method1() {} +func (TL) Method2() {} + +var ( + e interface{} + e_ interface{} + i1 I1 + i2 I2 + ts TS + tm TM + tl TL +) + +func BenchmarkConvT2ESmall(b *testing.B) { + for i := 0; i < b.N; i++ { + e = ts + } +} + +func BenchmarkConvT2EUintptr(b *testing.B) { + for i := 0; i < b.N; i++ { + e = tm + } +} + +func BenchmarkConvT2ELarge(b *testing.B) { + for i := 0; i < b.N; i++ { + e = tl + } +} + +func BenchmarkConvT2ISmall(b *testing.B) { + for i := 0; i < b.N; i++ { + i1 = ts + } +} + +func BenchmarkConvT2IUintptr(b *testing.B) { + for i := 0; i < b.N; i++ { + i1 = tm + } +} + +func BenchmarkConvT2ILarge(b *testing.B) { + for i := 0; i < b.N; i++ { + i1 = tl + } +} + +func BenchmarkConvI2E(b *testing.B) { + i2 = tm + for i := 0; i < b.N; i++ { + e = i2 + } +} + +func BenchmarkConvI2I(b *testing.B) { + i2 = tm + for i := 0; i < b.N; i++ { + i1 = i2 + } +} + +func BenchmarkAssertE2T(b *testing.B) { + e = tm + for i := 0; i < b.N; i++ { + tm = e.(TM) + } +} + +func BenchmarkAssertE2TLarge(b *testing.B) { + e = tl + for i := 0; i < b.N; i++ { + tl = e.(TL) + } +} + +func BenchmarkAssertE2I(b *testing.B) { + e = tm + for i := 0; i < b.N; i++ { + i1 = e.(I1) + } +} + +func BenchmarkAssertI2T(b *testing.B) { + i1 = tm + for i := 0; i < b.N; i++ { + tm = i1.(TM) + } +} + +func BenchmarkAssertI2I(b *testing.B) { + i1 = tm + for i := 0; i < b.N; i++ { + i2 = i1.(I2) + } +} + +func BenchmarkAssertI2E(b *testing.B) { + i1 = tm + for i := 0; i < b.N; i++ { + e = i1.(interface{}) + } +} + +func BenchmarkAssertE2E(b *testing.B) { + e = tm + for i := 0; i < b.N; i++ { + e_ = e + } +} diff --git a/src/pkg/runtime/lfstack.c b/src/pkg/runtime/lfstack.c new file mode 100644 index 000000000..1d48491aa --- /dev/null +++ b/src/pkg/runtime/lfstack.c @@ -0,0 +1,65 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Lock-free stack. + +#include "runtime.h" +#include "arch_GOARCH.h" + +#ifdef _64BIT +// Amd64 uses 48-bit virtual addresses, 47-th bit is used as kernel/user flag. +// So we use 17msb of pointers as ABA counter. +# define PTR_BITS 47 +#else +# define PTR_BITS 32 +#endif +#define PTR_MASK ((1ull<pushcnt++; + new = (uint64)(uintptr)node|(((uint64)node->pushcnt&CNT_MASK)<next = (LFNode*)(uintptr)(old&PTR_MASK); + if(runtime·cas64(head, &old, new)) + break; + } +} + +LFNode* +runtime·lfstackpop(uint64 *head) +{ + LFNode *node, *node2; + uint64 old, new; + + old = runtime·atomicload64(head); + for(;;) { + if(old == 0) + return nil; + node = (LFNode*)(uintptr)(old&PTR_MASK); + node2 = runtime·atomicloadp(&node->next); + new = 0; + if(node2 != nil) + new = (uint64)(uintptr)node2|(((uint64)node2->pushcnt&CNT_MASK)<key, 1); + if(runtime·xchg(&n->key, 1)) + runtime·throw("notewakeup - double wakeup"); runtime·futexwakeup(&n->key, 1); } diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc index 9ae3a9d61..ac131b3af 100644 --- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -9,17 +9,18 @@ package runtime #include "runtime.h" #include "arch_GOARCH.h" -#include "stack.h" #include "malloc.h" -#include "defs_GOOS_GOARCH.h" #include "type.h" +#include "typekind.h" +#include "race.h" -#pragma dataflag 16 /* mark mheap as 'no pointers', hiding from garbage collector */ -MHeap runtime·mheap; +MHeap *runtime·mheap; -extern MStats mstats; // defined in extern.go +int32 runtime·checking; -extern volatile int32 runtime·MemProfileRate; +extern MStats mstats; // defined in zruntime_def_$GOOS_$GOARCH.go + +extern volatile intgo runtime·MemProfileRate; // Allocate an object of at least size bytes. // Small objects are allocated from the per-thread cache's free lists. @@ -27,7 +28,8 @@ extern volatile int32 runtime·MemProfileRate; void* runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) { - int32 sizeclass, rate; + int32 sizeclass; + intgo rate; MCache *c; uintptr npages; MSpan *s; @@ -41,6 +43,9 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) if(size == 0) size = 1; + if(DebugTypeAtBlockEnd) + size += sizeof(uintptr); + c = m->mcache; c->local_nmalloc++; if(size <= MaxSmallSize) { @@ -60,7 +65,7 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) npages = size >> PageShift; if((size & PageMask) != 0) npages++; - s = runtime·MHeap_Alloc(&runtime·mheap, npages, 0, 1); + s = runtime·MHeap_Alloc(runtime·mheap, npages, 0, 1, zeroed); if(s == nil) runtime·throw("out of memory"); size = npages<local_total_alloc >= (1<<30)) { + // purge cache stats to prevent overflow + runtime·lock(runtime·mheap); + runtime·purgecachedstats(c); + runtime·unlock(runtime·mheap); + } + if(!(flag & FlagNoGC)) runtime·markallocated(v, size, (flag&FlagNoPointers) != 0); + if(DebugTypeAtBlockEnd) + *(uintptr*)((uintptr)v+size-sizeof(uintptr)) = 0; + m->mallocing = 0; if(!(flag & FlagNoProfiling) && (rate = runtime·MemProfileRate) > 0) { @@ -95,6 +111,11 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) if(dogc && mstats.heap_alloc >= mstats.next_gc) runtime·gc(0); + + if(raceenabled) { + runtime·racemalloc(v, size, m->racepc); + m->racepc = nil; + } return v; } @@ -130,6 +151,9 @@ runtime·free(void *v) } prof = runtime·blockspecial(v); + if(raceenabled) + runtime·racefree(v); + // Find size class for v. sizeclass = s->sizeclass; c = m->mcache; @@ -141,7 +165,7 @@ runtime·free(void *v) // they might coalesce v into other spans and change the bitmap further. runtime·markfreed(v, size); runtime·unmarkspan(v, 1<mcache->local_nlookup++; - s = runtime·MHeap_LookupMaybe(&runtime·mheap, v); + if (sizeof(void*) == 4 && m->mcache->local_nlookup >= (1<<30)) { + // purge cache stats to prevent overflow + runtime·lock(runtime·mheap); + runtime·purgecachedstats(m->mcache); + runtime·unlock(runtime·mheap); + } + + s = runtime·MHeap_LookupMaybe(runtime·mheap, v); if(sp) *sp = s; if(s == nil) { @@ -196,7 +227,7 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp) return 0; } - n = runtime·class_to_size[s->sizeclass]; + n = s->elemsize; if(base) { i = ((byte*)v - p)/n; *base = p + i*n; @@ -210,14 +241,15 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp) MCache* runtime·allocmcache(void) { - int32 rate; + intgo rate; MCache *c; - runtime·lock(&runtime·mheap); - c = runtime·FixAlloc_Alloc(&runtime·mheap.cachealloc); - mstats.mcache_inuse = runtime·mheap.cachealloc.inuse; - mstats.mcache_sys = runtime·mheap.cachealloc.sys; - runtime·unlock(&runtime·mheap); + runtime·lock(runtime·mheap); + c = runtime·FixAlloc_Alloc(&runtime·mheap->cachealloc); + mstats.mcache_inuse = runtime·mheap->cachealloc.inuse; + mstats.mcache_sys = runtime·mheap->cachealloc.sys; + runtime·unlock(runtime·mheap); + runtime·memclr((byte*)c, sizeof(*c)); // Set first allocation sample size. rate = runtime·MemProfileRate; @@ -230,12 +262,19 @@ runtime·allocmcache(void) } void -runtime·purgecachedstats(M* m) +runtime·freemcache(MCache *c) { - MCache *c; + runtime·MCache_ReleaseAll(c); + runtime·lock(runtime·mheap); + runtime·purgecachedstats(c); + runtime·FixAlloc_Free(&runtime·mheap->cachealloc, c); + runtime·unlock(runtime·mheap); +} +void +runtime·purgecachedstats(MCache *c) +{ // Protected by either heap or GC lock. - c = m->mcache; mstats.heap_alloc += c->local_cachealloc; c->local_cachealloc = 0; mstats.heap_objects += c->local_objects; @@ -274,6 +313,9 @@ runtime·mallocinit(void) USED(arena_size); USED(bitmap_size); + if((runtime·mheap = runtime·SysAlloc(sizeof(*runtime·mheap))) == nil) + runtime·throw("runtime: cannot allocate heap metadata"); + runtime·InitSizes(); limit = runtime·memlimit(); @@ -283,32 +325,30 @@ runtime·mallocinit(void) // enough to hold 4 bits per allocated word. if(sizeof(void*) == 8 && (limit == 0 || limit > (1<<30))) { // On a 64-bit machine, allocate from a single contiguous reservation. - // 16 GB should be big enough for now. + // 128 GB (MaxMem) should be big enough for now. // // The code will work with the reservation at any address, but ask - // SysReserve to use 0x000000f800000000 if possible. - // Allocating a 16 GB region takes away 36 bits, and the amd64 + // SysReserve to use 0x000000c000000000 if possible. + // Allocating a 128 GB region takes away 37 bits, and the amd64 // doesn't let us choose the top 17 bits, so that leaves the 11 bits - // in the middle of 0x00f8 for us to choose. Choosing 0x00f8 means - // that the valid memory addresses will begin 0x00f8, 0x00f9, 0x00fa, 0x00fb. - // None of the bytes f8 f9 fa fb can appear in valid UTF-8, and - // they are otherwise as far from ff (likely a common byte) as possible. - // Choosing 0x00 for the leading 6 bits was more arbitrary, but it - // is not a common ASCII code point either. Using 0x11f8 instead + // in the middle of 0x00c0 for us to choose. Choosing 0x00c0 means + // that the valid memory addresses will begin 0x00c0, 0x00c1, ..., 0x0x00df. + // In little-endian, that's c0 00, c1 00, ..., df 00. None of those are valid + // UTF-8 sequences, and they are otherwise as far away from + // ff (likely a common byte) as possible. An earlier attempt to use 0x11f8 // caused out of memory errors on OS X during thread allocations. // These choices are both for debuggability and to reduce the // odds of the conservative garbage collector not collecting memory // because some non-pointer block of memory had a bit pattern // that matched a memory address. // - // Actually we reserve 17 GB (because the bitmap ends up being 1 GB) - // but it hardly matters: fc is not valid UTF-8 either, and we have to - // allocate 15 GB before we get that far. + // Actually we reserve 136 GB (because the bitmap ends up being 8 GB) + // but it hardly matters: e0 00 is not valid UTF-8 either. // // If this fails we fall back to the 32 bit memory mechanism - arena_size = 16LL<<30; + arena_size = MaxMem; bitmap_size = arena_size / (sizeof(void*)*8/4); - p = runtime·SysReserve((void*)(0x00f8ULL<<32), bitmap_size + arena_size); + p = runtime·SysReserve((void*)(0x00c0ULL<<32), bitmap_size + arena_size); } if (p == nil) { // On a 32-bit machine, we can't typically get away @@ -354,13 +394,13 @@ runtime·mallocinit(void) if((uintptr)p & (((uintptr)1<bitmap = p; + runtime·mheap->arena_start = p + bitmap_size; + runtime·mheap->arena_used = runtime·mheap->arena_start; + runtime·mheap->arena_end = runtime·mheap->arena_start + arena_size; // Initialize the rest of the allocator. - runtime·MHeap_Init(&runtime·mheap, runtime·SysAlloc); + runtime·MHeap_Init(runtime·mheap, runtime·SysAlloc); m->mcache = runtime·allocmcache(); // See if it works. @@ -394,6 +434,8 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n) runtime·SysMap(p, n); h->arena_used += n; runtime·MHeap_MapBits(h); + if(raceenabled) + runtime·racemapshadow(p, n); return p; } @@ -420,11 +462,231 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n) if(h->arena_used > h->arena_end) h->arena_end = h->arena_used; runtime·MHeap_MapBits(h); + if(raceenabled) + runtime·racemapshadow(p, n); } return p; } +static Lock settype_lock; + +void +runtime·settype_flush(M *mp, bool sysalloc) +{ + uintptr *buf, *endbuf; + uintptr size, ofs, j, t; + uintptr ntypes, nbytes2, nbytes3; + uintptr *data2; + byte *data3; + bool sysalloc3; + void *v; + uintptr typ, p; + MSpan *s; + + buf = mp->settype_buf; + endbuf = buf + mp->settype_bufsize; + + runtime·lock(&settype_lock); + while(buf < endbuf) { + v = (void*)*buf; + *buf = 0; + buf++; + typ = *buf; + buf++; + + // (Manually inlined copy of runtime·MHeap_Lookup) + p = (uintptr)v>>PageShift; + if(sizeof(void*) == 8) + p -= (uintptr)runtime·mheap->arena_start >> PageShift; + s = runtime·mheap->map[p]; + + if(s->sizeclass == 0) { + s->types.compression = MTypes_Single; + s->types.data = typ; + continue; + } + + size = s->elemsize; + ofs = ((uintptr)v - (s->start<types.compression) { + case MTypes_Empty: + ntypes = (s->npages << PageShift) / size; + nbytes3 = 8*sizeof(uintptr) + 1*ntypes; + + if(!sysalloc) { + data3 = runtime·mallocgc(nbytes3, FlagNoPointers, 0, 1); + } else { + data3 = runtime·SysAlloc(nbytes3); + if(data3 == nil) + runtime·throw("runtime: cannot allocate memory"); + if(0) runtime·printf("settype(0->3): SysAlloc(%x) --> %p\n", (uint32)nbytes3, data3); + } + + s->types.compression = MTypes_Bytes; + s->types.sysalloc = sysalloc; + s->types.data = (uintptr)data3; + + ((uintptr*)data3)[1] = typ; + data3[8*sizeof(uintptr) + ofs] = 1; + break; + + case MTypes_Words: + ((uintptr*)s->types.data)[ofs] = typ; + break; + + case MTypes_Bytes: + data3 = (byte*)s->types.data; + for(j=1; j<8; j++) { + if(((uintptr*)data3)[j] == typ) { + break; + } + if(((uintptr*)data3)[j] == 0) { + ((uintptr*)data3)[j] = typ; + break; + } + } + if(j < 8) { + data3[8*sizeof(uintptr) + ofs] = j; + } else { + ntypes = (s->npages << PageShift) / size; + nbytes2 = ntypes * sizeof(uintptr); + + if(!sysalloc) { + data2 = runtime·mallocgc(nbytes2, FlagNoPointers, 0, 1); + } else { + data2 = runtime·SysAlloc(nbytes2); + if(data2 == nil) + runtime·throw("runtime: cannot allocate memory"); + if(0) runtime·printf("settype.(3->2): SysAlloc(%x) --> %p\n", (uint32)nbytes2, data2); + } + + sysalloc3 = s->types.sysalloc; + + s->types.compression = MTypes_Words; + s->types.sysalloc = sysalloc; + s->types.data = (uintptr)data2; + + // Move the contents of data3 to data2. Then deallocate data3. + for(j=0; j2): SysFree(%p,%x)\n", data3, (uint32)nbytes3); + runtime·SysFree(data3, nbytes3); + } + + data2[ofs] = typ; + } + break; + } + } + runtime·unlock(&settype_lock); + + mp->settype_bufsize = 0; +} + +// It is forbidden to use this function if it is possible that +// explicit deallocation via calling runtime·free(v) may happen. +void +runtime·settype(void *v, uintptr t) +{ + M *mp; + uintptr *buf; + uintptr i; + MSpan *s; + + if(t == 0) + runtime·throw("settype: zero type"); + + mp = m; + buf = mp->settype_buf; + i = mp->settype_bufsize; + buf[i+0] = (uintptr)v; + buf[i+1] = t; + i += 2; + mp->settype_bufsize = i; + + if(i == nelem(mp->settype_buf)) { + runtime·settype_flush(mp, false); + } + + if(DebugTypeAtBlockEnd) { + s = runtime·MHeap_Lookup(runtime·mheap, v); + *(uintptr*)((uintptr)v+s->elemsize-sizeof(uintptr)) = t; + } +} + +void +runtime·settype_sysfree(MSpan *s) +{ + uintptr ntypes, nbytes; + + if(!s->types.sysalloc) + return; + + nbytes = (uintptr)-1; + + switch (s->types.compression) { + case MTypes_Words: + ntypes = (s->npages << PageShift) / s->elemsize; + nbytes = ntypes * sizeof(uintptr); + break; + case MTypes_Bytes: + ntypes = (s->npages << PageShift) / s->elemsize; + nbytes = 8*sizeof(uintptr) + 1*ntypes; + break; + } + + if(nbytes != (uintptr)-1) { + if(0) runtime·printf("settype: SysFree(%p,%x)\n", (void*)s->types.data, (uint32)nbytes); + runtime·SysFree((void*)s->types.data, nbytes); + } +} + +uintptr +runtime·gettype(void *v) +{ + MSpan *s; + uintptr t, ofs; + byte *data; + + s = runtime·MHeap_LookupMaybe(runtime·mheap, v); + if(s != nil) { + t = 0; + switch(s->types.compression) { + case MTypes_Empty: + break; + case MTypes_Single: + t = s->types.data; + break; + case MTypes_Words: + ofs = (uintptr)v - (s->start<types.data)[ofs/s->elemsize]; + break; + case MTypes_Bytes: + ofs = (uintptr)v - (s->start<types.data; + t = data[8*sizeof(uintptr) + ofs/s->elemsize]; + t = ((uintptr*)data)[t]; + break; + default: + runtime·throw("runtime·gettype: invalid compression kind"); + } + if(0) { + runtime·lock(&settype_lock); + runtime·printf("%p -> %d,%X\n", v, (int32)s->types.compression, (int64)t); + runtime·unlock(&settype_lock); + } + return t; + } + return 0; +} + // Runtime stubs. void* @@ -433,46 +695,63 @@ runtime·mal(uintptr n) return runtime·mallocgc(n, 0, 1, 1); } -func new(typ *Type) (ret *uint8) { - uint32 flag = typ->kind&KindNoPointers ? FlagNoPointers : 0; - ret = runtime·mallocgc(typ->size, flag, 1, 1); +#pragma textflag 7 +void +runtime·new(Type *typ, uint8 *ret) +{ + uint32 flag; + + if(raceenabled) + m->racepc = runtime·getcallerpc(&typ); + + if(typ->size == 0) { + // All 0-length allocations use this pointer. + // The language does not require the allocations to + // have distinct values. + ret = (uint8*)&runtime·zerobase; + } else { + flag = typ->kind&KindNoPointers ? FlagNoPointers : 0; + ret = runtime·mallocgc(typ->size, flag, 1, 1); + + if(UseSpanType && !flag) { + if(false) { + runtime·printf("new %S: %p\n", *typ->string, ret); + } + runtime·settype(ret, (uintptr)typ | TypeInfo_SingleObject); + } + } + FLUSH(&ret); } +// same as runtime·new, but callable from C void* -runtime·stackalloc(uint32 n) +runtime·cnew(Type *typ) { - // Stackalloc must be called on scheduler stack, so that we - // never try to grow the stack during the code that stackalloc runs. - // Doing so would cause a deadlock (issue 1547). - if(g != m->g0) - runtime·throw("stackalloc not on scheduler stack"); - - // Stack allocator uses malloc/free most of the time, - // but if we're in the middle of malloc and need stack, - // we have to do something else to avoid deadlock. - // In that case, we fall back on a fixed-size free-list - // allocator, assuming that inside malloc all the stack - // frames are small, so that all the stack allocations - // will be a single size, the minimum (right now, 5k). - if(m->mallocing || m->gcing || n == FixedStack) { - if(n != FixedStack) { - runtime·printf("stackalloc: in malloc, size=%d want %d", FixedStack, n); - runtime·throw("stackalloc"); + uint32 flag; + void *ret; + + if(raceenabled) + m->racepc = runtime·getcallerpc(&typ); + + if(typ->size == 0) { + // All 0-length allocations use this pointer. + // The language does not require the allocations to + // have distinct values. + ret = (uint8*)&runtime·zerobase; + } else { + flag = typ->kind&KindNoPointers ? FlagNoPointers : 0; + ret = runtime·mallocgc(typ->size, flag, 1, 1); + + if(UseSpanType && !flag) { + if(false) { + runtime·printf("new %S: %p\n", *typ->string, ret); + } + runtime·settype(ret, (uintptr)typ | TypeInfo_SingleObject); } - return runtime·FixAlloc_Alloc(m->stackalloc); } - return runtime·mallocgc(n, FlagNoProfiling|FlagNoGC, 0, 0); -} -void -runtime·stackfree(void *v, uintptr n) -{ - if(m->mallocing || m->gcing || n == FixedStack) { - runtime·FixAlloc_Free(m->stackalloc, v); - return; - } - runtime·free(v); + return ret; } func GC() { @@ -483,7 +762,8 @@ func SetFinalizer(obj Eface, finalizer Eface) { byte *base; uintptr size; FuncType *ft; - int32 i, nret; + int32 i; + uintptr nret; Type *t; if(obj.type == nil) { diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h index d846f6810..38122bf8a 100644 --- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -85,6 +85,8 @@ typedef struct MHeap MHeap; typedef struct MSpan MSpan; typedef struct MStats MStats; typedef struct MLink MLink; +typedef struct MTypes MTypes; +typedef struct GCStats GCStats; enum { @@ -113,21 +115,30 @@ enum HeapAllocChunk = 1<<20, // Chunk size for heap growth // Number of bits in page to span calculations (4k pages). - // On 64-bit, we limit the arena to 16G, so 22 bits suffices. - // On 32-bit, we don't bother limiting anything: 20 bits for 4G. + // On 64-bit, we limit the arena to 128GB, or 37 bits. + // On 32-bit, we don't bother limiting anything, so we use the full 32-bit address. #ifdef _64BIT - MHeapMap_Bits = 22, + MHeapMap_Bits = 37 - PageShift, #else - MHeapMap_Bits = 20, + MHeapMap_Bits = 32 - PageShift, #endif // Max number of threads to run garbage collection. // 2, 3, and 4 are all plausible maximums depending // on the hardware details of the machine. The garbage - // collector scales well to 4 cpus. - MaxGcproc = 4, + // collector scales well to 8 cpus. + MaxGcproc = 8, }; +// Maximum memory allocation size, a hint for callers. +// This must be a #define instead of an enum because it +// is so large. +#ifdef _64BIT +#define MaxMem (1ULL<<(MHeapMap_Bits+PageShift)) /* 128 GB */ +#else +#define MaxMem ((uintptr)-1) +#endif + // A generic linked list of blocks. (Typically the block is bigger than sizeof(MLink).) struct MLink { @@ -188,7 +199,7 @@ void runtime·FixAlloc_Free(FixAlloc *f, void *p); // Statistics. -// Shared with Go: if you edit this structure, also edit extern.go. +// Shared with Go: if you edit this structure, also edit type MemStats in mem.go. struct MStats { // General statistics. @@ -219,7 +230,7 @@ struct MStats uint64 buckhash_sys; // profiling bucket hash table // Statistics about garbage collector. - // Protected by stopping the world during GC. + // Protected by mheap or stopping the world during GC. uint64 next_gc; // next GC (in heap_alloc time) uint64 last_gc; // last GC (in absolute time) uint64 pause_total_ns; @@ -239,7 +250,6 @@ struct MStats #define mstats runtime·memStats /* name shared with Go */ extern MStats mstats; - // Size classes. Computed and initialized by InitSizes. // // SizeToClass(0 <= n <= MaxSmallSize) returns the size class, @@ -273,19 +283,19 @@ struct MCacheList struct MCache { MCacheList list[NumSizeClasses]; - uint64 size; - int64 local_cachealloc; // bytes allocated (or freed) from cache since last lock of heap - int64 local_objects; // objects allocated (or freed) from cache since last lock of heap - int64 local_alloc; // bytes allocated (or freed) since last lock of heap - int64 local_total_alloc; // bytes allocated (even if freed) since last lock of heap - int64 local_nmalloc; // number of mallocs since last lock of heap - int64 local_nfree; // number of frees since last lock of heap - int64 local_nlookup; // number of pointer lookups since last lock of heap + uintptr size; + intptr local_cachealloc; // bytes allocated (or freed) from cache since last lock of heap + intptr local_objects; // objects allocated (or freed) from cache since last lock of heap + intptr local_alloc; // bytes allocated (or freed) since last lock of heap + uintptr local_total_alloc; // bytes allocated (even if freed) since last lock of heap + uintptr local_nmalloc; // number of mallocs since last lock of heap + uintptr local_nfree; // number of frees since last lock of heap + uintptr local_nlookup; // number of pointer lookups since last lock of heap int32 next_sample; // trigger heap sample after allocating this many bytes // Statistics about allocation size classes since last lock of heap struct { - int64 nmalloc; - int64 nfree; + uintptr nmalloc; + uintptr nfree; } local_by_size[NumSizeClasses]; }; @@ -294,6 +304,44 @@ void* runtime·MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zero void runtime·MCache_Free(MCache *c, void *p, int32 sizeclass, uintptr size); void runtime·MCache_ReleaseAll(MCache *c); +// MTypes describes the types of blocks allocated within a span. +// The compression field describes the layout of the data. +// +// MTypes_Empty: +// All blocks are free, or no type information is available for +// allocated blocks. +// The data field has no meaning. +// MTypes_Single: +// The span contains just one block. +// The data field holds the type information. +// The sysalloc field has no meaning. +// MTypes_Words: +// The span contains multiple blocks. +// The data field points to an array of type [NumBlocks]uintptr, +// and each element of the array holds the type of the corresponding +// block. +// MTypes_Bytes: +// The span contains at most seven different types of blocks. +// The data field points to the following structure: +// struct { +// type [8]uintptr // type[0] is always 0 +// index [NumBlocks]byte +// } +// The type of the i-th block is: data.type[data.index[i]] +enum +{ + MTypes_Empty = 0, + MTypes_Single = 1, + MTypes_Words = 2, + MTypes_Bytes = 3, +}; +struct MTypes +{ + byte compression; // one of MTypes_* + bool sysalloc; // whether (void*)data is from runtime·SysAlloc + uintptr data; +}; + // An MSpan is a run of pages. enum { @@ -306,16 +354,17 @@ struct MSpan { MSpan *next; // in a span linked list MSpan *prev; // in a span linked list - MSpan *allnext; // in the list of all spans PageID start; // starting page number uintptr npages; // number of pages in span MLink *freelist; // list of free objects uint32 ref; // number of allocated objects in this span - uint32 sizeclass; // size class + int32 sizeclass; // size class + uintptr elemsize; // computed from sizeclass or from npages uint32 state; // MSpanInUse etc int64 unusedsince; // First time spotted by GC in MSpanFree state uintptr npreleased; // number of pages released to the OS byte *limit; // end of data in span + MTypes types; // types of allocated objects in this span }; void runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages); @@ -342,6 +391,7 @@ struct MCentral void runtime·MCentral_Init(MCentral *c, int32 sizeclass); int32 runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **first); void runtime·MCentral_FreeList(MCentral *c, int32 n, MLink *first); +void runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end); // Main malloc heap. // The heap itself is the "free[]" and "large" arrays, @@ -351,7 +401,9 @@ struct MHeap Lock; MSpan free[MaxMHeapList]; // free lists of given length MSpan large; // free lists length >= MaxMHeapList - MSpan *allspans; + MSpan **allspans; + uint32 nspan; + uint32 nspancap; // span lookup MSpan *map[1<list[sizeclass]; if(l->list == nil) { // Replenish using central lists. - n = runtime·MCentral_AllocList(&runtime·mheap.central[sizeclass], + n = runtime·MCentral_AllocList(&runtime·mheap->central[sizeclass], runtime·class_to_transfercount[sizeclass], &first); if(n == 0) runtime·throw("out of memory"); @@ -43,11 +43,6 @@ runtime·MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zeroed) // block is zeroed iff second word is zero ... if(size > sizeof(uintptr) && ((uintptr*)v)[1] != 0) runtime·memclr((byte*)v, size); - else { - // ... except for the link pointer - // that we used above; zero that. - v->next = nil; - } } c->local_cachealloc += size; c->local_objects++; @@ -74,7 +69,7 @@ ReleaseN(MCache *c, MCacheList *l, int32 n, int32 sizeclass) c->size -= n*runtime·class_to_size[sizeclass]; // Return them to central free list. - runtime·MCentral_FreeList(&runtime·mheap.central[sizeclass], n, first); + runtime·MCentral_FreeList(&runtime·mheap->central[sizeclass], n, first); } void diff --git a/src/pkg/runtime/mcentral.c b/src/pkg/runtime/mcentral.c index ff0c2d11a..ac8b5aa0d 100644 --- a/src/pkg/runtime/mcentral.c +++ b/src/pkg/runtime/mcentral.c @@ -34,12 +34,13 @@ runtime·MCentral_Init(MCentral *c, int32 sizeclass) // Allocate up to n objects from the central free list. // Return the number of objects allocated. // The objects are linked together by their first words. -// On return, *pstart points at the first object and *pend at the last. +// On return, *pstart points at the first object. int32 runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst) { - MLink *first, *last, *v; - int32 i; + MSpan *s; + MLink *first, *last; + int32 cap, avail, i; runtime·lock(c); // Replenish central list if empty. @@ -50,47 +51,37 @@ runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst) return 0; } } + s = c->nonempty.next; + cap = (s->npages << PageShift) / s->elemsize; + avail = cap - s->ref; + if(avail < n) + n = avail; - // Copy from list, up to n. // First one is guaranteed to work, because we just grew the list. - first = MCentral_Alloc(c); + first = s->freelist; last = first; - for(i=1; inext = v; - last = v; + for(i=1; inext; } + s->freelist = last->next; last->next = nil; - c->nfree -= i; - - runtime·unlock(c); - *pfirst = first; - return i; -} + s->ref += n; + c->nfree -= n; -// Helper: allocate one object from the central free list. -static void* -MCentral_Alloc(MCentral *c) -{ - MSpan *s; - MLink *v; - - if(runtime·MSpanList_IsEmpty(&c->nonempty)) - return nil; - s = c->nonempty.next; - s->ref++; - v = s->freelist; - s->freelist = v->next; - if(s->freelist == nil) { + if(n == avail) { + if(s->freelist != nil || s->ref != cap) { + runtime·throw("invalid freelist"); + } runtime·MSpanList_Remove(s); runtime·MSpanList_Insert(&c->empty, s); } - return v; + + runtime·unlock(c); + *pfirst = first; + return n; } // Free n objects back into the central free list. -// Return the number of objects allocated. -// The objects are linked together by their first words. -// On return, *pstart points at the first object and *pend at the last. void runtime·MCentral_FreeList(MCentral *c, int32 n, MLink *start) { @@ -118,7 +109,7 @@ MCentral_Free(MCentral *c, void *v) int32 size; // Find span for v. - s = runtime·MHeap_Lookup(&runtime·mheap, v); + s = runtime·MHeap_Lookup(runtime·mheap, v); if(s == nil || s->ref == 0) runtime·throw("invalid free"); @@ -143,11 +134,47 @@ MCentral_Free(MCentral *c, void *v) s->freelist = nil; c->nfree -= (s->npages << PageShift) / size; runtime·unlock(c); - runtime·MHeap_Free(&runtime·mheap, s, 0); + runtime·MHeap_Free(runtime·mheap, s, 0); runtime·lock(c); } } +// Free n objects from a span s back into the central free list c. +// Called from GC. +void +runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end) +{ + int32 size; + + runtime·lock(c); + + // Move to nonempty if necessary. + if(s->freelist == nil) { + runtime·MSpanList_Remove(s); + runtime·MSpanList_Insert(&c->nonempty, s); + } + + // Add the objects back to s's free list. + end->next = s->freelist; + s->freelist = start; + s->ref -= n; + c->nfree += n; + + // If s is completely freed, return it to the heap. + if(s->ref == 0) { + size = runtime·class_to_size[c->sizeclass]; + runtime·MSpanList_Remove(s); + *(uintptr*)(s->start<freelist = nil; + c->nfree -= (s->npages << PageShift) / size; + runtime·unlock(c); + runtime·unmarkspan((byte*)(s->start<npages<sizeclass, &size, &npages, &n); - s = runtime·MHeap_Alloc(&runtime·mheap, npages, c->sizeclass, 0); + s = runtime·MHeap_Alloc(runtime·mheap, npages, c->sizeclass, 0, 1); if(s == nil) { // TODO(rsc): Log out of memory runtime·lock(c); diff --git a/src/pkg/runtime/mem.go b/src/pkg/runtime/mem.go index 76680086c..79edc5a60 100644 --- a/src/pkg/runtime/mem.go +++ b/src/pkg/runtime/mem.go @@ -6,6 +6,9 @@ package runtime import "unsafe" +// Note: the MemStats struct should be kept in sync with +// struct MStats in malloc.h + // A MemStats records statistics about the memory allocator. type MemStats struct { // General statistics. @@ -39,7 +42,7 @@ type MemStats struct { NextGC uint64 // next run in HeapAlloc time (bytes) LastGC uint64 // last run in absolute time (ns) PauseTotalNs uint64 - PauseNs [256]uint64 // most recent GC pause times + PauseNs [256]uint64 // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] NumGC uint32 EnableGC bool DebugGC bool diff --git a/src/pkg/runtime/mem_darwin.c b/src/pkg/runtime/mem_darwin.c index cde5601cf..04e719394 100644 --- a/src/pkg/runtime/mem_darwin.c +++ b/src/pkg/runtime/mem_darwin.c @@ -14,7 +14,7 @@ runtime·SysAlloc(uintptr n) void *v; mstats.sys += n; - v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(v < (void*)4096) return nil; return v; @@ -51,7 +51,7 @@ runtime·SysMap(void *v, uintptr n) void *p; mstats.sys += n; - p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); if(p == (void*)-ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) diff --git a/src/pkg/runtime/mem_freebsd.c b/src/pkg/runtime/mem_freebsd.c index d1c22583d..f217e9db1 100644 --- a/src/pkg/runtime/mem_freebsd.c +++ b/src/pkg/runtime/mem_freebsd.c @@ -14,7 +14,7 @@ runtime·SysAlloc(uintptr n) void *v; mstats.sys += n; - v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(v < (void*)4096) return nil; return v; @@ -23,9 +23,7 @@ runtime·SysAlloc(uintptr n) void runtime·SysUnused(void *v, uintptr n) { - USED(v); - USED(n); - // TODO(rsc): call madvise MADV_DONTNEED + runtime·madvise(v, n, MADV_FREE); } void @@ -61,7 +59,7 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8) { - p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(p == (void*)-ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { @@ -71,7 +69,7 @@ runtime·SysMap(void *v, uintptr n) return; } - p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); if(p == (void*)-ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) diff --git a/src/pkg/runtime/mem_linux.c b/src/pkg/runtime/mem_linux.c index b3e79cc41..ebcec1e86 100644 --- a/src/pkg/runtime/mem_linux.c +++ b/src/pkg/runtime/mem_linux.c @@ -10,6 +10,7 @@ enum { + EAGAIN = 11, ENOMEM = 12, _PAGE_SIZE = 4096, }; @@ -56,13 +57,17 @@ runtime·SysAlloc(uintptr n) void *p; mstats.sys += n; - p = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + p = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(p < (void*)4096) { if(p == (void*)EACCES) { runtime·printf("runtime: mmap: access denied\n"); runtime·printf("if you're running SELinux, enable execmem for this process.\n"); runtime·exit(2); } + if(p == (void*)EAGAIN) { + runtime·printf("runtime: mmap: too much locked memory (check 'ulimit -l').\n"); + runtime·exit(2); + } return nil; } return p; @@ -113,7 +118,7 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8 && (uintptr)v >= 0xffffffffU) { - p = mmap_fixed(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + p = mmap_fixed(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { @@ -123,7 +128,7 @@ runtime·SysMap(void *v, uintptr n) return; } - p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) diff --git a/src/pkg/runtime/mem_netbsd.c b/src/pkg/runtime/mem_netbsd.c index 34ff31d90..77ce04c4e 100644 --- a/src/pkg/runtime/mem_netbsd.c +++ b/src/pkg/runtime/mem_netbsd.c @@ -19,7 +19,7 @@ runtime·SysAlloc(uintptr n) void *v; mstats.sys += n; - v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(v < (void*)4096) return nil; return v; @@ -28,9 +28,7 @@ runtime·SysAlloc(uintptr n) void runtime·SysUnused(void *v, uintptr n) { - USED(v); - USED(n); - // TODO(rsc): call madvise MADV_DONTNEED + runtime·madvise(v, n, MADV_FREE); } void @@ -67,7 +65,7 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8) { - p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(p == (void*)-ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { @@ -77,7 +75,7 @@ runtime·SysMap(void *v, uintptr n) return; } - p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); if(p == (void*)-ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) diff --git a/src/pkg/runtime/mem_openbsd.c b/src/pkg/runtime/mem_openbsd.c index 34ff31d90..77ce04c4e 100644 --- a/src/pkg/runtime/mem_openbsd.c +++ b/src/pkg/runtime/mem_openbsd.c @@ -19,7 +19,7 @@ runtime·SysAlloc(uintptr n) void *v; mstats.sys += n; - v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(v < (void*)4096) return nil; return v; @@ -28,9 +28,7 @@ runtime·SysAlloc(uintptr n) void runtime·SysUnused(void *v, uintptr n) { - USED(v); - USED(n); - // TODO(rsc): call madvise MADV_DONTNEED + runtime·madvise(v, n, MADV_FREE); } void @@ -67,7 +65,7 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8) { - p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if(p == (void*)-ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { @@ -77,7 +75,7 @@ runtime·SysMap(void *v, uintptr n) return; } - p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); if(p == (void*)-ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) diff --git a/src/pkg/runtime/mem_plan9.c b/src/pkg/runtime/mem_plan9.c index 15cbc176b..26ca367f1 100644 --- a/src/pkg/runtime/mem_plan9.c +++ b/src/pkg/runtime/mem_plan9.c @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "defs_GOOS_GOARCH.h" #include "arch_GOARCH.h" #include "malloc.h" #include "os_GOOS.h" @@ -13,14 +14,14 @@ static Lock memlock; enum { - Round = 4095 + Round = PAGESIZE-1 }; void* runtime·SysAlloc(uintptr nbytes) { uintptr bl; - + runtime·lock(&memlock); mstats.sys += nbytes; // Plan 9 sbrk from /sys/src/libc/9sys/sbrk.c diff --git a/src/pkg/runtime/memclr_arm.s b/src/pkg/runtime/memclr_arm.s new file mode 100644 index 000000000..afc529d90 --- /dev/null +++ b/src/pkg/runtime/memclr_arm.s @@ -0,0 +1,91 @@ +// Inferno's libkern/memset-arm.s +// http://code.google.com/p/inferno-os/source/browse/libkern/memset-arm.s +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. +// Portions Copyright 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +TO = 8 +TOE = 11 +N = 12 +TMP = 12 /* N and TMP don't overlap */ + +TEXT runtime·memclr(SB),7,$0 + MOVW ptr+0(FP), R(TO) + MOVW n+4(FP), R(N) + MOVW $0, R(0) + + ADD R(N), R(TO), R(TOE) /* to end pointer */ + + CMP $4, R(N) /* need at least 4 bytes to copy */ + BLT _1tail + + AND $0xFF, R(0) /* it's a byte */ + SLL $8, R(0), R(TMP) /* replicate to a word */ + ORR R(TMP), R(0) + SLL $16, R(0), R(TMP) + ORR R(TMP), R(0) + +_4align: /* align on 4 */ + AND.S $3, R(TO), R(TMP) + BEQ _4aligned + + MOVBU.P R(0), 1(R(TO)) /* implicit write back */ + B _4align + +_4aligned: + SUB $31, R(TOE), R(TMP) /* do 32-byte chunks if possible */ + CMP R(TMP), R(TO) + BHS _4tail + + MOVW R0, R1 /* replicate */ + MOVW R0, R2 + MOVW R0, R3 + MOVW R0, R4 + MOVW R0, R5 + MOVW R0, R6 + MOVW R0, R7 + +_f32loop: + CMP R(TMP), R(TO) + BHS _4tail + + MOVM.IA.W [R0-R7], (R(TO)) + B _f32loop + +_4tail: + SUB $3, R(TOE), R(TMP) /* do remaining words if possible */ +_4loop: + CMP R(TMP), R(TO) + BHS _1tail + + MOVW.P R(0), 4(R(TO)) /* implicit write back */ + B _4loop + +_1tail: + CMP R(TO), R(TOE) + BEQ _return + + MOVBU.P R(0), 1(R(TO)) /* implicit write back */ + B _1tail + +_return: + RET diff --git a/src/pkg/runtime/memmove_arm.s b/src/pkg/runtime/memmove_arm.s index 5c0e57404..c5d7e9d70 100644 --- a/src/pkg/runtime/memmove_arm.s +++ b/src/pkg/runtime/memmove_arm.s @@ -23,19 +23,40 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// TE or TS are spilled to the stack during bulk register moves. TS = 0 -TE = 1 -FROM = 2 -N = 3 -TMP = 3 /* N and TMP don't overlap */ -TMP1 = 4 - -// TODO(kaib): This can be done with the existing registers of LR is re-used. Same for memset. -TEXT runtime·memmove(SB), 7, $8 - // save g and m - MOVW R9, 4(R13) - MOVW R10, 8(R13) - +TE = 8 + +// Warning: the linker will use R11 to synthesize certain instructions. Please +// take care and double check with objdump. +FROM = 11 +N = 12 +TMP = 12 /* N and TMP don't overlap */ +TMP1 = 5 + +RSHIFT = 5 +LSHIFT = 6 +OFFSET = 7 + +BR0 = 0 /* shared with TS */ +BW0 = 1 +BR1 = 1 +BW1 = 2 +BR2 = 2 +BW2 = 3 +BR3 = 3 +BW3 = 4 + +FW0 = 1 +FR0 = 2 +FW1 = 2 +FR1 = 3 +FW2 = 3 +FR2 = 4 +FW3 = 4 +FR3 = 8 /* shared with TE */ + +TEXT runtime·memmove(SB), 7, $4 _memmove: MOVW to+0(FP), R(TS) MOVW from+4(FP), R(FROM) @@ -64,15 +85,17 @@ _b4aligned: /* is source now aligned? */ BNE _bunaligned ADD $31, R(TS), R(TMP) /* do 32-byte chunks if possible */ + MOVW R(TS), savedts+4(SP) _b32loop: CMP R(TMP), R(TE) BLS _b4tail - MOVM.DB.W (R(FROM)), [R4-R11] - MOVM.DB.W [R4-R11], (R(TE)) + MOVM.DB.W (R(FROM)), [R0-R7] + MOVM.DB.W [R0-R7], (R(TE)) B _b32loop _b4tail: /* do remaining words if possible */ + MOVW savedts+4(SP), R(TS) ADD $3, R(TS), R(TMP) _b4loop: CMP R(TMP), R(TE) @@ -107,22 +130,24 @@ _f4aligned: /* is source now aligned? */ BNE _funaligned SUB $31, R(TE), R(TMP) /* do 32-byte chunks if possible */ + MOVW R(TE), savedte+4(SP) _f32loop: CMP R(TMP), R(TS) BHS _f4tail - MOVM.IA.W (R(FROM)), [R4-R11] - MOVM.IA.W [R4-R11], (R(TS)) + MOVM.IA.W (R(FROM)), [R1-R8] + MOVM.IA.W [R1-R8], (R(TS)) B _f32loop _f4tail: + MOVW savedte+4(SP), R(TE) SUB $3, R(TE), R(TMP) /* do remaining words if possible */ _f4loop: CMP R(TMP), R(TS) BHS _f1tail MOVW.P 4(R(FROM)), R(TMP1) /* implicit write back */ - MOVW.P R4, 4(R(TS)) /* implicit write back */ + MOVW.P R(TMP1), 4(R(TS)) /* implicit write back */ B _f4loop _f1tail: @@ -134,25 +159,9 @@ _f1tail: B _f1tail _return: - // restore g and m - MOVW 4(R13), R9 - MOVW 8(R13), R10 MOVW to+0(FP), R0 RET -RSHIFT = 4 -LSHIFT = 5 -OFFSET = 6 - -BR0 = 7 -BW0 = 8 -BR1 = 8 -BW1 = 9 -BR2 = 9 -BW2 = 10 -BR3 = 10 -BW3 = 11 - _bunaligned: CMP $2, R(TMP) /* is R(TMP) < 2 ? */ @@ -172,7 +181,8 @@ _bunaligned: CMP R(TMP), R(TE) BLS _b1tail - AND $~0x03, R(FROM) /* align source */ + BIC $3, R(FROM) /* align source */ + MOVW R(TS), savedts+4(SP) MOVW (R(FROM)), R(BR0) /* prime first block register */ _bu16loop: @@ -196,18 +206,10 @@ _bu16loop: B _bu16loop _bu1tail: + MOVW savedts+4(SP), R(TS) ADD R(OFFSET), R(FROM) B _b1tail -FW0 = 7 -FR0 = 8 -FW1 = 8 -FR1 = 9 -FW2 = 9 -FR2 = 10 -FW3 = 10 -FR3 = 11 - _funaligned: CMP $2, R(TMP) @@ -227,7 +229,8 @@ _funaligned: CMP R(TMP), R(TS) BHS _f1tail - AND $~0x03, R(FROM) /* align source */ + BIC $3, R(FROM) /* align source */ + MOVW R(TE), savedte+4(SP) MOVW.P 4(R(FROM)), R(FR3) /* prime last block register, implicit write back */ _fu16loop: @@ -235,7 +238,7 @@ _fu16loop: BHS _fu1tail MOVW R(FR3)>>R(RSHIFT), R(FW0) - MOVM.IA.W (R(FROM)), [R(FR0)-R(FR3)] + MOVM.IA.W (R(FROM)), [R(FR0),R(FR1),R(FR2),R(FR3)] ORR R(FR0)<>R(RSHIFT), R(FW1) @@ -247,9 +250,10 @@ _fu16loop: MOVW R(FR2)>>R(RSHIFT), R(FW3) ORR R(FR3)<arena_start || obj >= runtime·mheap->arena_used) + return false; + + // obj may be a pointer to a live object. + // Try to find the beginning of the object. + + // Round down to word boundary. + obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1)); + + // Find bits for this word. + off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start; + bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + xbits = *bitp; + bits = xbits >> shift; + + // Pointing at the beginning of a block? + if((bits & (bitAllocated|bitBlockBoundary)) != 0) + goto found; + + // Otherwise consult span table to find beginning. + // (Manually inlined copy of MHeap_LookupMaybe.) + k = (uintptr)obj>>PageShift; + x = k; + if(sizeof(void*) == 8) + x -= (uintptr)runtime·mheap->arena_start>>PageShift; + s = runtime·mheap->map[x]; + if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse) + return false; + p = (byte*)((uintptr)s->start<sizeclass == 0) { + obj = p; + } else { + if((byte*)obj >= (byte*)s->limit) + return false; + uintptr size = s->elemsize; + int32 i = ((byte*)obj - p)/size; + obj = p+i*size; + } + + // Now that we know the object header, reload bits. + off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start; + bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + xbits = *bitp; + bits = xbits >> shift; + +found: + // Now we have bits, bitp, and shift correct for + // obj pointing at the base of the object. + // Only care about allocated and not marked. + if((bits & (bitAllocated|bitMarked)) != bitAllocated) + return false; + *bitp |= bitMarked< PtrTarget (pointer targets) +// ↑ | +// | | flushptrbuf (1st part, +// | | find block start) +// | ↓ +// `--------- BitTarget (pointer targets and the corresponding locations in bitmap) +// flushptrbuf +// (2nd part, mark and enqueue) static void -scanblock(byte *b, int64 n) +flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, BitTarget *bitbuf) { - byte *obj, *arena_start, *arena_used, *p; - void **vp; - uintptr size, *bitp, bits, shift, i, j, x, xbits, off, nobj, nproc; + byte *p, *arena_start, *obj; + uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti, n; MSpan *s; PageID k; - void **wp; + Obj *wp; Workbuf *wbuf; - bool keepworking; + PtrTarget *ptrbuf_end; + BitTarget *bitbufpos, *bt; - if((int64)(uintptr)n != n || n < 0) { - runtime·printf("scanblock %p %D\n", b, n); - runtime·throw("scanblock"); - } + arena_start = runtime·mheap->arena_start; - // Memory arena parameters. - arena_start = runtime·mheap.arena_start; - arena_used = runtime·mheap.arena_used; - nproc = work.nproc; + wp = *_wp; + wbuf = *_wbuf; + nobj = *_nobj; - wbuf = nil; // current work buffer - wp = nil; // storage for next queued pointer (write pointer) - nobj = 0; // number of queued objects + ptrbuf_end = *ptrbufpos; + n = ptrbuf_end - ptrbuf; + *ptrbufpos = ptrbuf; - // Scanblock helpers pass b==nil. - // The main proc needs to return to make more - // calls to scanblock. But if work.nproc==1 then - // might as well process blocks as soon as we - // have them. - keepworking = b == nil || work.nproc == 1; + if(CollectStats) { + runtime·xadd64(&gcstats.ptr.sum, n); + runtime·xadd64(&gcstats.ptr.cnt, 1); + } - // Align b to a word boundary. - off = (uintptr)b & (PtrSize-1); - if(off != 0) { - b += PtrSize - off; - n -= PtrSize - off; + // If buffer is nearly full, get a new one. + if(wbuf == nil || nobj+n >= nelem(wbuf->obj)) { + if(wbuf != nil) + wbuf->nobj = nobj; + wbuf = getempty(wbuf); + wp = wbuf->obj; + nobj = 0; + + if(n >= nelem(wbuf->obj)) + runtime·throw("ptrbuf has to be smaller than WorkBuf"); } - for(;;) { - // Each iteration scans the block b of length n, queueing pointers in - // the work buffer. - if(Debug > 1) - runtime·printf("scanblock %p %D\n", b, n); + // TODO(atom): This block is a branch of an if-then-else statement. + // The single-threaded branch may be added in a next CL. + { + // Multi-threaded version. - vp = (void**)b; - n >>= (2+PtrSize/8); /* n /= PtrSize (4 or 8) */ - for(i=0; i= arena_used) - continue; + while(ptrbuf < ptrbuf_end) { + obj = ptrbuf->p; + ti = ptrbuf->ti; + ptrbuf++; + + // obj belongs to interval [mheap.arena_start, mheap.arena_used). + if(Debug > 1) { + if(obj < runtime·mheap->arena_start || obj >= runtime·mheap->arena_used) + runtime·throw("object is outside of mheap"); + } // obj may be a pointer to a live object. // Try to find the beginning of the object. // Round down to word boundary. - obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1)); + if(((uintptr)obj & ((uintptr)PtrSize-1)) != 0) { + obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1)); + ti = 0; + } // Find bits for this word. off = (uintptr*)obj - (uintptr*)arena_start; @@ -211,6 +392,8 @@ scanblock(byte *b, int64 n) if((bits & (bitAllocated|bitBlockBoundary)) != 0) goto found; + ti = 0; + // Pointing just past the beginning? // Scan backward a little to find a block boundary. for(j=shift; j-->0; ) { @@ -228,16 +411,16 @@ scanblock(byte *b, int64 n) x = k; if(sizeof(void*) == 8) x -= (uintptr)arena_start>>PageShift; - s = runtime·mheap.map[x]; + s = runtime·mheap->map[x]; if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse) continue; - p = (byte*)((uintptr)s->start<start<sizeclass == 0) { obj = p; } else { if((byte*)obj >= (byte*)s->limit) continue; - size = runtime·class_to_size[s->sizeclass]; + size = s->elemsize; int32 i = ((byte*)obj - p)/size; obj = p+i*size; } @@ -255,80 +438,606 @@ scanblock(byte *b, int64 n) // Only care about allocated and not marked. if((bits & (bitAllocated|bitMarked)) != bitAllocated) continue; - if(nproc == 1) - *bitp |= bitMarked<bitp; + bits = xbits >> bt->shift; + if((bits & bitMarked) != 0) + continue; + + // Mark the block + *bt->bitp = xbits | (bitMarked << bt->shift); // If object has no pointers, don't need to scan further. if((bits & bitNoPointers) != 0) continue; - // If another proc wants a pointer, give it some. - if(nobj > 4 && work.nwait > 0 && work.full == nil) { + obj = bt->p; + + // Ask span about size class. + // (Manually inlined copy of MHeap_Lookup.) + x = (uintptr)obj >> PageShift; + if(sizeof(void*) == 8) + x -= (uintptr)arena_start>>PageShift; + s = runtime·mheap->map[x]; + + PREFETCH(obj); + + *wp = (Obj){obj, s->elemsize, bt->ti}; + wp++; + nobj++; + } + runtime·unlock(&lock); + + // If another proc wants a pointer, give it some. + if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) { + wbuf->nobj = nobj; + wbuf = handoff(wbuf); + nobj = wbuf->nobj; + wp = wbuf->obj + nobj; + } + } + + *_wp = wp; + *_wbuf = wbuf; + *_nobj = nobj; +} + +static void +flushobjbuf(Obj *objbuf, Obj **objbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj) +{ + uintptr nobj, off; + Obj *wp, obj; + Workbuf *wbuf; + Obj *objbuf_end; + + wp = *_wp; + wbuf = *_wbuf; + nobj = *_nobj; + + objbuf_end = *objbufpos; + *objbufpos = objbuf; + + while(objbuf < objbuf_end) { + obj = *objbuf++; + + // Align obj.b to a word boundary. + off = (uintptr)obj.p & (PtrSize-1); + if(off != 0) { + obj.p += PtrSize - off; + obj.n -= PtrSize - off; + obj.ti = 0; + } + + if(obj.p == nil || obj.n == 0) + continue; + + // If buffer is full, get a new one. + if(wbuf == nil || nobj >= nelem(wbuf->obj)) { + if(wbuf != nil) wbuf->nobj = nobj; - wbuf = handoff(wbuf); - nobj = wbuf->nobj; - wp = wbuf->obj + nobj; + wbuf = getempty(wbuf); + wp = wbuf->obj; + nobj = 0; + } + + *wp = obj; + wp++; + nobj++; + } + + // If another proc wants a pointer, give it some. + if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) { + wbuf->nobj = nobj; + wbuf = handoff(wbuf); + nobj = wbuf->nobj; + wp = wbuf->obj + nobj; + } + + *_wp = wp; + *_wbuf = wbuf; + *_nobj = nobj; +} + +// Program that scans the whole block and treats every block element as a potential pointer +static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR}; + +// Hashmap iterator program +static uintptr mapProg[2] = {0, GC_MAP_NEXT}; + +// Hchan program +static uintptr chanProg[2] = {0, GC_CHAN}; + +// Local variables of a program fragment or loop +typedef struct Frame Frame; +struct Frame { + uintptr count, elemsize, b; + uintptr *loop_or_ret; +}; + +// scanblock scans a block of n bytes starting at pointer b for references +// to other objects, scanning any it finds recursively until there are no +// unscanned objects left. Instead of using an explicit recursion, it keeps +// a work list in the Workbuf* structures and loops in the main function +// body. Keeping an explicit work list is easier on the stack allocator and +// more efficient. +// +// wbuf: current work buffer +// wp: storage for next queued pointer (write pointer) +// nobj: number of queued objects +static void +scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) +{ + byte *b, *arena_start, *arena_used; + uintptr n, i, end_b, elemsize, size, ti, objti, count, type; + uintptr *pc, precise_type, nominal_size; + uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti; + void *obj; + Type *t; + Slice *sliceptr; + Frame *stack_ptr, stack_top, stack[GC_STACK_CAPACITY+4]; + BufferList *scanbuffers; + PtrTarget *ptrbuf, *ptrbuf_end, *ptrbufpos; + BitTarget *bitbuf; + Obj *objbuf, *objbuf_end, *objbufpos; + Eface *eface; + Iface *iface; + Hmap *hmap; + MapType *maptype; + bool didmark, mapkey_kind, mapval_kind; + struct hash_gciter map_iter; + struct hash_gciter_data d; + Hchan *chan; + ChanType *chantype; + + if(sizeof(Workbuf) % PageSize != 0) + runtime·throw("scanblock: size of Workbuf is suboptimal"); + + // Memory arena parameters. + arena_start = runtime·mheap->arena_start; + arena_used = runtime·mheap->arena_used; + + stack_ptr = stack+nelem(stack)-1; + + precise_type = false; + nominal_size = 0; + + // Allocate ptrbuf, bitbuf + { + runtime·lock(&lock); + + if(bufferList == nil) { + bufferList = runtime·SysAlloc(sizeof(*bufferList)); + if(bufferList == nil) + runtime·throw("runtime: cannot allocate memory"); + bufferList->next = nil; + } + scanbuffers = bufferList; + bufferList = bufferList->next; + + ptrbuf = &scanbuffers->ptrtarget[0]; + ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget); + bitbuf = &scanbuffers->bittarget[0]; + objbuf = &scanbuffers->obj[0]; + objbuf_end = &scanbuffers->obj[0] + nelem(scanbuffers->obj); + + runtime·unlock(&lock); + } + + ptrbufpos = ptrbuf; + objbufpos = objbuf; + + // (Silence the compiler) + map_ret = nil; + mapkey_size = mapval_size = 0; + mapkey_kind = mapval_kind = false; + mapkey_ti = mapval_ti = 0; + chan = nil; + chantype = nil; + + goto next_block; + + for(;;) { + // Each iteration scans the block b of length n, queueing pointers in + // the work buffer. + if(Debug > 1) { + runtime·printf("scanblock %p %D\n", b, (int64)n); + } + + if(CollectStats) { + runtime·xadd64(&gcstats.nbytes, n); + runtime·xadd64(&gcstats.obj.sum, nobj); + runtime·xadd64(&gcstats.obj.cnt, 1); + } + + if(ti != 0) { + pc = (uintptr*)(ti & ~(uintptr)PC_BITS); + precise_type = (ti & PRECISE); + stack_top.elemsize = pc[0]; + if(!precise_type) + nominal_size = pc[0]; + if(ti & LOOP) { + stack_top.count = 0; // 0 means an infinite number of iterations + stack_top.loop_or_ret = pc+1; + } else { + stack_top.count = 1; } + } else if(UseSpanType) { + if(CollectStats) + runtime·xadd64(&gcstats.obj.notype, 1); + + type = runtime·gettype(b); + if(type != 0) { + if(CollectStats) + runtime·xadd64(&gcstats.obj.typelookup, 1); + + t = (Type*)(type & ~(uintptr)(PtrSize-1)); + switch(type & (PtrSize-1)) { + case TypeInfo_SingleObject: + pc = (uintptr*)t->gc; + precise_type = true; // type information about 'b' is precise + stack_top.count = 1; + stack_top.elemsize = pc[0]; + break; + case TypeInfo_Array: + pc = (uintptr*)t->gc; + if(pc[0] == 0) + goto next_block; + precise_type = true; // type information about 'b' is precise + stack_top.count = 0; // 0 means an infinite number of iterations + stack_top.elemsize = pc[0]; + stack_top.loop_or_ret = pc+1; + break; + case TypeInfo_Map: + hmap = (Hmap*)b; + maptype = (MapType*)t; + if(hash_gciter_init(hmap, &map_iter)) { + mapkey_size = maptype->key->size; + mapkey_kind = maptype->key->kind; + mapkey_ti = (uintptr)maptype->key->gc | PRECISE; + mapval_size = maptype->elem->size; + mapval_kind = maptype->elem->kind; + mapval_ti = (uintptr)maptype->elem->gc | PRECISE; + + map_ret = 0; + pc = mapProg; + } else { + goto next_block; + } + break; + case TypeInfo_Chan: + chan = (Hchan*)b; + chantype = (ChanType*)t; + pc = chanProg; + break; + default: + runtime·throw("scanblock: invalid type"); + return; + } + } else { + pc = defaultProg; + } + } else { + pc = defaultProg; + } + + pc++; + stack_top.b = (uintptr)b; - // If buffer is full, get a new one. - if(wbuf == nil || nobj >= nelem(wbuf->obj)) { - if(wbuf != nil) - wbuf->nobj = nobj; - wbuf = getempty(wbuf); - wp = wbuf->obj; - nobj = 0; + end_b = (uintptr)b + n - PtrSize; + + for(;;) { + if(CollectStats) + runtime·xadd64(&gcstats.instr[pc[0]], 1); + + obj = nil; + objti = 0; + switch(pc[0]) { + case GC_PTR: + obj = *(void**)(stack_top.b + pc[1]); + objti = pc[2]; + pc += 3; + break; + + case GC_SLICE: + sliceptr = (Slice*)(stack_top.b + pc[1]); + if(sliceptr->cap != 0) { + obj = sliceptr->array; + objti = pc[2] | PRECISE | LOOP; } - *wp++ = obj; - nobj++; - continue_obj:; + pc += 3; + break; + + case GC_APTR: + obj = *(void**)(stack_top.b + pc[1]); + pc += 2; + break; + + case GC_STRING: + obj = *(void**)(stack_top.b + pc[1]); + pc += 2; + break; + + case GC_EFACE: + eface = (Eface*)(stack_top.b + pc[1]); + pc += 2; + if(eface->type != nil && (eface->data >= arena_start && eface->data < arena_used)) { + t = eface->type; + if(t->size <= sizeof(void*)) { + if((t->kind & KindNoPointers)) + break; + + obj = eface->data; + if((t->kind & ~KindNoPointers) == KindPtr) + objti = (uintptr)((PtrType*)t)->elem->gc; + } else { + obj = eface->data; + objti = (uintptr)t->gc; + } + } + break; + + case GC_IFACE: + iface = (Iface*)(stack_top.b + pc[1]); + pc += 2; + if(iface->tab == nil) + break; + + // iface->tab + if((void*)iface->tab >= arena_start && (void*)iface->tab < arena_used) { + *ptrbufpos++ = (PtrTarget){iface->tab, (uintptr)itabtype->gc}; + if(ptrbufpos == ptrbuf_end) + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + } + + // iface->data + if(iface->data >= arena_start && iface->data < arena_used) { + t = iface->tab->type; + if(t->size <= sizeof(void*)) { + if((t->kind & KindNoPointers)) + break; + + obj = iface->data; + if((t->kind & ~KindNoPointers) == KindPtr) + objti = (uintptr)((PtrType*)t)->elem->gc; + } else { + obj = iface->data; + objti = (uintptr)t->gc; + } + } + break; + + case GC_DEFAULT_PTR: + while((i = stack_top.b) <= end_b) { + stack_top.b += PtrSize; + obj = *(byte**)i; + if(obj >= arena_start && obj < arena_used) { + *ptrbufpos++ = (PtrTarget){obj, 0}; + if(ptrbufpos == ptrbuf_end) + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + } + } + goto next_block; + + case GC_END: + if(--stack_top.count != 0) { + // Next iteration of a loop if possible. + elemsize = stack_top.elemsize; + stack_top.b += elemsize; + if(stack_top.b + elemsize <= end_b+PtrSize) { + pc = stack_top.loop_or_ret; + continue; + } + i = stack_top.b; + } else { + // Stack pop if possible. + if(stack_ptr+1 < stack+nelem(stack)) { + pc = stack_top.loop_or_ret; + stack_top = *(++stack_ptr); + continue; + } + i = (uintptr)b + nominal_size; + } + if(!precise_type) { + // Quickly scan [b+i,b+n) for possible pointers. + for(; i<=end_b; i+=PtrSize) { + if(*(byte**)i != nil) { + // Found a value that may be a pointer. + // Do a rescan of the entire block. + enqueue((Obj){b, n, 0}, &wbuf, &wp, &nobj); + if(CollectStats) { + runtime·xadd64(&gcstats.rescan, 1); + runtime·xadd64(&gcstats.rescanbytes, n); + } + break; + } + } + } + goto next_block; + + case GC_ARRAY_START: + i = stack_top.b + pc[1]; + count = pc[2]; + elemsize = pc[3]; + pc += 4; + + // Stack push. + *stack_ptr-- = stack_top; + stack_top = (Frame){count, elemsize, i, pc}; + continue; + + case GC_ARRAY_NEXT: + if(--stack_top.count != 0) { + stack_top.b += stack_top.elemsize; + pc = stack_top.loop_or_ret; + } else { + // Stack pop. + stack_top = *(++stack_ptr); + pc += 1; + } + continue; + + case GC_CALL: + // Stack push. + *stack_ptr-- = stack_top; + stack_top = (Frame){1, 0, stack_top.b + pc[1], pc+3 /*return address*/}; + pc = (uintptr*)((byte*)pc + *(int32*)(pc+2)); // target of the CALL instruction + continue; + + case GC_MAP_PTR: + hmap = *(Hmap**)(stack_top.b + pc[1]); + if(hmap == nil) { + pc += 3; + continue; + } + runtime·lock(&lock); + didmark = markonly(hmap); + runtime·unlock(&lock); + if(didmark) { + maptype = (MapType*)pc[2]; + if(hash_gciter_init(hmap, &map_iter)) { + mapkey_size = maptype->key->size; + mapkey_kind = maptype->key->kind; + mapkey_ti = (uintptr)maptype->key->gc | PRECISE; + mapval_size = maptype->elem->size; + mapval_kind = maptype->elem->kind; + mapval_ti = (uintptr)maptype->elem->gc | PRECISE; + + // Start mapProg. + map_ret = pc+3; + pc = mapProg+1; + } else { + pc += 3; + } + } else { + pc += 3; + } + continue; + + case GC_MAP_NEXT: + // Add all keys and values to buffers, mark all subtables. + while(hash_gciter_next(&map_iter, &d)) { + // buffers: reserve space for 2 objects. + if(ptrbufpos+2 >= ptrbuf_end) + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + if(objbufpos+2 >= objbuf_end) + flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); + + if(d.st != nil) { + runtime·lock(&lock); + markonly(d.st); + runtime·unlock(&lock); + } + if(d.key_data != nil) { + if(!(mapkey_kind & KindNoPointers) || d.indirectkey) { + if(!d.indirectkey) + *objbufpos++ = (Obj){d.key_data, mapkey_size, mapkey_ti}; + else + *ptrbufpos++ = (PtrTarget){*(void**)d.key_data, mapkey_ti}; + } + if(!(mapval_kind & KindNoPointers) || d.indirectval) { + if(!d.indirectval) + *objbufpos++ = (Obj){d.val_data, mapval_size, mapval_ti}; + else + *ptrbufpos++ = (PtrTarget){*(void**)d.val_data, mapval_ti}; + } + } + } + if(map_ret == 0) + goto next_block; + pc = map_ret; + continue; + + case GC_REGION: + obj = (void*)(stack_top.b + pc[1]); + size = pc[2]; + objti = pc[3]; + pc += 4; + + *objbufpos++ = (Obj){obj, size, objti}; + if(objbufpos == objbuf_end) + flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); + break; + + case GC_CHAN: + // There are no heap pointers in struct Hchan, + // so we can ignore the leading sizeof(Hchan) bytes. + if(!(chantype->elem->kind & KindNoPointers)) { + // Channel's buffer follows Hchan immediately in memory. + // Size of buffer (cap(c)) is second int in the chan struct. + n = ((uintgo*)chan)[1]; + if(n > 0) { + // TODO(atom): split into two chunks so that only the + // in-use part of the circular buffer is scanned. + // (Channel routines zero the unused part, so the current + // code does not lead to leaks, it's just a little inefficient.) + *objbufpos++ = (Obj){(byte*)chan+runtime·Hchansize, n*chantype->elem->size, + (uintptr)chantype->elem->gc | PRECISE | LOOP}; + if(objbufpos == objbuf_end) + flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); + } + } + goto next_block; + + default: + runtime·throw("scanblock: invalid GC instruction"); + return; } + if(obj >= arena_start && obj < arena_used) { + *ptrbufpos++ = (PtrTarget){obj, objti}; + if(ptrbufpos == ptrbuf_end) + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + } + } + + next_block: // Done scanning [b, b+n). Prepare for the next iteration of - // the loop by setting b and n to the parameters for the next block. + // the loop by setting b, n, ti to the parameters for the next block. - // Fetch b from the work buffer. if(nobj == 0) { - if(!keepworking) { - putempty(wbuf); - return; + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); + + if(nobj == 0) { + if(!keepworking) { + if(wbuf) + putempty(wbuf); + goto endscan; + } + // Emptied our buffer: refill. + wbuf = getfull(wbuf); + if(wbuf == nil) + goto endscan; + nobj = wbuf->nobj; + wp = wbuf->obj + wbuf->nobj; } - // Emptied our buffer: refill. - wbuf = getfull(wbuf); - if(wbuf == nil) - return; - nobj = wbuf->nobj; - wp = wbuf->obj + wbuf->nobj; } - b = *--wp; - nobj--; - // Ask span about size class. - // (Manually inlined copy of MHeap_Lookup.) - x = (uintptr)b>>PageShift; - if(sizeof(void*) == 8) - x -= (uintptr)arena_start>>PageShift; - s = runtime·mheap.map[x]; - if(s->sizeclass == 0) - n = s->npages<sizeclass]; + // Fetch b from the work buffer. + --wp; + b = wp->p; + n = wp->n; + ti = wp->ti; + nobj--; } + +endscan: + runtime·lock(&lock); + scanbuffers->next = bufferList; + bufferList = scanbuffers; + runtime·unlock(&lock); } // debug_scanblock is the debug copy of scanblock. // it is simpler, slower, single-threaded, recursive, // and uses bitSpecial as the mark bit. static void -debug_scanblock(byte *b, int64 n) +debug_scanblock(byte *b, uintptr n) { byte *obj, *p; void **vp; @@ -338,8 +1047,8 @@ debug_scanblock(byte *b, int64 n) if(!DebugMark) runtime·throw("debug_scanblock without DebugMark"); - if((int64)(uintptr)n != n || n < 0) { - runtime·printf("debug_scanblock %p %D\n", b, n); + if((intptr)n < 0) { + runtime·printf("debug_scanblock %p %D\n", b, (int64)n); runtime·throw("debug_scanblock"); } @@ -356,33 +1065,31 @@ debug_scanblock(byte *b, int64 n) obj = (byte*)vp[i]; // Words outside the arena cannot be pointers. - if((byte*)obj < runtime·mheap.arena_start || (byte*)obj >= runtime·mheap.arena_used) + if((byte*)obj < runtime·mheap->arena_start || (byte*)obj >= runtime·mheap->arena_used) continue; // Round down to word boundary. obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1)); // Consult span table to find beginning. - s = runtime·MHeap_LookupMaybe(&runtime·mheap, obj); + s = runtime·MHeap_LookupMaybe(runtime·mheap, obj); if(s == nil) continue; - p = (byte*)((uintptr)s->start<elemsize; if(s->sizeclass == 0) { obj = p; - size = (uintptr)s->npages<= (byte*)s->limit) continue; - size = runtime·class_to_size[s->sizeclass]; int32 i = ((byte*)obj - p)/size; obj = p+i*size; } // Now that we know the object header, reload bits. - off = (uintptr*)obj - (uintptr*)runtime·mheap.arena_start; - bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start; + bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; xbits = *bitp; bits = xbits >> shift; @@ -404,53 +1111,98 @@ debug_scanblock(byte *b, int64 n) } } +// Append obj to the work buffer. +// _wbuf, _wp, _nobj are input/output parameters and are specifying the work buffer. +static void +enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj) +{ + uintptr nobj, off; + Obj *wp; + Workbuf *wbuf; + + if(Debug > 1) + runtime·printf("append obj(%p %D %p)\n", obj.p, (int64)obj.n, obj.ti); + + // Align obj.b to a word boundary. + off = (uintptr)obj.p & (PtrSize-1); + if(off != 0) { + obj.p += PtrSize - off; + obj.n -= PtrSize - off; + obj.ti = 0; + } + + if(obj.p == nil || obj.n == 0) + return; + + // Load work buffer state + wp = *_wp; + wbuf = *_wbuf; + nobj = *_nobj; + + // If another proc wants a pointer, give it some. + if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) { + wbuf->nobj = nobj; + wbuf = handoff(wbuf); + nobj = wbuf->nobj; + wp = wbuf->obj + nobj; + } + + // If buffer is full, get a new one. + if(wbuf == nil || nobj >= nelem(wbuf->obj)) { + if(wbuf != nil) + wbuf->nobj = nobj; + wbuf = getempty(wbuf); + wp = wbuf->obj; + nobj = 0; + } + + *wp = obj; + wp++; + nobj++; + + // Save work buffer state + *_wp = wp; + *_wbuf = wbuf; + *_nobj = nobj; +} + +static void +markroot(ParFor *desc, uint32 i) +{ + Obj *wp; + Workbuf *wbuf; + uintptr nobj; + + USED(&desc); + wp = nil; + wbuf = nil; + nobj = 0; + enqueue(work.roots[i], &wbuf, &wp, &nobj); + scanblock(wbuf, wp, nobj, false); +} + // Get an empty work buffer off the work.empty list, // allocating new buffers as needed. static Workbuf* getempty(Workbuf *b) { - if(work.nproc == 1) { - // Put b on full list. - if(b != nil) { - b->next = work.full; - work.full = b; + if(b != nil) + runtime·lfstackpush(&work.full, &b->node); + b = (Workbuf*)runtime·lfstackpop(&work.empty); + if(b == nil) { + // Need to allocate. + runtime·lock(&work); + if(work.nchunk < sizeof *b) { + work.nchunk = 1<<20; + work.chunk = runtime·SysAlloc(work.nchunk); + if(work.chunk == nil) + runtime·throw("runtime: cannot allocate memory"); } - // Grab from empty list if possible. - b = work.empty; - if(b != nil) { - work.empty = b->next; - goto haveb; - } - } else { - // Put b on full list. - if(b != nil) { - runtime·lock(&work.fmu); - b->next = work.full; - work.full = b; - runtime·unlock(&work.fmu); - } - // Grab from empty list if possible. - runtime·lock(&work.emu); - b = work.empty; - if(b != nil) - work.empty = b->next; - runtime·unlock(&work.emu); - if(b != nil) - goto haveb; + b = (Workbuf*)work.chunk; + work.chunk += sizeof *b; + work.nchunk -= sizeof *b; + runtime·unlock(&work); } - - // Need to allocate. - runtime·lock(&work); - if(work.nchunk < sizeof *b) { - work.nchunk = 1<<20; - work.chunk = runtime·SysAlloc(work.nchunk); - } - b = (Workbuf*)work.chunk; - work.chunk += sizeof *b; - work.nchunk -= sizeof *b; - runtime·unlock(&work); - -haveb: b->nobj = 0; return b; } @@ -458,19 +1210,10 @@ haveb: static void putempty(Workbuf *b) { - if(b == nil) - return; + if(CollectStats) + runtime·xadd64(&gcstats.putempty, 1); - if(work.nproc == 1) { - b->next = work.empty; - work.empty = b; - return; - } - - runtime·lock(&work.emu); - b->next = work.empty; - work.empty = b; - runtime·unlock(&work.emu); + runtime·lfstackpush(&work.empty, &b->node); } // Get a full work buffer off the work.full list, or return nil. @@ -478,63 +1221,37 @@ static Workbuf* getfull(Workbuf *b) { int32 i; - Workbuf *b1; - - if(work.nproc == 1) { - // Put b on empty list. - if(b != nil) { - b->next = work.empty; - work.empty = b; - } - // Grab from full list if possible. - // Since work.nproc==1, no one else is - // going to give us work. - b = work.full; - if(b != nil) - work.full = b->next; - return b; - } - putempty(b); + if(CollectStats) + runtime·xadd64(&gcstats.getfull, 1); - // Grab buffer from full list if possible. - for(;;) { - b1 = work.full; - if(b1 == nil) - break; - runtime·lock(&work.fmu); - if(work.full != nil) { - b1 = work.full; - work.full = b1->next; - runtime·unlock(&work.fmu); - return b1; - } - runtime·unlock(&work.fmu); - } + if(b != nil) + runtime·lfstackpush(&work.empty, &b->node); + b = (Workbuf*)runtime·lfstackpop(&work.full); + if(b != nil || work.nproc == 1) + return b; runtime·xadd(&work.nwait, +1); for(i=0;; i++) { - b1 = work.full; - if(b1 != nil) { - runtime·lock(&work.fmu); - if(work.full != nil) { - runtime·xadd(&work.nwait, -1); - b1 = work.full; - work.full = b1->next; - runtime·unlock(&work.fmu); - return b1; - } - runtime·unlock(&work.fmu); - continue; + if(work.full != 0) { + runtime·xadd(&work.nwait, -1); + b = (Workbuf*)runtime·lfstackpop(&work.full); + if(b != nil) + return b; + runtime·xadd(&work.nwait, +1); } if(work.nwait == work.nproc) return nil; - if(i < 10) + if(i < 10) { + m->gcstats.nprocyield++; runtime·procyield(20); - else if(i < 20) + } else if(i < 20) { + m->gcstats.nosyield++; runtime·osyield(); - else + } else { + m->gcstats.nsleep++; runtime·usleep(100); + } } } @@ -550,20 +1267,40 @@ handoff(Workbuf *b) b->nobj -= n; b1->nobj = n; runtime·memmove(b1->obj, b->obj+b->nobj, n*sizeof b1->obj[0]); - nhandoff += n; + m->gcstats.nhandoff++; + m->gcstats.nhandoffcnt += n; // Put b on full list - let first half of b get stolen. - runtime·lock(&work.fmu); - b->next = work.full; - work.full = b; - runtime·unlock(&work.fmu); - + runtime·lfstackpush(&work.full, &b->node); return b1; } -// Scanstack calls scanblock on each of gp's stack segments. static void -scanstack(void (*scanblock)(byte*, int64), G *gp) +addroot(Obj obj) +{ + uint32 cap; + Obj *new; + + if(work.nroot >= work.rootcap) { + cap = PageSize/sizeof(Obj); + if(cap < 2*work.rootcap) + cap = 2*work.rootcap; + new = (Obj*)runtime·SysAlloc(cap*sizeof(Obj)); + if(new == nil) + runtime·throw("runtime: cannot allocate memory"); + if(work.roots != nil) { + runtime·memmove(new, work.roots, work.rootcap*sizeof(Obj)); + runtime·SysFree(work.roots, work.rootcap*sizeof(Obj)); + } + work.roots = new; + work.rootcap = cap; + } + work.roots[work.nroot] = obj; + work.nroot++; +} + +static void +addstackroots(G *gp) { M *mp; int32 n; @@ -571,7 +1308,7 @@ scanstack(void (*scanblock)(byte*, int64), G *gp) byte *sp, *guard; stk = (Stktop*)gp->stackbase; - guard = gp->stackguard; + guard = (byte*)gp->stackguard; if(gp == g) { // Scanning our own stack: start at &gp. @@ -582,72 +1319,82 @@ scanstack(void (*scanblock)(byte*, int64), G *gp) } else { // Scanning another goroutine's stack. // The goroutine is usually asleep (the world is stopped). - sp = gp->sched.sp; + sp = (byte*)gp->sched.sp; // The exception is that if the goroutine is about to enter or might // have just exited a system call, it may be executing code such // as schedlock and may have needed to start a new stack segment. // Use the stack segment and stack pointer at the time of // the system call instead, since that won't change underfoot. - if(gp->gcstack != nil) { + if(gp->gcstack != (uintptr)nil) { stk = (Stktop*)gp->gcstack; - sp = gp->gcsp; - guard = gp->gcguard; + sp = (byte*)gp->gcsp; + guard = (byte*)gp->gcguard; } } - if(Debug > 1) - runtime·printf("scanstack %d %p\n", gp->goid, sp); n = 0; while(stk) { if(sp < guard-StackGuard || (byte*)stk < sp) { - runtime·printf("scanstack inconsistent: g%d#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk); + runtime·printf("scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk); runtime·throw("scanstack"); } - scanblock(sp, (byte*)stk - sp); - sp = stk->gobuf.sp; + addroot((Obj){sp, (byte*)stk - sp, 0}); + sp = (byte*)stk->gobuf.sp; guard = stk->stackguard; stk = (Stktop*)stk->stackbase; n++; } } -// Markfin calls scanblock on the blocks that have finalizers: -// the things pointed at cannot be freed until the finalizers have run. static void -markfin(void *v) +addfinroots(void *v) { uintptr size; + void *base; size = 0; - if(!runtime·mlookup(v, &v, &size, nil) || !runtime·blockspecial(v)) + if(!runtime·mlookup(v, &base, &size, nil) || !runtime·blockspecial(base)) runtime·throw("mark - finalizer inconsistency"); // do not mark the finalizer block itself. just mark the things it points at. - scanblock(v, size); + addroot((Obj){base, size, 0}); } static void -debug_markfin(void *v) -{ - uintptr size; - - if(!runtime·mlookup(v, &v, &size, nil)) - runtime·throw("debug_mark - finalizer inconsistency"); - debug_scanblock(v, size); -} - -// Mark -static void -mark(void (*scan)(byte*, int64)) +addroots(void) { G *gp; FinBlock *fb; + MSpan *s, **allspans; + uint32 spanidx; + + work.nroot = 0; + + // data & bss + // TODO(atom): load balancing + addroot((Obj){data, edata - data, (uintptr)gcdata}); + addroot((Obj){bss, ebss - bss, (uintptr)gcbss}); + + // MSpan.types + allspans = runtime·mheap->allspans; + for(spanidx=0; spanidxnspan; spanidx++) { + s = allspans[spanidx]; + if(s->state == MSpanInUse) { + switch(s->types.compression) { + case MTypes_Empty: + case MTypes_Single: + break; + case MTypes_Words: + case MTypes_Bytes: + // TODO(atom): consider using defaultProg instead of 0 + addroot((Obj){(byte*)&s->types.data, sizeof(void*), 0}); + break; + } + } + } - // mark data+bss. - scan(data, ebss - data); - - // mark stacks + // stacks for(gp=runtime·allg; gp!=nil; gp=gp->alllink) { switch(gp->status){ default: @@ -658,37 +1405,30 @@ mark(void (*scan)(byte*, int64)) case Grunning: if(gp != g) runtime·throw("mark - world not stopped"); - scanstack(scan, gp); + addstackroots(gp); break; case Grunnable: case Gsyscall: case Gwaiting: - scanstack(scan, gp); + addstackroots(gp); break; } } - // mark things pointed at by objects with finalizers - if(scan == debug_scanblock) - runtime·walkfintab(debug_markfin); - else - runtime·walkfintab(markfin); + runtime·walkfintab(addfinroots); for(fb=allfin; fb; fb=fb->alllink) - scanblock((byte*)fb->fin, fb->cnt*sizeof(fb->fin[0])); - - // in multiproc mode, join in the queued work. - scan(nil, 0); + addroot((Obj){(byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]), 0}); } static bool handlespecial(byte *p, uintptr size) { - void (*fn)(void*); - int32 nret; + FuncVal *fn; + uintptr nret; FinBlock *block; Finalizer *f; - + if(!runtime·getfinalizer(p, true, &fn, &nret)) { runtime·setblockspecial(p, false); runtime·MProf_Free(p, size); @@ -699,6 +1439,8 @@ handlespecial(byte *p, uintptr size) if(finq == nil || finq->cnt == finq->cap) { if(finc == nil) { finc = runtime·SysAlloc(PageSize); + if(finc == nil) + runtime·throw("runtime: cannot allocate memory"); finc->cap = (PageSize - sizeof(FinBlock)) / sizeof(Finalizer) + 1; finc->alllink = allfin; allfin = finc; @@ -713,124 +1455,225 @@ handlespecial(byte *p, uintptr size) f->fn = fn; f->nret = nret; f->arg = p; - runtime·unlock(&finlock); + runtime·unlock(&finlock); return true; } // Sweep frees or collects finalizers for blocks not marked in the mark phase. // It clears the mark bits in preparation for the next GC round. static void -sweep(void) +sweepspan(ParFor *desc, uint32 idx) { - MSpan *s; int32 cl, n, npages; uintptr size; byte *p; MCache *c; byte *arena_start; - int64 now; + MLink head, *end; + int32 nfree; + byte *type_data; + byte compression; + uintptr type_data_inc; + MSpan *s; - arena_start = runtime·mheap.arena_start; - now = runtime·nanotime(); + USED(&desc); + s = runtime·mheap->allspans[idx]; + if(s->state != MSpanInUse) + return; + arena_start = runtime·mheap->arena_start; + p = (byte*)(s->start << PageShift); + cl = s->sizeclass; + size = s->elemsize; + if(cl == 0) { + n = 1; + } else { + // Chunk full of small blocks. + npages = runtime·class_to_allocnpages[cl]; + n = (npages << PageShift) / size; + } + nfree = 0; + end = &head; + c = m->mcache; + + type_data = (byte*)s->types.data; + type_data_inc = sizeof(uintptr); + compression = s->types.compression; + switch(compression) { + case MTypes_Bytes: + type_data += 8*sizeof(uintptr); + type_data_inc = 1; + break; + } - for(;;) { - s = work.spans; - if(s == nil) - break; - if(!runtime·casp(&work.spans, s, s->allnext)) - continue; + // Sweep through n objects of given size starting at p. + // This thread owns the span now, so it can manipulate + // the block bitmap without atomic operations. + for(; n > 0; n--, p += size, type_data+=type_data_inc) { + uintptr off, *bitp, shift, bits; - // Stamp newly unused spans. The scavenger will use that - // info to potentially give back some pages to the OS. - if(s->state == MSpanFree && s->unusedsince == 0) - s->unusedsince = now; + off = (uintptr*)p - (uintptr*)arena_start; + bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1; + shift = off % wordsPerBitmapWord; + bits = *bitp>>shift; - if(s->state != MSpanInUse) + if((bits & bitAllocated) == 0) continue; - p = (byte*)(s->start << PageShift); - cl = s->sizeclass; + if((bits & bitMarked) != 0) { + if(DebugMark) { + if(!(bits & bitSpecial)) + runtime·printf("found spurious mark on %p\n", p); + *bitp &= ~(bitSpecial<npages<local_alloc -= size; + c->local_nfree++; } else { - // Chunk full of small blocks. - size = runtime·class_to_size[cl]; - npages = runtime·class_to_allocnpages[cl]; - n = (npages << PageShift) / size; + // Free small object. + switch(compression) { + case MTypes_Words: + *(uintptr*)type_data = 0; + break; + case MTypes_Bytes: + *(byte*)type_data = 0; + break; + } + if(size > sizeof(uintptr)) + ((uintptr*)p)[1] = 1; // mark as "needs to be zeroed" + + end->next = (MLink*)p; + end = (MLink*)p; + nfree++; } + } - // Sweep through n objects of given size starting at p. - // This thread owns the span now, so it can manipulate - // the block bitmap without atomic operations. - for(; n > 0; n--, p += size) { - uintptr off, *bitp, shift, bits; + if(nfree) { + c->local_by_size[cl].nfree += nfree; + c->local_alloc -= size * nfree; + c->local_nfree += nfree; + c->local_cachealloc -= nfree * size; + c->local_objects -= nfree; + runtime·MCentral_FreeSpan(&runtime·mheap->central[cl], s, nfree, head.next, end); + } +} - off = (uintptr*)p - (uintptr*)arena_start; - bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1; - shift = off % wordsPerBitmapWord; - bits = *bitp>>shift; +static void +dumpspan(uint32 idx) +{ + int32 sizeclass, n, npages, i, column; + uintptr size; + byte *p; + byte *arena_start; + MSpan *s; + bool allocated, special; - if((bits & bitAllocated) == 0) - continue; + s = runtime·mheap->allspans[idx]; + if(s->state != MSpanInUse) + return; + arena_start = runtime·mheap->arena_start; + p = (byte*)(s->start << PageShift); + sizeclass = s->sizeclass; + size = s->elemsize; + if(sizeclass == 0) { + n = 1; + } else { + npages = runtime·class_to_allocnpages[sizeclass]; + n = (npages << PageShift) / size; + } + + runtime·printf("%p .. %p:\n", p, p+n*size); + column = 0; + for(; n>0; n--, p+=size) { + uintptr off, *bitp, shift, bits; - if((bits & bitMarked) != 0) { - if(DebugMark) { - if(!(bits & bitSpecial)) - runtime·printf("found spurious mark on %p\n", p); - *bitp &= ~(bitSpecial<>shift; - // Special means it has a finalizer or is being profiled. - // In DebugMark mode, the bit has been coopted so - // we have to assume all blocks are special. - if(DebugMark || (bits & bitSpecial) != 0) { - if(handlespecial(p, size)) - continue; + allocated = ((bits & bitAllocated) != 0); + special = ((bits & bitSpecial) != 0); + + for(i=0; imcache; - if(s->sizeclass == 0) { - // Free large span. - runtime·unmarkspan(p, 1< sizeof(uintptr)) - ((uintptr*)p)[1] = 1; // mark as "needs to be zeroed" - c->local_by_size[s->sizeclass].nfree++; - runtime·MCache_Free(c, p, s->sizeclass, size); + if(i+sizeof(void*) >= size) { + runtime·printf(allocated ? ") " : "] "); + } + + column++; + if(column == 8) { + runtime·printf("\n"); + column = 0; } - c->local_alloc -= size; - c->local_nfree++; } } + runtime·printf("\n"); +} + +// A debugging function to dump the contents of memory +void +runtime·memorydump(void) +{ + uint32 spanidx; + + for(spanidx=0; spanidxnspan; spanidx++) { + dumpspan(spanidx); + } } void runtime·gchelper(void) { - // Wait until main proc is ready for mark help. - runtime·lock(&work.markgate); - runtime·unlock(&work.markgate); - scanblock(nil, 0); + // parallel mark for over gc roots + runtime·parfordo(work.markfor); + + // help other threads scan secondary blocks + scanblock(nil, nil, 0, true); - // Wait until main proc is ready for sweep help. - runtime·lock(&work.sweepgate); - runtime·unlock(&work.sweepgate); - sweep(); + if(DebugMark) { + // wait while the main thread executes mark(debug_scanblock) + while(runtime·atomicload(&work.debugmarkdone) == 0) + runtime·usleep(10); + } + runtime·parfordo(work.sweepfor); if(runtime·xadd(&work.ndone, +1) == work.nproc-1) runtime·notewakeup(&work.alldone); } +#define GcpercentUnknown (-2) + // Initialized from $GOGC. GOGC=off means no gc. // // Next gc is after we've allocated an extra amount of @@ -840,33 +1683,36 @@ runtime·gchelper(void) // proportion to the allocation cost. Adjusting gcpercent // just changes the linear constant (and also the amount of // extra memory used). -static int32 gcpercent = -2; +static int32 gcpercent = GcpercentUnknown; static void -stealcache(void) +cachestats(GCStats *stats) { - M *m; - - for(m=runtime·allm; m; m=m->alllink) - runtime·MCache_ReleaseAll(m->mcache); -} - -static void -cachestats(void) -{ - M *m; + M *mp; MCache *c; + P *p, **pp; int32 i; uint64 stacks_inuse; - uint64 stacks_sys; + uint64 *src, *dst; + if(stats) + runtime·memclr((byte*)stats, sizeof(*stats)); stacks_inuse = 0; - stacks_sys = 0; - for(m=runtime·allm; m; m=m->alllink) { - runtime·purgecachedstats(m); - stacks_inuse += m->stackalloc->inuse; - stacks_sys += m->stackalloc->sys; - c = m->mcache; + for(mp=runtime·allm; mp; mp=mp->alllink) { + stacks_inuse += mp->stackinuse*FixedStack; + if(stats) { + src = (uint64*)&mp->gcstats; + dst = (uint64*)stats; + for(i=0; igcstats, sizeof(mp->gcstats)); + } + } + for(pp=runtime·allp; p=*pp; pp++) { + c = p->mcache; + if(c==nil) + continue; + runtime·purgecachedstats(c); for(i=0; ilocal_by_size); i++) { mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc; c->local_by_size[i].nmalloc = 0; @@ -875,16 +1721,42 @@ cachestats(void) } } mstats.stacks_inuse = stacks_inuse; - mstats.stacks_sys = stacks_sys; +} + +// Structure of arguments passed to function gc(). +// This allows the arguments to be passed via reflect·call. +struct gc_args +{ + int32 force; +}; + +static void gc(struct gc_args *args); + +static int32 +readgogc(void) +{ + byte *p; + + p = runtime·getenv("GOGC"); + if(p == nil || p[0] == '\0') + return 100; + if(runtime·strcmp(p, (byte*)"off") == 0) + return -1; + return runtime·atoi(p); } void runtime·gc(int32 force) { - int64 t0, t1, t2, t3; - uint64 heap0, heap1, obj0, obj1; byte *p; - bool extra; + struct gc_args a, *ap; + FuncVal gcv; + + // The atomic operations are not atomic if the uint64s + // are not aligned on uint64 boundaries. This has been + // a problem in the past. + if((((uintptr)&work.empty) & 7) != 0) + runtime·throw("runtime: gc work buffer is misaligned"); // The gc is turned off (via enablegc) until // the bootstrap has completed. @@ -897,14 +1769,8 @@ runtime·gc(int32 force) if(!mstats.enablegc || m->locks > 0 || runtime·panicking) return; - if(gcpercent == -2) { // first time through - p = runtime·getenv("GOGC"); - if(p == nil || p[0] == '\0') - gcpercent = 100; - else if(runtime·strcmp(p, (byte*)"off") == 0) - gcpercent = -1; - else - gcpercent = runtime·atoi(p); + if(gcpercent == GcpercentUnknown) { // first time through + gcpercent = readgogc(); p = runtime·getenv("GOGCTRACE"); if(p != nil) @@ -913,103 +1779,171 @@ runtime·gc(int32 force) if(gcpercent < 0) return; + // Run gc on a bigger stack to eliminate + // a potentially large number of calls to runtime·morestack. + a.force = force; + ap = &a; + m->moreframesize_minalloc = StackBig; + gcv.fn = (void*)gc; + reflect·call(&gcv, (byte*)&ap, sizeof(ap)); + + if(gctrace > 1 && !force) { + a.force = 1; + gc(&a); + } +} + +static FuncVal runfinqv = {runfinq}; + +static void +gc(struct gc_args *args) +{ + int64 t0, t1, t2, t3, t4; + uint64 heap0, heap1, obj0, obj1, ninstr; + GCStats stats; + M *mp; + uint32 i; + Eface eface; + runtime·semacquire(&runtime·worldsema); - if(!force && mstats.heap_alloc < mstats.next_gc) { + if(!args->force && mstats.heap_alloc < mstats.next_gc) { runtime·semrelease(&runtime·worldsema); return; } t0 = runtime·nanotime(); - nhandoff = 0; m->gcing = 1; runtime·stoptheworld(); - cachestats(); - heap0 = mstats.heap_alloc; - obj0 = mstats.nmalloc - mstats.nfree; + if(CollectStats) + runtime·memclr((byte*)&gcstats, sizeof(gcstats)); - runtime·lock(&work.markgate); - runtime·lock(&work.sweepgate); + for(mp=runtime·allm; mp; mp=mp->alllink) + runtime·settype_flush(mp, false); - extra = false; - work.nproc = 1; - if(runtime·gomaxprocs > 1 && runtime·ncpu > 1) { - runtime·noteclear(&work.alldone); - work.nproc += runtime·helpgc(&extra); + heap0 = 0; + obj0 = 0; + if(gctrace) { + cachestats(nil); + heap0 = mstats.heap_alloc; + obj0 = mstats.nmalloc - mstats.nfree; } + + m->locks++; // disable gc during mallocs in parforalloc + if(work.markfor == nil) + work.markfor = runtime·parforalloc(MaxGcproc); + if(work.sweepfor == nil) + work.sweepfor = runtime·parforalloc(MaxGcproc); + m->locks--; + + if(itabtype == nil) { + // get C pointer to the Go type "itab" + runtime·gc_itab_ptr(&eface); + itabtype = ((PtrType*)eface.type)->elem; + } + work.nwait = 0; work.ndone = 0; + work.debugmarkdone = 0; + work.nproc = runtime·gcprocs(); + addroots(); + runtime·parforsetup(work.markfor, work.nproc, work.nroot, nil, false, markroot); + runtime·parforsetup(work.sweepfor, work.nproc, runtime·mheap->nspan, nil, true, sweepspan); + if(work.nproc > 1) { + runtime·noteclear(&work.alldone); + runtime·helpgc(work.nproc); + } - runtime·unlock(&work.markgate); // let the helpers in - mark(scanblock); - if(DebugMark) - mark(debug_scanblock); t1 = runtime·nanotime(); - work.spans = runtime·mheap.allspans; - runtime·unlock(&work.sweepgate); // let the helpers in - sweep(); + runtime·parfordo(work.markfor); + scanblock(nil, nil, 0, true); + + if(DebugMark) { + for(i=0; i 1) runtime·notesleep(&work.alldone); - t2 = runtime·nanotime(); - stealcache(); - cachestats(); + cachestats(&stats); + + stats.nprocyield += work.sweepfor->nprocyield; + stats.nosyield += work.sweepfor->nosyield; + stats.nsleep += work.sweepfor->nsleep; mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*gcpercent/100; m->gcing = 0; - m->locks++; // disable gc during the mallocs in newproc if(finq != nil) { + m->locks++; // disable gc during the mallocs in newproc // kick off or wake up goroutine to run queued finalizers if(fing == nil) - fing = runtime·newproc1((byte*)runfinq, nil, 0, 0, runtime·gc); + fing = runtime·newproc1(&runfinqv, nil, 0, 0, runtime·gc); else if(fingwait) { fingwait = 0; runtime·ready(fing); } + m->locks--; } - m->locks--; - cachestats(); heap1 = mstats.heap_alloc; obj1 = mstats.nmalloc - mstats.nfree; - t3 = runtime·nanotime(); - mstats.last_gc = t3; - mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t3 - t0; - mstats.pause_total_ns += t3 - t0; + t4 = runtime·nanotime(); + mstats.last_gc = t4; + mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0; + mstats.pause_total_ns += t4 - t0; mstats.numgc++; if(mstats.debuggc) - runtime·printf("pause %D\n", t3-t0); + runtime·printf("pause %D\n", t4-t0); if(gctrace) { - runtime·printf("gc%d(%d): %D+%D+%D ms %D -> %D MB %D -> %D (%D-%D) objects %D handoff\n", - mstats.numgc, work.nproc, (t1-t0)/1000000, (t2-t1)/1000000, (t3-t2)/1000000, + runtime·printf("gc%d(%d): %D+%D+%D ms, %D -> %D MB %D -> %D (%D-%D) objects," + " %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n", + mstats.numgc, work.nproc, (t2-t1)/1000000, (t3-t2)/1000000, (t1-t0+t4-t3)/1000000, heap0>>20, heap1>>20, obj0, obj1, mstats.nmalloc, mstats.nfree, - nhandoff); + stats.nhandoff, stats.nhandoffcnt, + work.sweepfor->nsteal, work.sweepfor->nstealcnt, + stats.nprocyield, stats.nosyield, stats.nsleep); + if(CollectStats) { + runtime·printf("scan: %D bytes, %D objects, %D untyped, %D types from MSpan\n", + gcstats.nbytes, gcstats.obj.cnt, gcstats.obj.notype, gcstats.obj.typelookup); + if(gcstats.ptr.cnt != 0) + runtime·printf("avg ptrbufsize: %D (%D/%D)\n", + gcstats.ptr.sum/gcstats.ptr.cnt, gcstats.ptr.sum, gcstats.ptr.cnt); + if(gcstats.obj.cnt != 0) + runtime·printf("avg nobj: %D (%D/%D)\n", + gcstats.obj.sum/gcstats.obj.cnt, gcstats.obj.sum, gcstats.obj.cnt); + runtime·printf("rescans: %D, %D bytes\n", gcstats.rescan, gcstats.rescanbytes); + + runtime·printf("instruction counts:\n"); + ninstr = 0; + for(i=0; i 1 && !force) - runtime·gc(1); } void @@ -1022,11 +1956,56 @@ runtime·ReadMemStats(MStats *stats) runtime·semacquire(&runtime·worldsema); m->gcing = 1; runtime·stoptheworld(); - cachestats(); + cachestats(nil); *stats = mstats; m->gcing = 0; runtime·semrelease(&runtime·worldsema); - runtime·starttheworld(false); + runtime·starttheworld(); +} + +void +runtime∕debug·readGCStats(Slice *pauses) +{ + uint64 *p; + uint32 i, n; + + // Calling code in runtime/debug should make the slice large enough. + if(pauses->cap < nelem(mstats.pause_ns)+3) + runtime·throw("runtime: short slice passed to readGCStats"); + + // Pass back: pauses, last gc (absolute time), number of gc, total pause ns. + p = (uint64*)pauses->array; + runtime·lock(runtime·mheap); + n = mstats.numgc; + if(n > nelem(mstats.pause_ns)) + n = nelem(mstats.pause_ns); + + // The pause buffer is circular. The most recent pause is at + // pause_ns[(numgc-1)%nelem(pause_ns)], and then backward + // from there to go back farther in time. We deliver the times + // most recent first (in p[0]). + for(i=0; ilen = n+3; +} + +void +runtime∕debug·setGCPercent(intgo in, intgo out) +{ + runtime·lock(runtime·mheap); + if(gcpercent == GcpercentUnknown) + gcpercent = readgogc(); + out = gcpercent; + if(in < 0) + in = -1; + gcpercent = in; + runtime·unlock(runtime·mheap); + FLUSH(&out); } static void @@ -1050,11 +2029,11 @@ runfinq(void) finq = nil; if(fb == nil) { fingwait = 1; - g->status = Gwaiting; - g->waitreason = "finalizer wait"; - runtime·gosched(); + runtime·park(nil, nil, "finalizer wait"); continue; } + if(raceenabled) + runtime·racefingo(); for(; fb; fb=next) { next = fb->next; for(i=0; icnt; i++) { @@ -1066,7 +2045,7 @@ runfinq(void) framecap = framesz; } *(void**)frame = f->arg; - reflect·call((byte*)f->fn, frame, sizeof(uintptr) + f->nret); + reflect·call(f->fn, frame, sizeof(uintptr) + f->nret); f->fn = nil; f->arg = nil; } @@ -1088,11 +2067,11 @@ runtime·markallocated(void *v, uintptr n, bool noptr) if(0) runtime·printf("markallocated %p+%p\n", v, n); - if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start) runtime·throw("markallocated: bad pointer"); - off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset - b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset + b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; for(;;) { @@ -1120,11 +2099,11 @@ runtime·markfreed(void *v, uintptr n) if(0) runtime·printf("markallocated %p+%p\n", v, n); - if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start) runtime·throw("markallocated: bad pointer"); - off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset - b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset + b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; for(;;) { @@ -1150,11 +2129,11 @@ runtime·checkfreed(void *v, uintptr n) if(!runtime·checking) return; - if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start) return; // not allocated, so okay - off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset - b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset + b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; bits = *b>>shift; @@ -1173,7 +2152,7 @@ runtime·markspan(void *v, uintptr size, uintptr n, bool leftover) uintptr *b, off, shift; byte *p; - if((byte*)v+size*n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + if((byte*)v+size*n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start) runtime·throw("markspan: bad pointer"); p = v; @@ -1184,8 +2163,8 @@ runtime·markspan(void *v, uintptr size, uintptr n, bool leftover) // the entire span, and each bitmap word has bits for only // one span, so no other goroutines are changing these // bitmap words. - off = (uintptr*)p - (uintptr*)runtime·mheap.arena_start; // word offset - b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)p - (uintptr*)runtime·mheap->arena_start; // word offset + b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; *b = (*b & ~(bitMask< (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start) + if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start) runtime·throw("markspan: bad pointer"); p = v; - off = p - (uintptr*)runtime·mheap.arena_start; // word offset + off = p - (uintptr*)runtime·mheap->arena_start; // word offset if(off % wordsPerBitmapWord != 0) runtime·throw("markspan: unaligned pointer"); - b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; n /= PtrSize; if(n%wordsPerBitmapWord != 0) runtime·throw("unmarkspan: unaligned length"); @@ -1225,8 +2204,8 @@ runtime·blockspecial(void *v) if(DebugMark) return true; - off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; - b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; + b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; return (*b & (bitSpecial<arena_start; + b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; for(;;) { diff --git a/src/pkg/runtime/mgc0.go b/src/pkg/runtime/mgc0.go new file mode 100644 index 000000000..b15054662 --- /dev/null +++ b/src/pkg/runtime/mgc0.go @@ -0,0 +1,15 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +// Called from C. Returns the Go type *m. +func gc_m_ptr(ret *interface{}) { + *ret = (*m)(nil) +} + +// Called from C. Returns the Go type *itab. +func gc_itab_ptr(ret *interface{}) { + *ret = (*itab)(nil) +} diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h new file mode 100644 index 000000000..87b604a36 --- /dev/null +++ b/src/pkg/runtime/mgc0.h @@ -0,0 +1,43 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Garbage collector (GC) + +// GC instruction opcodes. +// +// The opcode of an instruction is followed by zero or more +// arguments to the instruction. +// +// Meaning of arguments: +// off Offset (in bytes) from the start of the current object +// objgc Pointer to GC info of an object +// objgcrel Offset to GC info of an object +// len Length of an array +// elemsize Size (in bytes) of an element +// size Size (in bytes) +enum { + GC_END, // End of object, loop or subroutine. Args: none + GC_PTR, // A typed pointer. Args: (off, objgc) + GC_APTR, // Pointer to an arbitrary object. Args: (off) + GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize) + GC_ARRAY_NEXT, // The next element of an array. Args: none + GC_CALL, // Call a subroutine. Args: (off, objgcrel) + GC_MAP_PTR, // Go map. Args: (off, MapType*) + GC_STRING, // Go string. Args: (off) + GC_EFACE, // interface{}. Args: (off) + GC_IFACE, // interface{...}. Args: (off) + GC_SLICE, // Go slice. Args: (off, objgc) + GC_REGION, // A region/part of the current object. Args: (off, size, objgc) + + GC_NUM_INSTR, // Number of instruction opcodes +}; + +enum { + // Size of GC's fixed stack. + // + // The current GC implementation permits: + // - at most 1 stack allocation because of GC_CALL + // - at most GC_STACK_CAPACITY allocations because of GC_ARRAY_START + GC_STACK_CAPACITY = 8, +}; diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c index c877bfca9..f45149d63 100644 --- a/src/pkg/runtime/mheap.c +++ b/src/pkg/runtime/mheap.c @@ -27,11 +27,26 @@ RecordSpan(void *vh, byte *p) { MHeap *h; MSpan *s; + MSpan **all; + uint32 cap; h = vh; s = (MSpan*)p; - s->allnext = h->allspans; - h->allspans = s; + if(h->nspan >= h->nspancap) { + cap = 64*1024/sizeof(all[0]); + if(cap < h->nspancap*3/2) + cap = h->nspancap*3/2; + all = (MSpan**)runtime·SysAlloc(cap*sizeof(all[0])); + if(all == nil) + runtime·throw("runtime: cannot allocate memory"); + if(h->allspans) { + runtime·memmove(all, h->allspans, h->nspancap*sizeof(all[0])); + runtime·SysFree(h->allspans, h->nspancap*sizeof(all[0])); + } + h->allspans = all; + h->nspancap = cap; + } + h->allspans[h->nspan++] = s; } // Initialize the heap; fetch memory using alloc. @@ -53,12 +68,12 @@ runtime·MHeap_Init(MHeap *h, void *(*alloc)(uintptr)) // Allocate a new span of npage pages from the heap // and record its size class in the HeapMap and HeapMapCache. MSpan* -runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct) +runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32 zeroed) { MSpan *s; runtime·lock(h); - runtime·purgecachedstats(m); + runtime·purgecachedstats(m->mcache); s = MHeap_AllocLocked(h, npage, sizeclass); if(s != nil) { mstats.heap_inuse += npage<start<start<npages<start<start<state = MSpanInUse; MHeap_FreeLocked(h, t); + t->unusedsince = s->unusedsince; // preserve age } - - if(*(uintptr*)(s->start<start<npages<unusedsince = 0; // Record span info, because gc needs to be // able to map interior pointer to containing span. s->sizeclass = sizeclass; + s->elemsize = (sizeclass==0 ? s->npages<types.compression = MTypes_Empty; p = s->start; if(sizeof(void*) == 8) p -= ((uintptr)h->arena_start>>PageShift); @@ -259,7 +277,7 @@ void runtime·MHeap_Free(MHeap *h, MSpan *s, int32 acct) { runtime·lock(h); - runtime·purgecachedstats(m); + runtime·purgecachedstats(m->mcache); mstats.heap_inuse -= s->npages<npages<types.sysalloc) + runtime·settype_sysfree(s); + s->types.compression = MTypes_Empty; + if(s->state != MSpanInUse || s->ref != 0) { runtime·printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d\n", s, s->start<state, s->ref); runtime·throw("MHeap_FreeLocked - invalid free"); } mstats.heap_idle += s->npages<state = MSpanFree; - s->unusedsince = 0; - s->npreleased = 0; runtime·MSpanList_Remove(s); sp = (uintptr*)(s->start<unusedsince = runtime·nanotime(); + s->npreleased = 0; // Coalesce with earlier, later spans. p = s->start; @@ -325,6 +349,52 @@ MHeap_FreeLocked(MHeap *h, MSpan *s) runtime·MSpanList_Insert(&h->large, s); } +static void +forcegchelper(Note *note) +{ + runtime·gc(1); + runtime·notewakeup(note); +} + +static uintptr +scavengelist(MSpan *list, uint64 now, uint64 limit) +{ + uintptr released, sumreleased; + MSpan *s; + + if(runtime·MSpanList_IsEmpty(list)) + return 0; + + sumreleased = 0; + for(s=list->next; s != list; s=s->next) { + if((now - s->unusedsince) > limit) { + released = (s->npages - s->npreleased) << PageShift; + mstats.heap_released += released; + sumreleased += released; + s->npreleased = s->npages; + runtime·SysUnused((void*)(s->start << PageShift), s->npages << PageShift); + } + } + return sumreleased; +} + +static uintptr +scavenge(uint64 now, uint64 limit) +{ + uint32 i; + uintptr sumreleased; + MHeap *h; + + h = runtime·mheap; + sumreleased = 0; + for(i=0; i < nelem(h->free); i++) + sumreleased += scavengelist(&h->free[i], now, limit); + sumreleased += scavengelist(&h->large, now, limit); + return sumreleased; +} + +static FuncVal forcegchelperv = {(void(*)(void))forcegchelper}; + // Release (part of) unused memory to OS. // Goroutine created at startup. // Loop forever. @@ -332,13 +402,12 @@ void runtime·MHeap_Scavenger(void) { MHeap *h; - MSpan *s, *list; uint64 tick, now, forcegc, limit; - uint32 k, i; - uintptr released, sumreleased; + uint32 k; + uintptr sumreleased; byte *env; bool trace; - Note note; + Note note, *notep; // If we go two minutes without a garbage collection, force one to run. forcegc = 2*60*1e9; @@ -356,10 +425,10 @@ runtime·MHeap_Scavenger(void) if(env != nil) trace = runtime·atoi(env) > 0; - h = &runtime·mheap; + h = runtime·mheap; for(k=0;; k++) { runtime·noteclear(¬e); - runtime·entersyscall(); + runtime·entersyscallblock(); runtime·notetsleep(¬e, tick); runtime·exitsyscall(); @@ -367,30 +436,21 @@ runtime·MHeap_Scavenger(void) now = runtime·nanotime(); if(now - mstats.last_gc > forcegc) { runtime·unlock(h); - runtime·gc(1); + // The scavenger can not block other goroutines, + // otherwise deadlock detector can fire spuriously. + // GC blocks other goroutines via the runtime·worldsema. + runtime·noteclear(¬e); + notep = ¬e; + runtime·newproc1(&forcegchelperv, (byte*)¬ep, sizeof(notep), 0, runtime·MHeap_Scavenger); + runtime·entersyscallblock(); + runtime·notesleep(¬e); + runtime·exitsyscall(); + if(trace) + runtime·printf("scvg%d: GC forced\n", k); runtime·lock(h); now = runtime·nanotime(); - if (trace) - runtime·printf("scvg%d: GC forced\n", k); - } - sumreleased = 0; - for(i=0; i < nelem(h->free)+1; i++) { - if(i < nelem(h->free)) - list = &h->free[i]; - else - list = &h->large; - if(runtime·MSpanList_IsEmpty(list)) - continue; - for(s=list->next; s != list; s=s->next) { - if(s->unusedsince != 0 && (now - s->unusedsince) > limit) { - released = (s->npages - s->npreleased) << PageShift; - mstats.heap_released += released; - sumreleased += released; - s->npreleased = s->npages; - runtime·SysUnused((void*)(s->start << PageShift), s->npages << PageShift); - } - } } + sumreleased = scavenge(now, limit); runtime·unlock(h); if(trace) { @@ -403,6 +463,15 @@ runtime·MHeap_Scavenger(void) } } +void +runtime∕debug·freeOSMemory(void) +{ + runtime·gc(1); + runtime·lock(runtime·mheap); + scavenge(~(uintptr)0, 0); + runtime·unlock(runtime·mheap); +} + // Initialize a new span with the given start and npages. void runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages) @@ -414,9 +483,11 @@ runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages) span->freelist = nil; span->ref = 0; span->sizeclass = 0; + span->elemsize = 0; span->state = 0; span->unusedsince = 0; span->npreleased = 0; + span->types.compression = MTypes_Empty; } // Initialize an empty doubly-linked list. diff --git a/src/pkg/runtime/mprof.goc b/src/pkg/runtime/mprof.goc index 0bbce8583..ebc1e3e66 100644 --- a/src/pkg/runtime/mprof.goc +++ b/src/pkg/runtime/mprof.goc @@ -13,23 +13,73 @@ package runtime #include "type.h" // NOTE(rsc): Everything here could use cas if contention became an issue. -static Lock proflock; +static Lock proflock, alloclock; -// Per-call-stack allocation information. +// All memory allocations are local and do not escape outside of the profiler. +// The profiler is forbidden from referring to garbage-collected memory. + +static byte *pool; // memory allocation pool +static uintptr poolfree; // number of bytes left in the pool +enum { + Chunk = 32*PageSize, // initial size of the pool +}; + +// Memory allocation local to this file. +// There is no way to return the allocated memory back to the OS. +static void* +allocate(uintptr size) +{ + void *v; + + if(size == 0) + return nil; + + if(size >= Chunk/2) + return runtime·SysAlloc(size); + + runtime·lock(&alloclock); + if(size > poolfree) { + pool = runtime·SysAlloc(Chunk); + if(pool == nil) + runtime·throw("runtime: cannot allocate memory"); + poolfree = Chunk; + } + v = pool; + pool += size; + poolfree -= size; + runtime·unlock(&alloclock); + return v; +} + +enum { MProf, BProf }; // profile types + +// Per-call-stack profiling information. // Lookup by hashing call stack into a linked-list hash table. typedef struct Bucket Bucket; struct Bucket { Bucket *next; // next in hash list - Bucket *allnext; // next in list of all buckets - uintptr allocs; - uintptr frees; - uintptr alloc_bytes; - uintptr free_bytes; - uintptr recent_allocs; // since last gc - uintptr recent_frees; - uintptr recent_alloc_bytes; - uintptr recent_free_bytes; + Bucket *allnext; // next in list of all mbuckets/bbuckets + int32 typ; + union + { + struct // typ == MProf + { + uintptr allocs; + uintptr frees; + uintptr alloc_bytes; + uintptr free_bytes; + uintptr recent_allocs; // since last gc + uintptr recent_frees; + uintptr recent_alloc_bytes; + uintptr recent_free_bytes; + }; + struct // typ == BProf + { + int64 count; + int64 cycles; + }; + }; uintptr hash; uintptr nstk; uintptr stk[1]; @@ -38,12 +88,13 @@ enum { BuckHashSize = 179999, }; static Bucket **buckhash; -static Bucket *buckets; +static Bucket *mbuckets; // memory profile buckets +static Bucket *bbuckets; // blocking profile buckets static uintptr bucketmem; // Return the bucket for stk[0:nstk], allocating new bucket if needed. static Bucket* -stkbucket(uintptr *stk, int32 nstk, bool alloc) +stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc) { int32 i; uintptr h; @@ -51,6 +102,8 @@ stkbucket(uintptr *stk, int32 nstk, bool alloc) if(buckhash == nil) { buckhash = runtime·SysAlloc(BuckHashSize*sizeof buckhash[0]); + if(buckhash == nil) + runtime·throw("runtime: cannot allocate memory"); mstats.buckhash_sys += BuckHashSize*sizeof buckhash[0]; } @@ -66,33 +119,39 @@ stkbucket(uintptr *stk, int32 nstk, bool alloc) i = h%BuckHashSize; for(b = buckhash[i]; b; b=b->next) - if(b->hash == h && b->nstk == nstk && + if(b->typ == typ && b->hash == h && b->nstk == nstk && runtime·mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0) return b; if(!alloc) return nil; - b = runtime·mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0, 1); + b = allocate(sizeof *b + nstk*sizeof stk[0]); + if(b == nil) + runtime·throw("runtime: cannot allocate memory"); bucketmem += sizeof *b + nstk*sizeof stk[0]; runtime·memmove(b->stk, stk, nstk*sizeof stk[0]); + b->typ = typ; b->hash = h; b->nstk = nstk; b->next = buckhash[i]; buckhash[i] = b; - b->allnext = buckets; - buckets = b; + if(typ == MProf) { + b->allnext = mbuckets; + mbuckets = b; + } else { + b->allnext = bbuckets; + bbuckets = b; + } return b; } -// Record that a gc just happened: all the 'recent' statistics are now real. -void -runtime·MProf_GC(void) +static void +MProf_GC(void) { Bucket *b; - - runtime·lock(&proflock); - for(b=buckets; b; b=b->allnext) { + + for(b=mbuckets; b; b=b->allnext) { b->allocs += b->recent_allocs; b->frees += b->recent_frees; b->alloc_bytes += b->recent_alloc_bytes; @@ -102,25 +161,39 @@ runtime·MProf_GC(void) b->recent_alloc_bytes = 0; b->recent_free_bytes = 0; } +} + +// Record that a gc just happened: all the 'recent' statistics are now real. +void +runtime·MProf_GC(void) +{ + runtime·lock(&proflock); + MProf_GC(); runtime·unlock(&proflock); } // Map from pointer to Bucket* that allocated it. // Three levels: -// Linked-list hash table for top N-20 bits. -// Array index for next 13 bits. -// Linked list for next 7 bits. +// Linked-list hash table for top N-AddrHashShift bits. +// Array index for next AddrDenseBits bits. +// Linked list for next AddrHashShift-AddrDenseBits bits. // This is more efficient than using a general map, // because of the typical clustering of the pointer keys. typedef struct AddrHash AddrHash; typedef struct AddrEntry AddrEntry; +enum { + AddrHashBits = 12, // good for 4GB of used address space + AddrHashShift = 20, // each AddrHash knows about 1MB of address space + AddrDenseBits = 8, // good for a profiling rate of 4096 bytes +}; + struct AddrHash { AddrHash *next; // next in top-level hash table linked list uintptr addr; // addr>>20 - AddrEntry *dense[1<<13]; + AddrEntry *dense[1<>20)*HashMultiplier) >> (32-AddrHashBits); + h = (uint32)((addr>>AddrHashShift)*HashMultiplier) >> (32-AddrHashBits); for(ah=addrhash[h]; ah; ah=ah->next) - if(ah->addr == (addr>>20)) + if(ah->addr == (addr>>AddrHashShift)) goto found; - ah = runtime·mallocgc(sizeof *ah, FlagNoProfiling, 0, 1); + ah = allocate(sizeof *ah); addrmem += sizeof *ah; ah->next = addrhash[h]; - ah->addr = addr>>20; + ah->addr = addr>>AddrHashShift; addrhash[h] = ah; found: if((e = addrfree) == nil) { - e = runtime·mallocgc(64*sizeof *e, FlagNoProfiling, 0, 0); + e = allocate(64*sizeof *e); addrmem += 64*sizeof *e; for(i=0; i+1<64; i++) e[i].next = &e[i+1]; e[63].next = nil; } addrfree = e->next; - e->addr = (uint32)~(addr & ((1<<20)-1)); + e->addr = (uint32)~(addr & ((1<b = b; - h = (addr>>7)&(nelem(ah->dense)-1); // entry in dense is top 13 bits of low 20. + h = (addr>>(AddrHashShift-AddrDenseBits))&(nelem(ah->dense)-1); // entry in dense is top 8 bits of low 20. e->next = ah->dense[h]; ah->dense[h] = e; } @@ -191,16 +261,16 @@ getaddrbucket(uintptr addr) AddrEntry *e, **l; Bucket *b; - h = (uint32)((addr>>20)*HashMultiplier) >> (32-AddrHashBits); + h = (uint32)((addr>>AddrHashShift)*HashMultiplier) >> (32-AddrHashBits); for(ah=addrhash[h]; ah; ah=ah->next) - if(ah->addr == (addr>>20)) + if(ah->addr == (addr>>AddrHashShift)) goto found; return nil; found: - h = (addr>>7)&(nelem(ah->dense)-1); // entry in dense is top 13 bits of low 20. + h = (addr>>(AddrHashShift-AddrDenseBits))&(nelem(ah->dense)-1); // entry in dense is top 8 bits of low 20. for(l=&ah->dense[h]; (e=*l) != nil; l=&e->next) { - if(e->addr == (uint32)~(addr & ((1<<20)-1))) { + if(e->addr == (uint32)~(addr & ((1<next; b = e->b; e->next = addrfree; @@ -225,7 +295,7 @@ runtime·MProf_Malloc(void *p, uintptr size) m->nomemprof++; nstk = runtime·callers(1, stk, 32); runtime·lock(&proflock); - b = stkbucket(stk, nstk, true); + b = stkbucket(MProf, stk, nstk, true); b->recent_allocs++; b->recent_alloc_bytes += size; setaddrbucket((uintptr)p, b); @@ -253,9 +323,37 @@ runtime·MProf_Free(void *p, uintptr size) m->nomemprof--; } +int64 runtime·blockprofilerate; // in CPU ticks + +void +runtime·SetBlockProfileRate(intgo rate) +{ + runtime·atomicstore64((uint64*)&runtime·blockprofilerate, rate * runtime·tickspersecond() / (1000*1000*1000)); +} + +void +runtime·blockevent(int64 cycles, int32 skip) +{ + int32 nstk; + int64 rate; + uintptr stk[32]; + Bucket *b; -// Go interface to profile data. (Declared in extern.go) -// Assumes Go sizeof(int) == sizeof(int32) + if(cycles <= 0) + return; + rate = runtime·atomicload64((uint64*)&runtime·blockprofilerate); + if(rate <= 0 || (rate > cycles && runtime·fastrand1()%rate > cycles)) + return; + + nstk = runtime·callers(skip, stk, 32); + runtime·lock(&proflock); + b = stkbucket(BProf, stk, nstk, true); + b->count++; + b->cycles += cycles; + runtime·unlock(&proflock); +} + +// Go interface to profile data. (Declared in debug.go) // Must match MemProfileRecord in debug.go. typedef struct Record Record; @@ -281,52 +379,101 @@ record(Record *r, Bucket *b) r->stk[i] = 0; } -func MemProfile(p Slice, include_inuse_zero bool) (n int32, ok bool) { +func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) { Bucket *b; Record *r; + bool clear; runtime·lock(&proflock); n = 0; - for(b=buckets; b; b=b->allnext) + clear = true; + for(b=mbuckets; b; b=b->allnext) { if(include_inuse_zero || b->alloc_bytes != b->free_bytes) n++; + if(b->allocs != 0 || b->frees != 0) + clear = false; + } + if(clear) { + // Absolutely no data, suggesting that a garbage collection + // has not yet happened. In order to allow profiling when + // garbage collection is disabled from the beginning of execution, + // accumulate stats as if a GC just happened, and recount buckets. + MProf_GC(); + n = 0; + for(b=mbuckets; b; b=b->allnext) + if(include_inuse_zero || b->alloc_bytes != b->free_bytes) + n++; + } ok = false; if(n <= p.len) { ok = true; r = (Record*)p.array; - for(b=buckets; b; b=b->allnext) + for(b=mbuckets; b; b=b->allnext) if(include_inuse_zero || b->alloc_bytes != b->free_bytes) record(r++, b); } runtime·unlock(&proflock); } +// Must match BlockProfileRecord in debug.go. +typedef struct BRecord BRecord; +struct BRecord { + int64 count; + int64 cycles; + uintptr stk[32]; +}; + +func BlockProfile(p Slice) (n int, ok bool) { + Bucket *b; + BRecord *r; + int32 i; + + runtime·lock(&proflock); + n = 0; + for(b=bbuckets; b; b=b->allnext) + n++; + ok = false; + if(n <= p.len) { + ok = true; + r = (BRecord*)p.array; + for(b=bbuckets; b; b=b->allnext, r++) { + r->count = b->count; + r->cycles = b->cycles; + for(i=0; instk && istk); i++) + r->stk[i] = b->stk[i]; + for(; istk); i++) + r->stk[i] = 0; + } + } + runtime·unlock(&proflock); +} + // Must match StackRecord in debug.go. typedef struct TRecord TRecord; struct TRecord { uintptr stk[32]; }; -func ThreadCreateProfile(p Slice) (n int32, ok bool) { +func ThreadCreateProfile(p Slice) (n int, ok bool) { TRecord *r; - M *first, *m; + M *first, *mp; first = runtime·atomicloadp(&runtime·allm); n = 0; - for(m=first; m; m=m->alllink) + for(mp=first; mp; mp=mp->alllink) n++; ok = false; if(n <= p.len) { ok = true; r = (TRecord*)p.array; - for(m=first; m; m=m->alllink) { - runtime·memmove(r->stk, m->createstack, sizeof r->stk); + for(mp=first; mp; mp=mp->alllink) { + runtime·memmove(r->stk, mp->createstack, sizeof r->stk); r++; } } } -func Stack(b Slice, all bool) (n int32) { +func Stack(b Slice, all bool) (n int) { byte *pc, *sp; sp = runtime·getcallersp(&b); @@ -355,21 +502,21 @@ func Stack(b Slice, all bool) (n int32) { if(all) { m->gcing = 0; runtime·semrelease(&runtime·worldsema); - runtime·starttheworld(false); + runtime·starttheworld(); } } static void -saveg(byte *pc, byte *sp, G *g, TRecord *r) +saveg(byte *pc, byte *sp, G *gp, TRecord *r) { int32 n; - n = runtime·gentraceback(pc, sp, 0, g, 0, r->stk, nelem(r->stk)); + n = runtime·gentraceback(pc, sp, 0, gp, 0, r->stk, nelem(r->stk)); if(n < nelem(r->stk)) r->stk[n] = 0; } -func GoroutineProfile(b Slice) (n int32, ok bool) { +func GoroutineProfile(b Slice) (n int, ok bool) { byte *pc, *sp; TRecord *r; G *gp; @@ -392,13 +539,18 @@ func GoroutineProfile(b Slice) (n int32, ok bool) { for(gp = runtime·allg; gp != nil; gp = gp->alllink) { if(gp == g || gp->status == Gdead) continue; - saveg(gp->sched.pc, gp->sched.sp, gp, r++); + saveg(gp->sched.pc, (byte*)gp->sched.sp, gp, r++); } } m->gcing = 0; runtime·semrelease(&runtime·worldsema); - runtime·starttheworld(false); + runtime·starttheworld(); } } +void +runtime·mprofinit(void) +{ + addrhash = allocate((1< +#define _UC_SIGMASK 0x01 +#define _UC_CPU 0x04 diff --git a/src/pkg/runtime/os_openbsd.h b/src/pkg/runtime/os_openbsd.h index 4ecf78d88..a599aad05 100644 --- a/src/pkg/runtime/os_openbsd.h +++ b/src/pkg/runtime/os_openbsd.h @@ -4,18 +4,25 @@ #define SIG_DFL ((void*)0) #define SIG_IGN ((void*)1) +#define SIGHUP 1 +#define SS_DISABLE 4 + +#define SIG_BLOCK 1 +#define SIG_UNBLOCK 2 +#define SIG_SETMASK 3 struct sigaction; +void runtime·raisesigpipe(void); +void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); void runtime·sigpanic(void); -void runtime·sigaltstack(Sigaltstack*, Sigaltstack*); + +void runtime·setitimer(int32, Itimerval*, Itimerval*); void runtime·sigaction(int32, struct sigaction*, struct sigaction*); -void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); +void runtime·sigaltstack(Sigaltstack*, Sigaltstack*); void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); -void runtime·setitimer(int32, Itimerval*, Itimerval*); +Sigset runtime·sigprocmask(int32, Sigset); int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr); -void runtime·raisesigpipe(void); - #define NSIG 33 #define SI_USER 0 diff --git a/src/pkg/runtime/os_plan9.h b/src/pkg/runtime/os_plan9.h index cc6343c8e..c2cdf5b44 100644 --- a/src/pkg/runtime/os_plan9.h +++ b/src/pkg/runtime/os_plan9.h @@ -7,13 +7,22 @@ int32 runtime·open(uint8 *file, int32 mode); int32 runtime·pread(int32 fd, void *buf, int32 nbytes, int64 offset); int32 runtime·pwrite(int32 fd, void *buf, int32 nbytes, int64 offset); int32 runtime·read(int32 fd, void *buf, int32 nbytes); +int64 runtime·seek(int32 fd, int64 offset, int32 whence); int32 runtime·close(int32 fd); void runtime·exits(int8* msg); -int32 runtime·brk_(void*); +intptr runtime·brk_(void*); int32 runtime·sleep(int32 ms); -int32 runtime·rfork(int32 flags, void *stk, M *m, G *g, void (*fn)(void)); +int32 runtime·rfork(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void)); int32 runtime·plan9_semacquire(uint32 *addr, int32 block); +int32 runtime·plan9_tsemacquire(uint32 *addr, int32 ms); int32 runtime·plan9_semrelease(uint32 *addr, int32 count); +int32 runtime·notify(void (*fn)(void*, int8*)); +int32 runtime·noted(int32); +void runtime·sigtramp(void*, int8*); +int32 runtime·sighandler(void*, int8*, G*); +void runtime·sigpanic(void); +void runtime·goexitsall(int8*); +void runtime·setfpmasks(void); /* open */ enum @@ -45,6 +54,13 @@ enum RFNOMNT = (1<<14) }; +/* notify */ +enum +{ + NCONT = 0, + NDFLT = 1 +}; + typedef struct Tos Tos; typedef intptr Plink; @@ -66,4 +82,5 @@ struct Tos { /* top of stack is here */ }; -#define NSIG 1 +#define NSIG 5 /* number of signals in runtime·SigTab array */ +#define ERRMAX 128 /* max length of note string */ diff --git a/src/pkg/runtime/os_windows.h b/src/pkg/runtime/os_windows.h index 9d387b7ad..cf0ecb68e 100644 --- a/src/pkg/runtime/os_windows.h +++ b/src/pkg/runtime/os_windows.h @@ -28,5 +28,8 @@ uint32 runtime·ctrlhandler(uint32 type); byte *runtime·compilecallback(Eface fn, bool cleanstack); void *runtime·callbackasm(void); +void runtime·install_exception_handler(void); +void runtime·remove_exception_handler(void); + // TODO(brainman): should not need those #define NSIG 65 diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c new file mode 100644 index 000000000..2f553f417 --- /dev/null +++ b/src/pkg/runtime/panic.c @@ -0,0 +1,487 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "arch_GOARCH.h" +#include "stack.h" + +// Code related to defer, panic and recover. + +uint32 runtime·panicking; +static Lock paniclk; + +enum +{ + DeferChunkSize = 2048 +}; + +// Allocate a Defer, usually as part of the larger frame of deferred functions. +// Each defer must be released with both popdefer and freedefer. +static Defer* +newdefer(int32 siz) +{ + int32 total; + DeferChunk *c; + Defer *d; + + c = g->dchunk; + total = sizeof(*d) + ROUND(siz, sizeof(uintptr)) - sizeof(d->args); + if(c == nil || total > DeferChunkSize - c->off) { + if(total > DeferChunkSize / 2) { + // Not worth putting in any chunk. + // Allocate a separate block. + d = runtime·malloc(total); + d->siz = siz; + d->special = 1; + d->free = 1; + d->link = g->defer; + g->defer = d; + return d; + } + + // Cannot fit in current chunk. + // Switch to next chunk, allocating if necessary. + c = g->dchunknext; + if(c == nil) + c = runtime·malloc(DeferChunkSize); + c->prev = g->dchunk; + c->off = sizeof(*c); + g->dchunk = c; + g->dchunknext = nil; + } + + d = (Defer*)((byte*)c + c->off); + c->off += total; + d->siz = siz; + d->special = 0; + d->free = 0; + d->link = g->defer; + g->defer = d; + return d; +} + +// Pop the current defer from the defer stack. +// Its contents are still valid until the goroutine begins executing again. +// In particular it is safe to call reflect.call(d->fn, d->argp, d->siz) after +// popdefer returns. +static void +popdefer(void) +{ + Defer *d; + DeferChunk *c; + int32 total; + + d = g->defer; + if(d == nil) + runtime·throw("runtime: popdefer nil"); + g->defer = d->link; + if(d->special) { + // Nothing else to do. + return; + } + total = sizeof(*d) + ROUND(d->siz, sizeof(uintptr)) - sizeof(d->args); + c = g->dchunk; + if(c == nil || (byte*)d+total != (byte*)c+c->off) + runtime·throw("runtime: popdefer phase error"); + c->off -= total; + if(c->off == sizeof(*c)) { + // Chunk now empty, so pop from stack. + // Save in dchunknext both to help with pingponging between frames + // and to make sure d is still valid on return. + if(g->dchunknext != nil) + runtime·free(g->dchunknext); + g->dchunknext = c; + g->dchunk = c->prev; + } +} + +// Free the given defer. +// For defers in the per-goroutine chunk this just clears the saved arguments. +// For large defers allocated on the heap, this frees them. +// The defer cannot be used after this call. +static void +freedefer(Defer *d) +{ + if(d->special) { + if(d->free) + runtime·free(d); + } else { + runtime·memclr((byte*)d->args, d->siz); + } +} + +// Create a new deferred function fn with siz bytes of arguments. +// The compiler turns a defer statement into a call to this. +// Cannot split the stack because it assumes that the arguments +// are available sequentially after &fn; they would not be +// copied if a stack split occurred. It's OK for this to call +// functions that split the stack. +#pragma textflag 7 +uintptr +runtime·deferproc(int32 siz, FuncVal *fn, ...) +{ + Defer *d; + + d = newdefer(siz); + d->fn = fn; + d->pc = runtime·getcallerpc(&siz); + if(thechar == '5') + d->argp = (byte*)(&fn+2); // skip caller's saved link register + else + d->argp = (byte*)(&fn+1); + runtime·memmove(d->args, d->argp, d->siz); + + // deferproc returns 0 normally. + // a deferred func that stops a panic + // makes the deferproc return 1. + // the code the compiler generates always + // checks the return value and jumps to the + // end of the function if deferproc returns != 0. + return 0; +} + +// Run a deferred function if there is one. +// The compiler inserts a call to this at the end of any +// function which calls defer. +// If there is a deferred function, this will call runtime·jmpdefer, +// which will jump to the deferred function such that it appears +// to have been called by the caller of deferreturn at the point +// just before deferreturn was called. The effect is that deferreturn +// is called again and again until there are no more deferred functions. +// Cannot split the stack because we reuse the caller's frame to +// call the deferred function. +#pragma textflag 7 +void +runtime·deferreturn(uintptr arg0) +{ + Defer *d; + byte *argp; + FuncVal *fn; + + d = g->defer; + if(d == nil) + return; + argp = (byte*)&arg0; + if(d->argp != argp) + return; + runtime·memmove(argp, d->args, d->siz); + fn = d->fn; + popdefer(); + freedefer(d); + runtime·jmpdefer(fn, argp); +} + +// Run all deferred functions for the current goroutine. +static void +rundefer(void) +{ + Defer *d; + + while((d = g->defer) != nil) { + popdefer(); + reflect·call(d->fn, (byte*)d->args, d->siz); + freedefer(d); + } +} + +// Print all currently active panics. Used when crashing. +static void +printpanics(Panic *p) +{ + if(p->link) { + printpanics(p->link); + runtime·printf("\t"); + } + runtime·printf("panic: "); + runtime·printany(p->arg); + if(p->recovered) + runtime·printf(" [recovered]"); + runtime·printf("\n"); +} + +static void recovery(G*); + +// The implementation of the predeclared function panic. +void +runtime·panic(Eface e) +{ + Defer *d; + Panic *p; + void *pc, *argp; + + p = runtime·mal(sizeof *p); + p->arg = e; + p->link = g->panic; + p->stackbase = (byte*)g->stackbase; + g->panic = p; + + for(;;) { + d = g->defer; + if(d == nil) + break; + // take defer off list in case of recursive panic + popdefer(); + g->ispanic = true; // rock for newstack, where reflect.call ends up + argp = d->argp; + pc = d->pc; + reflect·call(d->fn, (byte*)d->args, d->siz); + freedefer(d); + if(p->recovered) { + g->panic = p->link; + if(g->panic == nil) // must be done with signal + g->sig = 0; + runtime·free(p); + // Pass information about recovering frame to recovery. + g->sigcode0 = (uintptr)argp; + g->sigcode1 = (uintptr)pc; + runtime·mcall(recovery); + runtime·throw("recovery failed"); // mcall should not return + } + } + + // ran out of deferred calls - old-school panic now + runtime·startpanic(); + printpanics(g->panic); + runtime·dopanic(0); +} + +// Unwind the stack after a deferred function calls recover +// after a panic. Then arrange to continue running as though +// the caller of the deferred function returned normally. +static void +recovery(G *gp) +{ + void *argp; + void *pc; + + // Info about defer passed in G struct. + argp = (void*)gp->sigcode0; + pc = (void*)gp->sigcode1; + + // Unwind to the stack frame with d's arguments in it. + runtime·unwindstack(gp, argp); + + // Make the deferproc for this d return again, + // this time returning 1. The calling function will + // jump to the standard return epilogue. + // The -2*sizeof(uintptr) makes up for the + // two extra words that are on the stack at + // each call to deferproc. + // (The pc we're returning to does pop pop + // before it tests the return value.) + // On the arm there are 2 saved LRs mixed in too. + if(thechar == '5') + gp->sched.sp = (uintptr)argp - 4*sizeof(uintptr); + else + gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr); + gp->sched.pc = pc; + runtime·gogo(&gp->sched, 1); +} + +// Free stack frames until we hit the last one +// or until we find the one that contains the sp. +void +runtime·unwindstack(G *gp, byte *sp) +{ + Stktop *top; + byte *stk; + + // Must be called from a different goroutine, usually m->g0. + if(g == gp) + runtime·throw("unwindstack on self"); + + while((top = (Stktop*)gp->stackbase) != nil && top->stackbase != nil) { + stk = (byte*)gp->stackguard - StackGuard; + if(stk <= sp && sp < (byte*)gp->stackbase) + break; + gp->stackbase = (uintptr)top->stackbase; + gp->stackguard = (uintptr)top->stackguard; + if(top->free != 0) + runtime·stackfree(stk, top->free); + } + + if(sp != nil && (sp < (byte*)gp->stackguard - StackGuard || (byte*)gp->stackbase < sp)) { + runtime·printf("recover: %p not in [%p, %p]\n", sp, gp->stackguard - StackGuard, gp->stackbase); + runtime·throw("bad unwindstack"); + } +} + +// The implementation of the predeclared function recover. +// Cannot split the stack because it needs to reliably +// find the stack segment of its caller. +#pragma textflag 7 +void +runtime·recover(byte *argp, Eface ret) +{ + Stktop *top, *oldtop; + Panic *p; + + // Must be a panic going on. + if((p = g->panic) == nil || p->recovered) + goto nomatch; + + // Frame must be at the top of the stack segment, + // because each deferred call starts a new stack + // segment as a side effect of using reflect.call. + // (There has to be some way to remember the + // variable argument frame size, and the segment + // code already takes care of that for us, so we + // reuse it.) + // + // As usual closures complicate things: the fp that + // the closure implementation function claims to have + // is where the explicit arguments start, after the + // implicit pointer arguments and PC slot. + // If we're on the first new segment for a closure, + // then fp == top - top->args is correct, but if + // the closure has its own big argument frame and + // allocated a second segment (see below), + // the fp is slightly above top - top->args. + // That condition can't happen normally though + // (stack pointers go down, not up), so we can accept + // any fp between top and top - top->args as + // indicating the top of the segment. + top = (Stktop*)g->stackbase; + if(argp < (byte*)top - top->argsize || (byte*)top < argp) + goto nomatch; + + // The deferred call makes a new segment big enough + // for the argument frame but not necessarily big + // enough for the function's local frame (size unknown + // at the time of the call), so the function might have + // made its own segment immediately. If that's the + // case, back top up to the older one, the one that + // reflect.call would have made for the panic. + // + // The fp comparison here checks that the argument + // frame that was copied during the split (the top->args + // bytes above top->fp) abuts the old top of stack. + // This is a correct test for both closure and non-closure code. + oldtop = (Stktop*)top->stackbase; + if(oldtop != nil && top->argp == (byte*)oldtop - top->argsize) + top = oldtop; + + // Now we have the segment that was created to + // run this call. It must have been marked as a panic segment. + if(!top->panic) + goto nomatch; + + // Okay, this is the top frame of a deferred call + // in response to a panic. It can see the panic argument. + p->recovered = 1; + ret = p->arg; + FLUSH(&ret); + return; + +nomatch: + ret.type = nil; + ret.data = nil; + FLUSH(&ret); +} + +void +runtime·startpanic(void) +{ + if(m->mcache == nil) // can happen if called from signal handler or throw + m->mcache = runtime·allocmcache(); + if(m->dying) { + runtime·printf("panic during panic\n"); + runtime·exit(3); + } + m->dying = 1; + runtime·xadd(&runtime·panicking, 1); + runtime·lock(&paniclk); +} + +void +runtime·dopanic(int32 unused) +{ + static bool didothers; + + if(g->sig != 0) + runtime·printf("[signal %x code=%p addr=%p pc=%p]\n", + g->sig, g->sigcode0, g->sigcode1, g->sigpc); + + if(runtime·gotraceback()){ + if(g != m->g0) { + runtime·printf("\n"); + runtime·goroutineheader(g); + runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g); + } + if(!didothers) { + didothers = true; + runtime·tracebackothers(g); + } + } + runtime·unlock(&paniclk); + if(runtime·xadd(&runtime·panicking, -1) != 0) { + // Some other m is panicking too. + // Let it print what it needs to print. + // Wait forever without chewing up cpu. + // It will exit when it's done. + static Lock deadlock; + runtime·lock(&deadlock); + runtime·lock(&deadlock); + } + + runtime·exit(2); +} + +void +runtime·panicindex(void) +{ + runtime·panicstring("index out of range"); +} + +void +runtime·panicslice(void) +{ + runtime·panicstring("slice bounds out of range"); +} + +void +runtime·throwreturn(void) +{ + // can only happen if compiler is broken + runtime·throw("no return at end of a typed function - compiler is broken"); +} + +void +runtime·throwinit(void) +{ + // can only happen with linker skew + runtime·throw("recursive call during initialization - linker skew"); +} + +void +runtime·throw(int8 *s) +{ + if(m->throwing == 0) + m->throwing = 1; + runtime·startpanic(); + runtime·printf("fatal error: %s\n", s); + runtime·dopanic(0); + *(int32*)0 = 0; // not reached + runtime·exit(1); // even more not reached +} + +void +runtime·panicstring(int8 *s) +{ + Eface err; + + if(m->gcing) { + runtime·printf("panic: %s\n", s); + runtime·throw("panic during gc"); + } + runtime·newErrorString(runtime·gostringnocopy((byte*)s), &err); + runtime·panic(err); +} + +void +runtime·Goexit(void) +{ + rundefer(); + runtime·goexit(); +} diff --git a/src/pkg/runtime/parfor.c b/src/pkg/runtime/parfor.c new file mode 100644 index 000000000..aa5537d02 --- /dev/null +++ b/src/pkg/runtime/parfor.c @@ -0,0 +1,215 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parallel for algorithm. + +#include "runtime.h" +#include "arch_GOARCH.h" + +struct ParForThread +{ + // the thread's iteration space [32lsb, 32msb) + uint64 pos; + // stats + uint64 nsteal; + uint64 nstealcnt; + uint64 nprocyield; + uint64 nosyield; + uint64 nsleep; + byte pad[CacheLineSize]; +}; + +ParFor* +runtime·parforalloc(uint32 nthrmax) +{ + ParFor *desc; + + // The ParFor object is followed by CacheLineSize padding + // and then nthrmax ParForThread. + desc = (ParFor*)runtime·malloc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread)); + desc->thr = (ParForThread*)((byte*)(desc+1) + CacheLineSize); + desc->nthrmax = nthrmax; + return desc; +} + +// For testing from Go +// func parforalloc2(nthrmax uint32) *ParFor +void +runtime·parforalloc2(uint32 nthrmax, ParFor *desc) +{ + desc = runtime·parforalloc(nthrmax); + FLUSH(&desc); +} + +void +runtime·parforsetup(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void (*body)(ParFor*, uint32)) +{ + uint32 i, begin, end; + + if(desc == nil || nthr == 0 || nthr > desc->nthrmax || body == nil) { + runtime·printf("desc=%p nthr=%d count=%d body=%p\n", desc, nthr, n, body); + runtime·throw("parfor: invalid args"); + } + + desc->body = body; + desc->done = 0; + desc->nthr = nthr; + desc->thrseq = 0; + desc->cnt = n; + desc->ctx = ctx; + desc->wait = wait; + desc->nsteal = 0; + desc->nstealcnt = 0; + desc->nprocyield = 0; + desc->nosyield = 0; + desc->nsleep = 0; + for(i=0; ithr[i].pos = (uint64)begin | (((uint64)end)<<32); + } +} + +// For testing from Go +// func parforsetup2(desc *ParFor, nthr, n uint32, ctx *byte, wait bool, body func(*ParFor, uint32)) +void +runtime·parforsetup2(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void *body) +{ + runtime·parforsetup(desc, nthr, n, ctx, wait, *(void(**)(ParFor*, uint32))body); +} + +void +runtime·parfordo(ParFor *desc) +{ + ParForThread *me; + uint32 tid, begin, end, begin2, try, victim, i; + uint64 *mypos, *victimpos, pos, newpos; + void (*body)(ParFor*, uint32); + bool idle; + + // Obtain 0-based thread index. + tid = runtime·xadd(&desc->thrseq, 1) - 1; + if(tid >= desc->nthr) { + runtime·printf("tid=%d nthr=%d\n", tid, desc->nthr); + runtime·throw("parfor: invalid tid"); + } + + // If single-threaded, just execute the for serially. + if(desc->nthr==1) { + for(i=0; icnt; i++) + desc->body(desc, i); + return; + } + + body = desc->body; + me = &desc->thr[tid]; + mypos = &me->pos; + for(;;) { + for(;;) { + // While there is local work, + // bump low index and execute the iteration. + pos = runtime·xadd64(mypos, 1); + begin = (uint32)pos-1; + end = (uint32)(pos>>32); + if(begin < end) { + body(desc, begin); + continue; + } + break; + } + + // Out of work, need to steal something. + idle = false; + for(try=0;; try++) { + // If we don't see any work for long enough, + // increment the done counter... + if(try > desc->nthr*4 && !idle) { + idle = true; + runtime·xadd(&desc->done, 1); + } + // ...if all threads have incremented the counter, + // we are done. + if(desc->done + !idle == desc->nthr) { + if(!idle) + runtime·xadd(&desc->done, 1); + goto exit; + } + // Choose a random victim for stealing. + victim = runtime·fastrand1() % (desc->nthr-1); + if(victim >= tid) + victim++; + victimpos = &desc->thr[victim].pos; + pos = runtime·atomicload64(victimpos); + for(;;) { + // See if it has any work. + begin = (uint32)pos; + end = (uint32)(pos>>32); + if(begin+1 >= end) { + begin = end = 0; + break; + } + if(idle) { + runtime·xadd(&desc->done, -1); + idle = false; + } + begin2 = begin + (end-begin)/2; + newpos = (uint64)begin | (uint64)begin2<<32; + if(runtime·cas64(victimpos, &pos, newpos)) { + begin = begin2; + break; + } + } + if(begin < end) { + // Has successfully stolen some work. + if(idle) + runtime·throw("parfor: should not be idle"); + runtime·atomicstore64(mypos, (uint64)begin | (uint64)end<<32); + me->nsteal++; + me->nstealcnt += end-begin; + break; + } + // Backoff. + if(try < desc->nthr) { + // nothing + } else if (try < 4*desc->nthr) { + me->nprocyield++; + runtime·procyield(20); + // If a caller asked not to wait for the others, exit now + // (assume that most work is already done at this point). + } else if (!desc->wait) { + if(!idle) + runtime·xadd(&desc->done, 1); + goto exit; + } else if (try < 6*desc->nthr) { + me->nosyield++; + runtime·osyield(); + } else { + me->nsleep++; + runtime·usleep(1); + } + } + } +exit: + runtime·xadd64(&desc->nsteal, me->nsteal); + runtime·xadd64(&desc->nstealcnt, me->nstealcnt); + runtime·xadd64(&desc->nprocyield, me->nprocyield); + runtime·xadd64(&desc->nosyield, me->nosyield); + runtime·xadd64(&desc->nsleep, me->nsleep); + me->nsteal = 0; + me->nstealcnt = 0; + me->nprocyield = 0; + me->nosyield = 0; + me->nsleep = 0; +} + +// For testing from Go +// func parforiters(desc *ParFor, tid uintptr) (uintptr, uintptr) +void +runtime·parforiters(ParFor *desc, uintptr tid, uintptr start, uintptr end) +{ + start = (uint32)desc->thr[tid].pos; + end = (uint32)(desc->thr[tid].pos>>32); + FLUSH(&start); + FLUSH(&end); +} diff --git a/src/pkg/runtime/parfor_test.go b/src/pkg/runtime/parfor_test.go new file mode 100644 index 000000000..4c69a68ce --- /dev/null +++ b/src/pkg/runtime/parfor_test.go @@ -0,0 +1,144 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The race detector does not understand ParFor synchronization. +// +build !race + +package runtime_test + +import ( + . "runtime" + "testing" + "unsafe" +) + +var gdata []uint64 + +// Simple serial sanity test for parallelfor. +func TestParFor(t *testing.T) { + const P = 1 + const N = 20 + data := make([]uint64, N) + for i := uint64(0); i < N; i++ { + data[i] = i + } + desc := NewParFor(P) + // Avoid making func a closure: parfor cannot invoke them. + // Since it doesn't happen in the C code, it's not worth doing + // just for the test. + gdata = data + ParForSetup(desc, P, N, nil, true, func(desc *ParFor, i uint32) { + data := gdata + data[i] = data[i]*data[i] + 1 + }) + ParForDo(desc) + for i := uint64(0); i < N; i++ { + if data[i] != i*i+1 { + t.Fatalf("Wrong element %d: %d", i, data[i]) + } + } +} + +// Test that nonblocking parallelfor does not block. +func TestParFor2(t *testing.T) { + const P = 7 + const N = 1003 + data := make([]uint64, N) + for i := uint64(0); i < N; i++ { + data[i] = i + } + desc := NewParFor(P) + ParForSetup(desc, P, N, (*byte)(unsafe.Pointer(&data)), false, func(desc *ParFor, i uint32) { + d := *(*[]uint64)(unsafe.Pointer(desc.Ctx)) + d[i] = d[i]*d[i] + 1 + }) + for p := 0; p < P; p++ { + ParForDo(desc) + } + for i := uint64(0); i < N; i++ { + if data[i] != i*i+1 { + t.Fatalf("Wrong element %d: %d", i, data[i]) + } + } +} + +// Test that iterations are properly distributed. +func TestParForSetup(t *testing.T) { + const P = 11 + const N = 101 + desc := NewParFor(P) + for n := uint32(0); n < N; n++ { + for p := uint32(1); p <= P; p++ { + ParForSetup(desc, p, n, nil, true, func(desc *ParFor, i uint32) {}) + sum := uint32(0) + size0 := uint32(0) + end0 := uint32(0) + for i := uint32(0); i < p; i++ { + begin, end := ParForIters(desc, i) + size := end - begin + sum += size + if i == 0 { + size0 = size + if begin != 0 { + t.Fatalf("incorrect begin: %d (n=%d, p=%d)", begin, n, p) + } + } else { + if size != size0 && size != size0+1 { + t.Fatalf("incorrect size: %d/%d (n=%d, p=%d)", size, size0, n, p) + } + if begin != end0 { + t.Fatalf("incorrect begin/end: %d/%d (n=%d, p=%d)", begin, end0, n, p) + } + } + end0 = end + } + if sum != n { + t.Fatalf("incorrect sum: %d/%d (p=%d)", sum, n, p) + } + } + } +} + +// Test parallel parallelfor. +func TestParForParallel(t *testing.T) { + if GOARCH != "amd64" { + t.Log("temporarily disabled, see http://golang.org/issue/4155") + return + } + + N := uint64(1e7) + if testing.Short() { + N /= 10 + } + data := make([]uint64, N) + for i := uint64(0); i < N; i++ { + data[i] = i + } + P := GOMAXPROCS(-1) + c := make(chan bool, P) + desc := NewParFor(uint32(P)) + gdata = data + ParForSetup(desc, uint32(P), uint32(N), nil, false, func(desc *ParFor, i uint32) { + data := gdata + data[i] = data[i]*data[i] + 1 + }) + for p := 1; p < P; p++ { + go func() { + ParForDo(desc) + c <- true + }() + } + ParForDo(desc) + for p := 1; p < P; p++ { + <-c + } + for i := uint64(0); i < N; i++ { + if data[i] != i*i+1 { + t.Fatalf("Wrong element %d: %d", i, data[i]) + } + } + + data, desc = nil, nil + GC() +} diff --git a/src/pkg/runtime/pprof/pprof.go b/src/pkg/runtime/pprof/pprof.go index f67e8a8f9..32c1098b9 100644 --- a/src/pkg/runtime/pprof/pprof.go +++ b/src/pkg/runtime/pprof/pprof.go @@ -36,8 +36,9 @@ import ( // goroutine - stack traces of all current goroutines // heap - a sampling of all heap allocations // threadcreate - stack traces that led to the creation of new OS threads +// block - stack traces that led to blocking on synchronization primitives // -// These predefine profiles maintain themselves and panic on an explicit +// These predefined profiles maintain themselves and panic on an explicit // Add or Remove method call. // // The CPU profile is not available as a Profile. It has a special API, @@ -76,6 +77,12 @@ var heapProfile = &Profile{ write: writeHeap, } +var blockProfile = &Profile{ + name: "block", + count: countBlock, + write: writeBlock, +} + func lockProfiles() { profiles.mu.Lock() if profiles.m == nil { @@ -84,6 +91,7 @@ func lockProfiles() { "goroutine": goroutineProfile, "threadcreate": threadcreateProfile, "heap": heapProfile, + "block": blockProfile, } } } @@ -310,21 +318,33 @@ func printCountProfile(w io.Writer, debug int, name string, p countProfile) erro // for a single stack trace. func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) { show := allFrames - for _, pc := range stk { + wasPanic := false + for i, pc := range stk { f := runtime.FuncForPC(pc) if f == nil { show = true fmt.Fprintf(w, "#\t%#x\n", pc) + wasPanic = false } else { - file, line := f.FileLine(pc) + tracepc := pc + // Back up to call instruction. + if i > 0 && pc > f.Entry() && !wasPanic { + if runtime.GOARCH == "386" || runtime.GOARCH == "amd64" { + tracepc-- + } else { + tracepc -= 4 // arm, etc + } + } + file, line := f.FileLine(tracepc) name := f.Name() // Hide runtime.goexit and any runtime functions at the beginning. // This is useful mainly for allocation traces. + wasPanic = name == "runtime.panic" if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") { continue } show = true - fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, f.Name(), pc-f.Entry(), file, line) + fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, name, pc-f.Entry(), file, line) } } if !show { @@ -352,26 +372,26 @@ func WriteHeapProfile(w io.Writer) error { // countHeap returns the number of records in the heap profile. func countHeap() int { - n, _ := runtime.MemProfile(nil, false) + n, _ := runtime.MemProfile(nil, true) return n } -// writeHeapProfile writes the current runtime heap profile to w. +// writeHeap writes the current runtime heap profile to w. func writeHeap(w io.Writer, debug int) error { - // Find out how many records there are (MemProfile(nil, false)), + // Find out how many records there are (MemProfile(nil, true)), // allocate that many records, and get the data. // There's a race—more records might be added between // the two calls—so allocate a few extra records for safety // and also try again if we're very unlucky. // The loop should only execute one iteration in the common case. var p []runtime.MemProfileRecord - n, ok := runtime.MemProfile(nil, false) + n, ok := runtime.MemProfile(nil, true) for { // Allocate room for a slightly bigger profile, // in case a few more entries have been added // since the call to MemProfile. p = make([]runtime.MemProfileRecord, n+50) - n, ok = runtime.MemProfile(p, false) + n, ok = runtime.MemProfile(p, true) if ok { p = p[0:n] break @@ -431,11 +451,14 @@ func writeHeap(w io.Writer, debug int) error { fmt.Fprintf(w, "# Sys = %d\n", s.Sys) fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups) fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs) + fmt.Fprintf(w, "# Frees = %d\n", s.Frees) fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc) fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys) fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle) fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse) + fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased) + fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects) fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys) fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys) @@ -597,3 +620,60 @@ func StopCPUProfile() { runtime.SetCPUProfileRate(0) <-cpu.done } + +type byCycles []runtime.BlockProfileRecord + +func (x byCycles) Len() int { return len(x) } +func (x byCycles) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byCycles) Less(i, j int) bool { return x[i].Cycles > x[j].Cycles } + +// countBlock returns the number of records in the blocking profile. +func countBlock() int { + n, _ := runtime.BlockProfile(nil) + return n +} + +// writeBlock writes the current blocking profile to w. +func writeBlock(w io.Writer, debug int) error { + var p []runtime.BlockProfileRecord + n, ok := runtime.BlockProfile(nil) + for { + p = make([]runtime.BlockProfileRecord, n+50) + n, ok = runtime.BlockProfile(p) + if ok { + p = p[:n] + break + } + } + + sort.Sort(byCycles(p)) + + b := bufio.NewWriter(w) + var tw *tabwriter.Writer + w = b + if debug > 0 { + tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) + w = tw + } + + fmt.Fprintf(w, "--- contention:\n") + fmt.Fprintf(w, "cycles/second=%v\n", runtime_cyclesPerSecond()) + for i := range p { + r := &p[i] + fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count) + for _, pc := range r.Stack() { + fmt.Fprintf(w, " %#x", pc) + } + fmt.Fprint(w, "\n") + if debug > 0 { + printStackRecord(w, r.Stack(), false) + } + } + + if tw != nil { + tw.Flush() + } + return b.Flush() +} + +func runtime_cyclesPerSecond() int64 diff --git a/src/pkg/runtime/pprof/pprof_test.go b/src/pkg/runtime/pprof/pprof_test.go index 82bb2a292..6d5764f4a 100644 --- a/src/pkg/runtime/pprof/pprof_test.go +++ b/src/pkg/runtime/pprof/pprof_test.go @@ -26,8 +26,7 @@ func TestCPUProfile(t *testing.T) { t.Logf("uname -a: %v", vers) // Lion uses "Darwin Kernel Version 11". if strings.Contains(vers, "Darwin Kernel Version 10") && strings.Contains(vers, "RELEASE_X86_64") { - t.Logf("skipping test on known-broken kernel (64-bit Leopard / Snow Leopard)") - return + t.Skip("skipping test on known-broken kernel (64-bit Leopard / Snow Leopard)") } case "plan9": // unimplemented @@ -49,19 +48,25 @@ func TestCPUProfile(t *testing.T) { // Convert []byte to []uintptr. bytes := prof.Bytes() + l := len(bytes) / int(unsafe.Sizeof(uintptr(0))) val := *(*[]uintptr)(unsafe.Pointer(&bytes)) - val = val[:len(bytes)/int(unsafe.Sizeof(uintptr(0)))] + val = val[:l] - if len(val) < 10 { + if l < 13 { t.Fatalf("profile too short: %#x", val) } - if val[0] != 0 || val[1] != 3 || val[2] != 0 || val[3] != 1e6/100 || val[4] != 0 { - t.Fatalf("unexpected header %#x", val[:5]) + + hd, val, tl := val[:5], val[5:l-3], val[l-3:] + if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 { + t.Fatalf("unexpected header %#x", hd) + } + + if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 { + t.Fatalf("malformed end-of-data marker %#x", tl) } // Check that profile is well formed and contains ChecksumIEEE. found := false - val = val[5:] for len(val) > 0 { if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] { t.Fatalf("malformed profile. leftover: %#x", val) diff --git a/src/pkg/runtime/print.c b/src/pkg/runtime/print.c index 6702c3cde..5b601599b 100644 --- a/src/pkg/runtime/print.c +++ b/src/pkg/runtime/print.c @@ -18,10 +18,10 @@ gwrite(void *v, int32 n) runtime·write(2, v, n); return; } - + if(g->writenbuf == 0) return; - + if(n > g->writenbuf) n = g->writenbuf; runtime·memmove(g->writebuf, v, n); @@ -84,40 +84,41 @@ vprintf(int8 *s, byte *base) narg = 0; switch(*p) { case 't': + case 'c': narg = arg + 1; break; case 'd': // 32-bit case 'x': - arg = runtime·rnd(arg, 4); + arg = ROUND(arg, 4); narg = arg + 4; break; case 'D': // 64-bit case 'U': case 'X': case 'f': - arg = runtime·rnd(arg, sizeof(uintptr)); + arg = ROUND(arg, sizeof(uintptr)); narg = arg + 8; break; case 'C': - arg = runtime·rnd(arg, sizeof(uintptr)); + arg = ROUND(arg, sizeof(uintptr)); narg = arg + 16; break; case 'p': // pointer-sized case 's': - arg = runtime·rnd(arg, sizeof(uintptr)); + arg = ROUND(arg, sizeof(uintptr)); narg = arg + sizeof(uintptr); break; case 'S': // pointer-aligned but bigger - arg = runtime·rnd(arg, sizeof(uintptr)); + arg = ROUND(arg, sizeof(uintptr)); narg = arg + sizeof(String); break; case 'a': // pointer-aligned but bigger - arg = runtime·rnd(arg, sizeof(uintptr)); + arg = ROUND(arg, sizeof(uintptr)); narg = arg + sizeof(Slice); break; case 'i': // pointer-aligned but bigger case 'e': - arg = runtime·rnd(arg, sizeof(uintptr)); + arg = ROUND(arg, sizeof(uintptr)); narg = arg + sizeof(Eface); break; } @@ -126,6 +127,9 @@ vprintf(int8 *s, byte *base) case 'a': runtime·printslice(*(Slice*)v); break; + case 'c': + runtime·printbyte(*(int8*)v); + break; case 'd': runtime·printint(*(int32*)v); break; @@ -202,6 +206,12 @@ runtime·printbool(bool v) gwrite((byte*)"false", 5); } +void +runtime·printbyte(int8 c) +{ + gwrite(&c, 1); +} + void runtime·printfloat(float64 v) { @@ -209,15 +219,15 @@ runtime·printfloat(float64 v) int32 e, s, i, n; float64 h; - if(runtime·isNaN(v)) { + if(ISNAN(v)) { gwrite("NaN", 3); return; } - if(runtime·isInf(v, 1)) { + if(v == runtime·posinf) { gwrite("+Inf", 4); return; } - if(runtime·isInf(v, -1)) { + if(v == runtime·neginf) { gwrite("-Inf", 4); return; } @@ -343,7 +353,7 @@ runtime·printstring(String v) extern uint32 runtime·maxstring; if(v.len > runtime·maxstring) { - gwrite("[invalid string]", 16); + gwrite("[string too long]", 17); return; } if(v.len > 0) diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c index 04a992628..4ce0a718c 100644 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@ -4,169 +4,112 @@ #include "runtime.h" #include "arch_GOARCH.h" -#include "defs_GOOS_GOARCH.h" #include "malloc.h" -#include "os_GOOS.h" #include "stack.h" +#include "race.h" +#include "type.h" -bool runtime·iscgo; - -static void unwindstack(G*, byte*); -static void schedule(G*); - -typedef struct Sched Sched; - -M runtime·m0; -G runtime·g0; // idle goroutine for m0 - -static int32 debug = 0; - -int32 runtime·gcwaiting; - -// Go scheduler -// -// The go scheduler's job is to match ready-to-run goroutines (`g's) -// with waiting-for-work schedulers (`m's). If there are ready g's -// and no waiting m's, ready() will start a new m running in a new -// OS thread, so that all ready g's can run simultaneously, up to a limit. -// For now, m's never go away. -// -// By default, Go keeps only one kernel thread (m) running user code -// at a single time; other threads may be blocked in the operating system. -// Setting the environment variable $GOMAXPROCS or calling -// runtime.GOMAXPROCS() will change the number of user threads -// allowed to execute simultaneously. $GOMAXPROCS is thus an -// approximation of the maximum number of cores to use. +// Goroutine scheduler +// The scheduler's job is to distribute ready-to-run goroutines over worker threads. // -// Even a program that can run without deadlock in a single process -// might use more m's if given the chance. For example, the prime -// sieve will use as many m's as there are primes (up to runtime·sched.mmax), -// allowing different stages of the pipeline to execute in parallel. -// We could revisit this choice, only kicking off new m's for blocking -// system calls, but that would limit the amount of parallel computation -// that go would try to do. +// The main concepts are: +// G - goroutine. +// M - worker thread, or machine. +// P - processor, a resource that is required to execute Go code. +// M must have an associated P to execute Go code, however it can be +// blocked or in a syscall w/o an associated P. // -// In general, one could imagine all sorts of refinements to the -// scheduler, but the goal now is just to get something working on -// Linux and OS X. +// Design doc at http://golang.org/s/go11sched. +typedef struct Sched Sched; struct Sched { Lock; - G *gfree; // available g's (status == Gdead) - int32 goidgen; - - G *ghead; // g's waiting to run - G *gtail; - int32 gwait; // number of g's waiting to run - int32 gcount; // number of g's that are alive - int32 grunning; // number of g's running on cpu or in syscall + uint64 goidgen; - M *mhead; // m's waiting for work - int32 mwait; // number of m's waiting for work - int32 mcount; // number of m's that have been created + M* midle; // idle m's waiting for work + int32 nmidle; // number of idle m's waiting for work + int32 mlocked; // number of locked m's waiting for work + int32 mcount; // number of m's that have been created - volatile uint32 atomic; // atomic scheduling word (see below) + P* pidle; // idle P's + uint32 npidle; + uint32 nmspinning; - int32 profilehz; // cpu profiling rate + // Global runnable queue. + G* runqhead; + G* runqtail; + int32 runqsize; - bool init; // running initialization - bool lockmain; // init called runtime.LockOSThread + // Global cache of dead G's. + Lock gflock; + G* gfree; - Note stopped; // one g can set waitstop and wait here for m's to stop -}; + int32 stopwait; + Note stopnote; + uint32 sysmonwait; + Note sysmonnote; -// The atomic word in sched is an atomic uint32 that -// holds these fields. -// -// [15 bits] mcpu number of m's executing on cpu -// [15 bits] mcpumax max number of m's allowed on cpu -// [1 bit] waitstop some g is waiting on stopped -// [1 bit] gwaiting gwait != 0 -// -// These fields are the information needed by entersyscall -// and exitsyscall to decide whether to coordinate with the -// scheduler. Packing them into a single machine word lets -// them use a fast path with a single atomic read/write and -// no lock/unlock. This greatly reduces contention in -// syscall- or cgo-heavy multithreaded programs. -// -// Except for entersyscall and exitsyscall, the manipulations -// to these fields only happen while holding the schedlock, -// so the routines holding schedlock only need to worry about -// what entersyscall and exitsyscall do, not the other routines -// (which also use the schedlock). -// -// In particular, entersyscall and exitsyscall only read mcpumax, -// waitstop, and gwaiting. They never write them. Thus, writes to those -// fields can be done (holding schedlock) without fear of write conflicts. -// There may still be logic conflicts: for example, the set of waitstop must -// be conditioned on mcpu >= mcpumax or else the wait may be a -// spurious sleep. The Promela model in proc.p verifies these accesses. -enum { - mcpuWidth = 15, - mcpuMask = (1<>mcpuShift)&mcpuMask) -#define atomic_mcpumax(v) (((v)>>mcpumaxShift)&mcpuMask) -#define atomic_waitstop(v) (((v)>>waitstopShift)&1) -#define atomic_gwaiting(v) (((v)>>gwaitingShift)&1) - -Sched runtime·sched; -int32 runtime·gomaxprocs; -bool runtime·singleproc; - -static bool canaddmcpu(void); - -// An m that is waiting for notewakeup(&m->havenextg). This may -// only be accessed while the scheduler lock is held. This is used to -// minimize the number of times we call notewakeup while the scheduler -// lock is held, since the m will normally move quickly to lock the -// scheduler itself, producing lock contention. -static M* mwakeup; - -// Scheduling helpers. Sched must be locked. -static void gput(G*); // put/get on ghead/gtail -static G* gget(void); -static void mput(M*); // put/get on mhead -static M* mget(G*); -static void gfput(G*); // put/get on gfree -static G* gfget(void); -static void matchmg(void); // match m's to g's -static void readylocked(G*); // ready, but sched is locked -static void mnextg(M*, G*); -static void mcommoninit(M*); - -void -setmcpumax(uint32 n) -{ - uint32 v, w; - - for(;;) { - v = runtime·sched.atomic; - w = v; - w &= ~(mcpuMask<nomemprof++; + runtime·mprofinit(); runtime·mallocinit(); mcommoninit(m); @@ -193,93 +137,70 @@ runtime·schedinit(void) // so that we don't need to call malloc when we crash. // runtime·findfunc(0); - runtime·gomaxprocs = 1; + procs = 1; p = runtime·getenv("GOMAXPROCS"); - if(p != nil && (n = runtime·atoi(p)) != 0) { - if(n > maxgomaxprocs) - n = maxgomaxprocs; - runtime·gomaxprocs = n; + if(p != nil && (n = runtime·atoi(p)) > 0) { + if(n > MaxGomaxprocs) + n = MaxGomaxprocs; + procs = n; } - // wait for the main goroutine to start before taking - // GOMAXPROCS into account. - setmcpumax(1); - runtime·singleproc = runtime·gomaxprocs == 1; - - canaddmcpu(); // mcpu++ to account for bootstrap m - m->helpgc = 1; // flag to tell schedule() to mcpu-- - runtime·sched.grunning++; + runtime·allp = runtime·malloc((MaxGomaxprocs+1)*sizeof(runtime·allp[0])); + procresize(procs); mstats.enablegc = 1; m->nomemprof--; + + if(raceenabled) + g->racectx = runtime·raceinit(); } extern void main·init(void); extern void main·main(void); +static FuncVal scavenger = {runtime·MHeap_Scavenger}; + // The main goroutine. void runtime·main(void) { + newm(sysmon, nil); + // Lock the main goroutine onto this, the main OS thread, // during initialization. Most programs won't care, but a few // do require certain calls to be made by the main thread. // Those can arrange for main.main to run in the main thread // by calling runtime.LockOSThread during initialization // to preserve the lock. - runtime·LockOSThread(); - // From now on, newgoroutines may use non-main threads. - setmcpumax(runtime·gomaxprocs); - runtime·sched.init = true; - scvg = runtime·newproc1((byte*)runtime·MHeap_Scavenger, nil, 0, 0, runtime·main); + runtime·lockOSThread(); + if(m != &runtime·m0) + runtime·throw("runtime·main not on m0"); + scvg = runtime·newproc1(&scavenger, nil, 0, 0, runtime·main); + scvg->issystem = true; main·init(); - runtime·sched.init = false; - if(!runtime·sched.lockmain) - runtime·UnlockOSThread(); - - // The deadlock detection has false negatives. - // Let scvg start up, to eliminate the false negative - // for the trivial program func main() { select{} }. - runtime·gosched(); + runtime·unlockOSThread(); main·main(); + if(raceenabled) + runtime·racefini(); + + // Make racy client program work: if panicking on + // another goroutine at the same time as main returns, + // let the other goroutine finish printing the panic trace. + // Once it does, it will exit. See issue 3934. + if(runtime·panicking) + runtime·park(nil, nil, "panicwait"); + runtime·exit(0); for(;;) *(int32*)runtime·main = 0; } -// Lock the scheduler. -static void -schedlock(void) -{ - runtime·lock(&runtime·sched); -} - -// Unlock the scheduler. -static void -schedunlock(void) -{ - M *m; - - m = mwakeup; - mwakeup = nil; - runtime·unlock(&runtime·sched); - if(m != nil) - runtime·notewakeup(&m->havenextg); -} - -void -runtime·goexit(void) -{ - g->status = Gmoribund; - runtime·gosched(); -} - void -runtime·goroutineheader(G *g) +runtime·goroutineheader(G *gp) { int8 *status; - switch(g->status) { + switch(gp->status) { case Gidle: status = "idle"; break; @@ -293,609 +214,748 @@ runtime·goroutineheader(G *g) status = "syscall"; break; case Gwaiting: - if(g->waitreason) - status = g->waitreason; + if(gp->waitreason) + status = gp->waitreason; else status = "waiting"; break; - case Gmoribund: - status = "moribund"; - break; default: status = "???"; break; } - runtime·printf("goroutine %d [%s]:\n", g->goid, status); + runtime·printf("goroutine %D [%s]:\n", gp->goid, status); } void runtime·tracebackothers(G *me) { - G *g; + G *gp; + int32 traceback; - for(g = runtime·allg; g != nil; g = g->alllink) { - if(g == me || g->status == Gdead) + traceback = runtime·gotraceback(); + for(gp = runtime·allg; gp != nil; gp = gp->alllink) { + if(gp == me || gp->status == Gdead) + continue; + if(gp->issystem && traceback < 2) continue; runtime·printf("\n"); - runtime·goroutineheader(g); - runtime·traceback(g->sched.pc, g->sched.sp, 0, g); + runtime·goroutineheader(gp); + runtime·traceback(gp->sched.pc, (byte*)gp->sched.sp, 0, gp); } } -// Mark this g as m's idle goroutine. -// This functionality might be used in environments where programs -// are limited to a single thread, to simulate a select-driven -// network server. It is not exposed via the standard runtime API. -void -runtime·idlegoroutine(void) -{ - if(g->idlem != nil) - runtime·throw("g is already an idle goroutine"); - g->idlem = m; -} - static void -mcommoninit(M *m) +mcommoninit(M *mp) { - m->id = runtime·sched.mcount++; - m->fastrand = 0x49f6428aUL + m->id + runtime·cputicks(); - m->stackalloc = runtime·malloc(sizeof(*m->stackalloc)); - runtime·FixAlloc_Init(m->stackalloc, FixedStack, runtime·SysAlloc, nil, nil); + // If there is no mcache runtime·callers() will crash, + // and we are most likely in sysmon thread so the stack is senseless anyway. + if(m->mcache) + runtime·callers(1, mp->createstack, nelem(mp->createstack)); - if(m->mcache == nil) - m->mcache = runtime·allocmcache(); + mp->fastrand = 0x49f6428aUL + mp->id + runtime·cputicks(); - runtime·callers(1, m->createstack, nelem(m->createstack)); + runtime·lock(&runtime·sched); + mp->id = runtime·sched.mcount++; + + runtime·mpreinit(mp); // Add to runtime·allm so garbage collector doesn't free m // when it is just in a register or thread-local storage. - m->alllink = runtime·allm; + mp->alllink = runtime·allm; // runtime·NumCgoCall() iterates over allm w/o schedlock, // so we need to publish it safely. - runtime·atomicstorep(&runtime·allm, m); + runtime·atomicstorep(&runtime·allm, mp); + runtime·unlock(&runtime·sched); } -// Try to increment mcpu. Report whether succeeded. -static bool -canaddmcpu(void) +// Mark gp ready to run. +void +runtime·ready(G *gp) { - uint32 v; - - for(;;) { - v = runtime·sched.atomic; - if(atomic_mcpu(v) >= atomic_mcpumax(v)) - return 0; - if(runtime·cas(&runtime·sched.atomic, v, v+(1<status != Gwaiting) { + runtime·printf("goroutine %D has status %d\n", gp->goid, gp->status); + runtime·throw("bad g->status in ready"); } + gp->status = Grunnable; + runqput(m->p, gp); + if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0) // TODO: fast atomic + wakep(); } -// Put on `g' queue. Sched must be locked. -static void -gput(G *g) +int32 +runtime·gcprocs(void) { - M *m; - - // If g is wired, hand it off directly. - if((m = g->lockedm) != nil && canaddmcpu()) { - mnextg(m, g); - return; - } + int32 n; - // If g is the idle goroutine for an m, hand it off. - if(g->idlem != nil) { - if(g->idlem->idleg != nil) { - runtime·printf("m%d idle out of sync: g%d g%d\n", - g->idlem->id, - g->idlem->idleg->goid, g->goid); - runtime·throw("runtime: double idle"); - } - g->idlem->idleg = g; - return; - } + // Figure out how many CPUs to use during GC. + // Limited by gomaxprocs, number of actual CPUs, and MaxGcproc. + runtime·lock(&runtime·sched); + n = runtime·gomaxprocs; + if(n > runtime·ncpu) + n = runtime·ncpu; + if(n > MaxGcproc) + n = MaxGcproc; + if(n > runtime·sched.nmidle+1) // one M is currently running + n = runtime·sched.nmidle+1; + runtime·unlock(&runtime·sched); + return n; +} - g->schedlink = nil; - if(runtime·sched.ghead == nil) - runtime·sched.ghead = g; - else - runtime·sched.gtail->schedlink = g; - runtime·sched.gtail = g; +static bool +needaddgcproc(void) +{ + int32 n; - // increment gwait. - // if it transitions to nonzero, set atomic gwaiting bit. - if(runtime·sched.gwait++ == 0) - runtime·xadd(&runtime·sched.atomic, 1< runtime·ncpu) + n = runtime·ncpu; + if(n > MaxGcproc) + n = MaxGcproc; + n -= runtime·sched.nmidle+1; // one M is currently running + runtime·unlock(&runtime·sched); + return n > 0; } -// Report whether gget would return something. -static bool -haveg(void) +void +runtime·helpgc(int32 nproc) { - return runtime·sched.ghead != nil || m->idleg != nil; + M *mp; + int32 n, pos; + + runtime·lock(&runtime·sched); + pos = 0; + for(n = 1; n < nproc; n++) { // one M is currently running + if(runtime·allp[pos]->mcache == m->mcache) + pos++; + mp = mget(); + if(mp == nil) + runtime·throw("runtime·gcprocs inconsistency"); + mp->helpgc = 1; + mp->mcache = runtime·allp[pos]->mcache; + pos++; + runtime·notewakeup(&mp->park); + } + runtime·unlock(&runtime·sched); } -// Get from `g' queue. Sched must be locked. -static G* -gget(void) +void +runtime·stoptheworld(void) { - G *g; + int32 i; + uint32 s; + P *p; + bool wait; - g = runtime·sched.ghead; - if(g){ - runtime·sched.ghead = g->schedlink; - if(runtime·sched.ghead == nil) - runtime·sched.gtail = nil; - // decrement gwait. - // if it transitions to zero, clear atomic gwaiting bit. - if(--runtime·sched.gwait == 0) - runtime·xadd(&runtime·sched.atomic, -1<idleg != nil) { - g = m->idleg; - m->idleg = nil; + runtime·lock(&runtime·sched); + runtime·sched.stopwait = runtime·gomaxprocs; + runtime·atomicstore((uint32*)&runtime·gcwaiting, 1); + // stop current P + m->p->status = Pgcstop; + runtime·sched.stopwait--; + // try to retake all P's in Psyscall status + for(i = 0; i < runtime·gomaxprocs; i++) { + p = runtime·allp[i]; + s = p->status; + if(s == Psyscall && runtime·cas(&p->status, s, Pgcstop)) + runtime·sched.stopwait--; + } + // stop idle P's + while(p = pidleget()) { + p->status = Pgcstop; + runtime·sched.stopwait--; + } + wait = runtime·sched.stopwait > 0; + runtime·unlock(&runtime·sched); + + // wait for remaining P's to stop voluntary + if(wait) { + runtime·notesleep(&runtime·sched.stopnote); + runtime·noteclear(&runtime·sched.stopnote); + } + if(runtime·sched.stopwait) + runtime·throw("stoptheworld: not stopped"); + for(i = 0; i < runtime·gomaxprocs; i++) { + p = runtime·allp[i]; + if(p->status != Pgcstop) + runtime·throw("stoptheworld: not stopped"); } - return g; } -// Put on `m' list. Sched must be locked. static void -mput(M *m) +mhelpgc(void) { - m->schedlink = runtime·sched.mhead; - runtime·sched.mhead = m; - runtime·sched.mwait++; + m->helpgc = 1; } -// Get an `m' to run `g'. Sched must be locked. -static M* -mget(G *g) +void +runtime·starttheworld(void) { - M *m; + P *p; + M *mp; + bool add; + + add = needaddgcproc(); + runtime·lock(&runtime·sched); + if(newprocs) { + procresize(newprocs); + newprocs = 0; + } else + procresize(runtime·gomaxprocs); + runtime·gcwaiting = 0; - // if g has its own m, use it. - if(g && (m = g->lockedm) != nil) - return m; + while(p = pidleget()) { + // procresize() puts p's with work at the beginning of the list. + // Once we reach a p without a run queue, the rest don't have one either. + if(p->runqhead == p->runqtail) { + pidleput(p); + break; + } + mp = mget(); + if(mp == nil) { + pidleput(p); + break; + } + if(mp->nextp) + runtime·throw("starttheworld: inconsistent mp->nextp"); + mp->nextp = p; + runtime·notewakeup(&mp->park); + } + if(runtime·sched.sysmonwait) { + runtime·sched.sysmonwait = false; + runtime·notewakeup(&runtime·sched.sysmonnote); + } + runtime·unlock(&runtime·sched); - // otherwise use general m pool. - if((m = runtime·sched.mhead) != nil){ - runtime·sched.mhead = m->schedlink; - runtime·sched.mwait--; + if(add) { + // If GC could have used another helper proc, start one now, + // in the hope that it will be available next time. + // It would have been even better to start it before the collection, + // but doing so requires allocating memory, so it's tricky to + // coordinate. This lazy approach works out in practice: + // we don't mind if the first couple gc rounds don't have quite + // the maximum number of procs. + newm(mhelpgc, nil); } - return m; } -// Mark g ready to run. +// Called to start an M. void -runtime·ready(G *g) +runtime·mstart(void) { - schedlock(); - readylocked(g); - schedunlock(); -} + // It is used by windows-386 only. Unfortunately, seh needs + // to be located on os stack, and mstart runs on os stack + // for both m0 and m. + SEH seh; -// Mark g ready to run. Sched is already locked. -// G might be running already and about to stop. -// The sched lock protects g->status from changing underfoot. -static void -readylocked(G *g) -{ - if(g->m){ - // Running on another machine. - // Ready it when it stops. - g->readyonstop = 1; - return; + if(g != m->g0) + runtime·throw("bad runtime·mstart"); + + // Record top of stack for use by mcall. + // Once we call schedule we're never coming back, + // so other calls can reuse this stack space. + runtime·gosave(&m->g0->sched); + m->g0->sched.pc = (void*)-1; // make sure it is never used + m->seh = &seh; + runtime·asminit(); + runtime·minit(); + + // Install signal handlers; after minit so that minit can + // prepare the thread to be able to handle the signals. + if(m == &runtime·m0) { + runtime·initsig(); + if(runtime·iscgo) + runtime·newextram(); } + + if(m->mstartfn) + m->mstartfn(); - // Mark runnable. - if(g->status == Grunnable || g->status == Grunning) { - runtime·printf("goroutine %d has status %d\n", g->goid, g->status); - runtime·throw("bad g->status in ready"); + if(m->helpgc) { + m->helpgc = false; + stopm(); + } else if(m != &runtime·m0) { + acquirep(m->nextp); + m->nextp = nil; } - g->status = Grunnable; + schedule(); - gput(g); - matchmg(); + // TODO(brainman): This point is never reached, because scheduler + // does not release os threads at the moment. But once this path + // is enabled, we must remove our seh here. } -static void -nop(void) -{ -} +// When running with cgo, we call _cgo_thread_start +// to start threads for us so that we can play nicely with +// foreign code. +void (*_cgo_thread_start)(void*); -// Same as readylocked but a different symbol so that -// debuggers can set a breakpoint here and catch all -// new goroutines. -static void -newprocreadylocked(G *g) +typedef struct CgoThreadStart CgoThreadStart; +struct CgoThreadStart { - nop(); // avoid inlining in 6l - readylocked(g); -} + M *m; + G *g; + void (*fn)(void); +}; -// Pass g to m for running. -// Caller has already incremented mcpu. -static void -mnextg(M *m, G *g) +// Allocate a new m unassociated with any thread. +// Can use p for allocation context if needed. +M* +runtime·allocm(P *p) { - runtime·sched.grunning++; - m->nextg = g; - if(m->waitnextg) { - m->waitnextg = 0; - if(mwakeup != nil) - runtime·notewakeup(&mwakeup->havenextg); - mwakeup = m; + M *mp; + static Type *mtype; // The Go type M + + m->locks++; // disable GC because it can be called from sysmon + if(m->p == nil) + acquirep(p); // temporarily borrow p for mallocs in this function + if(mtype == nil) { + Eface e; + runtime·gc_m_ptr(&e); + mtype = ((PtrType*)e.type)->elem; } -} -// Get the next goroutine that m should run. -// Sched must be locked on entry, is unlocked on exit. -// Makes sure that at most $GOMAXPROCS g's are -// running on cpus (not in system calls) at any given time. -static G* -nextgandunlock(void) -{ - G *gp; - uint32 v; + mp = runtime·cnew(mtype); + mcommoninit(mp); -top: - if(atomic_mcpu(runtime·sched.atomic) >= maxgomaxprocs) - runtime·throw("negative mcpu"); - - // If there is a g waiting as m->nextg, the mcpu++ - // happened before it was passed to mnextg. - if(m->nextg != nil) { - gp = m->nextg; - m->nextg = nil; - schedunlock(); - return gp; - } + // In case of cgo, pthread_create will make us a stack. + // Windows will layout sched stack on OS stack. + if(runtime·iscgo || Windows) + mp->g0 = runtime·malg(-1); + else + mp->g0 = runtime·malg(8192); - if(m->lockedg != nil) { - // We can only run one g, and it's not available. - // Make sure some other cpu is running to handle - // the ordinary run queue. - if(runtime·sched.gwait != 0) { - matchmg(); - // m->lockedg might have been on the queue. - if(m->nextg != nil) { - gp = m->nextg; - m->nextg = nil; - schedunlock(); - return gp; - } - } - } else { - // Look for work on global queue. - while(haveg() && canaddmcpu()) { - gp = gget(); - if(gp == nil) - runtime·throw("gget inconsistency"); - - if(gp->lockedm) { - mnextg(gp->lockedm, gp); - continue; - } - runtime·sched.grunning++; - schedunlock(); - return gp; - } + if(p == m->p) + releasep(); + m->locks--; - // The while loop ended either because the g queue is empty - // or because we have maxed out our m procs running go - // code (mcpu >= mcpumax). We need to check that - // concurrent actions by entersyscall/exitsyscall cannot - // invalidate the decision to end the loop. - // - // We hold the sched lock, so no one else is manipulating the - // g queue or changing mcpumax. Entersyscall can decrement - // mcpu, but if does so when there is something on the g queue, - // the gwait bit will be set, so entersyscall will take the slow path - // and use the sched lock. So it cannot invalidate our decision. - // - // Wait on global m queue. - mput(m); - } - - // Look for deadlock situation. - // There is a race with the scavenger that causes false negatives: - // if the scavenger is just starting, then we have - // scvg != nil && grunning == 0 && gwait == 0 - // and we do not detect a deadlock. It is possible that we should - // add that case to the if statement here, but it is too close to Go 1 - // to make such a subtle change. Instead, we work around the - // false negative in trivial programs by calling runtime.gosched - // from the main goroutine just before main.main. - // See runtime·main above. - // - // On a related note, it is also possible that the scvg == nil case is - // wrong and should include gwait, but that does not happen in - // standard Go programs, which all start the scavenger. - // - if((scvg == nil && runtime·sched.grunning == 0) || - (scvg != nil && runtime·sched.grunning == 1 && runtime·sched.gwait == 0 && - (scvg->status == Grunning || scvg->status == Gsyscall))) { - runtime·throw("all goroutines are asleep - deadlock!"); - } - - m->nextg = nil; - m->waitnextg = 1; - runtime·noteclear(&m->havenextg); - - // Stoptheworld is waiting for all but its cpu to go to stop. - // Entersyscall might have decremented mcpu too, but if so - // it will see the waitstop and take the slow path. - // Exitsyscall never increments mcpu beyond mcpumax. - v = runtime·atomicload(&runtime·sched.atomic); - if(atomic_waitstop(v) && atomic_mcpu(v) <= atomic_mcpumax(v)) { - // set waitstop = 0 (known to be 1) - runtime·xadd(&runtime·sched.atomic, -1<havenextg); - if(m->helpgc) { - runtime·gchelper(); - m->helpgc = 0; - runtime·lock(&runtime·sched); - goto top; - } - if((gp = m->nextg) == nil) - runtime·throw("bad m->nextg in nextgoroutine"); - m->nextg = nil; - return gp; + return mp; } -int32 -runtime·helpgc(bool *extra) +static M* lockextra(bool nilokay); +static void unlockextra(M*); + +// needm is called when a cgo callback happens on a +// thread without an m (a thread not created by Go). +// In this case, needm is expected to find an m to use +// and return with m, g initialized correctly. +// Since m and g are not set now (likely nil, but see below) +// needm is limited in what routines it can call. In particular +// it can only call nosplit functions (textflag 7) and cannot +// do any scheduling that requires an m. +// +// In order to avoid needing heavy lifting here, we adopt +// the following strategy: there is a stack of available m's +// that can be stolen. Using compare-and-swap +// to pop from the stack has ABA races, so we simulate +// a lock by doing an exchange (via casp) to steal the stack +// head and replace the top pointer with MLOCKED (1). +// This serves as a simple spin lock that we can use even +// without an m. The thread that locks the stack in this way +// unlocks the stack by storing a valid stack head pointer. +// +// In order to make sure that there is always an m structure +// available to be stolen, we maintain the invariant that there +// is always one more than needed. At the beginning of the +// program (if cgo is in use) the list is seeded with a single m. +// If needm finds that it has taken the last m off the list, its job +// is - once it has installed its own m so that it can do things like +// allocate memory - to create a spare m and put it on the list. +// +// Each of these extra m's also has a g0 and a curg that are +// pressed into service as the scheduling stack and current +// goroutine for the duration of the cgo callback. +// +// When the callback is done with the m, it calls dropm to +// put the m back on the list. +#pragma textflag 7 +void +runtime·needm(byte x) { M *mp; - int32 n, max; - // Figure out how many CPUs to use. - // Limited by gomaxprocs, number of actual CPUs, and MaxGcproc. - max = runtime·gomaxprocs; - if(max > runtime·ncpu) - max = runtime·ncpu; - if(max > MaxGcproc) - max = MaxGcproc; + // Lock extra list, take head, unlock popped list. + // nilokay=false is safe here because of the invariant above, + // that the extra list always contains or will soon contain + // at least one m. + mp = lockextra(false); + + // Set needextram when we've just emptied the list, + // so that the eventual call into cgocallbackg will + // allocate a new m for the extra list. We delay the + // allocation until then so that it can be done + // after exitsyscall makes sure it is okay to be + // running at all (that is, there's no garbage collection + // running right now). + mp->needextram = mp->schedlink == nil; + unlockextra(mp->schedlink); + + // Install m and g (= m->g0) and set the stack bounds + // to match the current stack. We don't actually know + // how big the stack is, like we don't know how big any + // scheduling stack is, but we assume there's at least 32 kB, + // which is more than enough for us. + runtime·setmg(mp, mp->g0); + g->stackbase = (uintptr)(&x + 1024); + g->stackguard = (uintptr)(&x - 32*1024); + + // On windows/386, we need to put an SEH frame (two words) + // somewhere on the current stack. We are called + // from needm, and we know there is some available + // space one word into the argument frame. Use that. + m->seh = (SEH*)((uintptr*)&x + 1); + + // Initialize this thread to use the m. + runtime·asminit(); + runtime·minit(); +} - // We're going to use one CPU no matter what. - // Figure out the max number of additional CPUs. - max--; +// newextram allocates an m and puts it on the extra list. +// It is called with a working local m, so that it can do things +// like call schedlock and allocate. +void +runtime·newextram(void) +{ + M *mp, *mnext; + G *gp; + // Create extra goroutine locked to extra m. + // The goroutine is the context in which the cgo callback will run. + // The sched.pc will never be returned to, but setting it to + // runtime.goexit makes clear to the traceback routines where + // the goroutine stack ends. + mp = runtime·allocm(nil); + gp = runtime·malg(4096); + gp->sched.pc = (void*)runtime·goexit; + gp->sched.sp = gp->stackbase; + gp->sched.g = gp; + gp->status = Gsyscall; + mp->curg = gp; + mp->locked = LockInternal; + mp->lockedg = gp; + gp->lockedm = mp; + // put on allg for garbage collector runtime·lock(&runtime·sched); - n = 0; - while(n < max && (mp = mget(nil)) != nil) { - n++; - mp->helpgc = 1; - mp->waitnextg = 0; - runtime·notewakeup(&mp->havenextg); - } + if(runtime·lastg == nil) + runtime·allg = gp; + else + runtime·lastg->alllink = gp; + runtime·lastg = gp; runtime·unlock(&runtime·sched); - if(extra) - *extra = n != max; - return n; + gp->goid = runtime·xadd64(&runtime·sched.goidgen, 1); + if(raceenabled) + gp->racectx = runtime·racegostart(runtime·newextram); + + // Add m to the extra list. + mnext = lockextra(true); + mp->schedlink = mnext; + unlockextra(mp); } +// dropm is called when a cgo callback has called needm but is now +// done with the callback and returning back into the non-Go thread. +// It puts the current m back onto the extra list. +// +// The main expense here is the call to signalstack to release the +// m's signal stack, and then the call to needm on the next callback +// from this thread. It is tempting to try to save the m for next time, +// which would eliminate both these costs, but there might not be +// a next time: the current thread (which Go does not control) might exit. +// If we saved the m for that thread, there would be an m leak each time +// such a thread exited. Instead, we acquire and release an m on each +// call. These should typically not be scheduling operations, just a few +// atomics, so the cost should be small. +// +// TODO(rsc): An alternative would be to allocate a dummy pthread per-thread +// variable using pthread_key_create. Unlike the pthread keys we already use +// on OS X, this dummy key would never be read by Go code. It would exist +// only so that we could register at thread-exit-time destructor. +// That destructor would put the m back onto the extra list. +// This is purely a performance optimization. The current version, +// in which dropm happens on each cgo call, is still correct too. +// We may have to keep the current version on systems with cgo +// but without pthreads, like Windows. void -runtime·stoptheworld(void) +runtime·dropm(void) { - uint32 v; + M *mp, *mnext; - schedlock(); - runtime·gcwaiting = 1; + // Undo whatever initialization minit did during needm. + runtime·unminit(); - setmcpumax(1); + // Clear m and g, and return m to the extra list. + // After the call to setmg we can only call nosplit functions. + mp = m; + runtime·setmg(nil, nil); - // while mcpu > 1 - for(;;) { - v = runtime·sched.atomic; - if(atomic_mcpu(v) <= 1) - break; + mnext = lockextra(true); + mp->schedlink = mnext; + unlockextra(mp); +} - // It would be unsafe for multiple threads to be using - // the stopped note at once, but there is only - // ever one thread doing garbage collection. - runtime·noteclear(&runtime·sched.stopped); - if(atomic_waitstop(v)) - runtime·throw("invalid waitstop"); +#define MLOCKED ((M*)1) - // atomic { waitstop = 1 }, predicated on mcpu <= 1 check above - // still being true. - if(!runtime·cas(&runtime·sched.atomic, v, v+(1<helpgc = 1; - runtime·sched.grunning++; - } - schedunlock(); + runtime·atomicstorep(&runtime·extram, mp); } -// Called to start an M. -void -runtime·mstart(void) + +// Create a new m. It will start off with a call to fn, or else the scheduler. +static void +newm(void(*fn)(void), P *p) { - if(g != m->g0) - runtime·throw("bad runtime·mstart"); + M *mp; - // Record top of stack for use by mcall. - // Once we call schedule we're never coming back, - // so other calls can reuse this stack space. - runtime·gosave(&m->g0->sched); - m->g0->sched.pc = (void*)-1; // make sure it is never used - runtime·asminit(); - runtime·minit(); + mp = runtime·allocm(p); + mp->nextp = p; + mp->mstartfn = fn; - // Install signal handlers; after minit so that minit can - // prepare the thread to be able to handle the signals. - if(m == &runtime·m0) - runtime·initsig(); + if(runtime·iscgo) { + CgoThreadStart ts; - schedule(nil); + if(_cgo_thread_start == nil) + runtime·throw("_cgo_thread_start missing"); + ts.m = mp; + ts.g = mp->g0; + ts.fn = runtime·mstart; + runtime·asmcgocall(_cgo_thread_start, &ts); + return; + } + runtime·newosproc(mp, (byte*)mp->g0->stackbase); } -// When running with cgo, we call libcgo_thread_start -// to start threads for us so that we can play nicely with -// foreign code. -void (*libcgo_thread_start)(void*); +// Stops execution of the current m until new work is available. +// Returns with acquired P. +static void +stopm(void) +{ + if(m->locks) + runtime·throw("stopm holding locks"); + if(m->p) + runtime·throw("stopm holding p"); + if(m->spinning) { + m->spinning = false; + runtime·xadd(&runtime·sched.nmspinning, -1); + } -typedef struct CgoThreadStart CgoThreadStart; -struct CgoThreadStart +retry: + runtime·lock(&runtime·sched); + mput(m); + runtime·unlock(&runtime·sched); + runtime·notesleep(&m->park); + runtime·noteclear(&m->park); + if(m->helpgc) { + m->helpgc = 0; + runtime·gchelper(); + m->mcache = nil; + goto retry; + } + acquirep(m->nextp); + m->nextp = nil; +} + +static void +mspinning(void) { - M *m; - G *g; - void (*fn)(void); -}; + m->spinning = true; +} -// Kick off new m's as needed (up to mcpumax). -// Sched is locked. +// Schedules some M to run the p (creates an M if necessary). +// If p==nil, tries to get an idle P, if no idle P's returns false. static void -matchmg(void) +startm(P *p, bool spinning) { - G *gp; M *mp; + void (*fn)(void); - if(m->mallocing || m->gcing) + runtime·lock(&runtime·sched); + if(p == nil) { + p = pidleget(); + if(p == nil) { + runtime·unlock(&runtime·sched); + if(spinning) + runtime·xadd(&runtime·sched.nmspinning, -1); + return; + } + } + mp = mget(); + runtime·unlock(&runtime·sched); + if(mp == nil) { + fn = nil; + if(spinning) + fn = mspinning; + newm(fn, p); return; - - while(haveg() && canaddmcpu()) { - gp = gget(); - if(gp == nil) - runtime·throw("gget inconsistency"); - - // Find the m that will run gp. - if((mp = mget(gp)) == nil) - mp = runtime·newm(); - mnextg(mp, gp); } + if(mp->spinning) + runtime·throw("startm: m is spinning"); + if(mp->nextp) + runtime·throw("startm: m has p"); + mp->spinning = spinning; + mp->nextp = p; + runtime·notewakeup(&mp->park); } -// Create a new m. It will start off with a call to runtime·mstart. -M* -runtime·newm(void) +// Hands off P from syscall or locked M. +static void +handoffp(P *p) { - M *m; + // if it has local work, start it straight away + if(p->runqhead != p->runqtail || runtime·sched.runqsize) { + startm(p, false); + return; + } + // no local work, check that there are no spinning/idle M's, + // otherwise our help is not required + if(runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) == 0 && // TODO: fast atomic + runtime·cas(&runtime·sched.nmspinning, 0, 1)) { + startm(p, true); + return; + } + runtime·lock(&runtime·sched); + if(runtime·gcwaiting) { + p->status = Pgcstop; + if(--runtime·sched.stopwait == 0) + runtime·notewakeup(&runtime·sched.stopnote); + runtime·unlock(&runtime·sched); + return; + } + if(runtime·sched.runqsize) { + runtime·unlock(&runtime·sched); + startm(p, false); + return; + } + pidleput(p); + runtime·unlock(&runtime·sched); +} - m = runtime·malloc(sizeof(M)); - mcommoninit(m); +// Tries to add one more P to execute G's. +// Called when a G is made runnable (newproc, ready). +static void +wakep(void) +{ + // be conservative about spinning threads + if(!runtime·cas(&runtime·sched.nmspinning, 0, 1)) + return; + startm(nil, true); +} - if(runtime·iscgo) { - CgoThreadStart ts; +// Stops execution of the current m that is locked to a g until the g is runnable again. +// Returns with acquired P. +static void +stoplockedm(void) +{ + P *p; - if(libcgo_thread_start == nil) - runtime·throw("libcgo_thread_start missing"); - // pthread_create will make us a stack. - m->g0 = runtime·malg(-1); - ts.m = m; - ts.g = m->g0; - ts.fn = runtime·mstart; - runtime·asmcgocall(libcgo_thread_start, &ts); - } else { - if(Windows) - // windows will layout sched stack on os stack - m->g0 = runtime·malg(-1); - else - m->g0 = runtime·malg(8192); - runtime·newosproc(m, m->g0, m->g0->stackbase, runtime·mstart); + if(m->lockedg == nil || m->lockedg->lockedm != m) + runtime·throw("stoplockedm: inconsistent locking"); + if(m->p) { + // Schedule another M to run this p. + p = releasep(); + handoffp(p); } + inclocked(1); + // Wait until another thread schedules lockedg again. + runtime·notesleep(&m->park); + runtime·noteclear(&m->park); + if(m->lockedg->status != Grunnable) + runtime·throw("stoplockedm: not runnable"); + acquirep(m->nextp); + m->nextp = nil; +} - return m; +// Schedules the locked m to run the locked gp. +static void +startlockedm(G *gp) +{ + M *mp; + P *p; + + mp = gp->lockedm; + if(mp == m) + runtime·throw("startlockedm: locked to me"); + if(mp->nextp) + runtime·throw("startlockedm: m has p"); + // directly handoff current P to the locked m + inclocked(-1); + p = releasep(); + mp->nextp = p; + runtime·notewakeup(&mp->park); + stopm(); +} + +// Stops the current m for stoptheworld. +// Returns when the world is restarted. +static void +gcstopm(void) +{ + P *p; + + if(!runtime·gcwaiting) + runtime·throw("gcstopm: not waiting for gc"); + if(m->spinning) { + m->spinning = false; + runtime·xadd(&runtime·sched.nmspinning, -1); + } + p = releasep(); + runtime·lock(&runtime·sched); + p->status = Pgcstop; + if(--runtime·sched.stopwait == 0) + runtime·notewakeup(&runtime·sched.stopnote); + runtime·unlock(&runtime·sched); + stopm(); } -// One round of scheduler: find a goroutine and run it. -// The argument is the goroutine that was running before -// schedule was called, or nil if this is the first call. +// Schedules gp to run on the current M. // Never returns. static void -schedule(G *gp) +execute(G *gp) { int32 hz; - uint32 v; - - schedlock(); - if(gp != nil) { - // Just finished running gp. - gp->m = nil; - runtime·sched.grunning--; - - // atomic { mcpu-- } - v = runtime·xadd(&runtime·sched.atomic, -1< maxgomaxprocs) - runtime·throw("negative mcpu in scheduler"); - - switch(gp->status){ - case Grunnable: - case Gdead: - // Shouldn't have been running! - runtime·throw("bad gp->status in sched"); - case Grunning: - gp->status = Grunnable; - gput(gp); - break; - case Gmoribund: - gp->status = Gdead; - if(gp->lockedm) { - gp->lockedm = nil; - m->lockedg = nil; - } - gp->idlem = nil; - unwindstack(gp, nil); - gfput(gp); - if(--runtime·sched.gcount == 0) - runtime·exit(0); - break; - } - if(gp->readyonstop){ - gp->readyonstop = 0; - readylocked(gp); - } - } else if(m->helpgc) { - // Bootstrap m or new m started by starttheworld. - // atomic { mcpu-- } - v = runtime·xadd(&runtime·sched.atomic, -1< maxgomaxprocs) - runtime·throw("negative mcpu in scheduler"); - // Compensate for increment in starttheworld(). - runtime·sched.grunning--; - m->helpgc = 0; - } else if(m->nextg != nil) { - // New m started by matchmg. - } else { - runtime·throw("invalid m state in scheduler"); - } - // Find (or wait for) g to run. Unlocks runtime·sched. - gp = nextgandunlock(); - gp->readyonstop = 0; + if(gp->status != Grunnable) { + runtime·printf("execute: bad g status %d\n", gp->status); + runtime·throw("execute: bad g status"); + } gp->status = Grunning; + m->p->tick++; m->curg = gp; gp->m = m; @@ -904,27 +964,209 @@ schedule(G *gp) if(m->profilehz != hz) runtime·resetcpuprofiler(hz); - if(gp->sched.pc == (byte*)runtime·goexit) { // kickoff - runtime·gogocall(&gp->sched, (void(*)(void))gp->entry); - } + if(gp->sched.pc == (byte*)runtime·goexit) // kickoff + runtime·gogocallfn(&gp->sched, gp->fnstart); runtime·gogo(&gp->sched, 0); } -// Enter scheduler. If g->status is Grunning, -// re-queues g and runs everyone else who is waiting -// before running g again. If g->status is Gmoribund, -// kills off g. -// Cannot split stack because it is called from exitsyscall. -// See comment below. -#pragma textflag 7 +// Finds a runnable goroutine to execute. +// Tries to steal from other P's and get g from global queue. +static G* +findrunnable(void) +{ + G *gp; + P *p; + int32 i; + +top: + if(runtime·gcwaiting) { + gcstopm(); + goto top; + } + // local runq + gp = runqget(m->p); + if(gp) + return gp; + // global runq + if(runtime·sched.runqsize) { + runtime·lock(&runtime·sched); + gp = globrunqget(m->p); + runtime·unlock(&runtime·sched); + if(gp) + return gp; + } + // If number of spinning M's >= number of busy P's, block. + // This is necessary to prevent excessive CPU consumption + // when GOMAXPROCS>>1 but the program parallelism is low. + if(!m->spinning && 2 * runtime·atomicload(&runtime·sched.nmspinning) >= runtime·gomaxprocs - runtime·atomicload(&runtime·sched.npidle)) // TODO: fast atomic + goto stop; + if(!m->spinning) { + m->spinning = true; + runtime·xadd(&runtime·sched.nmspinning, 1); + } + // random steal from other P's + for(i = 0; i < 2*runtime·gomaxprocs; i++) { + if(runtime·gcwaiting) + goto top; + p = runtime·allp[runtime·fastrand1()%runtime·gomaxprocs]; + if(p == m->p) + gp = runqget(p); + else + gp = runqsteal(m->p, p); + if(gp) + return gp; + } +stop: + // return P and block + runtime·lock(&runtime·sched); + if(runtime·gcwaiting) { + runtime·unlock(&runtime·sched); + goto top; + } + if(runtime·sched.runqsize) { + gp = globrunqget(m->p); + runtime·unlock(&runtime·sched); + return gp; + } + p = releasep(); + pidleput(p); + runtime·unlock(&runtime·sched); + if(m->spinning) { + m->spinning = false; + runtime·xadd(&runtime·sched.nmspinning, -1); + } + // check all runqueues once again + for(i = 0; i < runtime·gomaxprocs; i++) { + p = runtime·allp[i]; + if(p && p->runqhead != p->runqtail) { + runtime·lock(&runtime·sched); + p = pidleget(); + runtime·unlock(&runtime·sched); + if(p) { + acquirep(p); + goto top; + } + break; + } + } + stopm(); + goto top; +} + +// One round of scheduler: find a runnable goroutine and execute it. +// Never returns. +static void +schedule(void) +{ + G *gp; + + if(m->locks) + runtime·throw("schedule: holding locks"); + +top: + if(runtime·gcwaiting) { + gcstopm(); + goto top; + } + + gp = runqget(m->p); + if(gp == nil) + gp = findrunnable(); + + if(m->spinning) { + m->spinning = false; + runtime·xadd(&runtime·sched.nmspinning, -1); + } + + // M wakeup policy is deliberately somewhat conservative (see nmspinning handling), + // so see if we need to wakeup another M here. + if (m->p->runqhead != m->p->runqtail && + runtime·atomicload(&runtime·sched.nmspinning) == 0 && + runtime·atomicload(&runtime·sched.npidle) > 0) // TODO: fast atomic + wakep(); + + if(gp->lockedm) { + startlockedm(gp); + goto top; + } + + execute(gp); +} + +// Puts the current goroutine into a waiting state and unlocks the lock. +// The goroutine can be made runnable again by calling runtime·ready(gp). +void +runtime·park(void(*unlockf)(Lock*), Lock *lock, int8 *reason) +{ + m->waitlock = lock; + m->waitunlockf = unlockf; + g->waitreason = reason; + runtime·mcall(park0); +} + +// runtime·park continuation on g0. +static void +park0(G *gp) +{ + gp->status = Gwaiting; + gp->m = nil; + m->curg = nil; + if(m->waitunlockf) { + m->waitunlockf(m->waitlock); + m->waitunlockf = nil; + } + if(m->lockedg) { + stoplockedm(); + execute(gp); // Never returns. + } + schedule(); +} + +// Scheduler yield. void runtime·gosched(void) { - if(m->locks != 0) - runtime·throw("gosched holding locks"); - if(g == m->g0) - runtime·throw("gosched of g0"); - runtime·mcall(schedule); + runtime·mcall(gosched0); +} + +// runtime·gosched continuation on g0. +static void +gosched0(G *gp) +{ + gp->status = Grunnable; + gp->m = nil; + m->curg = nil; + runtime·lock(&runtime·sched); + globrunqput(gp); + runtime·unlock(&runtime·sched); + if(m->lockedg) { + stoplockedm(); + execute(gp); // Never returns. + } + schedule(); +} + +// Finishes execution of the current goroutine. +void +runtime·goexit(void) +{ + if(raceenabled) + runtime·racegoend(); + runtime·mcall(goexit0); +} + +// runtime·goexit continuation on g0. +static void +goexit0(G *gp) +{ + gp->status = Gdead; + gp->m = nil; + gp->lockedm = nil; + m->curg = nil; + m->lockedg = nil; + runtime·unwindstack(gp, nil); + gfput(m->p, gp); + schedule(); } // The goroutine g is about to enter a system call. @@ -935,21 +1177,19 @@ runtime·gosched(void) // Entersyscall cannot split the stack: the runtime·gosave must // make g->sched refer to the caller's stack segment, because // entersyscall is going to return immediately after. -// It's okay to call matchmg and notewakeup even after -// decrementing mcpu, because we haven't released the -// sched lock yet, so the garbage collector cannot be running. #pragma textflag 7 void -runtime·entersyscall(void) +·entersyscall(int32 dummy) { - uint32 v; - if(m->profilehz > 0) runtime·setprof(false); // Leave SP around for gc and traceback. - runtime·gosave(&g->sched); + g->sched.sp = (uintptr)runtime·getcallersp(&dummy); + g->sched.pc = runtime·getcallerpc(&dummy); + g->sched.g = g; g->gcsp = g->sched.sp; + g->gcpc = g->sched.pc; g->gcstack = g->stackbase; g->gcguard = g->stackguard; g->status = Gsyscall; @@ -959,34 +1199,61 @@ runtime·entersyscall(void) runtime·throw("entersyscall"); } - // Fast path. - // The slow path inside the schedlock/schedunlock will get - // through without stopping if it does: - // mcpu-- - // gwait not true - // waitstop && mcpu <= mcpumax not true - // If we can do the same with a single atomic add, - // then we can skip the locks. - v = runtime·xadd(&runtime·sched.atomic, -1< atomic_mcpumax(v))) - return; - - schedlock(); - v = runtime·atomicload(&runtime·sched.atomic); - if(atomic_gwaiting(v)) { - matchmg(); - v = runtime·atomicload(&runtime·sched.atomic); + if(runtime·atomicload(&runtime·sched.sysmonwait)) { // TODO: fast atomic + runtime·lock(&runtime·sched); + if(runtime·atomicload(&runtime·sched.sysmonwait)) { + runtime·atomicstore(&runtime·sched.sysmonwait, 0); + runtime·notewakeup(&runtime·sched.sysmonnote); + } + runtime·unlock(&runtime·sched); + runtime·gosave(&g->sched); // re-save for traceback } - if(atomic_waitstop(v) && atomic_mcpu(v) <= atomic_mcpumax(v)) { - runtime·xadd(&runtime·sched.atomic, -1<mcache = nil; + m->p->tick++; + m->p->m = nil; + runtime·atomicstore(&m->p->status, Psyscall); + if(runtime·gcwaiting) { + runtime·lock(&runtime·sched); + if (runtime·sched.stopwait > 0 && runtime·cas(&m->p->status, Psyscall, Pgcstop)) { + if(--runtime·sched.stopwait == 0) + runtime·notewakeup(&runtime·sched.stopnote); + } + runtime·unlock(&runtime·sched); + runtime·gosave(&g->sched); // re-save for traceback } +} + +// The same as runtime·entersyscall(), but with a hint that the syscall is blocking. +#pragma textflag 7 +void +·entersyscallblock(int32 dummy) +{ + P *p; - // Re-save sched in case one of the calls - // (notewakeup, matchmg) triggered something using it. - runtime·gosave(&g->sched); + if(m->profilehz > 0) + runtime·setprof(false); - schedunlock(); + // Leave SP around for gc and traceback. + g->sched.sp = (uintptr)runtime·getcallersp(&dummy); + g->sched.pc = runtime·getcallerpc(&dummy); + g->sched.g = g; + g->gcsp = g->sched.sp; + g->gcpc = g->sched.pc; + g->gcstack = g->stackbase; + g->gcguard = g->stackguard; + g->status = Gsyscall; + if(g->gcsp < g->gcguard-StackGuard || g->gcstack < g->gcsp) { + // runtime·printf("entersyscallblock inconsistent %p [%p,%p]\n", + // g->gcsp, g->gcguard-StackGuard, g->gcstack); + runtime·throw("entersyscallblock"); + } + + p = releasep(); + handoffp(p); + if(g == scvg) // do not consider blocked scavenger for deadlock detection + inclocked(1); + runtime·gosave(&g->sched); // re-save for traceback } // The goroutine g exited its system call. @@ -996,177 +1263,81 @@ runtime·entersyscall(void) void runtime·exitsyscall(void) { - uint32 v; + P *p; - // Fast path. - // If we can do the mcpu++ bookkeeping and - // find that we still have mcpu <= mcpumax, then we can - // start executing Go code immediately, without having to - // schedlock/schedunlock. - v = runtime·xadd(&runtime·sched.atomic, (1<profilehz == runtime·sched.profilehz && atomic_mcpu(v) <= atomic_mcpumax(v)) { + // Check whether the profiler needs to be turned on. + if(m->profilehz > 0) + runtime·setprof(true); + + // Try to re-acquire the last P. + if(m->p && m->p->status == Psyscall && runtime·cas(&m->p->status, Psyscall, Prunning)) { // There's a cpu for us, so we can run. + m->mcache = m->p->mcache; + m->p->m = m; + m->p->tick++; g->status = Grunning; // Garbage collector isn't running (since we are), - // so okay to clear gcstack. - g->gcstack = nil; - - if(m->profilehz > 0) - runtime·setprof(true); + // so okay to clear gcstack and gcsp. + g->gcstack = (uintptr)nil; + g->gcsp = (uintptr)nil; return; } - // Tell scheduler to put g back on the run queue: - // mostly equivalent to g->status = Grunning, - // but keeps the garbage collector from thinking - // that g is running right now, which it's not. - g->readyonstop = 1; + if(g == scvg) // do not consider blocked scavenger for deadlock detection + inclocked(-1); + // Try to get any other idle P. + m->p = nil; + if(runtime·sched.pidle) { + runtime·lock(&runtime·sched); + p = pidleget(); + runtime·unlock(&runtime·sched); + if(p) { + acquirep(p); + g->gcstack = (uintptr)nil; + g->gcsp = (uintptr)nil; + return; + } + } + + // Call the scheduler. + runtime·mcall(exitsyscall0); - // All the cpus are taken. - // The scheduler will ready g and put this m to sleep. - // When the scheduler takes g away from m, - // it will undo the runtime·sched.mcpu++ above. - runtime·gosched(); - - // Gosched returned, so we're allowed to run now. + // Scheduler returned, so we're allowed to run now. // Delete the gcstack information that we left for // the garbage collector during the system call. // Must wait until now because until gosched returns // we don't know for sure that the garbage collector // is not running. - g->gcstack = nil; + g->gcstack = (uintptr)nil; + g->gcsp = (uintptr)nil; } -// Called from runtime·lessstack when returning from a function which -// allocated a new stack segment. The function's return value is in -// m->cret. -void -runtime·oldstack(void) +// runtime·exitsyscall slow path on g0. +// Failed to acquire P, enqueue gp as runnable. +static void +exitsyscall0(G *gp) { - Stktop *top, old; - uint32 argsize; - uintptr cret; - byte *sp; - G *g1; - int32 goid; - -//printf("oldstack m->cret=%p\n", m->cret); - - g1 = m->curg; - top = (Stktop*)g1->stackbase; - sp = (byte*)top; - old = *top; - argsize = old.argsize; - if(argsize > 0) { - sp -= argsize; - runtime·memmove(top->argp, sp, argsize); - } - goid = old.gobuf.g->goid; // fault if g is bad, before gogo - USED(goid); - - if(old.free != 0) - runtime·stackfree(g1->stackguard - StackGuard, old.free); - g1->stackbase = old.stackbase; - g1->stackguard = old.stackguard; - - cret = m->cret; - m->cret = 0; // drop reference - runtime·gogo(&old.gobuf, cret); -} - -// Called from reflect·call or from runtime·morestack when a new -// stack segment is needed. Allocate a new stack big enough for -// m->moreframesize bytes, copy m->moreargsize bytes to the new frame, -// and then act as though runtime·lessstack called the function at -// m->morepc. -void -runtime·newstack(void) -{ - int32 framesize, argsize; - Stktop *top; - byte *stk, *sp; - G *g1; - Gobuf label; - bool reflectcall; - uintptr free; - - framesize = m->moreframesize; - argsize = m->moreargsize; - g1 = m->curg; - - if(m->morebuf.sp < g1->stackguard - StackGuard) { - runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, g1->stackguard - StackGuard); - runtime·throw("runtime: split stack overflow"); - } - if(argsize % sizeof(uintptr) != 0) { - runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize); - runtime·throw("runtime: stack split argsize"); - } - - reflectcall = framesize==1; - if(reflectcall) - framesize = 0; - - if(reflectcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > g1->stackguard) { - // special case: called from reflect.call (framesize==1) - // to call code with an arbitrary argument size, - // and we have enough space on the current stack. - // the new Stktop* is necessary to unwind, but - // we don't need to create a new segment. - top = (Stktop*)(m->morebuf.sp - sizeof(*top)); - stk = g1->stackguard - StackGuard; - free = 0; - } else { - // allocate new segment. - framesize += argsize; - framesize += StackExtra; // room for more functions, Stktop. - if(framesize < StackMin) - framesize = StackMin; - framesize += StackSystem; - stk = runtime·stackalloc(framesize); - top = (Stktop*)(stk+framesize-sizeof(*top)); - free = framesize; - } - -//runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n", -//framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, g1->stackbase); - - top->stackbase = g1->stackbase; - top->stackguard = g1->stackguard; - top->gobuf = m->morebuf; - top->argp = m->moreargp; - top->argsize = argsize; - top->free = free; - m->moreargp = nil; - m->morebuf.pc = nil; - m->morebuf.sp = nil; - - // copy flag from panic - top->panic = g1->ispanic; - g1->ispanic = false; - - g1->stackbase = (byte*)top; - g1->stackguard = stk + StackGuard; - - sp = (byte*)top; - if(argsize > 0) { - sp -= argsize; - runtime·memmove(sp, top->argp, argsize); + P *p; + + gp->status = Grunnable; + gp->m = nil; + m->curg = nil; + runtime·lock(&runtime·sched); + p = pidleget(); + if(p == nil) + globrunqput(gp); + runtime·unlock(&runtime·sched); + if(p) { + acquirep(p); + execute(gp); // Never returns. } - if(thechar == '5') { - // caller would have saved its LR below args. - sp -= sizeof(void*); - *(void**)sp = nil; + if(m->lockedg) { + // Wait until another thread schedules gp and so m again. + stoplockedm(); + execute(gp); // Never returns. } - - // Continue as if lessstack had just called m->morepc - // (the PC that decided to grow the stack). - label.sp = sp; - label.pc = (byte*)runtime·lessstack; - label.g = m->curg; - runtime·gogocall(&label, m->morepc); - - *(int32*)345 = 123; // never return + stopm(); + schedule(); // Never returns. } // Hook used by runtime·malg to call runtime·stackalloc on the @@ -1204,10 +1375,10 @@ runtime·malg(int32 stacksize) stk = g->param; g->param = nil; } - newg->stack0 = stk; - newg->stackguard = stk + StackGuard; - newg->stackbase = stk + StackSystem + stacksize - sizeof(Stktop); - runtime·memclr(newg->stackbase, sizeof(Stktop)); + newg->stack0 = (uintptr)stk; + newg->stackguard = (uintptr)stk + StackGuard; + newg->stackbase = (uintptr)stk + StackSystem + stacksize - sizeof(Stktop); + runtime·memclr((byte*)newg->stackbase, sizeof(Stktop)); } return newg; } @@ -1221,7 +1392,7 @@ runtime·malg(int32 stacksize) // functions that split the stack. #pragma textflag 7 void -runtime·newproc(int32 siz, byte* fn, ...) +runtime·newproc(int32 siz, FuncVal* fn, ...) { byte *argp; @@ -1237,7 +1408,7 @@ runtime·newproc(int32 siz, byte* fn, ...) // address of the go statement that created this. The new g is put // on the queue of g's waiting to run. G* -runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc) +runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerpc) { byte *sp; G *newg; @@ -1254,23 +1425,21 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc) if(siz > StackMin - 1024) runtime·throw("runtime.newproc: function arguments too large for new goroutine"); - schedlock(); - - if((newg = gfget()) != nil){ + if((newg = gfget(m->p)) != nil) { if(newg->stackguard - StackGuard != newg->stack0) runtime·throw("invalid stack in newg"); } else { newg = runtime·malg(StackMin); + runtime·lock(&runtime·sched); if(runtime·lastg == nil) runtime·allg = newg; else runtime·lastg->alllink = newg; runtime·lastg = newg; + runtime·unlock(&runtime·sched); } - newg->status = Gwaiting; - newg->waitreason = "new goroutine"; - sp = newg->stackbase; + sp = (byte*)newg->stackbase; sp -= siz; runtime·memmove(sp, argp, narg); if(thechar == '5') { @@ -1279,318 +1448,88 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc) *(void**)sp = nil; } - newg->sched.sp = sp; + newg->sched.sp = (uintptr)sp; newg->sched.pc = (byte*)runtime·goexit; newg->sched.g = newg; - newg->entry = fn; + newg->fnstart = fn; newg->gopc = (uintptr)callerpc; - - runtime·sched.gcount++; - runtime·sched.goidgen++; - newg->goid = runtime·sched.goidgen; - - newprocreadylocked(newg); - schedunlock(); - + newg->status = Grunnable; + newg->goid = runtime·xadd64(&runtime·sched.goidgen, 1); + if(raceenabled) + newg->racectx = runtime·racegostart(callerpc); + runqput(m->p, newg); + + if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0 && fn->fn != runtime·main) // TODO: fast atomic + wakep(); return newg; -//printf(" goid=%d\n", newg->goid); -} - -// Create a new deferred function fn with siz bytes of arguments. -// The compiler turns a defer statement into a call to this. -// Cannot split the stack because it assumes that the arguments -// are available sequentially after &fn; they would not be -// copied if a stack split occurred. It's OK for this to call -// functions that split the stack. -#pragma textflag 7 -uintptr -runtime·deferproc(int32 siz, byte* fn, ...) -{ - Defer *d; - - d = runtime·malloc(sizeof(*d) + siz - sizeof(d->args)); - d->fn = fn; - d->siz = siz; - d->pc = runtime·getcallerpc(&siz); - if(thechar == '5') - d->argp = (byte*)(&fn+2); // skip caller's saved link register - else - d->argp = (byte*)(&fn+1); - runtime·memmove(d->args, d->argp, d->siz); - - d->link = g->defer; - g->defer = d; - - // deferproc returns 0 normally. - // a deferred func that stops a panic - // makes the deferproc return 1. - // the code the compiler generates always - // checks the return value and jumps to the - // end of the function if deferproc returns != 0. - return 0; -} - -// Run a deferred function if there is one. -// The compiler inserts a call to this at the end of any -// function which calls defer. -// If there is a deferred function, this will call runtime·jmpdefer, -// which will jump to the deferred function such that it appears -// to have been called by the caller of deferreturn at the point -// just before deferreturn was called. The effect is that deferreturn -// is called again and again until there are no more deferred functions. -// Cannot split the stack because we reuse the caller's frame to -// call the deferred function. -#pragma textflag 7 -void -runtime·deferreturn(uintptr arg0) -{ - Defer *d; - byte *argp, *fn; - - d = g->defer; - if(d == nil) - return; - argp = (byte*)&arg0; - if(d->argp != argp) - return; - runtime·memmove(argp, d->args, d->siz); - g->defer = d->link; - fn = d->fn; - if(!d->nofree) - runtime·free(d); - runtime·jmpdefer(fn, argp); } -// Run all deferred functions for the current goroutine. +// Put on gfree list. +// If local list is too long, transfer a batch to the global list. static void -rundefer(void) +gfput(P *p, G *gp) { - Defer *d; - - while((d = g->defer) != nil) { - g->defer = d->link; - reflect·call(d->fn, d->args, d->siz); - if(!d->nofree) - runtime·free(d); + if(gp->stackguard - StackGuard != gp->stack0) + runtime·throw("invalid stack in gfput"); + gp->schedlink = p->gfree; + p->gfree = gp; + p->gfreecnt++; + if(p->gfreecnt >= 64) { + runtime·lock(&runtime·sched.gflock); + while(p->gfreecnt >= 32) { + p->gfreecnt--; + gp = p->gfree; + p->gfree = gp->schedlink; + gp->schedlink = runtime·sched.gfree; + runtime·sched.gfree = gp; + } + runtime·unlock(&runtime·sched.gflock); } } -// Free stack frames until we hit the last one -// or until we find the one that contains the argp. -static void -unwindstack(G *gp, byte *sp) +// Get from gfree list. +// If local list is empty, grab a batch from global list. +static G* +gfget(P *p) { - Stktop *top; - byte *stk; - - // Must be called from a different goroutine, usually m->g0. - if(g == gp) - runtime·throw("unwindstack on self"); + G *gp; - while((top = (Stktop*)gp->stackbase) != nil && top->stackbase != nil) { - stk = gp->stackguard - StackGuard; - if(stk <= sp && sp < gp->stackbase) - break; - gp->stackbase = top->stackbase; - gp->stackguard = top->stackguard; - if(top->free != 0) - runtime·stackfree(stk, top->free); +retry: + gp = p->gfree; + if(gp == nil && runtime·sched.gfree) { + runtime·lock(&runtime·sched.gflock); + while(p->gfreecnt < 32 && runtime·sched.gfree) { + p->gfreecnt++; + gp = runtime·sched.gfree; + runtime·sched.gfree = gp->schedlink; + gp->schedlink = p->gfree; + p->gfree = gp; + } + runtime·unlock(&runtime·sched.gflock); + goto retry; } - - if(sp != nil && (sp < gp->stackguard - StackGuard || gp->stackbase < sp)) { - runtime·printf("recover: %p not in [%p, %p]\n", sp, gp->stackguard - StackGuard, gp->stackbase); - runtime·throw("bad unwindstack"); + if(gp) { + p->gfree = gp->schedlink; + p->gfreecnt--; } + return gp; } -// Print all currently active panics. Used when crashing. +// Purge all cached G's from gfree list to the global list. static void -printpanics(Panic *p) -{ - if(p->link) { - printpanics(p->link); - runtime·printf("\t"); - } - runtime·printf("panic: "); - runtime·printany(p->arg); - if(p->recovered) - runtime·printf(" [recovered]"); - runtime·printf("\n"); -} - -static void recovery(G*); - -// The implementation of the predeclared function panic. -void -runtime·panic(Eface e) +gfpurge(P *p) { - Defer *d; - Panic *p; - - p = runtime·mal(sizeof *p); - p->arg = e; - p->link = g->panic; - p->stackbase = g->stackbase; - g->panic = p; + G *gp; - for(;;) { - d = g->defer; - if(d == nil) - break; - // take defer off list in case of recursive panic - g->defer = d->link; - g->ispanic = true; // rock for newstack, where reflect.call ends up - reflect·call(d->fn, d->args, d->siz); - if(p->recovered) { - g->panic = p->link; - if(g->panic == nil) // must be done with signal - g->sig = 0; - runtime·free(p); - // put recovering defer back on list - // for scheduler to find. - d->link = g->defer; - g->defer = d; - runtime·mcall(recovery); - runtime·throw("recovery failed"); // mcall should not return - } - if(!d->nofree) - runtime·free(d); + runtime·lock(&runtime·sched.gflock); + while(p->gfreecnt) { + p->gfreecnt--; + gp = p->gfree; + p->gfree = gp->schedlink; + gp->schedlink = runtime·sched.gfree; + runtime·sched.gfree = gp; } - - // ran out of deferred calls - old-school panic now - runtime·startpanic(); - printpanics(g->panic); - runtime·dopanic(0); -} - -// Unwind the stack after a deferred function calls recover -// after a panic. Then arrange to continue running as though -// the caller of the deferred function returned normally. -static void -recovery(G *gp) -{ - Defer *d; - - // Rewind gp's stack; we're running on m->g0's stack. - d = gp->defer; - gp->defer = d->link; - - // Unwind to the stack frame with d's arguments in it. - unwindstack(gp, d->argp); - - // Make the deferproc for this d return again, - // this time returning 1. The calling function will - // jump to the standard return epilogue. - // The -2*sizeof(uintptr) makes up for the - // two extra words that are on the stack at - // each call to deferproc. - // (The pc we're returning to does pop pop - // before it tests the return value.) - // On the arm there are 2 saved LRs mixed in too. - if(thechar == '5') - gp->sched.sp = (byte*)d->argp - 4*sizeof(uintptr); - else - gp->sched.sp = (byte*)d->argp - 2*sizeof(uintptr); - gp->sched.pc = d->pc; - if(!d->nofree) - runtime·free(d); - runtime·gogo(&gp->sched, 1); -} - -// The implementation of the predeclared function recover. -// Cannot split the stack because it needs to reliably -// find the stack segment of its caller. -#pragma textflag 7 -void -runtime·recover(byte *argp, Eface ret) -{ - Stktop *top, *oldtop; - Panic *p; - - // Must be a panic going on. - if((p = g->panic) == nil || p->recovered) - goto nomatch; - - // Frame must be at the top of the stack segment, - // because each deferred call starts a new stack - // segment as a side effect of using reflect.call. - // (There has to be some way to remember the - // variable argument frame size, and the segment - // code already takes care of that for us, so we - // reuse it.) - // - // As usual closures complicate things: the fp that - // the closure implementation function claims to have - // is where the explicit arguments start, after the - // implicit pointer arguments and PC slot. - // If we're on the first new segment for a closure, - // then fp == top - top->args is correct, but if - // the closure has its own big argument frame and - // allocated a second segment (see below), - // the fp is slightly above top - top->args. - // That condition can't happen normally though - // (stack pointers go down, not up), so we can accept - // any fp between top and top - top->args as - // indicating the top of the segment. - top = (Stktop*)g->stackbase; - if(argp < (byte*)top - top->argsize || (byte*)top < argp) - goto nomatch; - - // The deferred call makes a new segment big enough - // for the argument frame but not necessarily big - // enough for the function's local frame (size unknown - // at the time of the call), so the function might have - // made its own segment immediately. If that's the - // case, back top up to the older one, the one that - // reflect.call would have made for the panic. - // - // The fp comparison here checks that the argument - // frame that was copied during the split (the top->args - // bytes above top->fp) abuts the old top of stack. - // This is a correct test for both closure and non-closure code. - oldtop = (Stktop*)top->stackbase; - if(oldtop != nil && top->argp == (byte*)oldtop - top->argsize) - top = oldtop; - - // Now we have the segment that was created to - // run this call. It must have been marked as a panic segment. - if(!top->panic) - goto nomatch; - - // Okay, this is the top frame of a deferred call - // in response to a panic. It can see the panic argument. - p->recovered = 1; - ret = p->arg; - FLUSH(&ret); - return; - -nomatch: - ret.type = nil; - ret.data = nil; - FLUSH(&ret); -} - - -// Put on gfree list. Sched must be locked. -static void -gfput(G *g) -{ - if(g->stackguard - StackGuard != g->stack0) - runtime·throw("invalid stack in gfput"); - g->schedlink = runtime·sched.gfree; - runtime·sched.gfree = g; -} - -// Get from gfree list. Sched must be locked. -static G* -gfget(void) -{ - G *g; - - g = runtime·sched.gfree; - if(g) - runtime·sched.gfree = g->schedlink; - return g; + runtime·unlock(&runtime·sched.gflock); } void @@ -1599,13 +1538,6 @@ runtime·Breakpoint(void) runtime·breakpoint(); } -void -runtime·Goexit(void) -{ - rundefer(); - runtime·goexit(); -} - void runtime·Gosched(void) { @@ -1613,67 +1545,79 @@ runtime·Gosched(void) } // Implementation of runtime.GOMAXPROCS. -// delete when scheduler is stronger +// delete when scheduler is even stronger int32 runtime·gomaxprocsfunc(int32 n) { int32 ret; - uint32 v; - schedlock(); + if(n > MaxGomaxprocs) + n = MaxGomaxprocs; + runtime·lock(&runtime·sched); ret = runtime·gomaxprocs; - if(n <= 0) - n = ret; - if(n > maxgomaxprocs) - n = maxgomaxprocs; - runtime·gomaxprocs = n; - if(runtime·gomaxprocs > 1) - runtime·singleproc = false; - if(runtime·gcwaiting != 0) { - if(atomic_mcpumax(runtime·sched.atomic) != 1) - runtime·throw("invalid mcpumax during gc"); - schedunlock(); + if(n <= 0 || n == ret) { + runtime·unlock(&runtime·sched); return ret; } + runtime·unlock(&runtime·sched); - setmcpumax(n); + runtime·semacquire(&runtime·worldsema); + m->gcing = 1; + runtime·stoptheworld(); + newprocs = n; + m->gcing = 0; + runtime·semrelease(&runtime·worldsema); + runtime·starttheworld(); - // If there are now fewer allowed procs - // than procs running, stop. - v = runtime·atomicload(&runtime·sched.atomic); - if(atomic_mcpu(v) > n) { - schedunlock(); - runtime·gosched(); - return ret; - } - // handle more procs - matchmg(); - schedunlock(); return ret; } -void -runtime·LockOSThread(void) +static void +LockOSThread(void) { - if(m == &runtime·m0 && runtime·sched.init) { - runtime·sched.lockmain = true; - return; - } m->lockedg = g; g->lockedm = m; } void -runtime·UnlockOSThread(void) +runtime·LockOSThread(void) +{ + m->locked |= LockExternal; + LockOSThread(); +} + +void +runtime·lockOSThread(void) { - if(m == &runtime·m0 && runtime·sched.init) { - runtime·sched.lockmain = false; + m->locked += LockInternal; + LockOSThread(); +} + +static void +UnlockOSThread(void) +{ + if(m->locked != 0) return; - } m->lockedg = nil; g->lockedm = nil; } +void +runtime·UnlockOSThread(void) +{ + m->locked &= ~LockExternal; + UnlockOSThread(); +} + +void +runtime·unlockOSThread(void) +{ + if(m->locked < LockInternal) + runtime·throw("runtime: internal error: misuse of lockOSThread/unlockOSThread"); + m->locked -= LockInternal; + UnlockOSThread(); +} + bool runtime·lockedOSThread(void) { @@ -1697,16 +1641,31 @@ runtime·mid(uint32 ret) } void -runtime·NumGoroutine(int32 ret) +runtime·NumGoroutine(intgo ret) { - ret = runtime·sched.gcount; + ret = runtime·gcount(); FLUSH(&ret); } int32 runtime·gcount(void) { - return runtime·sched.gcount; + G *gp; + int32 n, s; + + n = 0; + runtime·lock(&runtime·sched); + // TODO(dvyukov): runtime.NumGoroutine() is O(N). + // We do not want to increment/decrement centralized counter in newproc/goexit, + // just to make runtime.NumGoroutine() faster. + // Compromise solution is to introduce per-P counters of active goroutines. + for(gp = runtime·allg; gp; gp = gp->alllink) { + s = gp->status; + if(s == Grunnable || s == Grunning || s == Gsyscall || s == Gwaiting) + n++; + } + runtime·unlock(&runtime·sched); + return n; } int32 @@ -1740,6 +1699,9 @@ runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp) { int32 n; + // Windows does profiling in a dedicated thread w/o m. + if(!Windows && (m == nil || m->mcache == nil)) + return; if(prof.fn == nil || prof.hz == 0) return; @@ -1783,27 +1745,533 @@ runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz) runtime·resetcpuprofiler(hz); } -void (*libcgo_setenv)(byte**); +// Change number of processors. The world is stopped, sched is locked. +static void +procresize(int32 new) +{ + int32 i, old; + G *gp; + P *p; + + old = runtime·gomaxprocs; + if(old < 0 || old > MaxGomaxprocs || new <= 0 || new >MaxGomaxprocs) + runtime·throw("procresize: invalid arg"); + // initialize new P's + for(i = 0; i < new; i++) { + p = runtime·allp[i]; + if(p == nil) { + p = (P*)runtime·mallocgc(sizeof(*p), 0, 0, 1); + p->status = Pgcstop; + runtime·atomicstorep(&runtime·allp[i], p); + } + if(p->mcache == nil) { + if(old==0 && i==0) + p->mcache = m->mcache; // bootstrap + else + p->mcache = runtime·allocmcache(); + } + if(p->runq == nil) { + p->runqsize = 128; + p->runq = (G**)runtime·mallocgc(p->runqsize*sizeof(G*), 0, 0, 1); + } + } + + // redistribute runnable G's evenly + for(i = 0; i < old; i++) { + p = runtime·allp[i]; + while(gp = runqget(p)) + globrunqput(gp); + } + // start at 1 because current M already executes some G and will acquire allp[0] below, + // so if we have a spare G we want to put it into allp[1]. + for(i = 1; runtime·sched.runqhead; i++) { + gp = runtime·sched.runqhead; + runtime·sched.runqhead = gp->schedlink; + runqput(runtime·allp[i%new], gp); + } + runtime·sched.runqtail = nil; + runtime·sched.runqsize = 0; + + // free unused P's + for(i = new; i < old; i++) { + p = runtime·allp[i]; + runtime·freemcache(p->mcache); + p->mcache = nil; + gfpurge(p); + p->status = Pdead; + // can't free P itself because it can be referenced by an M in syscall + } + + if(m->p) + m->p->m = nil; + m->p = nil; + m->mcache = nil; + p = runtime·allp[0]; + p->m = nil; + p->status = Pidle; + acquirep(p); + for(i = new-1; i > 0; i--) { + p = runtime·allp[i]; + p->status = Pidle; + pidleput(p); + } + runtime·singleproc = new == 1; + runtime·atomicstore((uint32*)&runtime·gomaxprocs, new); +} + +// Associate p and the current m. +static void +acquirep(P *p) +{ + if(m->p || m->mcache) + runtime·throw("acquirep: already in go"); + if(p->m || p->status != Pidle) { + runtime·printf("acquirep: p->m=%p(%d) p->status=%d\n", p->m, p->m ? p->m->id : 0, p->status); + runtime·throw("acquirep: invalid p state"); + } + m->mcache = p->mcache; + m->p = p; + p->m = m; + p->status = Prunning; +} + +// Disassociate p and the current m. +static P* +releasep(void) +{ + P *p; -// Update the C environment if cgo is loaded. -// Called from syscall.Setenv. -void -syscall·setenv_c(String k, String v) + if(m->p == nil || m->mcache == nil) + runtime·throw("releasep: invalid arg"); + p = m->p; + if(p->m != m || p->mcache != m->mcache || p->status != Prunning) { + runtime·printf("releasep: m=%p m->p=%p p->m=%p m->mcache=%p p->mcache=%p p->status=%d\n", + m, m->p, p->m, m->mcache, p->mcache, p->status); + runtime·throw("releasep: invalid p state"); + } + m->p = nil; + m->mcache = nil; + p->m = nil; + p->status = Pidle; + return p; +} + +static void +inclocked(int32 v) { - byte *arg[2]; + runtime·lock(&runtime·sched); + runtime·sched.mlocked += v; + if(v > 0) + checkdead(); + runtime·unlock(&runtime·sched); +} + +// Check for deadlock situation. +// The check is based on number of running M's, if 0 -> deadlock. +static void +checkdead(void) +{ + G *gp; + int32 run, grunning, s; - if(libcgo_setenv == nil) + // -1 for sysmon + run = runtime·sched.mcount - runtime·sched.nmidle - runtime·sched.mlocked - 1; + if(run > 0) return; + if(run < 0) { + runtime·printf("checkdead: nmidle=%d mlocked=%d mcount=%d\n", + runtime·sched.nmidle, runtime·sched.mlocked, runtime·sched.mcount); + runtime·throw("checkdead: inconsistent counts"); + } + grunning = 0; + for(gp = runtime·allg; gp; gp = gp->alllink) { + if(gp == scvg) + continue; + s = gp->status; + if(s == Gwaiting) + grunning++; + else if(s == Grunnable || s == Grunning || s == Gsyscall) { + runtime·printf("checkdead: find g %D in status %d\n", gp->goid, s); + runtime·throw("checkdead: runnable g"); + } + } + if(grunning == 0) // possible if main goroutine calls runtime·Goexit() + runtime·exit(0); + m->throwing = -1; // do not dump full stacks + runtime·throw("all goroutines are asleep - deadlock!"); +} + +static void +sysmon(void) +{ + uint32 idle, delay; + uint32 ticks[MaxGomaxprocs]; + + idle = 0; // how many cycles in succession we had not wokeup somebody + delay = 0; + for(;;) { + if(idle == 0) // start with 20us sleep... + delay = 20; + else if(idle > 50) // start doubling the sleep after 1ms... + delay *= 2; + if(delay > 10*1000) // up to 10ms + delay = 10*1000; + runtime·usleep(delay); + if(runtime·gcwaiting || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs) { // TODO: fast atomic + runtime·lock(&runtime·sched); + if(runtime·atomicload(&runtime·gcwaiting) || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs) { + runtime·atomicstore(&runtime·sched.sysmonwait, 1); + runtime·unlock(&runtime·sched); + runtime·notesleep(&runtime·sched.sysmonnote); + runtime·noteclear(&runtime·sched.sysmonnote); + idle = 0; + delay = 20; + } else + runtime·unlock(&runtime·sched); + } + if(retake(ticks)) + idle = 0; + else + idle++; + } +} + +static uint32 +retake(uint32 *ticks) +{ + uint32 i, s, n; + int64 t; + P *p; + + n = 0; + for(i = 0; i < runtime·gomaxprocs; i++) { + p = runtime·allp[i]; + if(p==nil) + continue; + t = p->tick; + if(ticks[i] != t) { + ticks[i] = t; + continue; + } + s = p->status; + if(s != Psyscall) + continue; + if(p->runqhead == p->runqtail && runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) > 0) // TODO: fast atomic + continue; + // Need to increment number of locked M's before the CAS. + // Otherwise the M from which we retake can exit the syscall, + // increment nmidle and report deadlock. + inclocked(-1); + if(runtime·cas(&p->status, s, Pidle)) { + n++; + handoffp(p); + } + inclocked(1); + } + return n; +} + +// Put mp on midle list. +// Sched must be locked. +static void +mput(M *mp) +{ + mp->schedlink = runtime·sched.midle; + runtime·sched.midle = mp; + runtime·sched.nmidle++; + checkdead(); +} + +// Try to get an m from midle list. +// Sched must be locked. +static M* +mget(void) +{ + M *mp; + + if((mp = runtime·sched.midle) != nil){ + runtime·sched.midle = mp->schedlink; + runtime·sched.nmidle--; + } + return mp; +} + +// Put gp on the global runnable queue. +// Sched must be locked. +static void +globrunqput(G *gp) +{ + gp->schedlink = nil; + if(runtime·sched.runqtail) + runtime·sched.runqtail->schedlink = gp; + else + runtime·sched.runqhead = gp; + runtime·sched.runqtail = gp; + runtime·sched.runqsize++; +} + +// Try get a batch of G's from the global runnable queue. +// Sched must be locked. +static G* +globrunqget(P *p) +{ + G *gp, *gp1; + int32 n; + + if(runtime·sched.runqsize == 0) + return nil; + n = runtime·sched.runqsize/runtime·gomaxprocs+1; + if(n > runtime·sched.runqsize) + n = runtime·sched.runqsize; + runtime·sched.runqsize -= n; + if(runtime·sched.runqsize == 0) + runtime·sched.runqtail = nil; + gp = runtime·sched.runqhead; + runtime·sched.runqhead = gp->schedlink; + n--; + while(n--) { + gp1 = runtime·sched.runqhead; + runtime·sched.runqhead = gp1->schedlink; + runqput(p, gp1); + } + return gp; +} + +// Put p to on pidle list. +// Sched must be locked. +static void +pidleput(P *p) +{ + p->link = runtime·sched.pidle; + runtime·sched.pidle = p; + runtime·xadd(&runtime·sched.npidle, 1); // TODO: fast atomic +} + +// Try get a p from pidle list. +// Sched must be locked. +static P* +pidleget(void) +{ + P *p; + + p = runtime·sched.pidle; + if(p) { + runtime·sched.pidle = p->link; + runtime·xadd(&runtime·sched.npidle, -1); // TODO: fast atomic + } + return p; +} + +// Put g on local runnable queue. +// TODO(dvyukov): consider using lock-free queue. +static void +runqput(P *p, G *gp) +{ + int32 h, t, s; + + runtime·lock(p); +retry: + h = p->runqhead; + t = p->runqtail; + s = p->runqsize; + if(t == h-1 || (h == 0 && t == s-1)) { + runqgrow(p); + goto retry; + } + p->runq[t++] = gp; + if(t == s) + t = 0; + p->runqtail = t; + runtime·unlock(p); +} + +// Get g from local runnable queue. +static G* +runqget(P *p) +{ + G *gp; + int32 t, h, s; + + if(p->runqhead == p->runqtail) + return nil; + runtime·lock(p); + h = p->runqhead; + t = p->runqtail; + s = p->runqsize; + if(t == h) { + runtime·unlock(p); + return nil; + } + gp = p->runq[h++]; + if(h == s) + h = 0; + p->runqhead = h; + runtime·unlock(p); + return gp; +} + +// Grow local runnable queue. +// TODO(dvyukov): consider using fixed-size array +// and transfer excess to the global list (local queue can grow way too big). +static void +runqgrow(P *p) +{ + G **q; + int32 s, t, h, t2; + + h = p->runqhead; + t = p->runqtail; + s = p->runqsize; + t2 = 0; + q = runtime·malloc(2*s*sizeof(*q)); + while(t != h) { + q[t2++] = p->runq[h++]; + if(h == s) + h = 0; + } + runtime·free(p->runq); + p->runq = q; + p->runqhead = 0; + p->runqtail = t2; + p->runqsize = 2*s; +} - arg[0] = runtime·malloc(k.len + 1); - runtime·memmove(arg[0], k.str, k.len); - arg[0][k.len] = 0; +// Steal half of elements from local runnable queue of p2 +// and put onto local runnable queue of p. +// Returns one of the stolen elements (or nil if failed). +static G* +runqsteal(P *p, P *p2) +{ + G *gp, *gp1; + int32 t, h, s, t2, h2, s2, c, i; + + if(p2->runqhead == p2->runqtail) + return nil; + // sort locks to prevent deadlocks + if(p < p2) + runtime·lock(p); + runtime·lock(p2); + if(p2->runqhead == p2->runqtail) { + runtime·unlock(p2); + if(p < p2) + runtime·unlock(p); + return nil; + } + if(p >= p2) + runtime·lock(p); + // now we've locked both queues and know the victim is not empty + h = p->runqhead; + t = p->runqtail; + s = p->runqsize; + h2 = p2->runqhead; + t2 = p2->runqtail; + s2 = p2->runqsize; + gp = p2->runq[h2++]; // return value + if(h2 == s2) + h2 = 0; + // steal roughly half + if(t2 > h2) + c = (t2 - h2) / 2; + else + c = (s2 - h2 + t2) / 2; + // copy + for(i = 0; i != c; i++) { + // the target queue is full? + if(t == h-1 || (h == 0 && t == s-1)) + break; + // the victim queue is empty? + if(t2 == h2) + break; + gp1 = p2->runq[h2++]; + if(h2 == s2) + h2 = 0; + p->runq[t++] = gp1; + if(t == s) + t = 0; + } + p->runqtail = t; + p2->runqhead = h2; + runtime·unlock(p2); + runtime·unlock(p); + return gp; +} - arg[1] = runtime·malloc(v.len + 1); - runtime·memmove(arg[1], v.str, v.len); - arg[1][v.len] = 0; +void +runtime·testSchedLocalQueue(void) +{ + P p; + G gs[1000]; + int32 i, j; + + runtime·memclr((byte*)&p, sizeof(p)); + p.runqsize = 1; + p.runqhead = 0; + p.runqtail = 0; + p.runq = runtime·malloc(p.runqsize*sizeof(*p.runq)); + + for(i = 0; i < nelem(gs); i++) { + if(runqget(&p) != nil) + runtime·throw("runq is not empty initially"); + for(j = 0; j < i; j++) + runqput(&p, &gs[i]); + for(j = 0; j < i; j++) { + if(runqget(&p) != &gs[i]) { + runtime·printf("bad element at iter %d/%d\n", i, j); + runtime·throw("bad element"); + } + } + if(runqget(&p) != nil) + runtime·throw("runq is not empty afterwards"); + } +} - runtime·asmcgocall((void*)libcgo_setenv, arg); - runtime·free(arg[0]); - runtime·free(arg[1]); +void +runtime·testSchedLocalQueueSteal(void) +{ + P p1, p2; + G gs[1000], *gp; + int32 i, j, s; + + runtime·memclr((byte*)&p1, sizeof(p1)); + p1.runqsize = 1; + p1.runqhead = 0; + p1.runqtail = 0; + p1.runq = runtime·malloc(p1.runqsize*sizeof(*p1.runq)); + + runtime·memclr((byte*)&p2, sizeof(p2)); + p2.runqsize = nelem(gs); + p2.runqhead = 0; + p2.runqtail = 0; + p2.runq = runtime·malloc(p2.runqsize*sizeof(*p2.runq)); + + for(i = 0; i < nelem(gs); i++) { + for(j = 0; j < i; j++) { + gs[j].sig = 0; + runqput(&p1, &gs[j]); + } + gp = runqsteal(&p2, &p1); + s = 0; + if(gp) { + s++; + gp->sig++; + } + while(gp = runqget(&p2)) { + s++; + gp->sig++; + } + while(gp = runqget(&p1)) + gp->sig++; + for(j = 0; j < i; j++) { + if(gs[j].sig != 1) { + runtime·printf("bad element %d(%d) at iter %d\n", j, gs[j].sig, i); + runtime·throw("bad element"); + } + } + if(s != i/2 && s != i/2+1) { + runtime·printf("bad steal %d, want %d or %d, iter %d\n", + s, i/2, i/2+1, i); + runtime·throw("bad steal"); + } + } } + diff --git a/src/pkg/runtime/proc_test.go b/src/pkg/runtime/proc_test.go index 32111080a..21fb9c2f7 100644 --- a/src/pkg/runtime/proc_test.go +++ b/src/pkg/runtime/proc_test.go @@ -5,9 +5,11 @@ package runtime_test import ( + "math" "runtime" "sync/atomic" "testing" + "time" ) var stop = make(chan bool, 1) @@ -22,8 +24,7 @@ func perpetuumMobile() { func TestStopTheWorldDeadlock(t *testing.T) { if testing.Short() { - t.Logf("skipping during short test") - return + t.Skip("skipping during short test") } maxprocs := runtime.GOMAXPROCS(3) compl := make(chan bool, 2) @@ -46,6 +47,66 @@ func TestStopTheWorldDeadlock(t *testing.T) { runtime.GOMAXPROCS(maxprocs) } +func TestYieldProgress(t *testing.T) { + testYieldProgress(t, false) +} + +func TestYieldLockedProgress(t *testing.T) { + testYieldProgress(t, true) +} + +func testYieldProgress(t *testing.T, locked bool) { + c := make(chan bool) + cack := make(chan bool) + go func() { + if locked { + runtime.LockOSThread() + } + for { + select { + case <-c: + cack <- true + return + default: + runtime.Gosched() + } + } + }() + time.Sleep(10 * time.Millisecond) + c <- true + <-cack +} + +func TestYieldLocked(t *testing.T) { + const N = 10 + c := make(chan bool) + go func() { + runtime.LockOSThread() + for i := 0; i < N; i++ { + runtime.Gosched() + time.Sleep(time.Millisecond) + } + c <- true + // runtime.UnlockOSThread() is deliberately omitted + }() + <-c +} + +func TestBlockLocked(t *testing.T) { + const N = 10 + c := make(chan bool) + go func() { + runtime.LockOSThread() + for i := 0; i < N; i++ { + c <- true + } + runtime.UnlockOSThread() + }() + for i := 0; i < N; i++ { + <-c + } +} + func stackGrowthRecursive(i int) { var pad [128]uint64 if i != 0 && pad[0] == 0 { @@ -53,7 +114,15 @@ func stackGrowthRecursive(i int) { } } -func BenchmarkStackGrowth(b *testing.B) { +func TestSchedLocalQueue(t *testing.T) { + runtime.TestSchedLocalQueue1() +} + +func TestSchedLocalQueueSteal(t *testing.T) { + runtime.TestSchedLocalQueueSteal1() +} + +func benchmarkStackGrowth(b *testing.B, rec int) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) @@ -63,7 +132,7 @@ func BenchmarkStackGrowth(b *testing.B) { for atomic.AddInt32(&N, -1) >= 0 { runtime.Gosched() for g := 0; g < CallsPerSched; g++ { - stackGrowthRecursive(10) + stackGrowthRecursive(rec) } } c <- true @@ -74,32 +143,33 @@ func BenchmarkStackGrowth(b *testing.B) { } } +func BenchmarkStackGrowth(b *testing.B) { + benchmarkStackGrowth(b, 10) +} + +func BenchmarkStackGrowthDeep(b *testing.B) { + benchmarkStackGrowth(b, 1024) +} + func BenchmarkSyscall(b *testing.B) { - const CallsPerSched = 1000 - procs := runtime.GOMAXPROCS(-1) - N := int32(b.N / CallsPerSched) - c := make(chan bool, procs) - for p := 0; p < procs; p++ { - go func() { - for atomic.AddInt32(&N, -1) >= 0 { - runtime.Gosched() - for g := 0; g < CallsPerSched; g++ { - runtime.Entersyscall() - runtime.Exitsyscall() - } - } - c <- true - }() - } - for p := 0; p < procs; p++ { - <-c - } + benchmarkSyscall(b, 0, 1) } func BenchmarkSyscallWork(b *testing.B) { + benchmarkSyscall(b, 100, 1) +} + +func BenchmarkSyscallExcess(b *testing.B) { + benchmarkSyscall(b, 0, 4) +} + +func BenchmarkSyscallExcessWork(b *testing.B) { + benchmarkSyscall(b, 100, 4) +} + +func benchmarkSyscall(b *testing.B, work, excess int) { const CallsPerSched = 1000 - const LocalWork = 100 - procs := runtime.GOMAXPROCS(-1) + procs := runtime.GOMAXPROCS(-1) * excess N := int32(b.N / CallsPerSched) c := make(chan bool, procs) for p := 0; p < procs; p++ { @@ -109,7 +179,7 @@ func BenchmarkSyscallWork(b *testing.B) { runtime.Gosched() for g := 0; g < CallsPerSched; g++ { runtime.Entersyscall() - for i := 0; i < LocalWork; i++ { + for i := 0; i < work; i++ { foo *= 2 foo /= 2 } @@ -123,3 +193,93 @@ func BenchmarkSyscallWork(b *testing.B) { <-c } } + +func BenchmarkCreateGoroutines(b *testing.B) { + benchmarkCreateGoroutines(b, 1) +} + +func BenchmarkCreateGoroutinesParallel(b *testing.B) { + benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1)) +} + +func benchmarkCreateGoroutines(b *testing.B, procs int) { + c := make(chan bool) + var f func(n int) + f = func(n int) { + if n == 0 { + c <- true + return + } + go f(n - 1) + } + for i := 0; i < procs; i++ { + go f(b.N / procs) + } + for i := 0; i < procs; i++ { + <-c + } +} + +type Matrix [][]float64 + +func BenchmarkMatmult(b *testing.B) { + b.StopTimer() + // matmult is O(N**3) but testing expects O(b.N), + // so we need to take cube root of b.N + n := int(math.Cbrt(float64(b.N))) + 1 + A := makeMatrix(n) + B := makeMatrix(n) + C := makeMatrix(n) + b.StartTimer() + matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8) +} + +func makeMatrix(n int) Matrix { + m := make(Matrix, n) + for i := 0; i < n; i++ { + m[i] = make([]float64, n) + for j := 0; j < n; j++ { + m[i][j] = float64(i*n + j) + } + } + return m +} + +func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) { + di := i1 - i0 + dj := j1 - j0 + dk := k1 - k0 + if di >= dj && di >= dk && di >= threshold { + // divide in two by y axis + mi := i0 + di/2 + done1 := make(chan struct{}, 1) + go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold) + matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold) + <-done1 + } else if dj >= dk && dj >= threshold { + // divide in two by x axis + mj := j0 + dj/2 + done1 := make(chan struct{}, 1) + go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold) + matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold) + <-done1 + } else if dk >= threshold { + // divide in two by "k" axis + // deliberately not parallel because of data races + mk := k0 + dk/2 + matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold) + matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold) + } else { + // the matrices are small enough, compute directly + for i := i0; i < i1; i++ { + for j := j0; j < j1; j++ { + for k := k0; k < k1; k++ { + C[i][j] += A[i][k] * B[k][j] + } + } + } + } + if done != nil { + done <- struct{}{} + } +} diff --git a/src/pkg/runtime/race.c b/src/pkg/runtime/race.c new file mode 100644 index 000000000..cfd97041a --- /dev/null +++ b/src/pkg/runtime/race.c @@ -0,0 +1,350 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Implementation of the race detector API. +// +build race + +#include "runtime.h" +#include "arch_GOARCH.h" +#include "malloc.h" +#include "race.h" + +void runtime∕race·Initialize(uintptr *racectx); +void runtime∕race·MapShadow(void *addr, uintptr size); +void runtime∕race·Finalize(void); +void runtime∕race·FinalizerGoroutine(uintptr racectx); +void runtime∕race·Read(uintptr racectx, void *addr, void *pc); +void runtime∕race·Write(uintptr racectx, void *addr, void *pc); +void runtime∕race·ReadRange(uintptr racectx, void *addr, uintptr sz, uintptr step, void *pc); +void runtime∕race·WriteRange(uintptr racectx, void *addr, uintptr sz, uintptr step, void *pc); +void runtime∕race·FuncEnter(uintptr racectx, void *pc); +void runtime∕race·FuncExit(uintptr racectx); +void runtime∕race·Malloc(uintptr racectx, void *p, uintptr sz, void *pc); +void runtime∕race·Free(void *p); +void runtime∕race·GoStart(uintptr racectx, uintptr *chracectx, void *pc); +void runtime∕race·GoEnd(uintptr racectx); +void runtime∕race·Acquire(uintptr racectx, void *addr); +void runtime∕race·Release(uintptr racectx, void *addr); +void runtime∕race·ReleaseMerge(uintptr racectx, void *addr); + +extern byte noptrdata[]; +extern byte enoptrbss[]; + +static bool onstack(uintptr argp); + +uintptr +runtime·raceinit(void) +{ + uintptr racectx; + + m->racecall = true; + runtime∕race·Initialize(&racectx); + runtime∕race·MapShadow(noptrdata, enoptrbss - noptrdata); + m->racecall = false; + return racectx; +} + +void +runtime·racefini(void) +{ + m->racecall = true; + runtime∕race·Finalize(); + m->racecall = false; +} + +void +runtime·racemapshadow(void *addr, uintptr size) +{ + m->racecall = true; + runtime∕race·MapShadow(addr, size); + m->racecall = false; +} + +// Called from instrumented code. +// If we split stack, getcallerpc() can return runtime·lessstack(). +#pragma textflag 7 +void +runtime·racewrite(uintptr addr) +{ + if(!onstack(addr)) { + m->racecall = true; + runtime∕race·Write(g->racectx, (void*)addr, runtime·getcallerpc(&addr)); + m->racecall = false; + } +} + +// Called from instrumented code. +// If we split stack, getcallerpc() can return runtime·lessstack(). +#pragma textflag 7 +void +runtime·raceread(uintptr addr) +{ + if(!onstack(addr)) { + m->racecall = true; + runtime∕race·Read(g->racectx, (void*)addr, runtime·getcallerpc(&addr)); + m->racecall = false; + } +} + +// Called from runtime·racefuncenter (assembly). +#pragma textflag 7 +void +runtime·racefuncenter1(uintptr pc) +{ + // If the caller PC is lessstack, use slower runtime·callers + // to walk across the stack split to find the real caller. + if(pc == (uintptr)runtime·lessstack) + runtime·callers(2, &pc, 1); + + m->racecall = true; + runtime∕race·FuncEnter(g->racectx, (void*)pc); + m->racecall = false; +} + +// Called from instrumented code. +#pragma textflag 7 +void +runtime·racefuncexit(void) +{ + m->racecall = true; + runtime∕race·FuncExit(g->racectx); + m->racecall = false; +} + +void +runtime·racemalloc(void *p, uintptr sz, void *pc) +{ + // use m->curg because runtime·stackalloc() is called from g0 + if(m->curg == nil) + return; + m->racecall = true; + runtime∕race·Malloc(m->curg->racectx, p, sz, pc); + m->racecall = false; +} + +void +runtime·racefree(void *p) +{ + m->racecall = true; + runtime∕race·Free(p); + m->racecall = false; +} + +uintptr +runtime·racegostart(void *pc) +{ + uintptr racectx; + + m->racecall = true; + runtime∕race·GoStart(g->racectx, &racectx, pc); + m->racecall = false; + return racectx; +} + +void +runtime·racegoend(void) +{ + m->racecall = true; + runtime∕race·GoEnd(g->racectx); + m->racecall = false; +} + +static void +memoryaccess(void *addr, uintptr callpc, uintptr pc, bool write) +{ + uintptr racectx; + + if(!onstack((uintptr)addr)) { + m->racecall = true; + racectx = g->racectx; + if(callpc) { + if(callpc == (uintptr)runtime·lessstack) + runtime·callers(3, &callpc, 1); + runtime∕race·FuncEnter(racectx, (void*)callpc); + } + if(write) + runtime∕race·Write(racectx, addr, (void*)pc); + else + runtime∕race·Read(racectx, addr, (void*)pc); + if(callpc) + runtime∕race·FuncExit(racectx); + m->racecall = false; + } +} + +void +runtime·racewritepc(void *addr, void *callpc, void *pc) +{ + memoryaccess(addr, (uintptr)callpc, (uintptr)pc, true); +} + +void +runtime·racereadpc(void *addr, void *callpc, void *pc) +{ + memoryaccess(addr, (uintptr)callpc, (uintptr)pc, false); +} + +static void +rangeaccess(void *addr, uintptr size, uintptr step, uintptr callpc, uintptr pc, bool write) +{ + uintptr racectx; + + if(!onstack((uintptr)addr)) { + m->racecall = true; + racectx = g->racectx; + if(callpc) { + if(callpc == (uintptr)runtime·lessstack) + runtime·callers(3, &callpc, 1); + runtime∕race·FuncEnter(racectx, (void*)callpc); + } + if(write) + runtime∕race·WriteRange(racectx, addr, size, step, (void*)pc); + else + runtime∕race·ReadRange(racectx, addr, size, step, (void*)pc); + if(callpc) + runtime∕race·FuncExit(racectx); + m->racecall = false; + } +} + +void +runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc) +{ + rangeaccess(addr, sz, step, (uintptr)callpc, (uintptr)pc, true); +} + +void +runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc) +{ + rangeaccess(addr, sz, step, (uintptr)callpc, (uintptr)pc, false); +} + +void +runtime·raceacquire(void *addr) +{ + runtime·raceacquireg(g, addr); +} + +void +runtime·raceacquireg(G *gp, void *addr) +{ + if(g->raceignore) + return; + m->racecall = true; + runtime∕race·Acquire(gp->racectx, addr); + m->racecall = false; +} + +void +runtime·racerelease(void *addr) +{ + runtime·racereleaseg(g, addr); +} + +void +runtime·racereleaseg(G *gp, void *addr) +{ + if(g->raceignore) + return; + m->racecall = true; + runtime∕race·Release(gp->racectx, addr); + m->racecall = false; +} + +void +runtime·racereleasemerge(void *addr) +{ + runtime·racereleasemergeg(g, addr); +} + +void +runtime·racereleasemergeg(G *gp, void *addr) +{ + if(g->raceignore) + return; + m->racecall = true; + runtime∕race·ReleaseMerge(gp->racectx, addr); + m->racecall = false; +} + +void +runtime·racefingo(void) +{ + m->racecall = true; + runtime∕race·FinalizerGoroutine(g->racectx); + m->racecall = false; +} + +// func RaceAcquire(addr unsafe.Pointer) +void +runtime·RaceAcquire(void *addr) +{ + runtime·raceacquire(addr); +} + +// func RaceRelease(addr unsafe.Pointer) +void +runtime·RaceRelease(void *addr) +{ + runtime·racerelease(addr); +} + +// func RaceReleaseMerge(addr unsafe.Pointer) +void +runtime·RaceReleaseMerge(void *addr) +{ + runtime·racereleasemerge(addr); +} + +// func RaceSemacquire(s *uint32) +void runtime·RaceSemacquire(uint32 *s) +{ + runtime·semacquire(s); +} + +// func RaceSemrelease(s *uint32) +void runtime·RaceSemrelease(uint32 *s) +{ + runtime·semrelease(s); +} + +// func RaceRead(addr unsafe.Pointer) +#pragma textflag 7 +void +runtime·RaceRead(void *addr) +{ + memoryaccess(addr, 0, (uintptr)runtime·getcallerpc(&addr), false); +} + +// func RaceWrite(addr unsafe.Pointer) +#pragma textflag 7 +void +runtime·RaceWrite(void *addr) +{ + memoryaccess(addr, 0, (uintptr)runtime·getcallerpc(&addr), true); +} + +// func RaceDisable() +void runtime·RaceDisable(void) +{ + g->raceignore++; +} + +// func RaceEnable() +void runtime·RaceEnable(void) +{ + g->raceignore--; +} + +static bool +onstack(uintptr argp) +{ + // noptrdata, data, bss, noptrbss + // the layout is in ../../cmd/ld/data.c + if((byte*)argp >= noptrdata && (byte*)argp < enoptrbss) + return false; + if((byte*)argp >= runtime·mheap->arena_start && (byte*)argp < runtime·mheap->arena_used) + return false; + return true; +} diff --git a/src/pkg/runtime/race.go b/src/pkg/runtime/race.go new file mode 100644 index 000000000..1d64ba389 --- /dev/null +++ b/src/pkg/runtime/race.go @@ -0,0 +1,29 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race + +// Public race detection API, present iff build with -race. + +package runtime + +import ( + "unsafe" +) + +// RaceDisable disables handling of race events in the current goroutine. +func RaceDisable() + +// RaceEnable re-enables handling of race events in the current goroutine. +func RaceEnable() + +func RaceAcquire(addr unsafe.Pointer) +func RaceRelease(addr unsafe.Pointer) +func RaceReleaseMerge(addr unsafe.Pointer) + +func RaceRead(addr unsafe.Pointer) +func RaceWrite(addr unsafe.Pointer) + +func RaceSemacquire(s *uint32) +func RaceSemrelease(s *uint32) diff --git a/src/pkg/runtime/race.h b/src/pkg/runtime/race.h new file mode 100644 index 000000000..432a8a97d --- /dev/null +++ b/src/pkg/runtime/race.h @@ -0,0 +1,33 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Definitions related to data race detection. + +#ifdef RACE +enum { raceenabled = 1 }; +#else +enum { raceenabled = 0 }; +#endif + +// Initialize race detection subsystem. +uintptr runtime·raceinit(void); +// Finalize race detection subsystem, does not return. +void runtime·racefini(void); + +void runtime·racemapshadow(void *addr, uintptr size); +void runtime·racemalloc(void *p, uintptr sz, void *pc); +void runtime·racefree(void *p); +uintptr runtime·racegostart(void *pc); +void runtime·racegoend(void); +void runtime·racewritepc(void *addr, void *callpc, void *pc); +void runtime·racereadpc(void *addr, void *callpc, void *pc); +void runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc); +void runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc); +void runtime·racefingo(void); +void runtime·raceacquire(void *addr); +void runtime·raceacquireg(G *gp, void *addr); +void runtime·racerelease(void *addr); +void runtime·racereleaseg(G *gp, void *addr); +void runtime·racereleasemerge(void *addr); +void runtime·racereleasemergeg(G *gp, void *addr); diff --git a/src/pkg/runtime/race/README b/src/pkg/runtime/race/README new file mode 100644 index 000000000..8bedb09cd --- /dev/null +++ b/src/pkg/runtime/race/README @@ -0,0 +1,11 @@ +runtime/race package contains the data race detector runtime library. +It is based on ThreadSanitizer race detector, that is currently a part of +the LLVM project. + +To update the .syso files you need to: +$ svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk +$ cd compiler-rt/lib/tsan/go +$ ./buildgo.sh + +Tested with gcc 4.6.1 and 4.7.0. On Windows it's built with 64-bit MinGW. + diff --git a/src/pkg/runtime/race/race.go b/src/pkg/runtime/race/race.go new file mode 100644 index 000000000..b0a5c9a50 --- /dev/null +++ b/src/pkg/runtime/race/race.go @@ -0,0 +1,122 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race,linux,amd64 race,darwin,amd64 race,windows,amd64 + +// Package race provides low-level facilities for data race detection. +package race + +/* +void __tsan_init(void **racectx); +void __tsan_fini(void); +void __tsan_map_shadow(void *addr, void *size); +void __tsan_go_start(void *racectx, void **chracectx, void *pc); +void __tsan_go_end(void *racectx); +void __tsan_read(void *racectx, void *addr, void *pc); +void __tsan_write(void *racectx, void *addr, void *pc); +void __tsan_read_range(void *racectx, void *addr, long sz, long step, void *pc); +void __tsan_write_range(void *racectx, void *addr, long sz, long step, void *pc); +void __tsan_func_enter(void *racectx, void *pc); +void __tsan_func_exit(void *racectx); +void __tsan_malloc(void *racectx, void *p, long sz, void *pc); +void __tsan_free(void *p); +void __tsan_acquire(void *racectx, void *addr); +void __tsan_release(void *racectx, void *addr); +void __tsan_release_merge(void *racectx, void *addr); +void __tsan_finalizer_goroutine(void *racectx); +*/ +import "C" + +import ( + "runtime" + "unsafe" +) + +func Initialize(racectx *uintptr) { + C.__tsan_init((*unsafe.Pointer)(unsafe.Pointer(racectx))) +} + +func Finalize() { + C.__tsan_fini() +} + +func MapShadow(addr, size uintptr) { + C.__tsan_map_shadow(unsafe.Pointer(addr), unsafe.Pointer(size)) +} + +func FinalizerGoroutine(racectx uintptr) { + C.__tsan_finalizer_goroutine(unsafe.Pointer(racectx)) +} + +func Read(racectx uintptr, addr, pc uintptr) { + C.__tsan_read(unsafe.Pointer(racectx), unsafe.Pointer(addr), unsafe.Pointer(pc)) +} + +func Write(racectx uintptr, addr, pc uintptr) { + C.__tsan_write(unsafe.Pointer(racectx), unsafe.Pointer(addr), unsafe.Pointer(pc)) +} + +func ReadRange(racectx uintptr, addr, sz, step, pc uintptr) { + C.__tsan_read_range(unsafe.Pointer(racectx), unsafe.Pointer(addr), + C.long(sz), C.long(step), unsafe.Pointer(pc)) +} + +func WriteRange(racectx uintptr, addr, sz, step, pc uintptr) { + C.__tsan_write_range(unsafe.Pointer(racectx), unsafe.Pointer(addr), + C.long(sz), C.long(step), unsafe.Pointer(pc)) +} + +func FuncEnter(racectx uintptr, pc uintptr) { + C.__tsan_func_enter(unsafe.Pointer(racectx), unsafe.Pointer(pc)) +} + +func FuncExit(racectx uintptr) { + C.__tsan_func_exit(unsafe.Pointer(racectx)) +} + +func Malloc(racectx uintptr, p, sz, pc uintptr) { + C.__tsan_malloc(unsafe.Pointer(racectx), unsafe.Pointer(p), C.long(sz), unsafe.Pointer(pc)) +} + +func Free(p uintptr) { + C.__tsan_free(unsafe.Pointer(p)) +} + +func GoStart(racectx uintptr, chracectx *uintptr, pc uintptr) { + C.__tsan_go_start(unsafe.Pointer(racectx), (*unsafe.Pointer)(unsafe.Pointer(chracectx)), unsafe.Pointer(pc)) +} + +func GoEnd(racectx uintptr) { + C.__tsan_go_end(unsafe.Pointer(racectx)) +} + +func Acquire(racectx uintptr, addr uintptr) { + C.__tsan_acquire(unsafe.Pointer(racectx), unsafe.Pointer(addr)) +} + +func Release(racectx uintptr, addr uintptr) { + C.__tsan_release(unsafe.Pointer(racectx), unsafe.Pointer(addr)) +} + +func ReleaseMerge(racectx uintptr, addr uintptr) { + C.__tsan_release_merge(unsafe.Pointer(racectx), unsafe.Pointer(addr)) +} + +//export __tsan_symbolize +func __tsan_symbolize(pc uintptr, fun, file **C.char, line, off *C.int) C.int { + f := runtime.FuncForPC(pc) + if f == nil { + *fun = C.CString("??") + *file = C.CString("-") + *line = 0 + *off = C.int(pc) + return 1 + } + fi, l := f.FileLine(pc) + *fun = C.CString(f.Name()) + *file = C.CString(fi) + *line = C.int(l) + *off = C.int(pc - f.Entry()) + return 1 +} diff --git a/src/pkg/runtime/race/race_darwin_amd64.syso b/src/pkg/runtime/race/race_darwin_amd64.syso new file mode 100644 index 000000000..24a00497c Binary files /dev/null and b/src/pkg/runtime/race/race_darwin_amd64.syso differ diff --git a/src/pkg/runtime/race/race_linux_amd64.syso b/src/pkg/runtime/race/race_linux_amd64.syso new file mode 100644 index 000000000..b15091ba8 Binary files /dev/null and b/src/pkg/runtime/race/race_linux_amd64.syso differ diff --git a/src/pkg/runtime/race/race_test.go b/src/pkg/runtime/race/race_test.go new file mode 100644 index 000000000..c77569c37 --- /dev/null +++ b/src/pkg/runtime/race/race_test.go @@ -0,0 +1,157 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race + +// This program is used to verify the race detector +// by running the tests and parsing their output. +// It does not check stack correctness, completeness or anything else: +// it merely verifies that if a test is expected to be racy +// then the race is detected. +package race_test + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +var ( + passedTests = 0 + totalTests = 0 + falsePos = 0 + falseNeg = 0 + failingPos = 0 + failingNeg = 0 + failed = false +) + +const ( + visibleLen = 40 + testPrefix = "=== RUN Test" +) + +func TestRace(t *testing.T) { + testOutput, err := runTests() + if err != nil { + t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput)) + } + reader := bufio.NewReader(bytes.NewBuffer(testOutput)) + + funcName := "" + var tsanLog []string + for { + s, err := nextLine(reader) + if err != nil { + fmt.Printf("%s\n", processLog(funcName, tsanLog)) + break + } + if strings.HasPrefix(s, testPrefix) { + fmt.Printf("%s\n", processLog(funcName, tsanLog)) + tsanLog = make([]string, 0, 100) + funcName = s[len(testPrefix):] + } else { + tsanLog = append(tsanLog, s) + } + } + + fmt.Printf("\nPassed %d of %d tests (%.02f%%, %d+, %d-)\n", + passedTests, totalTests, 100*float64(passedTests)/float64(totalTests), falsePos, falseNeg) + fmt.Printf("%d expected failures (%d has not fail)\n", failingPos+failingNeg, failingNeg) + if failed { + t.Fail() + } +} + +// nextLine is a wrapper around bufio.Reader.ReadString. +// It reads a line up to the next '\n' character. Error +// is non-nil if there are no lines left, and nil +// otherwise. +func nextLine(r *bufio.Reader) (string, error) { + s, err := r.ReadString('\n') + if err != nil { + if err != io.EOF { + log.Fatalf("nextLine: expected EOF, received %v", err) + } + return s, err + } + return s[:len(s)-1], nil +} + +// processLog verifies whether the given ThreadSanitizer's log +// contains a race report, checks this information against +// the name of the testcase and returns the result of this +// comparison. +func processLog(testName string, tsanLog []string) string { + if !strings.HasPrefix(testName, "Race") && !strings.HasPrefix(testName, "NoRace") { + return "" + } + gotRace := false + for _, s := range tsanLog { + if strings.Contains(s, "DATA RACE") { + gotRace = true + break + } + } + + failing := strings.Contains(testName, "Failing") + expRace := !strings.HasPrefix(testName, "No") + for len(testName) < visibleLen { + testName += " " + } + if expRace == gotRace { + passedTests++ + totalTests++ + if failing { + failed = true + failingNeg++ + } + return fmt.Sprintf("%s .", testName) + } + pos := "" + if expRace { + falseNeg++ + } else { + falsePos++ + pos = "+" + } + if failing { + failingPos++ + } else { + failed = true + } + totalTests++ + return fmt.Sprintf("%s %s%s", testName, "FAILED", pos) +} + +// runTests assures that the package and its dependencies is +// built with instrumentation enabled and returns the output of 'go test' +// which includes possible data race reports from ThreadSanitizer. +func runTests() ([]byte, error) { + tests, err := filepath.Glob("./testdata/*_test.go") + if err != nil { + return nil, err + } + args := []string{"test", "-race", "-v"} + args = append(args, tests...) + cmd := exec.Command("go", args...) + // The following flags turn off heuristics that suppress seemingly identical reports. + // It is required because the tests contain a lot of data races on the same addresses + // (the tests are simple and the memory is constantly reused). + for _, env := range os.Environ() { + if strings.HasPrefix(env, "GOMAXPROCS=") { + continue + } + cmd.Env = append(cmd.Env, env) + } + cmd.Env = append(cmd.Env, `GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0"`) + return cmd.CombinedOutput() +} diff --git a/src/pkg/runtime/race/race_windows_amd64.syso b/src/pkg/runtime/race/race_windows_amd64.syso new file mode 100644 index 000000000..0a3a58354 Binary files /dev/null and b/src/pkg/runtime/race/race_windows_amd64.syso differ diff --git a/src/pkg/runtime/race/testdata/atomic_test.go b/src/pkg/runtime/race/testdata/atomic_test.go new file mode 100644 index 000000000..0c5c2c008 --- /dev/null +++ b/src/pkg/runtime/race/testdata/atomic_test.go @@ -0,0 +1,271 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "runtime" + "sync/atomic" + "testing" + "unsafe" +) + +func TestNoRaceAtomicAddInt64(t *testing.T) { + var x1, x2 int8 + var s int64 + ch := make(chan bool, 2) + go func() { + x1 = 1 + if atomic.AddInt64(&s, 1) == 2 { + x2 = 1 + } + ch <- true + }() + go func() { + x2 = 1 + if atomic.AddInt64(&s, 1) == 2 { + x1 = 1 + } + ch <- true + }() + <-ch + <-ch +} + +func TestRaceAtomicAddInt64(t *testing.T) { + var x1, x2 int8 + var s int64 + ch := make(chan bool, 2) + go func() { + x1 = 1 + if atomic.AddInt64(&s, 1) == 1 { + x2 = 1 + } + ch <- true + }() + go func() { + x2 = 1 + if atomic.AddInt64(&s, 1) == 1 { + x1 = 1 + } + ch <- true + }() + <-ch + <-ch +} + +func TestNoRaceAtomicAddInt32(t *testing.T) { + var x1, x2 int8 + var s int32 + ch := make(chan bool, 2) + go func() { + x1 = 1 + if atomic.AddInt32(&s, 1) == 2 { + x2 = 1 + } + ch <- true + }() + go func() { + x2 = 1 + if atomic.AddInt32(&s, 1) == 2 { + x1 = 1 + } + ch <- true + }() + <-ch + <-ch +} + +func TestNoRaceAtomicLoadAddInt32(t *testing.T) { + var x int64 + var s int32 + go func() { + x = 2 + atomic.AddInt32(&s, 1) + }() + for atomic.LoadInt32(&s) != 1 { + runtime.Gosched() + } + x = 1 +} + +func TestNoRaceAtomicLoadStoreInt32(t *testing.T) { + var x int64 + var s int32 + go func() { + x = 2 + atomic.StoreInt32(&s, 1) + }() + for atomic.LoadInt32(&s) != 1 { + runtime.Gosched() + } + x = 1 +} + +func TestNoRaceAtomicStoreCASInt32(t *testing.T) { + var x int64 + var s int32 + go func() { + x = 2 + atomic.StoreInt32(&s, 1) + }() + for !atomic.CompareAndSwapInt32(&s, 1, 0) { + runtime.Gosched() + } + x = 1 +} + +func TestNoRaceAtomicCASLoadInt32(t *testing.T) { + var x int64 + var s int32 + go func() { + x = 2 + if !atomic.CompareAndSwapInt32(&s, 0, 1) { + panic("") + } + }() + for atomic.LoadInt32(&s) != 1 { + runtime.Gosched() + } + x = 1 +} + +func TestNoRaceAtomicCASCASInt32(t *testing.T) { + var x int64 + var s int32 + go func() { + x = 2 + if !atomic.CompareAndSwapInt32(&s, 0, 1) { + panic("") + } + }() + for !atomic.CompareAndSwapInt32(&s, 1, 0) { + runtime.Gosched() + } + x = 1 +} + +func TestNoRaceAtomicCASCASInt32_2(t *testing.T) { + var x1, x2 int8 + var s int32 + ch := make(chan bool, 2) + go func() { + x1 = 1 + if !atomic.CompareAndSwapInt32(&s, 0, 1) { + x2 = 1 + } + ch <- true + }() + go func() { + x2 = 1 + if !atomic.CompareAndSwapInt32(&s, 0, 1) { + x1 = 1 + } + ch <- true + }() + <-ch + <-ch +} + +func TestNoRaceAtomicLoadInt64(t *testing.T) { + var x int32 + var s int64 + go func() { + x = 2 + atomic.AddInt64(&s, 1) + }() + for atomic.LoadInt64(&s) != 1 { + runtime.Gosched() + } + x = 1 +} + +func TestNoRaceAtomicCASCASUInt64(t *testing.T) { + var x int64 + var s uint64 + go func() { + x = 2 + if !atomic.CompareAndSwapUint64(&s, 0, 1) { + panic("") + } + }() + for !atomic.CompareAndSwapUint64(&s, 1, 0) { + runtime.Gosched() + } + x = 1 +} + +func TestNoRaceAtomicLoadStorePointer(t *testing.T) { + var x int64 + var s unsafe.Pointer + var y int = 2 + var p unsafe.Pointer = unsafe.Pointer(&y) + go func() { + x = 2 + atomic.StorePointer(&s, p) + }() + for atomic.LoadPointer(&s) != p { + runtime.Gosched() + } + x = 1 +} + +func TestNoRaceAtomicStoreCASUint64(t *testing.T) { + var x int64 + var s uint64 + go func() { + x = 2 + atomic.StoreUint64(&s, 1) + }() + for !atomic.CompareAndSwapUint64(&s, 1, 0) { + runtime.Gosched() + } + x = 1 +} + +// Races with non-atomic loads are not detected. +func TestRaceFailingAtomicStoreLoad(t *testing.T) { + c := make(chan bool) + var a uint64 + go func() { + atomic.StoreUint64(&a, 1) + c <- true + }() + _ = a + <-c +} + +func TestRaceAtomicLoadStore(t *testing.T) { + c := make(chan bool) + var a uint64 + go func() { + _ = atomic.LoadUint64(&a) + c <- true + }() + a = 1 + <-c +} + +// Races with non-atomic loads are not detected. +func TestRaceFailingAtomicAddLoad(t *testing.T) { + c := make(chan bool) + var a uint64 + go func() { + atomic.AddUint64(&a, 1) + c <- true + }() + _ = a + <-c +} + +func TestRaceAtomicAddStore(t *testing.T) { + c := make(chan bool) + var a uint64 + go func() { + atomic.AddUint64(&a, 1) + c <- true + }() + a = 42 + <-c +} diff --git a/src/pkg/runtime/race/testdata/cgo_test.go b/src/pkg/runtime/race/testdata/cgo_test.go new file mode 100644 index 000000000..ba7e7b562 --- /dev/null +++ b/src/pkg/runtime/race/testdata/cgo_test.go @@ -0,0 +1,20 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "os" + "os/exec" + "testing" +) + +func TestNoRaceCgoSync(t *testing.T) { + cmd := exec.Command("go", "run", "-race", "cgo_test_main.go") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + t.Fatalf("program exited with error: %v\n", err) + } +} diff --git a/src/pkg/runtime/race/testdata/cgo_test_main.go b/src/pkg/runtime/race/testdata/cgo_test_main.go new file mode 100644 index 000000000..620cea18b --- /dev/null +++ b/src/pkg/runtime/race/testdata/cgo_test_main.go @@ -0,0 +1,30 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +int sync; + +void Notify(void) +{ + __sync_fetch_and_add(&sync, 1); +} + +void Wait(void) +{ + while(__sync_fetch_and_add(&sync, 0) == 0) {} +} +*/ +import "C" + +func main() { + data := 0 + go func() { + data = 1 + C.Notify() + }() + C.Wait() + _ = data +} diff --git a/src/pkg/runtime/race/testdata/chan_test.go b/src/pkg/runtime/race/testdata/chan_test.go new file mode 100644 index 000000000..2332f097e --- /dev/null +++ b/src/pkg/runtime/race/testdata/chan_test.go @@ -0,0 +1,457 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "runtime" + "testing" + "time" +) + +func TestNoRaceChanSync(t *testing.T) { + v := 0 + c := make(chan int) + go func() { + v = 1 + c <- 0 + }() + <-c + v = 2 +} + +func TestNoRaceChanSyncRev(t *testing.T) { + v := 0 + c := make(chan int) + go func() { + c <- 0 + v = 2 + }() + v = 1 + <-c +} + +func TestNoRaceChanAsync(t *testing.T) { + v := 0 + c := make(chan int, 10) + go func() { + v = 1 + c <- 0 + }() + <-c + v = 2 +} + +func TestRaceChanAsyncRev(t *testing.T) { + v := 0 + c := make(chan int, 10) + go func() { + c <- 0 + v = 1 + }() + v = 2 + <-c +} + +func TestNoRaceChanAsyncCloseRecv(t *testing.T) { + v := 0 + c := make(chan int, 10) + go func() { + v = 1 + close(c) + }() + func() { + defer func() { + recover() + v = 2 + }() + <-c + }() +} + +func TestNoRaceChanAsyncCloseRecv2(t *testing.T) { + v := 0 + c := make(chan int, 10) + go func() { + v = 1 + close(c) + }() + _, _ = <-c + v = 2 +} + +func TestNoRaceChanAsyncCloseRecv3(t *testing.T) { + v := 0 + c := make(chan int, 10) + go func() { + v = 1 + close(c) + }() + for _ = range c { + } + v = 2 +} + +func TestNoRaceChanSyncCloseRecv(t *testing.T) { + v := 0 + c := make(chan int) + go func() { + v = 1 + close(c) + }() + func() { + defer func() { + recover() + v = 2 + }() + <-c + }() +} + +func TestNoRaceChanSyncCloseRecv2(t *testing.T) { + v := 0 + c := make(chan int) + go func() { + v = 1 + close(c) + }() + _, _ = <-c + v = 2 +} + +func TestNoRaceChanSyncCloseRecv3(t *testing.T) { + v := 0 + c := make(chan int) + go func() { + v = 1 + close(c) + }() + for _ = range c { + } + v = 2 +} + +func TestRaceChanSyncCloseSend(t *testing.T) { + v := 0 + c := make(chan int) + go func() { + v = 1 + close(c) + }() + func() { + defer func() { + recover() + }() + c <- 0 + }() + v = 2 +} + +func TestRaceChanAsyncCloseSend(t *testing.T) { + v := 0 + c := make(chan int, 10) + go func() { + v = 1 + close(c) + }() + func() { + defer func() { + recover() + }() + for { + c <- 0 + } + }() + v = 2 +} + +func TestRaceChanCloseClose(t *testing.T) { + compl := make(chan bool, 2) + v1 := 0 + v2 := 0 + c := make(chan int) + go func() { + defer func() { + if recover() != nil { + v2 = 2 + } + compl <- true + }() + v1 = 1 + close(c) + }() + go func() { + defer func() { + if recover() != nil { + v1 = 2 + } + compl <- true + }() + v2 = 1 + close(c) + }() + <-compl + <-compl +} + +func TestRaceChanSendLen(t *testing.T) { + v := 0 + c := make(chan int, 10) + go func() { + v = 1 + c <- 1 + }() + for len(c) == 0 { + runtime.Gosched() + } + v = 2 +} + +func TestRaceChanRecvLen(t *testing.T) { + v := 0 + c := make(chan int, 10) + c <- 1 + go func() { + v = 1 + <-c + }() + for len(c) != 0 { + runtime.Gosched() + } + v = 2 +} + +func TestRaceChanSendSend(t *testing.T) { + compl := make(chan bool, 2) + v1 := 0 + v2 := 0 + c := make(chan int, 1) + go func() { + v1 = 1 + select { + case c <- 1: + default: + v2 = 2 + } + compl <- true + }() + go func() { + v2 = 1 + select { + case c <- 1: + default: + v1 = 2 + } + compl <- true + }() + <-compl + <-compl +} + +func TestNoRaceChanPtr(t *testing.T) { + type msg struct { + x int + } + c := make(chan *msg) + go func() { + c <- &msg{1} + }() + m := <-c + m.x = 2 +} + +func TestRaceChanWrongSend(t *testing.T) { + v1 := 0 + v2 := 0 + c := make(chan int, 2) + go func() { + v1 = 1 + c <- 1 + }() + go func() { + v2 = 2 + c <- 2 + }() + time.Sleep(1e7) + if <-c == 1 { + v2 = 3 + } else { + v1 = 3 + } +} + +func TestRaceChanWrongClose(t *testing.T) { + v1 := 0 + v2 := 0 + c := make(chan int, 1) + go func() { + defer func() { + recover() + }() + v1 = 1 + c <- 1 + }() + go func() { + time.Sleep(1e7) + v2 = 2 + close(c) + }() + time.Sleep(2e7) + if _, who := <-c; who { + v2 = 2 + } else { + v1 = 2 + } +} + +func TestRaceChanSendClose(t *testing.T) { + compl := make(chan bool, 2) + c := make(chan int, 1) + go func() { + defer func() { + recover() + }() + c <- 1 + compl <- true + }() + go func() { + time.Sleep(1e7) + close(c) + compl <- true + }() + <-compl + <-compl +} + +func TestNoRaceProducerConsumerUnbuffered(t *testing.T) { + type Task struct { + f func() + done chan bool + } + + queue := make(chan Task) + + go func() { + t := <-queue + t.f() + t.done <- true + }() + + doit := func(f func()) { + done := make(chan bool, 1) + queue <- Task{f, done} + <-done + } + + x := 0 + doit(func() { + x = 1 + }) + _ = x +} + +func TestRaceChanItselfSend(t *testing.T) { + compl := make(chan bool, 1) + c := make(chan int, 10) + go func() { + c <- 0 + compl <- true + }() + c = make(chan int, 20) + <-compl +} + +func TestRaceChanItselfRecv(t *testing.T) { + compl := make(chan bool, 1) + c := make(chan int, 10) + c <- 1 + go func() { + <-c + compl <- true + }() + time.Sleep(1e7) + c = make(chan int, 20) + <-compl +} + +func TestRaceChanItselfNil(t *testing.T) { + c := make(chan int, 10) + go func() { + c <- 0 + }() + time.Sleep(1e7) + c = nil + _ = c +} + +func TestRaceChanItselfClose(t *testing.T) { + compl := make(chan bool, 1) + c := make(chan int) + go func() { + close(c) + compl <- true + }() + c = make(chan int) + <-compl +} + +func TestRaceChanItselfLen(t *testing.T) { + compl := make(chan bool, 1) + c := make(chan int) + go func() { + _ = len(c) + compl <- true + }() + c = make(chan int) + <-compl +} + +func TestRaceChanItselfCap(t *testing.T) { + compl := make(chan bool, 1) + c := make(chan int) + go func() { + _ = cap(c) + compl <- true + }() + c = make(chan int) + <-compl +} + +func TestRaceChanCloseLen(t *testing.T) { + v := 0 + c := make(chan int, 10) + c <- 0 + go func() { + v = 1 + close(c) + }() + time.Sleep(1e7) + _ = len(c) + v = 2 +} + +func TestRaceChanSameCell(t *testing.T) { + c := make(chan int, 1) + v := 0 + go func() { + v = 1 + c <- 42 + <-c + }() + time.Sleep(1e7) + c <- 43 + <-c + _ = v +} + +func TestRaceChanCloseSend(t *testing.T) { + compl := make(chan bool, 1) + c := make(chan int, 10) + go func() { + close(c) + compl <- true + }() + c <- 0 + <-compl +} diff --git a/src/pkg/runtime/race/testdata/comp_test.go b/src/pkg/runtime/race/testdata/comp_test.go new file mode 100644 index 000000000..754e4db6d --- /dev/null +++ b/src/pkg/runtime/race/testdata/comp_test.go @@ -0,0 +1,132 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "testing" +) + +type P struct { + x, y int +} + +type S struct { + s1, s2 P +} + +func TestNoRaceComp(t *testing.T) { + c := make(chan bool, 1) + var s S + go func() { + s.s2.x = 1 + c <- true + }() + s.s2.y = 2 + <-c +} + +func TestNoRaceComp2(t *testing.T) { + c := make(chan bool, 1) + var s S + go func() { + s.s1.x = 1 + c <- true + }() + s.s1.y = 2 + <-c +} + +func TestRaceComp(t *testing.T) { + c := make(chan bool, 1) + var s S + go func() { + s.s2.y = 1 + c <- true + }() + s.s2.y = 2 + <-c +} + +func TestRaceComp2(t *testing.T) { + c := make(chan bool, 1) + var s S + go func() { + s.s1.x = 1 + c <- true + }() + s = S{} + <-c +} + +func TestRaceComp3(t *testing.T) { + c := make(chan bool, 1) + var s S + go func() { + s.s2.y = 1 + c <- true + }() + s = S{} + <-c +} + +func TestRaceCompArray(t *testing.T) { + c := make(chan bool, 1) + s := make([]S, 10) + x := 4 + go func() { + s[x].s2.y = 1 + c <- true + }() + x = 5 + <-c +} + +type Ptr struct { + s1, s2 *P +} + +func TestNoRaceCompPtr(t *testing.T) { + c := make(chan bool, 1) + p := Ptr{&P{}, &P{}} + go func() { + p.s1.x = 1 + c <- true + }() + p.s1.y = 2 + <-c +} + +func TestNoRaceCompPtr2(t *testing.T) { + c := make(chan bool, 1) + p := Ptr{&P{}, &P{}} + go func() { + p.s1.x = 1 + c <- true + }() + _ = p + <-c +} + +func TestRaceCompPtr(t *testing.T) { + c := make(chan bool, 1) + p := Ptr{&P{}, &P{}} + go func() { + p.s2.x = 1 + c <- true + }() + p.s2.x = 2 + <-c +} + +func TestRaceCompPtr2(t *testing.T) { + c := make(chan bool, 1) + p := Ptr{&P{}, &P{}} + go func() { + p.s2.x = 1 + c <- true + }() + p.s2 = &P{} + <-c +} diff --git a/src/pkg/runtime/race/testdata/finalizer_test.go b/src/pkg/runtime/race/testdata/finalizer_test.go new file mode 100644 index 000000000..2b2607689 --- /dev/null +++ b/src/pkg/runtime/race/testdata/finalizer_test.go @@ -0,0 +1,67 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "runtime" + "sync" + "testing" + "time" +) + +func TestNoRaceFin(t *testing.T) { + c := make(chan bool) + go func() { + x := new(int) + runtime.SetFinalizer(x, func(x *int) { + *x = 42 + }) + *x = 66 + c <- true + }() + <-c + runtime.GC() + time.Sleep(1e8) +} + +var finVar struct { + sync.Mutex + cnt int +} + +func TestNoRaceFinGlobal(t *testing.T) { + c := make(chan bool) + go func() { + x := new(int) + runtime.SetFinalizer(x, func(x *int) { + finVar.Lock() + finVar.cnt++ + finVar.Unlock() + }) + c <- true + }() + <-c + runtime.GC() + time.Sleep(1e8) + finVar.Lock() + finVar.cnt++ + finVar.Unlock() +} + +func TestRaceFin(t *testing.T) { + c := make(chan bool) + y := 0 + go func() { + x := new(int) + runtime.SetFinalizer(x, func(x *int) { + y = 42 + }) + c <- true + }() + <-c + runtime.GC() + time.Sleep(1e8) + y = 66 +} diff --git a/src/pkg/runtime/race/testdata/io_test.go b/src/pkg/runtime/race/testdata/io_test.go new file mode 100644 index 000000000..9eb3552dc --- /dev/null +++ b/src/pkg/runtime/race/testdata/io_test.go @@ -0,0 +1,69 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "testing" + "time" +) + +func TestNoRaceIOFile(t *testing.T) { + x := 0 + path, _ := ioutil.TempDir("", "race_test") + fname := filepath.Join(path, "data") + go func() { + x = 42 + f, _ := os.Create(fname) + f.Write([]byte("done")) + f.Close() + }() + for { + f, err := os.Open(fname) + if err != nil { + time.Sleep(1e6) + continue + } + buf := make([]byte, 100) + count, err := f.Read(buf) + if count == 0 { + time.Sleep(1e6) + continue + } + break + } + _ = x +} + +func TestNoRaceIOHttp(t *testing.T) { + x := 0 + go func() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + x = 41 + fmt.Fprintf(w, "test") + x = 42 + }) + err := http.ListenAndServe(":23651", nil) + if err != nil { + t.Fatalf("http.ListenAndServe: %v", err) + } + }() + time.Sleep(1e7) + x = 1 + _, err := http.Get("http://127.0.0.1:23651") + if err != nil { + t.Fatalf("http.Get: %v", err) + } + x = 2 + _, err = http.Get("http://127.0.0.1:23651") + if err != nil { + t.Fatalf("http.Get: %v", err) + } + x = 3 +} diff --git a/src/pkg/runtime/race/testdata/map_test.go b/src/pkg/runtime/race/testdata/map_test.go new file mode 100644 index 000000000..6f86a50b7 --- /dev/null +++ b/src/pkg/runtime/race/testdata/map_test.go @@ -0,0 +1,163 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "testing" +) + +func TestRaceMapRW(t *testing.T) { + m := make(map[int]int) + ch := make(chan bool, 1) + go func() { + _ = m[1] + ch <- true + }() + m[1] = 1 + <-ch +} + +func TestRaceMapRW2(t *testing.T) { + m := make(map[int]int) + ch := make(chan bool, 1) + go func() { + _, _ = m[1] + ch <- true + }() + m[1] = 1 + <-ch +} + +func TestRaceMapRWArray(t *testing.T) { + // Check instrumentation of unaddressable arrays (issue 4578). + m := make(map[int][2]int) + ch := make(chan bool, 1) + go func() { + _ = m[1][1] + ch <- true + }() + m[2] = [2]int{1, 2} + <-ch +} + +func TestNoRaceMapRR(t *testing.T) { + m := make(map[int]int) + ch := make(chan bool, 1) + go func() { + _, _ = m[1] + ch <- true + }() + _ = m[1] + <-ch +} + +func TestRaceMapRange(t *testing.T) { + m := make(map[int]int) + ch := make(chan bool, 1) + go func() { + for _ = range m { + } + ch <- true + }() + m[1] = 1 + <-ch +} + +func TestRaceMapRange2(t *testing.T) { + m := make(map[int]int) + ch := make(chan bool, 1) + go func() { + for _ = range m { + } + ch <- true + }() + m[1] = 1 + <-ch +} + +func TestNoRaceMapRangeRange(t *testing.T) { + m := make(map[int]int) + // now the map is not empty and range triggers an event + // should work without this (as in other tests) + // so it is suspicious if this test passes and others don't + m[0] = 0 + ch := make(chan bool, 1) + go func() { + for _ = range m { + } + ch <- true + }() + for _ = range m { + } + <-ch +} + +// Map len is not instrumented. +func TestRaceFailingMapLen(t *testing.T) { + m := make(map[string]bool) + ch := make(chan bool, 1) + go func() { + _ = len(m) + ch <- true + }() + m[""] = true + <-ch +} + +func TestRaceMapDelete(t *testing.T) { + m := make(map[string]bool) + ch := make(chan bool, 1) + go func() { + delete(m, "") + ch <- true + }() + m[""] = true + <-ch +} + +// Map len is not instrumented. +func TestRaceFailingMapLenDelete(t *testing.T) { + m := make(map[string]bool) + ch := make(chan bool, 1) + go func() { + delete(m, "a") + ch <- true + }() + _ = len(m) + <-ch +} + +func TestRaceMapVariable(t *testing.T) { + ch := make(chan bool, 1) + m := make(map[int]int) + go func() { + m = make(map[int]int) + ch <- true + }() + m = make(map[int]int) + <-ch +} + +func TestRaceMapVariable2(t *testing.T) { + ch := make(chan bool, 1) + m := make(map[int]int) + go func() { + m[1] = 1 + ch <- true + }() + m = make(map[int]int) + <-ch +} + +func TestRaceMapVariable3(t *testing.T) { + ch := make(chan bool, 1) + m := make(map[int]int) + go func() { + _ = m[1] + ch <- true + }() + m = make(map[int]int) + <-ch +} diff --git a/src/pkg/runtime/race/testdata/mop_test.go b/src/pkg/runtime/race/testdata/mop_test.go new file mode 100644 index 000000000..f2daa3730 --- /dev/null +++ b/src/pkg/runtime/race/testdata/mop_test.go @@ -0,0 +1,1384 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "errors" + "fmt" + "runtime" + "sync" + "testing" + "time" + "unsafe" +) + +type Point struct { + x, y int +} + +type NamedPoint struct { + name string + p Point +} + +type DummyWriter struct { + state int +} +type Writer interface { + Write(p []byte) (n int) +} + +func (d DummyWriter) Write(p []byte) (n int) { + return 0 +} + +var GlobalX, GlobalY int = 0, 0 +var GlobalCh chan int = make(chan int, 2) + +func GlobalFunc1() { + GlobalY = GlobalX + GlobalCh <- 1 +} + +func GlobalFunc2() { + GlobalX = 1 + GlobalCh <- 1 +} + +func TestRaceIntRWGlobalFuncs(t *testing.T) { + go GlobalFunc1() + go GlobalFunc2() + <-GlobalCh + <-GlobalCh +} + +func TestRaceIntRWClosures(t *testing.T) { + var x, y int + ch := make(chan int, 2) + + go func() { + y = x + ch <- 1 + }() + go func() { + x = 1 + ch <- 1 + }() + <-ch + <-ch +} + +func TestNoRaceIntRWClosures(t *testing.T) { + var x, y int + ch := make(chan int, 1) + + go func() { + y = x + ch <- 1 + }() + <-ch + go func() { + x = 1 + ch <- 1 + }() + <-ch + +} + +func TestRaceInt32RWClosures(t *testing.T) { + var x, y int32 + ch := make(chan bool, 2) + + go func() { + y = x + ch <- true + }() + go func() { + x = 1 + ch <- true + }() + <-ch + <-ch +} + +func TestNoRaceCase(t *testing.T) { + var y int + for x := -1; x <= 1; x++ { + switch { + case x < 0: + y = -1 + case x == 0: + y = 0 + case x > 0: + y = 1 + } + } + y++ +} + +func TestRaceCaseCondition(t *testing.T) { + var x int = 0 + ch := make(chan int, 2) + + go func() { + x = 2 + ch <- 1 + }() + go func() { + switch x < 2 { + case true: + x = 1 + //case false: + // x = 5 + } + ch <- 1 + }() + <-ch + <-ch +} + +func TestRaceCaseCondition2(t *testing.T) { + // switch body is rearranged by the compiler so the tests + // passes even if we don't instrument '<' + var x int = 0 + ch := make(chan int, 2) + + go func() { + x = 2 + ch <- 1 + }() + go func() { + switch x < 2 { + case true: + x = 1 + case false: + x = 5 + } + ch <- 1 + }() + <-ch + <-ch +} + +func TestRaceCaseBody(t *testing.T) { + var x, y int + ch := make(chan int, 2) + + go func() { + y = x + ch <- 1 + }() + go func() { + switch { + default: + x = 1 + case x == 100: + x = -x + } + ch <- 1 + }() + <-ch + <-ch +} + +func TestNoRaceCaseFallthrough(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + z = 1 + + go func() { + y = x + ch <- 1 + }() + go func() { + switch { + case z == 1: + case z == 2: + x = 2 + } + ch <- 1 + }() + <-ch + <-ch +} + +func TestRaceCaseFallthrough(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + z = 1 + + go func() { + y = x + ch <- 1 + }() + go func() { + switch { + case z == 1: + fallthrough + case z == 2: + x = 2 + } + ch <- 1 + }() + + <-ch + <-ch +} + +func TestNoRaceRange(t *testing.T) { + ch := make(chan int, 3) + a := [...]int{1, 2, 3} + for _, v := range a { + ch <- v + } + close(ch) +} + +func TestRaceRange(t *testing.T) { + const N = 2 + var a [N]int + var x, y int + done := make(chan bool, N) + for i, v := range a { + go func(i int) { + // we don't want a write-vs-write race + // so there is no array b here + if i == 0 { + x = v + } else { + y = v + } + done <- true + }(i) + } + for i := 0; i < N; i++ { + <-done + } +} + +func TestRacePlus(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + + go func() { + y = x + z + ch <- 1 + }() + go func() { + y = x + z + z + ch <- 1 + }() + <-ch + <-ch +} + +func TestRacePlus2(t *testing.T) { + var x, y, z int + ch := make(chan int, 2) + + go func() { + x = 1 + ch <- 1 + }() + go func() { + y = +x + z + ch <- 1 + }() + <-ch + <-ch +} + +func TestNoRacePlus(t *testing.T) { + var x, y, z, f int + ch := make(chan int, 2) + + go func() { + y = x + z + ch <- 1 + }() + go func() { + f = z + x + ch <- 1 + }() + <-ch + <-ch +} + +// May crash if the instrumentation is reckless. +func TestNoRaceEnoughRegisters(t *testing.T) { + // from erf.go + const ( + sa1 = 1 + sa2 = 2 + sa3 = 3 + sa4 = 4 + sa5 = 5 + sa6 = 6 + sa7 = 7 + sa8 = 8 + ) + var s, S float64 + s = 3.1415 + S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8))))))) + s = S +} + +// emptyFunc should not be inlined. +func emptyFunc(x int) { + if false { + fmt.Println(x) + } +} + +func TestRaceFuncArgument(t *testing.T) { + var x int + ch := make(chan bool, 1) + go func() { + emptyFunc(x) + ch <- true + }() + x = 1 + <-ch +} + +func TestRaceFuncArgument2(t *testing.T) { + var x int + ch := make(chan bool, 2) + go func() { + x = 42 + ch <- true + }() + go func(y int) { + ch <- true + }(x) + <-ch + <-ch +} + +func TestRaceSprint(t *testing.T) { + var x int + ch := make(chan bool, 1) + go func() { + fmt.Sprint(x) + ch <- true + }() + x = 1 + <-ch +} + +// Not implemented. +func TestRaceFailingArrayCopy(t *testing.T) { + ch := make(chan bool, 1) + var a [5]int + go func() { + a[3] = 1 + ch <- true + }() + a = [5]int{1, 2, 3, 4, 5} + <-ch +} + +func TestRaceStructRW(t *testing.T) { + p := Point{0, 0} + ch := make(chan bool, 1) + go func() { + p = Point{1, 1} + ch <- true + }() + q := p + <-ch + p = q +} + +func TestRaceStructFieldRW1(t *testing.T) { + p := Point{0, 0} + ch := make(chan bool, 1) + go func() { + p.x = 1 + ch <- true + }() + _ = p.x + <-ch +} + +func TestNoRaceStructFieldRW1(t *testing.T) { + // Same struct, different variables, no + // pointers. The layout is known (at compile time?) -> + // no read on p + // writes on x and y + p := Point{0, 0} + ch := make(chan bool, 1) + go func() { + p.x = 1 + ch <- true + }() + p.y = 1 + <-ch + _ = p +} + +func TestNoRaceStructFieldRW2(t *testing.T) { + // Same as NoRaceStructFieldRW1 + // but p is a pointer, so there is a read on p + p := Point{0, 0} + ch := make(chan bool, 1) + go func() { + p.x = 1 + ch <- true + }() + p.y = 1 + <-ch + _ = p +} + +func TestRaceStructFieldRW2(t *testing.T) { + p := &Point{0, 0} + ch := make(chan bool, 1) + go func() { + p.x = 1 + ch <- true + }() + _ = p.x + <-ch +} + +func TestRaceStructFieldRW3(t *testing.T) { + p := NamedPoint{name: "a", p: Point{0, 0}} + ch := make(chan bool, 1) + go func() { + p.p.x = 1 + ch <- true + }() + _ = p.p.x + <-ch +} + +func TestRaceEfaceWW(t *testing.T) { + var a, b interface{} + ch := make(chan bool, 1) + go func() { + a = 1 + ch <- true + }() + a = 2 + <-ch + _, _ = a, b +} + +func TestRaceIfaceWW(t *testing.T) { + var a, b Writer + ch := make(chan bool, 1) + go func() { + a = DummyWriter{1} + ch <- true + }() + a = DummyWriter{2} + <-ch + b = a + a = b +} + +func TestRaceEfaceConv(t *testing.T) { + c := make(chan bool) + v := 0 + go func() { + go func(x interface{}) { + }(v) + c <- true + }() + v = 42 + <-c +} + +type OsFile struct{} + +func (*OsFile) Read() { +} + +type IoReader interface { + Read() +} + +func TestRaceIfaceConv(t *testing.T) { + c := make(chan bool) + f := &OsFile{} + go func() { + go func(x IoReader) { + }(f) + c <- true + }() + f = &OsFile{} + <-c +} + +func TestRaceError(t *testing.T) { + ch := make(chan bool, 1) + var err error + go func() { + err = nil + ch <- true + }() + _ = err + <-ch +} + +func TestRaceIntptrRW(t *testing.T) { + var x, y int + var p *int = &x + ch := make(chan bool, 1) + go func() { + *p = 5 + ch <- true + }() + y = *p + x = y + <-ch +} + +func TestRaceStringRW(t *testing.T) { + ch := make(chan bool, 1) + s := "" + go func() { + s = "abacaba" + ch <- true + }() + _ = s + <-ch +} + +func TestRaceStringPtrRW(t *testing.T) { + ch := make(chan bool, 1) + var x string + p := &x + go func() { + *p = "a" + ch <- true + }() + _ = *p + <-ch +} + +func TestRaceFloat64WW(t *testing.T) { + var x, y float64 + ch := make(chan bool, 1) + go func() { + x = 1.0 + ch <- true + }() + x = 2.0 + <-ch + + y = x + x = y +} + +func TestRaceComplex128WW(t *testing.T) { + var x, y complex128 + ch := make(chan bool, 1) + go func() { + x = 2 + 2i + ch <- true + }() + x = 4 + 4i + <-ch + + y = x + x = y +} + +func TestRaceUnsafePtrRW(t *testing.T) { + var x, y, z int + x, y, z = 1, 2, 3 + var p unsafe.Pointer = unsafe.Pointer(&x) + ch := make(chan bool, 1) + go func() { + p = (unsafe.Pointer)(&z) + ch <- true + }() + y = *(*int)(p) + x = y + <-ch +} + +func TestRaceFuncVariableRW(t *testing.T) { + var f func(x int) int + f = func(x int) int { + return x * x + } + ch := make(chan bool, 1) + go func() { + f = func(x int) int { + return x + } + ch <- true + }() + y := f(1) + <-ch + x := y + y = x +} + +func TestRaceFuncVariableWW(t *testing.T) { + var f func(x int) int + ch := make(chan bool, 1) + go func() { + f = func(x int) int { + return x + } + ch <- true + }() + f = func(x int) int { + return x * x + } + <-ch +} + +// This one should not belong to mop_test +func TestRacePanic(t *testing.T) { + var x int + var zero int = 0 + ch := make(chan bool, 2) + go func() { + defer func() { + err := recover() + if err == nil { + panic("should be panicking") + } + x = 1 + ch <- true + }() + var y int = 1 / zero + zero = y + }() + go func() { + defer func() { + err := recover() + if err == nil { + panic("should be panicking") + } + x = 2 + ch <- true + }() + var y int = 1 / zero + zero = y + }() + + <-ch + <-ch + if zero != 0 { + panic("zero has changed") + } +} + +func TestNoRaceBlank(t *testing.T) { + var a [5]int + ch := make(chan bool, 1) + go func() { + _, _ = a[0], a[1] + ch <- true + }() + _, _ = a[2], a[3] + <-ch + a[1] = a[0] +} + +func TestRaceAppendRW(t *testing.T) { + a := make([]int, 10) + ch := make(chan bool) + go func() { + _ = append(a, 1) + ch <- true + }() + a[0] = 1 + <-ch +} + +func TestRaceAppendLenRW(t *testing.T) { + a := make([]int, 0) + ch := make(chan bool) + go func() { + a = append(a, 1) + ch <- true + }() + _ = len(a) + <-ch +} + +func TestRaceAppendCapRW(t *testing.T) { + a := make([]int, 0) + ch := make(chan string) + go func() { + a = append(a, 1) + ch <- "" + }() + _ = cap(a) + <-ch +} + +func TestNoRaceFuncArgsRW(t *testing.T) { + ch := make(chan byte, 1) + var x byte + go func(y byte) { + _ = y + ch <- 0 + }(x) + x = 1 + <-ch +} + +func TestRaceFuncArgsRW(t *testing.T) { + ch := make(chan byte, 1) + var x byte + go func(y *byte) { + _ = *y + ch <- 0 + }(&x) + x = 1 + <-ch +} + +// from the mailing list, slightly modified +// unprotected concurrent access to seen[] +func TestRaceCrawl(t *testing.T) { + url := "dummyurl" + depth := 3 + seen := make(map[string]bool) + ch := make(chan int, 100) + var wg sync.WaitGroup + var crawl func(string, int) + crawl = func(u string, d int) { + nurl := 0 + defer func() { + ch <- nurl + }() + seen[u] = true + if d <= 0 { + return + } + urls := [...]string{"a", "b", "c"} + for _, uu := range urls { + if _, ok := seen[uu]; !ok { + wg.Add(1) + go crawl(uu, d-1) + nurl++ + } + } + wg.Done() + } + wg.Add(1) + go crawl(url, depth) + wg.Wait() +} + +func TestRaceIndirection(t *testing.T) { + ch := make(chan struct{}, 1) + var y int + var x *int = &y + go func() { + *x = 1 + ch <- struct{}{} + }() + *x = 2 + <-ch + _ = *x +} + +func TestRaceRune(t *testing.T) { + c := make(chan bool) + var x rune + go func() { + x = 1 + c <- true + }() + _ = x + <-c +} + +func TestRaceEmptyInterface1(t *testing.T) { + c := make(chan bool) + var x interface{} + go func() { + x = nil + c <- true + }() + _ = x + <-c +} + +func TestRaceEmptyInterface2(t *testing.T) { + c := make(chan bool) + var x interface{} + go func() { + x = &Point{} + c <- true + }() + _ = x + <-c +} + +func TestRaceTLS(t *testing.T) { + comm := make(chan *int) + done := make(chan bool, 2) + go func() { + var x int + comm <- &x + x = 1 + x = *(<-comm) + done <- true + }() + go func() { + p := <-comm + *p = 2 + comm <- p + done <- true + }() + <-done + <-done +} + +func TestNoRaceHeapReallocation(t *testing.T) { + // It is possible that a future implementation + // of memory allocation will ruin this test. + // Increasing n might help in this case, so + // this test is a bit more generic than most of the + // others. + const n = 2 + done := make(chan bool, n) + empty := func(p *int) {} + for i := 0; i < n; i++ { + ms := i + go func() { + <-time.After(time.Duration(ms) * time.Millisecond) + runtime.GC() + var x int + empty(&x) // x goes to the heap + done <- true + }() + } + for i := 0; i < n; i++ { + <-done + } +} + +func TestRaceAnd(t *testing.T) { + c := make(chan bool) + x, y := 0, 0 + go func() { + x = 1 + c <- true + }() + if x == 1 && y == 1 { + } + <-c +} + +// OANDAND is not instrumented in the compiler. +func TestRaceFailingAnd2(t *testing.T) { + c := make(chan bool) + x, y := 0, 0 + go func() { + x = 1 + c <- true + }() + if y == 0 && x == 1 { + } + <-c +} + +func TestNoRaceAnd(t *testing.T) { + c := make(chan bool) + x, y := 0, 0 + go func() { + x = 1 + c <- true + }() + if y == 1 && x == 1 { + } + <-c +} + +func TestRaceOr(t *testing.T) { + c := make(chan bool) + x, y := 0, 0 + go func() { + x = 1 + c <- true + }() + if x == 1 || y == 1 { + } + <-c +} + +// OOROR is not instrumented in the compiler. +func TestRaceFailingOr2(t *testing.T) { + c := make(chan bool) + x, y := 0, 0 + go func() { + x = 1 + c <- true + }() + if y == 1 || x == 1 { + } + <-c +} + +func TestNoRaceOr(t *testing.T) { + c := make(chan bool) + x, y := 0, 0 + go func() { + x = 1 + c <- true + }() + if y == 0 || x == 1 { + } + <-c +} + +func TestNoRaceShortCalc(t *testing.T) { + c := make(chan bool) + x, y := 0, 0 + go func() { + y = 1 + c <- true + }() + if x == 0 || y == 0 { + } + <-c +} + +func TestNoRaceShortCalc2(t *testing.T) { + c := make(chan bool) + x, y := 0, 0 + go func() { + y = 1 + c <- true + }() + if x == 1 && y == 0 { + } + <-c +} + +func TestRaceFuncItself(t *testing.T) { + c := make(chan bool) + f := func() {} + go func() { + f() + c <- true + }() + f = func() {} + <-c +} + +func TestNoRaceFuncUnlock(t *testing.T) { + ch := make(chan bool, 1) + var mu sync.Mutex + x := 0 + go func() { + mu.Lock() + x = 42 + mu.Unlock() + ch <- true + }() + x = func(mu *sync.Mutex) int { + mu.Lock() + return 43 + }(&mu) + mu.Unlock() + <-ch +} + +func TestRaceStructInit(t *testing.T) { + type X struct { + x, y int + } + c := make(chan bool, 1) + y := 0 + go func() { + y = 42 + c <- true + }() + x := X{x: y} + _ = x + <-c +} + +func TestRaceArrayInit(t *testing.T) { + c := make(chan bool, 1) + y := 0 + go func() { + y = 42 + c <- true + }() + x := []int{0, y, 42} + _ = x + <-c +} + +func TestRaceMapInit(t *testing.T) { + c := make(chan bool, 1) + y := 0 + go func() { + y = 42 + c <- true + }() + x := map[int]int{0: 42, y: 42} + _ = x + <-c +} + +func TestRaceMapInit2(t *testing.T) { + c := make(chan bool, 1) + y := 0 + go func() { + y = 42 + c <- true + }() + x := map[int]int{0: 42, 42: y} + _ = x + <-c +} + +type Inter interface { + Foo(x int) +} +type InterImpl struct { + x, y int +} + +func (p InterImpl) Foo(x int) { + // prevent inlining + z := 42 + x = 85 + y := x / z + z = y * z + x = z * y + _, _, _ = x, y, z +} + +func TestRaceInterCall(t *testing.T) { + c := make(chan bool, 1) + p := InterImpl{} + var x Inter = p + go func() { + p2 := InterImpl{} + x = p2 + c <- true + }() + x.Foo(0) + <-c +} + +func TestRaceInterCall2(t *testing.T) { + c := make(chan bool, 1) + p := InterImpl{} + var x Inter = p + z := 0 + go func() { + z = 42 + c <- true + }() + x.Foo(z) + <-c +} + +func TestRaceFuncCall(t *testing.T) { + c := make(chan bool, 1) + f := func(x, y int) {} + x, y := 0, 0 + go func() { + y = 42 + c <- true + }() + f(x, y) + <-c +} + +func TestRaceMethodCall(t *testing.T) { + c := make(chan bool, 1) + i := InterImpl{} + x := 0 + go func() { + x = 42 + c <- true + }() + i.Foo(x) + <-c +} + +func TestRaceMethodCall2(t *testing.T) { + c := make(chan bool, 1) + i := &InterImpl{} + go func() { + i = &InterImpl{} + c <- true + }() + i.Foo(0) + <-c +} + +func TestRacePanicArg(t *testing.T) { + c := make(chan bool, 1) + err := errors.New("err") + go func() { + err = errors.New("err2") + c <- true + }() + defer func() { + recover() + <-c + }() + panic(err) +} + +func TestRaceDeferArg(t *testing.T) { + c := make(chan bool, 1) + x := 0 + go func() { + x = 42 + c <- true + }() + func() { + defer func(x int) { + }(x) + }() + <-c +} + +type DeferT int + +func (d DeferT) Foo() { +} + +func TestRaceDeferArg2(t *testing.T) { + c := make(chan bool, 1) + var x DeferT + go func() { + var y DeferT + x = y + c <- true + }() + func() { + defer x.Foo() + }() + <-c +} + +func TestNoRaceAddrExpr(t *testing.T) { + c := make(chan bool, 1) + x := 0 + go func() { + x = 42 + c <- true + }() + _ = &x + <-c +} + +type AddrT struct { + _ [256]byte + x int +} + +type AddrT2 struct { + _ [512]byte + p *AddrT +} + +func TestRaceAddrExpr(t *testing.T) { + c := make(chan bool, 1) + a := AddrT2{p: &AddrT{x: 42}} + go func() { + a.p = &AddrT{x: 43} + c <- true + }() + _ = &a.p.x + <-c +} + +func TestRaceTypeAssert(t *testing.T) { + c := make(chan bool, 1) + x := 0 + var i interface{} = x + go func() { + y := 0 + i = y + c <- true + }() + _ = i.(int) + <-c +} + +func TestRaceBlockAs(t *testing.T) { + c := make(chan bool, 1) + var x, y int + go func() { + x = 42 + c <- true + }() + x, y = y, x + <-c +} + +func TestRaceSliceSlice(t *testing.T) { + c := make(chan bool, 1) + x := make([]int, 10) + go func() { + x = make([]int, 20) + c <- true + }() + _ = x[2:3] + <-c +} + +func TestRaceSliceSlice2(t *testing.T) { + c := make(chan bool, 1) + x := make([]int, 10) + i := 2 + go func() { + i = 3 + c <- true + }() + _ = x[i:4] + <-c +} + +// http://golang.org/issue/4453 +func TestRaceFailingSliceStruct(t *testing.T) { + type X struct { + x, y int + } + c := make(chan bool, 1) + x := make([]X, 10) + go func() { + y := make([]X, 10) + copy(y, x) + c <- true + }() + x[1].y = 42 + <-c +} + +func TestRaceStructInd(t *testing.T) { + c := make(chan bool, 1) + type Item struct { + x, y int + } + i := Item{} + go func(p *Item) { + *p = Item{} + c <- true + }(&i) + i.y = 42 + <-c +} + +func TestRaceAsFunc1(t *testing.T) { + var s []byte + c := make(chan bool, 1) + go func() { + var err error + s, err = func() ([]byte, error) { + t := []byte("hello world") + return t, nil + }() + c <- true + _ = err + }() + _ = string(s) + <-c +} + +func TestRaceAsFunc2(t *testing.T) { + c := make(chan bool, 1) + x := 0 + go func() { + func(x int) { + }(x) + c <- true + }() + x = 42 + <-c +} + +func TestRaceAsFunc3(t *testing.T) { + c := make(chan bool, 1) + var mu sync.Mutex + x := 0 + go func() { + func(x int) { + mu.Lock() + }(x) // Read of x must be outside of the mutex. + mu.Unlock() + c <- true + }() + mu.Lock() + x = 42 + mu.Unlock() + <-c +} + +func TestNoRaceAsFunc4(t *testing.T) { + c := make(chan bool, 1) + var mu sync.Mutex + x := 0 + go func() { + x = func() int { // Write of x must be under the mutex. + mu.Lock() + return 42 + }() + mu.Unlock() + c <- true + }() + mu.Lock() + x = 42 + mu.Unlock() + <-c +} + +func TestRaceHeapParam(t *testing.T) { + x := func() (x int) { + go func() { + x = 42 + }() + return + }() + _ = x +} + +func TestNoRaceEmptyStruct(t *testing.T) { + type Empty struct{} + type X struct { + y int64 + Empty + } + type Y struct { + x X + y int64 + } + c := make(chan X) + var y Y + go func() { + x := y.x + c <- x + }() + y.y = 42 + <-c +} + +func TestRaceNestedStruct(t *testing.T) { + type X struct { + x, y int + } + type Y struct { + x X + } + c := make(chan Y) + var y Y + go func() { + c <- y + }() + y.x.y = 42 + <-c +} diff --git a/src/pkg/runtime/race/testdata/mutex_test.go b/src/pkg/runtime/race/testdata/mutex_test.go new file mode 100644 index 000000000..3cf03ae6b --- /dev/null +++ b/src/pkg/runtime/race/testdata/mutex_test.go @@ -0,0 +1,138 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "sync" + "testing" + "time" +) + +func TestNoRaceMutex(t *testing.T) { + var mu sync.Mutex + var x int16 = 0 + ch := make(chan bool, 2) + go func() { + mu.Lock() + defer mu.Unlock() + x = 1 + ch <- true + }() + go func() { + mu.Lock() + x = 2 + mu.Unlock() + ch <- true + }() + <-ch + <-ch +} + +func TestRaceMutex(t *testing.T) { + var mu sync.Mutex + var x int16 = 0 + ch := make(chan bool, 2) + go func() { + x = 1 + mu.Lock() + defer mu.Unlock() + ch <- true + }() + go func() { + x = 2 + mu.Lock() + mu.Unlock() + ch <- true + }() + <-ch + <-ch +} + +func TestRaceMutex2(t *testing.T) { + var mu1 sync.Mutex + var mu2 sync.Mutex + var x int8 = 0 + ch := make(chan bool, 2) + go func() { + mu1.Lock() + defer mu1.Unlock() + x = 1 + ch <- true + }() + go func() { + mu2.Lock() + x = 2 + mu2.Unlock() + ch <- true + }() + <-ch + <-ch +} + +func TestNoRaceMutexPureHappensBefore(t *testing.T) { + var mu sync.Mutex + var x int16 = 0 + ch := make(chan bool, 2) + go func() { + x = 1 + mu.Lock() + mu.Unlock() + ch <- true + }() + go func() { + <-time.After(1e5) + mu.Lock() + mu.Unlock() + x = 1 + ch <- true + }() + <-ch + <-ch +} + +func TestNoRaceMutexSemaphore(t *testing.T) { + var mu sync.Mutex + ch := make(chan bool, 2) + x := 0 + mu.Lock() + go func() { + x = 1 + mu.Unlock() + ch <- true + }() + go func() { + mu.Lock() + x = 2 + mu.Unlock() + ch <- true + }() + <-ch + <-ch +} + +// from doc/go_mem.html +func TestNoRaceMutexExampleFromHtml(t *testing.T) { + var l sync.Mutex + a := "" + + l.Lock() + go func() { + a = "hello, world" + l.Unlock() + }() + l.Lock() + _ = a +} + +func TestRaceMutexOverwrite(t *testing.T) { + c := make(chan bool, 1) + var mu sync.Mutex + go func() { + mu = sync.Mutex{} + c <- true + }() + mu.Lock() + <-c +} diff --git a/src/pkg/runtime/race/testdata/regression_test.go b/src/pkg/runtime/race/testdata/regression_test.go new file mode 100644 index 000000000..afe8cc5ec --- /dev/null +++ b/src/pkg/runtime/race/testdata/regression_test.go @@ -0,0 +1,150 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code patterns that caused problems in the past. + +package race_test + +import ( + "testing" +) + +type LogImpl struct { + x int +} + +func NewLog() (l LogImpl) { + c := make(chan bool) + go func() { + _ = l + c <- true + }() + l = LogImpl{} + <-c + return +} + +var _ LogImpl = NewLog() + +func MakeMap() map[int]int { + return make(map[int]int) +} + +func InstrumentMapLen() { + _ = len(MakeMap()) +} + +func InstrumentMapLen2() { + m := make(map[int]map[int]int) + _ = len(m[0]) +} + +func InstrumentMapLen3() { + m := make(map[int]*map[int]int) + _ = len(*m[0]) +} + +type Rect struct { + x, y int +} + +type Image struct { + min, max Rect +} + +func NewImage() Image { + var pleaseDoNotInlineMe stack + pleaseDoNotInlineMe.push(1) + _ = pleaseDoNotInlineMe.pop() + return Image{} +} + +func AddrOfTemp() { + _ = NewImage().min +} + +type TypeID int + +func (t *TypeID) encodeType(x int) (tt TypeID, err error) { + switch x { + case 0: + return t.encodeType(x * x) + } + return 0, nil +} + +type stack []int + +func (s *stack) push(x int) { + *s = append(*s, x) +} + +func (s *stack) pop() int { + i := len(*s) + n := (*s)[i-1] + *s = (*s)[:i-1] + return n +} + +func TestNoRaceStackPushPop(t *testing.T) { + var s stack + go func(s *stack) {}(&s) + s.push(1) + x := s.pop() + _ = x +} + +type RpcChan struct { + c chan bool +} + +var makeChanCalls int + +func makeChan() *RpcChan { + var pleaseDoNotInlineMe stack + pleaseDoNotInlineMe.push(1) + _ = pleaseDoNotInlineMe.pop() + + makeChanCalls++ + c := &RpcChan{make(chan bool, 1)} + c.c <- true + return c +} + +func call() bool { + x := <-makeChan().c + return x +} + +func TestNoRaceRpcChan(t *testing.T) { + makeChanCalls = 0 + _ = call() + if makeChanCalls != 1 { + t.Fatalf("makeChanCalls %d, expected 1\n", makeChanCalls) + } +} + +func divInSlice() { + v := make([]int64, 10) + i := 1 + _ = v[(i*4)/3] +} + +func TestNoRaceReturn(t *testing.T) { + c := make(chan int) + noRaceReturn(c) + <-c +} + +// Return used to do an implicit a = a, causing a read/write race +// with the goroutine. Compiler has an optimization to avoid that now. +// See issue 4014. +func noRaceReturn(c chan int) (a, b int) { + a = 42 + go func() { + _ = a + c <- 1 + }() + return a, 10 +} diff --git a/src/pkg/runtime/race/testdata/rwmutex_test.go b/src/pkg/runtime/race/testdata/rwmutex_test.go new file mode 100644 index 000000000..85cb5df3c --- /dev/null +++ b/src/pkg/runtime/race/testdata/rwmutex_test.go @@ -0,0 +1,134 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "sync" + "testing" + "time" +) + +func TestRaceMutexRWMutex(t *testing.T) { + var mu1 sync.Mutex + var mu2 sync.RWMutex + var x int16 = 0 + ch := make(chan bool, 2) + go func() { + mu1.Lock() + defer mu1.Unlock() + x = 1 + ch <- true + }() + go func() { + mu2.Lock() + x = 2 + mu2.Unlock() + ch <- true + }() + <-ch + <-ch +} + +func TestNoRaceRWMutex(t *testing.T) { + var mu sync.RWMutex + var x, y int64 = 0, 1 + ch := make(chan bool, 2) + go func() { + mu.Lock() + defer mu.Unlock() + x = 2 + ch <- true + }() + go func() { + mu.RLock() + y = x + mu.RUnlock() + ch <- true + }() + <-ch + <-ch +} + +func TestRaceRWMutexMultipleReaders(t *testing.T) { + var mu sync.RWMutex + var x, y int64 = 0, 1 + ch := make(chan bool, 3) + go func() { + mu.Lock() + defer mu.Unlock() + x = 2 + ch <- true + }() + go func() { + mu.RLock() + y = x + 1 + mu.RUnlock() + ch <- true + }() + go func() { + mu.RLock() + y = x + 2 + mu.RUnlock() + ch <- true + }() + <-ch + <-ch + <-ch + _ = y +} + +func TestNoRaceRWMutexMultipleReaders(t *testing.T) { + var mu sync.RWMutex + x := int64(0) + ch := make(chan bool, 3) + go func() { + mu.Lock() + defer mu.Unlock() + x = 2 + ch <- true + }() + go func() { + mu.RLock() + y := x + 1 + _ = y + mu.RUnlock() + ch <- true + }() + go func() { + mu.RLock() + y := x + 2 + _ = y + mu.RUnlock() + ch <- true + }() + <-ch + <-ch + <-ch +} + +func TestNoRaceRWMutexTransitive(t *testing.T) { + var mu sync.RWMutex + x := int64(0) + ch := make(chan bool, 2) + go func() { + mu.RLock() + _ = x + mu.RUnlock() + ch <- true + }() + go func() { + time.Sleep(1e7) + mu.RLock() + _ = x + mu.RUnlock() + ch <- true + }() + time.Sleep(2e7) + mu.Lock() + x = 42 + mu.Unlock() + <-ch + <-ch +} diff --git a/src/pkg/runtime/race/testdata/select_test.go b/src/pkg/runtime/race/testdata/select_test.go new file mode 100644 index 000000000..4a3a23647 --- /dev/null +++ b/src/pkg/runtime/race/testdata/select_test.go @@ -0,0 +1,286 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "runtime" + "testing" +) + +func TestNoRaceSelect1(t *testing.T) { + var x int + compl := make(chan bool) + c := make(chan bool) + c1 := make(chan bool) + + go func() { + x = 1 + // At least two channels are needed because + // otherwise the compiler optimizes select out. + // See comment in runtime/chan.c:^selectgo. + select { + case c <- true: + case c1 <- true: + } + compl <- true + }() + select { + case <-c: + case c1 <- true: + } + x = 2 + <-compl +} + +func TestNoRaceSelect2(t *testing.T) { + var x int + compl := make(chan bool) + c := make(chan bool) + c1 := make(chan bool) + go func() { + select { + case <-c: + case <-c1: + } + x = 1 + compl <- true + }() + x = 2 + close(c) + runtime.Gosched() + <-compl +} + +func TestNoRaceSelect3(t *testing.T) { + var x int + compl := make(chan bool) + c := make(chan bool, 10) + c1 := make(chan bool) + go func() { + x = 1 + select { + case c <- true: + case <-c1: + } + compl <- true + }() + <-c + x = 2 + <-compl +} + +func TestNoRaceSelect4(t *testing.T) { + type Task struct { + f func() + done chan bool + } + + queue := make(chan Task) + dummy := make(chan bool) + + go func() { + for { + select { + case t := <-queue: + t.f() + t.done <- true + } + } + }() + + doit := func(f func()) { + done := make(chan bool, 1) + select { + case queue <- Task{f, done}: + case <-dummy: + } + select { + case <-done: + case <-dummy: + } + } + + var x int + doit(func() { + x = 1 + }) + _ = x +} + +func TestNoRaceSelect5(t *testing.T) { + test := func(sel, needSched bool) { + var x int + ch := make(chan bool) + c1 := make(chan bool) + + done := make(chan bool, 2) + go func() { + if needSched { + runtime.Gosched() + } + // println(1) + x = 1 + if sel { + select { + case ch <- true: + case <-c1: + } + } else { + ch <- true + } + done <- true + }() + + go func() { + // println(2) + if sel { + select { + case <-ch: + case <-c1: + } + } else { + <-ch + } + x = 1 + done <- true + }() + <-done + <-done + } + + test(true, true) + test(true, false) + test(false, true) + test(false, false) +} + +func TestRaceSelect1(t *testing.T) { + var x int + compl := make(chan bool, 2) + c := make(chan bool) + c1 := make(chan bool) + + go func() { + <-c + <-c + }() + f := func() { + select { + case c <- true: + case c1 <- true: + } + x = 1 + compl <- true + } + go f() + go f() + <-compl + <-compl +} + +func TestRaceSelect2(t *testing.T) { + var x int + compl := make(chan bool) + c := make(chan bool) + c1 := make(chan bool) + go func() { + x = 1 + select { + case <-c: + case <-c1: + } + compl <- true + }() + close(c) + x = 2 + <-compl +} + +func TestRaceSelect3(t *testing.T) { + var x int + compl := make(chan bool) + c := make(chan bool) + c1 := make(chan bool) + go func() { + x = 1 + select { + case c <- true: + case c1 <- true: + } + compl <- true + }() + x = 2 + select { + case <-c: + } + <-compl +} + +func TestRaceSelect4(t *testing.T) { + done := make(chan bool, 1) + var x int + go func() { + select { + default: + x = 2 + } + done <- true + }() + _ = x + <-done +} + +// The idea behind this test: +// there are two variables, access to one +// of them is synchronized, access to the other +// is not. +// Select must (unconditionaly) choose the non-synchronized variable +// thus causing exactly one race. +// Currently this test doesn't look like it accomplishes +// this goal. +func TestRaceSelect5(t *testing.T) { + done := make(chan bool, 1) + c1 := make(chan bool, 1) + c2 := make(chan bool) + var x, y int + go func() { + select { + case c1 <- true: + x = 1 + case c2 <- true: + y = 1 + } + done <- true + }() + _ = x + _ = y + <-done +} + +// select statements may introduce +// flakiness: whether this test contains +// a race depends on the scheduling +// (some may argue that the code contains +// this race by definition) +/* +func TestFlakyDefault(t *testing.T) { + var x int + c := make(chan bool, 1) + done := make(chan bool, 1) + go func() { + select { + case <-c: + x = 2 + default: + x = 3 + } + done <- true + }() + x = 1 + c <- true + _ = x + <-done +} +*/ diff --git a/src/pkg/runtime/race/testdata/slice_test.go b/src/pkg/runtime/race/testdata/slice_test.go new file mode 100644 index 000000000..773463662 --- /dev/null +++ b/src/pkg/runtime/race/testdata/slice_test.go @@ -0,0 +1,465 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "testing" +) + +func TestRaceSliceRW(t *testing.T) { + ch := make(chan bool, 1) + a := make([]int, 2) + go func() { + a[1] = 1 + ch <- true + }() + _ = a[1] + <-ch +} + +func TestNoRaceSliceRW(t *testing.T) { + ch := make(chan bool, 1) + a := make([]int, 2) + go func() { + a[0] = 1 + ch <- true + }() + _ = a[1] + <-ch +} + +func TestRaceSliceWW(t *testing.T) { + a := make([]int, 10) + ch := make(chan bool, 1) + go func() { + a[1] = 1 + ch <- true + }() + a[1] = 2 + <-ch +} + +func TestNoRaceArrayWW(t *testing.T) { + var a [5]int + ch := make(chan bool, 1) + go func() { + a[0] = 1 + ch <- true + }() + a[1] = 2 + <-ch +} + +func TestRaceArrayWW(t *testing.T) { + var a [5]int + ch := make(chan bool, 1) + go func() { + a[1] = 1 + ch <- true + }() + a[1] = 2 + <-ch +} + +func TestNoRaceSliceWriteLen(t *testing.T) { + ch := make(chan bool, 1) + a := make([]bool, 1) + go func() { + a[0] = true + ch <- true + }() + _ = len(a) + <-ch +} + +func TestNoRaceSliceWriteCap(t *testing.T) { + ch := make(chan bool, 1) + a := make([]uint64, 100) + go func() { + a[50] = 123 + ch <- true + }() + _ = cap(a) + <-ch +} + +func TestRaceSliceCopyRead(t *testing.T) { + ch := make(chan bool, 1) + a := make([]int, 10) + b := make([]int, 10) + go func() { + _ = a[5] + ch <- true + }() + copy(a, b) + <-ch +} + +func TestNoRaceSliceWriteCopy(t *testing.T) { + ch := make(chan bool, 1) + a := make([]int, 10) + b := make([]int, 10) + go func() { + a[5] = 1 + ch <- true + }() + copy(a[:5], b[:5]) + <-ch +} + +func TestRaceSliceCopyWrite2(t *testing.T) { + ch := make(chan bool, 1) + a := make([]int, 10) + b := make([]int, 10) + go func() { + b[5] = 1 + ch <- true + }() + copy(a, b) + <-ch +} + +func TestRaceSliceCopyWrite3(t *testing.T) { + ch := make(chan bool, 1) + a := make([]byte, 10) + go func() { + a[7] = 1 + ch <- true + }() + copy(a, "qwertyqwerty") + <-ch +} + +func TestNoRaceSliceCopyRead(t *testing.T) { + ch := make(chan bool, 1) + a := make([]int, 10) + b := make([]int, 10) + go func() { + _ = b[5] + ch <- true + }() + copy(a, b) + <-ch +} + +func TestNoRaceSliceWriteSlice2(t *testing.T) { + ch := make(chan bool, 1) + a := make([]float64, 10) + go func() { + a[2] = 1.0 + ch <- true + }() + _ = a[0:5] + <-ch +} + +func TestRaceSliceWriteSlice(t *testing.T) { + ch := make(chan bool, 1) + a := make([]float64, 10) + go func() { + a[2] = 1.0 + ch <- true + }() + a = a[5:10] + <-ch +} + +func TestNoRaceSliceWriteSlice(t *testing.T) { + ch := make(chan bool, 1) + a := make([]float64, 10) + go func() { + a[2] = 1.0 + ch <- true + }() + _ = a[5:10] + <-ch +} + +func TestNoRaceSliceLenCap(t *testing.T) { + ch := make(chan bool, 1) + a := make([]struct{}, 10) + go func() { + _ = len(a) + ch <- true + }() + _ = cap(a) + <-ch +} + +func TestNoRaceStructSlicesRangeWrite(t *testing.T) { + type Str struct { + a []int + b []int + } + ch := make(chan bool, 1) + var s Str + s.a = make([]int, 10) + s.b = make([]int, 10) + go func() { + for _ = range s.a { + } + ch <- true + }() + s.b[5] = 5 + <-ch +} + +func TestRaceSliceDifferent(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + s2 := s + go func() { + s[3] = 3 + c <- true + }() + // false negative because s2 is PAUTO w/o PHEAP + // so we do not instrument it + s2[3] = 3 + <-c +} + +func TestRaceSliceRangeWrite(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + s[3] = 3 + c <- true + }() + for _, v := range s { + _ = v + } + <-c +} + +func TestNoRaceSliceRangeWrite(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + s[3] = 3 + c <- true + }() + for _ = range s { + } + <-c +} + +func TestRaceSliceRangeAppend(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + s = append(s, 3) + c <- true + }() + for _ = range s { + } + <-c +} + +func TestNoRaceSliceRangeAppend(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + _ = append(s, 3) + c <- true + }() + for _ = range s { + } + <-c +} + +func TestRaceSliceVarWrite(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + s[3] = 3 + c <- true + }() + s = make([]int, 20) + <-c +} + +func TestRaceSliceVarRead(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + _ = s[3] + c <- true + }() + s = make([]int, 20) + <-c +} + +func TestRaceSliceVarRange(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + for _ = range s { + } + c <- true + }() + s = make([]int, 20) + <-c +} + +func TestRaceSliceVarAppend(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + _ = append(s, 10) + c <- true + }() + s = make([]int, 20) + <-c +} + +func TestRaceSliceVarCopy(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + s2 := make([]int, 10) + copy(s, s2) + c <- true + }() + s = make([]int, 20) + <-c +} + +func TestRaceSliceVarCopy2(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + s2 := make([]int, 10) + copy(s2, s) + c <- true + }() + s = make([]int, 20) + <-c +} + +// Not implemented. +func TestRaceFailingSliceAppend(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10, 20) + go func() { + _ = append(s, 1) + c <- true + }() + _ = append(s, 2) + <-c +} + +func TestRaceSliceAppendWrite(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + _ = append(s, 1) + c <- true + }() + s[0] = 42 + <-c +} + +func TestRaceSliceAppendSlice(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + go func() { + s2 := make([]int, 10) + _ = append(s, s2...) + c <- true + }() + s[0] = 42 + <-c +} + +func TestRaceSliceAppendSlice2(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + s2foobar := make([]int, 10) + go func() { + _ = append(s, s2foobar...) + c <- true + }() + s2foobar[5] = 42 + <-c +} + +func TestRaceSliceAppendString(t *testing.T) { + c := make(chan bool, 1) + s := make([]byte, 10) + go func() { + _ = append(s, "qwerty"...) + c <- true + }() + s[0] = 42 + <-c +} + +func TestNoRaceSliceIndexAccess(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + v := 0 + go func() { + _ = v + c <- true + }() + s[v] = 1 + <-c +} + +func TestNoRaceSliceIndexAccess2(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + v := 0 + go func() { + _ = v + c <- true + }() + _ = s[v] + <-c +} + +func TestRaceSliceIndexAccess(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + v := 0 + go func() { + v = 1 + c <- true + }() + s[v] = 1 + <-c +} + +func TestRaceSliceIndexAccess2(t *testing.T) { + c := make(chan bool, 1) + s := make([]int, 10) + v := 0 + go func() { + v = 1 + c <- true + }() + _ = s[v] + <-c +} + +func TestRaceSliceByteToString(t *testing.T) { + c := make(chan string) + s := make([]byte, 10) + go func() { + c <- string(s) + }() + s[0] = 42 + <-c +} + +func TestRaceSliceRuneToString(t *testing.T) { + c := make(chan string) + s := make([]rune, 10) + go func() { + c <- string(s) + }() + s[9] = 42 + <-c +} diff --git a/src/pkg/runtime/race/testdata/sync_test.go b/src/pkg/runtime/race/testdata/sync_test.go new file mode 100644 index 000000000..e80ba3b74 --- /dev/null +++ b/src/pkg/runtime/race/testdata/sync_test.go @@ -0,0 +1,197 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "sync" + "testing" + "time" +) + +func TestNoRaceCond(t *testing.T) { // tsan's test02 + ch := make(chan bool, 1) + var x int = 0 + var mu sync.Mutex + var cond *sync.Cond = sync.NewCond(&mu) + var condition int = 0 + var waker func() + waker = func() { + x = 1 + mu.Lock() + condition = 1 + cond.Signal() + mu.Unlock() + } + + var waiter func() + waiter = func() { + go waker() + cond.L.Lock() + for condition != 1 { + cond.Wait() + } + cond.L.Unlock() + x = 2 + ch <- true + } + go waiter() + <-ch +} + +func TestRaceCond(t *testing.T) { // tsan's test50 + ch := make(chan bool, 2) + + var x int = 0 + var mu sync.Mutex + var condition int = 0 + var cond *sync.Cond = sync.NewCond(&mu) + + var waker func() = func() { + <-time.After(1e5) + x = 1 + mu.Lock() + condition = 1 + cond.Signal() + mu.Unlock() + <-time.After(1e5) + mu.Lock() + x = 3 + mu.Unlock() + ch <- true + } + + var waiter func() = func() { + mu.Lock() + for condition != 1 { + cond.Wait() + } + mu.Unlock() + x = 2 + ch <- true + } + x = 0 + go waker() + go waiter() + <-ch + <-ch +} + +// We do not currently automatically +// parse this test. It is intended that the creation +// stack is observed manually not to contain +// off-by-one errors +func TestRaceAnnounceThreads(t *testing.T) { + const N = 7 + allDone := make(chan bool, N) + + var x int + + var f, g, h func() + f = func() { + x = 1 + go g() + go func() { + x = 1 + allDone <- true + }() + x = 2 + allDone <- true + } + + g = func() { + for i := 0; i < 2; i++ { + go func() { + x = 1 + allDone <- true + }() + allDone <- true + } + } + + h = func() { + x = 1 + x = 2 + go f() + allDone <- true + } + + go h() + + for i := 0; i < N; i++ { + <-allDone + } +} + +func TestNoRaceAfterFunc1(t *testing.T) { + i := 2 + c := make(chan bool) + var f func() + f = func() { + i-- + if i >= 0 { + time.AfterFunc(0, f) + } else { + c <- true + } + } + + time.AfterFunc(0, f) + <-c +} + +func TestNoRaceAfterFunc2(t *testing.T) { + var x int + timer := time.AfterFunc(10, func() { + x = 1 + }) + defer timer.Stop() + _ = x +} + +func TestNoRaceAfterFunc3(t *testing.T) { + c := make(chan bool, 1) + x := 0 + time.AfterFunc(1e7, func() { + x = 1 + c <- true + }) + <-c +} + +func TestRaceAfterFunc3(t *testing.T) { + c := make(chan bool, 2) + x := 0 + time.AfterFunc(1e7, func() { + x = 1 + c <- true + }) + time.AfterFunc(2e7, func() { + x = 2 + c <- true + }) + <-c + <-c +} + +// This test's output is intended to be +// observed manually. One should check +// that goroutine creation stack is +// comprehensible. +func TestRaceGoroutineCreationStack(t *testing.T) { + var x int + var ch = make(chan bool, 1) + + f1 := func() { + x = 1 + ch <- true + } + f2 := func() { go f1() } + f3 := func() { go f2() } + f4 := func() { go f3() } + + go f4() + x = 2 + <-ch +} diff --git a/src/pkg/runtime/race/testdata/waitgroup_test.go b/src/pkg/runtime/race/testdata/waitgroup_test.go new file mode 100644 index 000000000..7ea21fa7e --- /dev/null +++ b/src/pkg/runtime/race/testdata/waitgroup_test.go @@ -0,0 +1,232 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "runtime" + "sync" + "testing" + "time" +) + +func TestNoRaceWaitGroup(t *testing.T) { + var x int + var wg sync.WaitGroup + n := 1 + for i := 0; i < n; i++ { + wg.Add(1) + j := i + go func() { + x = j + wg.Done() + }() + } + wg.Wait() +} + +func TestRaceWaitGroup(t *testing.T) { + var x int + var wg sync.WaitGroup + n := 2 + for i := 0; i < n; i++ { + wg.Add(1) + j := i + go func() { + x = j + wg.Done() + }() + } + wg.Wait() +} + +func TestNoRaceWaitGroup2(t *testing.T) { + var x int + var wg sync.WaitGroup + wg.Add(1) + go func() { + x = 1 + wg.Done() + }() + wg.Wait() + x = 2 +} + +// incrementing counter in Add and locking wg's mutex +func TestRaceWaitGroupAsMutex(t *testing.T) { + var x int + var wg sync.WaitGroup + c := make(chan bool, 2) + go func() { + wg.Wait() + time.Sleep(100 * time.Millisecond) + wg.Add(+1) + x = 1 + wg.Add(-1) + c <- true + }() + go func() { + wg.Wait() + time.Sleep(100 * time.Millisecond) + wg.Add(+1) + x = 2 + wg.Add(-1) + c <- true + }() + <-c + <-c +} + +// Incorrect usage: Add is too late. +func TestRaceWaitGroupWrongWait(t *testing.T) { + c := make(chan bool, 2) + var x int + var wg sync.WaitGroup + go func() { + wg.Add(1) + runtime.Gosched() + x = 1 + wg.Done() + c <- true + }() + go func() { + wg.Add(1) + runtime.Gosched() + x = 2 + wg.Done() + c <- true + }() + wg.Wait() + <-c + <-c +} + +// A common WaitGroup misuse that can potentially be caught be the race detector. +// For this simple case we must emulate Add() as read on &wg and Wait() as write on &wg. +// However it will have false positives if there are several concurrent Wait() calls. +func TestRaceFailingWaitGroupWrongAdd(t *testing.T) { + c := make(chan bool, 2) + var wg sync.WaitGroup + go func() { + wg.Add(1) + wg.Done() + c <- true + }() + go func() { + wg.Add(1) + wg.Done() + c <- true + }() + wg.Wait() + <-c + <-c +} + +func TestNoRaceWaitGroupMultipleWait(t *testing.T) { + c := make(chan bool, 2) + var wg sync.WaitGroup + go func() { + wg.Wait() + c <- true + }() + go func() { + wg.Wait() + c <- true + }() + wg.Wait() + <-c + <-c +} + +func TestNoRaceWaitGroupMultipleWait2(t *testing.T) { + c := make(chan bool, 2) + var wg sync.WaitGroup + wg.Add(2) + go func() { + wg.Done() + wg.Wait() + c <- true + }() + go func() { + wg.Done() + wg.Wait() + c <- true + }() + wg.Wait() + <-c + <-c +} + +// Correct usage but still a race +func TestRaceWaitGroup2(t *testing.T) { + var x int + var wg sync.WaitGroup + wg.Add(2) + go func() { + x = 1 + wg.Done() + }() + go func() { + x = 2 + wg.Done() + }() + wg.Wait() +} + +func TestNoRaceWaitGroupPanicRecover(t *testing.T) { + var x int + var wg sync.WaitGroup + defer func() { + err := recover() + if err != "sync: negative WaitGroup counter" { + t.Fatalf("Unexpected panic: %#v", err) + } + x = 2 + }() + x = 1 + wg.Add(-1) +} + +// TODO: this is actually a panic-synchronization test, not a +// WaitGroup test. Move it to another *_test file +// Is it possible to get a race by synchronization via panic? +func TestNoRaceWaitGroupPanicRecover2(t *testing.T) { + var x int + var wg sync.WaitGroup + ch := make(chan bool, 1) + var f func() = func() { + x = 2 + ch <- true + } + go func() { + defer func() { + err := recover() + if err != "sync: negative WaitGroup counter" { + } + go f() + }() + x = 1 + wg.Add(-1) + }() + + <-ch +} + +func TestNoRaceWaitGroupTransitive(t *testing.T) { + x, y := 0, 0 + var wg sync.WaitGroup + wg.Add(2) + go func() { + x = 42 + wg.Done() + }() + go func() { + time.Sleep(1e7) + y = 42 + wg.Done() + }() + wg.Wait() + _ = x + _ = y +} diff --git a/src/pkg/runtime/race0.c b/src/pkg/runtime/race0.c new file mode 100644 index 000000000..1c5f05a7e --- /dev/null +++ b/src/pkg/runtime/race0.c @@ -0,0 +1,133 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Stub implementation of the race detector API. +// +build !race + +#include "runtime.h" + +uintptr +runtime·raceinit(void) +{ + return 0; +} + +void +runtime·racefini(void) +{ +} + + +void +runtime·racemapshadow(void *addr, uintptr size) +{ + USED(addr); + USED(size); +} + +void +runtime·racewritepc(void *addr, void *callpc, void *pc) +{ + USED(addr); + USED(callpc); + USED(pc); +} + +void +runtime·racereadpc(void *addr, void *callpc, void *pc) +{ + USED(addr); + USED(callpc); + USED(pc); +} + +void +runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc) +{ + USED(addr); + USED(sz); + USED(step); + USED(callpc); + USED(pc); +} + +void +runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc) +{ + USED(addr); + USED(sz); + USED(step); + USED(callpc); + USED(pc); +} + +void +runtime·raceacquire(void *addr) +{ + USED(addr); +} + +void +runtime·raceacquireg(G *gp, void *addr) +{ + USED(gp); + USED(addr); +} + +void +runtime·racerelease(void *addr) +{ + USED(addr); +} + +void +runtime·racereleaseg(G *gp, void *addr) +{ + USED(gp); + USED(addr); +} + +void +runtime·racereleasemerge(void *addr) +{ + USED(addr); +} + +void +runtime·racereleasemergeg(G *gp, void *addr) +{ + USED(gp); + USED(addr); +} + +void +runtime·racefingo(void) +{ +} + +void +runtime·racemalloc(void *p, uintptr sz, void *pc) +{ + USED(p); + USED(sz); + USED(pc); +} + +void +runtime·racefree(void *p) +{ + USED(p); +} + +uintptr +runtime·racegostart(void *pc) +{ + USED(pc); + return 0; +} + +void +runtime·racegoend(void) +{ +} diff --git a/src/pkg/runtime/race_amd64.s b/src/pkg/runtime/race_amd64.s new file mode 100644 index 000000000..83e300905 --- /dev/null +++ b/src/pkg/runtime/race_amd64.s @@ -0,0 +1,14 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race + +// func runtime·racefuncenter(pc uintptr) +TEXT runtime·racefuncenter(SB), 7, $16 + MOVQ DX, saved-8(SP) // save function entry context (for closures) + MOVQ pc+0(FP), DX + MOVQ DX, arg-16(SP) + CALL runtime·racefuncenter1(SB) + MOVQ saved-8(SP), DX + RET diff --git a/src/pkg/runtime/rt0_freebsd_arm.s b/src/pkg/runtime/rt0_freebsd_arm.s new file mode 100644 index 000000000..085fccf9d --- /dev/null +++ b/src/pkg/runtime/rt0_freebsd_arm.s @@ -0,0 +1,8 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// FreeBSD and Linux use the same linkage to main + +TEXT _rt0_arm_freebsd(SB),7,$-4 + B _rt0_arm(SB) diff --git a/src/pkg/runtime/rt0_linux_arm.s b/src/pkg/runtime/rt0_linux_arm.s index e08cf907d..a648160cf 100644 --- a/src/pkg/runtime/rt0_linux_arm.s +++ b/src/pkg/runtime/rt0_linux_arm.s @@ -20,15 +20,27 @@ TEXT _rt0_arm_linux(SB),7,$-4 MOVM.DB.W [R0-R3], (R13) MOVW $4, R0 // SIGILL MOVW R13, R1 // sa - MOVW $0, R2 // old_sa + SUB $16, R13 + MOVW R13, R2 // old_sa MOVW $8, R3 // c MOVW $174, R7 // sys_sigaction BL oabi_syscall<>(SB) - ADD $16, R13 + // do an EABI syscall MOVW $20, R7 // sys_getpid - SWI $0 // this will trigger SIGILL on OABI systems + SWI $0 // this will trigger SIGILL on OABI systems + + MOVW $4, R0 // SIGILL + MOVW R13, R1 // sa + MOVW $0, R2 // old_sa + MOVW $8, R3 // c + MOVW $174, R7 // sys_sigaction + SWI $0 // restore signal handler + ADD $32, R13 + SUB $4, R13 // fake a stack frame for runtime·setup_auxv + BL runtime·setup_auxv(SB) + ADD $4, R13 B _rt0_arm(SB) TEXT bad_abi<>(SB),7,$-4 diff --git a/src/pkg/runtime/rt0_netbsd_arm.s b/src/pkg/runtime/rt0_netbsd_arm.s new file mode 100644 index 000000000..8c1588f2e --- /dev/null +++ b/src/pkg/runtime/rt0_netbsd_arm.s @@ -0,0 +1,8 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// FreeBSD/NetBSD and Linux use the same linkage to main + +TEXT _rt0_arm_netbsd(SB),7,$-4 + B _rt0_arm(SB) diff --git a/src/pkg/runtime/rt0_plan9_386.s b/src/pkg/runtime/rt0_plan9_386.s index b56c8b325..56f3a0f6c 100644 --- a/src/pkg/runtime/rt0_plan9_386.s +++ b/src/pkg/runtime/rt0_plan9_386.s @@ -25,6 +25,7 @@ argv_fix: ADDL $4, BP LOOP argv_fix + CALL runtime·asminit(SB) JMP _rt0_386(SB) DATA runtime·isplan9(SB)/4, $1 diff --git a/src/pkg/runtime/rt0_plan9_amd64.s b/src/pkg/runtime/rt0_plan9_amd64.s new file mode 100644 index 000000000..2b1fa2ae1 --- /dev/null +++ b/src/pkg/runtime/rt0_plan9_amd64.s @@ -0,0 +1,11 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +TEXT _rt0_amd64_plan9(SB),7, $0 + MOVQ $_rt0_amd64(SB), AX + MOVQ SP, DI + JMP AX + +DATA runtime·isplan9(SB)/4, $1 +GLOBL runtime·isplan9(SB), $4 diff --git a/src/pkg/runtime/rt0_windows_386.s b/src/pkg/runtime/rt0_windows_386.s index 3b023de2f..a06aa787e 100644 --- a/src/pkg/runtime/rt0_windows_386.s +++ b/src/pkg/runtime/rt0_windows_386.s @@ -3,11 +3,6 @@ // license that can be found in the LICENSE file. TEXT _rt0_386_windows(SB),7,$0 - // Set up SEH frame for bootstrap m - PUSHL $runtime·sigtramp(SB) - PUSHL 0(FS) - MOVL SP, 0(FS) - JMP _rt0_386(SB) DATA runtime·iswindows(SB)/4, $1 diff --git a/src/pkg/runtime/rune.c b/src/pkg/runtime/rune.c index 86ee76ddd..ed867269d 100644 --- a/src/pkg/runtime/rune.c +++ b/src/pkg/runtime/rune.c @@ -47,6 +47,9 @@ enum Runeerror = 0xFFFD, Runeself = 0x80, + SurrogateMin = 0xD800, + SurrogateMax = 0xDFFF, + Bad = Runeerror, Runemax = 0x10FFFF, /* maximum rune value */ @@ -128,6 +131,8 @@ runtime·charntorune(int32 *rune, uint8 *str, int32 length) l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; if(l <= Rune2) goto bad; + if (SurrogateMin <= l && l <= SurrogateMax) + goto bad; *rune = l; return 3; } @@ -193,13 +198,15 @@ runtime·runetochar(byte *str, int32 rune) /* note: in original, arg2 was point } /* - * If the Rune is out of range, convert it to the error rune. + * If the Rune is out of range or a surrogate half, convert it to the error rune. * Do this test here because the error rune encodes to three bytes. * Doing it earlier would duplicate work, since an out of range * Rune wouldn't have fit in one or two bytes. */ if (c > Runemax) c = Runeerror; + if (SurrogateMin <= c && c <= SurrogateMax) + c = Runeerror; /* * three character sequence diff --git a/src/pkg/runtime/runtime-gdb.py b/src/pkg/runtime/runtime-gdb.py index 629c39e98..eff9a4003 100644 --- a/src/pkg/runtime/runtime-gdb.py +++ b/src/pkg/runtime/runtime-gdb.py @@ -149,8 +149,8 @@ goobjfile.pretty_printers.extend([makematcher(k) for k in vars().values() if has # # For reference, this is what we're trying to do: -# eface: p *(*(struct 'runtime.commonType'*)'main.e'->type_->data)->string -# iface: p *(*(struct 'runtime.commonType'*)'main.s'->tab->Type->data)->string +# eface: p *(*(struct 'runtime.rtype'*)'main.e'->type_->data)->string +# iface: p *(*(struct 'runtime.rtype'*)'main.s'->tab->Type->data)->string # # interface types can't be recognized by their name, instead we check # if they have the expected fields. Unfortunately the mapping of @@ -186,8 +186,7 @@ def lookup_type(name): except: pass -_rctp_type = gdb.lookup_type("struct runtime.commonType").pointer() -_rtp_type = gdb.lookup_type("struct runtime._type").pointer() +_rctp_type = gdb.lookup_type("struct runtime.rtype").pointer() def iface_commontype(obj): if is_iface(obj): @@ -196,18 +195,13 @@ def iface_commontype(obj): go_type_ptr = obj['_type'] else: return - - # sanity check: reflection type description ends in a loop. - tt = go_type_ptr['_type'].cast(_rtp_type).dereference()['_type'] - if tt != tt.cast(_rtp_type).dereference()['_type']: - return - return go_type_ptr['ptr'].cast(_rctp_type).dereference() + return go_type_ptr.cast(_rctp_type).dereference() def iface_dtype(obj): "Decode type of the data field of an eface or iface struct." - # known issue: dtype_name decoded from runtime.commonType is "nested.Foo" + # known issue: dtype_name decoded from runtime.rtype is "nested.Foo" # but the dwarf table lists it as "full/path/to/nested.Foo" dynamic_go_type = iface_commontype(obj) @@ -381,6 +375,7 @@ class GoroutineCmd(gdb.Command): def invoke(self, arg, from_tty): goid, cmd = arg.split(None, 1) + goid = gdb.parse_and_eval(goid) pc, sp = find_goroutine(int(goid)) if not pc: print "No such goroutine: ", goid diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c index ebb5544fb..4d57cbafd 100644 --- a/src/pkg/runtime/runtime.c +++ b/src/pkg/runtime/runtime.c @@ -3,15 +3,12 @@ // license that can be found in the LICENSE file. #include "runtime.h" -#include "stack.h" +#include "arch_GOARCH.h" enum { maxround = sizeof(uintptr), }; -uint32 runtime·panicking; -void (*runtime·destroylock)(Lock*); - /* * We assume that all architectures turn faults and the like * into apparent calls to runtime.sigpanic. If we see a "call" @@ -31,103 +28,6 @@ runtime·gotraceback(void) return runtime·atoi(p); } -static Lock paniclk; - -void -runtime·startpanic(void) -{ - if(m->dying) { - runtime·printf("panic during panic\n"); - runtime·exit(3); - } - m->dying = 1; - runtime·xadd(&runtime·panicking, 1); - runtime·lock(&paniclk); -} - -void -runtime·dopanic(int32 unused) -{ - static bool didothers; - - if(g->sig != 0) - runtime·printf("[signal %x code=%p addr=%p pc=%p]\n", - g->sig, g->sigcode0, g->sigcode1, g->sigpc); - - if(runtime·gotraceback()){ - if(g != m->g0) { - runtime·printf("\n"); - runtime·goroutineheader(g); - runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g); - } - if(!didothers) { - didothers = true; - runtime·tracebackothers(g); - } - } - runtime·unlock(&paniclk); - if(runtime·xadd(&runtime·panicking, -1) != 0) { - // Some other m is panicking too. - // Let it print what it needs to print. - // Wait forever without chewing up cpu. - // It will exit when it's done. - static Lock deadlock; - runtime·lock(&deadlock); - runtime·lock(&deadlock); - } - - runtime·exit(2); -} - -void -runtime·panicindex(void) -{ - runtime·panicstring("index out of range"); -} - -void -runtime·panicslice(void) -{ - runtime·panicstring("slice bounds out of range"); -} - -void -runtime·throwreturn(void) -{ - // can only happen if compiler is broken - runtime·throw("no return at end of a typed function - compiler is broken"); -} - -void -runtime·throwinit(void) -{ - // can only happen with linker skew - runtime·throw("recursive call during initialization - linker skew"); -} - -void -runtime·throw(int8 *s) -{ - runtime·startpanic(); - runtime·printf("throw: %s\n", s); - runtime·dopanic(0); - *(int32*)0 = 0; // not reached - runtime·exit(1); // even more not reached -} - -void -runtime·panicstring(int8 *s) -{ - Eface err; - - if(m->gcing) { - runtime·printf("panic: %s\n", s); - runtime·throw("panic during gc"); - } - runtime·newErrorString(runtime·gostringnocopy((byte*)s), &err); - runtime·panic(err); -} - int32 runtime·mcmp(byte *s1, byte *s2, uint32 n) { @@ -155,30 +55,21 @@ runtime·mchr(byte *p, byte c, byte *ep) return nil; } -uint32 -runtime·rnd(uint32 n, uint32 m) -{ - uint32 r; - - if(m > maxround) - m = maxround; - r = n % m; - if(r) - n += m-r; - return n; -} - static int32 argc; static uint8** argv; Slice os·Args; Slice syscall·envs; +void (*runtime·sysargs)(int32, uint8**); + void runtime·args(int32 c, uint8 **v) { argc = c; argv = v; + if(runtime·sysargs != nil) + runtime·sysargs(c, v); } int32 runtime·isplan9; @@ -219,33 +110,6 @@ runtime·goenvs_unix(void) syscall·envs.cap = n; } -byte* -runtime·getenv(int8 *s) -{ - int32 i, j, len; - byte *v, *bs; - String* envv; - int32 envc; - - bs = (byte*)s; - len = runtime·findnull(bs); - envv = (String*)syscall·envs.array; - envc = syscall·envs.len; - for(i=0; ifastrand = x; return x; } + +static Lock ticksLock; +static int64 ticks; + +int64 +runtime·tickspersecond(void) +{ + int64 res, t0, t1, c0, c1; + + res = (int64)runtime·atomicload64((uint64*)&ticks); + if(res != 0) + return ticks; + runtime·lock(&ticksLock); + res = ticks; + if(res == 0) { + t0 = runtime·nanotime(); + c0 = runtime·cputicks(); + runtime·usleep(100*1000); + t1 = runtime·nanotime(); + c1 = runtime·cputicks(); + if(t1 == t0) + t1++; + res = (c1-c0)*1000*1000*1000/(t1-t0); + if(res == 0) + res++; + runtime·atomicstore64((uint64*)&ticks, res); + } + runtime·unlock(&ticksLock); + return res; +} + +void +runtime∕pprof·runtime_cyclesPerSecond(int64 res) +{ + res = runtime·tickspersecond(); + FLUSH(&res); +} diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 6f5aea11d..08f43a69b 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -19,9 +19,13 @@ typedef double float64; #ifdef _64BIT typedef uint64 uintptr; typedef int64 intptr; +typedef int64 intgo; // Go's int +typedef uint64 uintgo; // Go's uint #else typedef uint32 uintptr; -typedef int32 intptr; +typedef int32 intptr; +typedef int32 intgo; // Go's int +typedef uint32 uintgo; // Go's uint #endif /* @@ -48,44 +52,53 @@ typedef struct G G; typedef struct Gobuf Gobuf; typedef union Lock Lock; typedef struct M M; +typedef struct P P; typedef struct Mem Mem; typedef union Note Note; typedef struct Slice Slice; typedef struct Stktop Stktop; typedef struct String String; +typedef struct FuncVal FuncVal; typedef struct SigTab SigTab; typedef struct MCache MCache; typedef struct FixAlloc FixAlloc; typedef struct Iface Iface; typedef struct Itab Itab; +typedef struct InterfaceType InterfaceType; typedef struct Eface Eface; typedef struct Type Type; typedef struct ChanType ChanType; typedef struct MapType MapType; typedef struct Defer Defer; +typedef struct DeferChunk DeferChunk; typedef struct Panic Panic; typedef struct Hmap Hmap; typedef struct Hchan Hchan; typedef struct Complex64 Complex64; typedef struct Complex128 Complex128; typedef struct WinCall WinCall; +typedef struct SEH SEH; typedef struct Timers Timers; typedef struct Timer Timer; +typedef struct GCStats GCStats; +typedef struct LFNode LFNode; +typedef struct ParFor ParFor; +typedef struct ParForThread ParForThread; +typedef struct CgoMal CgoMal; /* - * per-cpu declaration. - * "extern register" is a special storage class implemented by 6c, 8c, etc. - * on machines with lots of registers, it allocates a register that will not be - * used in generated code. on the x86, it allocates a slot indexed by a - * segment register. + * Per-CPU declaration. * - * amd64: allocated downwards from R15 - * x86: allocated upwards from 0(GS) - * arm: allocated downwards from R10 + * "extern register" is a special storage class implemented by 6c, 8c, etc. + * On the ARM, it is an actual register; elsewhere it is a slot in thread- + * local storage indexed by a segment register. See zasmhdr in + * src/cmd/dist/buildruntime.c for details, and be aware that the linker may + * make further OS-specific changes to the compiler's output. For example, + * 6l/linux rewrites 0(GS) as -16(FS). * - * every C file linked into a Go program must include runtime.h - * so that the C compiler knows to avoid other uses of these registers. - * the Go compilers know to avoid them. + * Every C file linked into a Go program must include runtime.h so that the + * C compiler (6c, 8c, etc.) knows to avoid other uses of these dedicated + * registers. The Go compiler (6g, 8g, etc.) knows to avoid them. */ extern register G* g; extern register M* m; @@ -105,14 +118,34 @@ enum Grunning, Gsyscall, Gwaiting, - Gmoribund, + Gmoribund_unused, // currently unused, but hardcoded in gdb scripts Gdead, }; enum +{ + // P status + Pidle, + Prunning, + Psyscall, + Pgcstop, + Pdead, +}; +enum { true = 1, false = 0, }; +enum +{ + PtrSize = sizeof(void*), +}; +enum +{ + // Per-M stack segment cache size. + StackCacheSize = 32, + // Global <-> per-M stack segment cache transfer batch size. + StackCacheBatch = 16, +}; /* * structures @@ -130,7 +163,12 @@ union Note struct String { byte* str; - int32 len; + intgo len; +}; +struct FuncVal +{ + void (*fn)(void); + // variable-size, fn-specific data here }; struct Iface { @@ -156,47 +194,61 @@ struct Complex128 struct Slice { // must not move anything byte* array; // actual data - uint32 len; // number of elements - uint32 cap; // allocated number of elements + uintgo len; // number of elements + uintgo cap; // allocated number of elements }; struct Gobuf { // The offsets of these fields are known to (hard-coded in) libmach. - byte* sp; + uintptr sp; byte* pc; G* g; }; +struct GCStats +{ + // the struct must consist of only uint64's, + // because it is casted to uint64[]. + uint64 nhandoff; + uint64 nhandoffcnt; + uint64 nprocyield; + uint64 nosyield; + uint64 nsleep; +}; struct G { - byte* stackguard; // cannot move - also known to linker, libmach, runtime/cgo - byte* stackbase; // cannot move - also known to libmach, runtime/cgo + uintptr stackguard; // cannot move - also known to linker, libmach, runtime/cgo + uintptr stackbase; // cannot move - also known to libmach, runtime/cgo Defer* defer; Panic* panic; Gobuf sched; - byte* gcstack; // if status==Gsyscall, gcstack = stackbase to use during gc - byte* gcsp; // if status==Gsyscall, gcsp = sched.sp to use during gc - byte* gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc - byte* stack0; - byte* entry; // initial function + uintptr gcstack; // if status==Gsyscall, gcstack = stackbase to use during gc + uintptr gcsp; // if status==Gsyscall, gcsp = sched.sp to use during gc + byte* gcpc; // if status==Gsyscall, gcpc = sched.pc to use during gc + uintptr gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc + uintptr stack0; + FuncVal* fnstart; // initial function G* alllink; // on allg void* param; // passed parameter on wakeup int16 status; - int32 goid; + int64 goid; uint32 selgen; // valid sudog pointer int8* waitreason; // if status==Gwaiting G* schedlink; - bool readyonstop; bool ispanic; + bool issystem; + int8 raceignore; // ignore race detection events M* m; // for debuggers, but offset not hard-coded M* lockedm; - M* idlem; int32 sig; int32 writenbuf; byte* writebuf; + DeferChunk *dchunk; + DeferChunk *dchunknext; uintptr sigcode0; uintptr sigcode1; uintptr sigpc; uintptr gopc; // pc of go statement that created this goroutine + uintptr racectx; uintptr end[]; }; struct M @@ -213,43 +265,98 @@ struct M uintptr cret; // return value from C uint64 procid; // for debuggers, but offset not hard-coded G* gsignal; // signal-handling G - uint32 tls[8]; // thread-local storage (for 386 extern register) + uintptr tls[4]; // thread-local storage (for x86 extern register) + void (*mstartfn)(void); G* curg; // current running goroutine + P* p; // attached P for executing Go code (nil if not executing Go code) + P* nextp; int32 id; int32 mallocing; + int32 throwing; int32 gcing; int32 locks; int32 nomemprof; - int32 waitnextg; int32 dying; int32 profilehz; int32 helpgc; + bool blockingsyscall; + bool spinning; uint32 fastrand; - uint64 ncgocall; - Note havenextg; - G* nextg; + uint64 ncgocall; // number of cgo calls in total + int32 ncgo; // number of cgo calls currently in progress + CgoMal* cgomal; + Note park; M* alllink; // on allm M* schedlink; uint32 machport; // Return address for Mach IPC (OS X) MCache *mcache; - FixAlloc *stackalloc; + int32 stackinuse; + uint32 stackcachepos; + uint32 stackcachecnt; + void* stackcache[StackCacheSize]; G* lockedg; - G* idleg; uintptr createstack[32]; // Stack that created this thread. uint32 freglo[16]; // D[i] lsb and F[i] uint32 freghi[16]; // D[i] msb and F[i+16] uint32 fflag; // floating point compare flags + uint32 locked; // tracking for LockOSThread M* nextwaitm; // next M waiting for lock uintptr waitsema; // semaphore for parking on locks uint32 waitsemacount; uint32 waitsemalock; + GCStats gcstats; + bool racecall; + bool needextram; + void* racepc; + void (*waitunlockf)(Lock*); + Lock* waitlock; + uint32 moreframesize_minalloc; + + uintptr settype_buf[1024]; + uintptr settype_bufsize; #ifdef GOOS_windows void* thread; // thread handle #endif +#ifdef GOOS_plan9 + int8* notesig; +#endif + SEH* seh; uintptr end[]; }; +struct P +{ + Lock; + + uint32 status; // one of Pidle/Prunning/... + P* link; + uint32 tick; // incremented on every scheduler or system call + M* m; // back-link to associated M (nil if idle) + MCache* mcache; + + // Queue of runnable goroutines. + G** runq; + int32 runqhead; + int32 runqtail; + int32 runqsize; + + // Available G's (status == Gdead) + G* gfree; + int32 gfreecnt; + + byte pad[64]; +}; + +// The m->locked word holds a single bit saying whether +// external calls to LockOSThread are in effect, and then a counter +// of the internal nesting depth of lockOSThread / unlockOSThread. +enum +{ + LockExternal = 1, + LockInternal = 2, +}; + struct Stktop { // The offsets of these fields are known to (hard-coded in) libmach. @@ -288,8 +395,19 @@ struct Func uintptr pc0; // starting pc, ln for table int32 ln0; int32 frame; // stack frame size - int32 args; // number of 32-bit in/out args - int32 locals; // number of 32-bit locals + int32 args; // in/out args size + int32 locals; // locals size +}; + +// layout of Itab known to compilers +struct Itab +{ + InterfaceType* inter; + Type* type; + Itab* link; + int32 bad; + int32 unused; + void (*fun[])(void); }; struct WinCall @@ -301,6 +419,11 @@ struct WinCall uintptr r2; uintptr err; // error number }; +struct SEH +{ + void* prev; + void* handler; +}; #ifdef GOOS_windows enum { @@ -335,10 +458,46 @@ struct Timer // a well-behaved function and not block. int64 when; int64 period; - void (*f)(int64, Eface); + FuncVal *fv; Eface arg; }; +// Lock-free stack node. +struct LFNode +{ + LFNode *next; + uintptr pushcnt; +}; + +// Parallel for descriptor. +struct ParFor +{ + void (*body)(ParFor*, uint32); // executed for each element + uint32 done; // number of idle threads + uint32 nthr; // total number of threads + uint32 nthrmax; // maximum number of threads + uint32 thrseq; // thread id sequencer + uint32 cnt; // iteration space [0, cnt) + void *ctx; // arbitrary user context + bool wait; // if true, wait while all threads finish processing, + // otherwise parfor may return while other threads are still working + ParForThread *thr; // array of thread descriptors + // stats + uint64 nsteal; + uint64 nstealcnt; + uint64 nprocyield; + uint64 nosyield; + uint64 nsleep; +}; + +// Track memory allocated by code not written in Go during a cgo call, +// so that the garbage collector can see them. +struct CgoMal +{ + CgoMal *next; + byte *alloc; +}; + /* * defined macros * you need super-gopher-guru privilege @@ -347,6 +506,7 @@ struct Timer #define nelem(x) (sizeof(x)/sizeof((x)[0])) #define nil ((void*)0) #define offsetof(s,m) (uint32)(&(((s*)0)->m)) +#define ROUND(x, n) (((x)+(n)-1)&~((n)-1)) /* all-caps to mark as macro: it evaluates n twice */ /* * known to compiler @@ -430,12 +590,19 @@ void runtime·nilintercopy(uintptr, void*, void*); struct Defer { int32 siz; - bool nofree; + bool special; // not part of defer frame + bool free; // if special, free when done byte* argp; // where args were copied from byte* pc; - byte* fn; + FuncVal* fn; Defer* link; - byte args[8]; // padded to actual size + void* args[1]; // padded to actual size +}; + +struct DeferChunk +{ + DeferChunk *prev; + uintptr off; }; /* @@ -453,16 +620,21 @@ struct Panic * external data */ extern String runtime·emptystring; -G* runtime·allg; -G* runtime·lastg; -M* runtime·allm; +extern uintptr runtime·zerobase; +extern G* runtime·allg; +extern G* runtime·lastg; +extern M* runtime·allm; +extern P** runtime·allp; extern int32 runtime·gomaxprocs; extern bool runtime·singleproc; extern uint32 runtime·panicking; -extern int32 runtime·gcwaiting; // gc is waiting to run -int8* runtime·goos; -int32 runtime·ncpu; +extern uint32 runtime·gcwaiting; // gc is waiting to run +extern int8* runtime·goos; +extern int32 runtime·ncpu; extern bool runtime·iscgo; +extern void (*runtime·sysargs)(int32, uint8**); +extern uint32 runtime·maxstring; +extern uint32 runtime·Hchansize; /* * common functions and data @@ -481,7 +653,8 @@ int32 runtime·charntorune(int32*, uint8*, int32); #define FLUSH(x) USED(x) void runtime·gogo(Gobuf*, uintptr); -void runtime·gogocall(Gobuf*, void(*)(void)); +void runtime·gogocall(Gobuf*, void(*)(void), uintptr); +void runtime·gogocallfn(Gobuf*, FuncVal*); void runtime·gosave(Gobuf*); void runtime·lessstack(void); void runtime·goargs(void); @@ -490,7 +663,6 @@ void runtime·goenvs_unix(void); void* runtime·getu(void); void runtime·throw(int8*); void runtime·panicstring(int8*); -uint32 runtime·rnd(uint32, uint32); void runtime·prints(int8*); void runtime·printf(int8*, ...); byte* runtime·mchr(byte*, byte, byte*); @@ -499,8 +671,8 @@ void runtime·memmove(void*, void*, uint32); void* runtime·mal(uintptr); String runtime·catstring(String, String); String runtime·gostring(byte*); -String runtime·gostringn(byte*, int32); -Slice runtime·gobytes(byte*, int32); +String runtime·gostringn(byte*, intgo); +Slice runtime·gobytes(byte*, intgo); String runtime·gostringnocopy(byte*); String runtime·gostringw(uint16*); void runtime·initsig(void); @@ -512,38 +684,47 @@ void runtime·tracebackothers(G*); int32 runtime·write(int32, void*, int32); int32 runtime·mincore(void*, uintptr, byte*); bool runtime·cas(uint32*, uint32, uint32); +bool runtime·cas64(uint64*, uint64*, uint64); bool runtime·casp(void**, void*, void*); // Don't confuse with XADD x86 instruction, // this one is actually 'addx', that is, add-and-fetch. uint32 runtime·xadd(uint32 volatile*, int32); +uint64 runtime·xadd64(uint64 volatile*, int64); uint32 runtime·xchg(uint32 volatile*, uint32); uint32 runtime·atomicload(uint32 volatile*); void runtime·atomicstore(uint32 volatile*, uint32); +void runtime·atomicstore64(uint64 volatile*, uint64); +uint64 runtime·atomicload64(uint64 volatile*); void* runtime·atomicloadp(void* volatile*); void runtime·atomicstorep(void* volatile*, void*); -void runtime·jmpdefer(byte*, void*); +void runtime·jmpdefer(FuncVal*, void*); void runtime·exit1(int32); void runtime·ready(G*); byte* runtime·getenv(int8*); int32 runtime·atoi(byte*); -void runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)); -void runtime·signalstack(byte*, int32); +void runtime·newosproc(M *mp, void *stk); +void runtime·mstart(void); G* runtime·malg(int32); void runtime·asminit(void); +void runtime·mpreinit(M*); void runtime·minit(void); +void runtime·unminit(void); +void runtime·signalstack(byte*, int32); Func* runtime·findfunc(uintptr); int32 runtime·funcline(Func*, uintptr); void* runtime·stackalloc(uint32); void runtime·stackfree(void*, uintptr); MCache* runtime·allocmcache(void); +void runtime·freemcache(MCache*); void runtime·mallocinit(void); +void runtime·mprofinit(void); bool runtime·ifaceeq_c(Iface, Iface); bool runtime·efaceeq_c(Eface, Eface); -uintptr runtime·ifacehash(Iface); -uintptr runtime·efacehash(Eface); +uintptr runtime·ifacehash(Iface, uintptr); +uintptr runtime·efacehash(Eface, uintptr); void* runtime·malloc(uintptr size); void runtime·free(void *v); -bool runtime·addfinalizer(void*, void(*fn)(void*), int32); +bool runtime·addfinalizer(void*, FuncVal *fn, uintptr); void runtime·runpanic(Panic*); void* runtime·getcallersp(void*); int32 runtime·mcount(void); @@ -551,27 +732,35 @@ int32 runtime·gcount(void); void runtime·mcall(void(*)(G*)); uint32 runtime·fastrand1(void); +void runtime·setmg(M*, G*); +void runtime·newextram(void); void runtime·exit(int32); void runtime·breakpoint(void); void runtime·gosched(void); -void runtime·tsleep(int64); +void runtime·park(void(*)(Lock*), Lock*, int8*); +void runtime·tsleep(int64, int8*); M* runtime·newm(void); void runtime·goexit(void); void runtime·asmcgocall(void (*fn)(void*), void*); void runtime·entersyscall(void); +void runtime·entersyscallblock(void); void runtime·exitsyscall(void); -G* runtime·newproc1(byte*, byte*, int32, int32, void*); +G* runtime·newproc1(FuncVal*, byte*, int32, int32, void*); bool runtime·sigsend(int32 sig); int32 runtime·callers(int32, uintptr*, int32); int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32); int64 runtime·nanotime(void); void runtime·dopanic(int32); void runtime·startpanic(void); +void runtime·unwindstack(G*, byte*); void runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp); void runtime·resetcpuprofiler(int32); void runtime·setcpuprofilerate(void(*)(uintptr*, int32), int32); void runtime·usleep(uint32); int64 runtime·cputicks(void); +int64 runtime·tickspersecond(void); +void runtime·blockevent(int64, int32); +extern int64 runtime·blockprofilerate; #pragma varargck argpos runtime·printf 1 #pragma varargck type "d" int32 @@ -589,7 +778,7 @@ int64 runtime·cputicks(void); #pragma varargck type "S" String void runtime·stoptheworld(void); -void runtime·starttheworld(bool); +void runtime·starttheworld(void); extern uint32 runtime·worldsema; /* @@ -635,6 +824,27 @@ void runtime·semawakeup(M*); void runtime·futexsleep(uint32*, uint32, int64); void runtime·futexwakeup(uint32*, uint32); +/* + * Lock-free stack. + * Initialize uint64 head to 0, compare with 0 to test for emptiness. + * The stack does not keep pointers to nodes, + * so they can be garbage collected if there are no other pointers to nodes. + */ +void runtime·lfstackpush(uint64 *head, LFNode *node); +LFNode* runtime·lfstackpop(uint64 *head); + +/* + * Parallel for over [0, n). + * body() is executed for each iteration. + * nthr - total number of worker threads. + * ctx - arbitrary user context. + * if wait=true, threads return from parfor() when all work is done; + * otherwise, threads can return while other threads are still finishing processing. + */ +ParFor* runtime·parforalloc(uint32 nthrmax); +void runtime·parforsetup(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void (*body)(ParFor*, uint32)); +void runtime·parfordo(ParFor *desc); + /* * This is consistent across Linux and BSD. * If a new OS is added that is different, move this to @@ -645,6 +855,9 @@ void runtime·futexwakeup(uint32*, uint32); /* * low level C-called */ +// for mmap, we only pass the lower 32 bits of file offset to the +// assembly routine; the higher bits (if required), should be provided +// by the assembly routine as 0. uint8* runtime·mmap(byte*, uintptr, int32, int32, int32, uint32); void runtime·munmap(byte*, uintptr); void runtime·madvise(byte*, uintptr, int32); @@ -656,6 +869,7 @@ void* runtime·getcallerpc(void*); * runtime go-called */ void runtime·printbool(bool); +void runtime·printbyte(int8); void runtime·printfloat(float64); void runtime·printint(int64); void runtime·printiface(Iface); @@ -667,7 +881,7 @@ void runtime·printuint(uint64); void runtime·printhex(uint64); void runtime·printslice(Slice); void runtime·printcomplex(Complex128); -void reflect·call(byte*, byte*, uint32); +void reflect·call(FuncVal*, byte*, uint32); void runtime·panic(Eface); void runtime·panicindex(void); void runtime·panicslice(void); @@ -708,8 +922,8 @@ void runtime·semrelease(uint32*); int32 runtime·gomaxprocsfunc(int32 n); void runtime·procyield(uint32); void runtime·osyield(void); -void runtime·LockOSThread(void); -void runtime·UnlockOSThread(void); +void runtime·lockOSThread(void); +void runtime·unlockOSThread(void); void runtime·mapassign(MapType*, Hmap*, byte*, byte*); void runtime·mapaccess(MapType*, Hmap*, byte*, byte*, bool*); @@ -719,13 +933,11 @@ void runtime·mapiterkeyvalue(struct hash_iter*, void*, void*); Hmap* runtime·makemap_c(MapType*, int64); Hchan* runtime·makechan_c(ChanType*, int64); -void runtime·chansend(ChanType*, Hchan*, byte*, bool*); +void runtime·chansend(ChanType*, Hchan*, byte*, bool*, void*); void runtime·chanrecv(ChanType*, Hchan*, byte*, bool*, bool*); -int32 runtime·chanlen(Hchan*); -int32 runtime·chancap(Hchan*); -bool runtime·showframe(Func*); +bool runtime·showframe(Func*, bool); -void runtime·ifaceE2I(struct InterfaceType*, Eface, Iface*); +void runtime·ifaceE2I(InterfaceType*, Eface, Iface*); uintptr runtime·memlimit(void); @@ -738,3 +950,17 @@ uintptr runtime·memlimit(void); // is forced to deliver the signal to a thread that's actually running. // This is a no-op on other systems. void runtime·setprof(bool); + +// float.c +extern float64 runtime·nan; +extern float64 runtime·posinf; +extern float64 runtime·neginf; +extern uint64 ·nan; +extern uint64 ·posinf; +extern uint64 ·neginf; +#define ISNAN(f) ((f) != (f)) + +enum +{ + UseSpanType = 1, +}; diff --git a/src/pkg/runtime/runtime1.goc b/src/pkg/runtime/runtime1.goc index 667131c1e..d2c38dfef 100644 --- a/src/pkg/runtime/runtime1.goc +++ b/src/pkg/runtime/runtime1.goc @@ -5,10 +5,10 @@ package runtime #include "runtime.h" -func GOMAXPROCS(n int32) (ret int32) { +func GOMAXPROCS(n int) (ret int) { ret = runtime·gomaxprocsfunc(n); } -func NumCPU() (ret int32) { +func NumCPU() (ret int) { ret = runtime·ncpu; } diff --git a/src/pkg/runtime/runtime_test.go b/src/pkg/runtime/runtime_test.go index d68b363e9..e45879349 100644 --- a/src/pkg/runtime/runtime_test.go +++ b/src/pkg/runtime/runtime_test.go @@ -38,3 +38,44 @@ func BenchmarkIfaceCmpNil100(b *testing.B) { } } } + +func BenchmarkDefer(b *testing.B) { + for i := 0; i < b.N; i++ { + defer1() + } +} + +func defer1() { + defer func(x, y, z int) { + if recover() != nil || x != 1 || y != 2 || z != 3 { + panic("bad recover") + } + }(1, 2, 3) + return +} + +func BenchmarkDefer10(b *testing.B) { + for i := 0; i < b.N/10; i++ { + defer2() + } +} + +func defer2() { + for i := 0; i < 10; i++ { + defer func(x, y, z int) { + if recover() != nil || x != 1 || y != 2 || z != 3 { + panic("bad recover") + } + }(1, 2, 3) + } +} + +func BenchmarkDeferMany(b *testing.B) { + for i := 0; i < b.N; i++ { + defer func(x, y, z int) { + if recover() != nil || x != 1 || y != 2 || z != 3 { + panic("bad recover") + } + }(1, 2, 3) + } +} diff --git a/src/pkg/runtime/sema.goc b/src/pkg/runtime/sema.goc index 2300c56aa..c4b5247b3 100644 --- a/src/pkg/runtime/sema.goc +++ b/src/pkg/runtime/sema.goc @@ -24,30 +24,33 @@ package sync typedef struct Sema Sema; struct Sema { - uint32 volatile *addr; - G *g; - Sema *prev; - Sema *next; + uint32 volatile* addr; + G* g; + int64 releasetime; + Sema* prev; + Sema* next; }; typedef struct SemaRoot SemaRoot; struct SemaRoot { - Lock; - Sema *head; - Sema *tail; + Lock; + Sema* head; + Sema* tail; // Number of waiters. Read w/o the lock. - uint32 volatile nwait; + uint32 volatile nwait; }; // Prime to not correlate with any user patterns. #define SEMTABLESZ 251 -static union +union semtable { SemaRoot; uint8 pad[CacheLineSize]; -} semtable[SEMTABLESZ]; +}; +#pragma dataflag 16 /* mark semtable as 'no pointers', hiding from garbage collector */ +static union semtable semtable[SEMTABLESZ]; static SemaRoot* semroot(uint32 *addr) @@ -95,12 +98,13 @@ cansemacquire(uint32 *addr) return 0; } -void -runtime·semacquire(uint32 volatile *addr) +static void +semacquireimpl(uint32 volatile *addr, int32 profile) { - Sema s; + Sema s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it SemaRoot *root; - + int64 t0; + // Easy case. if(cansemacquire(addr)) return; @@ -112,6 +116,12 @@ runtime·semacquire(uint32 volatile *addr) // sleep // (waiter descriptor is dequeued by signaler) root = semroot(addr); + t0 = 0; + s.releasetime = 0; + if(profile && runtime·blockprofilerate > 0) { + t0 = runtime·cputicks(); + s.releasetime = -1; + } for(;;) { runtime·lock(root); // Add ourselves to nwait to disable "easy case" in semrelease. @@ -125,15 +135,21 @@ runtime·semacquire(uint32 volatile *addr) // Any semrelease after the cansemacquire knows we're waiting // (we set nwait above), so go to sleep. semqueue(root, addr, &s); - g->status = Gwaiting; - g->waitreason = "semacquire"; - runtime·unlock(root); - runtime·gosched(); - if(cansemacquire(addr)) + runtime·park(runtime·unlock, root, "semacquire"); + if(cansemacquire(addr)) { + if(t0) + runtime·blockevent(s.releasetime - t0, 3); return; + } } } +void +runtime·semacquire(uint32 volatile *addr) +{ + semacquireimpl(addr, 0); +} + void runtime·semrelease(uint32 volatile *addr) { @@ -165,12 +181,15 @@ runtime·semrelease(uint32 volatile *addr) } } runtime·unlock(root); - if(s) + if(s) { + if(s->releasetime) + s->releasetime = runtime·cputicks(); runtime·ready(s->g); + } } func runtime_Semacquire(addr *uint32) { - runtime·semacquire(addr); + semacquireimpl(addr, 1); } func runtime_Semrelease(addr *uint32) { diff --git a/src/pkg/runtime/signal_darwin_386.c b/src/pkg/runtime/signal_darwin_386.c index 9e986352b..132ca931b 100644 --- a/src/pkg/runtime/signal_darwin_386.c +++ b/src/pkg/runtime/signal_darwin_386.c @@ -47,7 +47,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Work around Leopard bug that doesn't set FPE_INTDIV. // Look at instruction to see if it is a divide. @@ -101,7 +101,11 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); } - runtime·printf("pc: %x\n", r->eip); + runtime·printf("PC=%x\n", r->eip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -121,6 +125,8 @@ runtime·signalstack(byte *p, int32 n) st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -129,6 +135,15 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(*(void**)sa.__sigaction_u == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_SIGINFO|SA_ONSTACK; if(restart) diff --git a/src/pkg/runtime/signal_darwin_amd64.c b/src/pkg/runtime/signal_darwin_amd64.c index d9c5f48e7..4b7256bf4 100644 --- a/src/pkg/runtime/signal_darwin_amd64.c +++ b/src/pkg/runtime/signal_darwin_amd64.c @@ -55,7 +55,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Work around Leopard bug that doesn't set FPE_INTDIV. // Look at instruction to see if it is a divide. @@ -111,7 +111,11 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); } - runtime·printf("pc: %X\n", r->rip); + runtime·printf("PC=%X\n", r->rip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -131,6 +135,8 @@ runtime·signalstack(byte *p, int32 n) st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -139,6 +145,15 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(*(void**)sa.__sigaction_u == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_SIGINFO|SA_ONSTACK; if(restart) diff --git a/src/pkg/runtime/signal_freebsd_386.c b/src/pkg/runtime/signal_freebsd_386.c index 80da95d98..254e5e277 100644 --- a/src/pkg/runtime/signal_freebsd_386.c +++ b/src/pkg/runtime/signal_freebsd_386.c @@ -15,7 +15,7 @@ typedef struct sigaction { void (*__sa_sigaction)(int32, Siginfo*, void *); } __sigaction_u; /* signal handler */ int32 sa_flags; /* see signal options below */ - int64 sa_mask; /* signal mask to apply */ + Sigset sa_mask; /* signal mask to apply */ } Sigaction; void @@ -54,7 +54,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. // Have to pass arguments out of band since @@ -97,6 +97,10 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); runtime·printf("PC=%X\n", r->mc_eip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -116,6 +120,8 @@ runtime·signalstack(byte *p, int32 n) st.ss_sp = (int8*)p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -124,11 +130,23 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_SIGINFO|SA_ONSTACK; if(restart) sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; + sa.sa_mask.__bits[0] = ~(uint32)0; + sa.sa_mask.__bits[1] = ~(uint32)0; + sa.sa_mask.__bits[2] = ~(uint32)0; + sa.sa_mask.__bits[3] = ~(uint32)0; if (fn == runtime·sighandler) fn = (void*)runtime·sigtramp; sa.__sigaction_u.__sa_sigaction = (void*)fn; diff --git a/src/pkg/runtime/signal_freebsd_amd64.c b/src/pkg/runtime/signal_freebsd_amd64.c index e4307682f..7dbf36075 100644 --- a/src/pkg/runtime/signal_freebsd_amd64.c +++ b/src/pkg/runtime/signal_freebsd_amd64.c @@ -15,7 +15,7 @@ typedef struct sigaction { void (*__sa_sigaction)(int32, Siginfo*, void *); } __sigaction_u; /* signal handler */ int32 sa_flags; /* see signal options below */ - int64 sa_mask; /* signal mask to apply */ + Sigset sa_mask; /* signal mask to apply */ } Sigaction; void @@ -62,7 +62,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. // Have to pass arguments out of band since @@ -105,6 +105,10 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); runtime·printf("PC=%X\n", r->mc_rip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -124,6 +128,8 @@ runtime·signalstack(byte *p, int32 n) st.ss_sp = (int8*)p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -132,11 +138,23 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_SIGINFO|SA_ONSTACK; if(restart) sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; + sa.sa_mask.__bits[0] = ~(uint32)0; + sa.sa_mask.__bits[1] = ~(uint32)0; + sa.sa_mask.__bits[2] = ~(uint32)0; + sa.sa_mask.__bits[3] = ~(uint32)0; if (fn == runtime·sighandler) fn = (void*)runtime·sigtramp; sa.__sigaction_u.__sa_sigaction = (void*)fn; diff --git a/src/pkg/runtime/signal_freebsd_arm.c b/src/pkg/runtime/signal_freebsd_arm.c new file mode 100644 index 000000000..50c3221bb --- /dev/null +++ b/src/pkg/runtime/signal_freebsd_arm.c @@ -0,0 +1,193 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "signals_GOOS.h" +#include "os_GOOS.h" + +#define r0 __gregs[0] +#define r1 __gregs[1] +#define r2 __gregs[2] +#define r3 __gregs[3] +#define r4 __gregs[4] +#define r5 __gregs[5] +#define r6 __gregs[6] +#define r7 __gregs[7] +#define r8 __gregs[8] +#define r9 __gregs[9] +#define r10 __gregs[10] +#define r11 __gregs[11] +#define r12 __gregs[12] +#define r13 __gregs[13] +#define r14 __gregs[14] +#define r15 __gregs[15] +#define cpsr __gregs[16] + +void +runtime·dumpregs(Mcontext *r) +{ + runtime·printf("r0 %x\n", r->r0); + runtime·printf("r1 %x\n", r->r1); + runtime·printf("r2 %x\n", r->r2); + runtime·printf("r3 %x\n", r->r3); + runtime·printf("r4 %x\n", r->r4); + runtime·printf("r5 %x\n", r->r5); + runtime·printf("r6 %x\n", r->r6); + runtime·printf("r7 %x\n", r->r7); + runtime·printf("r8 %x\n", r->r8); + runtime·printf("r9 %x\n", r->r9); + runtime·printf("r10 %x\n", r->r10); + runtime·printf("fp %x\n", r->r11); + runtime·printf("ip %x\n", r->r12); + runtime·printf("sp %x\n", r->r13); + runtime·printf("lr %x\n", r->r14); + runtime·printf("pc %x\n", r->r15); + runtime·printf("cpsr %x\n", r->cpsr); +} + +extern void runtime·sigtramp(void); + +typedef struct sigaction { + union { + void (*__sa_handler)(int32); + void (*__sa_sigaction)(int32, Siginfo*, void *); + } __sigaction_u; /* signal handler */ + int32 sa_flags; /* see signal options below */ + Sigset sa_mask; /* signal mask to apply */ +} Sigaction; + +void +runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) +{ + Ucontext *uc; + Mcontext *r; + SigTab *t; + + uc = context; + r = &uc->uc_mcontext; + + if(sig == SIGPROF) { + runtime·sigprof((uint8*)r->r15, (uint8*)r->r13, (uint8*)r->r14, gp); + return; + } + + t = &runtime·sigtab[sig]; + if(info->si_code != SI_USER && (t->flags & SigPanic)) { + if(gp == nil || gp == m->g0) + goto Throw; + // Make it look like a call to the signal func. + // Have to pass arguments out of band since + // augmenting the stack frame would break + // the unwinding code. + gp->sig = sig; + gp->sigcode0 = info->si_code; + gp->sigcode1 = (uintptr)info->si_addr; + gp->sigpc = r->r15; + + // Only push runtime·sigpanic if r->mc_rip != 0. + // If r->mc_rip == 0, probably panicked because of a + // call to a nil func. Not pushing that onto sp will + // make the trace look like a call to runtime·sigpanic instead. + // (Otherwise the trace will end at runtime·sigpanic and we + // won't get to see who faulted.) + if(r->r15 != 0) + r->r14 = r->r15; + // In case we are panicking from external C code + r->r10 = (uintptr)gp; + r->r9 = (uintptr)m; + r->r15 = (uintptr)runtime·sigpanic; + return; + } + + if(info->si_code == SI_USER || (t->flags & SigNotify)) + if(runtime·sigsend(sig)) + return; + if(t->flags & SigKill) + runtime·exit(2); + if(!(t->flags & SigThrow)) + return; + +Throw: + runtime·startpanic(); + + if(sig < 0 || sig >= NSIG) + runtime·printf("Signal %d\n", sig); + else + runtime·printf("%s\n", runtime·sigtab[sig].name); + + runtime·printf("PC=%x\n", r->r15); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } + runtime·printf("\n"); + + if(runtime·gotraceback()){ + runtime·traceback((void*)r->r15, (void*)r->r13, (void*)r->r14, gp); + runtime·tracebackothers(gp); + runtime·printf("\n"); + runtime·dumpregs(r); + } + +// breakpoint(); + runtime·exit(2); +} + +void +runtime·signalstack(byte *p, int32 n) +{ + Sigaltstack st; + + st.ss_sp = (uint8*)p; + st.ss_size = n; + st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; + runtime·sigaltstack(&st, nil); +} + +void +runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) +{ + Sigaction sa; + + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) + return; + } + + runtime·memclr((byte*)&sa, sizeof sa); + sa.sa_flags = SA_SIGINFO|SA_ONSTACK; + if(restart) + sa.sa_flags |= SA_RESTART; + sa.sa_mask.__bits[0] = ~(uint32)0; + sa.sa_mask.__bits[1] = ~(uint32)0; + sa.sa_mask.__bits[2] = ~(uint32)0; + sa.sa_mask.__bits[3] = ~(uint32)0; + if (fn == runtime·sighandler) + fn = (void*)runtime·sigtramp; + sa.__sigaction_u.__sa_sigaction = (void*)fn; + runtime·sigaction(i, &sa, nil); +} + +void +runtime·checkgoarm(void) +{ + // TODO(minux) +} + +#pragma textflag 7 +int64 +runtime·cputicks(void) +{ + // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). + // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + // TODO: need more entropy to better seed fastrand1. + return runtime·nanotime(); +} diff --git a/src/pkg/runtime/signal_linux_386.c b/src/pkg/runtime/signal_linux_386.c index b154ad887..9b45ec3bd 100644 --- a/src/pkg/runtime/signal_linux_386.c +++ b/src/pkg/runtime/signal_linux_386.c @@ -50,7 +50,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. // Have to pass arguments out of band since @@ -93,6 +93,10 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); runtime·printf("PC=%X\n", r->eip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -112,6 +116,8 @@ runtime·signalstack(byte *p, int32 n) st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -120,6 +126,16 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0) + runtime·throw("rt_sigaction read failure"); + if(sa.k_sa_handler == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; if(restart) @@ -129,7 +145,8 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) if(fn == runtime·sighandler) fn = (void*)runtime·sigtramp; sa.k_sa_handler = fn; - runtime·rt_sigaction(i, &sa, nil, 8); + if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0) + runtime·throw("rt_sigaction failure"); } #define AT_NULL 0 diff --git a/src/pkg/runtime/signal_linux_amd64.c b/src/pkg/runtime/signal_linux_amd64.c index 14095ba61..c4e39a6ab 100644 --- a/src/pkg/runtime/signal_linux_amd64.c +++ b/src/pkg/runtime/signal_linux_amd64.c @@ -60,7 +60,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. // Have to pass arguments out of band since @@ -103,6 +103,10 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); runtime·printf("PC=%X\n", r->rip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -122,6 +126,8 @@ runtime·signalstack(byte *p, int32 n) st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -130,14 +136,27 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0) + runtime·throw("rt_sigaction read failure"); + if(sa.sa_handler == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; if(restart) sa.sa_flags |= SA_RESTART; sa.sa_mask = ~0ULL; + // TODO(adonovan): Linux manpage says "sa_restorer element is + // obsolete and should not be used". Avoid it here, and test. sa.sa_restorer = (void*)runtime·sigreturn; if(fn == runtime·sighandler) fn = (void*)runtime·sigtramp; sa.sa_handler = fn; - runtime·rt_sigaction(i, &sa, nil, 8); + if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0) + runtime·throw("rt_sigaction failure"); } diff --git a/src/pkg/runtime/signal_linux_arm.c b/src/pkg/runtime/signal_linux_arm.c index 176a4ce56..c26caa7cd 100644 --- a/src/pkg/runtime/signal_linux_arm.c +++ b/src/pkg/runtime/signal_linux_arm.c @@ -57,7 +57,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. // Have to pass arguments out of band since @@ -68,13 +68,22 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) gp->sigcode1 = r->fault_address; gp->sigpc = r->arm_pc; - // If this is a leaf function, we do smash LR, - // but we're not going back there anyway. - // Don't bother smashing if r->arm_pc is 0, - // which is probably a call to a nil func: the - // old link register is more useful in the stack trace. + // We arrange lr, and pc to pretend the panicking + // function calls sigpanic directly. + // Always save LR to stack so that panics in leaf + // functions are correctly handled. This smashes + // the stack frame but we're not going back there + // anyway. + r->arm_sp -= 4; + *(uint32 *)r->arm_sp = r->arm_lr; + // Don't bother saving PC if it's zero, which is + // probably a call to a nil func: the old link register + // is more useful in the stack trace. if(r->arm_pc != 0) r->arm_lr = r->arm_pc; + // In case we are panicking from external C code + r->arm_r10 = (uintptr)gp; + r->arm_r9 = (uintptr)m; r->arm_pc = (uintptr)runtime·sigpanic; return; } @@ -98,6 +107,10 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); runtime·printf("PC=%x\n", r->arm_pc); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -119,6 +132,8 @@ runtime·signalstack(byte *p, int32 n) st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -127,6 +142,16 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0) + runtime·throw("rt_sigaction read failure"); + if(sa.sa_handler == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; if(restart) @@ -136,5 +161,81 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) if(fn == runtime·sighandler) fn = (void*)runtime·sigtramp; sa.sa_handler = fn; - runtime·rt_sigaction(i, &sa, nil, 8); + if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0) + runtime·throw("rt_sigaction failure"); +} + +#define AT_NULL 0 +#define AT_PLATFORM 15 // introduced in at least 2.6.11 +#define AT_HWCAP 16 // introduced in at least 2.6.11 +#define AT_RANDOM 25 // introduced in 2.6.29 +#define HWCAP_VFP (1 << 6) // introduced in at least 2.6.11 +#define HWCAP_VFPv3 (1 << 13) // introduced in 2.6.30 +static uint32 runtime·randomNumber; +uint8 runtime·armArch = 6; // we default to ARMv6 +uint32 runtime·hwcap; // set by setup_auxv +uint8 runtime·goarm; // set by 5l + +void +runtime·checkgoarm(void) +{ + if(runtime·goarm > 5 && !(runtime·hwcap & HWCAP_VFP)) { + runtime·printf("runtime: this CPU has no floating point hardware, so it cannot run\n"); + runtime·printf("this GOARM=%d binary. Recompile using GOARM=5.\n", runtime·goarm); + runtime·exit(1); + } + if(runtime·goarm > 6 && !(runtime·hwcap & HWCAP_VFPv3)) { + runtime·printf("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n"); + runtime·printf("this GOARM=%d binary. Recompile using GOARM=6.\n", runtime·goarm); + runtime·exit(1); + } +} + +#pragma textflag 7 +void +runtime·setup_auxv(int32 argc, void *argv_list) +{ + byte **argv; + byte **envp; + byte *rnd; + uint32 *auxv; + uint32 t; + + argv = &argv_list; + + // skip envp to get to ELF auxiliary vector. + for(envp = &argv[argc+1]; *envp != nil; envp++) + ; + envp++; + + for(auxv=(uint32*)envp; auxv[0] != AT_NULL; auxv += 2) { + switch(auxv[0]) { + case AT_RANDOM: // kernel provided 16-byte worth of random data + if(auxv[1]) { + rnd = (byte*)auxv[1]; + runtime·randomNumber = rnd[4] | rnd[5]<<8 | rnd[6]<<16 | rnd[7]<<24; + } + break; + case AT_PLATFORM: // v5l, v6l, v7l + if(auxv[1]) { + t = *(uint8*)(auxv[1]+1); + if(t >= '5' && t <= '7') + runtime·armArch = t - '0'; + } + break; + case AT_HWCAP: // CPU capability bit flags + runtime·hwcap = auxv[1]; + break; + } + } +} + +#pragma textflag 7 +int64 +runtime·cputicks(void) +{ + // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). + // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + // runtime·randomNumber provides better seeding of fastrand1. + return runtime·nanotime() + runtime·randomNumber; } diff --git a/src/pkg/runtime/signal_netbsd_386.c b/src/pkg/runtime/signal_netbsd_386.c index 39d829484..08744c425 100644 --- a/src/pkg/runtime/signal_netbsd_386.c +++ b/src/pkg/runtime/signal_netbsd_386.c @@ -7,76 +7,79 @@ #include "signals_GOOS.h" #include "os_GOOS.h" +extern void runtime·lwp_tramp(void); extern void runtime·sigtramp(void); typedef struct sigaction { union { - void (*__sa_handler)(int32); - void (*__sa_sigaction)(int32, Siginfo*, void *); - } __sigaction_u; /* signal handler */ - uint32 sa_mask; /* signal mask to apply */ + void (*_sa_handler)(int32); + void (*_sa_sigaction)(int32, Siginfo*, void *); + } _sa_u; /* signal handler */ + uint32 sa_mask[4]; /* signal mask to apply */ int32 sa_flags; /* see signal options below */ } Sigaction; void -runtime·dumpregs(Sigcontext *r) +runtime·dumpregs(McontextT *mc) { - runtime·printf("eax %x\n", r->sc_eax); - runtime·printf("ebx %x\n", r->sc_ebx); - runtime·printf("ecx %x\n", r->sc_ecx); - runtime·printf("edx %x\n", r->sc_edx); - runtime·printf("edi %x\n", r->sc_edi); - runtime·printf("esi %x\n", r->sc_esi); - runtime·printf("ebp %x\n", r->sc_ebp); - runtime·printf("esp %x\n", r->sc_esp); - runtime·printf("eip %x\n", r->sc_eip); - runtime·printf("eflags %x\n", r->sc_eflags); - runtime·printf("cs %x\n", r->sc_cs); - runtime·printf("fs %x\n", r->sc_fs); - runtime·printf("gs %x\n", r->sc_gs); + runtime·printf("eax %x\n", mc->__gregs[REG_EAX]); + runtime·printf("ebx %x\n", mc->__gregs[REG_EBX]); + runtime·printf("ecx %x\n", mc->__gregs[REG_ECX]); + runtime·printf("edx %x\n", mc->__gregs[REG_EDX]); + runtime·printf("edi %x\n", mc->__gregs[REG_EDI]); + runtime·printf("esi %x\n", mc->__gregs[REG_ESI]); + runtime·printf("ebp %x\n", mc->__gregs[REG_EBP]); + runtime·printf("esp %x\n", mc->__gregs[REG_UESP]); + runtime·printf("eip %x\n", mc->__gregs[REG_EIP]); + runtime·printf("eflags %x\n", mc->__gregs[REG_EFL]); + runtime·printf("cs %x\n", mc->__gregs[REG_CS]); + runtime·printf("fs %x\n", mc->__gregs[REG_FS]); + runtime·printf("gs %x\n", mc->__gregs[REG_GS]); } void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) { - Sigcontext *r = context; + UcontextT *uc = context; + McontextT *mc = &uc->uc_mcontext; uintptr *sp; SigTab *t; if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->sc_eip, (uint8*)r->sc_esp, nil, gp); + runtime·sigprof((uint8*)mc->__gregs[REG_EIP], + (uint8*)mc->__gregs[REG_UESP], nil, gp); return; } t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(info->_code != SI_USER && (t->flags & SigPanic)) { + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. - // Have to pass arguments out of band since + // We need to pass arguments out of band since // augmenting the stack frame would break // the unwinding code. gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = *(uintptr*)((byte*)info + 12); /* si_addr */ - gp->sigpc = r->sc_eip; - - // Only push runtime·sigpanic if r->sc_eip != 0. - // If r->sc_eip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->sc_eip != 0) { - sp = (uintptr*)r->sc_esp; - *--sp = r->sc_eip; - r->sc_esp = (uintptr)sp; + gp->sigcode0 = info->_code; + gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */ + gp->sigpc = mc->__gregs[REG_EIP]; + + // Only push runtime·sigpanic if __gregs[REG_EIP] != 0. + // If __gregs[REG_EIP] == 0, probably panicked because of a + // call to a nil func. Not pushing that onto sp will make the + // trace look like a call to runtime·sigpanic instead. + // (Otherwise the trace will end at runtime·sigpanic + // and we won't get to see who faulted.) + if(mc->__gregs[REG_EIP] != 0) { + sp = (uintptr*)mc->__gregs[REG_UESP]; + *--sp = mc->__gregs[REG_EIP]; + mc->__gregs[REG_UESP] = (uintptr)sp; } - r->sc_eip = (uintptr)runtime·sigpanic; + mc->__gregs[REG_EIP] = (uintptr)runtime·sigpanic; return; } - if(info->si_code == SI_USER || (t->flags & SigNotify)) + if(info->_code == SI_USER || (t->flags & SigNotify)) if(runtime·sigsend(sig)) return; if(t->flags & SigKill) @@ -92,13 +95,18 @@ Throw: else runtime·printf("%s\n", runtime·sigtab[sig].name); - runtime·printf("PC=%X\n", r->sc_eip); + runtime·printf("PC=%X\n", mc->__gregs[REG_EIP]); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ - runtime·traceback((void*)r->sc_eip, (void*)r->sc_esp, 0, gp); + runtime·traceback((void*)mc->__gregs[REG_EIP], + (void*)mc->__gregs[REG_UESP], 0, gp); runtime·tracebackothers(gp); - runtime·dumpregs(r); + runtime·dumpregs(mc); } runtime·exit(2); @@ -109,9 +117,11 @@ runtime·signalstack(byte *p, int32 n) { Sigaltstack st; - st.ss_sp = (int8*)p; + st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -120,13 +130,35 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(sa._sa_u._sa_sigaction == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_SIGINFO|SA_ONSTACK; if(restart) sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; + sa.sa_mask[0] = ~0U; + sa.sa_mask[1] = ~0U; + sa.sa_mask[2] = ~0U; + sa.sa_mask[3] = ~0U; if (fn == runtime·sighandler) fn = (void*)runtime·sigtramp; - sa.__sigaction_u.__sa_sigaction = (void*)fn; + sa._sa_u._sa_sigaction = (void*)fn; runtime·sigaction(i, &sa, nil); } + +void +runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) +{ + mc->__gregs[REG_EIP] = (uint32)runtime·lwp_tramp; + mc->__gregs[REG_UESP] = (uint32)stack; + mc->__gregs[REG_EBX] = (uint32)mp; + mc->__gregs[REG_EDX] = (uint32)gp; + mc->__gregs[REG_ESI] = (uint32)fn; +} diff --git a/src/pkg/runtime/signal_netbsd_amd64.c b/src/pkg/runtime/signal_netbsd_amd64.c index 8b4f624e7..46afb682b 100644 --- a/src/pkg/runtime/signal_netbsd_amd64.c +++ b/src/pkg/runtime/signal_netbsd_amd64.c @@ -7,85 +7,86 @@ #include "signals_GOOS.h" #include "os_GOOS.h" +extern void runtime·lwp_tramp(void); extern void runtime·sigtramp(void); typedef struct sigaction { union { - void (*__sa_handler)(int32); - void (*__sa_sigaction)(int32, Siginfo*, void *); - } __sigaction_u; /* signal handler */ - uint32 sa_mask; /* signal mask to apply */ + void (*_sa_handler)(int32); + void (*_sa_sigaction)(int32, Siginfo*, void *); + } _sa_u; /* signal handler */ + uint32 sa_mask[4]; /* signal mask to apply */ int32 sa_flags; /* see signal options below */ } Sigaction; void -runtime·dumpregs(Sigcontext *r) +runtime·dumpregs(McontextT *mc) { - runtime·printf("rax %X\n", r->sc_rax); - runtime·printf("rbx %X\n", r->sc_rbx); - runtime·printf("rcx %X\n", r->sc_rcx); - runtime·printf("rdx %X\n", r->sc_rdx); - runtime·printf("rdi %X\n", r->sc_rdi); - runtime·printf("rsi %X\n", r->sc_rsi); - runtime·printf("rbp %X\n", r->sc_rbp); - runtime·printf("rsp %X\n", r->sc_rsp); - runtime·printf("r8 %X\n", r->sc_r8); - runtime·printf("r9 %X\n", r->sc_r9); - runtime·printf("r10 %X\n", r->sc_r10); - runtime·printf("r11 %X\n", r->sc_r11); - runtime·printf("r12 %X\n", r->sc_r12); - runtime·printf("r13 %X\n", r->sc_r13); - runtime·printf("r14 %X\n", r->sc_r14); - runtime·printf("r15 %X\n", r->sc_r15); - runtime·printf("rip %X\n", r->sc_rip); - runtime·printf("rflags %X\n", r->sc_rflags); - runtime·printf("cs %X\n", r->sc_cs); - runtime·printf("fs %X\n", r->sc_fs); - runtime·printf("gs %X\n", r->sc_gs); + runtime·printf("rax %X\n", mc->__gregs[REG_RAX]); + runtime·printf("rbx %X\n", mc->__gregs[REG_RBX]); + runtime·printf("rcx %X\n", mc->__gregs[REG_RCX]); + runtime·printf("rdx %X\n", mc->__gregs[REG_RDX]); + runtime·printf("rdi %X\n", mc->__gregs[REG_RDI]); + runtime·printf("rsi %X\n", mc->__gregs[REG_RSI]); + runtime·printf("rbp %X\n", mc->__gregs[REG_RBP]); + runtime·printf("rsp %X\n", mc->__gregs[REG_RSP]); + runtime·printf("r8 %X\n", mc->__gregs[REG_R8]); + runtime·printf("r9 %X\n", mc->__gregs[REG_R9]); + runtime·printf("r10 %X\n", mc->__gregs[REG_R10]); + runtime·printf("r11 %X\n", mc->__gregs[REG_R11]); + runtime·printf("r12 %X\n", mc->__gregs[REG_R12]); + runtime·printf("r13 %X\n", mc->__gregs[REG_R13]); + runtime·printf("r14 %X\n", mc->__gregs[REG_R14]); + runtime·printf("r15 %X\n", mc->__gregs[REG_R15]); + runtime·printf("rip %X\n", mc->__gregs[REG_RIP]); + runtime·printf("rflags %X\n", mc->__gregs[REG_RFLAGS]); + runtime·printf("cs %X\n", mc->__gregs[REG_CS]); + runtime·printf("fs %X\n", mc->__gregs[REG_FS]); + runtime·printf("gs %X\n", mc->__gregs[REG_GS]); } void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) { - Sigcontext *r = context; + UcontextT *uc = context; + McontextT *mc = &uc->uc_mcontext; uintptr *sp; SigTab *t; if(sig == SIGPROF) { - runtime·sigprof((uint8*)r->sc_rip, - (uint8*)r->sc_rsp, nil, gp); + runtime·sigprof((uint8*)mc->__gregs[REG_RIP], + (uint8*)mc->__gregs[REG_RSP], nil, gp); return; } t = &runtime·sigtab[sig]; - if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(info->_code != SI_USER && (t->flags & SigPanic)) { + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. - // Have to pass arguments out of band since - // augmenting the stack frame would break - // the unwinding code. + // We need to pass arguments out of band since augmenting the + // stack frame would break the unwinding code. gp->sig = sig; - gp->sigcode0 = info->si_code; - gp->sigcode1 = *(uintptr*)((byte*)info + 16); /* si_addr */ - gp->sigpc = r->sc_rip; - - // Only push runtime·sigpanic if r->mc_rip != 0. - // If r->mc_rip == 0, probably panicked because of a - // call to a nil func. Not pushing that onto sp will - // make the trace look like a call to runtime·sigpanic instead. - // (Otherwise the trace will end at runtime·sigpanic and we - // won't get to see who faulted.) - if(r->sc_rip != 0) { - sp = (uintptr*)r->sc_rsp; - *--sp = r->sc_rip; - r->sc_rsp = (uintptr)sp; + gp->sigcode0 = info->_code; + gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */ + gp->sigpc = mc->__gregs[REG_RIP]; + + // Only push runtime·sigpanic if __gregs[REG_RIP] != 0. + // If __gregs[REG_RIP] == 0, probably panicked because of a + // call to a nil func. Not pushing that onto sp will make the + // trace look like a call to runtime·sigpanic instead. + // (Otherwise the trace will end at runtime·sigpanic + // and we won't get to see who faulted.) + if(mc->__gregs[REG_RIP] != 0) { + sp = (uintptr*)mc->__gregs[REG_RSP]; + *--sp = mc->__gregs[REG_RIP]; + mc->__gregs[REG_RSP] = (uintptr)sp; } - r->sc_rip = (uintptr)runtime·sigpanic; + mc->__gregs[REG_RIP] = (uintptr)runtime·sigpanic; return; } - if(info->si_code == SI_USER || (t->flags & SigNotify)) + if(info->_code == SI_USER || (t->flags & SigNotify)) if(runtime·sigsend(sig)) return; if(t->flags & SigKill) @@ -101,13 +102,18 @@ Throw: else runtime·printf("%s\n", runtime·sigtab[sig].name); - runtime·printf("PC=%X\n", r->sc_rip); + runtime·printf("PC=%X\n", mc->__gregs[REG_RIP]); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ - runtime·traceback((void*)r->sc_rip, (void*)r->sc_rsp, 0, gp); + runtime·traceback((void*)mc->__gregs[REG_RIP], + (void*)mc->__gregs[REG_RSP], 0, gp); runtime·tracebackothers(gp); - runtime·dumpregs(r); + runtime·dumpregs(mc); } runtime·exit(2); @@ -118,9 +124,11 @@ runtime·signalstack(byte *p, int32 n) { Sigaltstack st; - st.ss_sp = (int8*)p; + st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -129,13 +137,36 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(sa._sa_u._sa_sigaction == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_SIGINFO|SA_ONSTACK; if(restart) sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; + sa.sa_mask[0] = ~0U; + sa.sa_mask[1] = ~0U; + sa.sa_mask[2] = ~0U; + sa.sa_mask[3] = ~0U; if (fn == runtime·sighandler) fn = (void*)runtime·sigtramp; - sa.__sigaction_u.__sa_sigaction = (void*)fn; + sa._sa_u._sa_sigaction = (void*)fn; runtime·sigaction(i, &sa, nil); } + +void +runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) +{ + // Machine dependent mcontext initialisation for LWP. + mc->__gregs[REG_RIP] = (uint64)runtime·lwp_tramp; + mc->__gregs[REG_RSP] = (uint64)stack; + mc->__gregs[REG_R8] = (uint64)mp; + mc->__gregs[REG_R9] = (uint64)gp; + mc->__gregs[REG_R12] = (uint64)fn; +} diff --git a/src/pkg/runtime/signal_netbsd_arm.c b/src/pkg/runtime/signal_netbsd_arm.c new file mode 100644 index 000000000..97f62687b --- /dev/null +++ b/src/pkg/runtime/signal_netbsd_arm.c @@ -0,0 +1,208 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "signals_GOOS.h" +#include "os_GOOS.h" + +#define r0 __gregs[0] +#define r1 __gregs[1] +#define r2 __gregs[2] +#define r3 __gregs[3] +#define r4 __gregs[4] +#define r5 __gregs[5] +#define r6 __gregs[6] +#define r7 __gregs[7] +#define r8 __gregs[8] +#define r9 __gregs[9] +#define r10 __gregs[10] +#define r11 __gregs[11] +#define r12 __gregs[12] +#define r13 __gregs[13] +#define r14 __gregs[14] +#define r15 __gregs[15] +#define cpsr __gregs[16] + +void +runtime·dumpregs(McontextT *r) +{ + runtime·printf("r0 %x\n", r->r0); + runtime·printf("r1 %x\n", r->r1); + runtime·printf("r2 %x\n", r->r2); + runtime·printf("r3 %x\n", r->r3); + runtime·printf("r4 %x\n", r->r4); + runtime·printf("r5 %x\n", r->r5); + runtime·printf("r6 %x\n", r->r6); + runtime·printf("r7 %x\n", r->r7); + runtime·printf("r8 %x\n", r->r8); + runtime·printf("r9 %x\n", r->r9); + runtime·printf("r10 %x\n", r->r10); + runtime·printf("fp %x\n", r->r11); + runtime·printf("ip %x\n", r->r12); + runtime·printf("sp %x\n", r->r13); + runtime·printf("lr %x\n", r->r14); + runtime·printf("pc %x\n", r->r15); + runtime·printf("cpsr %x\n", r->cpsr); +} + +extern void runtime·lwp_tramp(void); +extern void runtime·sigtramp(void); + +typedef struct sigaction { + union { + void (*_sa_handler)(int32); + void (*_sa_sigaction)(int32, Siginfo*, void *); + } _sa_u; /* signal handler */ + uint32 sa_mask[4]; /* signal mask to apply */ + int32 sa_flags; /* see signal options below */ +} Sigaction; + +void +runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) +{ + UcontextT *uc; + McontextT *r; + SigTab *t; + + uc = context; + r = &uc->uc_mcontext; + + if(sig == SIGPROF) { + runtime·sigprof((uint8*)r->r15, (uint8*)r->r13, (uint8*)r->r14, gp); + return; + } + + t = &runtime·sigtab[sig]; + if(info->_code != SI_USER && (t->flags & SigPanic)) { + if(gp == nil || gp == m->g0) + goto Throw; + // Make it look like a call to the signal func. + // We have to pass arguments out of band since + // augmenting the stack frame would break + // the unwinding code. + gp->sig = sig; + gp->sigcode0 = info->_code; + gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */ + gp->sigpc = r->r15; + + // We arrange lr, and pc to pretend the panicking + // function calls sigpanic directly. + // Always save LR to stack so that panics in leaf + // functions are correctly handled. This smashes + // the stack frame but we're not going back there + // anyway. + r->r13 -= 4; + *(uint32 *)r->r13 = r->r14; + // Don't bother saving PC if it's zero, which is + // probably a call to a nil func: the old link register + // is more useful in the stack trace. + if(r->r15 != 0) + r->r14 = r->r15; + // In case we are panicking from external C code + r->r10 = (uintptr)gp; + r->r9 = (uintptr)m; + r->r15 = (uintptr)runtime·sigpanic; + return; + } + + if(info->_code == SI_USER || (t->flags & SigNotify)) + if(runtime·sigsend(sig)) + return; + if(t->flags & SigKill) + runtime·exit(2); + if(!(t->flags & SigThrow)) + return; + +Throw: + runtime·startpanic(); + + if(sig < 0 || sig >= NSIG) + runtime·printf("Signal %d\n", sig); + else + runtime·printf("%s\n", runtime·sigtab[sig].name); + + runtime·printf("PC=%x\n", r->r15); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } + runtime·printf("\n"); + + if(runtime·gotraceback()){ + runtime·traceback((void*)r->r15, (void*)r->r13, (void*)r->r14, gp); + runtime·tracebackothers(gp); + runtime·printf("\n"); + runtime·dumpregs(r); + } + +// breakpoint(); + runtime·exit(2); +} + +void +runtime·signalstack(byte *p, int32 n) +{ + Sigaltstack st; + + st.ss_sp = (uint8*)p; + st.ss_size = n; + st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; + runtime·sigaltstack(&st, nil); +} + +void +runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) +{ + Sigaction sa; + + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(sa._sa_u._sa_sigaction == SIG_IGN) + return; + } + + runtime·memclr((byte*)&sa, sizeof sa); + sa.sa_flags = SA_SIGINFO|SA_ONSTACK; + if(restart) + sa.sa_flags |= SA_RESTART; + sa.sa_mask[0] = ~0U; + sa.sa_mask[1] = ~0U; + sa.sa_mask[2] = ~0U; + sa.sa_mask[3] = ~0U; + if (fn == runtime·sighandler) + fn = (void*)runtime·sigtramp; + sa._sa_u._sa_sigaction = (void*)fn; + runtime·sigaction(i, &sa, nil); +} + +void +runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void)) +{ + mc->r15 = (uint32)runtime·lwp_tramp; + mc->r13 = (uint32)stack; + mc->r0 = (uint32)mp; + mc->r1 = (uint32)gp; + mc->r2 = (uint32)fn; +} + +void +runtime·checkgoarm(void) +{ + // TODO(minux) +} + +#pragma textflag 7 +int64 +runtime·cputicks() { + // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). + // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + // TODO: need more entropy to better seed fastrand1. + return runtime·nanotime(); +} diff --git a/src/pkg/runtime/signal_openbsd_386.c b/src/pkg/runtime/signal_openbsd_386.c index 39d829484..516797c8d 100644 --- a/src/pkg/runtime/signal_openbsd_386.c +++ b/src/pkg/runtime/signal_openbsd_386.c @@ -50,7 +50,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. // Have to pass arguments out of band since @@ -93,6 +93,10 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); runtime·printf("PC=%X\n", r->sc_eip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -109,9 +113,11 @@ runtime·signalstack(byte *p, int32 n) { Sigaltstack st; - st.ss_sp = (int8*)p; + st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -120,6 +126,15 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_SIGINFO|SA_ONSTACK; if(restart) diff --git a/src/pkg/runtime/signal_openbsd_amd64.c b/src/pkg/runtime/signal_openbsd_amd64.c index 8b4f624e7..0d0db770b 100644 --- a/src/pkg/runtime/signal_openbsd_amd64.c +++ b/src/pkg/runtime/signal_openbsd_amd64.c @@ -59,7 +59,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) t = &runtime·sigtab[sig]; if(info->si_code != SI_USER && (t->flags & SigPanic)) { - if(gp == nil) + if(gp == nil || gp == m->g0) goto Throw; // Make it look like a call to the signal func. // Have to pass arguments out of band since @@ -70,8 +70,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) gp->sigcode1 = *(uintptr*)((byte*)info + 16); /* si_addr */ gp->sigpc = r->sc_rip; - // Only push runtime·sigpanic if r->mc_rip != 0. - // If r->mc_rip == 0, probably panicked because of a + // Only push runtime·sigpanic if r->sc_rip != 0. + // If r->sc_rip == 0, probably panicked because of a // call to a nil func. Not pushing that onto sp will // make the trace look like a call to runtime·sigpanic instead. // (Otherwise the trace will end at runtime·sigpanic and we @@ -102,6 +102,10 @@ Throw: runtime·printf("%s\n", runtime·sigtab[sig].name); runtime·printf("PC=%X\n", r->sc_rip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ @@ -118,9 +122,11 @@ runtime·signalstack(byte *p, int32 n) { Sigaltstack st; - st.ss_sp = (int8*)p; + st.ss_sp = p; st.ss_size = n; st.ss_flags = 0; + if(p == nil) + st.ss_flags = SS_DISABLE; runtime·sigaltstack(&st, nil); } @@ -129,12 +135,21 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart) { Sigaction sa; + // If SIGHUP handler is SIG_IGN, assume running + // under nohup and do not set explicit handler. + if(i == SIGHUP) { + runtime·memclr((byte*)&sa, sizeof sa); + runtime·sigaction(i, nil, &sa); + if(sa.__sigaction_u.__sa_sigaction == SIG_IGN) + return; + } + runtime·memclr((byte*)&sa, sizeof sa); sa.sa_flags = SA_SIGINFO|SA_ONSTACK; if(restart) sa.sa_flags |= SA_RESTART; - sa.sa_mask = ~0ULL; - if (fn == runtime·sighandler) + sa.sa_mask = ~0U; + if(fn == runtime·sighandler) fn = (void*)runtime·sigtramp; sa.__sigaction_u.__sa_sigaction = (void*)fn; runtime·sigaction(i, &sa, nil); diff --git a/src/pkg/runtime/signal_plan9_386.c b/src/pkg/runtime/signal_plan9_386.c index d26688516..17bc11749 100644 --- a/src/pkg/runtime/signal_plan9_386.c +++ b/src/pkg/runtime/signal_plan9_386.c @@ -3,6 +3,107 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" +#include "signals_GOOS.h" + +void +runtime·dumpregs(Ureg *u) +{ + runtime·printf("ax %X\n", u->ax); + runtime·printf("bx %X\n", u->bx); + runtime·printf("cx %X\n", u->cx); + runtime·printf("dx %X\n", u->dx); + runtime·printf("di %X\n", u->di); + runtime·printf("si %X\n", u->si); + runtime·printf("bp %X\n", u->bp); + runtime·printf("sp %X\n", u->sp); + runtime·printf("pc %X\n", u->pc); + runtime·printf("flags %X\n", u->flags); + runtime·printf("cs %X\n", u->cs); + runtime·printf("fs %X\n", u->fs); + runtime·printf("gs %X\n", u->gs); +} + +int32 +runtime·sighandler(void *v, int8 *s, G *gp) +{ + Ureg *ureg; + uintptr *sp; + SigTab *sig, *nsig; + int32 len, i; + + if(!s) + return NCONT; + + len = runtime·findnull((byte*)s); + if(len <= 4 || runtime·mcmp((byte*)s, (byte*)"sys:", 4) != 0) + return NDFLT; + + nsig = nil; + sig = runtime·sigtab; + for(i=0; i < NSIG; i++) { + if(runtime·strstr((byte*)s, (byte*)sig->name)) { + nsig = sig; + break; + } + sig++; + } + + if(nsig == nil) + return NDFLT; + + ureg = v; + if(nsig->flags & SigPanic) { + if(gp == nil || m->notesig == 0) + goto Throw; + + // Save error string from sigtramp's stack, + // into gsignal->sigcode0, so we can reliably + // access it from the panic routines. + if(len > ERRMAX) + len = ERRMAX; + runtime·memmove((void*)m->notesig, (void*)s, len); + + gp->sig = i; + gp->sigpc = ureg->pc; + + // Only push runtime·sigpanic if ureg->pc != 0. + // If ureg->pc == 0, probably panicked because of a + // call to a nil func. Not pushing that onto sp will + // make the trace look like a call to runtime·sigpanic instead. + // (Otherwise the trace will end at runtime·sigpanic and we + // won't get to see who faulted.) + if(ureg->pc != 0) { + sp = (uintptr*)ureg->sp; + *--sp = ureg->pc; + ureg->sp = (uint32)sp; + } + ureg->pc = (uintptr)runtime·sigpanic; + return NCONT; + } + + if(!(nsig->flags & SigThrow)) + return NDFLT; + +Throw: + runtime·startpanic(); + + runtime·printf("%s\n", s); + runtime·printf("PC=%X\n", ureg->pc); + runtime·printf("\n"); + + if(runtime·gotraceback()) { + runtime·traceback((void*)ureg->pc, (void*)ureg->sp, 0, gp); + runtime·tracebackothers(gp); + runtime·dumpregs(ureg); + } + runtime·goexitsall(""); + runtime·exits(s); + + return 0; +} + void runtime·sigenable(uint32 sig) diff --git a/src/pkg/runtime/signal_plan9_amd64.c b/src/pkg/runtime/signal_plan9_amd64.c new file mode 100644 index 000000000..e4f946abc --- /dev/null +++ b/src/pkg/runtime/signal_plan9_amd64.c @@ -0,0 +1,127 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" +#include "signals_GOOS.h" + +void +runtime·dumpregs(Ureg *u) +{ + runtime·printf("ax %X\n", u->ax); + runtime·printf("bx %X\n", u->bx); + runtime·printf("cx %X\n", u->cx); + runtime·printf("dx %X\n", u->dx); + runtime·printf("di %X\n", u->di); + runtime·printf("si %X\n", u->si); + runtime·printf("bp %X\n", u->bp); + runtime·printf("sp %X\n", u->sp); + runtime·printf("r8 %X\n", u->r8); + runtime·printf("r9 %X\n", u->r9); + runtime·printf("r10 %X\n", u->r10); + runtime·printf("r11 %X\n", u->r11); + runtime·printf("r12 %X\n", u->r12); + runtime·printf("r13 %X\n", u->r13); + runtime·printf("r14 %X\n", u->r14); + runtime·printf("r15 %X\n", u->r15); + runtime·printf("ip %X\n", u->ip); + runtime·printf("flags %X\n", u->flags); + runtime·printf("cs %X\n", (uint64)u->cs); + runtime·printf("fs %X\n", (uint64)u->fs); + runtime·printf("gs %X\n", (uint64)u->gs); +} + +int32 +runtime·sighandler(void *v, int8 *s, G *gp) +{ + Ureg *ureg; + uintptr *sp; + SigTab *sig, *nsig; + int32 len, i; + + if(!s) + return NCONT; + + len = runtime·findnull((byte*)s); + if(len <= 4 || runtime·mcmp((byte*)s, (byte*)"sys:", 4) != 0) + return NDFLT; + + nsig = nil; + sig = runtime·sigtab; + for(i=0; i < NSIG; i++) { + if(runtime·strstr((byte*)s, (byte*)sig->name)) { + nsig = sig; + break; + } + sig++; + } + + if(nsig == nil) + return NDFLT; + + ureg = v; + if(nsig->flags & SigPanic) { + if(gp == nil || m->notesig == 0) + goto Throw; + + // Save error string from sigtramp's stack, + // into gsignal->sigcode0, so we can reliably + // access it from the panic routines. + if(len > ERRMAX) + len = ERRMAX; + runtime·memmove((void*)m->notesig, (void*)s, len); + + gp->sig = i; + gp->sigpc = ureg->ip; + + // Only push runtime·sigpanic if ureg->ip != 0. + // If ureg->ip == 0, probably panicked because of a + // call to a nil func. Not pushing that onto sp will + // make the trace look like a call to runtime·sigpanic instead. + // (Otherwise the trace will end at runtime·sigpanic and we + // won't get to see who faulted.) + if(ureg->ip != 0) { + sp = (uintptr*)ureg->sp; + *--sp = ureg->ip; + ureg->sp = (uint64)sp; + } + ureg->ip = (uintptr)runtime·sigpanic; + return NCONT; + } + + if(!(nsig->flags & SigThrow)) + return NDFLT; + +Throw: + runtime·startpanic(); + + runtime·printf("%s\n", s); + runtime·printf("PC=%X\n", ureg->ip); + runtime·printf("\n"); + + if(runtime·gotraceback()) { + runtime·traceback((void*)ureg->ip, (void*)ureg->sp, 0, gp); + runtime·tracebackothers(gp); + runtime·dumpregs(ureg); + } + runtime·goexitsall(""); + runtime·exits(s); + + return 0; +} + +void +runtime·sigenable(uint32 sig) +{ + USED(sig); +} + +void +runtime·resetcpuprofiler(int32 hz) +{ + // TODO: Enable profiling interrupts. + + m->profilehz = hz; +} diff --git a/src/pkg/runtime/signal_windows_386.c b/src/pkg/runtime/signal_windows_386.c index a248374db..d76d5bf4b 100644 --- a/src/pkg/runtime/signal_windows_386.c +++ b/src/pkg/runtime/signal_windows_386.c @@ -68,11 +68,15 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) info->ExceptionInformation[0], info->ExceptionInformation[1]); runtime·printf("PC=%x\n", r->Eip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ - runtime·traceback((void*)r->Eip, (void*)r->Esp, 0, m->curg); - runtime·tracebackothers(m->curg); + runtime·traceback((void*)r->Eip, (void*)r->Esp, 0, gp); + runtime·tracebackothers(gp); runtime·dumpregs(r); } diff --git a/src/pkg/runtime/signal_windows_amd64.c b/src/pkg/runtime/signal_windows_amd64.c index 1cdf1cac4..3729aa57b 100644 --- a/src/pkg/runtime/signal_windows_amd64.c +++ b/src/pkg/runtime/signal_windows_amd64.c @@ -75,6 +75,10 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp) info->ExceptionInformation[0], info->ExceptionInformation[1]); runtime·printf("PC=%X\n", r->Rip); + if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) { + runtime·printf("signal arrived during cgo execution\n"); + gp = m->lockedg; + } runtime·printf("\n"); if(runtime·gotraceback()){ diff --git a/src/pkg/runtime/signals_linux.h b/src/pkg/runtime/signals_linux.h index 345a6c5d1..9c3567007 100644 --- a/src/pkg/runtime/signals_linux.h +++ b/src/pkg/runtime/signals_linux.h @@ -42,7 +42,7 @@ SigTab runtime·sigtab[] = { /* 30 */ N, "SIGPWR: power failure restart", /* 31 */ N, "SIGSYS: bad system call", /* 32 */ N, "signal 32", - /* 33 */ N, "signal 33", + /* 33 */ 0, "signal 33", /* SIGSETXID; see issue 3871 */ /* 34 */ N, "signal 34", /* 35 */ N, "signal 35", /* 36 */ N, "signal 36", diff --git a/src/pkg/runtime/signals_netbsd.h b/src/pkg/runtime/signals_netbsd.h index 4d27e050d..7140de86f 100644 --- a/src/pkg/runtime/signals_netbsd.h +++ b/src/pkg/runtime/signals_netbsd.h @@ -9,16 +9,16 @@ #define D SigDefault SigTab runtime·sigtab[] = { - /* 0 */ 0, "SIGNONE: no trap", - /* 1 */ N+K, "SIGHUP: terminal line hangup", - /* 2 */ N+K, "SIGINT: interrupt", - /* 3 */ N+T, "SIGQUIT: quit", - /* 4 */ T, "SIGILL: illegal instruction", - /* 5 */ T, "SIGTRAP: trace trap", - /* 6 */ N+T, "SIGABRT: abort", - /* 7 */ T, "SIGEMT: emulate instruction executed", - /* 8 */ P, "SIGFPE: floating-point exception", - /* 9 */ 0, "SIGKILL: kill", + /* 0 */ 0, "SIGNONE: no trap", + /* 1 */ N+K, "SIGHUP: terminal line hangup", + /* 2 */ N+K, "SIGINT: interrupt", + /* 3 */ N+T, "SIGQUIT: quit", + /* 4 */ T, "SIGILL: illegal instruction", + /* 5 */ T, "SIGTRAP: trace trap", + /* 6 */ N+T, "SIGABRT: abort", + /* 7 */ T, "SIGEMT: emulate instruction executed", + /* 8 */ P, "SIGFPE: floating-point exception", + /* 9 */ 0, "SIGKILL: kill", /* 10 */ P, "SIGBUS: bus error", /* 11 */ P, "SIGSEGV: segmentation violation", /* 12 */ T, "SIGSYS: bad system call", diff --git a/src/pkg/runtime/signals_openbsd.h b/src/pkg/runtime/signals_openbsd.h index 4d27e050d..7140de86f 100644 --- a/src/pkg/runtime/signals_openbsd.h +++ b/src/pkg/runtime/signals_openbsd.h @@ -9,16 +9,16 @@ #define D SigDefault SigTab runtime·sigtab[] = { - /* 0 */ 0, "SIGNONE: no trap", - /* 1 */ N+K, "SIGHUP: terminal line hangup", - /* 2 */ N+K, "SIGINT: interrupt", - /* 3 */ N+T, "SIGQUIT: quit", - /* 4 */ T, "SIGILL: illegal instruction", - /* 5 */ T, "SIGTRAP: trace trap", - /* 6 */ N+T, "SIGABRT: abort", - /* 7 */ T, "SIGEMT: emulate instruction executed", - /* 8 */ P, "SIGFPE: floating-point exception", - /* 9 */ 0, "SIGKILL: kill", + /* 0 */ 0, "SIGNONE: no trap", + /* 1 */ N+K, "SIGHUP: terminal line hangup", + /* 2 */ N+K, "SIGINT: interrupt", + /* 3 */ N+T, "SIGQUIT: quit", + /* 4 */ T, "SIGILL: illegal instruction", + /* 5 */ T, "SIGTRAP: trace trap", + /* 6 */ N+T, "SIGABRT: abort", + /* 7 */ T, "SIGEMT: emulate instruction executed", + /* 8 */ P, "SIGFPE: floating-point exception", + /* 9 */ 0, "SIGKILL: kill", /* 10 */ P, "SIGBUS: bus error", /* 11 */ P, "SIGSEGV: segmentation violation", /* 12 */ T, "SIGSYS: bad system call", diff --git a/src/pkg/runtime/signals_plan9.h b/src/pkg/runtime/signals_plan9.h index 5df757613..0f1165e2a 100644 --- a/src/pkg/runtime/signals_plan9.h +++ b/src/pkg/runtime/signals_plan9.h @@ -1 +1,24 @@ -// nothing to see here +#define N SigNotify +#define T SigThrow +#define P SigPanic + +SigTab runtime·sigtab[] = { + P, "sys: fp:", + + // Go libraries expect to be able + // to recover from memory + // read/write errors, so we flag + // those as panics. All other traps + // are generally more serious and + // should immediately throw an + // exception. + P, "sys: trap: fault read addr", + P, "sys: trap: fault write addr", + T, "sys: trap:", + + N, "sys: bad sys call", +}; + +#undef N +#undef T +#undef P diff --git a/src/pkg/runtime/sigqueue.goc b/src/pkg/runtime/sigqueue.goc index b49fdba86..ab5f312e4 100644 --- a/src/pkg/runtime/sigqueue.goc +++ b/src/pkg/runtime/sigqueue.goc @@ -5,36 +5,24 @@ // This file implements runtime support for signal handling. // // Most synchronization primitives are not available from -// the signal handler (it cannot block and cannot use locks) +// the signal handler (it cannot block, allocate memory, or use locks) // so the handler communicates with a processing goroutine // via struct sig, below. // -// Ownership for sig.Note passes back and forth between -// the signal handler and the signal goroutine in rounds. -// The initial state is that sig.note is cleared (setup by signal_enable). -// At the beginning of each round, mask == 0. -// The round goes through three stages: -// -// (In parallel) -// 1a) One or more signals arrive and are handled -// by sigsend using cas to set bits in sig.mask. -// The handler that changes sig.mask from zero to non-zero -// calls notewakeup(&sig). -// 1b) Sigrecv calls notesleep(&sig) to wait for the wakeup. -// -// 2) Having received the wakeup, sigrecv knows that sigsend -// will not send another wakeup, so it can noteclear(&sig) -// to prepare for the next round. (Sigsend may still be adding -// signals to sig.mask at this point, which is fine.) -// -// 3) Sigrecv uses cas to grab the current sig.mask and zero it, -// triggering the next round. -// -// The signal handler takes ownership of the note by atomically -// changing mask from a zero to non-zero value. It gives up -// ownership by calling notewakeup. The signal goroutine takes -// ownership by returning from notesleep (caused by the notewakeup) -// and gives up ownership by clearing mask. +// sigsend() is called by the signal handler to queue a new signal. +// signal_recv() is called by the Go program to receive a newly queued signal. +// Synchronization between sigsend() and signal_recv() is based on the sig.state +// variable. It can be in 3 states: 0, HASWAITER and HASSIGNAL. +// HASWAITER means that signal_recv() is blocked on sig.Note and there are no +// new pending signals. +// HASSIGNAL means that sig.mask *may* contain new pending signals, +// signal_recv() can't be blocked in this state. +// 0 means that there are no new pending signals and signal_recv() is not blocked. +// Transitions between states are done atomically with CAS. +// When signal_recv() is unblocked, it resets sig.Note and rechecks sig.mask. +// If several sigsend()'s and signal_recv() execute concurrently, it can lead to +// unnecessary rechecks of sig.mask, but must not lead to missed signals +// nor deadlocks. package runtime #include "runtime.h" @@ -45,15 +33,20 @@ static struct { Note; uint32 mask[(NSIG+31)/32]; uint32 wanted[(NSIG+31)/32]; - uint32 kick; + uint32 state; bool inuse; } sig; +enum { + HASWAITER = 1, + HASSIGNAL = 2, +}; + // Called from sighandler to send a signal back out of the signal handling thread. bool runtime·sigsend(int32 s) { - uint32 bit, mask; + uint32 bit, mask, old, new; if(!sig.inuse || s < 0 || s >= 32*nelem(sig.wanted) || !(sig.wanted[s/32]&(1U<<(s&31)))) return false; @@ -65,8 +58,20 @@ runtime·sigsend(int32 s) if(runtime·cas(&sig.mask[s/32], mask, mask|bit)) { // Added to queue. // Only send a wakeup if the receiver needs a kick. - if(runtime·cas(&sig.kick, 1, 0)) - runtime·notewakeup(&sig); + for(;;) { + old = runtime·atomicload(&sig.state); + if(old == HASSIGNAL) + break; + if(old == HASWAITER) + new = 0; + else // if(old == 0) + new = HASSIGNAL; + if(runtime·cas(&sig.state, old, new)) { + if (old == HASWAITER) + runtime·notewakeup(&sig); + break; + } + } break; } } @@ -77,7 +82,7 @@ runtime·sigsend(int32 s) // Must only be called from a single goroutine at a time. func signal_recv() (m uint32) { static uint32 recv[nelem(sig.mask)]; - int32 i, more; + uint32 i, old, new; for(;;) { // Serve from local copy if there are bits left. @@ -89,15 +94,27 @@ func signal_recv() (m uint32) { } } - // Get a new local copy. - // Ask for a kick if more signals come in - // during or after our check (before the sleep). - if(sig.kick == 0) { - runtime·noteclear(&sig); - runtime·cas(&sig.kick, 0, 1); + // Check and update sig.state. + for(;;) { + old = runtime·atomicload(&sig.state); + if(old == HASWAITER) + runtime·throw("inconsistent state in signal_recv"); + if(old == HASSIGNAL) + new = 0; + else // if(old == 0) + new = HASWAITER; + if(runtime·cas(&sig.state, old, new)) { + if (new == HASWAITER) { + runtime·entersyscallblock(); + runtime·notesleep(&sig); + runtime·exitsyscall(); + runtime·noteclear(&sig); + } + break; + } } - more = 0; + // Get a new local copy. for(i=0; i MaxMem/elemsize check here is not strictly necessary, + // but it produces a 'len out of range' error instead of a 'cap out of range' error + // when someone does make([]T, bignumber). 'cap out of range' is true too, + // but since the cap is only being supplied implicitly, saying len is clearer. + // See issue 4085. + if(len < 0 || (intgo)len != len || t->elem->size > 0 && len > MaxMem / t->elem->size) runtime·panicstring("makeslice: len out of range"); - if(cap < len || (int32)cap != cap || t->elem->size > 0 && cap > ((uintptr)-1) / t->elem->size) + + if(cap < len || (intgo)cap != cap || t->elem->size > 0 && cap > MaxMem / t->elem->size) runtime·panicstring("makeslice: cap out of range"); makeslice1(t, len, cap, &ret); @@ -35,10 +43,10 @@ runtime·makeslice(SliceType *t, int64 len, int64 cap, Slice ret) // Dummy word to use as base pointer for make([]T, 0). // Since you cannot take the address of such a slice, // you can't tell that they all have the same base pointer. -static uintptr zerobase; +uintptr runtime·zerobase; static void -makeslice1(SliceType *t, int32 len, int32 cap, Slice *ret) +makeslice1(SliceType *t, intgo len, intgo cap, Slice *ret) { uintptr size; @@ -47,22 +55,34 @@ makeslice1(SliceType *t, int32 len, int32 cap, Slice *ret) ret->len = len; ret->cap = cap; - if(cap == 0) - ret->array = (byte*)&zerobase; + if(size == 0) + ret->array = (byte*)&runtime·zerobase; else if((t->elem->kind&KindNoPointers)) ret->array = runtime·mallocgc(size, FlagNoPointers, 1, 1); - else - ret->array = runtime·mal(size); + else { + ret->array = runtime·mallocgc(size, 0, 1, 1); + + if(UseSpanType) { + if(false) { + runtime·printf("new slice [%D]%S: %p\n", (int64)cap, *t->elem->string, ret->array); + } + runtime·settype(ret->array, (uintptr)t->elem | TypeInfo_Array); + } + } } // appendslice(type *Type, x, y, []T) []T +#pragma textflag 7 void runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret) { - int32 m; + intgo m; uintptr w; + void *pc; + uint8 *p, *q; m = x.len+y.len; + w = t->elem->size; if(m < x.len) runtime·throw("append: slice overflow"); @@ -72,30 +92,83 @@ runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret) else ret = x; - w = t->elem->size; - runtime·memmove(ret.array + ret.len*w, y.array, y.len*w); + if(raceenabled) { + // Don't mark read/writes on the newly allocated slice. + pc = runtime·getcallerpc(&t); + // read x[:len] + if(m > x.cap) + runtime·racereadrangepc(x.array, x.len*w, w, pc, runtime·appendslice); + // read y + runtime·racereadrangepc(y.array, y.len*w, w, pc, runtime·appendslice); + // write x[len(x):len(x)+len(y)] + if(m <= x.cap) + runtime·racewriterangepc(ret.array+ret.len*w, y.len*w, w, pc, runtime·appendslice); + } + + // A very common case is appending bytes. Small appends can avoid the overhead of memmove. + // We can generalize a bit here, and just pick small-sized appends. + p = ret.array+ret.len*w; + q = y.array; + w *= y.len; + if(w <= appendCrossover) { + if(p <= q || w <= p-q) // No overlap. + while(w-- > 0) + *p++ = *q++; + else { + p += w; + q += w; + while(w-- > 0) + *--p = *--q; + } + } else { + runtime·memmove(p, q, w); + } ret.len += y.len; FLUSH(&ret); } // appendstr([]byte, string) []byte +#pragma textflag 7 void runtime·appendstr(SliceType *t, Slice x, String y, Slice ret) { - int32 m; + intgo m; + void *pc; + uintptr w; + uint8 *p, *q; m = x.len+y.len; if(m < x.len) - runtime·throw("append: slice overflow"); + runtime·throw("append: string overflow"); if(m > x.cap) growslice1(t, x, m, &ret); else ret = x; - runtime·memmove(ret.array + ret.len, y.str, y.len); + if(raceenabled) { + // Don't mark read/writes on the newly allocated slice. + pc = runtime·getcallerpc(&t); + // read x[:len] + if(m > x.cap) + runtime·racereadrangepc(x.array, x.len, 1, pc, runtime·appendstr); + // write x[len(x):len(x)+len(y)] + if(m <= x.cap) + runtime·racewriterangepc(ret.array+ret.len, y.len, 1, pc, runtime·appendstr); + } + + // Small appends can avoid the overhead of memmove. + w = y.len; + p = ret.array+ret.len; + q = y.str; + if(w <= appendCrossover) { + while(w-- > 0) + *p++ = *q++; + } else { + runtime·memmove(p, q, w); + } ret.len += y.len; FLUSH(&ret); } @@ -106,15 +179,21 @@ void runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret) { int64 cap; + void *pc; if(n < 1) runtime·panicstring("growslice: invalid n"); cap = old.cap + n; - if((int32)cap != cap || cap > ((uintptr)-1) / t->elem->size) + if((intgo)cap != cap || cap < old.cap || (t->elem->size > 0 && cap > MaxMem/t->elem->size)) runtime·panicstring("growslice: cap out of range"); + if(raceenabled) { + pc = runtime·getcallerpc(&t); + runtime·racereadrangepc(old.array, old.len*t->elem->size, t->elem->size, pc, runtime·growslice); + } + growslice1(t, old, cap, &ret); FLUSH(&ret); @@ -128,12 +207,17 @@ runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret) } static void -growslice1(SliceType *t, Slice x, int32 newcap, Slice *ret) +growslice1(SliceType *t, Slice x, intgo newcap, Slice *ret) { - int32 m; + intgo m; m = x.cap; - if(m == 0) + + // Using newcap directly for m+m < newcap handles + // both the case where m == 0 and also the case where + // m+m/4 wraps around, in which case the loop + // below might never terminate. + if(m+m < newcap) m = newcap; else { do { @@ -147,153 +231,13 @@ growslice1(SliceType *t, Slice x, int32 newcap, Slice *ret) runtime·memmove(ret->array, x.array, ret->len * t->elem->size); } -// sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any); -void -runtime·sliceslice(Slice old, uint64 lb, uint64 hb, uint64 width, Slice ret) -{ - if(hb > old.cap || lb > hb) { - if(debug) { - runtime·prints("runtime.sliceslice: old="); - runtime·printslice(old); - runtime·prints("; lb="); - runtime·printint(lb); - runtime·prints("; hb="); - runtime·printint(hb); - runtime·prints("; width="); - runtime·printint(width); - runtime·prints("\n"); - - runtime·prints("oldarray: nel="); - runtime·printint(old.len); - runtime·prints("; cap="); - runtime·printint(old.cap); - runtime·prints("\n"); - } - runtime·panicslice(); - } - - // new array is inside old array - ret.len = hb - lb; - ret.cap = old.cap - lb; - ret.array = old.array + lb*width; - - FLUSH(&ret); - - if(debug) { - runtime·prints("runtime.sliceslice: old="); - runtime·printslice(old); - runtime·prints("; lb="); - runtime·printint(lb); - runtime·prints("; hb="); - runtime·printint(hb); - runtime·prints("; width="); - runtime·printint(width); - runtime·prints("; ret="); - runtime·printslice(ret); - runtime·prints("\n"); - } -} - -// sliceslice1(old []any, lb uint64, width uint64) (ary []any); -void -runtime·sliceslice1(Slice old, uint64 lb, uint64 width, Slice ret) -{ - if(lb > old.len) { - if(debug) { - runtime·prints("runtime.sliceslice: old="); - runtime·printslice(old); - runtime·prints("; lb="); - runtime·printint(lb); - runtime·prints("; width="); - runtime·printint(width); - runtime·prints("\n"); - - runtime·prints("oldarray: nel="); - runtime·printint(old.len); - runtime·prints("; cap="); - runtime·printint(old.cap); - runtime·prints("\n"); - } - runtime·panicslice(); - } - - // new array is inside old array - ret.len = old.len - lb; - ret.cap = old.cap - lb; - ret.array = old.array + lb*width; - - FLUSH(&ret); - - if(debug) { - runtime·prints("runtime.sliceslice: old="); - runtime·printslice(old); - runtime·prints("; lb="); - runtime·printint(lb); - runtime·prints("; width="); - runtime·printint(width); - runtime·prints("; ret="); - runtime·printslice(ret); - runtime·prints("\n"); - } -} - -// slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary []any); +// copy(to any, fr any, wid uintptr) int +#pragma textflag 7 void -runtime·slicearray(byte* old, uint64 nel, uint64 lb, uint64 hb, uint64 width, Slice ret) +runtime·copy(Slice to, Slice fm, uintptr width, intgo ret) { - if(nel > 0 && old == nil) { - // crash if old == nil. - // could give a better message - // but this is consistent with all the in-line checks - // that the compiler inserts for other uses. - *old = 0; - } - - if(hb > nel || lb > hb) { - if(debug) { - runtime·prints("runtime.slicearray: old="); - runtime·printpointer(old); - runtime·prints("; nel="); - runtime·printint(nel); - runtime·prints("; lb="); - runtime·printint(lb); - runtime·prints("; hb="); - runtime·printint(hb); - runtime·prints("; width="); - runtime·printint(width); - runtime·prints("\n"); - } - runtime·panicslice(); - } - - // new array is inside old array - ret.len = hb-lb; - ret.cap = nel-lb; - ret.array = old + lb*width; - - FLUSH(&ret); - - if(debug) { - runtime·prints("runtime.slicearray: old="); - runtime·printpointer(old); - runtime·prints("; nel="); - runtime·printint(nel); - runtime·prints("; lb="); - runtime·printint(lb); - runtime·prints("; hb="); - runtime·printint(hb); - runtime·prints("; width="); - runtime·printint(width); - runtime·prints("; ret="); - runtime·printslice(ret); - runtime·prints("\n"); - } -} + void *pc; -// copy(to any, fr any, wid uint32) int -void -runtime·copy(Slice to, Slice fm, uintptr width, int32 ret) -{ if(fm.len == 0 || to.len == 0 || width == 0) { ret = 0; goto out; @@ -303,6 +247,12 @@ runtime·copy(Slice to, Slice fm, uintptr width, int32 ret) if(to.len < ret) ret = to.len; + if(raceenabled) { + pc = runtime·getcallerpc(&to); + runtime·racewriterangepc(to.array, ret*width, width, pc, runtime·copy); + runtime·racereadrangepc(fm.array, ret*width, width, pc, runtime·copy); + } + if(ret == 1 && width == 1) { // common case worth about 2x to do here *to.array = *fm.array; // known to be a byte pointer } else { @@ -325,9 +275,12 @@ out: } } +#pragma textflag 7 void -runtime·slicestringcopy(Slice to, String fm, int32 ret) +runtime·slicestringcopy(Slice to, String fm, intgo ret) { + void *pc; + if(fm.len == 0 || to.len == 0) { ret = 0; goto out; @@ -337,6 +290,11 @@ runtime·slicestringcopy(Slice to, String fm, int32 ret) if(to.len < ret) ret = to.len; + if(raceenabled) { + pc = runtime·getcallerpc(&to); + runtime·racewriterangepc(to.array, ret, 1, pc, runtime·slicestringcopy); + } + runtime·memmove(to.array, fm.str, ret); out: diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c index bd73cb15b..9a5440630 100644 --- a/src/pkg/runtime/softfloat_arm.c +++ b/src/pkg/runtime/softfloat_arm.c @@ -420,6 +420,23 @@ stage3: // regd, regm are 4bit variables regd, regm, m->freghi[regd], m->freglo[regd]); break; + case 0xeeb00bc0: // D[regd] = abs D[regm] + m->freglo[regd] = m->freglo[regm]; + m->freghi[regd] = m->freghi[regm] & ((1<<31)-1); + + if(trace) + runtime·printf("*** D[%d] = abs D[%d] %x-%x\n", + regd, regm, m->freghi[regd], m->freglo[regd]); + break; + + case 0xeeb00ac0: // F[regd] = abs F[regm] + m->freglo[regd] = m->freglo[regm] & ((1<<31)-1); + + if(trace) + runtime·printf("*** F[%d] = abs F[%d] %x\n", + regd, regm, m->freglo[regd]); + break; + case 0xeeb40bc0: // D[regd] :: D[regm] (CMPD) runtime·fcmp64c(getd(regd), getd(regm), &cmp, &nan); m->fflag = fstatus(nan, cmp); diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c new file mode 100644 index 000000000..e9a35672f --- /dev/null +++ b/src/pkg/runtime/stack.c @@ -0,0 +1,282 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "arch_GOARCH.h" +#include "malloc.h" +#include "stack.h" + +typedef struct StackCacheNode StackCacheNode; +struct StackCacheNode +{ + StackCacheNode *next; + void* batch[StackCacheBatch-1]; +}; + +static StackCacheNode *stackcache; +static Lock stackcachemu; + +// stackcacherefill/stackcacherelease implement a global cache of stack segments. +// The cache is required to prevent unlimited growth of per-thread caches. +static void +stackcacherefill(void) +{ + StackCacheNode *n; + int32 i, pos; + + runtime·lock(&stackcachemu); + n = stackcache; + if(n) + stackcache = n->next; + runtime·unlock(&stackcachemu); + if(n == nil) { + n = (StackCacheNode*)runtime·SysAlloc(FixedStack*StackCacheBatch); + if(n == nil) + runtime·throw("out of memory (stackcacherefill)"); + runtime·xadd64(&mstats.stacks_sys, FixedStack*StackCacheBatch); + for(i = 0; i < StackCacheBatch-1; i++) + n->batch[i] = (byte*)n + (i+1)*FixedStack; + } + pos = m->stackcachepos; + for(i = 0; i < StackCacheBatch-1; i++) { + m->stackcache[pos] = n->batch[i]; + pos = (pos + 1) % StackCacheSize; + } + m->stackcache[pos] = n; + pos = (pos + 1) % StackCacheSize; + m->stackcachepos = pos; + m->stackcachecnt += StackCacheBatch; +} + +static void +stackcacherelease(void) +{ + StackCacheNode *n; + uint32 i, pos; + + pos = (m->stackcachepos - m->stackcachecnt) % StackCacheSize; + n = (StackCacheNode*)m->stackcache[pos]; + pos = (pos + 1) % StackCacheSize; + for(i = 0; i < StackCacheBatch-1; i++) { + n->batch[i] = m->stackcache[pos]; + pos = (pos + 1) % StackCacheSize; + } + m->stackcachecnt -= StackCacheBatch; + runtime·lock(&stackcachemu); + n->next = stackcache; + stackcache = n; + runtime·unlock(&stackcachemu); +} + +void* +runtime·stackalloc(uint32 n) +{ + uint32 pos; + void *v; + + // Stackalloc must be called on scheduler stack, so that we + // never try to grow the stack during the code that stackalloc runs. + // Doing so would cause a deadlock (issue 1547). + if(g != m->g0) + runtime·throw("stackalloc not on scheduler stack"); + + // Stack allocator uses malloc/free most of the time, + // but if we're in the middle of malloc and need stack, + // we have to do something else to avoid deadlock. + // In that case, we fall back on a fixed-size free-list + // allocator, assuming that inside malloc all the stack + // frames are small, so that all the stack allocations + // will be a single size, the minimum (right now, 5k). + if(n == FixedStack || m->mallocing || m->gcing) { + if(n != FixedStack) { + runtime·printf("stackalloc: in malloc, size=%d want %d\n", FixedStack, n); + runtime·throw("stackalloc"); + } + if(m->stackcachecnt == 0) + stackcacherefill(); + pos = m->stackcachepos; + pos = (pos - 1) % StackCacheSize; + v = m->stackcache[pos]; + m->stackcachepos = pos; + m->stackcachecnt--; + m->stackinuse++; + return v; + } + return runtime·mallocgc(n, FlagNoProfiling|FlagNoGC, 0, 0); +} + +void +runtime·stackfree(void *v, uintptr n) +{ + uint32 pos; + + if(n == FixedStack || m->mallocing || m->gcing) { + if(m->stackcachecnt == StackCacheSize) + stackcacherelease(); + pos = m->stackcachepos; + m->stackcache[pos] = v; + m->stackcachepos = (pos + 1) % StackCacheSize; + m->stackcachecnt++; + m->stackinuse--; + return; + } + runtime·free(v); +} + +// Called from runtime·lessstack when returning from a function which +// allocated a new stack segment. The function's return value is in +// m->cret. +void +runtime·oldstack(void) +{ + Stktop *top; + Gobuf label; + uint32 argsize; + uintptr cret; + byte *sp, *old; + uintptr *src, *dst, *dstend; + G *gp; + int64 goid; + +//printf("oldstack m->cret=%p\n", m->cret); + + gp = m->curg; + top = (Stktop*)gp->stackbase; + old = (byte*)gp->stackguard - StackGuard; + sp = (byte*)top; + argsize = top->argsize; + if(argsize > 0) { + sp -= argsize; + dst = (uintptr*)top->argp; + dstend = dst + argsize/sizeof(*dst); + src = (uintptr*)sp; + while(dst < dstend) + *dst++ = *src++; + } + goid = top->gobuf.g->goid; // fault if g is bad, before gogo + USED(goid); + + label = top->gobuf; + gp->stackbase = (uintptr)top->stackbase; + gp->stackguard = (uintptr)top->stackguard; + if(top->free != 0) + runtime·stackfree(old, top->free); + + cret = m->cret; + m->cret = 0; // drop reference + runtime·gogo(&label, cret); +} + +// Called from reflect·call or from runtime·morestack when a new +// stack segment is needed. Allocate a new stack big enough for +// m->moreframesize bytes, copy m->moreargsize bytes to the new frame, +// and then act as though runtime·lessstack called the function at +// m->morepc. +void +runtime·newstack(void) +{ + int32 framesize, minalloc, argsize; + Stktop *top; + byte *stk, *sp; + uintptr *src, *dst, *dstend; + G *gp; + Gobuf label; + bool reflectcall; + uintptr free; + + framesize = m->moreframesize; + argsize = m->moreargsize; + gp = m->curg; + + if(m->morebuf.sp < gp->stackguard - StackGuard) { + runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, gp->stackguard - StackGuard); + runtime·throw("runtime: split stack overflow"); + } + if(argsize % sizeof(uintptr) != 0) { + runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize); + runtime·throw("runtime: stack split argsize"); + } + + minalloc = 0; + reflectcall = framesize==1; + if(reflectcall) { + framesize = 0; + // moreframesize_minalloc is only set in runtime·gc(), + // that calls newstack via reflect·call(). + minalloc = m->moreframesize_minalloc; + m->moreframesize_minalloc = 0; + if(framesize < minalloc) + framesize = minalloc; + } + + if(reflectcall && minalloc == 0 && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) { + // special case: called from reflect.call (framesize==1) + // to call code with an arbitrary argument size, + // and we have enough space on the current stack. + // the new Stktop* is necessary to unwind, but + // we don't need to create a new segment. + top = (Stktop*)(m->morebuf.sp - sizeof(*top)); + stk = (byte*)gp->stackguard - StackGuard; + free = 0; + } else { + // allocate new segment. + framesize += argsize; + framesize += StackExtra; // room for more functions, Stktop. + if(framesize < StackMin) + framesize = StackMin; + framesize += StackSystem; + stk = runtime·stackalloc(framesize); + top = (Stktop*)(stk+framesize-sizeof(*top)); + free = framesize; + } + + if(0) { + runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n", + framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, gp->stackbase); + } + + top->stackbase = (byte*)gp->stackbase; + top->stackguard = (byte*)gp->stackguard; + top->gobuf = m->morebuf; + top->argp = m->moreargp; + top->argsize = argsize; + top->free = free; + m->moreargp = nil; + m->morebuf.pc = nil; + m->morebuf.sp = (uintptr)nil; + + // copy flag from panic + top->panic = gp->ispanic; + gp->ispanic = false; + + gp->stackbase = (uintptr)top; + gp->stackguard = (uintptr)stk + StackGuard; + + sp = (byte*)top; + if(argsize > 0) { + sp -= argsize; + dst = (uintptr*)sp; + dstend = dst + argsize/sizeof(*dst); + src = (uintptr*)top->argp; + while(dst < dstend) + *dst++ = *src++; + } + if(thechar == '5') { + // caller would have saved its LR below args. + sp -= sizeof(void*); + *(void**)sp = nil; + } + + // Continue as if lessstack had just called m->morepc + // (the PC that decided to grow the stack). + label.sp = (uintptr)sp; + label.pc = (byte*)runtime·lessstack; + label.g = m->curg; + if(reflectcall) + runtime·gogocallfn(&label, (FuncVal*)m->morepc); + else + runtime·gogocall(&label, m->morepc, m->cret); + + *(int32*)345 = 123; // never return +} diff --git a/src/pkg/runtime/stack.h b/src/pkg/runtime/stack.h index d42385d6c..06b0c568c 100644 --- a/src/pkg/runtime/stack.h +++ b/src/pkg/runtime/stack.h @@ -55,13 +55,19 @@ functions to make sure that this limit cannot be violated. enum { // StackSystem is a number of additional bytes to add // to each stack below the usual guard area for OS-specific - // purposes like signal handling. Used on Windows because - // it does not use a separate stack. + // purposes like signal handling. Used on Windows and on + // Plan 9 because they do not use a separate stack. #ifdef GOOS_windows StackSystem = 512 * sizeof(uintptr), +#else +#ifdef GOOS_plan9 + // The size of the note handler frame varies among architectures, + // but 512 bytes should be enough for every implementation. + StackSystem = 512, #else StackSystem = 0, -#endif +#endif // Plan 9 +#endif // Windows // The amount of extra stack to allocate beyond the size // needed for the single frame that triggered the split. diff --git a/src/pkg/runtime/stack_test.go b/src/pkg/runtime/stack_test.go index 01c6b6e2f..759f7c46e 100644 --- a/src/pkg/runtime/stack_test.go +++ b/src/pkg/runtime/stack_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Test stack split logic by calling functions of every frame size +// Test stack split logic by calling functions of every frame size // from near 0 up to and beyond the default segment size (4k). // Each of those functions reports its SP + stack limit, and then // the test (the caller) checks that those make sense. By not @@ -34,6 +34,7 @@ package runtime_test import ( . "runtime" "testing" + "time" "unsafe" ) @@ -1526,3 +1527,51 @@ func stack4988() (uintptr, uintptr) { var buf [4988]byte; use(buf[:]); return St func stack4992() (uintptr, uintptr) { var buf [4992]byte; use(buf[:]); return Stackguard() } func stack4996() (uintptr, uintptr) { var buf [4996]byte; use(buf[:]); return Stackguard() } func stack5000() (uintptr, uintptr) { var buf [5000]byte; use(buf[:]); return Stackguard() } + +// TestStackMem measures per-thread stack segment cache behavior. +// The test consumed up to 500MB in the past. +func TestStackMem(t *testing.T) { + const ( + BatchSize = 32 + BatchCount = 512 + ArraySize = 1024 + RecursionDepth = 128 + ) + if testing.Short() { + return + } + defer GOMAXPROCS(GOMAXPROCS(BatchSize)) + s0 := new(MemStats) + ReadMemStats(s0) + for b := 0; b < BatchCount; b++ { + c := make(chan bool, BatchSize) + for i := 0; i < BatchSize; i++ { + go func() { + var f func(k int, a [ArraySize]byte) + f = func(k int, a [ArraySize]byte) { + if k == 0 { + time.Sleep(time.Millisecond) + return + } + f(k-1, a) + } + f(RecursionDepth, [ArraySize]byte{}) + c <- true + }() + } + for i := 0; i < BatchSize; i++ { + <-c + } + } + s1 := new(MemStats) + ReadMemStats(s1) + consumed := s1.StackSys - s0.StackSys + t.Logf("Consumed %vMB for stack mem", consumed>>20) + estimate := uint64(8 * BatchSize * ArraySize * RecursionDepth) // 8 is to reduce flakiness. + if consumed > estimate { + t.Fatalf("Stack mem: want %v, got %v", estimate, consumed) + } + if s1.StackInuse > 4<<20 { + t.Fatalf("Stack inuse: want %v, got %v", 4<<20, s1.StackInuse) + } +} diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc index 7e1f8a1e8..c0d3f2bde 100644 --- a/src/pkg/runtime/string.goc +++ b/src/pkg/runtime/string.goc @@ -6,6 +6,7 @@ package runtime #include "runtime.h" #include "arch_GOARCH.h" #include "malloc.h" +#include "race.h" String runtime·emptystring; @@ -33,10 +34,10 @@ runtime·findnullw(uint16 *s) return l; } -uint32 runtime·maxstring = 256; +uint32 runtime·maxstring = 256; // a hint for print static String -gostringsize(int32 l) +gostringsize(intgo l) { String s; uint32 ms; @@ -58,7 +59,7 @@ gostringsize(int32 l) String runtime·gostring(byte *str) { - int32 l; + intgo l; String s; l = runtime·findnull(str); @@ -68,7 +69,7 @@ runtime·gostring(byte *str) } String -runtime·gostringn(byte *str, int32 l) +runtime·gostringn(byte *str, intgo l) { String s; @@ -78,7 +79,7 @@ runtime·gostringn(byte *str, int32 l) } Slice -runtime·gobytes(byte *p, int32 n) +runtime·gobytes(byte *p, intgo n) { Slice sl; @@ -102,7 +103,7 @@ runtime·gostringnocopy(byte *str) String runtime·gostringw(uint16 *str) { - int32 n1, n2, i; + intgo n1, n2, i; byte buf[8]; String s; @@ -139,17 +140,26 @@ runtime·catstring(String s1, String s2) } static String -concatstring(int32 n, String *s) +concatstring(intgo n, String *s) { - int32 i, l; + intgo i, l, count; String out; l = 0; + count = 0; for(i=0; i 0) { + count++; + out = s[i]; + } } + if(count == 0) + return runtime·emptystring; + if(count == 1) // zero or one non-empty string in concatenation + return out; out = gostringsize(l); l = 0; @@ -163,14 +173,14 @@ concatstring(int32 n, String *s) #pragma textflag 7 // s1 is the first of n strings. // the output string follows. -func concatstring(n int32, s1 String) { +func concatstring(n int, s1 String) { (&s1)[n] = concatstring(n, &s1); } static int32 cmpstring(String s1, String s2) { - uint32 i, l; + uintgo i, l; byte c1, c2; l = s1.len; @@ -191,14 +201,34 @@ cmpstring(String s1, String s2) return 0; } -func cmpstring(s1 String, s2 String) (v int32) { +func cmpstring(s1 String, s2 String) (v int) { v = cmpstring(s1, s2); } +func eqstring(s1 String, s2 String) (v bool) { + uintgo i, l; + + if(s1.len != s2.len) { + v = false; + return; + } + if(s1.str == s2.str) { + v = true; + return; + } + l = s1.len; + for(i=0; i si.len || - hindex < lindex || hindex > si.len) { - runtime·panicslice(); - } - - l = hindex-lindex; - so.str = si.str + lindex; - so.len = l; -} - -func slicestring1(si String, lindex int32) (so String) { - int32 l; - - if(lindex < 0 || lindex > si.len) { - runtime·panicslice(); - } - - l = si.len-lindex; - so.str = si.str + lindex; - so.len = l; -} - func intstring(v int64) (s String) { s = gostringsize(8); s.len = runtime·runetochar(s.str, v); @@ -267,6 +272,12 @@ func intstring(v int64) (s String) { } func slicebytetostring(b Slice) (s String) { + void *pc; + + if(raceenabled) { + pc = runtime·getcallerpc(&b); + runtime·racereadrangepc(b.array, b.len, 1, pc, runtime·slicebytetostring); + } s = gostringsize(b.len); runtime·memmove(s.str, b.array, s.len); } @@ -279,10 +290,15 @@ func stringtoslicebyte(s String) (b Slice) { } func slicerunetostring(b Slice) (s String) { - int32 siz1, siz2, i; + intgo siz1, siz2, i; int32 *a; byte dum[8]; + void *pc; + if(raceenabled) { + pc = runtime·getcallerpc(&b); + runtime·racereadrangepc(b.array, b.len*sizeof(*a), sizeof(*a), pc, runtime·slicerunetostring); + } a = (int32*)b.array; siz1 = 0; for(i=0; i= s.len) { @@ -351,7 +367,7 @@ func stringiter(s String, k int32) (retk int32) { out: } -func stringiter2(s String, k int32) (retk int32, retv int32) { +func stringiter2(s String, k int) (retk int, retv int32) { if(k >= s.len) { // retk=0 is end of iteration retk = 0; diff --git a/src/pkg/runtime/string_test.go b/src/pkg/runtime/string_test.go new file mode 100644 index 000000000..8f13f0f42 --- /dev/null +++ b/src/pkg/runtime/string_test.go @@ -0,0 +1,45 @@ +package runtime_test + +import ( + "testing" +) + +func BenchmarkCompareStringEqual(b *testing.B) { + bytes := []byte("Hello Gophers!") + s1, s2 := string(bytes), string(bytes) + for i := 0; i < b.N; i++ { + if s1 != s2 { + b.Fatal("s1 != s2") + } + } +} + +func BenchmarkCompareStringIdentical(b *testing.B) { + s1 := "Hello Gophers!" + s2 := s1 + for i := 0; i < b.N; i++ { + if s1 != s2 { + b.Fatal("s1 != s2") + } + } +} + +func BenchmarkCompareStringSameLength(b *testing.B) { + s1 := "Hello Gophers!" + s2 := "Hello, Gophers" + for i := 0; i < b.N; i++ { + if s1 == s2 { + b.Fatal("s1 == s2") + } + } +} + +func BenchmarkCompareStringDifferentLength(b *testing.B) { + s1 := "Hello Gophers!" + s2 := "Hello, Gophers!" + for i := 0; i < b.N; i++ { + if s1 == s2 { + b.Fatal("s1 == s2") + } + } +} diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c index f29276bd7..d7221c476 100644 --- a/src/pkg/runtime/symtab.c +++ b/src/pkg/runtime/symtab.c @@ -2,20 +2,59 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Runtime symbol table access. Work in progress. -// The Plan 9 symbol table is not in a particularly convenient form. -// The routines here massage it into a more usable form; eventually -// we'll change 6l to do this for us, but it is easier to experiment -// here than to change 6l and all the other tools. +// Runtime symbol table parsing. // -// The symbol table also needs to be better integrated with the type -// strings table in the future. This is just a quick way to get started -// and figure out exactly what we want. +// The Go tools use a symbol table derived from the Plan 9 symbol table +// format. The symbol table is kept in its own section treated as +// read-only memory when the binary is running: the binary consults the +// table. +// +// The format used by Go 1.0 was basically the Plan 9 format. Each entry +// is variable sized but had this format: +// +// 4-byte value, big endian +// 1-byte type ([A-Za-z] + 0x80) +// name, NUL terminated (or for 'z' and 'Z' entries, double-NUL terminated) +// 4-byte Go type address, big endian (new in Go) +// +// In order to support greater interoperation with standard toolchains, +// Go 1.1 uses a more flexible yet smaller encoding of the entries. +// The overall structure is unchanged from Go 1.0 and, for that matter, +// from Plan 9. +// +// The Go 1.1 table is a re-encoding of the data in a Go 1.0 table. +// To identify a new table as new, it begins one of two eight-byte +// sequences: +// +// FF FF FF FD 00 00 00 xx - big endian new table +// FD FF FF FF 00 00 00 xx - little endian new table +// +// This sequence was chosen because old tables stop at an entry with type +// 0, so old code reading a new table will see only an empty table. The +// first four bytes are the target-endian encoding of 0xfffffffd. The +// final xx gives AddrSize, the width of a full-width address. +// +// After that header, each entry is encoded as follows. +// +// 1-byte type (0-51 + two flag bits) +// AddrSize-byte value, host-endian OR varint-encoded value +// AddrSize-byte Go type address OR nothing +// [n] name, terminated as before +// +// The type byte comes first, but 'A' encodes as 0 and 'a' as 26, so that +// the type itself is only in the low 6 bits. The upper two bits specify +// the format of the next two fields. If the 0x40 bit is set, the value +// is encoded as an full-width 4- or 8-byte target-endian word. Otherwise +// the value is a varint-encoded number. If the 0x80 bit is set, the Go +// type is present, again as a 4- or 8-byte target-endian word. If not, +// there is no Go type in this entry. The NUL-terminated name ends the +// entry. #include "runtime.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" #include "arch_GOARCH.h" +#include "malloc.h" extern byte pclntab[], epclntab[], symtab[], esymtab[]; @@ -28,24 +67,100 @@ struct Sym // byte *gotype; }; +static uintptr mainoffset; + +// A dynamically allocated string containing multiple substrings. +// Individual strings are slices of hugestring. +static String hugestring; +static int32 hugestring_len; + +extern void main·main(void); + +static uintptr +readword(byte **pp, byte *ep) +{ + byte *p; + + p = *pp; + if(ep - p < sizeof(void*)) { + *pp = ep; + return 0; + } + *pp = p + sizeof(void*); + + // Hairy, but only one of these four cases gets compiled. + if(sizeof(void*) == 8) { + if(BigEndian) { + return ((uint64)p[0]<<56) | ((uint64)p[1]<<48) | ((uint64)p[2]<<40) | ((uint64)p[3]<<32) | + ((uint64)p[4]<<24) | ((uint64)p[5]<<16) | ((uint64)p[6]<<8) | ((uint64)p[7]); + } + return ((uint64)p[7]<<56) | ((uint64)p[6]<<48) | ((uint64)p[5]<<40) | ((uint64)p[4]<<32) | + ((uint64)p[3]<<24) | ((uint64)p[2]<<16) | ((uint64)p[1]<<8) | ((uint64)p[0]); + } + if(BigEndian) { + return ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]); + } + return ((uint32)p[3]<<24) | ((uint32)p[2]<<16) | ((uint32)p[1]<<8) | ((uint32)p[0]); +} + // Walk over symtab, calling fn(&s) for each symbol. static void walksymtab(void (*fn)(Sym*)) { byte *p, *ep, *q; Sym s; + int32 widevalue, havetype, shift; p = symtab; ep = esymtab; + + // Table must begin with correct magic number. + if(ep - p < 8 || p[4] != 0x00 || p[5] != 0x00 || p[6] != 0x00 || p[7] != sizeof(void*)) + return; + if(BigEndian) { + if(p[0] != 0xff || p[1] != 0xff || p[2] != 0xff || p[3] != 0xfd) + return; + } else { + if(p[0] != 0xfd || p[1] != 0xff || p[2] != 0xff || p[3] != 0xff) + return; + } + p += 8; + while(p < ep) { - if(p + 7 > ep) - break; - s.value = ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]); + s.symtype = p[0]&0x3F; + widevalue = p[0]&0x40; + havetype = p[0]&0x80; + if(s.symtype < 26) + s.symtype += 'A'; + else + s.symtype += 'a' - 26; + p++; + + // Value, either full-width or varint-encoded. + if(widevalue) { + s.value = readword(&p, ep); + } else { + s.value = 0; + shift = 0; + while(p < ep && (p[0]&0x80) != 0) { + s.value |= (uintptr)(p[0]&0x7F)<= ep) + break; + s.value |= (uintptr)p[0]<symtype) { case 't': case 'T': @@ -93,6 +209,11 @@ dofunc(Sym *sym) case 'L': if(runtime·strcmp(sym->name, (byte*)"etext") == 0) break; + if(sym->value < lastvalue) { + runtime·printf("symbols out of order: %p before %p\n", lastvalue, sym->value); + runtime·throw("malformed symbol table"); + } + lastvalue = sym->value; if(func == nil) { nfunc++; break; @@ -104,24 +225,24 @@ dofunc(Sym *sym) f->frame = -sizeof(uintptr); break; case 'm': - if(nfunc > 0 && func != nil) - func[nfunc-1].frame += sym->value; - break; - case 'p': - if(nfunc > 0 && func != nil) { - f = &func[nfunc-1]; - // args counts 32-bit words. - // sym->value is the arg's offset. - // don't know width of this arg, so assume it is 64 bits. - if(f->args < sym->value/4 + 2) - f->args = sym->value/4 + 2; + if(nfunc <= 0 || func == nil) + break; + if(runtime·strcmp(sym->name, (byte*)".frame") == 0) + func[nfunc-1].frame = sym->value; + else if(runtime·strcmp(sym->name, (byte*)".locals") == 0) + func[nfunc-1].locals = sym->value; + else if(runtime·strcmp(sym->name, (byte*)".args") == 0) + func[nfunc-1].args = sym->value; + else { + runtime·printf("invalid 'm' symbol named '%s'\n", sym->name); + runtime·throw("mangled symbol table"); } break; case 'f': if(fname == nil) { if(sym->value >= nfname) { if(sym->value >= 0x10000) { - runtime·printf("invalid symbol file index %p\n", sym->value); + runtime·printf("runtime: invalid symbol file index %p\n", sym->value); runtime·throw("mangled symbol table"); } nfname = sym->value+1; @@ -135,14 +256,15 @@ dofunc(Sym *sym) // put together the path name for a z entry. // the f entries have been accumulated into fname already. -static void +// returns the length of the path name. +static int32 makepath(byte *buf, int32 nbuf, byte *path) { int32 n, len; byte *p, *ep, *q; if(nbuf <= 0) - return; + return 0; p = buf; ep = buf + nbuf; @@ -163,6 +285,26 @@ makepath(byte *buf, int32 nbuf, byte *path) runtime·memmove(p, q, len+1); p += len; } + return p - buf; +} + +// appends p to hugestring +static String +gostringn(byte *p, int32 l) +{ + String s; + + if(l == 0) + return runtime·emptystring; + if(hugestring.str == nil) { + hugestring_len += l; + return runtime·emptystring; + } + s.str = hugestring.str + hugestring.len; + s.len = l; + hugestring.len += s.len; + runtime·memmove(s.str, p, l); + return s; } // walk symtab accumulating path names for use by pc/ln table. @@ -181,11 +323,13 @@ dosrcline(Sym *sym) static int32 incstart; static int32 nfunc, nfile, nhist; Func *f; - int32 i; + int32 i, l; switch(sym->symtype) { case 't': case 'T': + if(hugestring.str == nil) + break; if(runtime·strcmp(sym->name, (byte*)"etext") == 0) break; f = &func[nfunc++]; @@ -200,23 +344,23 @@ dosrcline(Sym *sym) case 'z': if(sym->value == 1) { // entry for main source file for a new object. - makepath(srcbuf, sizeof srcbuf, sym->name+1); + l = makepath(srcbuf, sizeof srcbuf, sym->name+1); nhist = 0; nfile = 0; if(nfile == nelem(files)) return; - files[nfile].srcstring = runtime·gostring(srcbuf); + files[nfile].srcstring = gostringn(srcbuf, l); files[nfile].aline = 0; files[nfile++].delta = 0; } else { // push or pop of included file. - makepath(srcbuf, sizeof srcbuf, sym->name+1); + l = makepath(srcbuf, sizeof srcbuf, sym->name+1); if(srcbuf[0] != '\0') { if(nhist++ == 0) incstart = sym->value; if(nhist == 0 && nfile < nelem(files)) { // new top-level file - files[nfile].srcstring = runtime·gostring(srcbuf); + files[nfile].srcstring = gostringn(srcbuf, l); files[nfile].aline = sym->value; // this is "line 0" files[nfile++].delta = sym->value - 1; @@ -275,7 +419,7 @@ splitpcln(void) line += *p++; else line -= *p++ - 64; - + // pc, line now match. // Because the state machine begins at pc==entry and line==0, // it can happen - just at the beginning! - that the update may @@ -297,7 +441,7 @@ splitpcln(void) while(f < ef && pc >= (f+1)->entry); f->pcln.array = p; // pc0 and ln0 are the starting values for - // the loop over f->pcln, so pc must be + // the loop over f->pcln, so pc must be // adjusted by the same pcquant update // that we're going to do as we continue our loop. f->pc0 = pc + pcquant; @@ -323,11 +467,11 @@ runtime·funcline(Func *f, uintptr targetpc) uintptr pc; int32 line; int32 pcquant; - + enum { debug = 0 }; - + switch(thechar) { case '5': pcquant = 4; @@ -354,7 +498,7 @@ runtime·funcline(Func *f, uintptr targetpc) if(debug && !runtime·panicking) runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line); - + // If the pc has advanced too far or we're out of data, // stop and the last known line number. if(pc > targetpc || p >= ep) @@ -382,7 +526,7 @@ runtime·funcline(Func *f, uintptr targetpc) } void -runtime·funcline_go(Func *f, uintptr targetpc, String retfile, int32 retline) +runtime·funcline_go(Func *f, uintptr targetpc, String retfile, intgo retline) { retfile = f->src; retline = runtime·funcline(f, targetpc); @@ -406,20 +550,30 @@ buildfuncs(void) // count funcs, fnames nfunc = 0; nfname = 0; + lastvalue = 0; walksymtab(dofunc); - // initialize tables - func = runtime·mal((nfunc+1)*sizeof func[0]); + // Initialize tables. + // Can use FlagNoPointers - all pointers either point into sections of the executable + // or point into hugestring. + func = runtime·mallocgc((nfunc+1)*sizeof func[0], FlagNoPointers, 0, 1); func[nfunc].entry = (uint64)etext; - fname = runtime·mal(nfname*sizeof fname[0]); + fname = runtime·mallocgc(nfname*sizeof fname[0], FlagNoPointers, 0, 1); nfunc = 0; + lastvalue = 0; walksymtab(dofunc); // split pc/ln table by func splitpcln(); // record src file and line info for each func - walksymtab(dosrcline); + walksymtab(dosrcline); // pass 1: determine hugestring_len + hugestring.str = runtime·mallocgc(hugestring_len, FlagNoPointers, 0, 0); + hugestring.len = 0; + walksymtab(dosrcline); // pass 2: fill and use hugestring + + if(hugestring.len != hugestring_len) + runtime·throw("buildfunc: problem in initialization procedure"); m->nomemprof--; } @@ -482,7 +636,7 @@ static bool hasprefix(String s, int8 *p) { int32 i; - + for(i=0; ithrowing > 0) + return 1; if(traceback < 0) traceback = runtime·gotraceback(); - return traceback > 1 || contains(f->name, ".") && !hasprefix(f->name, "runtime."); + return traceback > 1 || f != nil && contains(f->name, ".") && !hasprefix(f->name, "runtime."); } diff --git a/src/pkg/runtime/sys_darwin_386.s b/src/pkg/runtime/sys_darwin_386.s index 3cf3506ad..8a938f9f4 100644 --- a/src/pkg/runtime/sys_darwin_386.s +++ b/src/pkg/runtime/sys_darwin_386.s @@ -47,8 +47,7 @@ TEXT runtime·mmap(SB),7,$0 TEXT runtime·madvise(SB),7,$0 MOVL $75, AX INT $0x80 - JAE 2(PC) - MOVL $0xf1, 0xf1 // crash + // ignore failure - maybe pages are locked RET TEXT runtime·munmap(SB),7,$0 @@ -63,40 +62,133 @@ TEXT runtime·setitimer(SB),7,$0 INT $0x80 RET -// func now() (sec int64, nsec int32) -TEXT time·now(SB), 7, $32 - LEAL 12(SP), AX // must be non-nil, unused - MOVL AX, 4(SP) - MOVL $0, 8(SP) // time zone pointer - MOVL $116, AX - INT $0x80 - MOVL DX, BX +// OS X comm page time offsets +// http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/cpu_capabilities.h +#define cpu_capabilities 0x20 +#define nt_tsc_base 0x50 +#define nt_scale 0x58 +#define nt_shift 0x5c +#define nt_ns_base 0x60 +#define nt_generation 0x68 +#define gtod_generation 0x6c +#define gtod_ns_base 0x70 +#define gtod_sec_base 0x78 + +// called from assembly +// 64-bit unix nanoseconds returned in DX:AX. +// I'd much rather write this in C but we need +// assembly for the 96-bit multiply and RDTSC. +TEXT runtime·now(SB),7,$40 + MOVL $0xffff0000, BP /* comm page base */ + + // Test for slow CPU. If so, the math is completely + // different, and unimplemented here, so use the + // system call. + MOVL cpu_capabilities(BP), AX + TESTL $0x4000, AX + JNZ systime + + // Loop trying to take a consistent snapshot + // of the time parameters. +timeloop: + MOVL gtod_generation(BP), BX + TESTL BX, BX + JZ systime + MOVL nt_generation(BP), CX + TESTL CX, CX + JZ timeloop + RDTSC + MOVL nt_tsc_base(BP), SI + MOVL (nt_tsc_base+4)(BP), DI + MOVL SI, 0(SP) + MOVL DI, 4(SP) + MOVL nt_scale(BP), SI + MOVL SI, 8(SP) + MOVL nt_ns_base(BP), SI + MOVL (nt_ns_base+4)(BP), DI + MOVL SI, 12(SP) + MOVL DI, 16(SP) + CMPL nt_generation(BP), CX + JNE timeloop + MOVL gtod_ns_base(BP), SI + MOVL (gtod_ns_base+4)(BP), DI + MOVL SI, 20(SP) + MOVL DI, 24(SP) + MOVL gtod_sec_base(BP), SI + MOVL (gtod_sec_base+4)(BP), DI + MOVL SI, 28(SP) + MOVL DI, 32(SP) + CMPL gtod_generation(BP), BX + JNE timeloop + + // Gathered all the data we need. Compute time. + // ((tsc - nt_tsc_base) * nt_scale) >> 32 + nt_ns_base - gtod_ns_base + gtod_sec_base*1e9 + // The multiply and shift extracts the top 64 bits of the 96-bit product. + SUBL 0(SP), AX // DX:AX = (tsc - nt_tsc_base) + SBBL 4(SP), DX + + // We have x = tsc - nt_tsc_base - DX:AX to be + // multiplied by y = nt_scale = 8(SP), keeping the top 64 bits of the 96-bit product. + // x*y = (x&0xffffffff)*y + (x&0xffffffff00000000)*y + // (x*y)>>32 = ((x&0xffffffff)*y)>>32 + (x>>32)*y + MOVL DX, CX // SI = (x&0xffffffff)*y >> 32 + MOVL $0, DX + MULL 8(SP) + MOVL DX, SI - // sec is in AX, usec in BX - MOVL AX, sec+0(FP) - MOVL $0, sec+4(FP) - IMULL $1000, BX - MOVL BX, nsec+8(FP) + MOVL CX, AX // DX:AX = (x>>32)*y + MOVL $0, DX + MULL 8(SP) + + ADDL SI, AX // DX:AX += (x&0xffffffff)*y >> 32 + ADCL $0, DX + + // DX:AX is now ((tsc - nt_tsc_base) * nt_scale) >> 32. + ADDL 12(SP), AX // DX:AX += nt_ns_base + ADCL 16(SP), DX + SUBL 20(SP), AX // DX:AX -= gtod_ns_base + SBBL 24(SP), DX + MOVL AX, SI // DI:SI = DX:AX + MOVL DX, DI + MOVL 28(SP), AX // DX:AX = gtod_sec_base*1e9 + MOVL 32(SP), DX + MOVL $1000000000, CX + MULL CX + ADDL SI, AX // DX:AX += DI:SI + ADCL DI, DX RET -// int64 nanotime(void) so really -// void nanotime(int64 *nsec) -TEXT runtime·nanotime(SB), 7, $32 +systime: + // Fall back to system call (usually first call in this thread) LEAL 12(SP), AX // must be non-nil, unused MOVL AX, 4(SP) MOVL $0, 8(SP) // time zone pointer MOVL $116, AX INT $0x80 - MOVL DX, BX - - // sec is in AX, usec in BX + // sec is in AX, usec in DX // convert to DX:AX nsec + MOVL DX, BX MOVL $1000000000, CX MULL CX IMULL $1000, BX ADDL BX, AX ADCL $0, DX + RET + +// func now() (sec int64, nsec int32) +TEXT time·now(SB),7,$0 + CALL runtime·now(SB) + MOVL $1000000000, CX + DIVL CX + MOVL AX, sec+0(FP) + MOVL $0, sec+4(FP) + MOVL DX, nsec+8(FP) + RET +// int64 nanotime(void) so really +// void nanotime(int64 *nsec) +TEXT runtime·nanotime(SB),7,$0 + CALL runtime·now(SB) MOVL ret+0(FP), DI MOVL AX, 0(DI) MOVL DX, 4(DI) @@ -120,8 +212,8 @@ TEXT runtime·sigaction(SB),7,$0 // It is called with the following arguments on the stack: // 0(FP) "return address" - ignored // 4(FP) actual handler -// 8(FP) siginfo style - ignored -// 12(FP) signal number +// 8(FP) signal number +// 12(FP) siginfo style // 16(FP) siginfo // 20(FP) context TEXT runtime·sigtramp(SB),7,$40 @@ -130,8 +222,11 @@ TEXT runtime·sigtramp(SB),7,$40 // check that m exists MOVL m(CX), BP CMPL BP, $0 - JNE 2(PC) + JNE 5(PC) + MOVL sig+8(FP), BX + MOVL BX, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVL g(CX), DI @@ -196,7 +291,7 @@ TEXT runtime·usleep(SB),7,$32 INT $0x80 RET -// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void)) +// void bsdthread_create(void *stk, M *mp, G *gp, void (*fn)(void)) // System call args are: func arg stack pthread flags. TEXT runtime·bsdthread_create(SB),7,$32 MOVL $360, AX @@ -268,8 +363,10 @@ TEXT runtime·bsdthread_register(SB),7,$40 MOVL $0, 20(SP) // targetconc_ptr MOVL $0, 24(SP) // dispatchqueue_offset INT $0x80 - JAE 2(PC) - MOVL $0xf1, 0xf1 // crash + JAE 3(PC) + NEGL AX + RET + MOVL $0, AX RET // Invoke Mach system call. diff --git a/src/pkg/runtime/sys_darwin_amd64.s b/src/pkg/runtime/sys_darwin_amd64.s index 90571baae..4e43a76c3 100644 --- a/src/pkg/runtime/sys_darwin_amd64.s +++ b/src/pkg/runtime/sys_darwin_amd64.s @@ -61,30 +61,63 @@ TEXT runtime·madvise(SB), 7, $0 MOVL 24(SP), DX // arg 3 advice MOVL $(0x2000000+75), AX // syscall entry madvise SYSCALL - JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + // ignore failure - maybe pages are locked RET -// func now() (sec int64, nsec int32) -TEXT time·now(SB), 7, $32 - MOVQ SP, DI // must be non-nil, unused - MOVQ $0, SI - MOVL $(0x2000000+116), AX - SYSCALL - - // sec is in AX, usec in DX - MOVQ AX, sec+0(FP) - IMULQ $1000, DX - MOVL DX, nsec+8(FP) - RET +// OS X comm page time offsets +// http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/cpu_capabilities.h +#define nt_tsc_base 0x50 +#define nt_scale 0x58 +#define nt_shift 0x5c +#define nt_ns_base 0x60 +#define nt_generation 0x68 +#define gtod_generation 0x6c +#define gtod_ns_base 0x70 +#define gtod_sec_base 0x78 // int64 nanotime(void) TEXT runtime·nanotime(SB), 7, $32 + MOVQ $0x7fffffe00000, BP /* comm page base */ + // Loop trying to take a consistent snapshot + // of the time parameters. +timeloop: + MOVL gtod_generation(BP), R8 + TESTL R8, R8 + JZ systime + MOVL nt_generation(BP), R9 + TESTL R9, R9 + JZ timeloop + RDTSC + MOVQ nt_tsc_base(BP), R10 + MOVL nt_scale(BP), R11 + MOVQ nt_ns_base(BP), R12 + CMPL nt_generation(BP), R9 + JNE timeloop + MOVQ gtod_ns_base(BP), R13 + MOVQ gtod_sec_base(BP), R14 + CMPL gtod_generation(BP), R8 + JNE timeloop + + // Gathered all the data we need. Compute time. + // ((tsc - nt_tsc_base) * nt_scale) >> 32 + nt_ns_base - gtod_ns_base + gtod_sec_base*1e9 + // The multiply and shift extracts the top 64 bits of the 96-bit product. + SHLQ $32, DX + ADDQ DX, AX + SUBQ R10, AX + MULQ R11 + SHRQ $32, AX:DX + ADDQ R12, AX + SUBQ R13, AX + IMULQ $1000000000, R14 + ADDQ R14, AX + RET + +systime: + // Fall back to system call (usually first call in this thread). MOVQ SP, DI // must be non-nil, unused MOVQ $0, SI MOVL $(0x2000000+116), AX SYSCALL - // sec is in AX, usec in DX // return nsec in AX IMULQ $1000000000, AX @@ -92,6 +125,25 @@ TEXT runtime·nanotime(SB), 7, $32 ADDQ DX, AX RET +// func now() (sec int64, nsec int32) +TEXT time·now(SB),7,$0 + CALL runtime·nanotime(SB) + + // generated code for + // func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 } + // adapted to reduce duplication + MOVQ AX, CX + MOVQ $1360296554856532783, AX + MULQ CX + ADDQ CX, DX + RCRQ $1, DX + SHRQ $29, DX + MOVQ DX, sec+0(FP) + IMULQ $1000000000, DX + SUBQ DX, CX + MOVL CX, nsec+8(FP) + RET + TEXT runtime·sigprocmask(SB),7,$0 MOVL 8(SP), DI MOVQ 16(SP), SI @@ -120,8 +172,10 @@ TEXT runtime·sigtramp(SB),7,$64 // check that m exists MOVQ m(BX), BP CMPQ BP, $0 - JNE 2(PC) + JNE 4(PC) + MOVL DX, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVQ g(BX), R10 @@ -199,7 +253,7 @@ TEXT runtime·usleep(SB),7,$16 SYSCALL RET -// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void)) +// void bsdthread_create(void *stk, M *mp, G *gp, void (*fn)(void)) TEXT runtime·bsdthread_create(SB),7,$0 // Set up arguments to bsdthread_create system call. // The ones in quotes pass through to the thread callback @@ -265,8 +319,10 @@ TEXT runtime·bsdthread_register(SB),7,$0 MOVQ $0, R9 // dispatchqueue_offset MOVQ $(0x2000000+366), AX // bsdthread_register SYSCALL - JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + JCC 3(PC) + NEGL AX + RET + MOVL $0, AX RET // Mach system calls use 0x1000000 instead of the BSD's 0x2000000. diff --git a/src/pkg/runtime/sys_freebsd_386.s b/src/pkg/runtime/sys_freebsd_386.s index a72d8972b..d5370267a 100644 --- a/src/pkg/runtime/sys_freebsd_386.s +++ b/src/pkg/runtime/sys_freebsd_386.s @@ -39,6 +39,7 @@ TEXT runtime·thr_start(SB),7,$0 MOVL AX, m(CX) CALL runtime·stackcheck(SB) // smashes AX CALL runtime·mstart(SB) + MOVL 0, AX // crash (not reached) // Exit the entire program (like C exit) @@ -89,7 +90,7 @@ TEXT runtime·mmap(SB),7,$32 MOVSL MOVSL MOVSL - MOVL $0, AX // top 64 bits of file offset + MOVL $0, AX // top 32 bits of file offset STOSL MOVL $477, AX INT $0x80 @@ -102,6 +103,12 @@ TEXT runtime·munmap(SB),7,$-4 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·madvise(SB),7,$-4 + MOVL $75, AX // madvise + INT $0x80 + // ignore failure - maybe pages are locked + RET + TEXT runtime·setitimer(SB), 7, $-4 MOVL $83, AX INT $0x80 @@ -109,40 +116,38 @@ TEXT runtime·setitimer(SB), 7, $-4 // func now() (sec int64, nsec int32) TEXT time·now(SB), 7, $32 - MOVL $116, AX + MOVL $232, AX LEAL 12(SP), BX - MOVL BX, 4(SP) - MOVL $0, 8(SP) + MOVL $0, 4(SP) + MOVL BX, 8(SP) INT $0x80 MOVL 12(SP), AX // sec - MOVL 16(SP), BX // usec + MOVL 16(SP), BX // nsec - // sec is in AX, usec in BX + // sec is in AX, nsec in BX MOVL AX, sec+0(FP) MOVL $0, sec+4(FP) - IMULL $1000, BX MOVL BX, nsec+8(FP) RET // int64 nanotime(void) so really // void nanotime(int64 *nsec) TEXT runtime·nanotime(SB), 7, $32 - MOVL $116, AX + MOVL $232, AX LEAL 12(SP), BX - MOVL BX, 4(SP) - MOVL $0, 8(SP) + MOVL $0, 4(SP) + MOVL BX, 8(SP) INT $0x80 MOVL 12(SP), AX // sec - MOVL 16(SP), BX // usec + MOVL 16(SP), BX // nsec - // sec is in AX, usec in BX + // sec is in AX, nsec in BX // convert to DX:AX nsec MOVL $1000000000, CX MULL CX - IMULL $1000, BX ADDL BX, AX ADCL $0, DX - + MOVL ret+0(FP), DI MOVL AX, 0(DI) MOVL DX, 4(DI) @@ -162,8 +167,11 @@ TEXT runtime·sigtramp(SB),7,$44 // check that m exists MOVL m(CX), BX CMPL BX, $0 - JNE 2(PC) + JNE 5(PC) + MOVL signo+0(FP), BX + MOVL BX, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVL g(CX), DI diff --git a/src/pkg/runtime/sys_freebsd_amd64.s b/src/pkg/runtime/sys_freebsd_amd64.s index 36e034a80..40c6237e2 100644 --- a/src/pkg/runtime/sys_freebsd_amd64.s +++ b/src/pkg/runtime/sys_freebsd_amd64.s @@ -38,8 +38,9 @@ TEXT runtime·thr_start(SB),7,$0 MOVQ m_g0(R13), DI MOVQ DI, g(CX) - CALL runtime·stackcheck(SB) - CALL runtime·mstart(SB) + CALL runtime·stackcheck(SB) + CALL runtime·mstart(SB) + MOVQ 0, AX // crash (not reached) // Exit the entire program (like C exit) @@ -94,31 +95,29 @@ TEXT runtime·setitimer(SB), 7, $-8 // func now() (sec int64, nsec int32) TEXT time·now(SB), 7, $32 - MOVL $116, AX - LEAQ 8(SP), DI - MOVQ $0, SI + MOVL $232, AX + MOVQ $0, DI + LEAQ 8(SP), SI SYSCALL MOVQ 8(SP), AX // sec - MOVL 16(SP), DX // usec + MOVQ 16(SP), DX // nsec - // sec is in AX, usec in DX + // sec is in AX, nsec in DX MOVQ AX, sec+0(FP) - IMULQ $1000, DX MOVL DX, nsec+8(FP) RET TEXT runtime·nanotime(SB), 7, $32 - MOVL $116, AX - LEAQ 8(SP), DI - MOVQ $0, SI + MOVL $232, AX + MOVQ $0, DI + LEAQ 8(SP), SI SYSCALL MOVQ 8(SP), AX // sec - MOVL 16(SP), DX // usec + MOVQ 16(SP), DX // nsec - // sec is in AX, usec in DX + // sec is in AX, nsec in DX // return nsec in AX IMULQ $1000000000, AX - IMULQ $1000, DX ADDQ DX, AX RET @@ -138,8 +137,10 @@ TEXT runtime·sigtramp(SB),7,$64 // check that m exists MOVQ m(BX), BP CMPQ BP, $0 - JNE 2(PC) + JNE 4(PC) + MOVQ DI, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVQ g(BX), R10 @@ -182,6 +183,15 @@ TEXT runtime·munmap(SB),7,$0 MOVL $0xf1, 0xf1 // crash RET +TEXT runtime·madvise(SB),7,$0 + MOVQ 8(SP), DI + MOVQ 16(SP), SI + MOVQ 24(SP), DX + MOVQ $75, AX // madvise + SYSCALL + // ignore failure - maybe pages are locked + RET + TEXT runtime·sigaltstack(SB),7,$-8 MOVQ new+8(SP), DI MOVQ old+16(SP), SI diff --git a/src/pkg/runtime/sys_freebsd_arm.s b/src/pkg/runtime/sys_freebsd_arm.s new file mode 100644 index 000000000..77050e8d0 --- /dev/null +++ b/src/pkg/runtime/sys_freebsd_arm.s @@ -0,0 +1,260 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// System calls and other sys.stuff for ARM, FreeBSD +// /usr/src/sys/kern/syscalls.master for syscall numbers. +// + +#include "zasm_GOOS_GOARCH.h" + +TEXT runtime·sys_umtx_op(SB),7,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW 12(FP), R3 + ADD $20, R13 // arg 5 is passed on stack + SWI $454 + SUB $20, R13 + // BCS error + RET + +TEXT runtime·thr_new(SB),7,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + SWI $455 + RET + +TEXT runtime·thr_start(SB),7,$0 + MOVW R0, R9 // m + + // TODO(minux): set up TLS? + + // set up g + MOVW m_g0(R9), R10 + BL runtime·emptyfunc(SB) // fault if stack check is wrong + BL runtime·mstart(SB) + + MOVW $2, R9 // crash (not reached) + MOVW R9, (R9) + RET + +// Exit the entire program (like C exit) +TEXT runtime·exit(SB),7,$-8 + MOVW 0(FP), R0 // arg 1 exit status + SWI $1 + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·exit1(SB),7,$-8 + MOVW 0(FP), R0 // arg 1 exit status + SWI $431 + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·write(SB),7,$-8 + MOVW 0(FP), R0 // arg 1 fd + MOVW 4(FP), R1 // arg 2 buf + MOVW 8(FP), R2 // arg 3 count + SWI $4 + RET + +TEXT runtime·getrlimit(SB),7,$-8 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + SWI $194 + RET + +TEXT runtime·raisesigpipe(SB),7,$8 + // thr_self(&4(R13)) + MOVW $4(R13), R0 // arg 1 &4(R13) + SWI $432 + // thr_kill(self, SIGPIPE) + MOVW 4(R13), R0 // arg 1 id + MOVW $13, R1 // arg 2 SIGPIPE + SWI $433 + RET + +TEXT runtime·setitimer(SB), 7, $-8 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + SWI $83 + RET + +// func now() (sec int64, nsec int32) +TEXT time·now(SB), 7, $32 + MOVW $0, R0 // CLOCK_REALTIME + MOVW $8(R13), R1 + SWI $232 // clock_gettime + + MOVW 8(R13), R0 // sec.low + MOVW 12(R13), R1 // sec.high + MOVW 16(R13), R2 // nsec + + MOVW R0, 0(FP) + MOVW R1, 4(FP) + MOVW R2, 8(FP) + RET + +// int64 nanotime(void) so really +// void nanotime(int64 *nsec) +TEXT runtime·nanotime(SB), 7, $32 + MOVW $0, R0 // CLOCK_REALTIME + MOVW $8(R13), R1 + SWI $232 // clock_gettime + + MOVW 8(R13), R0 // sec.low + MOVW 12(R13), R4 // sec.high + MOVW 16(R13), R2 // nsec + + MOVW $1000000000, R3 + MULLU R0, R3, (R1, R0) + MUL R3, R4 + ADD.S R2, R0 + ADC R4, R1 + + MOVW 0(FP), R3 + MOVW R0, 0(R3) + MOVW R1, 4(R3) + RET + +TEXT runtime·sigaction(SB),7,$-8 + MOVW 0(FP), R0 // arg 1 sig + MOVW 4(FP), R1 // arg 2 act + MOVW 8(FP), R2 // arg 3 oact + SWI $416 + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·sigtramp(SB),7,$24 + // this might be called in external code context, + // where g and m are not set. + // first save R0, because _cgo_load_gm will clobber it + // TODO(adonovan): call runtime·badsignal if m=0, like other platforms? + MOVW R0, 4(R13) // signum + MOVW _cgo_load_gm(SB), R0 + CMP $0, R0 + BL.NE (R0) + + // save g + MOVW R10, R4 + MOVW R10, 20(R13) + + // g = m->signal + MOVW m_gsignal(R9), R10 + + // R0 is already saved + MOVW R1, 8(R13) // info + MOVW R2, 12(R13) // context + MOVW R4, 16(R13) // oldg + + BL runtime·sighandler(SB) + + // restore g + MOVW 20(R13), R10 + RET + +TEXT runtime·mmap(SB),7,$12 + MOVW 0(FP), R0 // arg 1 addr + MOVW 4(FP), R1 // arg 2 len + MOVW 8(FP), R2 // arg 3 prot + MOVW 12(FP), R3 // arg 4 flags + // arg 5 (fid) and arg6 (offset_lo, offset_hi) are passed on stack + // note the C runtime only passes the 32-bit offset_lo to us + MOVW 16(FP), R4 // arg 5 + MOVW R4, 4(R13) + MOVW 20(FP), R5 // arg 6 lower 32-bit + MOVW R5, 8(R13) + MOVW $0, R6 // higher 32-bit for arg 6 + MOVW R6, 12(R13) + ADD $4, R13 // pass arg 5 and arg 6 on stack + SWI $477 + SUB $4, R13 + RET + +TEXT runtime·munmap(SB),7,$0 + MOVW 0(FP), R0 // arg 1 addr + MOVW 4(FP), R1 // arg 2 len + SWI $73 + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·madvise(SB),7,$0 + MOVW 0(FP), R0 // arg 1 addr + MOVW 4(FP), R1 // arg 2 len + MOVW 8(FP), R2 // arg 3 flags + SWI $75 + // ignore failure - maybe pages are locked + RET + +TEXT runtime·sigaltstack(SB),7,$-8 + MOVW new+0(FP), R0 + MOVW old+4(FP), R1 + SWI $53 + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·usleep(SB),7,$16 + MOVW usec+0(FP), R0 + MOVW R0, R2 + MOVW $1000000, R1 + DIV R1, R0 + // 0(R13) is the saved LR, don't use it + MOVW R0, 4(R13) // tv_sec.low + MOVW $0, R0 + MOVW R0, 8(R13) // tv_sec.high + MOD R1, R2 + MOVW $1000, R1 + MUL R1, R2 + MOVW R2, 12(R13) // tv_nsec + + MOVW $4(R13), R0 // arg 1 - rqtp + MOVW $0, R1 // arg 2 - rmtp + SWI $240 // sys_nanosleep + RET + +TEXT runtime·sysctl(SB),7,$0 + MOVW 0(FP), R0 // arg 1 - name + MOVW 4(FP), R1 // arg 2 - namelen + MOVW 8(FP), R2 // arg 3 - oldp + MOVW 12(FP), R3 // arg 4 - oldlenp + // arg 5 (newp) and arg 6 (newlen) are passed on stack + ADD $20, R13 + SWI $202 // sys___sysctl + SUB.CS $0, R0, R0 + SUB $20, R13 + RET + +TEXT runtime·osyield(SB),7,$-4 + SWI $331 // sys_sched_yield + RET + +TEXT runtime·sigprocmask(SB),7,$0 + MOVW $3, R0 // arg 1 - how (SIG_SETMASK) + MOVW 0(FP), R1 // arg 2 - set + MOVW 4(FP), R2 // arg 3 - oset + SWI $340 // sys_sigprocmask + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·casp(SB),7,$0 + B runtime·cas(SB) + +// TODO(minux): this is only valid for ARMv6+ +// bool armcas(int32 *val, int32 old, int32 new) +// Atomically: +// if(*val == old){ +// *val = new; +// return 1; +// }else +// return 0; +TEXT runtime·cas(SB),7,$0 + B runtime·armcas(SB) diff --git a/src/pkg/runtime/sys_linux_386.s b/src/pkg/runtime/sys_linux_386.s index 602d9ddac..f27fd4713 100644 --- a/src/pkg/runtime/sys_linux_386.s +++ b/src/pkg/runtime/sys_linux_386.s @@ -104,40 +104,38 @@ TEXT runtime·mincore(SB),7,$0-24 // func now() (sec int64, nsec int32) TEXT time·now(SB), 7, $32 - MOVL $78, AX // syscall - gettimeofday - LEAL 8(SP), BX - MOVL $0, CX + MOVL $265, AX // syscall - clock_gettime + MOVL $0, BX + LEAL 8(SP), CX MOVL $0, DX CALL *runtime·_vdso(SB) MOVL 8(SP), AX // sec - MOVL 12(SP), BX // usec + MOVL 12(SP), BX // nsec - // sec is in AX, usec in BX + // sec is in AX, nsec in BX MOVL AX, sec+0(FP) MOVL $0, sec+4(FP) - IMULL $1000, BX MOVL BX, nsec+8(FP) RET // int64 nanotime(void) so really // void nanotime(int64 *nsec) TEXT runtime·nanotime(SB), 7, $32 - MOVL $78, AX // syscall - gettimeofday - LEAL 8(SP), BX - MOVL $0, CX + MOVL $265, AX // syscall - clock_gettime + MOVL $0, BX + LEAL 8(SP), CX MOVL $0, DX CALL *runtime·_vdso(SB) MOVL 8(SP), AX // sec - MOVL 12(SP), BX // usec + MOVL 12(SP), BX // nsec - // sec is in AX, usec in BX + // sec is in AX, nsec in BX // convert to DX:AX nsec MOVL $1000000000, CX MULL CX - IMULL $1000, BX ADDL BX, AX ADCL $0, DX - + MOVL ret+0(FP), DI MOVL AX, 0(DI) MOVL DX, 4(DI) @@ -170,8 +168,11 @@ TEXT runtime·sigtramp(SB),7,$44 // check that m exists MOVL m(CX), BX CMPL BX, $0 - JNE 2(PC) + JNE 5(PC) + MOVL sig+0(FP), BX + MOVL BX, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVL g(CX), DI @@ -240,9 +241,7 @@ TEXT runtime·madvise(SB),7,$0 MOVL 8(SP), CX MOVL 12(SP), DX CALL *runtime·_vdso(SB) - CMPL AX, $0xfffff001 - JLS 2(PC) - INT $3 + // ignore failure - maybe pages are locked RET // int32 futex(int32 *uaddr, int32 op, int32 val, @@ -258,7 +257,7 @@ TEXT runtime·futex(SB),7,$0 CALL *runtime·_vdso(SB) RET -// int32 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); +// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void)); TEXT runtime·clone(SB),7,$0 MOVL $120, AX // clone MOVL flags+4(SP), BX @@ -266,7 +265,7 @@ TEXT runtime·clone(SB),7,$0 MOVL $0, DX // parent tid ptr MOVL $0, DI // child tid ptr - // Copy m, g, fn off parent stack for use by child. + // Copy mp, gp, fn off parent stack for use by child. SUBL $16, CX MOVL mm+12(SP), SI MOVL SI, 0(CX) @@ -423,3 +422,11 @@ TEXT runtime·osyield(SB),7,$0 MOVL $158, AX CALL *runtime·_vdso(SB) RET + +TEXT runtime·sched_getaffinity(SB),7,$0 + MOVL $242, AX // syscall - sched_getaffinity + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + CALL *runtime·_vdso(SB) + RET diff --git a/src/pkg/runtime/sys_linux_amd64.s b/src/pkg/runtime/sys_linux_amd64.s index 657ab7e0b..e45943758 100644 --- a/src/pkg/runtime/sys_linux_amd64.s +++ b/src/pkg/runtime/sys_linux_amd64.s @@ -101,32 +101,61 @@ TEXT runtime·mincore(SB),7,$0-24 RET // func now() (sec int64, nsec int32) -TEXT time·now(SB), 7, $32 - LEAQ 8(SP), DI - MOVQ $0, SI - MOVQ $0xffffffffff600000, AX +TEXT time·now(SB),7,$16 + // Be careful. We're calling a function with gcc calling convention here. + // We're guaranteed 128 bytes on entry, and we've taken 16, and the + // call uses another 8. + // That leaves 104 for the gettime code to use. Hope that's enough! + MOVQ runtime·__vdso_clock_gettime_sym(SB), AX + CMPQ AX, $0 + JEQ fallback_gtod + MOVL $0, DI // CLOCK_REALTIME + LEAQ 0(SP), SI CALL AX - MOVQ 8(SP), AX // sec - MOVL 16(SP), DX // usec - - // sec is in AX, usec in DX + MOVQ 0(SP), AX // sec + MOVQ 8(SP), DX // nsec MOVQ AX, sec+0(FP) - IMULQ $1000, DX MOVL DX, nsec+8(FP) RET - -TEXT runtime·nanotime(SB), 7, $32 - LEAQ 8(SP), DI +fallback_gtod: + LEAQ 0(SP), DI MOVQ $0, SI - MOVQ $0xffffffffff600000, AX + MOVQ runtime·__vdso_gettimeofday_sym(SB), AX CALL AX - MOVQ 8(SP), AX // sec - MOVL 16(SP), DX // usec + MOVQ 0(SP), AX // sec + MOVL 8(SP), DX // usec + IMULQ $1000, DX + MOVQ AX, sec+0(FP) + MOVL DX, nsec+8(FP) + RET - // sec is in AX, usec in DX +TEXT runtime·nanotime(SB),7,$16 + // Duplicate time.now here to avoid using up precious stack space. + // See comment above in time.now. + MOVQ runtime·__vdso_clock_gettime_sym(SB), AX + CMPQ AX, $0 + JEQ fallback_gtod_nt + MOVL $0, DI // CLOCK_REALTIME + LEAQ 0(SP), SI + CALL AX + MOVQ 0(SP), AX // sec + MOVQ 8(SP), DX // nsec + // sec is in AX, nsec in DX // return nsec in AX IMULQ $1000000000, AX + ADDQ DX, AX + RET +fallback_gtod_nt: + LEAQ 0(SP), DI + MOVQ $0, SI + MOVQ runtime·__vdso_gettimeofday_sym(SB), AX + CALL AX + MOVQ 0(SP), AX // sec + MOVL 8(SP), DX // usec IMULQ $1000, DX + // sec is in AX, nsec in DX + // return nsec in AX + IMULQ $1000000000, AX ADDQ DX, AX RET @@ -157,8 +186,10 @@ TEXT runtime·sigtramp(SB),7,$64 // check that m exists MOVQ m(BX), BP CMPQ BP, $0 - JNE 2(PC) + JNE 4(PC) + MOVQ DI, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVQ g(BX), R10 @@ -219,9 +250,7 @@ TEXT runtime·madvise(SB),7,$0 MOVQ 24(SP), DX MOVQ $28, AX // madvise SYSCALL - CMPQ AX, $0xfffffffffffff001 - JLS 2(PC) - MOVL $0xf1, 0xf1 // crash + // ignore failure - maybe pages are locked RET // int64 futex(int32 *uaddr, int32 op, int32 val, @@ -237,12 +266,12 @@ TEXT runtime·futex(SB),7,$0 SYSCALL RET -// int64 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); +// int64 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void)); TEXT runtime·clone(SB),7,$0 MOVL flags+8(SP), DI MOVQ stack+16(SP), SI - // Copy m, g, fn off parent stack for use by child. + // Copy mp, gp, fn off parent stack for use by child. // Careful: Linux system call clobbers CX and R11. MOVQ mm+24(SP), R8 MOVQ gg+32(SP), R9 @@ -310,3 +339,11 @@ TEXT runtime·osyield(SB),7,$0 MOVL $24, AX SYSCALL RET + +TEXT runtime·sched_getaffinity(SB),7,$0 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVQ 24(SP), DX + MOVL $204, AX // syscall entry + SYSCALL + RET diff --git a/src/pkg/runtime/sys_linux_arm.s b/src/pkg/runtime/sys_linux_arm.s index 03e173d26..8bae2933f 100644 --- a/src/pkg/runtime/sys_linux_arm.s +++ b/src/pkg/runtime/sys_linux_arm.s @@ -34,9 +34,10 @@ #define SYS_sched_yield (SYS_BASE + 158) #define SYS_select (SYS_BASE + 142) // newselect #define SYS_ugetrlimit (SYS_BASE + 191) +#define SYS_sched_getaffinity (SYS_BASE + 242) +#define SYS_clock_gettime (SYS_BASE + 263) #define ARM_BASE (SYS_BASE + 0x0f0000) -#define SYS_ARM_cacheflush (ARM_BASE + 2) TEXT runtime·open(SB),7,$0 MOVW 0(FP), R0 @@ -131,10 +132,7 @@ TEXT runtime·madvise(SB),7,$0 MOVW 8(FP), R2 MOVW $SYS_madvise, R7 SWI $0 - MOVW $0xfffff001, R6 - CMP R6, R0 - MOVW.HI $0, R9 // crash on syscall failure - MOVW.HI R9, (R9) + // ignore failure - maybe pages are locked RET TEXT runtime·setitimer(SB),7,$0 @@ -154,41 +152,37 @@ TEXT runtime·mincore(SB),7,$0 RET TEXT time·now(SB), 7, $32 - MOVW $8(R13), R0 // timeval - MOVW $0, R1 // zone - MOVW $SYS_gettimeofday, R7 + MOVW $0, R0 // CLOCK_REALTIME + MOVW $8(R13), R1 // timespec + MOVW $SYS_clock_gettime, R7 SWI $0 MOVW 8(R13), R0 // sec - MOVW 12(R13), R2 // usec + MOVW 12(R13), R2 // nsec MOVW R0, 0(FP) MOVW $0, R1 MOVW R1, 4(FP) - MOVW $1000, R3 - MUL R3, R2 MOVW R2, 8(FP) RET // int64 nanotime(void) so really // void nanotime(int64 *nsec) TEXT runtime·nanotime(SB),7,$32 - MOVW $8(R13), R0 // timeval - MOVW $0, R1 // zone - MOVW $SYS_gettimeofday, R7 + MOVW $0, R0 // CLOCK_REALTIME + MOVW $8(R13), R1 // timespec + MOVW $SYS_clock_gettime, R7 SWI $0 MOVW 8(R13), R0 // sec - MOVW 12(R13), R2 // usec + MOVW 12(R13), R2 // nsec MOVW $1000000000, R3 MULLU R0, R3, (R1, R0) - MOVW $1000, R3 MOVW $0, R4 - MUL R3, R2 ADD.S R2, R0 ADC R4, R1 - + MOVW 0(FP), R3 MOVW R0, 0(R3) MOVW R1, 4(R3) @@ -208,7 +202,7 @@ TEXT runtime·futex(SB),7,$0 RET -// int32 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); +// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void)); TEXT runtime·clone(SB),7,$0 MOVW flags+0(FP), R0 MOVW stack+4(FP), R1 @@ -217,7 +211,7 @@ TEXT runtime·clone(SB),7,$0 MOVW $0, R4 // child tid ptr MOVW $0, R5 - // Copy m, g, fn off parent stack for use by child. + // Copy mp, gp, fn off parent stack for use by child. // TODO(kaib): figure out which registers are clobbered by clone and avoid stack copying MOVW $-16(R1), R1 MOVW mm+8(FP), R6 @@ -272,15 +266,6 @@ TEXT runtime·clone(SB),7,$0 MOVW $1005, R1 MOVW R0, (R1) - -TEXT runtime·cacheflush(SB),7,$0 - MOVW 0(FP), R0 - MOVW 4(FP), R1 - MOVW $0, R2 - MOVW $SYS_ARM_cacheflush, R7 - SWI $0 - RET - TEXT runtime·sigaltstack(SB),7,$0 MOVW 0(FP), R0 MOVW 4(FP), R1 @@ -293,6 +278,15 @@ TEXT runtime·sigaltstack(SB),7,$0 RET TEXT runtime·sigtramp(SB),7,$24 + // this might be called in external code context, + // where g and m are not set. + // first save R0, because _cgo_load_gm will clobber it + // TODO(adonovan): call runtime·badsignal if m=0, like other platforms? + MOVW R0, 4(R13) + MOVW _cgo_load_gm(SB), R0 + CMP $0, R0 + BL.NE (R0) + // save g MOVW g, R3 MOVW g, 20(R13) @@ -301,7 +295,7 @@ TEXT runtime·sigtramp(SB),7,$24 MOVW m_gsignal(m), g // copy arguments for call to sighandler - MOVW R0, 4(R13) + // R0 is already saved above MOVW R1, 8(R13) MOVW R2, 12(R13) MOVW R3, 16(R13) @@ -385,3 +379,11 @@ TEXT runtime·osyield(SB),7,$0 MOVW $SYS_sched_yield, R7 SWI $0 RET + +TEXT runtime·sched_getaffinity(SB),7,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW $SYS_sched_getaffinity, R7 + SWI $0 + RET diff --git a/src/pkg/runtime/sys_netbsd_386.s b/src/pkg/runtime/sys_netbsd_386.s index 11f8c7aaa..3d3d31273 100644 --- a/src/pkg/runtime/sys_netbsd_386.s +++ b/src/pkg/runtime/sys_netbsd_386.s @@ -12,14 +12,14 @@ TEXT runtime·exit(SB),7,$-4 MOVL $1, AX INT $0x80 - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·exit1(SB),7,$-4 - MOVL $302, AX // sys_threxit + MOVL $310, AX // sys__lwp_exit INT $0x80 JAE 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·write(SB),7,$-4 @@ -27,31 +27,32 @@ TEXT runtime·write(SB),7,$-4 INT $0x80 RET -TEXT runtime·usleep(SB),7,$20 +TEXT runtime·usleep(SB),7,$24 MOVL $0, DX MOVL usec+0(FP), AX MOVL $1000000, CX DIVL CX - MOVL AX, 12(SP) // tv_sec + MOVL AX, 12(SP) // tv_sec - l32 + MOVL $0, 16(SP) // tv_sec - h32 MOVL $1000, AX MULL DX - MOVL AX, 16(SP) // tv_nsec + MOVL AX, 20(SP) // tv_nsec MOVL $0, 0(SP) LEAL 12(SP), AX MOVL AX, 4(SP) // arg 1 - rqtp MOVL $0, 8(SP) // arg 2 - rmtp - MOVL $240, AX // sys_nanosleep + MOVL $430, AX // sys_nanosleep INT $0x80 RET TEXT runtime·raisesigpipe(SB),7,$12 - MOVL $299, AX // sys_getthrid + MOVL $311, AX // sys__lwp_self INT $0x80 MOVL $0, 0(SP) - MOVL AX, 4(SP) // arg 1 - pid - MOVL $13, 8(SP) // arg 2 - signum == SIGPIPE - MOVL $37, AX // sys_kill + MOVL AX, 4(SP) // arg 1 - target + MOVL $13, 8(SP) // arg 2 - signo == SIGPIPE + MOVL $318, AX // sys__lwp_kill INT $0x80 RET @@ -67,7 +68,7 @@ TEXT runtime·mmap(SB),7,$36 MOVL $0, AX STOSL // arg 6 - pad MOVSL // arg 7 - offset - MOVL $0, AX // top 64 bits of file offset + MOVL $0, AX // top 32 bits of file offset STOSL MOVL $197, AX // sys_mmap INT $0x80 @@ -79,60 +80,100 @@ TEXT runtime·munmap(SB),7,$-4 MOVL $73, AX // sys_munmap INT $0x80 JAE 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·madvise(SB),7,$-4 + MOVL $75, AX // sys_madvise + INT $0x80 + // ignore failure - maybe pages are locked RET TEXT runtime·setitimer(SB),7,$-4 - MOVL $83, AX + MOVL $425, AX // sys_setitimer INT $0x80 RET // func now() (sec int64, nsec int32) TEXT time·now(SB), 7, $32 - MOVL $116, AX LEAL 12(SP), BX - MOVL BX, 4(SP) - MOVL $0, 8(SP) + MOVL $0, 4(SP) // arg 1 - clock_id + MOVL BX, 8(SP) // arg 2 - tp + MOVL $427, AX // sys_clock_gettime INT $0x80 - MOVL 12(SP), AX // sec - MOVL 16(SP), BX // usec - // sec is in AX, usec in BX + MOVL 12(SP), AX // sec - l32 MOVL AX, sec+0(FP) - MOVL $0, sec+4(FP) - IMULL $1000, BX + MOVL 16(SP), AX // sec - h32 + MOVL AX, sec+4(FP) + + MOVL 20(SP), BX // nsec MOVL BX, nsec+8(FP) RET // int64 nanotime(void) so really // void nanotime(int64 *nsec) TEXT runtime·nanotime(SB),7,$32 - MOVL $116, AX LEAL 12(SP), BX - MOVL BX, 4(SP) - MOVL $0, 8(SP) + MOVL $0, 4(SP) // arg 1 - clock_id + MOVL BX, 8(SP) // arg 2 - tp + MOVL $427, AX // sys_clock_gettime INT $0x80 - MOVL 12(SP), AX // sec - MOVL 16(SP), BX // usec - - // sec is in AX, usec in BX - // convert to DX:AX nsec - MOVL $1000000000, CX - MULL CX - IMULL $1000, BX + + MOVL 16(SP), CX // sec - h32 + IMULL $1000000000, CX + + MOVL 12(SP), AX // sec - l32 + MOVL $1000000000, BX + MULL BX // result in dx:ax + + MOVL 20(SP), BX // nsec ADDL BX, AX - ADCL $0, DX - + ADCL CX, DX // add high bits with carry + MOVL ret+0(FP), DI MOVL AX, 0(DI) MOVL DX, 4(DI) RET -TEXT runtime·sigaction(SB),7,$-4 - MOVL $46, AX // sys_sigaction +TEXT runtime·getcontext(SB),7,$-4 + MOVL $307, AX // sys_getcontext + INT $0x80 + JAE 2(PC) + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·sigprocmask(SB),7,$-4 + MOVL $293, AX // sys_sigprocmask + INT $0x80 + JAE 2(PC) + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·sigreturn_tramp(SB),7,$0 + LEAL 140(SP), AX // Load address of ucontext + MOVL AX, 4(SP) + MOVL $308, AX // sys_setcontext + INT $0x80 + MOVL $-1, 4(SP) // Something failed... + MOVL $1, AX // sys_exit + INT $0x80 + +TEXT runtime·sigaction(SB),7,$24 + LEAL arg0+0(FP), SI + LEAL 4(SP), DI + CLD + MOVSL // arg 1 - sig + MOVSL // arg 2 - act + MOVSL // arg 3 - oact + LEAL runtime·sigreturn_tramp(SB), AX + STOSL // arg 4 - tramp + MOVL $2, AX + STOSL // arg 5 - vers + MOVL $340, AX // sys___sigaction_sigtramp INT $0x80 JAE 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·sigtramp(SB),7,$44 @@ -141,13 +182,16 @@ TEXT runtime·sigtramp(SB),7,$44 // check that m exists MOVL m(CX), BX CMPL BX, $0 - JNE 2(PC) + JNE 5(PC) + MOVL signo+0(FP), BX + MOVL BX, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVL g(CX), DI MOVL DI, 20(SP) - + // g = m->gsignal MOVL m_gsignal(BX), BX MOVL BX, g(CX) @@ -167,67 +211,24 @@ TEXT runtime·sigtramp(SB),7,$44 get_tls(CX) MOVL 20(SP), BX MOVL BX, g(CX) - - // call sigreturn - MOVL context+8(FP), AX - MOVL $0, 0(SP) // syscall gap - MOVL AX, 4(SP) // arg 1 - sigcontext - MOVL $103, AX // sys_sigreturn - INT $0x80 - MOVL $0xf1, 0xf1 // crash RET -// int32 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); -TEXT runtime·rfork_thread(SB),7,$8 - MOVL flags+8(SP), AX - MOVL stack+12(SP), CX - - // Copy m, g, fn off parent stack for use by child. - SUBL $16, CX - MOVL mm+16(SP), SI - MOVL SI, 0(CX) - MOVL gg+20(SP), SI - MOVL SI, 4(CX) - MOVL fn+24(SP), SI - MOVL SI, 8(CX) - MOVL $1234, 12(CX) - MOVL CX, SI - - MOVL $0, 0(SP) // syscall gap - MOVL AX, 4(SP) // arg 1 - flags - MOVL $251, AX // sys_rfork +// int32 lwp_create(void *context, uintptr flags, void *lwpid); +TEXT runtime·lwp_create(SB),7,$16 + MOVL $0, 0(SP) + MOVL context+0(FP), AX + MOVL AX, 4(SP) // arg 1 - context + MOVL flags+4(FP), AX + MOVL AX, 8(SP) // arg 2 - flags + MOVL lwpid+8(FP), AX + MOVL AX, 12(SP) // arg 3 - lwpid + MOVL $309, AX // sys__lwp_create INT $0x80 - - // Return if rfork syscall failed - JCC 4(PC) + JCC 2(PC) NEGL AX - MOVL AX, 48(SP) RET - // In parent, return. - CMPL AX, $0 - JEQ 3(PC) - MOVL AX, 48(SP) - RET - - // In child, on new stack. - MOVL SI, SP - - // Paranoia: check that SP is as we expect. - MOVL 12(SP), BP - CMPL BP, $1234 - JEQ 2(PC) - INT $3 - - // Reload registers - MOVL 0(SP), BX // m - MOVL 4(SP), DX // g - MOVL 8(SP), SI // fn - - // Initialize m->procid to thread ID - MOVL $299, AX // sys_getthrid - INT $0x80 - MOVL AX, m_procid(BX) +TEXT runtime·lwp_tramp(SB),7,$0 // Set FS to point at m->tls LEAL m_tls(BX), BP @@ -236,7 +237,7 @@ TEXT runtime·rfork_thread(SB),7,$8 CALL runtime·settls(SB) POPL AX POPAL - + // Now segment is established. Initialize m, g. get_tls(AX) MOVL DX, g(AX) @@ -259,7 +260,7 @@ TEXT runtime·rfork_thread(SB),7,$8 RET TEXT runtime·sigaltstack(SB),7,$-8 - MOVL $288, AX // sys_sigaltstack + MOVL $281, AX // sys___sigaltstack14 MOVL new+4(SP), BX MOVL old+8(SP), CX INT $0x80 @@ -277,30 +278,33 @@ TEXT runtime·setldt(SB),7,$8 TEXT runtime·settls(SB),7,$16 // adjust for ELF: wants to use -8(GS) and -4(GS) for g and m - MOVL 20(SP), CX + MOVL base+0(FP), CX ADDL $8, CX - MOVL CX, 0(CX) MOVL $0, 0(SP) // syscall gap - MOVL $9, 4(SP) // I386_SET_GSBASE (machine/sysarch.h) - MOVL CX, 8(SP) // pointer to base - MOVL $165, AX // sys_sysarch + MOVL CX, 4(SP) // arg 1 - ptr + MOVL $317, AX // sys__lwp_setprivate INT $0x80 JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·osyield(SB),7,$-4 - MOVL $298, AX // sys_sched_yield + MOVL $350, AX // sys_sched_yield + INT $0x80 + RET + +TEXT runtime·lwp_park(SB),7,$-4 + MOVL $434, AX // sys__lwp_park INT $0x80 RET -TEXT runtime·thrsleep(SB),7,$-4 - MOVL $300, AX // sys_thrsleep +TEXT runtime·lwp_unpark(SB),7,$-4 + MOVL $321, AX // sys__lwp_unpark INT $0x80 RET -TEXT runtime·thrwakeup(SB),7,$-4 - MOVL $301, AX // sys_thrwakeup +TEXT runtime·lwp_self(SB),7,$-4 + MOVL $311, AX // sys__lwp_self INT $0x80 RET diff --git a/src/pkg/runtime/sys_netbsd_amd64.s b/src/pkg/runtime/sys_netbsd_amd64.s index 0b83cd4d8..e73e83ded 100644 --- a/src/pkg/runtime/sys_netbsd_amd64.s +++ b/src/pkg/runtime/sys_netbsd_amd64.s @@ -8,42 +8,24 @@ #include "zasm_GOOS_GOARCH.h" -// int64 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); -TEXT runtime·rfork_thread(SB),7,$0 - MOVL flags+8(SP), DI - MOVQ stack+16(SP), SI - - // Copy m, g, fn off parent stack for use by child. - MOVQ mm+24(SP), R8 - MOVQ gg+32(SP), R9 - MOVQ fn+40(SP), R12 - - MOVL $251, AX // sys_rfork +// int32 lwp_create(void *context, uintptr flags, void *lwpid) +TEXT runtime·lwp_create(SB),7,$0 + MOVQ context+0(FP), DI + MOVQ flags+8(FP), SI + MOVQ lwpid+16(FP), DX + MOVL $309, AX // sys__lwp_create SYSCALL - - // Return if rfork syscall failed - JCC 3(PC) + JCC 2(PC) NEGL AX RET - // In parent, return. - CMPL AX, $0 - JEQ 2(PC) - RET - - // In child, on new stack. - MOVQ SI, SP - - // Initialize m->procid to thread ID - MOVL $299, AX // sys_getthrid - SYSCALL - MOVQ AX, m_procid(R8) - +TEXT runtime·lwp_tramp(SB),7,$0 + // Set FS to point at m->tls. LEAQ m_tls(R8), DI CALL runtime·settls(SB) - // In child, set up new stack + // Set up new stack. get_tls(CX) MOVQ R8, m(CX) MOVQ R9, g(CX) @@ -52,29 +34,34 @@ TEXT runtime·rfork_thread(SB),7,$0 // Call fn CALL R12 - // It shouldn't return. If it does, exit - MOVL $302, AX // sys_threxit + // It shouldn't return. If it does, exit. + MOVL $310, AX // sys__lwp_exit SYSCALL JMP -3(PC) // keep exiting TEXT runtime·osyield(SB),7,$0 - MOVL $298, AX // sys_sched_yield + MOVL $350, AX // sys_sched_yield SYSCALL RET -TEXT runtime·thrsleep(SB),7,$0 - MOVQ 8(SP), DI // arg 1 - ident - MOVL 16(SP), SI // arg 2 - clock_id - MOVQ 24(SP), DX // arg 3 - tp - MOVQ 32(SP), R10 // arg 4 - lock - MOVL $300, AX // sys_thrsleep +TEXT runtime·lwp_park(SB),7,$0 + MOVQ 8(SP), DI // arg 1 - abstime + MOVL 16(SP), SI // arg 2 - unpark + MOVQ 24(SP), DX // arg 3 - hint + MOVQ 32(SP), R10 // arg 4 - unparkhint + MOVL $434, AX // sys__lwp_park SYSCALL RET -TEXT runtime·thrwakeup(SB),7,$0 - MOVQ 8(SP), DI // arg 1 - ident - MOVL 16(SP), SI // arg 2 - n - MOVL $301, AX // sys_thrwakeup +TEXT runtime·lwp_unpark(SB),7,$0 + MOVQ 8(SP), DI // arg 1 - lwp + MOVL 16(SP), SI // arg 2 - hint + MOVL $321, AX // sys__lwp_unpark + SYSCALL + RET + +TEXT runtime·lwp_self(SB),7,$0 + MOVL $311, AX // sys__lwp_self SYSCALL RET @@ -83,13 +70,13 @@ TEXT runtime·exit(SB),7,$-8 MOVL 8(SP), DI // arg 1 - exit status MOVL $1, AX // sys_exit SYSCALL - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·exit1(SB),7,$-8 - MOVL $302, AX // sys_threxit + MOVL $310, AX // sys__lwp_exit SYSCALL - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·write(SB),7,$-8 @@ -112,16 +99,16 @@ TEXT runtime·usleep(SB),7,$16 MOVQ SP, DI // arg 1 - rqtp MOVQ $0, SI // arg 2 - rmtp - MOVL $240, AX // sys_nanosleep + MOVL $430, AX // sys_nanosleep SYSCALL RET TEXT runtime·raisesigpipe(SB),7,$16 - MOVL $299, AX // sys_getthrid + MOVL $311, AX // sys__lwp_self SYSCALL - MOVQ AX, DI // arg 1 - pid - MOVQ $13, SI // arg 2 - signum == SIGPIPE - MOVL $37, AX // sys_kill + MOVQ AX, DI // arg 1 - target + MOVQ $13, SI // arg 2 - signo == SIGPIPE + MOVL $318, AX // sys__lwp_kill SYSCALL RET @@ -129,72 +116,101 @@ TEXT runtime·setitimer(SB),7,$-8 MOVL 8(SP), DI // arg 1 - which MOVQ 16(SP), SI // arg 2 - itv MOVQ 24(SP), DX // arg 3 - oitv - MOVL $83, AX // sys_setitimer + MOVL $425, AX // sys_setitimer SYSCALL RET // func now() (sec int64, nsec int32) TEXT time·now(SB), 7, $32 - LEAQ 8(SP), DI // arg 1 - tp - MOVQ $0, SI // arg 2 - tzp - MOVL $116, AX // sys_gettimeofday + MOVQ $0, DI // arg 1 - clock_id + LEAQ 8(SP), SI // arg 2 - tp + MOVL $427, AX // sys_clock_gettime SYSCALL MOVQ 8(SP), AX // sec - MOVL 16(SP), DX // usec + MOVL 16(SP), DX // nsec - // sec is in AX, usec in DX + // sec is in AX, nsec in DX MOVQ AX, sec+0(FP) - IMULQ $1000, DX MOVL DX, nsec+8(FP) RET TEXT runtime·nanotime(SB),7,$32 - LEAQ 8(SP), DI // arg 1 - tp - MOVQ $0, SI // arg 2 - tzp - MOVL $116, AX // sys_gettimeofday + MOVQ $0, DI // arg 1 - clock_id + LEAQ 8(SP), SI // arg 2 - tp + MOVL $427, AX // sys_clock_gettime SYSCALL MOVQ 8(SP), AX // sec - MOVL 16(SP), DX // usec + MOVL 16(SP), DX // nsec - // sec is in AX, usec in DX + // sec is in AX, nsec in DX // return nsec in AX IMULQ $1000000000, AX - IMULQ $1000, DX ADDQ DX, AX RET +TEXT runtime·getcontext(SB),7,$-8 + MOVQ 8(SP), DI // arg 1 - context + MOVL $307, AX // sys_getcontext + SYSCALL + JCC 2(PC) + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·sigprocmask(SB),7,$0 + MOVL 8(SP), DI // arg 1 - how + MOVQ 16(SP), SI // arg 2 - set + MOVQ 24(SP), DX // arg 3 - oset + MOVL $293, AX // sys_sigprocmask + SYSCALL + JCC 2(PC) + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·sigreturn_tramp(SB),7,$-8 + MOVQ R15, DI // Load address of ucontext + MOVQ $308, AX // sys_setcontext + SYSCALL + MOVQ $-1, DI // Something failed... + MOVL $1, AX // sys_exit + SYSCALL + TEXT runtime·sigaction(SB),7,$-8 MOVL 8(SP), DI // arg 1 - signum MOVQ 16(SP), SI // arg 2 - nsa MOVQ 24(SP), DX // arg 3 - osa - MOVL $46, AX + // arg 4 - tramp + LEAQ runtime·sigreturn_tramp(SB), R10 + MOVQ $2, R8 // arg 5 - vers + MOVL $340, AX // sys___sigaction_sigtramp SYSCALL JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·sigtramp(SB),7,$64 get_tls(BX) - + // check that m exists MOVQ m(BX), BP CMPQ BP, $0 - JNE 2(PC) + JNE 4(PC) + MOVQ DI, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVQ g(BX), R10 MOVQ R10, 40(SP) - + // g = m->signal MOVQ m_gsignal(BP), BP MOVQ BP, g(BX) - + MOVQ DI, 0(SP) MOVQ SI, 8(SP) MOVQ DX, 16(SP) MOVQ R10, 24(SP) - + CALL runtime·sighandler(SB) // restore g @@ -213,7 +229,7 @@ TEXT runtime·mmap(SB),7,$0 SUBQ $16, SP MOVQ R9, 8(SP) // arg 7 - offset (passed on stack) MOVQ $0, R9 // arg 6 - pad - MOVL $197, AX + MOVL $197, AX // sys_mmap SYSCALL JCC 2(PC) NEGL AX @@ -226,29 +242,36 @@ TEXT runtime·munmap(SB),7,$0 MOVL $73, AX // sys_munmap SYSCALL JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash + RET + + +TEXT runtime·madvise(SB),7,$0 + MOVQ addr+0(FP), DI // arg 1 - addr + MOVQ len+8(FP), SI // arg 2 - len + MOVQ behav+16(FP), DX // arg 3 - behav + MOVQ $75, AX // sys_madvise + SYSCALL + // ignore failure - maybe pages are locked RET TEXT runtime·sigaltstack(SB),7,$-8 MOVQ new+8(SP), DI // arg 1 - nss MOVQ old+16(SP), SI // arg 2 - oss - MOVQ $288, AX // sys_sigaltstack + MOVQ $281, AX // sys___sigaltstack14 SYSCALL JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET // set tls base to DI TEXT runtime·settls(SB),7,$8 // adjust for ELF: wants to use -16(FS) and -8(FS) for g and m - ADDQ $16, DI - MOVQ DI, 0(SP) - MOVQ SP, SI - MOVQ $12, DI // AMD64_SET_FSBASE (machine/sysarch.h) - MOVQ $165, AX // sys_sysarch + ADDQ $16, DI // arg 1 - ptr + MOVQ $317, AX // sys__lwp_setprivate SYSCALL JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·sysctl(SB),7,$0 diff --git a/src/pkg/runtime/sys_netbsd_arm.s b/src/pkg/runtime/sys_netbsd_arm.s new file mode 100644 index 000000000..4a119c5de --- /dev/null +++ b/src/pkg/runtime/sys_netbsd_arm.s @@ -0,0 +1,280 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// System calls and other sys.stuff for ARM, NetBSD +// /usr/src/sys/kern/syscalls.master for syscall numbers. +// + +#include "zasm_GOOS_GOARCH.h" + +// Exit the entire program (like C exit) +TEXT runtime·exit(SB),7,$-4 + MOVW 0(FP), R0 // arg 1 exit status + SWI $0xa00001 + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·exit1(SB),7,$-4 + SWI $0xa00136 // sys__lwp_exit + MOVW $1, R9 // crash + MOVW R9, (R9) + RET + +TEXT runtime·write(SB),7,$-4 + MOVW 0(FP), R0 // arg 1 - fd + MOVW 4(FP), R1 // arg 2 - buf + MOVW 8(FP), R2 // arg 3 - nbyte + SWI $0xa00004 // sys_write + RET + +// int32 lwp_create(void *context, uintptr flags, void *lwpid) +TEXT runtime·lwp_create(SB),7,$0 + MOVW context+0(FP), R0 + MOVW flags+4(FP), R1 + MOVW lwpid+8(FP), R2 + SWI $0xa00135 // sys__lwp_create + RET + +TEXT runtime·osyield(SB),7,$0 + SWI $0xa0015e // sys_sched_yield + RET + +TEXT runtime·lwp_park(SB),7,$0 + MOVW 0(FP), R0 // arg 1 - abstime + MOVW 4(FP), R1 // arg 2 - unpark + MOVW 8(FP), R2 // arg 3 - hint + MOVW 12(FP), R3 // arg 4 - unparkhint + SWI $0xa001b2 // sys__lwp_park + RET + +TEXT runtime·lwp_unpark(SB),7,$0 + MOVW 0(FP), R0 // arg 1 - lwp + MOVW 4(FP), R1 // arg 2 - hint + SWI $0xa00141 // sys__lwp_unpark + RET + +TEXT runtime·lwp_self(SB),7,$0 + SWI $0xa00137 // sys__lwp_self + RET + +TEXT runtime·lwp_tramp(SB),7,$0 + MOVW R0, R9 // m + MOVW R1, R10 // g + + BL runtime·emptyfunc(SB) // fault if stack check is wrong + BL (R2) + MOVW $2, R9 // crash (not reached) + MOVW R9, (R9) + RET + +TEXT runtime·usleep(SB),7,$16 + MOVW usec+0(FP), R0 + MOVW R0, R2 + MOVW $1000000, R1 + DIV R1, R0 + // 0(R13) is the saved LR, don't use it + MOVW R0, 4(R13) // tv_sec.low + MOVW $0, R0 + MOVW R0, 8(R13) // tv_sec.high + MOD R1, R2 + MOVW $1000, R1 + MUL R1, R2 + MOVW R2, 12(R13) // tv_nsec + + MOVW $4(R13), R0 // arg 1 - rqtp + MOVW $0, R1 // arg 2 - rmtp + SWI $0xa001ae // sys_nanosleep + RET + +TEXT runtime·raisesigpipe(SB),7,$16 + SWI $0xa00137 // sys__lwp_self, the returned R0 is arg 1 + MOVW $13, R1 // arg 2 - signo == SIGPIPE + SWI $0xa0013e // sys__lwp_kill + RET + +TEXT runtime·setitimer(SB),7,$-4 + MOVW 0(FP), R0 // arg 1 - which + MOVW 4(FP), R1 // arg 2 - itv + MOVW 8(FP), R2 // arg 3 - oitv + SWI $0xa001a9 // sys_setitimer + RET + +// func now() (sec int64, nsec int32) +TEXT time·now(SB), 7, $32 + MOVW $0, R0 // CLOCK_REALTIME + MOVW $8(R13), R1 + SWI $0xa001ab // clock_gettime + + MOVW 8(R13), R0 // sec.low + MOVW 12(R13), R1 // sec.high + MOVW 16(R13), R2 // nsec + + MOVW R0, 0(FP) + MOVW R1, 4(FP) + MOVW R2, 8(FP) + RET + +// int64 nanotime(void) so really +// void nanotime(int64 *nsec) +TEXT runtime·nanotime(SB), 7, $32 + MOVW $0, R0 // CLOCK_REALTIME + MOVW $8(R13), R1 + SWI $0xa001ab // clock_gettime + + MOVW 8(R13), R0 // sec.low + MOVW 12(R13), R4 // sec.high + MOVW 16(R13), R2 // nsec + + MOVW $1000000000, R3 + MULLU R0, R3, (R1, R0) + MUL R3, R4 + ADD.S R2, R0 + ADC R4, R1 + + MOVW 0(FP), R3 + MOVW R0, 0(R3) + MOVW R1, 4(R3) + RET + +TEXT runtime·getcontext(SB),7,$-4 + MOVW 0(FP), R0 // arg 1 - context + SWI $0xa00133 // sys_getcontext + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·sigprocmask(SB),7,$0 + MOVW 0(FP), R0 // arg 1 - how + MOVW 4(FP), R1 // arg 2 - set + MOVW 8(FP), R2 // arg 3 - oset + SWI $0xa00125 // sys_sigprocmask + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·sigreturn_tramp(SB),7,$-4 + // in runtime·sigtramp, we saved ucontext into m->tls[0], + // here we just load it and call sys_setcontext + MOVW m_tls(m), R0 + SWI $0xa00134 // sys_setcontext + // something failed, we have to exit + MOVW $0x4242, R0 // magic return number + SWI $0xa00001 // sys_exit + B -2(PC) // continue exit + +TEXT runtime·sigaction(SB),7,$4 + MOVW 0(FP), R0 // arg 1 - signum + MOVW 4(FP), R1 // arg 2 - nsa + MOVW 8(FP), R2 // arg 3 - osa + MOVW $runtime·sigreturn_tramp(SB), R3 // arg 4 - tramp + MOVW $2, R4 // arg 5 - vers + MOVW R4, 4(R13) + ADD $4, R13 // pass arg 5 on stack + SWI $0xa00154 // sys___sigaction_sigtramp + SUB $4, R13 + MOVW.CS $3, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·sigtramp(SB),7,$24 + // this might be called in external code context, + // where g and m are not set. + // first save R0, because _cgo_load_gm will clobber it + // TODO(adonovan): call runtime·badsignal if m=0, like other platforms? + MOVW R0, 4(R13) // signum + MOVW _cgo_load_gm(SB), R0 + CMP $0, R0 + BL.NE (R0) + + // save g + MOVW R10, R4 + MOVW R10, 20(R13) + + // g = m->signal + MOVW m_gsignal(R9), R10 + + // R0 is already saved + MOVW R1, 8(R13) // info + MOVW R2, 12(R13) // context + MOVW R4, 16(R13) // gp + // we also save the ucontext into m->tls[0] for easy + // signal return + MOVW R2, m_tls(m) + + BL runtime·sighandler(SB) + + // restore g + MOVW 20(R13), R10 + RET + +TEXT runtime·mmap(SB),7,$12 + MOVW 0(FP), R0 // arg 1 - addr + MOVW 4(FP), R1 // arg 2 - len + MOVW 8(FP), R2 // arg 3 - prot + MOVW 12(FP), R3 // arg 4 - flags + // arg 5 (fid) and arg6 (offset_lo, offset_hi) are passed on stack + // note the C runtime only passes the 32-bit offset_lo to us + MOVW 16(FP), R4 // arg 5 + MOVW R4, 4(R13) + MOVW 20(FP), R5 // arg 6 lower 32-bit + MOVW R5, 8(R13) + MOVW $0, R6 // higher 32-bit for arg 6 + MOVW R6, 12(R13) + ADD $4, R13 // pass arg 5 and arg 6 on stack + SWI $0xa000c5 // sys_mmap + SUB $4, R13 + RET + +TEXT runtime·munmap(SB),7,$0 + MOVW 0(FP), R0 // arg 1 - addr + MOVW 4(FP), R1 // arg 2 - len + SWI $0xa00049 // sys_munmap + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·madvise(SB),7,$0 + MOVW 0(FP), R0 // arg 1 - addr + MOVW 4(FP), R1 // arg 2 - len + MOVW 8(FP), R2 // arg 3 - behav + SWI $0xa0004b // sys_madvise + // ignore failure - maybe pages are locked + RET + +TEXT runtime·sigaltstack(SB),7,$-4 + MOVW 0(FP), R0 // arg 1 - nss + MOVW 4(FP), R1 // arg 2 - oss + SWI $0xa00119 // sys___sigaltstack14 + MOVW.CS $0, R9 // crash on syscall failure + MOVW.CS R9, (R9) + RET + +TEXT runtime·sysctl(SB),7,$8 + MOVW 0(FP), R0 // arg 1 - name + MOVW 4(FP), R1 // arg 2 - namelen + MOVW 8(FP), R2 // arg 3 - oldp + MOVW 12(FP), R3 // arg 4 - oldlenp + MOVW 16(FP), R4 // arg 5 - newp + MOVW R4, 4(R13) + MOVW 20(FP), R4 // arg 6 - newlen + MOVW R4, 8(R13) + ADD $4, R13 // pass arg 5 and 6 on stack + SWI $0xa000ca // sys___sysctl + SUB $4, R13 + RET + +TEXT runtime·casp(SB),7,$0 + B runtime·cas(SB) + +// TODO(minux): this is only valid for ARMv6+ +// bool armcas(int32 *val, int32 old, int32 new) +// Atomically: +// if(*val == old){ +// *val = new; +// return 1; +// }else +// return 0; +TEXT runtime·cas(SB),7,$0 + B runtime·armcas(SB) diff --git a/src/pkg/runtime/sys_openbsd_386.s b/src/pkg/runtime/sys_openbsd_386.s index 593b4a9df..c62e0f949 100644 --- a/src/pkg/runtime/sys_openbsd_386.s +++ b/src/pkg/runtime/sys_openbsd_386.s @@ -12,14 +12,16 @@ TEXT runtime·exit(SB),7,$-4 MOVL $1, AX INT $0x80 - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET -TEXT runtime·exit1(SB),7,$-4 - MOVL $302, AX // sys_threxit +TEXT runtime·exit1(SB),7,$8 + MOVL $0, 0(SP) + MOVL $0, 4(SP) // arg 1 - notdead + MOVL $302, AX // sys___threxit INT $0x80 JAE 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·write(SB),7,$-4 @@ -67,7 +69,7 @@ TEXT runtime·mmap(SB),7,$36 MOVL $0, AX STOSL // arg 6 - pad MOVSL // arg 7 - offset - MOVL $0, AX // top 64 bits of file offset + MOVL $0, AX // top 32 bits of file offset STOSL MOVL $197, AX // sys_mmap INT $0x80 @@ -79,7 +81,14 @@ TEXT runtime·munmap(SB),7,$-4 MOVL $73, AX // sys_munmap INT $0x80 JAE 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·madvise(SB),7,$-4 + MOVL $75, AX // sys_madvise + INT $0x80 + JAE 2(PC) + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·setitimer(SB),7,$-4 @@ -89,40 +98,38 @@ TEXT runtime·setitimer(SB),7,$-4 // func now() (sec int64, nsec int32) TEXT time·now(SB), 7, $32 - MOVL $116, AX + MOVL $232, AX LEAL 12(SP), BX - MOVL BX, 4(SP) - MOVL $0, 8(SP) + MOVL $0, 4(SP) + MOVL BX, 8(SP) INT $0x80 MOVL 12(SP), AX // sec - MOVL 16(SP), BX // usec + MOVL 16(SP), BX // nsec - // sec is in AX, usec in BX + // sec is in AX, nsec in BX MOVL AX, sec+0(FP) MOVL $0, sec+4(FP) - IMULL $1000, BX MOVL BX, nsec+8(FP) RET // int64 nanotime(void) so really // void nanotime(int64 *nsec) TEXT runtime·nanotime(SB),7,$32 - MOVL $116, AX + MOVL $232, AX LEAL 12(SP), BX - MOVL BX, 4(SP) - MOVL $0, 8(SP) + MOVL $0, 4(SP) + MOVL BX, 8(SP) INT $0x80 MOVL 12(SP), AX // sec - MOVL 16(SP), BX // usec + MOVL 16(SP), BX // nsec - // sec is in AX, usec in BX + // sec is in AX, nsec in BX // convert to DX:AX nsec MOVL $1000000000, CX MULL CX - IMULL $1000, BX ADDL BX, AX ADCL $0, DX - + MOVL ret+0(FP), DI MOVL AX, 0(DI) MOVL DX, 4(DI) @@ -132,7 +139,15 @@ TEXT runtime·sigaction(SB),7,$-4 MOVL $46, AX // sys_sigaction INT $0x80 JAE 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·sigprocmask(SB),7,$-4 + MOVL $48, AX // sys_sigprocmask + INT $0x80 + JAE 2(PC) + MOVL $0xf1, 0xf1 // crash + MOVL AX, oset+0(FP) RET TEXT runtime·sigtramp(SB),7,$44 @@ -141,8 +156,11 @@ TEXT runtime·sigtramp(SB),7,$44 // check that m exists MOVL m(CX), BX CMPL BX, $0 - JNE 2(PC) + JNE 5(PC) + MOVL signo+0(FP), BX + MOVL BX, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVL g(CX), DI @@ -174,62 +192,59 @@ TEXT runtime·sigtramp(SB),7,$44 MOVL AX, 4(SP) // arg 1 - sigcontext MOVL $103, AX // sys_sigreturn INT $0x80 - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET -// int32 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); -TEXT runtime·rfork_thread(SB),7,$8 - MOVL flags+8(SP), AX - MOVL stack+12(SP), CX +// int32 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void)); +TEXT runtime·tfork(SB),7,$12 - // Copy m, g, fn off parent stack for use by child. + // Copy mp, gp and fn from the parent stack onto the child stack. + MOVL params+4(FP), AX + MOVL 8(AX), CX // tf_stack SUBL $16, CX - MOVL mm+16(SP), SI + MOVL CX, 8(AX) + MOVL mm+12(FP), SI MOVL SI, 0(CX) - MOVL gg+20(SP), SI + MOVL gg+16(FP), SI MOVL SI, 4(CX) - MOVL fn+24(SP), SI + MOVL fn+20(FP), SI MOVL SI, 8(CX) MOVL $1234, 12(CX) - MOVL CX, SI MOVL $0, 0(SP) // syscall gap - MOVL AX, 4(SP) // arg 1 - flags - MOVL $251, AX // sys_rfork + MOVL params+4(FP), AX + MOVL AX, 4(SP) // arg 1 - param + MOVL psize+8(FP), AX + MOVL AX, 8(SP) // arg 2 - psize + MOVL $8, AX // sys___tfork INT $0x80 - // Return if rfork syscall failed - JCC 4(PC) + // Return if tfork syscall failed. + JCC 5(PC) NEGL AX - MOVL AX, 48(SP) + MOVL ret+0(FP), DX + MOVL AX, 0(DX) RET // In parent, return. CMPL AX, $0 - JEQ 3(PC) - MOVL AX, 48(SP) + JEQ 4(PC) + MOVL ret+0(FP), DX + MOVL AX, 0(DX) RET - // In child, on new stack. - MOVL SI, SP - // Paranoia: check that SP is as we expect. MOVL 12(SP), BP CMPL BP, $1234 JEQ 2(PC) INT $3 - // Reload registers + // Reload registers. MOVL 0(SP), BX // m MOVL 4(SP), DX // g MOVL 8(SP), SI // fn - // Initialize m->procid to thread ID - MOVL $299, AX // sys_getthrid - INT $0x80 - MOVL AX, m_procid(BX) - - // Set FS to point at m->tls + // Set FS to point at m->tls. LEAL m_tls(BX), BP PUSHAL // save registers PUSHL BP @@ -246,12 +261,12 @@ TEXT runtime·rfork_thread(SB),7,$8 MOVL 0(DX), DX // paranoia; check they are not nil MOVL 0(BX), BX - // more paranoia; check that stack splitting code works + // More paranoia; check that stack splitting code works. PUSHAL CALL runtime·emptyfunc(SB) POPAL - // Call fn + // Call fn. CALL SI CALL runtime·exit1(SB) @@ -268,25 +283,23 @@ TEXT runtime·sigaltstack(SB),7,$-8 INT $3 RET -TEXT runtime·setldt(SB),7,$8 +TEXT runtime·setldt(SB),7,$4 // Under OpenBSD we set the GS base instead of messing with the LDT. - MOVL 16(SP), AX // tls0 + MOVL tls0+4(FP), AX MOVL AX, 0(SP) CALL runtime·settls(SB) RET -TEXT runtime·settls(SB),7,$16 +TEXT runtime·settls(SB),7,$8 // adjust for ELF: wants to use -8(GS) and -4(GS) for g and m - MOVL 20(SP), CX + MOVL tlsbase+0(FP), CX ADDL $8, CX - MOVL CX, 0(CX) MOVL $0, 0(SP) // syscall gap - MOVL $9, 4(SP) // I386_SET_GSBASE (machine/sysarch.h) - MOVL CX, 8(SP) // pointer to base - MOVL $165, AX // sys_sysarch + MOVL CX, 4(SP) // arg 1 - tcb + MOVL $329, AX // sys___set_tcb INT $0x80 JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·osyield(SB),7,$-4 @@ -295,12 +308,12 @@ TEXT runtime·osyield(SB),7,$-4 RET TEXT runtime·thrsleep(SB),7,$-4 - MOVL $300, AX // sys_thrsleep + MOVL $300, AX // sys___thrsleep INT $0x80 RET TEXT runtime·thrwakeup(SB),7,$-4 - MOVL $301, AX // sys_thrwakeup + MOVL $301, AX // sys___thrwakeup INT $0x80 RET diff --git a/src/pkg/runtime/sys_openbsd_amd64.s b/src/pkg/runtime/sys_openbsd_amd64.s index d2d48e6b5..8a736507f 100644 --- a/src/pkg/runtime/sys_openbsd_amd64.s +++ b/src/pkg/runtime/sys_openbsd_amd64.s @@ -8,20 +8,20 @@ #include "zasm_GOOS_GOARCH.h" -// int64 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); -TEXT runtime·rfork_thread(SB),7,$0 - MOVL flags+8(SP), DI - MOVQ stack+16(SP), SI +// int64 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void)); +TEXT runtime·tfork(SB),7,$32 - // Copy m, g, fn off parent stack for use by child. - MOVQ mm+24(SP), R8 - MOVQ gg+32(SP), R9 - MOVQ fn+40(SP), R12 + // Copy mp, gp and fn off parent stack for use by child. + MOVQ mm+16(FP), R8 + MOVQ gg+24(FP), R9 + MOVQ fn+32(FP), R12 - MOVL $251, AX // sys_rfork + MOVQ param+0(FP), DI + MOVQ psize+8(FP), SI + MOVL $8, AX // sys___tfork SYSCALL - // Return if rfork syscall failed + // Return if tfork syscall failed. JCC 3(PC) NEGL AX RET @@ -31,19 +31,11 @@ TEXT runtime·rfork_thread(SB),7,$0 JEQ 2(PC) RET - // In child, on new stack. - MOVQ SI, SP - - // Initialize m->procid to thread ID - MOVL $299, AX // sys_getthrid - SYSCALL - MOVQ AX, m_procid(R8) - // Set FS to point at m->tls. LEAQ m_tls(R8), DI CALL runtime·settls(SB) - // In child, set up new stack + // In child, set up new stack. get_tls(CX) MOVQ R8, m(CX) MOVQ R9, g(CX) @@ -53,12 +45,13 @@ TEXT runtime·rfork_thread(SB),7,$0 CALL R12 // It shouldn't return. If it does, exit - MOVL $302, AX // sys_threxit + MOVQ $0, DI // arg 1 - notdead + MOVL $302, AX // sys___threxit SYSCALL JMP -3(PC) // keep exiting TEXT runtime·osyield(SB),7,$0 - MOVL $298, AX // sys_sched_yield + MOVL $298, AX // sys_sched_yield SYSCALL RET @@ -67,14 +60,15 @@ TEXT runtime·thrsleep(SB),7,$0 MOVL 16(SP), SI // arg 2 - clock_id MOVQ 24(SP), DX // arg 3 - tp MOVQ 32(SP), R10 // arg 4 - lock - MOVL $300, AX // sys_thrsleep + MOVQ 40(SP), R8 // arg 5 - abort + MOVL $300, AX // sys___thrsleep SYSCALL RET TEXT runtime·thrwakeup(SB),7,$0 MOVQ 8(SP), DI // arg 1 - ident MOVL 16(SP), SI // arg 2 - n - MOVL $301, AX // sys_thrwakeup + MOVL $301, AX // sys___thrwakeup SYSCALL RET @@ -83,13 +77,14 @@ TEXT runtime·exit(SB),7,$-8 MOVL 8(SP), DI // arg 1 - exit status MOVL $1, AX // sys_exit SYSCALL - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·exit1(SB),7,$-8 - MOVL $302, AX // sys_threxit + MOVQ $0, DI // arg 1 - notdead + MOVL $302, AX // sys___threxit SYSCALL - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·write(SB),7,$-8 @@ -135,31 +130,29 @@ TEXT runtime·setitimer(SB),7,$-8 // func now() (sec int64, nsec int32) TEXT time·now(SB), 7, $32 - LEAQ 8(SP), DI // arg 1 - tp - MOVQ $0, SI // arg 2 - tzp - MOVL $116, AX // sys_gettimeofday + MOVQ $0, DI // arg 1 - clock_id + LEAQ 8(SP), SI // arg 2 - tp + MOVL $232, AX // sys_clock_gettime SYSCALL - MOVQ 8(SP), AX // sec - MOVL 16(SP), DX // usec + MOVL 8(SP), AX // sec + MOVQ 16(SP), DX // nsec - // sec is in AX, usec in DX + // sec is in AX, nsec in DX MOVQ AX, sec+0(FP) - IMULQ $1000, DX MOVL DX, nsec+8(FP) RET TEXT runtime·nanotime(SB),7,$32 - LEAQ 8(SP), DI // arg 1 - tp - MOVQ $0, SI // arg 2 - tzp - MOVL $116, AX // sys_gettimeofday + MOVQ $0, DI // arg 1 - clock_id + LEAQ 8(SP), SI // arg 2 - tp + MOVL $232, AX // sys_clock_gettime SYSCALL - MOVQ 8(SP), AX // sec - MOVL 16(SP), DX // usec + MOVL 8(SP), AX // sec + MOVQ 16(SP), DX // nsec - // sec is in AX, usec in DX + // sec is in AX, nsec in DX // return nsec in AX IMULQ $1000000000, AX - IMULQ $1000, DX ADDQ DX, AX RET @@ -170,7 +163,17 @@ TEXT runtime·sigaction(SB),7,$-8 MOVL $46, AX SYSCALL JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·sigprocmask(SB),7,$0 + MOVL 8(SP), DI // arg 1 - how + MOVL 12(SP), SI // arg 2 - set + MOVL $48, AX // sys_sigprocmask + SYSCALL + JCC 2(PC) + MOVL $0xf1, 0xf1 // crash + MOVL AX, oset+0(FP) // Return oset RET TEXT runtime·sigtramp(SB),7,$64 @@ -179,8 +182,10 @@ TEXT runtime·sigtramp(SB),7,$64 // check that m exists MOVQ m(BX), BP CMPQ BP, $0 - JNE 2(PC) + JNE 4(PC) + MOVQ DI, 0(SP) CALL runtime·badsignal(SB) + RET // save g MOVQ g(BX), R10 @@ -226,7 +231,16 @@ TEXT runtime·munmap(SB),7,$0 MOVL $73, AX // sys_munmap SYSCALL JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash + RET + +TEXT runtime·madvise(SB),7,$0 + MOVQ addr+0(FP), DI // arg 1 - addr + MOVQ len+8(FP), SI // arg 2 - len + MOVQ behav+16(FP), DX // arg 3 - behav + MOVQ $75, AX // sys_madvise + SYSCALL + // ignore failure - maybe pages are locked RET TEXT runtime·sigaltstack(SB),7,$-8 @@ -235,20 +249,17 @@ TEXT runtime·sigaltstack(SB),7,$-8 MOVQ $288, AX // sys_sigaltstack SYSCALL JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET // set tls base to DI -TEXT runtime·settls(SB),7,$8 +TEXT runtime·settls(SB),7,$0 // adjust for ELF: wants to use -16(FS) and -8(FS) for g and m ADDQ $16, DI - MOVQ DI, 0(SP) - MOVQ SP, SI - MOVQ $12, DI // AMD64_SET_FSBASE (machine/sysarch.h) - MOVQ $165, AX // sys_sysarch + MOVQ $329, AX // sys___settcb SYSCALL JCC 2(PC) - MOVL $0xf1, 0xf1 // crash + MOVL $0xf1, 0xf1 // crash RET TEXT runtime·sysctl(SB),7,$0 @@ -260,7 +271,7 @@ TEXT runtime·sysctl(SB),7,$0 MOVQ 48(SP), R9 // arg 6 - newlen MOVQ $202, AX // sys___sysctl SYSCALL - JCC 3(PC) + JCC 3(PC) NEGL AX RET MOVL $0, AX diff --git a/src/pkg/runtime/sys_plan9_386.s b/src/pkg/runtime/sys_plan9_386.s index 94c36aa41..3385b083a 100644 --- a/src/pkg/runtime/sys_plan9_386.s +++ b/src/pkg/runtime/sys_plan9_386.s @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "defs_GOOS_GOARCH.h" #include "zasm_GOOS_GOARCH.h" // setldt(int entry, int address, int limit) @@ -24,9 +23,19 @@ TEXT runtime·pwrite(SB),7,$0 INT $64 RET +TEXT runtime·seek(SB),7,$0 + MOVL $39, AX + INT $64 + CMPL AX, $-1 + JNE 4(PC) + MOVL a+0(FP), CX + MOVL AX, 0(CX) + MOVL AX, 4(CX) + RET + TEXT runtime·close(SB),7,$0 MOVL $4, AX - INT $64 + INT $64 RET TEXT runtime·exits(SB),7,$0 @@ -48,6 +57,21 @@ TEXT runtime·plan9_semacquire(SB),7,$0 MOVL $37, AX INT $64 RET + +TEXT runtime·plan9_tsemacquire(SB),7,$0 + MOVL $52, AX + INT $64 + RET + +TEXT runtime·notify(SB),7,$0 + MOVL $28, AX + INT $64 + RET + +TEXT runtime·noted(SB),7,$0 + MOVL $29, AX + INT $64 + RET TEXT runtime·plan9_semrelease(SB),7,$0 MOVL $38, AX @@ -77,9 +101,8 @@ TEXT runtime·rfork(SB),7,$0 MOVL DX, g(AX) MOVL BX, m(AX) - // Initialize AX from _tos->pid - MOVL _tos(SB), AX - MOVL tos_pid(AX), AX + // Initialize AX from TOS struct. + MOVL procid(AX), AX MOVL AX, m_procid(BX) // save pid as m->procid CALL runtime·stackcheck(SB) // smashes AX, CX @@ -95,3 +118,55 @@ TEXT runtime·rfork(SB),7,$0 CALL SI // fn() CALL runtime·exit(SB) RET + +// void sigtramp(void *ureg, int8 *note) +TEXT runtime·sigtramp(SB),7,$0 + get_tls(AX) + + // check that m exists + MOVL m(AX), BX + CMPL BX, $0 + JNE 3(PC) + CALL runtime·badsignal(SB) // will exit + RET + + // save args + MOVL ureg+4(SP), CX + MOVL note+8(SP), DX + + // change stack + MOVL m_gsignal(BX), BP + MOVL g_stackbase(BP), BP + MOVL BP, SP + + // make room for args and g + SUBL $16, SP + + // save g + MOVL g(AX), BP + MOVL BP, 12(SP) + + // g = m->gsignal + MOVL m_gsignal(BX), DI + MOVL DI, g(AX) + + // load args and call sighandler + MOVL CX, 0(SP) + MOVL DX, 4(SP) + MOVL BP, 8(SP) + + CALL runtime·sighandler(SB) + + // restore g + get_tls(BX) + MOVL 12(SP), BP + MOVL BP, g(BX) + + // call noted(AX) + MOVL AX, 0(SP) + CALL runtime·noted(SB) + RET + +// Only used by the 64-bit runtime. +TEXT runtime·setfpmasks(SB),7,$0 + RET diff --git a/src/pkg/runtime/sys_plan9_amd64.s b/src/pkg/runtime/sys_plan9_amd64.s new file mode 100644 index 000000000..b34f98a68 --- /dev/null +++ b/src/pkg/runtime/sys_plan9_amd64.s @@ -0,0 +1,208 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "zasm_GOOS_GOARCH.h" + +// setldt(int entry, int address, int limit) +TEXT runtime·setldt(SB),7,$0 + RET + +TEXT runtime·open(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $14, BP + SYSCALL + RET + +TEXT runtime·pread(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $50, BP + SYSCALL + RET + +TEXT runtime·pwrite(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $51, BP + SYSCALL + RET + +// int32 _seek(int64*, int32, int64, int32) +TEXT _seek<>(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $39, BP + SYSCALL + RET + +// int64 seek(int32, int64, int32) +TEXT runtime·seek(SB),7,$56 + LEAQ new+48(SP), CX + MOVQ CX, 0(SP) + MOVQ fd+0(FP), CX + MOVQ CX, 8(SP) + MOVQ off+8(FP), CX + MOVQ CX, 16(SP) + MOVQ whence+16(FP), CX + MOVQ CX, 24(SP) + CALL _seek<>(SB) + CMPL AX, $0 + JGE 2(PC) + MOVQ $-1, new+48(SP) + MOVQ new+48(SP), AX + RET + +TEXT runtime·close(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $4, BP + SYSCALL + RET + +TEXT runtime·exits(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $8, BP + SYSCALL + RET + +TEXT runtime·brk_(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $24, BP + SYSCALL + RET + +TEXT runtime·sleep(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $17, BP + SYSCALL + RET + +TEXT runtime·plan9_semacquire(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $37, BP + SYSCALL + RET + +TEXT runtime·plan9_tsemacquire(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $52, BP + SYSCALL + RET + +TEXT runtime·notify(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $28, BP + SYSCALL + RET + +TEXT runtime·noted(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $29, BP + SYSCALL + RET + +TEXT runtime·plan9_semrelease(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $38, BP + SYSCALL + RET + +TEXT runtime·nanotime(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $60, BP + SYSCALL + RET + +TEXT runtime·rfork(SB),7,$0 + MOVQ $0x8000, AX + MOVQ $19, BP // rfork + SYSCALL + + // In parent, return. + CMPQ AX, $0 + JEQ 2(PC) + RET + + // In child on forked stack. + MOVQ mm+24(SP), BX // m + MOVQ gg+32(SP), DX // g + MOVQ fn+40(SP), SI // fn + + // set SP to be on the new child stack + MOVQ stack+16(SP), CX + MOVQ CX, SP + + // Initialize m, g. + get_tls(AX) + MOVQ DX, g(AX) + MOVQ BX, m(AX) + + // Initialize AX from pid in TLS. + MOVQ procid(AX), AX + MOVQ AX, m_procid(BX) // save pid as m->procid + + CALL runtime·stackcheck(SB) // smashes AX, CX + + MOVQ 0(DX), DX // paranoia; check they are not nil + MOVQ 0(BX), BX + + CALL SI // fn() + CALL runtime·exit(SB) + RET + +// This is needed by asm_amd64.s +TEXT runtime·settls(SB),7,$0 + RET + +// void sigtramp(void *ureg, int8 *note) +TEXT runtime·sigtramp(SB),7,$0 + get_tls(AX) + + // check that m exists + MOVQ m(AX), BX + CMPQ BX, $0 + JNE 3(PC) + CALL runtime·badsignal(SB) // will exit + RET + + // save args + MOVQ ureg+8(SP), CX + MOVQ note+16(SP), DX + + // change stack + MOVQ m_gsignal(BX), R10 + MOVQ g_stackbase(R10), BP + MOVQ BP, SP + + // make room for args and g + SUBQ $32, SP + + // save g + MOVQ g(AX), BP + MOVQ BP, 24(SP) + + // g = m->gsignal + MOVQ R10, g(AX) + + // load args and call sighandler + MOVQ CX, 0(SP) + MOVQ DX, 8(SP) + MOVQ BP, 16(SP) + + CALL runtime·sighandler(SB) + + // restore g + get_tls(BX) + MOVQ 24(SP), R10 + MOVQ R10, g(BX) + + // call noted(AX) + MOVQ AX, 0(SP) + CALL runtime·noted(SB) + RET + +TEXT runtime·setfpmasks(SB),7,$8 + STMXCSR 0(SP) + MOVL 0(SP), AX + ANDL $~0x3F, AX + ORL $(0x3F<<7), AX + MOVL AX, 0(SP) + LDMXCSR 0(SP) + RET diff --git a/src/pkg/runtime/sys_windows_386.s b/src/pkg/runtime/sys_windows_386.s index d5646bfea..ca59f0a1d 100644 --- a/src/pkg/runtime/sys_windows_386.s +++ b/src/pkg/runtime/sys_windows_386.s @@ -216,7 +216,7 @@ TEXT runtime·callbackasm+0(SB),7,$0 CLD - CALL runtime·cgocallback(SB) + CALL runtime·cgocallback_gofunc(SB) POPL AX POPL CX @@ -243,11 +243,6 @@ TEXT runtime·tstart(SB),7,$0 MOVL newm+4(SP), CX // m MOVL m_g0(CX), DX // g - // Set up SEH frame - PUSHL $runtime·sigtramp(SB) - PUSHL 0(FS) - MOVL SP, 0(FS) - // Layout new m scheduler stack on os stack. MOVL SP, AX MOVL AX, g_stackbase(DX) @@ -264,14 +259,8 @@ TEXT runtime·tstart(SB),7,$0 CLD CALL runtime·stackcheck(SB) // clobbers AX,CX - CALL runtime·mstart(SB) - // Pop SEH frame - MOVL 0(FS), SP - POPL 0(FS) - POPL CX - RET // uint32 tstart_stdcall(M *newm); @@ -296,3 +285,32 @@ TEXT runtime·setldt(SB),7,$0 MOVL address+4(FP), CX MOVL CX, 0x14(FS) RET + +// void install_exception_handler() +TEXT runtime·install_exception_handler(SB),7,$0 + get_tls(CX) + MOVL m(CX), CX // m + + // Set up SEH frame + MOVL m_seh(CX), DX + MOVL $runtime·sigtramp(SB), AX + MOVL AX, seh_handler(DX) + MOVL 0(FS), AX + MOVL AX, seh_prev(DX) + + // Install it + MOVL DX, 0(FS) + + RET + +// void remove_exception_handler() +TEXT runtime·remove_exception_handler(SB),7,$0 + get_tls(CX) + MOVL m(CX), CX // m + + // Remove SEH frame + MOVL m_seh(CX), DX + MOVL seh_prev(DX), AX + MOVL AX, 0(FS) + + RET diff --git a/src/pkg/runtime/sys_windows_amd64.s b/src/pkg/runtime/sys_windows_amd64.s index 11909cda2..fe88f3b75 100644 --- a/src/pkg/runtime/sys_windows_amd64.s +++ b/src/pkg/runtime/sys_windows_amd64.s @@ -272,13 +272,13 @@ TEXT runtime·callbackasm(SB),7,$0 MOVQ R15, 0(SP) // prepare call stack. use SUBQ to hide from stack frame checks - // cgocallback(void (*fn)(void*), void *frame, uintptr framesize) + // cgocallback(Go func, void *frame, uintptr framesize) SUBQ $24, SP MOVQ DX, 16(SP) // uintptr framesize MOVQ CX, 8(SP) // void *frame - MOVQ AX, 0(SP) // void (*fn)(void*) + MOVQ AX, 0(SP) // Go func CLD - CALL runtime·cgocallback(SB) + CALL runtime·cgocallback_gofunc(SB) MOVQ 0(SP), AX MOVQ 8(SP), CX MOVQ 16(SP), DX @@ -328,7 +328,6 @@ TEXT runtime·tstart_stdcall(SB),7,$0 // Someday the convention will be D is always cleared. CLD - CALL runtime·setstacklimits(SB) CALL runtime·stackcheck(SB) // clobbers AX,CX CALL runtime·mstart(SB) @@ -337,6 +336,13 @@ TEXT runtime·tstart_stdcall(SB),7,$0 // set tls base to DI TEXT runtime·settls(SB),7,$0 - CALL runtime·setstacklimits(SB) MOVQ DI, 0x28(GS) RET + +// void install_exception_handler() +TEXT runtime·install_exception_handler(SB),7,$0 + CALL runtime·setstacklimits(SB) + RET + +TEXT runtime·remove_exception_handler(SB),7,$0 + RET diff --git a/src/pkg/runtime/syscall_windows_test.go b/src/pkg/runtime/syscall_windows_test.go index c8327fdef..f04d2cd54 100644 --- a/src/pkg/runtime/syscall_windows_test.go +++ b/src/pkg/runtime/syscall_windows_test.go @@ -112,9 +112,10 @@ func Test64BitReturnStdCall(t *testing.T) { func TestCDecl(t *testing.T) { var buf [50]byte + fmtp, _ := syscall.BytePtrFromString("%d %d %d") a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call( uintptr(unsafe.Pointer(&buf[0])), - uintptr(unsafe.Pointer(syscall.StringBytePtr("%d %d %d"))), + uintptr(unsafe.Pointer(fmtp)), 1000, 2000, 3000) if string(buf[:a]) != "1000 2000 3000" { t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a]) diff --git a/src/pkg/runtime/thread_darwin.c b/src/pkg/runtime/thread_darwin.c index 6a83e48a3..adb1ffe6a 100644 --- a/src/pkg/runtime/thread_darwin.c +++ b/src/pkg/runtime/thread_darwin.c @@ -9,8 +9,8 @@ extern SigTab runtime·sigtab[]; -static Sigset sigset_all = ~(Sigset)0; static Sigset sigset_none; +static Sigset sigset_all = ~(Sigset)0; static Sigset sigset_prof = 1<<(SIGPROF-1); static void @@ -50,11 +50,8 @@ runtime·semacreate(void) void runtime·osinit(void) { - // Register our thread-creation callback (see sys_darwin_{amd64,386}.s) - // but only if we're not using cgo. If we are using cgo we need - // to let the C pthread libary install its own thread-creation callback. - if(!runtime·iscgo) - runtime·bsdthread_register(); + // bsdthread_register delayed until end of goenvs so that we + // can look at the environment first. // Use sysctl to fetch hw.ncpu. uint32 mib[2]; @@ -75,22 +72,34 @@ void runtime·goenvs(void) { runtime·goenvs_unix(); + + // Register our thread-creation callback (see sys_darwin_{amd64,386}.s) + // but only if we're not using cgo. If we are using cgo we need + // to let the C pthread libary install its own thread-creation callback. + if(!runtime·iscgo) { + if(runtime·bsdthread_register() != 0) { + if(runtime·getenv("DYLD_INSERT_LIBRARIES")) + runtime·throw("runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)"); + runtime·throw("runtime: bsdthread_register error"); + } + } + } void -runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) +runtime·newosproc(M *mp, void *stk) { int32 errno; Sigset oset; - m->tls[0] = m->id; // so 386 asm can find it + mp->tls[0] = mp->id; // so 386 asm can find it if(0){ - runtime·printf("newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n", - stk, m, g, fn, m->id, m->tls[0], &m); + runtime·printf("newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n", + stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp); } runtime·sigprocmask(SIG_SETMASK, &sigset_all, &oset); - errno = runtime·bsdthread_create(stk, m, g, fn); + errno = runtime·bsdthread_create(stk, mp, mp->g0, runtime·mstart); runtime·sigprocmask(SIG_SETMASK, &oset, nil); if(errno < 0) { @@ -100,17 +109,30 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) } // Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +void +runtime·mpreinit(M *mp) +{ + mp->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, can not allocate memory. void runtime·minit(void) { // Initialize signal handling. - m->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K - runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024); + runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024); - if(m->profilehz > 0) - runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil); - else - runtime·sigprocmask(SIG_SETMASK, &sigset_prof, nil); + runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil); + runtime·setprof(m->profilehz > 0); +} + +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); } // Mach IPC, to get at semaphores @@ -431,10 +453,11 @@ runtime·sigpanic(void) runtime·panicstring(runtime·sigtab[g->sig].name); } -// TODO(rsc): place holder to fix build. +#pragma textflag 7 void runtime·osyield(void) { + runtime·usleep(1); } uintptr @@ -488,12 +511,22 @@ runtime·badcallback(void) runtime·write(2, badcallback, sizeof badcallback - 1); } -static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n"; +static int8 badsignal[] = "runtime: signal received on thread not created by Go: "; // This runs on a foreign stack, without an m or a g. No stack split. #pragma textflag 7 void -runtime·badsignal(void) +runtime·badsignal(int32 sig) { + if (sig == SIGPROF) { + return; // Ignore SIGPROFs intended for a non-Go thread. + } runtime·write(2, badsignal, sizeof badsignal - 1); + if (0 <= sig && sig < NSIG) { + // Call runtime·findnull dynamically to circumvent static stack size check. + static int32 (*findnull)(byte*) = runtime·findnull; + runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name)); + } + runtime·write(2, "\n", 1); + runtime·exit(1); } diff --git a/src/pkg/runtime/thread_freebsd.c b/src/pkg/runtime/thread_freebsd.c index 4c546178f..3ae14ee0a 100644 --- a/src/pkg/runtime/thread_freebsd.c +++ b/src/pkg/runtime/thread_freebsd.c @@ -13,8 +13,8 @@ extern int32 runtime·sys_umtx_op(uint32*, int32, uint32, void*, void*); #define CTL_HW 6 #define HW_NCPU 3 +static Sigset sigset_none; static Sigset sigset_all = { ~(uint32)0, ~(uint32)0, ~(uint32)0, ~(uint32)0, }; -static Sigset sigset_none = { 0, 0, 0, 0, }; static int32 getncpu(void) @@ -77,32 +77,33 @@ runtime·futexwakeup(uint32 *addr, uint32 cnt) void runtime·thr_start(void*); void -runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) +runtime·newosproc(M *mp, void *stk) { ThrParam param; Sigset oset; - USED(fn); // thr_start assumes fn == mstart - USED(g); // thr_start assumes g == m->g0 - if(0){ - runtime·printf("newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n", - stk, m, g, fn, m->id, m->tls[0], &m); + runtime·printf("newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n", + stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp); } runtime·sigprocmask(&sigset_all, &oset); runtime·memclr((byte*)¶m, sizeof param); param.start_func = runtime·thr_start; - param.arg = m; - param.stack_base = (int8*)g->stackbase; - param.stack_size = (byte*)stk - (byte*)g->stackbase; - param.child_tid = (intptr*)&m->procid; + param.arg = (byte*)mp; + + // NOTE(rsc): This code is confused. stackbase is the top of the stack + // and is equal to stk. However, it's working, so I'm not changing it. + param.stack_base = (void*)mp->g0->stackbase; + param.stack_size = (byte*)stk - (byte*)mp->g0->stackbase; + + param.child_tid = (intptr*)&mp->procid; param.parent_tid = nil; - param.tls_base = (int8*)&m->tls[0]; - param.tls_size = sizeof m->tls; + param.tls_base = (void*)&mp->tls[0]; + param.tls_size = sizeof mp->tls; - m->tls[0] = m->id; // so 386 asm can find it + mp->tls[0] = mp->id; // so 386 asm can find it runtime·thr_new(¶m, sizeof param); runtime·sigprocmask(&oset, nil); @@ -121,15 +122,30 @@ runtime·goenvs(void) } // Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +void +runtime·mpreinit(M *mp) +{ + mp->gsignal = runtime·malg(32*1024); +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, can not allocate memory. void runtime·minit(void) { // Initialize signal handling - m->gsignal = runtime·malg(32*1024); - runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024); + runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024); runtime·sigprocmask(&sigset_none, nil); } +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); +} + void runtime·sigpanic(void) { @@ -206,12 +222,22 @@ runtime·badcallback(void) runtime·write(2, badcallback, sizeof badcallback - 1); } -static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n"; +static int8 badsignal[] = "runtime: signal received on thread not created by Go: "; // This runs on a foreign stack, without an m or a g. No stack split. #pragma textflag 7 void -runtime·badsignal(void) +runtime·badsignal(int32 sig) { + if (sig == SIGPROF) { + return; // Ignore SIGPROFs intended for a non-Go thread. + } runtime·write(2, badsignal, sizeof badsignal - 1); + if (0 <= sig && sig < NSIG) { + // Call runtime·findnull dynamically to circumvent static stack size check. + static int32 (*findnull)(byte*) = runtime·findnull; + runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name)); + } + runtime·write(2, "\n", 1); + runtime·exit(1); } diff --git a/src/pkg/runtime/thread_linux.c b/src/pkg/runtime/thread_linux.c index 858be7036..78ddef878 100644 --- a/src/pkg/runtime/thread_linux.c +++ b/src/pkg/runtime/thread_linux.c @@ -13,8 +13,8 @@ int32 runtime·open(uint8*, int32, int32); int32 runtime·close(int32); int32 runtime·read(int32, void*, int32); -static Sigset sigset_all = { ~(uint32)0, ~(uint32)0 }; static Sigset sigset_none; +static Sigset sigset_all = { ~(uint32)0, ~(uint32)0 }; // Linux futex. // @@ -80,33 +80,23 @@ runtime·futexwakeup(uint32 *addr, uint32 cnt) *(int32*)0x1006 = 0x1006; } +extern runtime·sched_getaffinity(uintptr pid, uintptr len, uintptr *buf); static int32 getproccount(void) { - int32 fd, rd, cnt, cpustrlen; - byte *cpustr, *pos, *bufpos; - byte buf[256]; + uintptr buf[16], t; + int32 r, cnt, i; - fd = runtime·open((byte*)"/proc/stat", O_RDONLY|O_CLOEXEC, 0); - if(fd == -1) - return 1; cnt = 0; - bufpos = buf; - cpustr = (byte*)"\ncpu"; - cpustrlen = runtime·findnull(cpustr); - for(;;) { - rd = runtime·read(fd, bufpos, sizeof(buf)-cpustrlen); - if(rd == -1) - break; - bufpos[rd] = 0; - for(pos=buf; pos=runtime·strstr(pos, cpustr); cnt++, pos++) { - } - if(rd < cpustrlen) - break; - runtime·memmove(buf, bufpos+rd-cpustrlen+1, cpustrlen-1); - bufpos = buf+cpustrlen-1; + r = runtime·sched_getaffinity(0, sizeof(buf), buf); + if(r > 0) + for(i = 0; i < r/sizeof(buf[0]); i++) { + t = buf[i]; + t = t - ((t >> 1) & 0x5555555555555555ULL); + t = (t & 0x3333333333333333ULL) + ((t >> 2) & 0x3333333333333333ULL); + cnt += (int32)((((t + (t >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56); } - runtime·close(fd); + return cnt ? cnt : 1; } @@ -134,7 +124,7 @@ enum }; void -runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) +runtime·newosproc(M *mp, void *stk) { int32 ret; int32 flags; @@ -150,16 +140,16 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) | CLONE_THREAD /* revisit - okay for now */ ; - m->tls[0] = m->id; // so 386 asm can find it + mp->tls[0] = mp->id; // so 386 asm can find it if(0){ - runtime·printf("newosproc stk=%p m=%p g=%p fn=%p clone=%p id=%d/%d ostk=%p\n", - stk, m, g, fn, runtime·clone, m->id, m->tls[0], &m); + runtime·printf("newosproc stk=%p m=%p g=%p clone=%p id=%d/%d ostk=%p\n", + stk, mp, mp->g0, runtime·clone, mp->id, (int32)mp->tls[0], &mp); } // Disable signals during clone, so that the new thread starts // with signals disabled. It will enable them in minit. runtime·rtsigprocmask(SIG_SETMASK, &sigset_all, &oset, sizeof oset); - ret = runtime·clone(flags, stk, m, g, fn); + ret = runtime·clone(flags, stk, mp, mp->g0, runtime·mstart); runtime·rtsigprocmask(SIG_SETMASK, &oset, nil, sizeof oset); if(ret < 0) { @@ -181,13 +171,28 @@ runtime·goenvs(void) } // Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +void +runtime·mpreinit(M *mp) +{ + mp->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, can not allocate memory. void runtime·minit(void) { // Initialize signal handling. - m->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K - runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024); - runtime·rtsigprocmask(SIG_SETMASK, &sigset_none, nil, sizeof sigset_none); + runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024); + runtime·rtsigprocmask(SIG_SETMASK, &sigset_none, nil, sizeof(Sigset)); +} + +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); } void @@ -266,12 +271,22 @@ runtime·badcallback(void) runtime·write(2, badcallback, sizeof badcallback - 1); } -static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n"; +static int8 badsignal[] = "runtime: signal received on thread not created by Go: "; // This runs on a foreign stack, without an m or a g. No stack split. #pragma textflag 7 void -runtime·badsignal(void) +runtime·badsignal(int32 sig) { + if (sig == SIGPROF) { + return; // Ignore SIGPROFs intended for a non-Go thread. + } runtime·write(2, badsignal, sizeof badsignal - 1); + if (0 <= sig && sig < NSIG) { + // Call runtime·findnull dynamically to circumvent static stack size check. + static int32 (*findnull)(byte*) = runtime·findnull; + runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name)); + } + runtime·write(2, "\n", 1); + runtime·exit(1); } diff --git a/src/pkg/runtime/thread_netbsd.c b/src/pkg/runtime/thread_netbsd.c index 1b2df85cd..f333c6dd8 100644 --- a/src/pkg/runtime/thread_netbsd.c +++ b/src/pkg/runtime/thread_netbsd.c @@ -11,7 +11,7 @@ enum ESRCH = 3, ENOTSUP = 91, - // From NetBSD's sys/time.h + // From NetBSD's CLOCK_REALTIME = 0, CLOCK_VIRTUAL = 1, CLOCK_PROF = 2, @@ -20,9 +20,15 @@ enum extern SigTab runtime·sigtab[]; -extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); -extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock); -extern int32 runtime·thrwakeup(void *ident, int32 n); +static Sigset sigset_none; +static Sigset sigset_all = { ~(uint32)0, ~(uint32)0, ~(uint32)0, ~(uint32)0, }; + +extern void runtime·getcontext(UcontextT *context); +extern int32 runtime·lwp_create(UcontextT *context, uintptr flags, void *lwpid); +extern void runtime·lwp_mcontext_init(void *mc, void *stack, M *mp, G *gp, void (*fn)(void)); +extern int32 runtime·lwp_park(Timespec *abstime, int32 unpark, void *hint, void *unparkhint); +extern int32 runtime·lwp_unpark(int32 lwp, void *hint); +extern int32 runtime·lwp_self(void); // From NetBSD's #define CTL_HW 6 @@ -68,13 +74,30 @@ runtime·semasleep(int64 ns) if(m->waitsemacount == 0) { // sleep until semaphore != 0 or timeout. // thrsleep unlocks m->waitsemalock. - if(ns < 0) - runtime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock); - else { + if(ns < 0) { + // TODO(jsing) - potential deadlock! + // + // There is a potential deadlock here since we + // have to release the waitsemalock mutex + // before we call lwp_park() to suspend the + // thread. This allows another thread to + // release the lock and call lwp_unpark() + // before the thread is actually suspended. + // If this occurs the current thread will end + // up sleeping indefinitely. Unfortunately + // the NetBSD kernel does not appear to provide + // a mechanism for unlocking the userspace + // mutex once the thread is actually parked. + runtime·atomicstore(&m->waitsemalock, 0); + runtime·lwp_park(nil, 0, &m->waitsemacount, nil); + } else { ns += runtime·nanotime(); ts.tv_sec = ns/1000000000LL; ts.tv_nsec = ns%1000000000LL; - runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock); + // TODO(jsing) - potential deadlock! + // See above for details. + runtime·atomicstore(&m->waitsemalock, 0); + runtime·lwp_park(&ts, 0, &m->waitsemacount, nil); } // reacquire lock while(runtime·xchg(&m->waitsemalock, 1)) @@ -112,39 +135,41 @@ runtime·semawakeup(M *mp) while(runtime·xchg(&mp->waitsemalock, 1)) runtime·osyield(); mp->waitsemacount++; - ret = runtime·thrwakeup(&mp->waitsemacount, 1); + // TODO(jsing) - potential deadlock, see semasleep() for details. + // Confirm that LWP is parked before unparking... + ret = runtime·lwp_unpark(mp->procid, &mp->waitsemacount); if(ret != 0 && ret != ESRCH) runtime·printf("thrwakeup addr=%p sem=%d ret=%d\n", &mp->waitsemacount, mp->waitsemacount, ret); // spin-mutex unlock runtime·atomicstore(&mp->waitsemalock, 0); } -// From NetBSD's sys/param.h -#define RFPROC (1<<4) /* change child (else changes curproc) */ -#define RFMEM (1<<5) /* share `address space' */ -#define RFNOWAIT (1<<6) /* parent need not wait() on child */ -#define RFTHREAD (1<<13) /* create a thread, not a process */ - void -runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) +runtime·newosproc(M *mp, void *stk) { - int32 flags; + UcontextT uc; int32 ret; - flags = RFPROC | RFTHREAD | RFMEM | RFNOWAIT; - - if (0) { + if(0) { runtime·printf( - "newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n", - stk, m, g, fn, m->id, m->tls[0], &m); + "newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n", + stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp); } - m->tls[0] = m->id; // so 386 asm can find it + mp->tls[0] = mp->id; // so 386 asm can find it + + runtime·getcontext(&uc); + + uc.uc_flags = _UC_SIGMASK | _UC_CPU; + uc.uc_link = nil; + uc.uc_sigmask = sigset_all; + + runtime·lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp->g0, runtime·mstart); - if((ret = runtime·rfork_thread(flags, stk, m, g, fn)) < 0) { + ret = runtime·lwp_create(&uc, 0, &mp->procid); + + if(ret < 0) { runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount() - 1, -ret); - if (ret == -ENOTSUP) - runtime·printf("runtime: is kern.rthreads disabled?\n"); runtime·throw("runtime.newosproc"); } } @@ -162,12 +187,30 @@ runtime·goenvs(void) } // Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +void +runtime·mpreinit(M *mp) +{ + mp->gsignal = runtime·malg(32*1024); +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, can not allocate memory. void runtime·minit(void) { + m->procid = runtime·lwp_self(); + // Initialize signal handling - m->gsignal = runtime·malg(32*1024); - runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024); + runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024); + runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil); +} + +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); } void @@ -224,12 +267,22 @@ runtime·badcallback(void) runtime·write(2, badcallback, sizeof badcallback - 1); } -static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n"; +static int8 badsignal[] = "runtime: signal received on thread not created by Go: "; // This runs on a foreign stack, without an m or a g. No stack split. #pragma textflag 7 void -runtime·badsignal(void) +runtime·badsignal(int32 sig) { + if (sig == SIGPROF) { + return; // Ignore SIGPROFs intended for a non-Go thread. + } runtime·write(2, badsignal, sizeof badsignal - 1); + if (0 <= sig && sig < NSIG) { + // Call runtime·findnull dynamically to circumvent static stack size check. + static int32 (*findnull)(byte*) = runtime·findnull; + runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name)); + } + runtime·write(2, "\n", 1); + runtime·exit(1); } diff --git a/src/pkg/runtime/thread_openbsd.c b/src/pkg/runtime/thread_openbsd.c index d0f947210..700c48147 100644 --- a/src/pkg/runtime/thread_openbsd.c +++ b/src/pkg/runtime/thread_openbsd.c @@ -20,8 +20,11 @@ enum extern SigTab runtime·sigtab[]; -extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); -extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock); +static Sigset sigset_none; +static Sigset sigset_all = ~(Sigset)0; + +extern int64 runtime·tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void)); +extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock, const int32 *abort); extern int32 runtime·thrwakeup(void *ident, int32 n); // From OpenBSD's @@ -69,12 +72,12 @@ runtime·semasleep(int64 ns) // sleep until semaphore != 0 or timeout. // thrsleep unlocks m->waitsemalock. if(ns < 0) - runtime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock); + runtime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock, nil); else { ns += runtime·nanotime(); ts.tv_sec = ns/1000000000LL; ts.tv_nsec = ns%1000000000LL; - runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock); + runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock, nil); } // reacquire lock while(runtime·xchg(&m->waitsemalock, 1)) @@ -119,29 +122,30 @@ runtime·semawakeup(M *mp) runtime·atomicstore(&mp->waitsemalock, 0); } -// From OpenBSD's sys/param.h -#define RFPROC (1<<4) /* change child (else changes curproc) */ -#define RFMEM (1<<5) /* share `address space' */ -#define RFNOWAIT (1<<6) /* parent need not wait() on child */ -#define RFTHREAD (1<<13) /* create a thread, not a process */ - void -runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) +runtime·newosproc(M *mp, void *stk) { - int32 flags; + Tfork param; + Sigset oset; int32 ret; - flags = RFPROC | RFTHREAD | RFMEM | RFNOWAIT; - - if (0) { + if(0) { runtime·printf( - "newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n", - stk, m, g, fn, m->id, m->tls[0], &m); + "newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n", + stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp); } - m->tls[0] = m->id; // so 386 asm can find it + mp->tls[0] = mp->id; // so 386 asm can find it + + param.tf_tcb = (byte*)&mp->tls[0]; + param.tf_tid = (int32*)&mp->procid; + param.tf_stack = stk; - if((ret = runtime·rfork_thread(flags, stk, m, g, fn)) < 0) { + oset = runtime·sigprocmask(SIG_SETMASK, sigset_all); + ret = runtime·tfork((byte*)¶m, sizeof(param), mp, mp->g0, runtime·mstart); + runtime·sigprocmask(SIG_SETMASK, oset); + + if(ret < 0) { runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount() - 1, -ret); if (ret == -ENOTSUP) runtime·printf("runtime: is kern.rthreads disabled?\n"); @@ -162,12 +166,28 @@ runtime·goenvs(void) } // Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +void +runtime·mpreinit(M *mp) +{ + mp->gsignal = runtime·malg(32*1024); +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, can not allocate memory. void runtime·minit(void) { // Initialize signal handling - m->gsignal = runtime·malg(32*1024); - runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024); + runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024); + runtime·sigprocmask(SIG_SETMASK, sigset_none); +} + +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·signalstack(nil, 0); } void @@ -224,12 +244,22 @@ runtime·badcallback(void) runtime·write(2, badcallback, sizeof badcallback - 1); } -static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n"; +static int8 badsignal[] = "runtime: signal received on thread not created by Go: "; // This runs on a foreign stack, without an m or a g. No stack split. #pragma textflag 7 void -runtime·badsignal(void) +runtime·badsignal(int32 sig) { + if (sig == SIGPROF) { + return; // Ignore SIGPROFs intended for a non-Go thread. + } runtime·write(2, badsignal, sizeof badsignal - 1); + if (0 <= sig && sig < NSIG) { + // Call runtime·findnull dynamically to circumvent static stack size check. + static int32 (*findnull)(byte*) = runtime·findnull; + runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name)); + } + runtime·write(2, "\n", 1); + runtime·exit(1); } diff --git a/src/pkg/runtime/thread_plan9.c b/src/pkg/runtime/thread_plan9.c index 3b0dca69f..7f94623e7 100644 --- a/src/pkg/runtime/thread_plan9.c +++ b/src/pkg/runtime/thread_plan9.c @@ -7,12 +7,37 @@ #include "arch_GOARCH.h" int8 *goos = "plan9"; +extern SigTab runtime·sigtab[]; +int32 runtime·postnote(int32, int8*); + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +void +runtime·mpreinit(M *mp) +{ + // Initialize stack and goroutine for note handling. + mp->gsignal = runtime·malg(32*1024); + mp->notesig = (int8*)runtime·malloc(ERRMAX*sizeof(int8)); +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, can not allocate memory. void runtime·minit(void) +{ + // Mask all SSE floating-point exceptions + // when running on the 64-bit kernel. + runtime·setfpmasks(); +} + +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) { } + static int32 getproccount(void) { @@ -36,10 +61,30 @@ getproccount(void) return ncpu > 0 ? ncpu : 1; } +static int32 +getpid(void) +{ + byte b[20], *c; + int32 fd; + + runtime·memclr(b, sizeof(b)); + fd = runtime·open((byte*)"#c/pid", 0); + if(fd >= 0) { + runtime·read(fd, b, sizeof(b)); + runtime·close(fd); + } + c = b; + while(*c == ' ' || *c == '\t') + c++; + return runtime·atoi(c); +} + void runtime·osinit(void) { runtime·ncpu = getproccount(); + m->procid = getpid(); + runtime·notify(runtime·sigtramp); } void @@ -52,6 +97,7 @@ runtime·initsig(void) { } +#pragma textflag 7 void runtime·osyield(void) { @@ -69,34 +115,6 @@ runtime·usleep(uint32 µs) runtime·sleep(ms); } -int64 -runtime·nanotime(void) -{ - static int32 fd = -1; - byte b[8]; - uint32 hi, lo; - - // As long as all goroutines share the same file - // descriptor table we can get away with using - // just a static fd. Without a lock the file can - // be opened twice but that's okay. - // - // Using /dev/bintime gives us a latency on the - // order of ten microseconds between two calls. - // - // The naïve implementation (without the cached - // file descriptor) is roughly four times slower - // in 9vx on a 2.16 GHz Intel Core 2 Duo. - - if(fd < 0 && (fd = runtime·open((byte*)"/dev/bintime", OREAD|OCEXEC)) < 0) - return 0; - if(runtime·pread(fd, b, sizeof b, 0) != sizeof b) - return 0; - hi = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; - lo = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7]; - return (int64)hi<<32 | (int64)lo; -} - void time·now(int64 sec, int32 nsec) { @@ -109,48 +127,111 @@ time·now(int64 sec, int32 nsec) FLUSH(&nsec); } -extern Tos *_tos; void -runtime·exit(int32) +runtime·itoa(int32 n, byte *p, uint32 len) { - int32 fd; + byte *q, c; + uint32 i; + + if(len <= 1) + return; + + runtime·memclr(p, len); + q = p; + + if(n==0) { + *q++ = '0'; + USED(q); + return; + } + if(n < 0) { + *q++ = '-'; + p++; + n = -n; + } + for(i=0; n > 0 && i < len; i++) { + *q++ = '0' + (n%10); + n = n/10; + } + for(q--; q >= p; ) { + c = *p; + *p++ = *q; + *q-- = c; + } +} + +void +runtime·goexitsall(int8 *status) +{ + M *mp; + int32 pid; + + pid = getpid(); + for(mp=runtime·atomicloadp(&runtime·allm); mp; mp=mp->alllink) + if(mp->procid != pid) + runtime·postnote(mp->procid, status); +} + +int32 +runtime·postnote(int32 pid, int8* msg) +{ + int32 fd, len; uint8 buf[128]; uint8 tmp[16]; uint8 *p, *q; - int32 pid; runtime·memclr(buf, sizeof buf); - runtime·memclr(tmp, sizeof tmp); - pid = _tos->pid; - /* build path string /proc/pid/notepg */ - for(q=tmp; pid > 0;) { - *q++ = '0' + (pid%10); - pid = pid/10; - } + /* build path string /proc/pid/note */ + q = tmp; p = buf; + runtime·itoa(pid, tmp, sizeof tmp); runtime·memmove((void*)p, (void*)"/proc/", 6); - p += 6; - for(q--; q >= tmp;) - *p++ = *q--; - runtime·memmove((void*)p, (void*)"/notepg", 7); + for(p += 6; *p++ = *q++; ); + p--; + runtime·memmove((void*)p, (void*)"/note", 5); - /* post interrupt note */ fd = runtime·open(buf, OWRITE); - runtime·write(fd, "interrupt", 9); - runtime·exits(nil); + if(fd < 0) + return -1; + + len = runtime·findnull((byte*)msg); + if(runtime·write(fd, msg, len) != len) { + runtime·close(fd); + return -1; + } + runtime·close(fd); + return 0; } void -runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) +runtime·exit(int32 e) { - m->tls[0] = m->id; // so 386 asm can find it + byte tmp[16]; + int8 *status; + + if(e == 0) + status = ""; + else { + /* build error string */ + runtime·itoa(e, tmp, sizeof tmp); + status = (int8*)tmp; + } + + runtime·goexitsall(status); + runtime·exits(status); +} + +void +runtime·newosproc(M *mp, void *stk) +{ + mp->tls[0] = mp->id; // so 386 asm can find it if(0){ - runtime·printf("newosproc stk=%p m=%p g=%p fn=%p rfork=%p id=%d/%d ostk=%p\n", - stk, m, g, fn, runtime·rfork, m->id, m->tls[0], &m); + runtime·printf("newosproc stk=%p m=%p g=%p rfork=%p id=%d/%d ostk=%p\n", + stk, mp, mp->g0, runtime·rfork, mp->id, (int32)mp->tls[0], &mp); } - if(runtime·rfork(RFPROC|RFMEM|RFNOWAIT, stk, m, g, fn) < 0) + if(runtime·rfork(RFPROC|RFMEM|RFNOWAIT, stk, mp, mp->g0, runtime·mstart) < 0) runtime·throw("newosproc: rfork failed"); } @@ -167,36 +248,18 @@ runtime·semasleep(int64 ns) int32 ms; if(ns >= 0) { - // TODO: Plan 9 needs a new system call, tsemacquire. - // The kernel implementation is the same as semacquire - // except with a tsleep and check for timeout. - // It would be great if the implementation returned the - // value that was added to the semaphore, so that on - // timeout the return value would be 0, on success 1. - // Then the error string does not have to be parsed - // to detect timeout. - // - // If a negative time indicates no timeout, then - // semacquire can be implemented (in the kernel) - // as tsemacquire(p, v, -1). - runtime·throw("semasleep: timed sleep not implemented on Plan 9"); - - /* - if(ns < 0) - ms = -1; - else if(ns/1000 > 0x7fffffffll) + if(ns/1000000 > 0x7fffffffll) ms = 0x7fffffff; else - ms = ns/1000; - ret = runtime·plan9_tsemacquire(&m->waitsemacount, 1, ms); + ms = ns/1000000; + ret = runtime·plan9_tsemacquire(&m->waitsemacount, ms); if(ret == 1) return 0; // success return -1; // timeout or interrupted - */ } while(runtime·plan9_semacquire(&m->waitsemacount, 1) < 0) { - /* interrupted; try again */ + /* interrupted; try again (c.f. lock_sema.c) */ } return 0; // success } @@ -213,15 +276,15 @@ os·sigpipe(void) runtime·throw("too many writes on closed pipe"); } -/* - * placeholder - once notes are implemented, - * a signal generating a panic must appear as - * a call to this function for correct handling by - * traceback. - */ void runtime·sigpanic(void) { + if(g->sigpc == 0) + runtime·panicstring("call of nil func value"); + runtime·panicstring(m->notesig); + + if(g->sig == 1 || g->sig == 2) + runtime·throw("fault"); } int32 @@ -266,4 +329,5 @@ void runtime·badsignal(void) { runtime·pwrite(2, badsignal, sizeof badsignal - 1, -1LL); + runtime·exits(badsignal); } diff --git a/src/pkg/runtime/thread_windows.c b/src/pkg/runtime/thread_windows.c index f684d3733..ae4e82e50 100644 --- a/src/pkg/runtime/thread_windows.c +++ b/src/pkg/runtime/thread_windows.c @@ -135,6 +135,7 @@ runtime·write(int32 fd, void *buf, int32 n) return written; } +#pragma textflag 7 void runtime·osyield(void) { @@ -186,28 +187,43 @@ runtime·semacreate(void) #define STACK_SIZE_PARAM_IS_A_RESERVATION ((uintptr)0x00010000) void -runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) +runtime·newosproc(M *mp, void *stk) { void *thandle; USED(stk); - USED(g); // assuming g = m->g0 - USED(fn); // assuming fn = mstart thandle = runtime·stdcall(runtime·CreateThread, 6, - nil, (uintptr)0x20000, runtime·tstart_stdcall, m, + nil, (uintptr)0x20000, runtime·tstart_stdcall, mp, STACK_SIZE_PARAM_IS_A_RESERVATION, nil); if(thandle == nil) { runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror()); runtime·throw("runtime.newosproc"); } - runtime·atomicstorep(&m->thread, thandle); + runtime·atomicstorep(&mp->thread, thandle); +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +void +runtime·mpreinit(M *mp) +{ + USED(mp); } // Called to initialize a new m (including the bootstrap m). +// Called on the new thread, can not allocate memory. void runtime·minit(void) { + runtime·install_exception_handler(); +} + +// Called from dropm to undo the effect of an minit. +void +runtime·unminit(void) +{ + runtime·remove_exception_handler(); } int64 diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc index b18902f00..2babb173d 100644 --- a/src/pkg/runtime/time.goc +++ b/src/pkg/runtime/time.goc @@ -11,6 +11,7 @@ package time #include "os_GOOS.h" #include "arch_GOARCH.h" #include "malloc.h" +#include "race.h" static Timers timers; static void addtimer(Timer*); @@ -23,14 +24,16 @@ static bool deltimer(Timer*); // Sleep puts the current goroutine to sleep for at least ns nanoseconds. func Sleep(ns int64) { - g->status = Gwaiting; - g->waitreason = "sleep"; - runtime·tsleep(ns); + runtime·tsleep(ns, "sleep"); } // startTimer adds t to the timer heap. func startTimer(t *Timer) { + if(raceenabled) + runtime·racerelease(t); + runtime·lock(&timers); addtimer(t); + runtime·unlock(&timers); } // stopTimer removes t from the timer heap if it is there. @@ -54,27 +57,28 @@ ready(int64 now, Eface e) runtime·ready(e.data); } +static FuncVal readyv = {(void(*)(void))ready}; + // Put the current goroutine to sleep for ns nanoseconds. -// The caller must have set g->status and g->waitreason. void -runtime·tsleep(int64 ns) +runtime·tsleep(int64 ns, int8 *reason) { Timer t; - if(ns <= 0) { - g->status = Grunning; - g->waitreason = nil; + if(ns <= 0) return; - } t.when = runtime·nanotime() + ns; t.period = 0; - t.f = ready; + t.fv = &readyv; t.arg.data = g; + runtime·lock(&timers); addtimer(&t); - runtime·gosched(); + runtime·park(runtime·unlock, &timers, reason); } +static FuncVal timerprocv = {timerproc}; + // Add a timer to the heap and start or kick the timer proc // if the new timer is earlier than any of the others. static void @@ -83,7 +87,6 @@ addtimer(Timer *t) int32 n; Timer **nt; - runtime·lock(&timers); if(timers.len >= timers.cap) { // Grow slice. n = 16; @@ -109,9 +112,10 @@ addtimer(Timer *t) runtime·ready(timers.timerproc); } } - if(timers.timerproc == nil) - timers.timerproc = runtime·newproc1((byte*)timerproc, nil, 0, 0, addtimer); - runtime·unlock(&timers); + if(timers.timerproc == nil) { + timers.timerproc = runtime·newproc1(&timerprocv, nil, 0, 0, addtimer); + timers.timerproc->issystem = true; + } } // Delete timer t from the heap. @@ -182,26 +186,25 @@ timerproc(void) siftdown(0); t->i = -1; // mark as removed } - f = t->f; + f = (void*)t->fv->fn; arg = t->arg; runtime·unlock(&timers); + if(raceenabled) + runtime·raceacquire(t); f(now, arg); runtime·lock(&timers); } if(delta < 0) { // No timers left - put goroutine to sleep. timers.rescheduling = true; - g->status = Gwaiting; - g->waitreason = "timer goroutine (idle)"; - runtime·unlock(&timers); - runtime·gosched(); + runtime·park(runtime·unlock, &timers, "timer goroutine (idle)"); continue; } // At least one timer pending. Sleep until then. timers.sleeping = true; runtime·noteclear(&timers.waitnote); runtime·unlock(&timers); - runtime·entersyscall(); + runtime·entersyscallblock(); runtime·notetsleep(&timers.waitnote, delta); runtime·exitsyscall(); } diff --git a/src/pkg/runtime/time_plan9_386.c b/src/pkg/runtime/time_plan9_386.c new file mode 100644 index 000000000..a29d45715 --- /dev/null +++ b/src/pkg/runtime/time_plan9_386.c @@ -0,0 +1,34 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "os_GOOS.h" + +int64 +runtime·nanotime(void) +{ + static int32 fd = -1; + byte b[8]; + uint32 hi, lo; + + // As long as all goroutines share the same file + // descriptor table we can get away with using + // just a static fd. Without a lock the file can + // be opened twice but that's okay. + // + // Using /dev/bintime gives us a latency on the + // order of ten microseconds between two calls. + // + // The naïve implementation (without the cached + // file descriptor) is roughly four times slower + // in 9vx on a 2.16 GHz Intel Core 2 Duo. + + if(fd < 0 && (fd = runtime·open((byte*)"/dev/bintime", OREAD|OCEXEC)) < 0) + return 0; + if(runtime·pread(fd, b, sizeof b, 0) != sizeof b) + return 0; + hi = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3]; + lo = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7]; + return (int64)hi<<32 | (int64)lo; +} diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c index 22e0bc3a6..dd85cc02c 100644 --- a/src/pkg/runtime/traceback_arm.c +++ b/src/pkg/runtime/traceback_arm.c @@ -17,23 +17,23 @@ void _divu(void); void _modu(void); int32 -runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 max) +runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max) { int32 i, n, iter; uintptr pc, lr, tracepc, x; - byte *fp, *p; + byte *fp; bool waspanic; Stktop *stk; Func *f; - + pc = (uintptr)pc0; lr = (uintptr)lr0; fp = nil; waspanic = false; // If the PC is goexit, the goroutine hasn't started yet. - if(pc == (uintptr)runtime·goexit) { - pc = (uintptr)g->entry; + if(pc == (uintptr)runtime·goexit && gp->fnstart != nil) { + pc = (uintptr)gp->fnstart->fn; lr = (uintptr)runtime·goexit; } @@ -45,7 +45,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr } n = 0; - stk = (Stktop*)g->stackbase; + stk = (Stktop*)gp->stackbase; for(iter = 0; iter < 100 && n < max; iter++) { // iter avoids looping forever // Typically: // pc is the PC of the running function. @@ -57,52 +57,17 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr if(pc == (uintptr)runtime·lessstack) { // Hit top of stack segment. Unwind to next segment. pc = (uintptr)stk->gobuf.pc; - sp = stk->gobuf.sp; + sp = (byte*)stk->gobuf.sp; lr = 0; fp = nil; - if(pcbuf == nil) + if(pcbuf == nil && runtime·showframe(nil, gp == m->curg)) runtime·printf("----- stack segment boundary -----\n"); stk = (Stktop*)stk->stackbase; continue; } - if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) { - // Dangerous, but worthwhile: see if this is a closure by - // decoding the instruction stream. - // - // We check p < p+4 to avoid wrapping and faulting if - // we have lost track of where we are. - p = (byte*)pc; - if((pc&3) == 0 && p < p+4 && - runtime·mheap.arena_start < p && - p+4 < runtime·mheap.arena_used) { - x = *(uintptr*)p; - if((x&0xfffff000) == 0xe49df000) { - // End of closure: - // MOVW.P frame(R13), R15 - pc = *(uintptr*)sp; - lr = 0; - sp += x & 0xfff; - fp = nil; - continue; - } - if((x&0xfffff000) == 0xe52de000 && lr == (uintptr)runtime·goexit) { - // Beginning of closure. - // Closure at top of stack, not yet started. - p += 5*4; - if((x&0xfff) != 4) { - // argument copying - p += 7*4; - } - if((byte*)pc < p && p < p+4 && p+4 < runtime·mheap.arena_used) { - pc = *(uintptr*)p; - fp = nil; - continue; - } - } - } + if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) break; - } // Found an actual function. if(lr == 0) @@ -118,15 +83,17 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr else if(pcbuf != nil) pcbuf[n++] = pc; else { - if(runtime·showframe(f)) { + if(runtime·showframe(f, gp == m->curg)) { // Print during crash. // main(0x1, 0x2, 0x3) // /home/rsc/go/src/runtime/x.go:23 +0xf tracepc = pc; // back up to CALL instruction for funcline. if(n > 0 && pc > f->entry && !waspanic) tracepc -= sizeof(uintptr); + if(m->throwing && gp == m->curg) + runtime·printf("[fp=%p] ", fp); runtime·printf("%S(", f->name); - for(i = 0; i < f->args; i++) { + for(i = 0; i < f->args/sizeof(uintptr); i++) { if(i != 0) runtime·prints(", "); runtime·printhex(((uintptr*)fp)[1+i]); @@ -146,22 +113,22 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr waspanic = f->entry == (uintptr)runtime·sigpanic; - if(pcbuf == nil && f->entry == (uintptr)runtime·newstack && g == m->g0) { - runtime·printf("----- newstack called from goroutine %d -----\n", m->curg->goid); + if(pcbuf == nil && f->entry == (uintptr)runtime·newstack && gp == m->g0) { + runtime·printf("----- newstack called from goroutine %D -----\n", m->curg->goid); pc = (uintptr)m->morepc; sp = (byte*)m->moreargp - sizeof(void*); lr = (uintptr)m->morebuf.pc; - fp = m->morebuf.sp; - g = m->curg; - stk = (Stktop*)g->stackbase; + fp = (byte*)m->morebuf.sp; + gp = m->curg; + stk = (Stktop*)gp->stackbase; continue; } - if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && g == m->g0) { - runtime·printf("----- lessstack called from goroutine %d -----\n", m->curg->goid); - g = m->curg; - stk = (Stktop*)g->stackbase; - sp = stk->gobuf.sp; + if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) { + runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid); + gp = m->curg; + stk = (Stktop*)gp->stackbase; + sp = (byte*)stk->gobuf.sp; pc = (uintptr)stk->gobuf.pc; fp = nil; lr = 0; @@ -182,9 +149,21 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr // If this was deferproc or newproc, the caller had an extra 12. if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc) sp += 12; + + // sighandler saves the lr on stack before faking a call to sigpanic + if(waspanic) { + x = *(uintptr *)sp; + sp += 4; + f = runtime·findfunc(pc); + if (f == nil) { + pc = x; + } else if (f->frame == 0) + lr = x; + } } - if(pcbuf == nil && (pc = g->gopc) != 0 && (f = runtime·findfunc(pc)) != nil && g->goid != 1) { + if(pcbuf == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil + && runtime·showframe(f, gp == m->curg) && gp->goid != 1) { runtime·printf("created by %S\n", f->name); tracepc = pc; // back up to CALL instruction for funcline. if(n > 0 && pc > f->entry) @@ -199,9 +178,15 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr } void -runtime·traceback(byte *pc0, byte *sp, byte *lr, G *g) +runtime·traceback(byte *pc0, byte *sp, byte *lr, G *gp) { - runtime·gentraceback(pc0, sp, lr, g, 0, nil, 100); + if(gp->status == Gsyscall) { + // Override signal registers if blocked in system call. + pc0 = gp->sched.pc; + sp = (byte*)gp->sched.sp; + lr = nil; + } + runtime·gentraceback(pc0, sp, lr, gp, 0, nil, 100); } // func caller(n int) (pc uintptr, file string, line int, ok bool) diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c index be35bab00..72603ae8e 100644 --- a/src/pkg/runtime/traceback_x86.c +++ b/src/pkg/runtime/traceback_x86.c @@ -8,7 +8,6 @@ #include "arch_GOARCH.h" #include "malloc.h" -static uintptr isclosureentry(uintptr); void runtime·deferproc(void); void runtime·newproc(void); void runtime·newstack(void); @@ -23,9 +22,8 @@ void runtime·sigpanic(void); // A little clunky to merge the two but avoids duplicating // the code and all its subtlety. int32 -runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 max) +runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max) { - byte *p; int32 i, n, iter, sawnewstack; uintptr pc, lr, tracepc; byte *fp; @@ -40,19 +38,12 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr waspanic = false; // If the PC is goexit, the goroutine hasn't started yet. - if(pc0 == g->sched.pc && sp == g->sched.sp && pc0 == (byte*)runtime·goexit) { + if(pc0 == gp->sched.pc && sp == (byte*)gp->sched.sp && pc0 == (byte*)runtime·goexit && gp->fnstart != nil) { fp = sp; lr = pc; - pc = (uintptr)g->entry; + pc = (uintptr)gp->fnstart->fn; } - // If the PC is zero, it's likely a nil function call. - // Start in the caller's frame. - if(pc == 0) { - pc = lr; - lr = 0; - } - // If the PC is zero, it's likely a nil function call. // Start in the caller's frame. if(pc == 0) { @@ -62,7 +53,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr n = 0; sawnewstack = 0; - stk = (Stktop*)g->stackbase; + stk = (Stktop*)gp->stackbase; for(iter = 0; iter < 100 && n < max; iter++) { // iter avoids looping forever // Typically: // pc is the PC of the running function. @@ -74,41 +65,16 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr if(pc == (uintptr)runtime·lessstack) { // Hit top of stack segment. Unwind to next segment. pc = (uintptr)stk->gobuf.pc; - sp = stk->gobuf.sp; + sp = (byte*)stk->gobuf.sp; lr = 0; fp = nil; - if(pcbuf == nil) + if(pcbuf == nil && runtime·showframe(nil, gp == m->curg)) runtime·printf("----- stack segment boundary -----\n"); stk = (Stktop*)stk->stackbase; continue; } - if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) { - // Dangerous, but worthwhile: see if this is a closure: - // ADDQ $wwxxyyzz, SP; RET - // [48] 81 c4 zz yy xx ww c3 - // The 0x48 byte is only on amd64. - p = (byte*)pc; - // We check p < p+8 to avoid wrapping and faulting if we lose track. - if(runtime·mheap.arena_start < p && p < p+8 && p+8 < runtime·mheap.arena_used && // pointer in allocated memory - (sizeof(uintptr) != 8 || *p++ == 0x48) && // skip 0x48 byte on amd64 - p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { - sp += *(uint32*)(p+2); - pc = *(uintptr*)sp; - sp += sizeof(uintptr); - lr = 0; - fp = nil; - continue; - } - - // Closure at top of stack, not yet started. - if(lr == (uintptr)runtime·goexit && (pc = isclosureentry(pc)) != 0) { - fp = sp; - continue; - } - - // Unknown pc: stop. + if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) break; - } // Found an actual function. if(fp == nil) { @@ -126,7 +92,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr else if(pcbuf != nil) pcbuf[n++] = pc; else { - if(runtime·showframe(f)) { + if(runtime·showframe(f, gp == m->curg)) { // Print during crash. // main(0x1, 0x2, 0x3) // /home/rsc/go/src/runtime/x.go:23 +0xf @@ -134,8 +100,10 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr tracepc = pc; // back up to CALL instruction for funcline. if(n > 0 && pc > f->entry && !waspanic) tracepc--; + if(m->throwing && gp == m->curg) + runtime·printf("[fp=%p] ", fp); runtime·printf("%S(", f->name); - for(i = 0; i < f->args; i++) { + for(i = 0; i < f->args/sizeof(uintptr); i++) { if(i != 0) runtime·prints(", "); runtime·printhex(((uintptr*)fp)[i]); @@ -161,27 +129,27 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr if(f->entry == (uintptr)runtime·newstack) sawnewstack = 1; - if(pcbuf == nil && f->entry == (uintptr)runtime·morestack && g == m->g0 && sawnewstack) { + if(pcbuf == nil && f->entry == (uintptr)runtime·morestack && gp == m->g0 && sawnewstack) { // The fact that we saw newstack means that morestack // has managed to record its information in m, so we can // use it to keep unwinding the stack. - runtime·printf("----- morestack called from goroutine %d -----\n", m->curg->goid); + runtime·printf("----- morestack called from goroutine %D -----\n", m->curg->goid); pc = (uintptr)m->morepc; - sp = m->morebuf.sp - sizeof(void*); + sp = (byte*)m->morebuf.sp - sizeof(void*); lr = (uintptr)m->morebuf.pc; - fp = m->morebuf.sp; + fp = (byte*)m->morebuf.sp; sawnewstack = 0; - g = m->curg; - stk = (Stktop*)g->stackbase; + gp = m->curg; + stk = (Stktop*)gp->stackbase; continue; } - if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && g == m->g0) { + if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) { // Lessstack is running on scheduler stack. Switch to original goroutine. - runtime·printf("----- lessstack called from goroutine %d -----\n", m->curg->goid); - g = m->curg; - stk = (Stktop*)g->stackbase; - sp = stk->gobuf.sp; + runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid); + gp = m->curg; + stk = (Stktop*)gp->stackbase; + sp = (byte*)stk->gobuf.sp; pc = (uintptr)stk->gobuf.pc; fp = nil; lr = 0; @@ -196,7 +164,8 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr } // Show what created goroutine, except main goroutine (goid 1). - if(pcbuf == nil && (pc = g->gopc) != 0 && (f = runtime·findfunc(pc)) != nil && g->goid != 1) { + if(pcbuf == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil + && runtime·showframe(f, gp == m->curg) && gp->goid != 1) { runtime·printf("created by %S\n", f->name); tracepc = pc; // back up to CALL instruction for funcline. if(n > 0 && pc > f->entry) @@ -211,9 +180,14 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr } void -runtime·traceback(byte *pc0, byte *sp, byte*, G *g) +runtime·traceback(byte *pc0, byte *sp, byte*, G *gp) { - runtime·gentraceback(pc0, sp, nil, g, 0, nil, 100); + if(gp->status == Gsyscall) { + // Override signal registers if blocked in system call. + pc0 = gp->sched.pc; + sp = (byte*)gp->sched.sp; + } + runtime·gentraceback(pc0, sp, nil, gp, 0, nil, 100); } int32 @@ -227,77 +201,3 @@ runtime·callers(int32 skip, uintptr *pcbuf, int32 m) return runtime·gentraceback(pc, sp, nil, g, skip, pcbuf, m); } - -static uintptr -isclosureentry(uintptr pc) -{ - byte *p; - int32 i, siz; - - p = (byte*)pc; - if(p < runtime·mheap.arena_start || p+32 > runtime·mheap.arena_used) - return 0; - - if(*p == 0xe8) { - // CALL fn - return pc+5+*(int32*)(p+1); - } - - if(sizeof(uintptr) == 8 && p[0] == 0x48 && p[1] == 0xb9 && p[10] == 0xff && p[11] == 0xd1) { - // MOVQ $fn, CX; CALL *CX - return *(uintptr*)(p+2); - } - - // SUBQ $siz, SP - if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0x81 || *p++ != 0xec) - return 0; - siz = *(uint32*)p; - p += 4; - - // MOVQ $q, SI - if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xbe) - return 0; - p += sizeof(uintptr); - - // MOVQ SP, DI - if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0x89 || *p++ != 0xe7) - return 0; - - // CLD on 32-bit - if(sizeof(uintptr) == 4 && *p++ != 0xfc) - return 0; - - if(siz <= 4*sizeof(uintptr)) { - // MOVSQ... - for(i=0; i> 4) +#define ELF64_ST_TYPE(val) ((val) & 0xf) + +#define EI_NIDENT (16) + +typedef uint16 Elf64_Half; +typedef uint32 Elf64_Word; +typedef int32 Elf64_Sword; +typedef uint64 Elf64_Xword; +typedef int64 Elf64_Sxword; +typedef uint64 Elf64_Addr; +typedef uint64 Elf64_Off; +typedef uint16 Elf64_Section; +typedef Elf64_Half Elf64_Versym; + + +typedef struct +{ + Elf64_Word st_name; + byte st_info; + byte st_other; + Elf64_Section st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef entry */ +} Elf64_Verdef; + +typedef struct +{ + byte e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux entry */ +} Elf64_Verdaux; + +typedef struct +{ + uint64 a_type; /* Entry type */ + union + { + uint64 a_val; /* Integer value */ + } a_un; +} Elf64_auxv_t; + + +typedef struct { + byte* name; + void** var_ptr; +} symbol_key; + +typedef struct { + byte* version; + int32 ver_hash; +} version_key; + +struct vdso_info { + bool valid; + + /* Load information */ + uintptr load_addr; + uintptr load_offset; /* load_addr - recorded vaddr */ + + /* Symbol table */ + int32 num_sym; + Elf64_Sym *symtab; + const byte *symstrings; + + /* Version table */ + Elf64_Versym *versym; + Elf64_Verdef *verdef; +}; + +static version_key linux26 = { (byte*)"LINUX_2.6", 0x3ae75f6 }; + +// initialize with vsyscall fallbacks +void* runtime·__vdso_time_sym = (void*)0xffffffffff600400ULL; +void* runtime·__vdso_gettimeofday_sym = (void*)0xffffffffff600000ULL; +void* runtime·__vdso_clock_gettime_sym = (void*)0; + +#define SYM_KEYS_COUNT 3 +static symbol_key sym_keys[] = { + { (byte*)"__vdso_time", &runtime·__vdso_time_sym }, + { (byte*)"__vdso_gettimeofday", &runtime·__vdso_gettimeofday_sym }, + { (byte*)"__vdso_clock_gettime", &runtime·__vdso_clock_gettime_sym }, +}; + +static void +vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr) +{ + uint64 i; + bool found_vaddr = false; + + vdso_info->load_addr = (uintptr) hdr; + + Elf64_Phdr *pt = (Elf64_Phdr*)(vdso_info->load_addr + hdr->e_phoff); + Elf64_Shdr *sh = (Elf64_Shdr*)(vdso_info->load_addr + hdr->e_shoff); + Elf64_Dyn *dyn = 0; + + for(i=0; ie_shnum; i++) { + if(sh[i].sh_type == SHT_DYNSYM) { + vdso_info->num_sym = sh[i].sh_size / sizeof(Elf64_Sym); + } + } + + // We need two things from the segment table: the load offset + // and the dynamic table. + for(i=0; ie_phnum; i++) { + if(pt[i].p_type == PT_LOAD && found_vaddr == false) { + found_vaddr = true; + vdso_info->load_offset = (uintptr)hdr + + (uintptr)pt[i].p_offset + - (uintptr)pt[i].p_vaddr; + } else if(pt[i].p_type == PT_DYNAMIC) { + dyn = (Elf64_Dyn*)((uintptr)hdr + pt[i].p_offset); + } + } + + if(found_vaddr == false || dyn == nil) + return; // Failed + + // Fish out the useful bits of the dynamic table. + for(i=0; dyn[i].d_tag!=DT_NULL; i++) { + switch(dyn[i].d_tag) { + case DT_STRTAB: + vdso_info->symstrings = (const byte *) + ((uintptr)dyn[i].d_un.d_ptr + + vdso_info->load_offset); + break; + case DT_SYMTAB: + vdso_info->symtab = (Elf64_Sym *) + ((uintptr)dyn[i].d_un.d_ptr + + vdso_info->load_offset); + break; + case DT_VERSYM: + vdso_info->versym = (Elf64_Versym *) + ((uintptr)dyn[i].d_un.d_ptr + + vdso_info->load_offset); + break; + case DT_VERDEF: + vdso_info->verdef = (Elf64_Verdef *) + ((uintptr)dyn[i].d_un.d_ptr + + vdso_info->load_offset); + break; + } + } + if(vdso_info->symstrings == nil || vdso_info->symtab == nil) + return; // Failed + + if(vdso_info->verdef == nil) + vdso_info->versym = 0; + + // That's all we need. + vdso_info->valid = true; +} + +static int32 +vdso_find_version(struct vdso_info *vdso_info, version_key* ver) +{ + if(vdso_info->valid == false) { + return 0; + } + Elf64_Verdef *def = vdso_info->verdef; + while(true) { + if((def->vd_flags & VER_FLG_BASE) == 0) { + Elf64_Verdaux *aux = (Elf64_Verdaux*)((byte *)def + def->vd_aux); + if(def->vd_hash == ver->ver_hash && + runtime·strcmp(ver->version, vdso_info->symstrings + aux->vda_name) == 0) { + return def->vd_ndx & 0x7fff; + } + } + + if(def->vd_next == 0) { + break; + } + def = (Elf64_Verdef *)((byte *)def + def->vd_next); + } + return 0; +} + +static void +vdso_parse_symbols(struct vdso_info *vdso_info, int32 version) +{ + int32 i, j; + + if(vdso_info->valid == false) + return; + + for(i=0; inum_sym; i++) { + Elf64_Sym *sym = &vdso_info->symtab[i]; + + // Check for a defined global or weak function w/ right name. + if(ELF64_ST_TYPE(sym->st_info) != STT_FUNC) + continue; + if(ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && + ELF64_ST_BIND(sym->st_info) != STB_WEAK) + continue; + if(sym->st_shndx == SHN_UNDEF) + continue; + + for(j=0; jsymstrings + sym->st_name) != 0) + continue; + + // Check symbol version. + if(vdso_info->versym != nil && version != 0 + && vdso_info->versym[i] & 0x7fff != version) + continue; + + *sym_keys[j].var_ptr = (void *)(vdso_info->load_offset + sym->st_value); + } + } +} + +static void +runtime·linux_setup_vdso(int32 argc, uint8** argv) +{ + struct vdso_info vdso_info; + + // skip argvc + byte **p = argv; + p = &p[argc+1]; + + // skip envp to get to ELF auxiliary vector. + for(; *p!=0; p++) {} + + // skip NULL separator + p++; + + // now, p points to auxv + Elf64_auxv_t *elf_auxv = (Elf64_auxv_t*) p; + + for(int32 i=0; elf_auxv[i].a_type!=AT_NULL; i++) { + if(elf_auxv[i].a_type == AT_SYSINFO_EHDR) { + if(elf_auxv[i].a_un.a_val == 0) { + // Something went wrong + return; + } + vdso_init_from_sysinfo_ehdr(&vdso_info, (Elf64_Ehdr*)elf_auxv[i].a_un.a_val); + vdso_parse_symbols(&vdso_info, vdso_find_version(&vdso_info, &linux26)); + return; + } + } +} + +void (*runtime·sysargs)(int32, uint8**) = runtime·linux_setup_vdso; diff --git a/src/pkg/runtime/vlop_arm.s b/src/pkg/runtime/vlop_arm.s index fc679f0ee..0dedc316a 100644 --- a/src/pkg/runtime/vlop_arm.s +++ b/src/pkg/runtime/vlop_arm.s @@ -23,9 +23,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#define UMULL(Rs,Rm,Rhi,Rlo,S) WORD $((14<<28)|(4<<21)|(S<<20)|(Rhi<<16)|(Rlo<<12)|(Rs<<8)|(9<<4)|Rm) -#define UMLAL(Rs,Rm,Rhi,Rlo,S) WORD $((14<<28)|(5<<21)|(S<<20)|(Rhi<<16)|(Rlo<<12)|(Rs<<8)|(9<<4)|Rm) -#define MUL(Rs,Rm,Rd,S) WORD $((14<<28)|(0<<21)|(S<<20)|(Rd<<16)|(Rs<<8)|(9<<4)|Rm) arg=0 /* replaced use of R10 by R11 because the former can be the data segment base register */ @@ -36,136 +33,15 @@ TEXT _mulv(SB), $0 MOVW 8(FP), R11 /* h0 */ MOVW 12(FP), R4 /* l1 */ MOVW 16(FP), R5 /* h1 */ - UMULL(4, 2, 7, 6, 0) - MUL(11, 4, 8, 0) + MULLU R4, R2, (R7,R6) + MUL R11, R4, R8 ADD R8, R7 - MUL(2, 5, 8, 0) + MUL R2, R5, R8 ADD R8, R7 MOVW R6, 0(R(arg)) MOVW R7, 4(R(arg)) RET - -Q = 0 -N = 1 -D = 2 -CC = 3 -TMP = 11 - -TEXT save<>(SB), 7, $0 - MOVW R(Q), 0(FP) - MOVW R(N), 4(FP) - MOVW R(D), 8(FP) - MOVW R(CC), 12(FP) - - MOVW R(TMP), R(Q) /* numerator */ - MOVW 20(FP), R(D) /* denominator */ - CMP $0, R(D) - BNE s1 - BL runtime·panicdivide(SB) -/* MOVW -1(R(D)), R(TMP) /* divide by zero fault */ -s1: RET - -TEXT rest<>(SB), 7, $0 - MOVW 0(FP), R(Q) - MOVW 4(FP), R(N) - MOVW 8(FP), R(D) - MOVW 12(FP), R(CC) -/* - * return to caller - * of rest<> - */ - MOVW 0(R13), R14 - ADD $20, R13 - B (R14) - -TEXT div<>(SB), 7, $0 - MOVW $32, R(CC) -/* - * skip zeros 8-at-a-time - */ -e1: - AND.S $(0xff<<24),R(Q), R(N) - BNE e2 - SLL $8, R(Q) - SUB.S $8, R(CC) - BNE e1 - RET -e2: - MOVW $0, R(N) - -loop: -/* - * shift R(N||Q) left one - */ - SLL $1, R(N) - CMP $0, R(Q) - ORR.LT $1, R(N) - SLL $1, R(Q) - -/* - * compare numerator to denominator - * if less, subtract and set quotient bit - */ - CMP R(D), R(N) - ORR.HS $1, R(Q) - SUB.HS R(D), R(N) - SUB.S $1, R(CC) - BNE loop - RET - -TEXT _div(SB), 7, $16 - BL save<>(SB) - CMP $0, R(Q) - BGE d1 - RSB $0, R(Q), R(Q) - CMP $0, R(D) - BGE d2 - RSB $0, R(D), R(D) -d0: - BL div<>(SB) /* none/both neg */ - MOVW R(Q), R(TMP) - B out -d1: - CMP $0, R(D) - BGE d0 - RSB $0, R(D), R(D) -d2: - BL div<>(SB) /* one neg */ - RSB $0, R(Q), R(TMP) - B out - -TEXT _mod(SB), 7, $16 - BL save<>(SB) - CMP $0, R(D) - RSB.LT $0, R(D), R(D) - CMP $0, R(Q) - BGE m1 - RSB $0, R(Q), R(Q) - BL div<>(SB) /* neg numerator */ - RSB $0, R(N), R(TMP) - B out -m1: - BL div<>(SB) /* pos numerator */ - MOVW R(N), R(TMP) - B out - -TEXT _divu(SB), 7, $16 - BL save<>(SB) - BL div<>(SB) - MOVW R(Q), R(TMP) - B out - -TEXT _modu(SB), 7, $16 - BL save<>(SB) - BL div<>(SB) - MOVW R(N), R(TMP) - B out - -out: - BL rest<>(SB) - B out - // trampoline for _sfloat2. passes LR as arg0 and // saves registers R0-R13 and CPSR on the stack. R0-R12 and CPSR flags can // be changed by _sfloat2. @@ -186,5 +62,189 @@ TEXT _sfloat(SB), 7, $64 // 4 arg + 14*4 saved regs + cpsr MOVM.IA.W (R0), [R1-R12] MOVW 8(R13), R0 RET - +// func udiv(n, d uint32) (q, r uint32) +// Reference: +// Sloss, Andrew et. al; ARM System Developer's Guide: Designing and Optimizing System Software +// Morgan Kaufmann; 1 edition (April 8, 2004), ISBN 978-1558608740 +q = 0 // input d, output q +r = 1 // input n, output r +s = 2 // three temporary variables +m = 3 +a = 11 +// Please be careful when changing this, it is pretty fragile: +// 1, don't use unconditional branch as the linker is free to reorder the blocks; +// 2. if a == 11, beware that the linker will use R11 if you use certain instructions. +TEXT udiv<>(SB),7,$-4 + CLZ R(q), R(s) // find normalizing shift + MOVW.S R(q)<>25, PC, R(a) // most significant 7 bits of divisor + MOVBU.NE (4*36-64)(R(a)), R(a) // 36 == number of inst. between fast_udiv_tab and begin + +begin: + SUB.S $7, R(s) + RSB $0, R(q), R(m) // m = -q + MOVW.PL R(a)<>32) + TEQ R(m)->1, R(m) // check for d=0 or d=1 + + // 2nd Newton iteration + MUL.NE R(m), R(q), R(a) + MOVW.NE $0, R(s) + MULAL.NE R(q), R(a), (R(q),R(s)) + BEQ udiv_by_0_or_1 + + // q now accurate enough for a remainder r, 0<=r<3*d + MULLU R(q), R(r), (R(q),R(s)) // q = (r * q) >> 32 + ADD R(m), R(r), R(r) // r = n - d + MULA R(m), R(q), R(r), R(r) // r = n - (q+1)*d + + // since 0 <= n-q*d < 3*d; thus -d <= r < 2*d + CMN R(m), R(r) // t = r-d + SUB.CS R(m), R(r), R(r) // if (t<-d || t>=0) r=r+d + ADD.CC $1, R(q) + ADD.PL R(m)<<1, R(r) + ADD.PL $2, R(q) + + // return, can't use RET here or fast_udiv_tab will be dropped during linking + MOVW R14, R15 + +udiv_by_large_d: + // at this point we know d>=2^(31-6)=2^25 + SUB $4, R(a), R(a) + RSB $0, R(s), R(s) + MOVW R(a)>>R(s), R(q) + MULLU R(q), R(r), (R(q),R(s)) + MULA R(m), R(q), R(r), R(r) + + // q now accurate enough for a remainder r, 0<=r<4*d + CMN R(r)>>1, R(m) // if(r/2 >= d) + ADD.CS R(m)<<1, R(r) + ADD.CS $2, R(q) + CMN R(r), R(m) + ADD.CS R(m), R(r) + ADD.CS $1, R(q) + + // return, can't use RET here or fast_udiv_tab will be dropped during linking + MOVW R14, R15 + +udiv_by_0_or_1: + // carry set if d==1, carry clear if d==0 + MOVW.CS R(r), R(q) + MOVW.CS $0, R(r) + BL.CC runtime·panicdivide(SB) // no way back + + // return, can't use RET here or fast_udiv_tab will be dropped during linking + MOVW R14, R15 + +fast_udiv_tab: + // var tab [64]byte + // tab[0] = 255; for i := 1; i <= 63; i++ { tab[i] = (1<<14)/(64+i) } + // laid out here as little-endian uint32s + WORD $0xf4f8fcff + WORD $0xe6eaedf0 + WORD $0xdadde0e3 + WORD $0xcfd2d4d7 + WORD $0xc5c7cacc + WORD $0xbcbec0c3 + WORD $0xb4b6b8ba + WORD $0xacaeb0b2 + WORD $0xa5a7a8aa + WORD $0x9fa0a2a3 + WORD $0x999a9c9d + WORD $0x93949697 + WORD $0x8e8f9092 + WORD $0x898a8c8d + WORD $0x85868788 + WORD $0x81828384 + +// The linker will pass numerator in R(TMP), and it also +// expects the result in R(TMP) +TMP = 11 + +TEXT _divu(SB), 7, $16 + MOVW R(q), 4(R13) + MOVW R(r), 8(R13) + MOVW R(s), 12(R13) + MOVW R(m), 16(R13) + + MOVW R(TMP), R(r) /* numerator */ + MOVW 0(FP), R(q) /* denominator */ + BL udiv<>(SB) + MOVW R(q), R(TMP) + MOVW 4(R13), R(q) + MOVW 8(R13), R(r) + MOVW 12(R13), R(s) + MOVW 16(R13), R(m) + RET + +TEXT _modu(SB), 7, $16 + MOVW R(q), 4(R13) + MOVW R(r), 8(R13) + MOVW R(s), 12(R13) + MOVW R(m), 16(R13) + + MOVW R(TMP), R(r) /* numerator */ + MOVW 0(FP), R(q) /* denominator */ + BL udiv<>(SB) + MOVW R(r), R(TMP) + MOVW 4(R13), R(q) + MOVW 8(R13), R(r) + MOVW 12(R13), R(s) + MOVW 16(R13), R(m) + RET + +TEXT _div(SB),7,$16 + MOVW R(q), 4(R13) + MOVW R(r), 8(R13) + MOVW R(s), 12(R13) + MOVW R(m), 16(R13) + MOVW R(TMP), R(r) /* numerator */ + MOVW 0(FP), R(q) /* denominator */ + CMP $0, R(r) + BGE d1 + RSB $0, R(r), R(r) + CMP $0, R(q) + BGE d2 + RSB $0, R(q), R(q) +d0: + BL udiv<>(SB) /* none/both neg */ + MOVW R(q), R(TMP) + B out +d1: + CMP $0, R(q) + BGE d0 + RSB $0, R(q), R(q) +d2: + BL udiv<>(SB) /* one neg */ + RSB $0, R(q), R(TMP) + B out + +TEXT _mod(SB),7,$16 + MOVW R(q), 4(R13) + MOVW R(r), 8(R13) + MOVW R(s), 12(R13) + MOVW R(m), 16(R13) + MOVW R(TMP), R(r) /* numerator */ + MOVW 0(FP), R(q) /* denominator */ + CMP $0, R(q) + RSB.LT $0, R(q), R(q) + CMP $0, R(r) + BGE m1 + RSB $0, R(r), R(r) + BL udiv<>(SB) /* neg numerator */ + RSB $0, R(r), R(TMP) + B out +m1: + BL udiv<>(SB) /* pos numerator */ + MOVW R(r), R(TMP) +out: + MOVW 4(R13), R(q) + MOVW 8(R13), R(r) + MOVW 12(R13), R(s) + MOVW 16(R13), R(m) + RET diff --git a/src/pkg/runtime/vlop_arm_test.go b/src/pkg/runtime/vlop_arm_test.go new file mode 100644 index 000000000..cd28419ad --- /dev/null +++ b/src/pkg/runtime/vlop_arm_test.go @@ -0,0 +1,70 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime_test + +import "testing" + +// arm soft division benchmarks adapted from +// http://ridiculousfish.com/files/division_benchmarks.tar.gz + +const numeratorsSize = 1 << 21 + +var numerators = randomNumerators() + +type randstate struct { + hi, lo uint32 +} + +func (r *randstate) rand() uint32 { + r.hi = r.hi<<16 + r.hi>>16 + r.hi += r.lo + r.lo += r.hi + return r.hi +} + +func randomNumerators() []uint32 { + numerators := make([]uint32, numeratorsSize) + random := &randstate{2147483563, 2147483563 ^ 0x49616E42} + for i := range numerators { + numerators[i] = random.rand() + } + return numerators +} + +func bmUint32Div(divisor uint32, b *testing.B) { + var sum uint32 + for i := 0; i < b.N; i++ { + sum += numerators[i&(numeratorsSize-1)] / divisor + } +} + +func BenchmarkUint32Div7(b *testing.B) { bmUint32Div(7, b) } +func BenchmarkUint32Div37(b *testing.B) { bmUint32Div(37, b) } +func BenchmarkUint32Div123(b *testing.B) { bmUint32Div(123, b) } +func BenchmarkUint32Div763(b *testing.B) { bmUint32Div(763, b) } +func BenchmarkUint32Div1247(b *testing.B) { bmUint32Div(1247, b) } +func BenchmarkUint32Div9305(b *testing.B) { bmUint32Div(9305, b) } +func BenchmarkUint32Div13307(b *testing.B) { bmUint32Div(13307, b) } +func BenchmarkUint32Div52513(b *testing.B) { bmUint32Div(52513, b) } +func BenchmarkUint32Div60978747(b *testing.B) { bmUint32Div(60978747, b) } +func BenchmarkUint32Div106956295(b *testing.B) { bmUint32Div(106956295, b) } + +func bmUint32Mod(divisor uint32, b *testing.B) { + var sum uint32 + for i := 0; i < b.N; i++ { + sum += numerators[i&(numeratorsSize-1)] % divisor + } +} + +func BenchmarkUint32Mod7(b *testing.B) { bmUint32Mod(7, b) } +func BenchmarkUint32Mod37(b *testing.B) { bmUint32Mod(37, b) } +func BenchmarkUint32Mod123(b *testing.B) { bmUint32Mod(123, b) } +func BenchmarkUint32Mod763(b *testing.B) { bmUint32Mod(763, b) } +func BenchmarkUint32Mod1247(b *testing.B) { bmUint32Mod(1247, b) } +func BenchmarkUint32Mod9305(b *testing.B) { bmUint32Mod(9305, b) } +func BenchmarkUint32Mod13307(b *testing.B) { bmUint32Mod(13307, b) } +func BenchmarkUint32Mod52513(b *testing.B) { bmUint32Mod(52513, b) } +func BenchmarkUint32Mod60978747(b *testing.B) { bmUint32Mod(60978747, b) } +func BenchmarkUint32Mod106956295(b *testing.B) { bmUint32Mod(106956295, b) } diff --git a/src/pkg/sort/example_keys_test.go b/src/pkg/sort/example_keys_test.go new file mode 100644 index 000000000..a8e47e492 --- /dev/null +++ b/src/pkg/sort/example_keys_test.go @@ -0,0 +1,96 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sort_test + +import ( + "fmt" + "sort" +) + +// A couple of type definitions to make the units clear. +type earthMass float64 +type au float64 + +// A Planet defines the properties of a solar system object. +type Planet struct { + name string + mass earthMass + distance au +} + +// By is the type of a "less" function that defines the ordering of its Planet arguments. +type By func(p1, p2 *Planet) bool + +// Sort is a method on the function type, By, that sorts the argument slice according to the function. +func (by By) Sort(planets []Planet) { + ps := &planetSorter{ + planets: planets, + by: by, // The Sort method's receiver is the function (closure) that defines the sort order. + } + sort.Sort(ps) +} + +// planetSorter joins a By function and a slice of Planets to be sorted. +type planetSorter struct { + planets []Planet + by func(p1, p2 *Planet) bool // Closure used in the Less method. +} + +// Len is part of sort.Interface. +func (s *planetSorter) Len() int { + return len(s.planets) +} + +// Swap is part of sort.Interface. +func (s *planetSorter) Swap(i, j int) { + s.planets[i], s.planets[j] = s.planets[j], s.planets[i] +} + +// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. +func (s *planetSorter) Less(i, j int) bool { + return s.by(&s.planets[i], &s.planets[j]) +} + +var planets = []Planet{ + {"Mercury", 0.055, 0.4}, + {"Venus", 0.815, 0.7}, + {"Earth", 1.0, 1.0}, + {"Mars", 0.107, 1.5}, +} + +// ExampleSortKeys demonstrates a technique for sorting a struct type using programmable sort criteria. +func Example_sortKeys() { + // Closures that order the Planet structure. + name := func(p1, p2 *Planet) bool { + return p1.name < p2.name + } + mass := func(p1, p2 *Planet) bool { + return p1.mass < p2.mass + } + distance := func(p1, p2 *Planet) bool { + return p1.distance < p2.distance + } + decreasingDistance := func(p1, p2 *Planet) bool { + return !distance(p1, p2) + } + + // Sort the planets by the various criteria. + By(name).Sort(planets) + fmt.Println("By name:", planets) + + By(mass).Sort(planets) + fmt.Println("By mass:", planets) + + By(distance).Sort(planets) + fmt.Println("By distance:", planets) + + By(decreasingDistance).Sort(planets) + fmt.Println("By decreasing distance:", planets) + + // Output: By name: [{Earth 1 1} {Mars 0.107 1.5} {Mercury 0.055 0.4} {Venus 0.815 0.7}] + // By mass: [{Mercury 0.055 0.4} {Mars 0.107 1.5} {Venus 0.815 0.7} {Earth 1 1}] + // By distance: [{Mercury 0.055 0.4} {Venus 0.815 0.7} {Earth 1 1} {Mars 0.107 1.5}] + // By decreasing distance: [{Mars 0.107 1.5} {Earth 1 1} {Venus 0.815 0.7} {Mercury 0.055 0.4}] +} diff --git a/src/pkg/sort/example_reverse_test.go b/src/pkg/sort/example_reverse_test.go deleted file mode 100644 index 7c7f05bf3..000000000 --- a/src/pkg/sort/example_reverse_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sort_test - -import ( - "fmt" - "sort" -) - -// Reverse embeds a sort.Interface value and implements a reverse sort over -// that value. -type Reverse struct { - // This embedded Interface permits Reverse to use the methods of - // another Interface implementation. - sort.Interface -} - -// Less returns the opposite of the embedded implementation's Less method. -func (r Reverse) Less(i, j int) bool { - return r.Interface.Less(j, i) -} - -func ExampleInterface_reverse() { - s := []int{5, 2, 6, 3, 1, 4} // unsorted - sort.Sort(Reverse{sort.IntSlice(s)}) - fmt.Println(s) - // Output: [6 5 4 3 2 1] -} diff --git a/src/pkg/sort/example_test.go b/src/pkg/sort/example_test.go index f57d02546..f7372bec3 100644 --- a/src/pkg/sort/example_test.go +++ b/src/pkg/sort/example_test.go @@ -15,3 +15,10 @@ func ExampleInts() { fmt.Println(s) // Output: [1 2 3 4 5 6] } + +func ExampleReverse() { + s := []int{5, 2, 6, 3, 1, 4} // unsorted + sort.Sort(sort.Reverse(sort.IntSlice(s))) + fmt.Println(s) + // Output: [6 5 4 3 2 1] +} diff --git a/src/pkg/sort/search.go b/src/pkg/sort/search.go index 4f0ce55c3..8a2c1c33b 100644 --- a/src/pkg/sort/search.go +++ b/src/pkg/sort/search.go @@ -7,11 +7,13 @@ package sort // Search uses binary search to find and return the smallest index i -// in [0, n) at which f(i) is true, assuming that on the range [0, n), +// in [0, n) at which f(i) is true, assuming that on the range [0, n), // f(i) == true implies f(i+1) == true. That is, Search requires that // f is false for some (possibly empty) prefix of the input range [0, n) // and then true for the (possibly empty) remainder; Search returns // the first true index. If there is no such index, Search returns n. +// (Note that the "not found" return value is not -1 as in, for instance, +// strings.Index). // Search calls f(i) only for i in the range [0, n). // // A common use of Search is to find the index i for a value x in @@ -74,22 +76,28 @@ func Search(n int, f func(int) bool) int { // Convenience wrappers for common cases. // SearchInts searches for x in a sorted slice of ints and returns the index -// as specified by Search. The slice must be sorted in ascending order. +// as specified by Search. The return value is the index to insert x if x is +// not present (it could be len(a)). +// The slice must be sorted in ascending order. // func SearchInts(a []int, x int) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } // SearchFloat64s searches for x in a sorted slice of float64s and returns the index -// as specified by Search. The slice must be sorted in ascending order. -// +// as specified by Search. The return value is the index to insert x if x is not +// present (it could be len(a)). +// The slice must be sorted in ascending order. +// func SearchFloat64s(a []float64, x float64) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } -// SearchStrings searches for x slice a sorted slice of strings and returns the index -// as specified by Search. The slice must be sorted in ascending order. -// +// SearchStrings searches for x in a sorted slice of strings and returns the index +// as specified by Search. The return value is the index to insert x if x is not +// present (it could be len(a)). +// The slice must be sorted in ascending order. +// func SearchStrings(a []string, x string) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } diff --git a/src/pkg/sort/search_test.go b/src/pkg/sort/search_test.go index 07295ffa9..4d8d6d930 100644 --- a/src/pkg/sort/search_test.go +++ b/src/pkg/sort/search_test.go @@ -117,6 +117,28 @@ func TestSearchWrappers(t *testing.T) { } } +func runSearchWrappers() { + SearchInts(data, 11) + SearchFloat64s(fdata, 2.1) + SearchStrings(sdata, "") + IntSlice(data).Search(0) + Float64Slice(fdata).Search(2.0) + StringSlice(sdata).Search("x") +} + +func TestSearchWrappersDontAlloc(t *testing.T) { + allocs := testing.AllocsPerRun(100, runSearchWrappers) + if allocs != 0 { + t.Errorf("expected no allocs for runSearchWrappers, got %v", allocs) + } +} + +func BenchmarkSearchWrappers(b *testing.B) { + for i := 0; i < b.N; i++ { + runSearchWrappers() + } +} + // Abstract exhaustive test: all sizes up to 100, // all possible return values. If there are any small // corner cases, this test exercises them. diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go index 62a4d55e7..e10961992 100644 --- a/src/pkg/sort/sort.go +++ b/src/pkg/sort/sort.go @@ -124,26 +124,31 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) { // into the middle of the slice. pivot := lo a, b, c, d := lo+1, lo+1, hi, hi - for b < c { - if data.Less(b, pivot) { // data[b] < pivot - b++ - continue - } - if !data.Less(pivot, b) { // data[b] = pivot - data.Swap(a, b) - a++ - b++ - continue + for { + for b < c { + if data.Less(b, pivot) { // data[b] < pivot + b++ + } else if !data.Less(pivot, b) { // data[b] = pivot + data.Swap(a, b) + a++ + b++ + } else { + break + } } - if data.Less(pivot, c-1) { // data[c-1] > pivot - c-- - continue + for b < c { + if data.Less(pivot, c-1) { // data[c-1] > pivot + c-- + } else if !data.Less(c-1, pivot) { // data[c-1] = pivot + data.Swap(c-1, d-1) + c-- + d-- + } else { + break + } } - if !data.Less(c-1, pivot) { // data[c-1] = pivot - data.Swap(c-1, d-1) - c-- - d-- - continue + if b >= c { + break } // data[b] > pivot; data[c-1] < pivot data.Swap(b, c-1) @@ -197,6 +202,22 @@ func Sort(data Interface) { quickSort(data, 0, n, maxDepth) } +type reverse struct { + // This embedded Interface permits Reverse to use the methods of + // another Interface implementation. + Interface +} + +// Less returns the opposite of the embedded implementation's Less method. +func (r reverse) Less(i, j int) bool { + return r.Interface.Less(j, i) +} + +// Reverse returns the reverse order for data. +func Reverse(data Interface) Interface { + return &reverse{data} +} + // IsSorted reports whether data is sorted. func IsSorted(data Interface) bool { n := data.Len() diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go index ee8a9d0e8..5daf8482b 100644 --- a/src/pkg/sort/sort_test.go +++ b/src/pkg/sort/sort_test.go @@ -92,6 +92,23 @@ func TestSortLarge_Random(t *testing.T) { } } +func TestReverseSortIntSlice(t *testing.T) { + data := ints + data1 := ints + a := IntSlice(data[0:]) + Sort(a) + r := IntSlice(data1[0:]) + Sort(Reverse(r)) + for i := 0; i < len(data); i++ { + if a[i] != r[len(data)-1-i] { + t.Errorf("reverse sort didn't sort") + } + if i > len(data)/2 { + break + } + } +} + func BenchmarkSortString1K(b *testing.B) { b.StopTimer() for i := 0; i < b.N; i++ { @@ -151,15 +168,18 @@ const ( ) type testingData struct { - desc string - t *testing.T - data []int - maxswap int // number of swaps allowed - nswap int + desc string + t *testing.T + data []int + maxswap int // number of swaps allowed + ncmp, nswap int } -func (d *testingData) Len() int { return len(d.data) } -func (d *testingData) Less(i, j int) bool { return d.data[i] < d.data[j] } +func (d *testingData) Len() int { return len(d.data) } +func (d *testingData) Less(i, j int) bool { + d.ncmp++ + return d.data[i] < d.data[j] +} func (d *testingData) Swap(i, j int) { if d.nswap >= d.maxswap { d.t.Errorf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data)) @@ -192,8 +212,7 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) { dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"} modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"} var tmp1, tmp2 [1025]int - for ni := 0; ni < len(sizes); ni++ { - n := sizes[ni] + for _, n := range sizes { for m := 1; m < 2*n; m *= 2 { for dist := 0; dist < _NDist; dist++ { j := 0 @@ -259,8 +278,10 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) { } desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode]) - d := &testingData{desc, t, mdata[0:n], n * lg(n) * 12 / 10, 0} + d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: n * lg(n) * 12 / 10} sort(d) + // Uncomment if you are trying to improve the number of compares/swaps. + //t.Logf("%s: ncmp=%d, nswp=%d", desc, d.ncmp, d.nswap) // If we were testing C qsort, we'd have to make a copy // of the slice and sort it ourselves and then compare diff --git a/src/pkg/strconv/atof.go b/src/pkg/strconv/atof.go index cd3031b0e..b4fe97d12 100644 --- a/src/pkg/strconv/atof.go +++ b/src/pkg/strconv/atof.go @@ -37,17 +37,28 @@ func equalIgnoreCase(s1, s2 string) bool { } func special(s string) (f float64, ok bool) { - switch { - case equalIgnoreCase(s, "nan"): - return math.NaN(), true - case equalIgnoreCase(s, "-inf"), - equalIgnoreCase(s, "-infinity"): - return math.Inf(-1), true - case equalIgnoreCase(s, "+inf"), - equalIgnoreCase(s, "+infinity"), - equalIgnoreCase(s, "inf"), - equalIgnoreCase(s, "infinity"): - return math.Inf(1), true + if len(s) == 0 { + return + } + switch s[0] { + default: + return + case '+': + if equalIgnoreCase(s, "+inf") || equalIgnoreCase(s, "+infinity") { + return math.Inf(1), true + } + case '-': + if equalIgnoreCase(s, "-inf") || equalIgnoreCase(s, "-infinity") { + return math.Inf(-1), true + } + case 'n', 'N': + if equalIgnoreCase(s, "nan") { + return math.NaN(), true + } + case 'i', 'I': + if equalIgnoreCase(s, "inf") || equalIgnoreCase(s, "infinity") { + return math.Inf(1), true + } } return } @@ -142,6 +153,105 @@ func (b *decimal) set(s string) (ok bool) { return } +// readFloat reads a decimal mantissa and exponent from a float +// string representation. It sets ok to false if the number could +// not fit return types or is invalid. +func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { + const uint64digits = 19 + i := 0 + + // optional sign + if i >= len(s) { + return + } + switch { + case s[i] == '+': + i++ + case s[i] == '-': + neg = true + i++ + } + + // digits + sawdot := false + sawdigits := false + nd := 0 + ndMant := 0 + dp := 0 + for ; i < len(s); i++ { + switch c := s[i]; true { + case c == '.': + if sawdot { + return + } + sawdot = true + dp = nd + continue + + case '0' <= c && c <= '9': + sawdigits = true + if c == '0' && nd == 0 { // ignore leading zeros + dp-- + continue + } + nd++ + if ndMant < uint64digits { + mantissa *= 10 + mantissa += uint64(c - '0') + ndMant++ + } else if s[i] != '0' { + trunc = true + } + continue + } + break + } + if !sawdigits { + return + } + if !sawdot { + dp = nd + } + + // optional exponent moves decimal point. + // if we read a very large, very long number, + // just be sure to move the decimal point by + // a lot (say, 100000). it doesn't matter if it's + // not the exact number. + if i < len(s) && (s[i] == 'e' || s[i] == 'E') { + i++ + if i >= len(s) { + return + } + esign := 1 + if s[i] == '+' { + i++ + } else if s[i] == '-' { + i++ + esign = -1 + } + if i >= len(s) || s[i] < '0' || s[i] > '9' { + return + } + e := 0 + for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + if e < 10000 { + e = e*10 + int(s[i]) - '0' + } + } + dp += e * esign + } + + if i != len(s) { + return + } + + exp = dp - ndMant + ok = true + return + +} + // decimal power of ten to binary power of two. var powtab = []int{1, 3, 6, 9, 13, 16, 19, 23, 26} @@ -243,19 +353,6 @@ out: return bits, overflow } -// Compute exact floating-point integer from d's digits. -// Caller is responsible for avoiding overflow. -func (d *decimal) atof64int() float64 { - f := 0.0 - for i := 0; i < d.nd; i++ { - f = f*10 + float64(d.d[i]-'0') - } - if d.neg { - f = -f - } - return f -} - func (d *decimal) atof32int() float32 { f := float32(0) for i := 0; i < d.nd; i++ { @@ -267,18 +364,6 @@ func (d *decimal) atof32int() float32 { return f } -// Reads a uint64 decimal mantissa, which might be truncated. -func (d *decimal) atou64() (mant uint64, digits int) { - const uint64digits = 19 - for i, c := range d.d[:d.nd] { - if i == uint64digits { - return mant, i - } - mant = 10*mant + uint64(c-'0') - } - return mant, d.nd -} - // Exact powers of 10. var float64pow10 = []float64{ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, @@ -287,69 +372,74 @@ var float64pow10 = []float64{ } var float32pow10 = []float32{1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10} -// If possible to convert decimal d to 64-bit float f exactly, +// If possible to convert decimal representation to 64-bit float f exactly, // entirely in floating-point math, do so, avoiding the expense of decimalToFloatBits. // Three common cases: // value is exact integer // value is exact integer * exact power of ten // value is exact integer / exact power of ten // These all produce potentially inexact but correctly rounded answers. -func (d *decimal) atof64() (f float64, ok bool) { - // Exact integers are <= 10^15. - // Exact powers of ten are <= 10^22. - if d.nd > 15 { +func atof64exact(mantissa uint64, exp int, neg bool) (f float64, ok bool) { + if mantissa>>float64info.mantbits != 0 { return } + f = float64(mantissa) + if neg { + f = -f + } switch { - case d.dp == d.nd: // int - f := d.atof64int() + case exp == 0: + // an integer. return f, true - - case d.dp > d.nd && d.dp <= 15+22: // int * 10^k - f := d.atof64int() - k := d.dp - d.nd + // Exact integers are <= 10^15. + // Exact powers of ten are <= 10^22. + case exp > 0 && exp <= 15+22: // int * 10^k // If exponent is big but number of digits is not, // can move a few zeros into the integer part. - if k > 22 { - f *= float64pow10[k-22] - k = 22 + if exp > 22 { + f *= float64pow10[exp-22] + exp = 22 } - return f * float64pow10[k], true - - case d.dp < d.nd && d.nd-d.dp <= 22: // int / 10^k - f := d.atof64int() - return f / float64pow10[d.nd-d.dp], true + if f > 1e15 || f < -1e15 { + // the exponent was really too large. + return + } + return f * float64pow10[exp], true + case exp < 0 && exp >= -22: // int / 10^k + return f / float64pow10[-exp], true } return } -// If possible to convert decimal d to 32-bit float f exactly, +// If possible to compute mantissa*10^exp to 32-bit float f exactly, // entirely in floating-point math, do so, avoiding the machinery above. -func (d *decimal) atof32() (f float32, ok bool) { - // Exact integers are <= 10^7. - // Exact powers of ten are <= 10^10. - if d.nd > 7 { +func atof32exact(mantissa uint64, exp int, neg bool) (f float32, ok bool) { + if mantissa>>float32info.mantbits != 0 { return } + f = float32(mantissa) + if neg { + f = -f + } switch { - case d.dp == d.nd: // int - f := d.atof32int() + case exp == 0: return f, true - - case d.dp > d.nd && d.dp <= 7+10: // int * 10^k - f := d.atof32int() - k := d.dp - d.nd + // Exact integers are <= 10^7. + // Exact powers of ten are <= 10^10. + case exp > 0 && exp <= 7+10: // int * 10^k // If exponent is big but number of digits is not, // can move a few zeros into the integer part. - if k > 10 { - f *= float32pow10[k-10] - k = 10 + if exp > 10 { + f *= float32pow10[exp-10] + exp = 10 } - return f * float32pow10[k], true - - case d.dp < d.nd && d.nd-d.dp <= 10: // int / 10^k - f := d.atof32int() - return f / float32pow10[d.nd-d.dp], true + if f > 1e7 || f < -1e7 { + // the exponent was really too large. + return + } + return f * float32pow10[exp], true + case exp < 0 && exp >= -10: // int / 10^k + return f / float32pow10[-exp], true } return } @@ -361,15 +451,32 @@ func atof32(s string) (f float32, err error) { return float32(val), nil } + if optimize { + // Parse mantissa and exponent. + mantissa, exp, neg, trunc, ok := readFloat(s) + if ok { + // Try pure floating-point arithmetic conversion. + if !trunc { + if f, ok := atof32exact(mantissa, exp, neg); ok { + return f, nil + } + } + // Try another fast path. + ext := new(extFloat) + if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float32info); ok { + b, ovf := ext.floatBits(&float32info) + f = math.Float32frombits(uint32(b)) + if ovf { + err = rangeError(fnParseFloat, s) + } + return f, err + } + } + } var d decimal if !d.set(s) { return 0, syntaxError(fnParseFloat, s) } - if optimize { - if f, ok := d.atof32(); ok { - return f, nil - } - } b, ovf := d.floatBits(&float32info) f = math.Float32frombits(uint32(b)) if ovf { @@ -383,26 +490,32 @@ func atof64(s string) (f float64, err error) { return val, nil } - var d decimal - if !d.set(s) { - return 0, syntaxError(fnParseFloat, s) - } if optimize { - if f, ok := d.atof64(); ok { - return f, nil - } - - // Try another fast path. - ext := new(extFloat) - if ok := ext.AssignDecimal(&d); ok { - b, ovf := ext.floatBits() - f = math.Float64frombits(b) - if ovf { - err = rangeError(fnParseFloat, s) + // Parse mantissa and exponent. + mantissa, exp, neg, trunc, ok := readFloat(s) + if ok { + // Try pure floating-point arithmetic conversion. + if !trunc { + if f, ok := atof64exact(mantissa, exp, neg); ok { + return f, nil + } + } + // Try another fast path. + ext := new(extFloat) + if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok { + b, ovf := ext.floatBits(&float64info) + f = math.Float64frombits(b) + if ovf { + err = rangeError(fnParseFloat, s) + } + return f, err } - return f, err } } + var d decimal + if !d.set(s) { + return 0, syntaxError(fnParseFloat, s) + } b, ovf := d.floatBits(&float64info) f = math.Float64frombits(b) if ovf { diff --git a/src/pkg/strconv/atof_test.go b/src/pkg/strconv/atof_test.go index 599502382..ba4933218 100644 --- a/src/pkg/strconv/atof_test.go +++ b/src/pkg/strconv/atof_test.go @@ -110,6 +110,7 @@ var atoftests = []atofTest{ {"1e", "0", ErrSyntax}, {"1e-", "0", ErrSyntax}, {".e-1", "0", ErrSyntax}, + {"1\x00.2", "0", ErrSyntax}, // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ {"2.2250738585072012e-308", "2.2250738585072014e-308", nil}, @@ -134,6 +135,54 @@ var atoftests = []atofTest{ {"1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1", "1.0000000000000002", nil}, } +var atof32tests = []atofTest{ + // Exactly halfway between 1 and the next float32. + // Round to even (down). + {"1.000000059604644775390625", "1", nil}, + // Slightly lower. + {"1.000000059604644775390624", "1", nil}, + // Slightly higher. + {"1.000000059604644775390626", "1.0000001", nil}, + // Slightly higher, but you have to read all the way to the end. + {"1.000000059604644775390625" + strings.Repeat("0", 10000) + "1", "1.0000001", nil}, + + // largest float32: (1<<128) * (1 - 2^-24) + {"340282346638528859811704183484516925440", "3.4028235e+38", nil}, + {"-340282346638528859811704183484516925440", "-3.4028235e+38", nil}, + // next float32 - too large + {"3.4028236e38", "+Inf", ErrRange}, + {"-3.4028236e38", "-Inf", ErrRange}, + // the border is 3.40282356779...e+38 + // borderline - okay + {"3.402823567e38", "3.4028235e+38", nil}, + {"-3.402823567e38", "-3.4028235e+38", nil}, + // borderline - too large + {"3.4028235678e38", "+Inf", ErrRange}, + {"-3.4028235678e38", "-Inf", ErrRange}, + + // Denormals: less than 2^-126 + {"1e-38", "1e-38", nil}, + {"1e-39", "1e-39", nil}, + {"1e-40", "1e-40", nil}, + {"1e-41", "1e-41", nil}, + {"1e-42", "1e-42", nil}, + {"1e-43", "1e-43", nil}, + {"1e-44", "1e-44", nil}, + {"6e-45", "6e-45", nil}, // 4p-149 = 5.6e-45 + {"5e-45", "6e-45", nil}, + // Smallest denormal + {"1e-45", "1e-45", nil}, // 1p-149 = 1.4e-45 + {"2e-45", "1e-45", nil}, + + // 2^92 = 8388608p+69 = 4951760157141521099596496896 (4.9517602e27) + // is an exact power of two that needs 8 decimal digits to be correctly + // parsed back. + // The float32 before is 16777215p+68 = 4.95175986e+27 + // The halfway is 4.951760009. A bad algorithm that thinks the previous + // float32 is 8388607p+69 will shorten incorrectly to 4.95176e+27. + {"4951760157141521099596496896", "4.9517602e+27", nil}, +} + type atofSimpleTest struct { x float64 s string @@ -154,6 +203,12 @@ func init() { test.err = &NumError{"ParseFloat", test.in, test.err} } } + for i := range atof32tests { + test := &atof32tests[i] + if test.err != nil { + test.err = &NumError{"ParseFloat", test.in, test.err} + } + } // Generate random inputs for tests and benchmarks rand.Seed(time.Now().UnixNano()) @@ -206,6 +261,19 @@ func testAtof(t *testing.T, opt bool) { } } } + for _, test := range atof32tests { + out, err := ParseFloat(test.in, 32) + out32 := float32(out) + if float64(out32) != out { + t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32)) + continue + } + outs := FormatFloat(float64(out32), 'g', -1, 32) + if outs != test.out || !reflect.DeepEqual(err, test.err) { + t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v # %v", + test.in, out32, err, test.out, test.err, out) + } + } SetOptimize(oldopt) } @@ -264,6 +332,35 @@ func TestRoundTrip(t *testing.T) { } } +// TestRoundTrip32 tries a fraction of all finite positive float32 values. +func TestRoundTrip32(t *testing.T) { + step := uint32(997) + if testing.Short() { + step = 99991 + } + count := 0 + for i := uint32(0); i < 0xff<<23; i += step { + f := math.Float32frombits(i) + if i&1 == 1 { + f = -f // negative + } + s := FormatFloat(float64(f), 'g', -1, 32) + + parsed, err := ParseFloat(s, 32) + parsed32 := float32(parsed) + switch { + case err != nil: + t.Errorf("ParseFloat(%q, 32) gave error %s", s, err) + case float64(parsed32) != parsed: + t.Errorf("ParseFloat(%q, 32) = %v, not a float32 (nearest is %v)", s, parsed, parsed32) + case parsed32 != f: + t.Errorf("ParseFloat(%q, 32) = %b (expected %b)", s, parsed32, f) + } + count++ + } + t.Logf("tested %d float32's", count) +} + func BenchmarkAtof64Decimal(b *testing.B) { for i := 0; i < b.N; i++ { ParseFloat("33909", 64) @@ -299,3 +396,35 @@ func BenchmarkAtof64RandomFloats(b *testing.B) { ParseFloat(benchmarksRandomNormal[i%1024], 64) } } + +func BenchmarkAtof32Decimal(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseFloat("33909", 32) + } +} + +func BenchmarkAtof32Float(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseFloat("339.778", 32) + } +} + +func BenchmarkAtof32FloatExp(b *testing.B) { + for i := 0; i < b.N; i++ { + ParseFloat("12.3456e32", 32) + } +} + +var float32strings [4096]string + +func BenchmarkAtof32Random(b *testing.B) { + n := uint32(997) + for i := range float32strings { + n = (99991*n + 42) % (0xff << 23) + float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', -1, 32) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + ParseFloat(float32strings[i%4096], 32) + } +} diff --git a/src/pkg/strconv/atoi.go b/src/pkg/strconv/atoi.go index 59ef264d1..21c690096 100644 --- a/src/pkg/strconv/atoi.go +++ b/src/pkg/strconv/atoi.go @@ -20,7 +20,7 @@ type NumError struct { } func (e *NumError) Error() string { - return "strconv." + e.Func + ": " + `parsing "` + e.Num + `": ` + e.Err.Error() + return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error() } func syntaxError(fn, str string) *NumError { @@ -44,7 +44,7 @@ func cutoff64(base int) uint64 { } // ParseUint is like ParseInt but for unsigned numbers. -func ParseUint(s string, b int, bitSize int) (n uint64, err error) { +func ParseUint(s string, base int, bitSize int) (n uint64, err error) { var cutoff, maxVal uint64 if bitSize == 0 { @@ -57,32 +57,32 @@ func ParseUint(s string, b int, bitSize int) (n uint64, err error) { err = ErrSyntax goto Error - case 2 <= b && b <= 36: + case 2 <= base && base <= 36: // valid base; nothing to do - case b == 0: + case base == 0: // Look for octal, hex prefix. switch { case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): - b = 16 + base = 16 s = s[2:] if len(s) < 1 { err = ErrSyntax goto Error } case s[0] == '0': - b = 8 + base = 8 default: - b = 10 + base = 10 } default: - err = errors.New("invalid base " + Itoa(b)) + err = errors.New("invalid base " + Itoa(base)) goto Error } n = 0 - cutoff = cutoff64(b) + cutoff = cutoff64(base) maxVal = 1<= b { + if int(v) >= base { n = 0 err = ErrSyntax goto Error } if n >= cutoff { - // n*b overflows + // n*base overflows n = 1<<64 - 1 err = ErrRange goto Error } - n *= uint64(b) + n *= uint64(base) n1 := n + uint64(v) if n1 < n || n1 > maxVal { diff --git a/src/pkg/strconv/atoi_test.go b/src/pkg/strconv/atoi_test.go index d0e7b61db..940757307 100644 --- a/src/pkg/strconv/atoi_test.go +++ b/src/pkg/strconv/atoi_test.go @@ -5,6 +5,7 @@ package strconv_test import ( + "errors" "reflect" . "strconv" "testing" @@ -146,6 +147,16 @@ var atoi32tests = []atoi32Test{ {"-2147483649", -1 << 31, ErrRange}, } +type numErrorTest struct { + num, want string +} + +var numErrorTests = []numErrorTest{ + {"0", `strconv.ParseFloat: parsing "0": failed`}, + {"`", "strconv.ParseFloat: parsing \"`\": failed"}, + {"1\x00.2", `strconv.ParseFloat: parsing "1\x00.2": failed`}, +} + func init() { // The atoi routines return NumErrors wrapping // the error and the string. Convert the tables above. @@ -277,6 +288,19 @@ func TestParseInt(t *testing.T) { } } +func TestNumError(t *testing.T) { + for _, test := range numErrorTests { + err := &NumError{ + Func: "ParseFloat", + Num: test.num, + Err: errors.New("failed"), + } + if got := err.Error(); got != test.want { + t.Errorf(`(&NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want) + } + } +} + func BenchmarkAtoi(b *testing.B) { for i := 0; i < b.N; i++ { ParseInt("12345678", 10, 0) diff --git a/src/pkg/strconv/decimal.go b/src/pkg/strconv/decimal.go index a75071dcc..42601283d 100644 --- a/src/pkg/strconv/decimal.go +++ b/src/pkg/strconv/decimal.go @@ -79,7 +79,7 @@ func trim(a *decimal) { // Assign v to a. func (a *decimal) Assign(v uint64) { - var buf [50]byte + var buf [24]byte // Write reversed decimal in buf. n := 0 diff --git a/src/pkg/strconv/extfloat.go b/src/pkg/strconv/extfloat.go index aa5e5607c..b7eaaa61b 100644 --- a/src/pkg/strconv/extfloat.go +++ b/src/pkg/strconv/extfloat.go @@ -4,8 +4,6 @@ package strconv -import "math" - // An extFloat represents an extended floating-point number, with more // precision than a float64. It does not try to save bits: the // number represented by the structure is mant*(2^exp), with a negative @@ -127,8 +125,7 @@ var powersOfTen = [...]extFloat{ // floatBits returns the bits of the float64 that best approximates // the extFloat passed as receiver. Overflow is set to true if // the resulting float64 is ±Inf. -func (f *extFloat) floatBits() (bits uint64, overflow bool) { - flt := &float64info +func (f *extFloat) floatBits(flt *floatInfo) (bits uint64, overflow bool) { f.Normalize() exp := f.exp + 63 @@ -140,7 +137,7 @@ func (f *extFloat) floatBits() (bits uint64, overflow bool) { exp += n } - // Extract 1+flt.mantbits bits. + // Extract 1+flt.mantbits bits from the 64-bit mantissa. mant := f.mant >> (63 - flt.mantbits) if f.mant&(1<<(62-flt.mantbits)) != 0 { // Round up. @@ -155,22 +152,14 @@ func (f *extFloat) floatBits() (bits uint64, overflow bool) { // Infinities. if exp-flt.bias >= 1<>(flt.expbits+flt.mantbits) != 0 - expBiased := int(bits>>flt.mantbits) & (1<>uint(-f.exp))<>= uint(-f.exp) + f.exp = 0 + return *f, *f + } + expBiased := exp - flt.bias upper = extFloat{mant: 2*f.mant + 1, exp: f.exp - 1, neg: f.neg} - if mant != 0 || expBiased == 1 { + if mant != 1<>(64-32) == 0 { + mant <<= 32 + exp -= 32 + } + if mant>>(64-16) == 0 { + mant <<= 16 + exp -= 16 } - for f.mant < (1 << 63) { - f.mant <<= 1 - f.exp -= 1 + if mant>>(64-8) == 0 { + mant <<= 8 + exp -= 8 } - return uint(exp_before - f.exp) + if mant>>(64-4) == 0 { + mant <<= 4 + exp -= 4 + } + if mant>>(64-2) == 0 { + mant <<= 2 + exp -= 2 + } + if mant>>(64-1) == 0 { + mant <<= 1 + exp -= 1 + } + shift = uint(f.exp - exp) + f.mant, f.exp = mant, exp + return } // Multiply sets f to the product f*g: the result is correctly rounded, @@ -264,24 +255,22 @@ var uint64pow10 = [...]uint64{ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, } -// AssignDecimal sets f to an approximate value of the decimal d. It +// AssignDecimal sets f to an approximate value mantissa*10^exp. It // returns true if the value represented by f is guaranteed to be the -// best approximation of d after being rounded to a float64. -func (f *extFloat) AssignDecimal(d *decimal) (ok bool) { +// best approximation of d after being rounded to a float64 or +// float32 depending on flt. +func (f *extFloat) AssignDecimal(mantissa uint64, exp10 int, neg bool, trunc bool, flt *floatInfo) (ok bool) { const uint64digits = 19 const errorscale = 8 - mant10, digits := d.atou64() - exp10 := d.dp - digits errors := 0 // An upper bound for error, computed in errorscale*ulp. - - if digits < d.nd { + if trunc { // the decimal number was truncated. errors += errorscale / 2 } - f.mant = mant10 + f.mant = mantissa f.exp = 0 - f.neg = d.neg + f.neg = neg // Multiply by powers of ten. i := (exp10 - firstPowerOfTen) / stepPowerOfTen @@ -291,9 +280,9 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) { adjExp := (exp10 - firstPowerOfTen) % stepPowerOfTen // We multiply by exp%step - if digits+adjExp <= uint64digits { - // We can multiply the mantissa - f.mant *= uint64(float64pow10[adjExp]) + if adjExp < uint64digits && mantissa < uint64pow10[uint64digits-adjExp] { + // We can multiply the mantissa exactly. + f.mant *= uint64pow10[adjExp] f.Normalize() } else { f.Normalize() @@ -318,10 +307,10 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) { // The 64 bits mantissa is 1 + 52 bits for float64 + 11 extra bits. // // In many cases the approximation will be good enough. - const denormalExp = -1023 - 63 - flt := &float64info + denormalExp := flt.bias - 63 var extrabits uint if f.exp <= denormalExp { + // f.mant * 2^f.exp is smaller than 2^(flt.bias+1). extrabits = uint(63 - flt.mantbits + 1 + uint(denormalExp-f.exp)) } else { extrabits = uint(63 - flt.mantbits) @@ -344,16 +333,17 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) { // f by an approximate power of ten 10^-exp, and returns exp10, so // that f*10^exp10 has the same value as the old f, up to an ulp, // as well as the index of 10^-exp in the powersOfTen table. -// The arguments expMin and expMax constrain the final value of the -// binary exponent of f. -func (f *extFloat) frexp10(expMin, expMax int) (exp10, index int) { - // it is illegal to call this function with a too restrictive exponent range. - if expMax-expMin <= 25 { - panic("strconv: invalid exponent range") - } +func (f *extFloat) frexp10() (exp10, index int) { + // The constants expMin and expMax constrain the final value of the + // binary exponent of f. We want a small integral part in the result + // because finding digits of an integer requires divisions, whereas + // digits of the fractional part can be found by repeatedly multiplying + // by 10. + const expMin = -60 + const expMax = -32 // Find power of ten such that x * 10^n has a binary exponent - // between expMin and expMax - approxExp10 := -(f.exp + 100) * 28 / 93 // log(10)/log(2) is close to 93/28. + // between expMin and expMax. + approxExp10 := ((expMin+expMax)/2 - f.exp) * 28 / 93 // log(10)/log(2) is close to 93/28. i := (approxExp10 - firstPowerOfTen) / stepPowerOfTen Loop: for { @@ -375,26 +365,202 @@ Loop: } // frexp10Many applies a common shift by a power of ten to a, b, c. -func frexp10Many(expMin, expMax int, a, b, c *extFloat) (exp10 int) { - exp10, i := c.frexp10(expMin, expMax) +func frexp10Many(a, b, c *extFloat) (exp10 int) { + exp10, i := c.frexp10() a.Multiply(powersOfTen[i]) b.Multiply(powersOfTen[i]) return } +// FixedDecimal stores in d the first n significant digits +// of the decimal representation of f. It returns false +// if it cannot be sure of the answer. +func (f *extFloat) FixedDecimal(d *decimalSlice, n int) bool { + if f.mant == 0 { + d.nd = 0 + d.dp = 0 + d.neg = f.neg + return true + } + if n == 0 { + panic("strconv: internal error: extFloat.FixedDecimal called with n == 0") + } + // Multiply by an appropriate power of ten to have a reasonable + // number to process. + f.Normalize() + exp10, _ := f.frexp10() + + shift := uint(-f.exp) + integer := uint32(f.mant >> shift) + fraction := f.mant - (uint64(integer) << shift) + ε := uint64(1) // ε is the uncertainty we have on the mantissa of f. + + // Write exactly n digits to d. + needed := n // how many digits are left to write. + integerDigits := 0 // the number of decimal digits of integer. + pow10 := uint64(1) // the power of ten by which f was scaled. + for i, pow := 0, uint64(1); i < 20; i++ { + if pow > uint64(integer) { + integerDigits = i + break + } + pow *= 10 + } + rest := integer + if integerDigits > needed { + // the integral part is already large, trim the last digits. + pow10 = uint64pow10[integerDigits-needed] + integer /= uint32(pow10) + rest -= integer * uint32(pow10) + } else { + rest = 0 + } + + // Write the digits of integer: the digits of rest are omitted. + var buf [32]byte + pos := len(buf) + for v := integer; v > 0; { + v1 := v / 10 + v -= 10 * v1 + pos-- + buf[pos] = byte(v + '0') + v = v1 + } + for i := pos; i < len(buf); i++ { + d.d[i-pos] = buf[i] + } + nd := len(buf) - pos + d.nd = nd + d.dp = integerDigits + exp10 + needed -= nd + + if needed > 0 { + if rest != 0 || pow10 != 1 { + panic("strconv: internal error, rest != 0 but needed > 0") + } + // Emit digits for the fractional part. Each time, 10*fraction + // fits in a uint64 without overflow. + for needed > 0 { + fraction *= 10 + ε *= 10 // the uncertainty scales as we multiply by ten. + if 2*ε > 1<> shift + d.d[nd] = byte(digit + '0') + fraction -= digit << shift + nd++ + needed-- + } + d.nd = nd + } + + // We have written a truncation of f (a numerator / 10^d.dp). The remaining part + // can be interpreted as a small number (< 1) to be added to the last digit of the + // numerator. + // + // If rest > 0, the amount is: + // (rest< 0 guarantees that pow10 << shift does not overflow a uint64. + // + // If rest = 0, pow10 == 1 and the amount is + // fraction / (1 << shift) + // fraction being known with a ±ε uncertainty. + // + // We pass this information to the rounding routine for adjustment. + + ok := adjustLastDigitFixed(d, uint64(rest)<= 0; i-- { + if d.d[i] != '0' { + d.nd = i + 1 + break + } + } + return true +} + +// adjustLastDigitFixed assumes d contains the representation of the integral part +// of some number, whose fractional part is num / (den << shift). The numerator +// num is only known up to an uncertainty of size ε, assumed to be less than +// (den << shift)/2. +// +// It will increase the last digit by one to account for correct rounding, typically +// when the fractional part is greater than 1/2, and will return false if ε is such +// that no correct answer can be given. +func adjustLastDigitFixed(d *decimalSlice, num, den uint64, shift uint, ε uint64) bool { + if num > den< den< den< (den< den<= 0; i-- { + if d.d[i] == '9' { + d.nd-- + } else { + break + } + } + if i < 0 { + d.d[0] = '1' + d.nd = 1 + d.dp++ + } else { + d.d[i]++ + } + return true + } + return false +} + // ShortestDecimal stores in d the shortest decimal representation of f // which belongs to the open interval (lower, upper), where f is supposed // to lie. It returns false whenever the result is unsure. The implementation // uses the Grisu3 algorithm. -func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool { +func (f *extFloat) ShortestDecimal(d *decimalSlice, lower, upper *extFloat) bool { if f.mant == 0 { - d.d[0] = '0' - d.nd = 1 + d.nd = 0 d.dp = 0 d.neg = f.neg + return true + } + if f.exp == 0 && *lower == *f && *lower == *upper { + // an exact integer. + var buf [24]byte + n := len(buf) - 1 + for v := f.mant; v > 0; { + v1 := v / 10 + v -= 10 * v1 + buf[n] = byte(v + '0') + n-- + v = v1 + } + nd := len(buf) - n - 1 + for i := 0; i < nd; i++ { + d.d[i] = buf[n+1+i] + } + d.nd, d.dp = nd, nd + for d.nd > 0 && d.d[d.nd-1] == '0' { + d.nd-- + } + if d.nd == 0 { + d.dp = 0 + } + d.neg = f.neg + return true } - const minExp = -60 - const maxExp = -32 upper.Normalize() // Uniformize exponents. if f.exp > upper.exp { @@ -406,7 +572,7 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool { lower.exp = upper.exp } - exp10 := frexp10Many(minExp, maxExp, lower, f, upper) + exp10 := frexp10Many(lower, f, upper) // Take a safety margin due to rounding in frexp10Many, but we lose precision. upper.mant++ lower.mant-- @@ -424,10 +590,12 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool { // Count integral digits: there are at most 10. var integerDigits int - for i, pow := range uint64pow10 { - if uint64(integer) >= pow { - integerDigits = i + 1 + for i, pow := 0, uint64(1); i < 20; i++ { + if pow > uint64(integer) { + integerDigits = i + break } + pow *= 10 } for i := 0; i < integerDigits; i++ { pow := uint64pow10[integerDigits-i-1] @@ -471,11 +639,11 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool { return false } -// adjustLastDigit modifies d = x-currentDiff*ε, to get closest to +// adjustLastDigit modifies d = x-currentDiff*ε, to get closest to // d = x-targetDiff*ε, without becoming smaller than x-maxDiff*ε. // It assumes that a decimal digit is worth ulpDecimal*ε, and that // all data is known with a error estimate of ulpBinary*ε. -func adjustLastDigit(d *decimal, currentDiff, targetDiff, maxDiff, ulpDecimal, ulpBinary uint64) bool { +func adjustLastDigit(d *decimalSlice, currentDiff, targetDiff, maxDiff, ulpDecimal, ulpBinary uint64) bool { if ulpDecimal < 2*ulpBinary { // Approximation is too wide. return false diff --git a/src/pkg/strconv/fp_test.go b/src/pkg/strconv/fp_test.go index 171defa44..6de2f8bc6 100644 --- a/src/pkg/strconv/fp_test.go +++ b/src/pkg/strconv/fp_test.go @@ -7,7 +7,6 @@ package strconv_test import ( "bufio" "fmt" - "io" "os" "strconv" "strings" @@ -96,31 +95,22 @@ func myatof32(s string) (f float32, ok bool) { } func TestFp(t *testing.T) { - f, err := os.Open("testfp.txt") + f, err := os.Open("testdata/testfp.txt") if err != nil { - t.Fatal("testfp: open testfp.txt:", err) + t.Fatal("testfp: open testdata/testfp.txt:", err) } defer f.Close() - b := bufio.NewReader(f) + s := bufio.NewScanner(f) - lineno := 0 - for { - line, err2 := b.ReadString('\n') - if err2 == io.EOF { - break - } - if err2 != nil { - t.Fatal("testfp: read testfp.txt: " + err2.Error()) - } - line = line[0 : len(line)-1] - lineno++ + for lineno := 1; s.Scan(); lineno++ { + line := s.Text() if len(line) == 0 || line[0] == '#' { continue } a := strings.Split(line, " ") if len(a) != 4 { - t.Error("testfp.txt:", lineno, ": wrong field count") + t.Error("testdata/testfp.txt:", lineno, ": wrong field count") continue } var s string @@ -130,22 +120,25 @@ func TestFp(t *testing.T) { var ok bool v, ok = myatof64(a[2]) if !ok { - t.Error("testfp.txt:", lineno, ": cannot atof64 ", a[2]) + t.Error("testdata/testfp.txt:", lineno, ": cannot atof64 ", a[2]) continue } s = fmt.Sprintf(a[1], v) case "float32": v1, ok := myatof32(a[2]) if !ok { - t.Error("testfp.txt:", lineno, ": cannot atof32 ", a[2]) + t.Error("testdata/testfp.txt:", lineno, ": cannot atof32 ", a[2]) continue } s = fmt.Sprintf(a[1], v1) v = float64(v1) } if s != a[3] { - t.Error("testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ", + t.Error("testdata/testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ", "want ", a[3], " got ", s) } } + if s.Err() != nil { + t.Fatal("testfp: read testdata/testfp.txt: ", s.Err()) + } } diff --git a/src/pkg/strconv/ftoa.go b/src/pkg/strconv/ftoa.go index 8eefbee79..1a9c41b85 100644 --- a/src/pkg/strconv/ftoa.go +++ b/src/pkg/strconv/ftoa.go @@ -98,42 +98,79 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte { return fmtB(dst, neg, mant, exp, flt) } + if !optimize { + return bigFtoa(dst, prec, fmt, neg, mant, exp, flt) + } + + var digs decimalSlice + ok := false // Negative precision means "only as much as needed to be exact." shortest := prec < 0 - - d := new(decimal) if shortest { - ok := false - if optimize && bitSize == 64 { - // Try Grisu3 algorithm. - f := new(extFloat) - lower, upper := f.AssignComputeBounds(val) - ok = f.ShortestDecimal(d, &lower, &upper) - } + // Try Grisu3 algorithm. + f := new(extFloat) + lower, upper := f.AssignComputeBounds(mant, exp, neg, flt) + var buf [32]byte + digs.d = buf[:] + ok = f.ShortestDecimal(&digs, &lower, &upper) if !ok { - // Create exact decimal representation. - // The shift is exp - flt.mantbits because mant is a 1-bit integer - // followed by a flt.mantbits fraction, and we are treating it as - // a 1+flt.mantbits-bit integer. - d.Assign(mant) - d.Shift(exp - int(flt.mantbits)) - roundShortest(d, mant, exp, flt) + return bigFtoa(dst, prec, fmt, neg, mant, exp, flt) } // Precision for shortest representation mode. - if prec < 0 { - switch fmt { - case 'e', 'E': - prec = d.nd - 1 - case 'f': - prec = max(d.nd-d.dp, 0) - case 'g', 'G': - prec = d.nd + switch fmt { + case 'e', 'E': + prec = digs.nd - 1 + case 'f': + prec = max(digs.nd-digs.dp, 0) + case 'g', 'G': + prec = digs.nd + } + } else if fmt != 'f' { + // Fixed number of digits. + digits := prec + switch fmt { + case 'e', 'E': + digits++ + case 'g', 'G': + if prec == 0 { + prec = 1 } + digits = prec + } + if digits <= 15 { + // try fast algorithm when the number of digits is reasonable. + var buf [24]byte + digs.d = buf[:] + f := extFloat{mant, exp - int(flt.mantbits), neg} + ok = f.FixedDecimal(&digs, digits) + } + } + if !ok { + return bigFtoa(dst, prec, fmt, neg, mant, exp, flt) + } + return formatDigits(dst, shortest, neg, digs, prec, fmt) +} + +// bigFtoa uses multiprecision computations to format a float. +func bigFtoa(dst []byte, prec int, fmt byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte { + d := new(decimal) + d.Assign(mant) + d.Shift(exp - int(flt.mantbits)) + var digs decimalSlice + shortest := prec < 0 + if shortest { + roundShortest(d, mant, exp, flt) + digs = decimalSlice{d: d.d[:], nd: d.nd, dp: d.dp} + // Precision for shortest representation mode. + switch fmt { + case 'e', 'E': + prec = digs.nd - 1 + case 'f': + prec = max(digs.nd-digs.dp, 0) + case 'g', 'G': + prec = digs.nd } } else { - // Create exact decimal representation. - d.Assign(mant) - d.Shift(exp - int(flt.mantbits)) // Round appropriately. switch fmt { case 'e', 'E': @@ -146,18 +183,22 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte { } d.Round(prec) } + digs = decimalSlice{d: d.d[:], nd: d.nd, dp: d.dp} } + return formatDigits(dst, shortest, neg, digs, prec, fmt) +} +func formatDigits(dst []byte, shortest bool, neg bool, digs decimalSlice, prec int, fmt byte) []byte { switch fmt { case 'e', 'E': - return fmtE(dst, neg, d, prec, fmt) + return fmtE(dst, neg, digs, prec, fmt) case 'f': - return fmtF(dst, neg, d, prec) + return fmtF(dst, neg, digs, prec) case 'g', 'G': // trailing fractional zeros in 'e' form will be trimmed. eprec := prec - if eprec > d.nd && d.nd >= d.dp { - eprec = d.nd + if eprec > digs.nd && digs.nd >= digs.dp { + eprec = digs.nd } // %e is used if the exponent from the conversion // is less than -4 or greater than or equal to the precision. @@ -165,17 +206,17 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte { if shortest { eprec = 6 } - exp := d.dp - 1 + exp := digs.dp - 1 if exp < -4 || exp >= eprec { - if prec > d.nd { - prec = d.nd + if prec > digs.nd { + prec = digs.nd } - return fmtE(dst, neg, d, prec-1, fmt+'e'-'g') + return fmtE(dst, neg, digs, prec-1, fmt+'e'-'g') } - if prec > d.dp { - prec = d.nd + if prec > digs.dp { + prec = digs.nd } - return fmtF(dst, neg, d, max(prec-d.dp, 0)) + return fmtF(dst, neg, digs, max(prec-digs.dp, 0)) } // unknown format @@ -214,7 +255,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { // d = mant << (exp - mantbits) // Next highest floating point number is mant+1 << exp-mantbits. - // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1. + // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1. upper := new(decimal) upper.Assign(mant*2 + 1) upper.Shift(exp - int(flt.mantbits) - 1) @@ -224,7 +265,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { // unless mant-1 drops the significant bit and exp is not the minimum exp, // in which case the next lowest is mant*2-1 << exp-mantbits-1. // Either way, call it mantlo << explo-mantbits. - // Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1. + // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1. var mantlo uint64 var explo int if mant > 1< 0 { dst = append(dst, '.') - for i := 1; i <= prec; i++ { - ch = '0' - if i < d.nd { - ch = d.d[i] - } - dst = append(dst, ch) + i := 1 + m := d.nd + prec + 1 - max(d.nd, prec+1) + for i < m { + dst = append(dst, d.d[i]) + i++ + } + for i <= prec { + dst = append(dst, '0') + i++ } } @@ -335,17 +385,20 @@ func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte { i-- buf[i] = byte(exp + '0') - // leading zeroes - if i > len(buf)-2 { - i-- - buf[i] = '0' + switch i { + case 0: + dst = append(dst, buf[0], buf[1], buf[2]) + case 1: + dst = append(dst, buf[1], buf[2]) + case 2: + // leading zeroes + dst = append(dst, '0', buf[2]) } - - return append(dst, buf[i:]...) + return dst } // %f: -ddddddd.ddddd -func fmtF(dst []byte, neg bool, d *decimal, prec int) []byte { +func fmtF(dst []byte, neg bool, d decimalSlice, prec int) []byte { // sign if neg { dst = append(dst, '-') diff --git a/src/pkg/strconv/ftoa_test.go b/src/pkg/strconv/ftoa_test.go index 7d8617a85..39b861547 100644 --- a/src/pkg/strconv/ftoa_test.go +++ b/src/pkg/strconv/ftoa_test.go @@ -163,6 +163,7 @@ func TestFtoaRandom(t *testing.T) { for i := 0; i < N; i++ { bits := uint64(rand.Uint32())<<32 | uint64(rand.Uint32()) x := math.Float64frombits(bits) + shortFast := FormatFloat(x, 'g', -1, 64) SetOptimize(false) shortSlow := FormatFloat(x, 'g', -1, 64) @@ -170,23 +171,15 @@ func TestFtoaRandom(t *testing.T) { if shortSlow != shortFast { t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow) } - } -} -func TestAppendFloatDoesntAllocate(t *testing.T) { - n := numAllocations(func() { - var buf [64]byte - AppendFloat(buf[:0], 1.23, 'g', 5, 64) - }) - want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better - if n != want { - t.Errorf("with local buffer, did %d allocations, want %d", n, want) - } - n = numAllocations(func() { - AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64) - }) - if n != 0 { - t.Errorf("with reused buffer, did %d allocations, want 0", n) + prec := rand.Intn(12) + 5 + shortFast = FormatFloat(x, 'e', prec, 64) + SetOptimize(false) + shortSlow = FormatFloat(x, 'e', prec, 64) + SetOptimize(true) + if shortSlow != shortFast { + t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow) + } } } @@ -220,37 +213,28 @@ func BenchmarkFormatFloatBig(b *testing.B) { } } -func BenchmarkAppendFloatDecimal(b *testing.B) { - dst := make([]byte, 0, 30) +func benchmarkAppendFloat(b *testing.B, f float64, fmt byte, prec, bitSize int) { + dst := make([]byte, 30) for i := 0; i < b.N; i++ { - AppendFloat(dst, 33909, 'g', -1, 64) + AppendFloat(dst[:0], f, fmt, prec, bitSize) } } -func BenchmarkAppendFloat(b *testing.B) { - dst := make([]byte, 0, 30) - for i := 0; i < b.N; i++ { - AppendFloat(dst, 339.7784, 'g', -1, 64) - } -} - -func BenchmarkAppendFloatExp(b *testing.B) { - dst := make([]byte, 0, 30) - for i := 0; i < b.N; i++ { - AppendFloat(dst, -5.09e75, 'g', -1, 64) - } +func BenchmarkAppendFloatDecimal(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 64) } +func BenchmarkAppendFloat(b *testing.B) { benchmarkAppendFloat(b, 339.7784, 'g', -1, 64) } +func BenchmarkAppendFloatExp(b *testing.B) { benchmarkAppendFloat(b, -5.09e75, 'g', -1, 64) } +func BenchmarkAppendFloatNegExp(b *testing.B) { benchmarkAppendFloat(b, -5.11e-95, 'g', -1, 64) } +func BenchmarkAppendFloatBig(b *testing.B) { + benchmarkAppendFloat(b, 123456789123456789123456789, 'g', -1, 64) } -func BenchmarkAppendFloatNegExp(b *testing.B) { - dst := make([]byte, 0, 30) - for i := 0; i < b.N; i++ { - AppendFloat(dst, -5.11e-95, 'g', -1, 64) - } -} +func BenchmarkAppendFloat32Integer(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 32) } +func BenchmarkAppendFloat32ExactFraction(b *testing.B) { benchmarkAppendFloat(b, 3.375, 'g', -1, 32) } +func BenchmarkAppendFloat32Point(b *testing.B) { benchmarkAppendFloat(b, 339.7784, 'g', -1, 32) } +func BenchmarkAppendFloat32Exp(b *testing.B) { benchmarkAppendFloat(b, -5.09e25, 'g', -1, 32) } +func BenchmarkAppendFloat32NegExp(b *testing.B) { benchmarkAppendFloat(b, -5.11e-25, 'g', -1, 32) } -func BenchmarkAppendFloatBig(b *testing.B) { - dst := make([]byte, 0, 30) - for i := 0; i < b.N; i++ { - AppendFloat(dst, 123456789123456789123456789, 'g', -1, 64) - } -} +func BenchmarkAppendFloat64Fixed1(b *testing.B) { benchmarkAppendFloat(b, 123456, 'e', 3, 64) } +func BenchmarkAppendFloat64Fixed2(b *testing.B) { benchmarkAppendFloat(b, 123.456, 'e', 3, 64) } +func BenchmarkAppendFloat64Fixed3(b *testing.B) { benchmarkAppendFloat(b, 1.23456e+78, 'e', 3, 64) } +func BenchmarkAppendFloat64Fixed4(b *testing.B) { benchmarkAppendFloat(b, 1.23456e-78, 'e', 3, 64) } diff --git a/src/pkg/strconv/isprint.go b/src/pkg/strconv/isprint.go index a03a07bfb..db5f0fbae 100644 --- a/src/pkg/strconv/isprint.go +++ b/src/pkg/strconv/isprint.go @@ -3,7 +3,7 @@ package strconv -// (474+134+42)*2 + (180)*4 = 2020 bytes +// (470+136+60)*2 + (218)*4 = 2204 bytes var isPrint16 = []uint16{ 0x0020, 0x007e, @@ -12,7 +12,7 @@ var isPrint16 = []uint16{ 0x0384, 0x0527, 0x0531, 0x0556, 0x0559, 0x058a, - 0x0591, 0x05c7, + 0x058f, 0x05c7, 0x05d0, 0x05ea, 0x05f0, 0x05f4, 0x0606, 0x061b, @@ -23,7 +23,8 @@ var isPrint16 = []uint16{ 0x0800, 0x082d, 0x0830, 0x085b, 0x085e, 0x085e, - 0x0900, 0x098c, + 0x08a0, 0x08ac, + 0x08e4, 0x098c, 0x098f, 0x0990, 0x0993, 0x09b2, 0x09b6, 0x09b9, @@ -99,12 +100,12 @@ var isPrint16 = []uint16{ 0x0eaa, 0x0ebd, 0x0ec0, 0x0ecd, 0x0ed0, 0x0ed9, - 0x0edc, 0x0edd, + 0x0edc, 0x0edf, 0x0f00, 0x0f6c, 0x0f71, 0x0fda, - 0x1000, 0x10c5, - 0x10d0, 0x10fc, - 0x1100, 0x124d, + 0x1000, 0x10c7, + 0x10cd, 0x10cd, + 0x10d0, 0x124d, 0x1250, 0x125d, 0x1260, 0x128d, 0x1290, 0x12b5, @@ -120,8 +121,7 @@ var isPrint16 = []uint16{ 0x1720, 0x1736, 0x1740, 0x1753, 0x1760, 0x1773, - 0x1780, 0x17b3, - 0x17b6, 0x17dd, + 0x1780, 0x17dd, 0x17e0, 0x17e9, 0x17f0, 0x17f9, 0x1800, 0x180d, @@ -145,13 +145,12 @@ var isPrint16 = []uint16{ 0x1aa0, 0x1aad, 0x1b00, 0x1b4b, 0x1b50, 0x1b7c, - 0x1b80, 0x1baa, - 0x1bae, 0x1bb9, - 0x1bc0, 0x1bf3, + 0x1b80, 0x1bf3, 0x1bfc, 0x1c37, 0x1c3b, 0x1c49, 0x1c4d, 0x1c7f, - 0x1cd0, 0x1cf2, + 0x1cc0, 0x1cc7, + 0x1cd0, 0x1cf6, 0x1d00, 0x1de6, 0x1dfc, 0x1f15, 0x1f18, 0x1f1d, @@ -165,7 +164,7 @@ var isPrint16 = []uint16{ 0x2030, 0x205e, 0x2070, 0x2071, 0x2074, 0x209c, - 0x20a0, 0x20b9, + 0x20a0, 0x20ba, 0x20d0, 0x20f0, 0x2100, 0x2189, 0x2190, 0x23f3, @@ -173,12 +172,13 @@ var isPrint16 = []uint16{ 0x2440, 0x244a, 0x2460, 0x2b4c, 0x2b50, 0x2b59, - 0x2c00, 0x2cf1, - 0x2cf9, 0x2d25, - 0x2d30, 0x2d65, + 0x2c00, 0x2cf3, + 0x2cf9, 0x2d27, + 0x2d2d, 0x2d2d, + 0x2d30, 0x2d67, 0x2d6f, 0x2d70, 0x2d7f, 0x2d96, - 0x2da0, 0x2e31, + 0x2da0, 0x2e3b, 0x2e80, 0x2ef3, 0x2f00, 0x2fd5, 0x2ff0, 0x2ffb, @@ -188,16 +188,15 @@ var isPrint16 = []uint16{ 0x3131, 0x31ba, 0x31c0, 0x31e3, 0x31f0, 0x4db5, - 0x4dc0, 0x9fcb, + 0x4dc0, 0x9fcc, 0xa000, 0xa48c, 0xa490, 0xa4c6, 0xa4d0, 0xa62b, - 0xa640, 0xa673, - 0xa67c, 0xa697, - 0xa6a0, 0xa6f7, - 0xa700, 0xa791, - 0xa7a0, 0xa7a9, - 0xa7fa, 0xa82b, + 0xa640, 0xa697, + 0xa69f, 0xa6f7, + 0xa700, 0xa793, + 0xa7a0, 0xa7aa, + 0xa7f8, 0xa82b, 0xa830, 0xa839, 0xa840, 0xa877, 0xa880, 0xa8c4, @@ -212,7 +211,7 @@ var isPrint16 = []uint16{ 0xaa50, 0xaa59, 0xaa5c, 0xaa7b, 0xaa80, 0xaac2, - 0xaadb, 0xaadf, + 0xaadb, 0xaaf6, 0xab01, 0xab06, 0xab09, 0xab0e, 0xab11, 0xab16, @@ -222,8 +221,7 @@ var isPrint16 = []uint16{ 0xac00, 0xd7a3, 0xd7b0, 0xd7c6, 0xd7cb, 0xd7fb, - 0xf900, 0xfa2d, - 0xfa30, 0xfa6d, + 0xf900, 0xfa6d, 0xfa70, 0xfad9, 0xfb00, 0xfb06, 0xfb13, 0xfb17, @@ -252,8 +250,11 @@ var isNotPrint16 = []uint16{ 0x03a2, 0x0560, 0x0588, + 0x0590, 0x06dd, 0x083f, + 0x08a1, + 0x08ff, 0x0978, 0x0980, 0x0984, @@ -275,7 +276,6 @@ var isNotPrint16 = []uint16{ 0x0ab4, 0x0ac6, 0x0aca, - 0x0af0, 0x0b04, 0x0b29, 0x0b31, @@ -327,6 +327,7 @@ var isNotPrint16 = []uint16{ 0x0f98, 0x0fbd, 0x0fcd, + 0x10c6, 0x1249, 0x1257, 0x1259, @@ -351,10 +352,9 @@ var isNotPrint16 = []uint16{ 0x1ff5, 0x208f, 0x2700, - 0x27cb, - 0x27cd, 0x2c2f, 0x2c5f, + 0x2d26, 0x2da7, 0x2daf, 0x2db7, @@ -406,6 +406,8 @@ var isPrint32 = []uint32{ 0x010900, 0x01091b, 0x01091f, 0x010939, 0x01093f, 0x01093f, + 0x010980, 0x0109b7, + 0x0109be, 0x0109bf, 0x010a00, 0x010a06, 0x010a0c, 0x010a33, 0x010a38, 0x010a3a, @@ -421,11 +423,21 @@ var isPrint32 = []uint32{ 0x011000, 0x01104d, 0x011052, 0x01106f, 0x011080, 0x0110c1, + 0x0110d0, 0x0110e8, + 0x0110f0, 0x0110f9, + 0x011100, 0x011143, + 0x011180, 0x0111c8, + 0x0111d0, 0x0111d9, + 0x011680, 0x0116b7, + 0x0116c0, 0x0116c9, 0x012000, 0x01236e, 0x012400, 0x012462, 0x012470, 0x012473, 0x013000, 0x01342e, 0x016800, 0x016a38, + 0x016f00, 0x016f44, + 0x016f50, 0x016f7e, + 0x016f8f, 0x016f9f, 0x01b000, 0x01b001, 0x01d000, 0x01d0f5, 0x01d100, 0x01d126, @@ -442,13 +454,21 @@ var isPrint32 = []uint32{ 0x01d54a, 0x01d6a5, 0x01d6a8, 0x01d7cb, 0x01d7ce, 0x01d7ff, + 0x01ee00, 0x01ee24, + 0x01ee27, 0x01ee3b, + 0x01ee42, 0x01ee42, + 0x01ee47, 0x01ee54, + 0x01ee57, 0x01ee64, + 0x01ee67, 0x01ee9b, + 0x01eea1, 0x01eebb, + 0x01eef0, 0x01eef1, 0x01f000, 0x01f02b, 0x01f030, 0x01f093, 0x01f0a0, 0x01f0ae, 0x01f0b1, 0x01f0be, 0x01f0c1, 0x01f0df, 0x01f100, 0x01f10a, - 0x01f110, 0x01f169, + 0x01f110, 0x01f16b, 0x01f170, 0x01f19a, 0x01f1e6, 0x01f202, 0x01f210, 0x01f23a, @@ -461,10 +481,9 @@ var isPrint32 = []uint32{ 0x01f3e0, 0x01f3f0, 0x01f400, 0x01f4fc, 0x01f500, 0x01f53d, + 0x01f540, 0x01f543, 0x01f550, 0x01f567, - 0x01f5fb, 0x01f625, - 0x01f628, 0x01f62d, - 0x01f630, 0x01f640, + 0x01f5fb, 0x01f640, 0x01f645, 0x01f64f, 0x01f680, 0x01f6c5, 0x01f700, 0x01f773, @@ -489,6 +508,7 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry 0x0a14, 0x0a18, 0x10bd, + 0x1135, 0xd455, 0xd49d, 0xd4ad, @@ -502,6 +522,32 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry 0xd53f, 0xd545, 0xd551, + 0xee04, + 0xee20, + 0xee23, + 0xee28, + 0xee33, + 0xee38, + 0xee3a, + 0xee48, + 0xee4a, + 0xee4c, + 0xee50, + 0xee53, + 0xee58, + 0xee5a, + 0xee5c, + 0xee5e, + 0xee60, + 0xee63, + 0xee6b, + 0xee73, + 0xee78, + 0xee7d, + 0xee7f, + 0xee8a, + 0xeea4, + 0xeeaa, 0xf0d0, 0xf12f, 0xf336, @@ -509,13 +555,4 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry 0xf43f, 0xf441, 0xf4f8, - 0xf600, - 0xf611, - 0xf615, - 0xf617, - 0xf619, - 0xf61b, - 0xf61f, - 0xf62c, - 0xf634, } diff --git a/src/pkg/strconv/itoa_test.go b/src/pkg/strconv/itoa_test.go index 1486ee214..e0213ae9a 100644 --- a/src/pkg/strconv/itoa_test.go +++ b/src/pkg/strconv/itoa_test.go @@ -5,7 +5,6 @@ package strconv_test import ( - "runtime" . "strconv" "testing" ) @@ -126,35 +125,6 @@ func TestUitoa(t *testing.T) { } } -func numAllocations(f func()) int { - runtime.GC() - memstats := new(runtime.MemStats) - runtime.ReadMemStats(memstats) - n0 := memstats.Mallocs - f() - runtime.ReadMemStats(memstats) - return int(memstats.Mallocs - n0) -} - -var globalBuf [64]byte - -func TestAppendUintDoesntAllocate(t *testing.T) { - n := numAllocations(func() { - var buf [64]byte - AppendInt(buf[:0], 123, 10) - }) - want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better - if n != want { - t.Errorf("with local buffer, did %d allocations, want %d", n, want) - } - n = numAllocations(func() { - AppendInt(globalBuf[:0], 123, 10) - }) - if n != 0 { - t.Errorf("with reused buffer, did %d allocations, want 0", n) - } -} - func BenchmarkFormatInt(b *testing.B) { for i := 0; i < b.N; i++ { for _, test := range itob64tests { diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go index 8a73f9d3b..8cbef88b5 100644 --- a/src/pkg/strconv/quote.go +++ b/src/pkg/strconv/quote.go @@ -139,8 +139,9 @@ func AppendQuoteRuneToASCII(dst []byte, r rune) []byte { return append(dst, QuoteRuneToASCII(r)...) } -// CanBackquote returns whether the string s would be -// a valid Go string literal if enclosed in backquotes. +// CanBackquote reports whether the string s can be represented +// unchanged as a single-line backquoted string without control +// characters other than space and tab. func CanBackquote(s string) bool { for i := 0; i < len(s); i++ { if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' { diff --git a/src/pkg/strconv/strconv_test.go b/src/pkg/strconv/strconv_test.go new file mode 100644 index 000000000..c3c538926 --- /dev/null +++ b/src/pkg/strconv/strconv_test.go @@ -0,0 +1,52 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package strconv_test + +import ( + . "strconv" + "strings" + "testing" +) + +var ( + globalBuf [64]byte + nextToOne = "1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1" + + mallocTest = []struct { + count int + desc string + fn func() + }{ + // TODO(bradfitz): this might be 0, once escape analysis is better + {1, `AppendInt(localBuf[:0], 123, 10)`, func() { + var localBuf [64]byte + AppendInt(localBuf[:0], 123, 10) + }}, + {0, `AppendInt(globalBuf[:0], 123, 10)`, func() { AppendInt(globalBuf[:0], 123, 10) }}, + // TODO(bradfitz): this might be 0, once escape analysis is better + {1, `AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)`, func() { + var localBuf [64]byte + AppendFloat(localBuf[:0], 1.23, 'g', 5, 64) + }}, + {0, `AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64)`, func() { AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64) }}, + {0, `ParseFloat("123.45", 64)`, func() { ParseFloat("123.45", 64) }}, + {0, `ParseFloat("123.456789123456789", 64)`, func() { ParseFloat("123.456789123456789", 64) }}, + {0, `ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64)`, func() { + ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64) + }}, + {0, `ParseFloat("1.0000000000000001110223024625156540423631668090820312500...001", 64)`, func() { + ParseFloat(nextToOne, 64) + }}, + } +) + +func TestCountMallocs(t *testing.T) { + for _, mt := range mallocTest { + allocs := testing.AllocsPerRun(100, mt.fn) + if max := float64(mt.count); allocs > max { + t.Errorf("%s: %v allocs, want <=%v", mt.desc, allocs, max) + } + } +} diff --git a/src/pkg/strconv/testdata/testfp.txt b/src/pkg/strconv/testdata/testfp.txt new file mode 100644 index 000000000..08d3c4ef0 --- /dev/null +++ b/src/pkg/strconv/testdata/testfp.txt @@ -0,0 +1,181 @@ +# Floating-point conversion test cases. +# Empty lines and lines beginning with # are ignored. +# The rest have four fields per line: type, format, input, and output. +# The input is given either in decimal or binary scientific notation. +# The output is the string that should be produced by formatting the +# input with the given format. +# +# The formats are as in C's printf, except that %b means print +# binary scientific notation: NpE = N x 2^E. + +# TODO: +# Powers of 10. +# Powers of 2. +# %.20g versions. +# random sources +# random targets +# random targets ± half a ULP + +# Difficult boundary cases, derived from tables given in +# Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion +# ftp://ftp.ee.lbl.gov/testbase-report.ps.Z + +# Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP +float64 %b 5e+125 6653062250012735p+365 +float64 %b 69e+267 4705683757438170p+841 +float64 %b 999e-026 6798841691080350p-129 +float64 %b 7861e-034 8975675289889240p-153 +float64 %b 75569e-254 6091718967192243p-880 +float64 %b 928609e-261 7849264900213743p-900 +float64 %b 9210917e+080 8341110837370930p+236 +float64 %b 84863171e+114 4625202867375927p+353 +float64 %b 653777767e+273 5068902999763073p+884 +float64 %b 5232604057e-298 5741343011915040p-1010 +float64 %b 27235667517e-109 6707124626673586p-380 +float64 %b 653532977297e-123 7078246407265384p-422 +float64 %b 3142213164987e-294 8219991337640559p-988 +float64 %b 46202199371337e-072 5224462102115359p-246 +float64 %b 231010996856685e-073 5224462102115359p-247 +float64 %b 9324754620109615e+212 5539753864394442p+705 +float64 %b 78459735791271921e+049 8388176519442766p+166 +float64 %b 272104041512242479e+200 5554409530847367p+670 +float64 %b 6802601037806061975e+198 5554409530847367p+668 +float64 %b 20505426358836677347e-221 4524032052079546p-722 +float64 %b 836168422905420598437e-234 5070963299887562p-760 +float64 %b 4891559871276714924261e+222 6452687840519111p+757 + +# Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP +float64 %b 9e-265 8168427841980010p-930 +float64 %b 85e-037 6360455125664090p-169 +float64 %b 623e+100 6263531988747231p+289 +float64 %b 3571e+263 6234526311072170p+833 +float64 %b 81661e+153 6696636728760206p+472 +float64 %b 920657e-023 5975405561110124p-109 +float64 %b 4603285e-024 5975405561110124p-110 +float64 %b 87575437e-309 8452160731874668p-1053 +float64 %b 245540327e+122 4985336549131723p+381 +float64 %b 6138508175e+120 4985336549131723p+379 +float64 %b 83356057653e+193 5986732817132056p+625 +float64 %b 619534293513e+124 4798406992060657p+399 +float64 %b 2335141086879e+218 5419088166961646p+713 +float64 %b 36167929443327e-159 8135819834632444p-536 +float64 %b 609610927149051e-255 4576664294594737p-850 +float64 %b 3743626360493413e-165 6898586531774201p-549 +float64 %b 94080055902682397e-242 6273271706052298p-800 +float64 %b 899810892172646163e+283 7563892574477827p+947 +float64 %b 7120190517612959703e+120 5385467232557565p+409 +float64 %b 25188282901709339043e-252 5635662608542340p-825 +float64 %b 308984926168550152811e-052 5644774693823803p-157 +float64 %b 6372891218502368041059e+064 4616868614322430p+233 + +# Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP +float64 %.0e 8511030020275656p-342 9e-88 +float64 %.1e 5201988407066741p-824 4.6e-233 +float64 %.2e 6406892948269899p+237 1.41e+87 +float64 %.3e 8431154198732492p+72 3.981e+37 +float64 %.4e 6475049196144587p+99 4.1040e+45 +float64 %.5e 8274307542972842p+726 2.92084e+234 +float64 %.6e 5381065484265332p-456 2.891946e-122 +float64 %.7e 6761728585499734p-1057 4.3787718e-303 +float64 %.8e 7976538478610756p+376 1.22770163e+129 +float64 %.9e 5982403858958067p+377 1.841552452e+129 +float64 %.10e 5536995190630837p+93 5.4835744350e+43 +float64 %.11e 7225450889282194p+710 3.89190181146e+229 +float64 %.12e 7225450889282194p+709 1.945950905732e+229 +float64 %.13e 8703372741147379p+117 1.4460958381605e+51 +float64 %.14e 8944262675275217p-1001 4.17367747458531e-286 +float64 %.15e 7459803696087692p-707 1.107950772878888e-197 +float64 %.16e 6080469016670379p-381 1.2345501366327440e-99 +float64 %.17e 8385515147034757p+721 9.25031711960365024e+232 +float64 %.18e 7514216811389786p-828 4.198047150284889840e-234 +float64 %.19e 8397297803260511p-345 1.1716315319786511046e-88 +float64 %.20e 6733459239310543p+202 4.32810072844612493629e+76 +float64 %.21e 8091450587292794p-473 3.317710118160031081518e-127 + +# Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP +float64 %.0e 6567258882077402p+952 3e+302 +float64 %.1e 6712731423444934p+535 7.6e+176 +float64 %.2e 6712731423444934p+534 3.78e+176 +float64 %.3e 5298405411573037p-957 4.350e-273 +float64 %.4e 5137311167659507p-144 2.3037e-28 +float64 %.5e 6722280709661868p+363 1.26301e+125 +float64 %.6e 5344436398034927p-169 7.142211e-36 +float64 %.7e 8369123604277281p-853 1.3934574e-241 +float64 %.8e 8995822108487663p-780 1.41463449e-219 +float64 %.9e 8942832835564782p-383 4.539277920e-100 +float64 %.10e 8942832835564782p-384 2.2696389598e-100 +float64 %.11e 8942832835564782p-385 1.13481947988e-100 +float64 %.12e 6965949469487146p-249 7.700366561890e-60 +float64 %.13e 6965949469487146p-250 3.8501832809448e-60 +float64 %.14e 6965949469487146p-251 1.92509164047238e-60 +float64 %.15e 7487252720986826p+548 6.898586531774201e+180 +float64 %.16e 5592117679628511p+164 1.3076622631878654e+65 +float64 %.17e 8887055249355788p+665 1.36052020756121240e+216 +float64 %.18e 6994187472632449p+690 3.592810217475959676e+223 +float64 %.19e 8797576579012143p+588 8.9125197712484551899e+192 +float64 %.20e 7363326733505337p+272 5.58769757362301140950e+97 +float64 %.21e 8549497411294502p-448 1.176257830728540379990e-119 + +# Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP +# NOTE: The lines with exponent p-149 have been changed from the +# paper. Those entries originally read p-150 and had a mantissa +# twice as large (and even), but IEEE single-precision has no p-150: +# that's the start of the denormals. +float32 %b 5e-20 15474250p-88 +float32 %b 67e+14 12479722p+29 +float32 %b 985e+15 14333636p+36 +# float32 %b 7693e-42 10979816p-150 +float32 %b 7693e-42 5489908p-149 +float32 %b 55895e-16 12888509p-61 +# float32 %b 996622e-44 14224264p-150 +float32 %b 996622e-44 7112132p-149 +float32 %b 7038531e-32 11420669p-107 +# float32 %b 60419369e-46 8623340p-150 +float32 %b 60419369e-46 4311670p-149 +float32 %b 702990899e-20 16209866p-61 +# float32 %b 6930161142e-48 9891056p-150 +float32 %b 6930161142e-48 4945528p-149 +float32 %b 25933168707e+13 14395800p+54 +float32 %b 596428896559e+20 12333860p+82 + +# Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP +float32 %b 3e-23 9507380p-98 +float32 %b 57e+18 12960300p+42 +float32 %b 789e-35 10739312p-130 +float32 %b 2539e-18 11990089p-72 +float32 %b 76173e+28 9845130p+86 +float32 %b 887745e-11 9760860p-40 +float32 %b 5382571e-37 11447463p-124 +float32 %b 82381273e-35 8554961p-113 +float32 %b 750486563e-38 9975678p-120 +float32 %b 3752432815e-39 9975678p-121 +float32 %b 75224575729e-45 13105970p-137 +float32 %b 459926601011e+15 12466336p+65 + +# Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP +float32 %.0e 12676506p-102 2e-24 +float32 %.1e 12676506p-103 1.2e-24 +float32 %.2e 15445013p+86 1.19e+33 +float32 %.3e 13734123p-138 3.941e-35 +float32 %.4e 12428269p-130 9.1308e-33 +float32 %.5e 15334037p-146 1.71900e-37 +float32 %.6e 11518287p-41 5.237910e-06 +float32 %.7e 12584953p-145 2.8216440e-37 +float32 %.8e 15961084p-125 3.75243281e-31 +float32 %.9e 14915817p-146 1.672120916e-37 +float32 %.10e 10845484p-102 2.1388945814e-24 +float32 %.11e 16431059p-61 7.12583594561e-12 + +# Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP +float32 %.0e 16093626p+69 1e+28 +float32 %.1e 9983778p+25 3.4e+14 +float32 %.2e 12745034p+104 2.59e+38 +float32 %.3e 12706553p+72 6.001e+28 +float32 %.4e 11005028p+45 3.8721e+20 +float32 %.5e 15059547p+71 3.55584e+28 +float32 %.6e 16015691p-99 2.526831e-23 +float32 %.7e 8667859p+56 6.2458507e+23 +float32 %.8e 14855922p-82 3.07213267e-18 +float32 %.9e 14855922p-83 1.536066333e-18 +float32 %.10e 10144164p-110 7.8147796834e-27 +float32 %.11e 13248074p+95 5.24810279937e+35 diff --git a/src/pkg/strconv/testfp.txt b/src/pkg/strconv/testfp.txt deleted file mode 100644 index 08d3c4ef0..000000000 --- a/src/pkg/strconv/testfp.txt +++ /dev/null @@ -1,181 +0,0 @@ -# Floating-point conversion test cases. -# Empty lines and lines beginning with # are ignored. -# The rest have four fields per line: type, format, input, and output. -# The input is given either in decimal or binary scientific notation. -# The output is the string that should be produced by formatting the -# input with the given format. -# -# The formats are as in C's printf, except that %b means print -# binary scientific notation: NpE = N x 2^E. - -# TODO: -# Powers of 10. -# Powers of 2. -# %.20g versions. -# random sources -# random targets -# random targets ± half a ULP - -# Difficult boundary cases, derived from tables given in -# Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion -# ftp://ftp.ee.lbl.gov/testbase-report.ps.Z - -# Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP -float64 %b 5e+125 6653062250012735p+365 -float64 %b 69e+267 4705683757438170p+841 -float64 %b 999e-026 6798841691080350p-129 -float64 %b 7861e-034 8975675289889240p-153 -float64 %b 75569e-254 6091718967192243p-880 -float64 %b 928609e-261 7849264900213743p-900 -float64 %b 9210917e+080 8341110837370930p+236 -float64 %b 84863171e+114 4625202867375927p+353 -float64 %b 653777767e+273 5068902999763073p+884 -float64 %b 5232604057e-298 5741343011915040p-1010 -float64 %b 27235667517e-109 6707124626673586p-380 -float64 %b 653532977297e-123 7078246407265384p-422 -float64 %b 3142213164987e-294 8219991337640559p-988 -float64 %b 46202199371337e-072 5224462102115359p-246 -float64 %b 231010996856685e-073 5224462102115359p-247 -float64 %b 9324754620109615e+212 5539753864394442p+705 -float64 %b 78459735791271921e+049 8388176519442766p+166 -float64 %b 272104041512242479e+200 5554409530847367p+670 -float64 %b 6802601037806061975e+198 5554409530847367p+668 -float64 %b 20505426358836677347e-221 4524032052079546p-722 -float64 %b 836168422905420598437e-234 5070963299887562p-760 -float64 %b 4891559871276714924261e+222 6452687840519111p+757 - -# Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP -float64 %b 9e-265 8168427841980010p-930 -float64 %b 85e-037 6360455125664090p-169 -float64 %b 623e+100 6263531988747231p+289 -float64 %b 3571e+263 6234526311072170p+833 -float64 %b 81661e+153 6696636728760206p+472 -float64 %b 920657e-023 5975405561110124p-109 -float64 %b 4603285e-024 5975405561110124p-110 -float64 %b 87575437e-309 8452160731874668p-1053 -float64 %b 245540327e+122 4985336549131723p+381 -float64 %b 6138508175e+120 4985336549131723p+379 -float64 %b 83356057653e+193 5986732817132056p+625 -float64 %b 619534293513e+124 4798406992060657p+399 -float64 %b 2335141086879e+218 5419088166961646p+713 -float64 %b 36167929443327e-159 8135819834632444p-536 -float64 %b 609610927149051e-255 4576664294594737p-850 -float64 %b 3743626360493413e-165 6898586531774201p-549 -float64 %b 94080055902682397e-242 6273271706052298p-800 -float64 %b 899810892172646163e+283 7563892574477827p+947 -float64 %b 7120190517612959703e+120 5385467232557565p+409 -float64 %b 25188282901709339043e-252 5635662608542340p-825 -float64 %b 308984926168550152811e-052 5644774693823803p-157 -float64 %b 6372891218502368041059e+064 4616868614322430p+233 - -# Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP -float64 %.0e 8511030020275656p-342 9e-88 -float64 %.1e 5201988407066741p-824 4.6e-233 -float64 %.2e 6406892948269899p+237 1.41e+87 -float64 %.3e 8431154198732492p+72 3.981e+37 -float64 %.4e 6475049196144587p+99 4.1040e+45 -float64 %.5e 8274307542972842p+726 2.92084e+234 -float64 %.6e 5381065484265332p-456 2.891946e-122 -float64 %.7e 6761728585499734p-1057 4.3787718e-303 -float64 %.8e 7976538478610756p+376 1.22770163e+129 -float64 %.9e 5982403858958067p+377 1.841552452e+129 -float64 %.10e 5536995190630837p+93 5.4835744350e+43 -float64 %.11e 7225450889282194p+710 3.89190181146e+229 -float64 %.12e 7225450889282194p+709 1.945950905732e+229 -float64 %.13e 8703372741147379p+117 1.4460958381605e+51 -float64 %.14e 8944262675275217p-1001 4.17367747458531e-286 -float64 %.15e 7459803696087692p-707 1.107950772878888e-197 -float64 %.16e 6080469016670379p-381 1.2345501366327440e-99 -float64 %.17e 8385515147034757p+721 9.25031711960365024e+232 -float64 %.18e 7514216811389786p-828 4.198047150284889840e-234 -float64 %.19e 8397297803260511p-345 1.1716315319786511046e-88 -float64 %.20e 6733459239310543p+202 4.32810072844612493629e+76 -float64 %.21e 8091450587292794p-473 3.317710118160031081518e-127 - -# Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP -float64 %.0e 6567258882077402p+952 3e+302 -float64 %.1e 6712731423444934p+535 7.6e+176 -float64 %.2e 6712731423444934p+534 3.78e+176 -float64 %.3e 5298405411573037p-957 4.350e-273 -float64 %.4e 5137311167659507p-144 2.3037e-28 -float64 %.5e 6722280709661868p+363 1.26301e+125 -float64 %.6e 5344436398034927p-169 7.142211e-36 -float64 %.7e 8369123604277281p-853 1.3934574e-241 -float64 %.8e 8995822108487663p-780 1.41463449e-219 -float64 %.9e 8942832835564782p-383 4.539277920e-100 -float64 %.10e 8942832835564782p-384 2.2696389598e-100 -float64 %.11e 8942832835564782p-385 1.13481947988e-100 -float64 %.12e 6965949469487146p-249 7.700366561890e-60 -float64 %.13e 6965949469487146p-250 3.8501832809448e-60 -float64 %.14e 6965949469487146p-251 1.92509164047238e-60 -float64 %.15e 7487252720986826p+548 6.898586531774201e+180 -float64 %.16e 5592117679628511p+164 1.3076622631878654e+65 -float64 %.17e 8887055249355788p+665 1.36052020756121240e+216 -float64 %.18e 6994187472632449p+690 3.592810217475959676e+223 -float64 %.19e 8797576579012143p+588 8.9125197712484551899e+192 -float64 %.20e 7363326733505337p+272 5.58769757362301140950e+97 -float64 %.21e 8549497411294502p-448 1.176257830728540379990e-119 - -# Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP -# NOTE: The lines with exponent p-149 have been changed from the -# paper. Those entries originally read p-150 and had a mantissa -# twice as large (and even), but IEEE single-precision has no p-150: -# that's the start of the denormals. -float32 %b 5e-20 15474250p-88 -float32 %b 67e+14 12479722p+29 -float32 %b 985e+15 14333636p+36 -# float32 %b 7693e-42 10979816p-150 -float32 %b 7693e-42 5489908p-149 -float32 %b 55895e-16 12888509p-61 -# float32 %b 996622e-44 14224264p-150 -float32 %b 996622e-44 7112132p-149 -float32 %b 7038531e-32 11420669p-107 -# float32 %b 60419369e-46 8623340p-150 -float32 %b 60419369e-46 4311670p-149 -float32 %b 702990899e-20 16209866p-61 -# float32 %b 6930161142e-48 9891056p-150 -float32 %b 6930161142e-48 4945528p-149 -float32 %b 25933168707e+13 14395800p+54 -float32 %b 596428896559e+20 12333860p+82 - -# Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP -float32 %b 3e-23 9507380p-98 -float32 %b 57e+18 12960300p+42 -float32 %b 789e-35 10739312p-130 -float32 %b 2539e-18 11990089p-72 -float32 %b 76173e+28 9845130p+86 -float32 %b 887745e-11 9760860p-40 -float32 %b 5382571e-37 11447463p-124 -float32 %b 82381273e-35 8554961p-113 -float32 %b 750486563e-38 9975678p-120 -float32 %b 3752432815e-39 9975678p-121 -float32 %b 75224575729e-45 13105970p-137 -float32 %b 459926601011e+15 12466336p+65 - -# Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP -float32 %.0e 12676506p-102 2e-24 -float32 %.1e 12676506p-103 1.2e-24 -float32 %.2e 15445013p+86 1.19e+33 -float32 %.3e 13734123p-138 3.941e-35 -float32 %.4e 12428269p-130 9.1308e-33 -float32 %.5e 15334037p-146 1.71900e-37 -float32 %.6e 11518287p-41 5.237910e-06 -float32 %.7e 12584953p-145 2.8216440e-37 -float32 %.8e 15961084p-125 3.75243281e-31 -float32 %.9e 14915817p-146 1.672120916e-37 -float32 %.10e 10845484p-102 2.1388945814e-24 -float32 %.11e 16431059p-61 7.12583594561e-12 - -# Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP -float32 %.0e 16093626p+69 1e+28 -float32 %.1e 9983778p+25 3.4e+14 -float32 %.2e 12745034p+104 2.59e+38 -float32 %.3e 12706553p+72 6.001e+28 -float32 %.4e 11005028p+45 3.8721e+20 -float32 %.5e 15059547p+71 3.55584e+28 -float32 %.6e 16015691p-99 2.526831e-23 -float32 %.7e 8667859p+56 6.2458507e+23 -float32 %.8e 14855922p-82 3.07213267e-18 -float32 %.9e 14855922p-83 1.536066333e-18 -float32 %.10e 10144164p-110 7.8147796834e-27 -float32 %.11e 13248074p+95 5.24810279937e+35 diff --git a/src/pkg/strings/example_test.go b/src/pkg/strings/example_test.go index 733caf5f2..36e0a42fb 100644 --- a/src/pkg/strings/example_test.go +++ b/src/pkg/strings/example_test.go @@ -179,3 +179,19 @@ func ExampleToLower() { fmt.Println(strings.ToLower("Gopher")) // Output: gopher } + +func ExampleTrimSuffix() { + var s = "Hello, goodbye, etc!" + s = strings.TrimSuffix(s, "goodbye, etc!") + s = strings.TrimSuffix(s, "planet") + fmt.Print(s, "world!") + // Output: Hello, world! +} + +func ExampleTrimPrefix() { + var s = "Goodbye,, world!" + s = strings.TrimPrefix(s, "Goodbye,") + s = strings.TrimPrefix(s, "Howdy,") + fmt.Print("Hello" + s) + // Output: Hello, world! +} diff --git a/src/pkg/strings/export_test.go b/src/pkg/strings/export_test.go index dcfec513c..17c806aa5 100644 --- a/src/pkg/strings/export_test.go +++ b/src/pkg/strings/export_test.go @@ -7,3 +7,39 @@ package strings func (r *Replacer) Replacer() interface{} { return r.r } + +func (r *Replacer) PrintTrie() string { + gen := r.r.(*genericReplacer) + return gen.printNode(&gen.root, 0) +} + +func (r *genericReplacer) printNode(t *trieNode, depth int) (s string) { + if t.priority > 0 { + s += "+" + } else { + s += "-" + } + s += "\n" + + if t.prefix != "" { + s += Repeat(".", depth) + t.prefix + s += r.printNode(t.next, depth+len(t.prefix)) + } else if t.table != nil { + for b, m := range r.mapping { + if int(m) != r.tableSize && t.table[m] != nil { + s += Repeat(".", depth) + string([]byte{byte(b)}) + s += r.printNode(t.table[m], depth+1) + } + } + } + return +} + +func StringFind(pattern, text string) int { + return makeStringFinder(pattern).next(text) +} + +func DumpTables(pattern string) ([]int, []int) { + finder := makeStringFinder(pattern) + return finder.badCharSkip[:], finder.goodSuffixSkip +} diff --git a/src/pkg/strings/reader.go b/src/pkg/strings/reader.go index 856980555..11240efc0 100644 --- a/src/pkg/strings/reader.go +++ b/src/pkg/strings/reader.go @@ -10,7 +10,7 @@ import ( "unicode/utf8" ) -// A Reader implements the io.Reader, io.ReaderAt, io.Seeker, +// A Reader implements the io.Reader, io.ReaderAt, io.Seeker, io.WriterTo, // io.ByteScanner, and io.RuneScanner interfaces by reading // from a string. type Reader struct { @@ -120,6 +120,25 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) { return abs, nil } +// WriteTo implements the io.WriterTo interface. +func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { + r.prevRune = -1 + if r.i >= len(r.s) { + return 0, nil + } + s := r.s[r.i:] + m, err := io.WriteString(w, s) + if m > len(s) { + panic("strings.Reader.WriteTo: invalid WriteString count") + } + r.i += m + n = int64(m) + if m != len(s) && err == nil { + err = io.ErrShortWrite + } + return +} + // NewReader returns a new Reader reading from s. // It is similar to bytes.NewBufferString but more efficient and read-only. func NewReader(s string) *Reader { return &Reader{s, 0, -1} } diff --git a/src/pkg/strings/reader_test.go b/src/pkg/strings/reader_test.go index a99ae2a0e..4fdddcdb5 100644 --- a/src/pkg/strings/reader_test.go +++ b/src/pkg/strings/reader_test.go @@ -5,6 +5,7 @@ package strings_test import ( + "bytes" "fmt" "io" "os" @@ -86,3 +87,25 @@ func TestReaderAt(t *testing.T) { } } } + +func TestWriteTo(t *testing.T) { + const str = "0123456789" + for i := 0; i <= len(str); i++ { + s := str[i:] + r := strings.NewReader(s) + var b bytes.Buffer + n, err := r.WriteTo(&b) + if expect := int64(len(s)); n != expect { + t.Errorf("got %v; want %v", n, expect) + } + if err != nil { + t.Errorf("for length %d: got error = %v; want nil", len(s), err) + } + if b.String() != s { + t.Errorf("got string %q; want %q", b.String(), s) + } + if r.Len() != 0 { + t.Errorf("reader contains %v bytes; want 0", r.Len()) + } + } +} diff --git a/src/pkg/strings/replace.go b/src/pkg/strings/replace.go index f53a96ee0..f63b1792c 100644 --- a/src/pkg/strings/replace.go +++ b/src/pkg/strings/replace.go @@ -33,47 +33,45 @@ func NewReplacer(oldnew ...string) *Replacer { panic("strings.NewReplacer: odd argument count") } - // Possible implementations. - var ( - bb byteReplacer - bs byteStringReplacer - gen genericReplacer - ) - - allOldBytes, allNewBytes := true, true - for len(oldnew) > 0 { - old, new := oldnew[0], oldnew[1] - oldnew = oldnew[2:] - if len(old) != 1 { - allOldBytes = false + if len(oldnew) == 2 && len(oldnew[0]) > 1 { + return &Replacer{r: makeSingleStringReplacer(oldnew[0], oldnew[1])} + } + + allNewBytes := true + for i := 0; i < len(oldnew); i += 2 { + if len(oldnew[i]) != 1 { + return &Replacer{r: makeGenericReplacer(oldnew)} } - if len(new) != 1 { + if len(oldnew[i+1]) != 1 { allNewBytes = false } + } - // generic - gen.p = append(gen.p, pair{old, new}) - - // byte -> string - if allOldBytes { - bs.old.set(old[0]) - bs.new[old[0]] = []byte(new) - } - - // byte -> byte - if allOldBytes && allNewBytes { - bb.old.set(old[0]) - bb.new[old[0]] = new[0] + if allNewBytes { + bb := &byteReplacer{} + for i := 0; i < len(oldnew); i += 2 { + o, n := oldnew[i][0], oldnew[i+1][0] + if bb.old[o>>5]&uint32(1<<(o&31)) != 0 { + // Later old->new maps do not override previous ones with the same old string. + continue + } + bb.old.set(o) + bb.new[o] = n } + return &Replacer{r: bb} } - if allOldBytes && allNewBytes { - return &Replacer{r: &bb} - } - if allOldBytes { - return &Replacer{r: &bs} + bs := &byteStringReplacer{} + for i := 0; i < len(oldnew); i += 2 { + o, new := oldnew[i][0], oldnew[i+1] + if bs.old[o>>5]&uint32(1<<(o&31)) != 0 { + // Later old->new maps do not override previous ones with the same old string. + continue + } + bs.old.set(o) + bs.new[o] = []byte(new) } - return &Replacer{r: &gen} + return &Replacer{r: bs} } // Replace returns a copy of s with all replacements performed. @@ -86,79 +84,326 @@ func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error) { return r.r.WriteString(w, s) } -// genericReplacer is the fully generic (and least optimized) algorithm. +// trieNode is a node in a lookup trie for prioritized key/value pairs. Keys +// and values may be empty. For example, the trie containing keys "ax", "ay", +// "bcbc", "x" and "xy" could have eight nodes: +// +// n0 - +// n1 a- +// n2 .x+ +// n3 .y+ +// n4 b- +// n5 .cbc+ +// n6 x+ +// n7 .y+ +// +// n0 is the root node, and its children are n1, n4 and n6; n1's children are +// n2 and n3; n4's child is n5; n6's child is n7. Nodes n0, n1 and n4 (marked +// with a trailing "-") are partial keys, and nodes n2, n3, n5, n6 and n7 +// (marked with a trailing "+") are complete keys. +type trieNode struct { + // value is the value of the trie node's key/value pair. It is empty if + // this node is not a complete key. + value string + // priority is the priority (higher is more important) of the trie node's + // key/value pair; keys are not necessarily matched shortest- or longest- + // first. Priority is positive if this node is a complete key, and zero + // otherwise. In the example above, positive/zero priorities are marked + // with a trailing "+" or "-". + priority int + + // A trie node may have zero, one or more child nodes: + // * if the remaining fields are zero, there are no children. + // * if prefix and next are non-zero, there is one child in next. + // * if table is non-zero, it defines all the children. + // + // Prefixes are preferred over tables when there is one child, but the + // root node always uses a table for lookup efficiency. + + // prefix is the difference in keys between this trie node and the next. + // In the example above, node n4 has prefix "cbc" and n4's next node is n5. + // Node n5 has no children and so has zero prefix, next and table fields. + prefix string + next *trieNode + + // table is a lookup table indexed by the next byte in the key, after + // remapping that byte through genericReplacer.mapping to create a dense + // index. In the example above, the keys only use 'a', 'b', 'c', 'x' and + // 'y', which remap to 0, 1, 2, 3 and 4. All other bytes remap to 5, and + // genericReplacer.tableSize will be 5. Node n0's table will be + // []*trieNode{ 0:n1, 1:n4, 3:n6 }, where the 0, 1 and 3 are the remapped + // 'a', 'b' and 'x'. + table []*trieNode +} + +func (t *trieNode) add(key, val string, priority int, r *genericReplacer) { + if key == "" { + if t.priority == 0 { + t.value = val + t.priority = priority + } + return + } + + if t.prefix != "" { + // Need to split the prefix among multiple nodes. + var n int // length of the longest common prefix + for ; n < len(t.prefix) && n < len(key); n++ { + if t.prefix[n] != key[n] { + break + } + } + if n == len(t.prefix) { + t.next.add(key[n:], val, priority, r) + } else if n == 0 { + // First byte differs, start a new lookup table here. Looking up + // what is currently t.prefix[0] will lead to prefixNode, and + // looking up key[0] will lead to keyNode. + var prefixNode *trieNode + if len(t.prefix) == 1 { + prefixNode = t.next + } else { + prefixNode = &trieNode{ + prefix: t.prefix[1:], + next: t.next, + } + } + keyNode := new(trieNode) + t.table = make([]*trieNode, r.tableSize) + t.table[r.mapping[t.prefix[0]]] = prefixNode + t.table[r.mapping[key[0]]] = keyNode + t.prefix = "" + t.next = nil + keyNode.add(key[1:], val, priority, r) + } else { + // Insert new node after the common section of the prefix. + next := &trieNode{ + prefix: t.prefix[n:], + next: t.next, + } + t.prefix = t.prefix[:n] + t.next = next + next.add(key[n:], val, priority, r) + } + } else if t.table != nil { + // Insert into existing table. + m := r.mapping[key[0]] + if t.table[m] == nil { + t.table[m] = new(trieNode) + } + t.table[m].add(key[1:], val, priority, r) + } else { + t.prefix = key + t.next = new(trieNode) + t.next.add("", val, priority, r) + } +} + +func (r *genericReplacer) lookup(s string, ignoreRoot bool) (val string, keylen int, found bool) { + // Iterate down the trie to the end, and grab the value and keylen with + // the highest priority. + bestPriority := 0 + node := &r.root + n := 0 + for node != nil { + if node.priority > bestPriority && !(ignoreRoot && node == &r.root) { + bestPriority = node.priority + val = node.value + keylen = n + found = true + } + + if s == "" { + break + } + if node.table != nil { + index := r.mapping[s[0]] + if int(index) == r.tableSize { + break + } + node = node.table[index] + s = s[1:] + n++ + } else if node.prefix != "" && HasPrefix(s, node.prefix) { + n += len(node.prefix) + s = s[len(node.prefix):] + node = node.next + } else { + break + } + } + return +} + +// genericReplacer is the fully generic algorithm. // It's used as a fallback when nothing faster can be used. type genericReplacer struct { - p []pair + root trieNode + // tableSize is the size of a trie node's lookup table. It is the number + // of unique key bytes. + tableSize int + // mapping maps from key bytes to a dense index for trieNode.table. + mapping [256]byte } -type pair struct{ old, new string } +func makeGenericReplacer(oldnew []string) *genericReplacer { + r := new(genericReplacer) + // Find each byte used, then assign them each an index. + for i := 0; i < len(oldnew); i += 2 { + key := oldnew[i] + for j := 0; j < len(key); j++ { + r.mapping[key[j]] = 1 + } + } + + for _, b := range r.mapping { + r.tableSize += int(b) + } + + var index byte + for i, b := range r.mapping { + if b == 0 { + r.mapping[i] = byte(r.tableSize) + } else { + r.mapping[i] = index + index++ + } + } + // Ensure root node uses a lookup table (for performance). + r.root.table = make([]*trieNode, r.tableSize) -type appendSliceWriter struct { - b []byte + for i := 0; i < len(oldnew); i += 2 { + r.root.add(oldnew[i], oldnew[i+1], len(oldnew)-i, r) + } + return r } +type appendSliceWriter []byte + +// Write writes to the buffer to satisfy io.Writer. func (w *appendSliceWriter) Write(p []byte) (int, error) { - w.b = append(w.b, p...) + *w = append(*w, p...) return len(p), nil } +// WriteString writes to the buffer without string->[]byte->string allocations. +func (w *appendSliceWriter) WriteString(s string) (int, error) { + *w = append(*w, s...) + return len(s), nil +} + +type stringWriterIface interface { + WriteString(string) (int, error) +} + +type stringWriter struct { + w io.Writer +} + +func (w stringWriter) WriteString(s string) (int, error) { + return w.w.Write([]byte(s)) +} + +func getStringWriter(w io.Writer) stringWriterIface { + sw, ok := w.(stringWriterIface) + if !ok { + sw = stringWriter{w} + } + return sw +} + func (r *genericReplacer) Replace(s string) string { - // TODO(bradfitz): optimized version - n, _ := r.WriteString(discard, s) - w := appendSliceWriter{make([]byte, 0, n)} - r.WriteString(&w, s) - return string(w.b) + buf := make(appendSliceWriter, 0, len(s)) + r.WriteString(&buf, s) + return string(buf) } func (r *genericReplacer) WriteString(w io.Writer, s string) (n int, err error) { - lastEmpty := false // the last replacement was of the empty string -Input: - // TODO(bradfitz): optimized version - for i := 0; i < len(s); { - for _, p := range r.p { - if p.old == "" && lastEmpty { - // Don't let old match twice in a row. - // (it doesn't advance the input and - // would otherwise loop forever) - continue + sw := getStringWriter(w) + var last, wn int + var prevMatchEmpty bool + for i := 0; i <= len(s); { + // Ignore the empty match iff the previous loop found the empty match. + val, keylen, match := r.lookup(s[i:], prevMatchEmpty) + prevMatchEmpty = match && keylen == 0 + if match { + wn, err = sw.WriteString(s[last:i]) + n += wn + if err != nil { + return } - if HasPrefix(s[i:], p.old) { - if p.new != "" { - wn, err := w.Write([]byte(p.new)) - n += wn - if err != nil { - return n, err - } - } - i += len(p.old) - lastEmpty = p.old == "" - continue Input + wn, err = sw.WriteString(val) + n += wn + if err != nil { + return } - } - wn, err := w.Write([]byte{s[i]}) - n += wn - if err != nil { - return n, err + i += keylen + last = i + continue } i++ } + if last != len(s) { + wn, err = sw.WriteString(s[last:]) + n += wn + } + return +} - // Final empty match at end. - for _, p := range r.p { - if p.old == "" { - if p.new != "" { - wn, err := w.Write([]byte(p.new)) - n += wn - if err != nil { - return n, err - } - } +// singleStringReplacer is the implementation that's used when there is only +// one string to replace (and that string has more than one byte). +type singleStringReplacer struct { + finder *stringFinder + // value is the new string that replaces that pattern when it's found. + value string +} + +func makeSingleStringReplacer(pattern string, value string) *singleStringReplacer { + return &singleStringReplacer{finder: makeStringFinder(pattern), value: value} +} + +func (r *singleStringReplacer) Replace(s string) string { + var buf []byte + i := 0 + for { + match := r.finder.next(s[i:]) + if match == -1 { break } + buf = append(buf, s[i:i+match]...) + buf = append(buf, r.value...) + i += match + len(r.finder.pattern) + } + if buf == nil { + return s } + buf = append(buf, s[i:]...) + return string(buf) +} - return n, nil +func (r *singleStringReplacer) WriteString(w io.Writer, s string) (n int, err error) { + sw := getStringWriter(w) + var i, wn int + for { + match := r.finder.next(s[i:]) + if match == -1 { + break + } + wn, err = sw.WriteString(s[i : i+match]) + n += wn + if err != nil { + return + } + wn, err = sw.WriteString(r.value) + n += wn + if err != nil { + return + } + i += match + len(r.finder.pattern) + } + wn, err = sw.WriteString(s[i:]) + n += wn + return } // byteReplacer is the implementation that's used when all the "old" @@ -301,12 +546,3 @@ func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err erro } return n, nil } - -// strings is too low-level to import io/ioutil -var discard io.Writer = devNull(0) - -type devNull int - -func (devNull) Write(p []byte) (int, error) { - return len(p), nil -} diff --git a/src/pkg/strings/replace_test.go b/src/pkg/strings/replace_test.go index 23c7e2e53..d33dea95b 100644 --- a/src/pkg/strings/replace_test.go +++ b/src/pkg/strings/replace_test.go @@ -7,105 +7,390 @@ package strings_test import ( "bytes" "fmt" - "log" . "strings" "testing" ) -var _ = log.Printf - -type ReplacerTest struct { - r *Replacer - in string - out string -} +var htmlEscaper = NewReplacer( + "&", "&", + "<", "<", + ">", ">", + `"`, """, + "'", "'", +) -var htmlEscaper = NewReplacer("&", "&", "<", "<", ">", ">", "\"", """) +var htmlUnescaper = NewReplacer( + "&", "&", + "<", "<", + ">", ">", + """, `"`, + "'", "'", +) // The http package's old HTML escaping function. -func oldhtmlEscape(s string) string { +func oldHTMLEscape(s string) string { s = Replace(s, "&", "&", -1) s = Replace(s, "<", "<", -1) s = Replace(s, ">", ">", -1) - s = Replace(s, "\"", """, -1) + s = Replace(s, `"`, """, -1) s = Replace(s, "'", "'", -1) return s } -var replacer = NewReplacer("aaa", "3[aaa]", "aa", "2[aa]", "a", "1[a]", "i", "i", - "longerst", "most long", "longer", "medium", "long", "short", - "X", "Y", "Y", "Z") - var capitalLetters = NewReplacer("a", "A", "b", "B") -var blankToXReplacer = NewReplacer("", "X", "o", "O") +// TestReplacer tests the replacer implementations. +func TestReplacer(t *testing.T) { + type testCase struct { + r *Replacer + in, out string + } + var testCases []testCase -var ReplacerTests = []ReplacerTest{ - // byte->string - {htmlEscaper, "No changes", "No changes"}, - {htmlEscaper, "I <3 escaping & stuff", "I <3 escaping & stuff"}, - {htmlEscaper, "&&&", "&&&"}, + // str converts 0xff to "\xff". This isn't just string(b) since that converts to UTF-8. + str := func(b byte) string { + return string([]byte{b}) + } + var s []string - // generic - {replacer, "fooaaabar", "foo3[aaa]b1[a]r"}, - {replacer, "long, longerst, longer", "short, most long, medium"}, - {replacer, "XiX", "YiY"}, + // inc maps "\x00"->"\x01", ..., "a"->"b", "b"->"c", ..., "\xff"->"\x00". + s = nil + for i := 0; i < 256; i++ { + s = append(s, str(byte(i)), str(byte(i+1))) + } + inc := NewReplacer(s...) - // byte->byte - {capitalLetters, "brad", "BrAd"}, - {capitalLetters, Repeat("a", (32<<10)+123), Repeat("A", (32<<10)+123)}, + // Test cases with 1-byte old strings, 1-byte new strings. + testCases = append(testCases, + testCase{capitalLetters, "brad", "BrAd"}, + testCase{capitalLetters, Repeat("a", (32<<10)+123), Repeat("A", (32<<10)+123)}, + testCase{capitalLetters, "", ""}, - // hitting "" special case - {blankToXReplacer, "oo", "XOXOX"}, -} + testCase{inc, "brad", "csbe"}, + testCase{inc, "\x00\xff", "\x01\x00"}, + testCase{inc, "", ""}, -func TestReplacer(t *testing.T) { - for i, tt := range ReplacerTests { - if s := tt.r.Replace(tt.in); s != tt.out { - t.Errorf("%d. Replace(%q) = %q, want %q", i, tt.in, s, tt.out) + testCase{NewReplacer("a", "1", "a", "2"), "brad", "br1d"}, + ) + + // repeat maps "a"->"a", "b"->"bb", "c"->"ccc", ... + s = nil + for i := 0; i < 256; i++ { + n := i + 1 - 'a' + if n < 1 { + n = 1 + } + s = append(s, str(byte(i)), Repeat(str(byte(i)), n)) + } + repeat := NewReplacer(s...) + + // Test cases with 1-byte old strings, variable length new strings. + testCases = append(testCases, + testCase{htmlEscaper, "No changes", "No changes"}, + testCase{htmlEscaper, "I <3 escaping & stuff", "I <3 escaping & stuff"}, + testCase{htmlEscaper, "&&&", "&&&"}, + testCase{htmlEscaper, "", ""}, + + testCase{repeat, "brad", "bbrrrrrrrrrrrrrrrrrradddd"}, + testCase{repeat, "abba", "abbbba"}, + testCase{repeat, "", ""}, + + testCase{NewReplacer("a", "11", "a", "22"), "brad", "br11d"}, + ) + + // The remaining test cases have variable length old strings. + + testCases = append(testCases, + testCase{htmlUnescaper, "&amp;", "&"}, + testCase{htmlUnescaper, "<b>HTML's neat</b>", "HTML's neat"}, + testCase{htmlUnescaper, "", ""}, + + testCase{NewReplacer("a", "1", "a", "2", "xxx", "xxx"), "brad", "br1d"}, + + testCase{NewReplacer("a", "1", "aa", "2", "aaa", "3"), "aaaa", "1111"}, + + testCase{NewReplacer("aaa", "3", "aa", "2", "a", "1"), "aaaa", "31"}, + ) + + // gen1 has multiple old strings of variable length. There is no + // overall non-empty common prefix, but some pairwise common prefixes. + gen1 := NewReplacer( + "aaa", "3[aaa]", + "aa", "2[aa]", + "a", "1[a]", + "i", "i", + "longerst", "most long", + "longer", "medium", + "long", "short", + "xx", "xx", + "x", "X", + "X", "Y", + "Y", "Z", + ) + testCases = append(testCases, + testCase{gen1, "fooaaabar", "foo3[aaa]b1[a]r"}, + testCase{gen1, "long, longerst, longer", "short, most long, medium"}, + testCase{gen1, "xxxxx", "xxxxX"}, + testCase{gen1, "XiX", "YiY"}, + testCase{gen1, "", ""}, + ) + + // gen2 has multiple old strings with no pairwise common prefix. + gen2 := NewReplacer( + "roses", "red", + "violets", "blue", + "sugar", "sweet", + ) + testCases = append(testCases, + testCase{gen2, "roses are red, violets are blue...", "red are red, blue are blue..."}, + testCase{gen2, "", ""}, + ) + + // gen3 has multiple old strings with an overall common prefix. + gen3 := NewReplacer( + "abracadabra", "poof", + "abracadabrakazam", "splat", + "abraham", "lincoln", + "abrasion", "scrape", + "abraham", "isaac", + ) + testCases = append(testCases, + testCase{gen3, "abracadabrakazam abraham", "poofkazam lincoln"}, + testCase{gen3, "abrasion abracad", "scrape abracad"}, + testCase{gen3, "abba abram abrasive", "abba abram abrasive"}, + testCase{gen3, "", ""}, + ) + + // foo{1,2,3,4} have multiple old strings with an overall common prefix + // and 1- or 2- byte extensions from the common prefix. + foo1 := NewReplacer( + "foo1", "A", + "foo2", "B", + "foo3", "C", + ) + foo2 := NewReplacer( + "foo1", "A", + "foo2", "B", + "foo31", "C", + "foo32", "D", + ) + foo3 := NewReplacer( + "foo11", "A", + "foo12", "B", + "foo31", "C", + "foo32", "D", + ) + foo4 := NewReplacer( + "foo12", "B", + "foo32", "D", + ) + testCases = append(testCases, + testCase{foo1, "fofoofoo12foo32oo", "fofooA2C2oo"}, + testCase{foo1, "", ""}, + + testCase{foo2, "fofoofoo12foo32oo", "fofooA2Doo"}, + testCase{foo2, "", ""}, + + testCase{foo3, "fofoofoo12foo32oo", "fofooBDoo"}, + testCase{foo3, "", ""}, + + testCase{foo4, "fofoofoo12foo32oo", "fofooBDoo"}, + testCase{foo4, "", ""}, + ) + + // genAll maps "\x00\x01\x02...\xfe\xff" to "[all]", amongst other things. + allBytes := make([]byte, 256) + for i := range allBytes { + allBytes[i] = byte(i) + } + allString := string(allBytes) + genAll := NewReplacer( + allString, "[all]", + "\xff", "[ff]", + "\x00", "[00]", + ) + testCases = append(testCases, + testCase{genAll, allString, "[all]"}, + testCase{genAll, "a\xff" + allString + "\x00", "a[ff][all][00]"}, + testCase{genAll, "", ""}, + ) + + // Test cases with empty old strings. + + blankToX1 := NewReplacer("", "X") + blankToX2 := NewReplacer("", "X", "", "") + blankHighPriority := NewReplacer("", "X", "o", "O") + blankLowPriority := NewReplacer("o", "O", "", "X") + blankNoOp1 := NewReplacer("", "") + blankNoOp2 := NewReplacer("", "", "", "A") + blankFoo := NewReplacer("", "X", "foobar", "R", "foobaz", "Z") + testCases = append(testCases, + testCase{blankToX1, "foo", "XfXoXoX"}, + testCase{blankToX1, "", "X"}, + + testCase{blankToX2, "foo", "XfXoXoX"}, + testCase{blankToX2, "", "X"}, + + testCase{blankHighPriority, "oo", "XOXOX"}, + testCase{blankHighPriority, "ii", "XiXiX"}, + testCase{blankHighPriority, "oiio", "XOXiXiXOX"}, + testCase{blankHighPriority, "iooi", "XiXOXOXiX"}, + testCase{blankHighPriority, "", "X"}, + + testCase{blankLowPriority, "oo", "OOX"}, + testCase{blankLowPriority, "ii", "XiXiX"}, + testCase{blankLowPriority, "oiio", "OXiXiOX"}, + testCase{blankLowPriority, "iooi", "XiOOXiX"}, + testCase{blankLowPriority, "", "X"}, + + testCase{blankNoOp1, "foo", "foo"}, + testCase{blankNoOp1, "", ""}, + + testCase{blankNoOp2, "foo", "foo"}, + testCase{blankNoOp2, "", ""}, + + testCase{blankFoo, "foobarfoobaz", "XRXZX"}, + testCase{blankFoo, "foobar-foobaz", "XRX-XZX"}, + testCase{blankFoo, "", "X"}, + ) + + // single string replacer + + abcMatcher := NewReplacer("abc", "[match]") + + testCases = append(testCases, + testCase{abcMatcher, "", ""}, + testCase{abcMatcher, "ab", "ab"}, + testCase{abcMatcher, "abcd", "[match]d"}, + testCase{abcMatcher, "cabcabcdabca", "c[match][match]d[match]a"}, + ) + + // No-arg test cases. + + nop := NewReplacer() + testCases = append(testCases, + testCase{nop, "abc", "abc"}, + testCase{nop, "", ""}, + ) + + // Run the test cases. + + for i, tc := range testCases { + if s := tc.r.Replace(tc.in); s != tc.out { + t.Errorf("%d. Replace(%q) = %q, want %q", i, tc.in, s, tc.out) } var buf bytes.Buffer - n, err := tt.r.WriteString(&buf, tt.in) + n, err := tc.r.WriteString(&buf, tc.in) if err != nil { t.Errorf("%d. WriteString: %v", i, err) continue } got := buf.String() - if got != tt.out { - t.Errorf("%d. WriteString(%q) wrote %q, want %q", i, tt.in, got, tt.out) + if got != tc.out { + t.Errorf("%d. WriteString(%q) wrote %q, want %q", i, tc.in, got, tc.out) continue } - if n != len(tt.out) { + if n != len(tc.out) { t.Errorf("%d. WriteString(%q) wrote correct string but reported %d bytes; want %d (%q)", - i, tt.in, n, len(tt.out), tt.out) + i, tc.in, n, len(tc.out), tc.out) } } } -// pickAlgorithmTest is a test that verifies that given input for a -// Replacer that we pick the correct algorithm. -type pickAlgorithmTest struct { - r *Replacer - want string // name of algorithm +// TestPickAlgorithm tests that NewReplacer picks the correct algorithm. +func TestPickAlgorithm(t *testing.T) { + testCases := []struct { + r *Replacer + want string + }{ + {capitalLetters, "*strings.byteReplacer"}, + {htmlEscaper, "*strings.byteStringReplacer"}, + {NewReplacer("12", "123"), "*strings.singleStringReplacer"}, + {NewReplacer("1", "12"), "*strings.byteStringReplacer"}, + {NewReplacer("", "X"), "*strings.genericReplacer"}, + {NewReplacer("a", "1", "b", "12", "cde", "123"), "*strings.genericReplacer"}, + } + for i, tc := range testCases { + got := fmt.Sprintf("%T", tc.r.Replacer()) + if got != tc.want { + t.Errorf("%d. algorithm = %s, want %s", i, got, tc.want) + } + } } -var pickAlgorithmTests = []pickAlgorithmTest{ - {capitalLetters, "*strings.byteReplacer"}, - {NewReplacer("12", "123"), "*strings.genericReplacer"}, - {NewReplacer("1", "12"), "*strings.byteStringReplacer"}, - {htmlEscaper, "*strings.byteStringReplacer"}, -} +// TestGenericTrieBuilding verifies the structure of the generated trie. There +// is one node per line, and the key ending with the current line is in the +// trie if it ends with a "+". +func TestGenericTrieBuilding(t *testing.T) { + testCases := []struct{ in, out string }{ + {"abc;abdef;abdefgh;xx;xy;z", `- + a- + .b- + ..c+ + ..d- + ...ef+ + .....gh+ + x- + .x+ + .y+ + z+ + `}, + {"abracadabra;abracadabrakazam;abraham;abrasion", `- + a- + .bra- + ....c- + .....adabra+ + ...........kazam+ + ....h- + .....am+ + ....s- + .....ion+ + `}, + {"aaa;aa;a;i;longerst;longer;long;xx;x;X;Y", `- + X+ + Y+ + a+ + .a+ + ..a+ + i+ + l- + .ong+ + ....er+ + ......st+ + x+ + .x+ + `}, + {"foo;;foo;foo1", `+ + f- + .oo+ + ...1+ + `}, + } -func TestPickAlgorithm(t *testing.T) { - for i, tt := range pickAlgorithmTests { - got := fmt.Sprintf("%T", tt.r.Replacer()) - if got != tt.want { - t.Errorf("%d. algorithm = %s, want %s", i, got, tt.want) + for _, tc := range testCases { + keys := Split(tc.in, ";") + args := make([]string, len(keys)*2) + for i, key := range keys { + args[i*2] = key + } + + got := NewReplacer(args...).PrintTrie() + // Remove tabs from tc.out + wantbuf := make([]byte, 0, len(tc.out)) + for i := 0; i < len(tc.out); i++ { + if tc.out[i] != '\t' { + wantbuf = append(wantbuf, tc.out[i]) + } + } + want := string(wantbuf) + + if got != want { + t.Errorf("PrintTrie(%q)\ngot\n%swant\n%s", tc.in, got, want) } } } -func BenchmarkGenericMatch(b *testing.B) { +func BenchmarkGenericNoMatch(b *testing.B) { str := Repeat("A", 100) + Repeat("B", 100) generic := NewReplacer("a", "A", "b", "B", "12", "123") // varying lengths forces generic for i := 0; i < b.N; i++ { @@ -113,6 +398,42 @@ func BenchmarkGenericMatch(b *testing.B) { } } +func BenchmarkGenericMatch1(b *testing.B) { + str := Repeat("a", 100) + Repeat("b", 100) + generic := NewReplacer("a", "A", "b", "B", "12", "123") + for i := 0; i < b.N; i++ { + generic.Replace(str) + } +} + +func BenchmarkGenericMatch2(b *testing.B) { + str := Repeat("It's <b>HTML</b>!", 100) + for i := 0; i < b.N; i++ { + htmlUnescaper.Replace(str) + } +} + +func benchmarkSingleString(b *testing.B, pattern, text string) { + r := NewReplacer(pattern, "[match]") + b.SetBytes(int64(len(text))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + r.Replace(text) + } +} + +func BenchmarkSingleMaxSkipping(b *testing.B) { + benchmarkSingleString(b, Repeat("b", 25), Repeat("a", 10000)) +} + +func BenchmarkSingleLongSuffixFail(b *testing.B) { + benchmarkSingleString(b, "b"+Repeat("a", 500), Repeat("a", 1002)) +} + +func BenchmarkSingleMatch(b *testing.B) { + benchmarkSingleString(b, "abcdef", Repeat("abcdefghijklmno", 1000)) +} + func BenchmarkByteByteNoMatch(b *testing.B) { str := Repeat("A", 100) + Repeat("B", 100) for i := 0; i < b.N; i++ { @@ -144,7 +465,7 @@ func BenchmarkHTMLEscapeNew(b *testing.B) { func BenchmarkHTMLEscapeOld(b *testing.B) { str := "I <3 to escape HTML & other text too." for i := 0; i < b.N; i++ { - oldhtmlEscape(str) + oldHTMLEscape(str) } } diff --git a/src/pkg/strings/search.go b/src/pkg/strings/search.go new file mode 100644 index 000000000..f77c879c5 --- /dev/null +++ b/src/pkg/strings/search.go @@ -0,0 +1,124 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package strings + +// stringFinder efficiently finds strings in a source text. It's implemented +// using the Boyer-Moore string search algorithm: +// http://en.wikipedia.org/wiki/Boyer-Moore_string_search_algorithm +// http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf (note: this aged +// document uses 1-based indexing) +type stringFinder struct { + // pattern is the string that we are searching for in the text. + pattern string + + // badCharSkip[b] contains the distance between the last byte of pattern + // and the rightmost occurrence of b in pattern. If b is not in pattern, + // badCharSkip[b] is len(pattern). + // + // Whenever a mismatch is found with byte b in the text, we can safely + // shift the matching frame at least badCharSkip[b] until the next time + // the matching char could be in alignment. + badCharSkip [256]int + + // goodSuffixSkip[i] defines how far we can shift the matching frame given + // that the suffix pattern[i+1:] matches, but the byte pattern[i] does + // not. There are two cases to consider: + // + // 1. The matched suffix occurs elsewhere in pattern (with a different + // byte preceding it that we might possibly match). In this case, we can + // shift the matching frame to align with the next suffix chunk. For + // example, the pattern "mississi" has the suffix "issi" next occurring + // (in right-to-left order) at index 1, so goodSuffixSkip[3] == + // shift+len(suffix) == 3+4 == 7. + // + // 2. If the matched suffix does not occur elsewhere in pattern, then the + // matching frame may share part of its prefix with the end of the + // matching suffix. In this case, goodSuffixSkip[i] will contain how far + // to shift the frame to align this portion of the prefix to the + // suffix. For example, in the pattern "abcxxxabc", when the first + // mismatch from the back is found to be in position 3, the matching + // suffix "xxabc" is not found elsewhere in the pattern. However, its + // rightmost "abc" (at position 6) is a prefix of the whole pattern, so + // goodSuffixSkip[3] == shift+len(suffix) == 6+5 == 11. + goodSuffixSkip []int +} + +func makeStringFinder(pattern string) *stringFinder { + f := &stringFinder{ + pattern: pattern, + goodSuffixSkip: make([]int, len(pattern)), + } + // last is the index of the last character in the pattern. + last := len(pattern) - 1 + + // Build bad character table. + // Bytes not in the pattern can skip one pattern's length. + for i := range f.badCharSkip { + f.badCharSkip[i] = len(pattern) + } + // The loop condition is < instead of <= so that the last byte does not + // have a zero distance to itself. Finding this byte out of place implies + // that it is not in the last position. + for i := 0; i < last; i++ { + f.badCharSkip[pattern[i]] = last - i + } + + // Build good suffix table. + // First pass: set each value to the next index which starts a prefix of + // pattern. + lastPrefix := last + for i := last; i >= 0; i-- { + if HasPrefix(pattern, pattern[i+1:]) { + lastPrefix = i + 1 + } + // lastPrefix is the shift, and (last-i) is len(suffix). + f.goodSuffixSkip[i] = lastPrefix + last - i + } + // Second pass: find repeats of pattern's suffix starting from the front. + for i := 0; i < last; i++ { + lenSuffix := longestCommonSuffix(pattern, pattern[1:i+1]) + if pattern[i-lenSuffix] != pattern[last-lenSuffix] { + // (last-i) is the shift, and lenSuffix is len(suffix). + f.goodSuffixSkip[last-lenSuffix] = lenSuffix + last - i + } + } + + return f +} + +func longestCommonSuffix(a, b string) (i int) { + for ; i < len(a) && i < len(b); i++ { + if a[len(a)-1-i] != b[len(b)-1-i] { + break + } + } + return +} + +// next returns the index in text of the first occurrence of the pattern. If +// the pattern is not found, it returns -1. +func (f *stringFinder) next(text string) int { + i := len(f.pattern) - 1 + for i < len(text) { + // Compare backwards from the end until the first unmatching character. + j := len(f.pattern) - 1 + for j >= 0 && text[i] == f.pattern[j] { + i-- + j-- + } + if j < 0 { + return i + 1 // match + } + i += max(f.badCharSkip[text[i]], f.goodSuffixSkip[j]) + } + return -1 +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/src/pkg/strings/search_test.go b/src/pkg/strings/search_test.go new file mode 100644 index 000000000..966c05e65 --- /dev/null +++ b/src/pkg/strings/search_test.go @@ -0,0 +1,90 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package strings_test + +import ( + "reflect" + . "strings" + "testing" +) + +func TestFinderNext(t *testing.T) { + testCases := []struct { + pat, text string + index int + }{ + {"", "", 0}, + {"", "abc", 0}, + {"abc", "", -1}, + {"abc", "abc", 0}, + {"d", "abcdefg", 3}, + {"nan", "banana", 2}, + {"pan", "anpanman", 2}, + {"nnaaman", "anpanmanam", -1}, + {"abcd", "abc", -1}, + {"abcd", "bcd", -1}, + {"bcd", "abcd", 1}, + {"abc", "acca", -1}, + {"aa", "aaa", 0}, + {"baa", "aaaaa", -1}, + {"at that", "which finally halts. at that point", 22}, + } + + for _, tc := range testCases { + got := StringFind(tc.pat, tc.text) + want := tc.index + if got != want { + t.Errorf("stringFind(%q, %q) got %d, want %d\n", tc.pat, tc.text, got, want) + } + } +} + +func TestFinderCreation(t *testing.T) { + testCases := []struct { + pattern string + bad [256]int + suf []int + }{ + { + "abc", + [256]int{'a': 2, 'b': 1, 'c': 3}, + []int{5, 4, 1}, + }, + { + "mississi", + [256]int{'i': 3, 'm': 7, 's': 1}, + []int{15, 14, 13, 7, 11, 10, 7, 1}, + }, + // From http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf + { + "abcxxxabc", + [256]int{'a': 2, 'b': 1, 'c': 6, 'x': 3}, + []int{14, 13, 12, 11, 10, 9, 11, 10, 1}, + }, + { + "abyxcdeyx", + [256]int{'a': 8, 'b': 7, 'c': 4, 'd': 3, 'e': 2, 'y': 1, 'x': 5}, + []int{17, 16, 15, 14, 13, 12, 7, 10, 1}, + }, + } + + for _, tc := range testCases { + bad, good := DumpTables(tc.pattern) + + for i, got := range bad { + want := tc.bad[i] + if want == 0 { + want = len(tc.pattern) + } + if got != want { + t.Errorf("boyerMoore(%q) bad['%c']: got %d want %d", tc.pattern, i, got, want) + } + } + + if !reflect.DeepEqual(good, tc.suf) { + t.Errorf("boyerMoore(%q) got %v want %v", tc.pattern, good, tc.suf) + } + } +} diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go index b411ba5d8..ccf415e69 100644 --- a/src/pkg/strings/strings.go +++ b/src/pkg/strings/strings.go @@ -36,27 +36,69 @@ func explode(s string, n int) []string { return a } +// primeRK is the prime base used in Rabin-Karp algorithm. +const primeRK = 16777619 + +// hashstr returns the hash and the appropriate multiplicative +// factor for use in Rabin-Karp algorithm. +func hashstr(sep string) (uint32, uint32) { + hash := uint32(0) + for i := 0; i < len(sep); i++ { + hash = hash*primeRK + uint32(sep[i]) + + } + var pow, sq uint32 = 1, primeRK + for i := len(sep); i > 0; i >>= 1 { + if i&1 != 0 { + pow *= sq + } + sq *= sq + } + return hash, pow +} + // Count counts the number of non-overlapping instances of sep in s. func Count(s, sep string) int { - if sep == "" { - return utf8.RuneCountInString(s) + 1 - } - c := sep[0] - l := len(sep) n := 0 - if l == 1 { + // special cases + switch { + case len(sep) == 0: + return utf8.RuneCountInString(s) + 1 + case len(sep) == 1: // special case worth making fast + c := sep[0] for i := 0; i < len(s); i++ { if s[i] == c { n++ } } return n + case len(sep) > len(s): + return 0 + case len(sep) == len(s): + if sep == s { + return 1 + } + return 0 } - for i := 0; i+l <= len(s); i++ { - if s[i] == c && s[i:i+l] == sep { + hashsep, pow := hashstr(sep) + h := uint32(0) + for i := 0; i < len(sep); i++ { + h = h*primeRK + uint32(s[i]) + } + lastmatch := 0 + if h == hashsep && s[:len(sep)] == sep { + n++ + lastmatch = len(sep) + } + for i := len(sep); i < len(s); { + h *= primeRK + h += uint32(s[i]) + h -= pow * uint32(s[i-len(sep)]) + i++ + if h == hashsep && lastmatch <= i-len(sep) && s[i-len(sep):i] == sep { n++ - i += l - 1 + lastmatch = i } } return n @@ -80,11 +122,11 @@ func ContainsRune(s string, r rune) bool { // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. func Index(s, sep string) int { n := len(sep) - if n == 0 { + switch { + case n == 0: return 0 - } - c := sep[0] - if n == 1 { + case n == 1: + c := sep[0] // special case worth making fast for i := 0; i < len(s); i++ { if s[i] == c { @@ -92,11 +134,30 @@ func Index(s, sep string) int { } } return -1 + case n == len(s): + if sep == s { + return 0 + } + return -1 + case n > len(s): + return -1 } - // n > 1 - for i := 0; i+n <= len(s); i++ { - if s[i] == c && s[i:i+n] == sep { - return i + // Hash sep. + hashsep, pow := hashstr(sep) + var h uint32 + for i := 0; i < n; i++ { + h = h*primeRK + uint32(s[i]) + } + if h == hashsep && s[:n] == sep { + return 0 + } + for i := n; i < len(s); { + h *= primeRK + h += uint32(s[i]) + h -= pow * uint32(s[i-n]) + i++ + if h == hashsep && s[i-n:i] == sep { + return i - n } } return -1 @@ -244,7 +305,8 @@ func SplitAfter(s, sep string) []string { } // Fields splits the string s around each instance of one or more consecutive white space -// characters, returning an array of substrings of s or an empty list if s contains only white space. +// characters, as defined by unicode.IsSpace, returning an array of substrings of s or an +// empty list if s contains only white space. func Fields(s string) []string { return FieldsFunc(s, unicode.IsSpace) } @@ -558,6 +620,24 @@ func TrimSpace(s string) string { return TrimFunc(s, unicode.IsSpace) } +// TrimPrefix returns s without the provided leading prefix string. +// If s doesn't start with prefix, s is returned unchanged. +func TrimPrefix(s, prefix string) string { + if HasPrefix(s, prefix) { + return s[len(prefix):] + } + return s +} + +// TrimSuffix returns s without the provided trailing suffix string. +// If s doesn't end with suffix, s is returned unchanged. +func TrimSuffix(s, suffix string) string { + if HasSuffix(s, suffix) { + return s[:len(s)-len(suffix)] + } + return s +} + // Replace returns a copy of the string s with the first n // non-overlapping instances of old replaced by new. // If n < 0, there is no limit on the number of replacements. diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go index 54046d68a..09de49e5f 100644 --- a/src/pkg/strings/strings_test.go +++ b/src/pkg/strings/strings_test.go @@ -7,6 +7,7 @@ package strings_test import ( "bytes" "io" + "math/rand" "reflect" . "strings" "testing" @@ -311,6 +312,13 @@ var FieldsFuncTests = []FieldsTest{ } func TestFieldsFunc(t *testing.T) { + for _, tt := range fieldstests { + a := FieldsFunc(tt.s, unicode.IsSpace) + if !eq(a, tt.a) { + t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a) + continue + } + } pred := func(c rune) bool { return c == 'X' } for _, tt := range FieldsFuncTests { a := FieldsFunc(tt.s, pred) @@ -488,8 +496,8 @@ func TestSpecialCase(t *testing.T) { func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) } var trimTests = []struct { - f string - in, cutset, out string + f string + in, arg, out string }{ {"Trim", "abba", "a", "bb"}, {"Trim", "abba", "ab", ""}, @@ -512,6 +520,10 @@ var trimTests = []struct { {"TrimRight", "", "123", ""}, {"TrimRight", "", "", ""}, {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, + {"TrimPrefix", "aabb", "a", "abb"}, + {"TrimPrefix", "aabb", "b", "aabb"}, + {"TrimSuffix", "aabb", "a", "aabb"}, + {"TrimSuffix", "aabb", "b", "aab"}, } func TestTrim(t *testing.T) { @@ -525,12 +537,16 @@ func TestTrim(t *testing.T) { f = TrimLeft case "TrimRight": f = TrimRight + case "TrimPrefix": + f = TrimPrefix + case "TrimSuffix": + f = TrimSuffix default: t.Errorf("Undefined trim function %s", name) } - actual := f(tc.in, tc.cutset) + actual := f(tc.in, tc.arg) if actual != tc.out { - t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out) + t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out) } } } @@ -951,7 +967,7 @@ var ContainsRuneTests = []struct { func TestContainsRune(t *testing.T) { for _, ct := range ContainsRuneTests { if ContainsRune(ct.str, ct.r) != ct.expected { - t.Errorf("ContainsRune(%s, %s) = %v, want %v", + t.Errorf("ContainsRune(%q, %q) = %v, want %v", ct.str, ct.r, !ct.expected, ct.expected) } } @@ -984,3 +1000,98 @@ func TestEqualFold(t *testing.T) { } } } + +func makeBenchInputHard() string { + tokens := [...]string{ + "", "

", "", "", + "", "

", "", "", + "hello", "world", + } + x := make([]byte, 0, 1<<20) + for len(x) < 1<<20 { + i := rand.Intn(len(tokens)) + x = append(x, tokens[i]...) + } + return string(x) +} + +var benchInputHard = makeBenchInputHard() + +func benchmarkIndexHard(b *testing.B, sep string) { + for i := 0; i < b.N; i++ { + Index(benchInputHard, sep) + } +} + +func benchmarkCountHard(b *testing.B, sep string) { + for i := 0; i < b.N; i++ { + Count(benchInputHard, sep) + } +} + +func BenchmarkIndexHard1(b *testing.B) { benchmarkIndexHard(b, "<>") } +func BenchmarkIndexHard2(b *testing.B) { benchmarkIndexHard(b, "") } +func BenchmarkIndexHard3(b *testing.B) { benchmarkIndexHard(b, "hello world") } + +func BenchmarkCountHard1(b *testing.B) { benchmarkCountHard(b, "<>") } +func BenchmarkCountHard2(b *testing.B) { benchmarkCountHard(b, "") } +func BenchmarkCountHard3(b *testing.B) { benchmarkCountHard(b, "hello world") } + +var benchInputTorture = Repeat("ABC", 1<<10) + "123" + Repeat("ABC", 1<<10) +var benchNeedleTorture = Repeat("ABC", 1<<10+1) + +func BenchmarkIndexTorture(b *testing.B) { + for i := 0; i < b.N; i++ { + Index(benchInputTorture, benchNeedleTorture) + } +} + +func BenchmarkCountTorture(b *testing.B) { + for i := 0; i < b.N; i++ { + Count(benchInputTorture, benchNeedleTorture) + } +} + +func BenchmarkCountTortureOverlapping(b *testing.B) { + A := Repeat("ABC", 1<<20) + B := Repeat("ABC", 1<<10) + for i := 0; i < b.N; i++ { + Count(A, B) + } +} + +var makeFieldsInput = func() string { + x := make([]byte, 1<<20) + // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. + for i := range x { + switch rand.Intn(10) { + case 0: + x[i] = ' ' + case 1: + if i > 0 && x[i-1] == 'x' { + copy(x[i-1:], "χ") + break + } + fallthrough + default: + x[i] = 'x' + } + } + return string(x) +} + +var fieldsInput = makeFieldsInput() + +func BenchmarkFields(b *testing.B) { + b.SetBytes(int64(len(fieldsInput))) + for i := 0; i < b.N; i++ { + Fields(fieldsInput) + } +} + +func BenchmarkFieldsFunc(b *testing.B) { + b.SetBytes(int64(len(fieldsInput))) + for i := 0; i < b.N; i++ { + FieldsFunc(fieldsInput, unicode.IsSpace) + } +} diff --git a/src/pkg/sync/atomic/64bit_arm.go b/src/pkg/sync/atomic/64bit_arm.go new file mode 100644 index 000000000..f070e78bd --- /dev/null +++ b/src/pkg/sync/atomic/64bit_arm.go @@ -0,0 +1,36 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package atomic + +func loadUint64(addr *uint64) (val uint64) { + for { + val = *addr + if CompareAndSwapUint64(addr, val, val) { + break + } + } + return +} + +func storeUint64(addr *uint64, val uint64) { + for { + old := *addr + if CompareAndSwapUint64(addr, old, val) { + break + } + } + return +} + +func addUint64(val *uint64, delta uint64) (new uint64) { + for { + old := *val + new = old + delta + if CompareAndSwapUint64(val, old, new) { + break + } + } + return +} diff --git a/src/pkg/sync/atomic/asm_386.s b/src/pkg/sync/atomic/asm_386.s index a406852f4..7a98a61d8 100644 --- a/src/pkg/sync/atomic/asm_386.s +++ b/src/pkg/sync/atomic/asm_386.s @@ -2,17 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !race + TEXT ·CompareAndSwapInt32(SB),7,$0 JMP ·CompareAndSwapUint32(SB) TEXT ·CompareAndSwapUint32(SB),7,$0 - MOVL valptr+0(FP), BP + MOVL addr+0(FP), BP MOVL old+4(FP), AX MOVL new+8(FP), CX // CMPXCHGL was introduced on the 486. LOCK CMPXCHGL CX, 0(BP) - SETEQ ret+12(FP) + SETEQ swapped+12(FP) RET TEXT ·CompareAndSwapUintptr(SB),7,$0 @@ -25,29 +27,29 @@ TEXT ·CompareAndSwapInt64(SB),7,$0 JMP ·CompareAndSwapUint64(SB) TEXT ·CompareAndSwapUint64(SB),7,$0 - MOVL valptr+0(FP), BP - MOVL oldlo+4(FP), AX - MOVL oldhi+8(FP), DX - MOVL newlo+12(FP), BX - MOVL newhi+16(FP), CX + MOVL addr+0(FP), BP + MOVL old+4(FP), AX + MOVL old+8(FP), DX + MOVL new+12(FP), BX + MOVL new+16(FP), CX // CMPXCHG8B was introduced on the Pentium. LOCK CMPXCHG8B 0(BP) - SETEQ ret+20(FP) + SETEQ swapped+20(FP) RET TEXT ·AddInt32(SB),7,$0 JMP ·AddUint32(SB) TEXT ·AddUint32(SB),7,$0 - MOVL valptr+0(FP), BP + MOVL addr+0(FP), BP MOVL delta+4(FP), AX MOVL AX, CX // XADD was introduced on the 486. LOCK XADDL AX, 0(BP) ADDL AX, CX - MOVL CX, ret+8(FP) + MOVL CX, new+8(FP) RET TEXT ·AddUintptr(SB),7,$0 @@ -58,24 +60,24 @@ TEXT ·AddInt64(SB),7,$0 TEXT ·AddUint64(SB),7,$0 // no XADDQ so use CMPXCHG8B loop - MOVL valptr+0(FP), BP + MOVL addr+0(FP), BP // DI:SI = delta - MOVL deltalo+4(FP), SI - MOVL deltahi+8(FP), DI - // DX:AX = *valptr + MOVL delta+4(FP), SI + MOVL delta+8(FP), DI + // DX:AX = *addr MOVL 0(BP), AX MOVL 4(BP), DX addloop: - // CX:BX = DX:AX (*valptr) + DI:SI (delta) + // CX:BX = DX:AX (*addr) + DI:SI (delta) MOVL AX, BX MOVL DX, CX ADDL SI, BX ADCL DI, CX - // if *valptr == DX:AX { - // *valptr = CX:BX + // if *addr == DX:AX { + // *addr = CX:BX // } else { - // DX:AX = *valptr + // DX:AX = *addr // } // all in one instruction LOCK @@ -85,24 +87,24 @@ addloop: // success // return CX:BX - MOVL BX, retlo+12(FP) - MOVL CX, rethi+16(FP) + MOVL BX, new+12(FP) + MOVL CX, new+16(FP) RET TEXT ·LoadInt32(SB),7,$0 JMP ·LoadUint32(SB) TEXT ·LoadUint32(SB),7,$0 - MOVL addrptr+0(FP), AX + MOVL addr+0(FP), AX MOVL 0(AX), AX - MOVL AX, ret+4(FP) + MOVL AX, val+4(FP) RET TEXT ·LoadInt64(SB),7,$0 JMP ·LoadUint64(SB) TEXT ·LoadUint64(SB),7,$0 - MOVL addrptr+0(FP), AX + MOVL addr+0(FP), AX // MOVQ and EMMS were introduced on the Pentium MMX. // MOVQ (%EAX), %MM0 BYTE $0x0f; BYTE $0x6f; BYTE $0x00 @@ -121,7 +123,7 @@ TEXT ·StoreInt32(SB),7,$0 JMP ·StoreUint32(SB) TEXT ·StoreUint32(SB),7,$0 - MOVL addrptr+0(FP), BP + MOVL addr+0(FP), BP MOVL val+4(FP), AX XCHGL AX, 0(BP) RET @@ -130,7 +132,7 @@ TEXT ·StoreInt64(SB),7,$0 JMP ·StoreUint64(SB) TEXT ·StoreUint64(SB),7,$0 - MOVL addrptr+0(FP), AX + MOVL addr+0(FP), AX // MOVQ and EMMS were introduced on the Pentium MMX. // MOVQ 0x8(%ESP), %MM0 BYTE $0x0f; BYTE $0x6f; BYTE $0x44; BYTE $0x24; BYTE $0x08 diff --git a/src/pkg/sync/atomic/asm_amd64.s b/src/pkg/sync/atomic/asm_amd64.s index 6f8bde068..58bda9e4f 100644 --- a/src/pkg/sync/atomic/asm_amd64.s +++ b/src/pkg/sync/atomic/asm_amd64.s @@ -2,16 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !race + TEXT ·CompareAndSwapInt32(SB),7,$0 JMP ·CompareAndSwapUint32(SB) TEXT ·CompareAndSwapUint32(SB),7,$0 - MOVQ valptr+0(FP), BP + MOVQ addr+0(FP), BP MOVL old+8(FP), AX MOVL new+12(FP), CX LOCK CMPXCHGL CX, 0(BP) - SETEQ ret+16(FP) + SETEQ swapped+16(FP) RET TEXT ·CompareAndSwapUintptr(SB),7,$0 @@ -24,25 +26,25 @@ TEXT ·CompareAndSwapInt64(SB),7,$0 JMP ·CompareAndSwapUint64(SB) TEXT ·CompareAndSwapUint64(SB),7,$0 - MOVQ valptr+0(FP), BP + MOVQ addr+0(FP), BP MOVQ old+8(FP), AX MOVQ new+16(FP), CX LOCK CMPXCHGQ CX, 0(BP) - SETEQ ret+24(FP) + SETEQ swapped+24(FP) RET TEXT ·AddInt32(SB),7,$0 JMP ·AddUint32(SB) TEXT ·AddUint32(SB),7,$0 - MOVQ valptr+0(FP), BP + MOVQ addr+0(FP), BP MOVL delta+8(FP), AX MOVL AX, CX LOCK XADDL AX, 0(BP) ADDL AX, CX - MOVL CX, ret+16(FP) + MOVL CX, new+16(FP) RET TEXT ·AddUintptr(SB),7,$0 @@ -52,47 +54,47 @@ TEXT ·AddInt64(SB),7,$0 JMP ·AddUint64(SB) TEXT ·AddUint64(SB),7,$0 - MOVQ valptr+0(FP), BP + MOVQ addr+0(FP), BP MOVQ delta+8(FP), AX MOVQ AX, CX LOCK XADDQ AX, 0(BP) ADDQ AX, CX - MOVQ CX, ret+16(FP) + MOVQ CX, new+16(FP) RET TEXT ·LoadInt32(SB),7,$0 JMP ·LoadUint32(SB) TEXT ·LoadUint32(SB),7,$0 - MOVQ addrptr+0(FP), AX + MOVQ addr+0(FP), AX MOVL 0(AX), AX - MOVL AX, ret+8(FP) + MOVL AX, val+8(FP) RET TEXT ·LoadInt64(SB),7,$0 JMP ·LoadUint64(SB) TEXT ·LoadUint64(SB),7,$0 - MOVQ addrptr+0(FP), AX + MOVQ addr+0(FP), AX MOVQ 0(AX), AX - MOVQ AX, ret+8(FP) + MOVQ AX, val+8(FP) RET TEXT ·LoadUintptr(SB),7,$0 JMP ·LoadPointer(SB) TEXT ·LoadPointer(SB),7,$0 - MOVQ addrptr+0(FP), AX + MOVQ addr+0(FP), AX MOVQ 0(AX), AX - MOVQ AX, ret+8(FP) + MOVQ AX, val+8(FP) RET TEXT ·StoreInt32(SB),7,$0 JMP ·StoreUint32(SB) TEXT ·StoreUint32(SB),7,$0 - MOVQ addrptr+0(FP), BP + MOVQ addr+0(FP), BP MOVL val+8(FP), AX XCHGL AX, 0(BP) RET @@ -101,7 +103,7 @@ TEXT ·StoreInt64(SB),7,$0 JMP ·StoreUint64(SB) TEXT ·StoreUint64(SB),7,$0 - MOVQ addrptr+0(FP), BP + MOVQ addr+0(FP), BP MOVQ val+8(FP), AX XCHGQ AX, 0(BP) RET @@ -110,7 +112,7 @@ TEXT ·StoreUintptr(SB),7,$0 JMP ·StorePointer(SB) TEXT ·StorePointer(SB),7,$0 - MOVQ addrptr+0(FP), BP + MOVQ addr+0(FP), BP MOVQ val+8(FP), AX XCHGQ AX, 0(BP) RET diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s index 2d10a922b..4faf5b5d9 100644 --- a/src/pkg/sync/atomic/asm_arm.s +++ b/src/pkg/sync/atomic/asm_arm.s @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !race + // ARM atomic operations, for use by asm_$(GOOS)_arm.s. TEXT ·armCompareAndSwapUint32(SB),7,$0 - MOVW valptr+0(FP), R1 + MOVW addr+0(FP), R1 MOVW old+4(FP), R2 MOVW new+8(FP), R3 casloop: @@ -26,7 +28,7 @@ casfail: TEXT ·armCompareAndSwapUint64(SB),7,$0 BL fastCheck64<>(SB) - MOVW valptr+0(FP), R1 + MOVW addr+0(FP), R1 MOVW oldlo+4(FP), R2 MOVW oldhi+8(FP), R3 MOVW newlo+12(FP), R4 @@ -50,7 +52,7 @@ cas64fail: RET TEXT ·armAddUint32(SB),7,$0 - MOVW valptr+0(FP), R1 + MOVW addr+0(FP), R1 MOVW delta+4(FP), R2 addloop: // LDREX and STREX were introduced in ARM 6. @@ -64,7 +66,7 @@ addloop: TEXT ·armAddUint64(SB),7,$0 BL fastCheck64<>(SB) - MOVW valptr+0(FP), R1 + MOVW addr+0(FP), R1 MOVW deltalo+4(FP), R2 MOVW deltahi+8(FP), R3 add64loop: @@ -81,7 +83,7 @@ add64loop: TEXT ·armLoadUint64(SB),7,$0 BL fastCheck64<>(SB) - MOVW addrptr+0(FP), R1 + MOVW addr+0(FP), R1 load64loop: LDREXD (R1), R2 // loads R2 and R3 STREXD R2, (R1), R0 // stores R2 and R3 @@ -93,7 +95,7 @@ load64loop: TEXT ·armStoreUint64(SB),7,$0 BL fastCheck64<>(SB) - MOVW addrptr+0(FP), R1 + MOVW addr+0(FP), R1 MOVW vallo+4(FP), R2 MOVW valhi+8(FP), R3 store64loop: diff --git a/src/pkg/sync/atomic/asm_freebsd_arm.s b/src/pkg/sync/atomic/asm_freebsd_arm.s new file mode 100644 index 000000000..6590921b0 --- /dev/null +++ b/src/pkg/sync/atomic/asm_freebsd_arm.s @@ -0,0 +1,89 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// FreeBSD/ARM atomic operations. +// TODO(minux): this only supports ARMv6K or higher. + +TEXT ·CompareAndSwapInt32(SB),7,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUint32(SB),7,$0 + B ·armCompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUintptr(SB),7,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapPointer(SB),7,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·AddInt32(SB),7,$0 + B ·AddUint32(SB) + +TEXT ·AddUint32(SB),7,$0 + B ·armAddUint32(SB) + +TEXT ·AddUintptr(SB),7,$0 + B ·AddUint32(SB) + +TEXT ·CompareAndSwapInt64(SB),7,$0 + B ·CompareAndSwapUint64(SB) + +TEXT ·CompareAndSwapUint64(SB),7,$-4 + B ·armCompareAndSwapUint64(SB) + +TEXT ·AddInt64(SB),7,$0 + B ·addUint64(SB) + +TEXT ·AddUint64(SB),7,$0 + B ·addUint64(SB) + +TEXT ·LoadInt32(SB),7,$0 + B ·LoadUint32(SB) + +TEXT ·LoadUint32(SB),7,$0 + MOVW addr+0(FP), R1 +load32loop: + LDREX (R1), R2 // loads R2 + STREX R2, (R1), R0 // stores R2 + CMP $0, R0 + BNE load32loop + MOVW R2, val+4(FP) + RET + +TEXT ·LoadInt64(SB),7,$0 + B ·loadUint64(SB) + +TEXT ·LoadUint64(SB),7,$0 + B ·loadUint64(SB) + +TEXT ·LoadUintptr(SB),7,$0 + B ·LoadUint32(SB) + +TEXT ·LoadPointer(SB),7,$0 + B ·LoadUint32(SB) + +TEXT ·StoreInt32(SB),7,$0 + B ·StoreUint32(SB) + +TEXT ·StoreUint32(SB),7,$0 + MOVW addr+0(FP), R1 + MOVW val+4(FP), R2 +storeloop: + LDREX (R1), R4 // loads R4 + STREX R2, (R1), R0 // stores R2 + CMP $0, R0 + BNE storeloop + RET + +TEXT ·StoreInt64(SB),7,$0 + B ·storeUint64(SB) + +TEXT ·StoreUint64(SB),7,$0 + B ·storeUint64(SB) + +TEXT ·StoreUintptr(SB),7,$0 + B ·StoreUint32(SB) + +TEXT ·StorePointer(SB),7,$0 + B ·StoreUint32(SB) diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s index 25dc85804..098acf35b 100644 --- a/src/pkg/sync/atomic/asm_linux_arm.s +++ b/src/pkg/sync/atomic/asm_linux_arm.s @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !race + // Linux/ARM atomic operations. // Because there is so much variation in ARM devices, @@ -9,7 +11,7 @@ // implementation at address 0xffff0fc0. Caller sets: // R0 = old value // R1 = new value -// R2 = valptr +// R2 = addr // LR = return address // The function returns with CS true if the swap happened. // http://lxr.linux.no/linux+v2.6.37.2/arch/arm/kernel/entry-armv.S#L850 @@ -27,7 +29,7 @@ TEXT ·CompareAndSwapInt32(SB),7,$0 // Implement using kernel cas for portability. TEXT ·CompareAndSwapUint32(SB),7,$0 - MOVW valptr+0(FP), R2 + MOVW addr+0(FP), R2 MOVW old+4(FP), R0 casagain: MOVW new+8(FP), R1 @@ -39,7 +41,7 @@ casret: RET cascheck: // Kernel lies; double-check. - MOVW valptr+0(FP), R2 + MOVW addr+0(FP), R2 MOVW old+4(FP), R0 MOVW 0(R2), R3 CMP R0, R3 @@ -58,7 +60,7 @@ TEXT ·AddInt32(SB),7,$0 // Implement using kernel cas for portability. TEXT ·AddUint32(SB),7,$0 - MOVW valptr+0(FP), R2 + MOVW addr+0(FP), R2 MOVW delta+4(FP), R4 addloop1: MOVW 0(R2), R0 @@ -72,26 +74,75 @@ addloop1: TEXT ·AddUintptr(SB),7,$0 B ·AddUint32(SB) -// The kernel provides no 64-bit compare-and-swap, -// so use native ARM instructions, which will only work on -// ARM 11 and later devices. +TEXT cas64<>(SB),7,$0 + MOVW $0xffff0f60, PC // __kuser_cmpxchg64: Linux-3.1 and above + +TEXT kernelCAS64<>(SB),7,$0 + // int (*__kuser_cmpxchg64_t)(const int64_t *oldval, const int64_t *newval, volatile int64_t *ptr); + MOVW addr+0(FP), R2 // ptr + MOVW $4(FP), R0 // oldval + MOVW $12(FP), R1 // newval + BL cas64<>(SB) + MOVW.CS $1, R0 // C is set if the kernel has changed *ptr + MOVW.CC $0, R0 + MOVW R0, 20(FP) + RET + +TEXT generalCAS64<>(SB),7,$20 + // bool runtime·cas64(uint64 volatile *addr, uint64 *old, uint64 new) + MOVW addr+0(FP), R0 + MOVW R0, 4(R13) + MOVW $4(FP), R1 // oldval + MOVW R1, 8(R13) + MOVW newlo+12(FP), R2 + MOVW R2, 12(R13) + MOVW newhi+16(FP), R3 + MOVW R3, 16(R13) + BL runtime·cas64(SB) + MOVW R0, 20(FP) + RET + +GLOBL armCAS64(SB), $4 + +TEXT setupAndCallCAS64<>(SB),7,$-4 + MOVW $0xffff0ffc, R0 // __kuser_helper_version + MOVW (R0), R0 + // __kuser_cmpxchg64 only present if helper version >= 5 + CMP $5, R0 + MOVW.CS $kernelCAS64<>(SB), R1 + MOVW.CS R1, armCAS64(SB) + MOVW.CS R1, PC + MOVB runtime·armArch(SB), R0 + // LDREXD, STREXD only present on ARMv6K or higher + CMP $6, R0 // TODO(minux): how to differentiate ARMv6 with ARMv6K? + MOVW.CS $·armCompareAndSwapUint64(SB), R1 + MOVW.CS R1, armCAS64(SB) + MOVW.CS R1, PC + // we are out of luck, can only use runtime's emulated 64-bit cas + MOVW $generalCAS64<>(SB), R1 + MOVW R1, armCAS64(SB) + MOVW R1, PC + TEXT ·CompareAndSwapInt64(SB),7,$0 - B ·armCompareAndSwapUint64(SB) + B ·CompareAndSwapUint64(SB) -TEXT ·CompareAndSwapUint64(SB),7,$0 - B ·armCompareAndSwapUint64(SB) +TEXT ·CompareAndSwapUint64(SB),7,$-4 + MOVW armCAS64(SB), R0 + CMP $0, R0 + MOVW.NE R0, PC + B setupAndCallCAS64<>(SB) TEXT ·AddInt64(SB),7,$0 - B ·armAddUint64(SB) + B ·addUint64(SB) TEXT ·AddUint64(SB),7,$0 - B ·armAddUint64(SB) + B ·addUint64(SB) TEXT ·LoadInt32(SB),7,$0 B ·LoadUint32(SB) TEXT ·LoadUint32(SB),7,$0 - MOVW addrptr+0(FP), R2 + MOVW addr+0(FP), R2 loadloop1: MOVW 0(R2), R0 MOVW R0, R1 @@ -101,10 +152,10 @@ loadloop1: RET TEXT ·LoadInt64(SB),7,$0 - B ·armLoadUint64(SB) + B ·loadUint64(SB) TEXT ·LoadUint64(SB),7,$0 - B ·armLoadUint64(SB) + B ·loadUint64(SB) TEXT ·LoadUintptr(SB),7,$0 B ·LoadUint32(SB) @@ -116,7 +167,7 @@ TEXT ·StoreInt32(SB),7,$0 B ·StoreUint32(SB) TEXT ·StoreUint32(SB),7,$0 - MOVW addrptr+0(FP), R2 + MOVW addr+0(FP), R2 MOVW val+4(FP), R1 storeloop1: MOVW 0(R2), R0 @@ -125,10 +176,10 @@ storeloop1: RET TEXT ·StoreInt64(SB),7,$0 - B ·armStoreUint64(SB) + B ·storeUint64(SB) TEXT ·StoreUint64(SB),7,$0 - B ·armStoreUint64(SB) + B ·storeUint64(SB) TEXT ·StoreUintptr(SB),7,$0 B ·StoreUint32(SB) diff --git a/src/pkg/sync/atomic/asm_netbsd_arm.s b/src/pkg/sync/atomic/asm_netbsd_arm.s new file mode 100644 index 000000000..677f3daaa --- /dev/null +++ b/src/pkg/sync/atomic/asm_netbsd_arm.s @@ -0,0 +1,89 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// NetBSD/ARM atomic operations. +// TODO(minux): this only supports ARMv6K or higher. + +TEXT ·CompareAndSwapInt32(SB),7,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUint32(SB),7,$0 + B ·armCompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUintptr(SB),7,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapPointer(SB),7,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·AddInt32(SB),7,$0 + B ·AddUint32(SB) + +TEXT ·AddUint32(SB),7,$0 + B ·armAddUint32(SB) + +TEXT ·AddUintptr(SB),7,$0 + B ·AddUint32(SB) + +TEXT ·CompareAndSwapInt64(SB),7,$0 + B ·CompareAndSwapUint64(SB) + +TEXT ·CompareAndSwapUint64(SB),7,$-4 + B ·armCompareAndSwapUint64(SB) + +TEXT ·AddInt64(SB),7,$0 + B ·addUint64(SB) + +TEXT ·AddUint64(SB),7,$0 + B ·addUint64(SB) + +TEXT ·LoadInt32(SB),7,$0 + B ·LoadUint32(SB) + +TEXT ·LoadUint32(SB),7,$0 + MOVW addr+0(FP), R1 +load32loop: + LDREX (R1), R2 // loads R2 + STREX R2, (R1), R0 // stores R2 + CMP $0, R0 + BNE load32loop + MOVW R2, val+4(FP) + RET + +TEXT ·LoadInt64(SB),7,$0 + B ·loadUint64(SB) + +TEXT ·LoadUint64(SB),7,$0 + B ·loadUint64(SB) + +TEXT ·LoadUintptr(SB),7,$0 + B ·LoadUint32(SB) + +TEXT ·LoadPointer(SB),7,$0 + B ·LoadUint32(SB) + +TEXT ·StoreInt32(SB),7,$0 + B ·StoreUint32(SB) + +TEXT ·StoreUint32(SB),7,$0 + MOVW addr+0(FP), R1 + MOVW val+4(FP), R2 +storeloop: + LDREX (R1), R4 // loads R4 + STREX R2, (R1), R0 // stores R2 + CMP $0, R0 + BNE storeloop + RET + +TEXT ·StoreInt64(SB),7,$0 + B ·storeUint64(SB) + +TEXT ·StoreUint64(SB),7,$0 + B ·storeUint64(SB) + +TEXT ·StoreUintptr(SB),7,$0 + B ·StoreUint32(SB) + +TEXT ·StorePointer(SB),7,$0 + B ·StoreUint32(SB) diff --git a/src/pkg/sync/atomic/atomic_test.go b/src/pkg/sync/atomic/atomic_test.go index f60d997ce..3e105561c 100644 --- a/src/pkg/sync/atomic/atomic_test.go +++ b/src/pkg/sync/atomic/atomic_test.go @@ -82,8 +82,7 @@ func TestAddUint32(t *testing.T) { func TestAddInt64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } var x struct { before int64 @@ -107,8 +106,7 @@ func TestAddInt64(t *testing.T) { func TestAddUint64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } var x struct { before uint64 @@ -213,8 +211,7 @@ func TestCompareAndSwapUint32(t *testing.T) { func TestCompareAndSwapInt64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } var x struct { before int64 @@ -246,8 +243,7 @@ func TestCompareAndSwapInt64(t *testing.T) { func TestCompareAndSwapUint64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } var x struct { before uint64 @@ -381,8 +377,7 @@ func TestLoadUint32(t *testing.T) { func TestLoadInt64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } var x struct { before int64 @@ -405,8 +400,7 @@ func TestLoadInt64(t *testing.T) { func TestLoadUint64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } var x struct { before uint64 @@ -515,8 +509,7 @@ func TestStoreUint32(t *testing.T) { func TestStoreInt64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } var x struct { before int64 @@ -540,8 +533,7 @@ func TestStoreInt64(t *testing.T) { func TestStoreUint64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } var x struct { before uint64 @@ -640,73 +632,73 @@ func init() { } } -func hammerAddInt32(uval *uint32, count int) { - val := (*int32)(unsafe.Pointer(uval)) +func hammerAddInt32(uaddr *uint32, count int) { + addr := (*int32)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { - AddInt32(val, 1) + AddInt32(addr, 1) } } -func hammerAddUint32(val *uint32, count int) { +func hammerAddUint32(addr *uint32, count int) { for i := 0; i < count; i++ { - AddUint32(val, 1) + AddUint32(addr, 1) } } -func hammerAddUintptr32(uval *uint32, count int) { +func hammerAddUintptr32(uaddr *uint32, count int) { // only safe when uintptr is 32-bit. // not called on 64-bit systems. - val := (*uintptr)(unsafe.Pointer(uval)) + addr := (*uintptr)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { - AddUintptr(val, 1) + AddUintptr(addr, 1) } } -func hammerCompareAndSwapInt32(uval *uint32, count int) { - val := (*int32)(unsafe.Pointer(uval)) +func hammerCompareAndSwapInt32(uaddr *uint32, count int) { + addr := (*int32)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { for { - v := *val - if CompareAndSwapInt32(val, v, v+1) { + v := *addr + if CompareAndSwapInt32(addr, v, v+1) { break } } } } -func hammerCompareAndSwapUint32(val *uint32, count int) { +func hammerCompareAndSwapUint32(addr *uint32, count int) { for i := 0; i < count; i++ { for { - v := *val - if CompareAndSwapUint32(val, v, v+1) { + v := *addr + if CompareAndSwapUint32(addr, v, v+1) { break } } } } -func hammerCompareAndSwapUintptr32(uval *uint32, count int) { +func hammerCompareAndSwapUintptr32(uaddr *uint32, count int) { // only safe when uintptr is 32-bit. // not called on 64-bit systems. - val := (*uintptr)(unsafe.Pointer(uval)) + addr := (*uintptr)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { for { - v := *val - if CompareAndSwapUintptr(val, v, v+1) { + v := *addr + if CompareAndSwapUintptr(addr, v, v+1) { break } } } } -func hammerCompareAndSwapPointer32(uval *uint32, count int) { +func hammerCompareAndSwapPointer32(uaddr *uint32, count int) { // only safe when uintptr is 32-bit. // not called on 64-bit systems. - val := (*unsafe.Pointer)(unsafe.Pointer(uval)) + addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { for { - v := *val - if CompareAndSwapPointer(val, v, unsafe.Pointer(uintptr(v)+1)) { + v := *addr + if CompareAndSwapPointer(addr, v, unsafe.Pointer(uintptr(v)+1)) { break } } @@ -765,73 +757,73 @@ func init() { } } -func hammerAddInt64(uval *uint64, count int) { - val := (*int64)(unsafe.Pointer(uval)) +func hammerAddInt64(uaddr *uint64, count int) { + addr := (*int64)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { - AddInt64(val, 1) + AddInt64(addr, 1) } } -func hammerAddUint64(val *uint64, count int) { +func hammerAddUint64(addr *uint64, count int) { for i := 0; i < count; i++ { - AddUint64(val, 1) + AddUint64(addr, 1) } } -func hammerAddUintptr64(uval *uint64, count int) { +func hammerAddUintptr64(uaddr *uint64, count int) { // only safe when uintptr is 64-bit. // not called on 32-bit systems. - val := (*uintptr)(unsafe.Pointer(uval)) + addr := (*uintptr)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { - AddUintptr(val, 1) + AddUintptr(addr, 1) } } -func hammerCompareAndSwapInt64(uval *uint64, count int) { - val := (*int64)(unsafe.Pointer(uval)) +func hammerCompareAndSwapInt64(uaddr *uint64, count int) { + addr := (*int64)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { for { - v := *val - if CompareAndSwapInt64(val, v, v+1) { + v := *addr + if CompareAndSwapInt64(addr, v, v+1) { break } } } } -func hammerCompareAndSwapUint64(val *uint64, count int) { +func hammerCompareAndSwapUint64(addr *uint64, count int) { for i := 0; i < count; i++ { for { - v := *val - if CompareAndSwapUint64(val, v, v+1) { + v := *addr + if CompareAndSwapUint64(addr, v, v+1) { break } } } } -func hammerCompareAndSwapUintptr64(uval *uint64, count int) { +func hammerCompareAndSwapUintptr64(uaddr *uint64, count int) { // only safe when uintptr is 64-bit. // not called on 32-bit systems. - val := (*uintptr)(unsafe.Pointer(uval)) + addr := (*uintptr)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { for { - v := *val - if CompareAndSwapUintptr(val, v, v+1) { + v := *addr + if CompareAndSwapUintptr(addr, v, v+1) { break } } } } -func hammerCompareAndSwapPointer64(uval *uint64, count int) { +func hammerCompareAndSwapPointer64(uaddr *uint64, count int) { // only safe when uintptr is 64-bit. // not called on 32-bit systems. - val := (*unsafe.Pointer)(unsafe.Pointer(uval)) + addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { for { - v := *val - if CompareAndSwapPointer(val, v, unsafe.Pointer(uintptr(v)+1)) { + v := *addr + if CompareAndSwapPointer(addr, v, unsafe.Pointer(uintptr(v)+1)) { break } } @@ -840,8 +832,7 @@ func hammerCompareAndSwapPointer64(uval *uint64, count int) { func TestHammer64(t *testing.T) { if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } const p = 4 n := 100000 @@ -871,9 +862,9 @@ func TestHammer64(t *testing.T) { } } -func hammerStoreLoadInt32(t *testing.T, valp unsafe.Pointer) { - val := (*int32)(valp) - v := LoadInt32(val) +func hammerStoreLoadInt32(t *testing.T, paddr unsafe.Pointer) { + addr := (*int32)(paddr) + v := LoadInt32(addr) vlo := v & ((1 << 16) - 1) vhi := v >> 16 if vlo != vhi { @@ -883,12 +874,12 @@ func hammerStoreLoadInt32(t *testing.T, valp unsafe.Pointer) { if vlo == 1e4 { new = 0 } - StoreInt32(val, new) + StoreInt32(addr, new) } -func hammerStoreLoadUint32(t *testing.T, valp unsafe.Pointer) { - val := (*uint32)(valp) - v := LoadUint32(val) +func hammerStoreLoadUint32(t *testing.T, paddr unsafe.Pointer) { + addr := (*uint32)(paddr) + v := LoadUint32(addr) vlo := v & ((1 << 16) - 1) vhi := v >> 16 if vlo != vhi { @@ -898,38 +889,38 @@ func hammerStoreLoadUint32(t *testing.T, valp unsafe.Pointer) { if vlo == 1e4 { new = 0 } - StoreUint32(val, new) + StoreUint32(addr, new) } -func hammerStoreLoadInt64(t *testing.T, valp unsafe.Pointer) { - val := (*int64)(valp) - v := LoadInt64(val) +func hammerStoreLoadInt64(t *testing.T, paddr unsafe.Pointer) { + addr := (*int64)(paddr) + v := LoadInt64(addr) vlo := v & ((1 << 32) - 1) vhi := v >> 32 if vlo != vhi { t.Fatalf("Int64: %#x != %#x", vlo, vhi) } new := v + 1 + 1<<32 - StoreInt64(val, new) + StoreInt64(addr, new) } -func hammerStoreLoadUint64(t *testing.T, valp unsafe.Pointer) { - val := (*uint64)(valp) - v := LoadUint64(val) +func hammerStoreLoadUint64(t *testing.T, paddr unsafe.Pointer) { + addr := (*uint64)(paddr) + v := LoadUint64(addr) vlo := v & ((1 << 32) - 1) vhi := v >> 32 if vlo != vhi { t.Fatalf("Uint64: %#x != %#x", vlo, vhi) } new := v + 1 + 1<<32 - StoreUint64(val, new) + StoreUint64(addr, new) } -func hammerStoreLoadUintptr(t *testing.T, valp unsafe.Pointer) { - val := (*uintptr)(valp) +func hammerStoreLoadUintptr(t *testing.T, paddr unsafe.Pointer) { + addr := (*uintptr)(paddr) var test64 uint64 = 1 << 50 arch32 := uintptr(test64) == 0 - v := LoadUintptr(val) + v := LoadUintptr(addr) new := v if arch32 { vlo := v & ((1 << 16) - 1) @@ -950,14 +941,14 @@ func hammerStoreLoadUintptr(t *testing.T, valp unsafe.Pointer) { inc := uint64(1 + 1<<32) new = v + uintptr(inc) } - StoreUintptr(val, new) + StoreUintptr(addr, new) } -func hammerStoreLoadPointer(t *testing.T, valp unsafe.Pointer) { - val := (*unsafe.Pointer)(valp) +func hammerStoreLoadPointer(t *testing.T, paddr unsafe.Pointer) { + addr := (*unsafe.Pointer)(paddr) var test64 uint64 = 1 << 50 arch32 := uintptr(test64) == 0 - v := uintptr(LoadPointer(val)) + v := uintptr(LoadPointer(addr)) new := v if arch32 { vlo := v & ((1 << 16) - 1) @@ -978,7 +969,7 @@ func hammerStoreLoadPointer(t *testing.T, valp unsafe.Pointer) { inc := uint64(1 + 1<<32) new = v + uintptr(inc) } - StorePointer(val, unsafe.Pointer(new)) + StorePointer(addr, unsafe.Pointer(new)) } func TestHammerStoreLoad(t *testing.T) { @@ -1013,8 +1004,7 @@ func TestHammerStoreLoad(t *testing.T) { func TestStoreLoadSeqCst32(t *testing.T) { if runtime.NumCPU() == 1 { - t.Logf("Skipping test on %v processor machine", runtime.NumCPU()) - return + t.Skipf("Skipping test on %v processor machine", runtime.NumCPU()) } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) N := int32(1e3) @@ -1054,12 +1044,10 @@ func TestStoreLoadSeqCst32(t *testing.T) { func TestStoreLoadSeqCst64(t *testing.T) { if runtime.NumCPU() == 1 { - t.Logf("Skipping test on %v processor machine", runtime.NumCPU()) - return + t.Skipf("Skipping test on %v processor machine", runtime.NumCPU()) } if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) N := int64(1e3) @@ -1099,8 +1087,7 @@ func TestStoreLoadSeqCst64(t *testing.T) { func TestStoreLoadRelAcq32(t *testing.T) { if runtime.NumCPU() == 1 { - t.Logf("Skipping test on %v processor machine", runtime.NumCPU()) - return + t.Skipf("Skipping test on %v processor machine", runtime.NumCPU()) } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) N := int32(1e3) @@ -1132,7 +1119,7 @@ func TestStoreLoadRelAcq32(t *testing.T) { d1 := X.data1 d2 := X.data2 if d1 != i || d2 != float32(i) { - t.Fatalf("incorrect data: %d/%d (%d)", d1, d2, i) + t.Fatalf("incorrect data: %d/%g (%d)", d1, d2, i) } } } @@ -1145,12 +1132,10 @@ func TestStoreLoadRelAcq32(t *testing.T) { func TestStoreLoadRelAcq64(t *testing.T) { if runtime.NumCPU() == 1 { - t.Logf("Skipping test on %v processor machine", runtime.NumCPU()) - return + t.Skipf("Skipping test on %v processor machine", runtime.NumCPU()) } if test64err != nil { - t.Logf("Skipping 64-bit tests: %v", test64err) - return + t.Skipf("Skipping 64-bit tests: %v", test64err) } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) N := int64(1e3) @@ -1182,7 +1167,7 @@ func TestStoreLoadRelAcq64(t *testing.T) { d1 := X.data1 d2 := X.data2 if d1 != i || d2 != float64(i) { - t.Fatalf("incorrect data: %d/%d (%d)", d1, d2, i) + t.Fatalf("incorrect data: %d/%g (%d)", d1, d2, i) } } } diff --git a/src/pkg/sync/atomic/doc.go b/src/pkg/sync/atomic/doc.go index ecb4808ce..27a12c984 100644 --- a/src/pkg/sync/atomic/doc.go +++ b/src/pkg/sync/atomic/doc.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !race + // Package atomic provides low-level atomic memory primitives // useful for implementing synchronization algorithms. // @@ -14,54 +16,67 @@ // The compare-and-swap operation, implemented by the CompareAndSwapT // functions, is the atomic equivalent of: // -// if *val == old { -// *val = new +// if *addr == old { +// *addr = new // return true // } // return false // +// The add operation, implemented by the AddT functions, is the atomic +// equivalent of: +// +// *addr += delta +// return *addr +// +// The load and store operations, implemented by the LoadT and StoreT +// functions, are the atomic equivalents of "return *addr" and +// "*addr = val". +// package atomic import ( "unsafe" ) -// BUG(rsc): On ARM, the 64-bit functions use instructions unavailable before ARM 11. +// BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX. // -// On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX. +// On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit +// alignment of 64-bit words accessed atomically. The first word in a global +// variable or in an allocated struct or slice can be relied upon to be +// 64-bit aligned. // CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value. -func CompareAndSwapInt32(val *int32, old, new int32) (swapped bool) +func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool) // CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value. -func CompareAndSwapInt64(val *int64, old, new int64) (swapped bool) +func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool) // CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value. -func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) +func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool) // CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value. -func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) +func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool) // CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value. -func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) +func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool) // CompareAndSwapPointer executes the compare-and-swap operation for a unsafe.Pointer value. -func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) +func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) -// AddInt32 atomically adds delta to *val and returns the new value. -func AddInt32(val *int32, delta int32) (new int32) +// AddInt32 atomically adds delta to *addr and returns the new value. +func AddInt32(addr *int32, delta int32) (new int32) -// AddUint32 atomically adds delta to *val and returns the new value. -func AddUint32(val *uint32, delta uint32) (new uint32) +// AddUint32 atomically adds delta to *addr and returns the new value. +func AddUint32(addr *uint32, delta uint32) (new uint32) -// AddInt64 atomically adds delta to *val and returns the new value. -func AddInt64(val *int64, delta int64) (new int64) +// AddInt64 atomically adds delta to *addr and returns the new value. +func AddInt64(addr *int64, delta int64) (new int64) -// AddUint64 atomically adds delta to *val and returns the new value. -func AddUint64(val *uint64, delta uint64) (new uint64) +// AddUint64 atomically adds delta to *addr and returns the new value. +func AddUint64(addr *uint64, delta uint64) (new uint64) -// AddUintptr atomically adds delta to *val and returns the new value. -func AddUintptr(val *uintptr, delta uintptr) (new uintptr) +// AddUintptr atomically adds delta to *addr and returns the new value. +func AddUintptr(addr *uintptr, delta uintptr) (new uintptr) // LoadInt32 atomically loads *addr. func LoadInt32(addr *int32) (val int32) diff --git a/src/pkg/sync/atomic/race.go b/src/pkg/sync/atomic/race.go new file mode 100644 index 000000000..242bbf298 --- /dev/null +++ b/src/pkg/sync/atomic/race.go @@ -0,0 +1,213 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race + +package atomic + +import ( + "runtime" + "unsafe" +) + +// We use runtime.RaceRead() inside of atomic operations to catch races +// between atomic and non-atomic operations. It will also catch races +// between Mutex.Lock() and mutex overwrite (mu = Mutex{}). Since we use +// only RaceRead() we won't catch races with non-atomic loads. +// Otherwise (if we use RaceWrite()) we will report races +// between atomic operations (false positives). + +var mtx uint32 = 1 // same for all + +func CompareAndSwapInt32(val *int32, old, new int32) bool { + return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new)) +} + +func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) { + swapped = false + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(val)) + runtime.RaceAcquire(unsafe.Pointer(val)) + if *val == old { + *val = new + swapped = true + runtime.RaceReleaseMerge(unsafe.Pointer(val)) + } + runtime.RaceSemrelease(&mtx) + return +} + +func CompareAndSwapInt64(val *int64, old, new int64) bool { + return CompareAndSwapUint64((*uint64)(unsafe.Pointer(val)), uint64(old), uint64(new)) +} + +func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) { + swapped = false + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(val)) + runtime.RaceAcquire(unsafe.Pointer(val)) + if *val == old { + *val = new + swapped = true + runtime.RaceReleaseMerge(unsafe.Pointer(val)) + } + runtime.RaceSemrelease(&mtx) + return +} + +func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) { + swapped = false + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(val)) + runtime.RaceAcquire(unsafe.Pointer(val)) + if *val == old { + *val = new + swapped = true + runtime.RaceReleaseMerge(unsafe.Pointer(val)) + } + runtime.RaceSemrelease(&mtx) + return +} + +func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) { + swapped = false + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(val)) + runtime.RaceAcquire(unsafe.Pointer(val)) + if *val == old { + *val = new + swapped = true + runtime.RaceReleaseMerge(unsafe.Pointer(val)) + } + runtime.RaceSemrelease(&mtx) + return +} + +func AddInt32(val *int32, delta int32) int32 { + return int32(AddUint32((*uint32)(unsafe.Pointer(val)), uint32(delta))) +} + +func AddUint32(val *uint32, delta uint32) (new uint32) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(val)) + runtime.RaceAcquire(unsafe.Pointer(val)) + *val = *val + delta + new = *val + runtime.RaceReleaseMerge(unsafe.Pointer(val)) + runtime.RaceSemrelease(&mtx) + + return +} + +func AddInt64(val *int64, delta int64) int64 { + return int64(AddUint64((*uint64)(unsafe.Pointer(val)), uint64(delta))) +} + +func AddUint64(val *uint64, delta uint64) (new uint64) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(val)) + runtime.RaceAcquire(unsafe.Pointer(val)) + *val = *val + delta + new = *val + runtime.RaceReleaseMerge(unsafe.Pointer(val)) + runtime.RaceSemrelease(&mtx) + + return +} + +func AddUintptr(val *uintptr, delta uintptr) (new uintptr) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(val)) + runtime.RaceAcquire(unsafe.Pointer(val)) + *val = *val + delta + new = *val + runtime.RaceReleaseMerge(unsafe.Pointer(val)) + runtime.RaceSemrelease(&mtx) + + return +} + +func LoadInt32(addr *int32) int32 { + return int32(LoadUint32((*uint32)(unsafe.Pointer(addr)))) +} + +func LoadUint32(addr *uint32) (val uint32) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + val = *addr + runtime.RaceSemrelease(&mtx) + return +} + +func LoadInt64(addr *int64) int64 { + return int64(LoadUint64((*uint64)(unsafe.Pointer(addr)))) +} + +func LoadUint64(addr *uint64) (val uint64) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + val = *addr + runtime.RaceSemrelease(&mtx) + return +} + +func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + val = *addr + runtime.RaceSemrelease(&mtx) + return +} + +func LoadUintptr(addr *uintptr) (val uintptr) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + val = *addr + runtime.RaceSemrelease(&mtx) + return +} + +func StoreInt32(addr *int32, val int32) { + StoreUint32((*uint32)(unsafe.Pointer(addr)), uint32(val)) +} + +func StoreUint32(addr *uint32, val uint32) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + *addr = val + runtime.RaceRelease(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) +} + +func StoreInt64(addr *int64, val int64) { + StoreUint64((*uint64)(unsafe.Pointer(addr)), uint64(val)) +} + +func StoreUint64(addr *uint64, val uint64) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + *addr = val + runtime.RaceRelease(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) +} + +func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + *addr = val + runtime.RaceRelease(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) +} + +func StoreUintptr(addr *uintptr, val uintptr) { + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + *addr = val + runtime.RaceRelease(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) +} diff --git a/src/pkg/sync/cond.go b/src/pkg/sync/cond.go index 1fc3deaf1..491b98569 100644 --- a/src/pkg/sync/cond.go +++ b/src/pkg/sync/cond.go @@ -56,6 +56,9 @@ func NewCond(l Locker) *Cond { // c.L.Unlock() // func (c *Cond) Wait() { + if raceenabled { + raceDisable() + } c.m.Lock() if c.newSema == nil { c.newSema = new(uint32) @@ -63,6 +66,9 @@ func (c *Cond) Wait() { s := c.newSema c.newWaiters++ c.m.Unlock() + if raceenabled { + raceEnable() + } c.L.Unlock() runtime_Semacquire(s) c.L.Lock() @@ -73,6 +79,9 @@ func (c *Cond) Wait() { // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Signal() { + if raceenabled { + raceDisable() + } c.m.Lock() if c.oldWaiters == 0 && c.newWaiters > 0 { // Retire old generation; rename new to old. @@ -86,6 +95,9 @@ func (c *Cond) Signal() { runtime_Semrelease(c.oldSema) } c.m.Unlock() + if raceenabled { + raceEnable() + } } // Broadcast wakes all goroutines waiting on c. @@ -93,6 +105,9 @@ func (c *Cond) Signal() { // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Broadcast() { + if raceenabled { + raceDisable() + } c.m.Lock() // Wake both generations. if c.oldWaiters > 0 { @@ -109,4 +124,7 @@ func (c *Cond) Broadcast() { c.newSema = nil } c.m.Unlock() + if raceenabled { + raceEnable() + } } diff --git a/src/pkg/sync/example_test.go b/src/pkg/sync/example_test.go index 156492400..031c87f03 100644 --- a/src/pkg/sync/example_test.go +++ b/src/pkg/sync/example_test.go @@ -24,10 +24,10 @@ func ExampleWaitGroup() { wg.Add(1) // Launch a goroutine to fetch the URL. go func(url string) { + // Decrement the counter when the goroutine completes. + defer wg.Done() // Fetch the URL. http.Get(url) - // Decrement the counter. - wg.Done() }(url) } // Wait for all HTTP fetches to complete. @@ -37,7 +37,7 @@ func ExampleWaitGroup() { func ExampleOnce() { var once sync.Once onceBody := func() { - fmt.Printf("Only once\n") + fmt.Println("Only once") } done := make(chan bool) for i := 0; i < 10; i++ { diff --git a/src/pkg/sync/mutex.go b/src/pkg/sync/mutex.go index 9494cc3f8..b4629ebca 100644 --- a/src/pkg/sync/mutex.go +++ b/src/pkg/sync/mutex.go @@ -10,7 +10,10 @@ // Values containing the types defined in this package should not be copied. package sync -import "sync/atomic" +import ( + "sync/atomic" + "unsafe" +) // A Mutex is a mutual exclusion lock. // Mutexes can be created as part of other structures; @@ -38,6 +41,9 @@ const ( func (m *Mutex) Lock() { // Fast path: grab unlocked mutex. if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { + if raceenabled { + raceAcquire(unsafe.Pointer(m)) + } return } @@ -61,6 +67,10 @@ func (m *Mutex) Lock() { awoke = true } } + + if raceenabled { + raceAcquire(unsafe.Pointer(m)) + } } // Unlock unlocks m. @@ -70,6 +80,10 @@ func (m *Mutex) Lock() { // It is allowed for one goroutine to lock a Mutex and then // arrange for another goroutine to unlock it. func (m *Mutex) Unlock() { + if raceenabled { + raceRelease(unsafe.Pointer(m)) + } + // Fast path: drop lock bit. new := atomic.AddInt32(&m.state, -mutexLocked) if (new+mutexLocked)&mutexLocked == 0 { diff --git a/src/pkg/sync/once.go b/src/pkg/sync/once.go index 04b714a3e..1699e86a9 100644 --- a/src/pkg/sync/once.go +++ b/src/pkg/sync/once.go @@ -38,6 +38,6 @@ func (o *Once) Do(f func()) { defer o.m.Unlock() if o.done == 0 { f() - atomic.CompareAndSwapUint32(&o.done, 0, 1) + atomic.StoreUint32(&o.done, 1) } } diff --git a/src/pkg/sync/once_test.go b/src/pkg/sync/once_test.go index 37075af17..183069a1a 100644 --- a/src/pkg/sync/once_test.go +++ b/src/pkg/sync/once_test.go @@ -17,8 +17,11 @@ func (o *one) Increment() { *o++ } -func run(once *Once, o *one, c chan bool) { +func run(t *testing.T, once *Once, o *one, c chan bool) { once.Do(func() { o.Increment() }) + if v := *o; v != 1 { + t.Errorf("once failed inside run: %d is not 1", v) + } c <- true } @@ -28,14 +31,34 @@ func TestOnce(t *testing.T) { c := make(chan bool) const N = 10 for i := 0; i < N; i++ { - go run(once, o, c) + go run(t, once, o, c) } for i := 0; i < N; i++ { <-c } if *o != 1 { - t.Errorf("once failed: %d is not 1", *o) + t.Errorf("once failed outside run: %d is not 1", *o) + } +} + +func TestOncePanic(t *testing.T) { + once := new(Once) + for i := 0; i < 2; i++ { + func() { + defer func() { + if recover() == nil { + t.Fatalf("Once.Do() has not panic'ed") + } + }() + once.Do(func() { + panic("failed") + }) + }() } + once.Do(func() {}) + once.Do(func() { + t.Fatalf("Once called twice") + }) } func BenchmarkOnce(b *testing.B) { diff --git a/src/pkg/sync/race.go b/src/pkg/sync/race.go new file mode 100644 index 000000000..d9431af6f --- /dev/null +++ b/src/pkg/sync/race.go @@ -0,0 +1,34 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race + +package sync + +import ( + "runtime" + "unsafe" +) + +const raceenabled = true + +func raceAcquire(addr unsafe.Pointer) { + runtime.RaceAcquire(addr) +} + +func raceRelease(addr unsafe.Pointer) { + runtime.RaceRelease(addr) +} + +func raceReleaseMerge(addr unsafe.Pointer) { + runtime.RaceReleaseMerge(addr) +} + +func raceDisable() { + runtime.RaceDisable() +} + +func raceEnable() { + runtime.RaceEnable() +} diff --git a/src/pkg/sync/race0.go b/src/pkg/sync/race0.go new file mode 100644 index 000000000..bef14f974 --- /dev/null +++ b/src/pkg/sync/race0.go @@ -0,0 +1,28 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !race + +package sync + +import ( + "unsafe" +) + +const raceenabled = false + +func raceAcquire(addr unsafe.Pointer) { +} + +func raceRelease(addr unsafe.Pointer) { +} + +func raceReleaseMerge(addr unsafe.Pointer) { +} + +func raceDisable() { +} + +func raceEnable() { +} diff --git a/src/pkg/sync/rwmutex.go b/src/pkg/sync/rwmutex.go index 782a9c319..b494c6435 100644 --- a/src/pkg/sync/rwmutex.go +++ b/src/pkg/sync/rwmutex.go @@ -4,7 +4,10 @@ package sync -import "sync/atomic" +import ( + "sync/atomic" + "unsafe" +) // An RWMutex is a reader/writer mutual exclusion lock. // The lock can be held by an arbitrary number of readers @@ -24,10 +27,17 @@ const rwmutexMaxReaders = 1 << 30 // RLock locks rw for reading. func (rw *RWMutex) RLock() { + if raceenabled { + raceDisable() + } if atomic.AddInt32(&rw.readerCount, 1) < 0 { // A writer is pending, wait for it. runtime_Semacquire(&rw.readerSem) } + if raceenabled { + raceEnable() + raceAcquire(unsafe.Pointer(&rw.readerSem)) + } } // RUnlock undoes a single RLock call; @@ -35,6 +45,10 @@ func (rw *RWMutex) RLock() { // It is a run-time error if rw is not locked for reading // on entry to RUnlock. func (rw *RWMutex) RUnlock() { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&rw.writerSem)) + raceDisable() + } if atomic.AddInt32(&rw.readerCount, -1) < 0 { // A writer is pending. if atomic.AddInt32(&rw.readerWait, -1) == 0 { @@ -42,6 +56,9 @@ func (rw *RWMutex) RUnlock() { runtime_Semrelease(&rw.writerSem) } } + if raceenabled { + raceEnable() + } } // Lock locks rw for writing. @@ -51,6 +68,9 @@ func (rw *RWMutex) RUnlock() { // a blocked Lock call excludes new readers from acquiring // the lock. func (rw *RWMutex) Lock() { + if raceenabled { + raceDisable() + } // First, resolve competition with other writers. rw.w.Lock() // Announce to readers there is a pending writer. @@ -59,6 +79,11 @@ func (rw *RWMutex) Lock() { if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { runtime_Semacquire(&rw.writerSem) } + if raceenabled { + raceEnable() + raceAcquire(unsafe.Pointer(&rw.readerSem)) + raceAcquire(unsafe.Pointer(&rw.writerSem)) + } } // Unlock unlocks rw for writing. It is a run-time error if rw is @@ -68,6 +93,12 @@ func (rw *RWMutex) Lock() { // goroutine. One goroutine may RLock (Lock) an RWMutex and then // arrange for another goroutine to RUnlock (Unlock) it. func (rw *RWMutex) Unlock() { + if raceenabled { + raceRelease(unsafe.Pointer(&rw.readerSem)) + raceRelease(unsafe.Pointer(&rw.writerSem)) + raceDisable() + } + // Announce to readers there is no active writer. r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) // Unblock blocked readers, if any. @@ -76,6 +107,9 @@ func (rw *RWMutex) Unlock() { } // Allow other writers to proceed. rw.w.Unlock() + if raceenabled { + raceEnable() + } } // RLocker returns a Locker interface that implements diff --git a/src/pkg/sync/waitgroup.go b/src/pkg/sync/waitgroup.go index 0165b1ffb..1277f1c6d 100644 --- a/src/pkg/sync/waitgroup.go +++ b/src/pkg/sync/waitgroup.go @@ -4,7 +4,10 @@ package sync -import "sync/atomic" +import ( + "sync/atomic" + "unsafe" +) // A WaitGroup waits for a collection of goroutines to finish. // The main goroutine calls Add to set the number of @@ -31,11 +34,22 @@ type WaitGroup struct { // G3: Wait() // G1 still hasn't run, G3 finds sema == 1, unblocked! Bug. // Add adds delta, which may be negative, to the WaitGroup counter. -// If the counter becomes zero, all goroutines blocked on Wait() are released. +// If the counter becomes zero, all goroutines blocked on Wait are released. +// If the counter goes negative, Add panics. +// +// Note that calls with positive delta must happen before the call to Wait, +// or else Wait may wait for too small a group. Typically this means the calls +// to Add should execute before the statement creating the goroutine or +// other event to be waited for. See the WaitGroup example. func (wg *WaitGroup) Add(delta int) { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(wg)) + raceDisable() + defer raceEnable() + } v := atomic.AddInt32(&wg.counter, int32(delta)) if v < 0 { - panic("sync: negative WaitGroup count") + panic("sync: negative WaitGroup counter") } if v > 0 || atomic.LoadInt32(&wg.waiters) == 0 { return @@ -56,7 +70,14 @@ func (wg *WaitGroup) Done() { // Wait blocks until the WaitGroup counter is zero. func (wg *WaitGroup) Wait() { + if raceenabled { + raceDisable() + } if atomic.LoadInt32(&wg.counter) == 0 { + if raceenabled { + raceEnable() + raceAcquire(unsafe.Pointer(wg)) + } return } wg.m.Lock() @@ -67,7 +88,15 @@ func (wg *WaitGroup) Wait() { // to avoid missing an Add. if atomic.LoadInt32(&wg.counter) == 0 { atomic.AddInt32(&wg.waiters, -1) + if raceenabled { + raceEnable() + raceAcquire(unsafe.Pointer(wg)) + raceDisable() + } wg.m.Unlock() + if raceenabled { + raceEnable() + } return } if wg.sema == nil { @@ -76,4 +105,8 @@ func (wg *WaitGroup) Wait() { s := wg.sema wg.m.Unlock() runtime_Semacquire(s) + if raceenabled { + raceEnable() + raceAcquire(unsafe.Pointer(wg)) + } } diff --git a/src/pkg/sync/waitgroup_test.go b/src/pkg/sync/waitgroup_test.go index 34430fc21..84c4cfc37 100644 --- a/src/pkg/sync/waitgroup_test.go +++ b/src/pkg/sync/waitgroup_test.go @@ -50,7 +50,7 @@ func TestWaitGroup(t *testing.T) { func TestWaitGroupMisuse(t *testing.T) { defer func() { err := recover() - if err != "sync: negative WaitGroup count" { + if err != "sync: negative WaitGroup counter" { t.Fatalf("Unexpected panic: %#v", err) } }() diff --git a/src/pkg/syscall/asm_freebsd_amd64.s b/src/pkg/syscall/asm_freebsd_amd64.s index 022db697a..fbf917804 100644 --- a/src/pkg/syscall/asm_freebsd_amd64.s +++ b/src/pkg/syscall/asm_freebsd_amd64.s @@ -8,6 +8,7 @@ // func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64); // func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64); +// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64) // Trap # in AX, args in DI SI DX, return in AX DX TEXT ·Syscall(SB),7,$0 @@ -56,6 +57,39 @@ ok6: CALL runtime·exitsyscall(SB) RET +TEXT ·Syscall9(SB),7,$0 + CALL runtime·entersyscall(SB) + MOVQ 8(SP), AX + MOVQ 16(SP), DI + MOVQ 24(SP), SI + MOVQ 32(SP), DX + MOVQ 40(SP), R10 + MOVQ 48(SP), R8 + MOVQ 56(SP), R9 + + // shift around the last three arguments so they're at the + // top of the stack when the syscall is called. + MOVQ 64(SP), R11 // arg 7 + MOVQ R11, 8(SP) + MOVQ 72(SP), R11 // arg 8 + MOVQ R11, 16(SP) + MOVQ 80(SP), R11 // arg 9 + MOVQ R11, 24(SP) + + SYSCALL + JCC ok9 + MOVQ $-1, 88(SP) // r1 + MOVQ $0, 96(SP) // r2 + MOVQ AX, 104(SP) // errno + CALL runtime·exitsyscall(SB) + RET +ok9: + MOVQ AX, 88(SP) // r1 + MOVQ DX, 96(SP) // r2 + MOVQ $0, 104(SP) // errno + CALL runtime·exitsyscall(SB) + RET + TEXT ·RawSyscall(SB),7,$0 MOVQ 16(SP), DI MOVQ 24(SP), SI diff --git a/src/pkg/syscall/asm_freebsd_arm.s b/src/pkg/syscall/asm_freebsd_arm.s new file mode 100644 index 000000000..ab08ab5f5 --- /dev/null +++ b/src/pkg/syscall/asm_freebsd_arm.s @@ -0,0 +1,124 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +// System call support for ARM, FreeBSD +// + +// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32); +// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32); +// func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32) + +TEXT ·Syscall(SB),7,$0 + BL runtime·entersyscall(SB) + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + SWI $0 // syscall + MOVW $0, R2 + BCS error + MOVW R0, 16(FP) // r1 + MOVW R1, 20(FP) // r2 + MOVW R2, 24(FP) // err + BL runtime·exitsyscall(SB) + RET +error: + MOVW $-1, R3 + MOVW R3, 16(FP) // r1 + MOVW R2, 20(FP) // r2 + MOVW R0, 24(FP) // err + BL runtime·exitsyscall(SB) + RET + +TEXT ·Syscall6(SB),7,$0 + BL runtime·entersyscall(SB) + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + MOVW R13, R4 + MOVW $16(FP), R13 // a4 to a6 are passed on stack + SWI $0 // syscall + MOVW R4, R13 + MOVW $0, R2 + BCS error6 + MOVW R0, 28(FP) // r1 + MOVW R1, 32(FP) // r2 + MOVW R2, 36(FP) // err + BL runtime·exitsyscall(SB) + RET +error6: + MOVW $-1, R3 + MOVW R3, 28(FP) // r1 + MOVW R2, 32(FP) // r2 + MOVW R0, 36(FP) // err + BL runtime·exitsyscall(SB) + RET + +TEXT ·Syscall9(SB),7,$0 + BL runtime·entersyscall(SB) + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + MOVW R13, R4 + MOVW $16(FP), R13 // a4 to a9 are passed on stack + SWI $0 // syscall + MOVW R4, R13 + MOVW $0, R2 + BCS error9 + MOVW R0, 40(FP) // r1 + MOVW R1, 44(FP) // r2 + MOVW R2, 48(FP) // err + BL runtime·exitsyscall(SB) + RET +error9: + MOVW $-1, R3 + MOVW R3, 40(FP) // r1 + MOVW R2, 44(FP) // r2 + MOVW R0, 48(FP) // err + BL runtime·exitsyscall(SB) + RET + +TEXT ·RawSyscall(SB),7,$0 + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + SWI $0 // syscall + MOVW $0, R2 + BCS errorr + MOVW R0, 16(FP) // r1 + MOVW R1, 20(FP) // r2 + MOVW R2, 24(FP) // err + RET +errorr: + MOVW $-1, R3 + MOVW R3, 16(FP) // r1 + MOVW R2, 20(FP) // r2 + MOVW R0, 24(FP) // err + RET + +TEXT ·RawSyscall6(SB),7,$0 + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + MOVW R13, R4 + MOVW $16(FP), R13 // a4 to a9 are passed on stack + SWI $0 // syscall + MOVW R4, R13 + MOVW $0, R2 + BCS errorr6 + MOVW R0, 28(FP) // r1 + MOVW R1, 32(FP) // r2 + MOVW R2, 36(FP) // err + RET +errorr6: + MOVW $-1, R3 + MOVW R3, 28(FP) // r1 + MOVW R2, 32(FP) // r2 + MOVW R0, 36(FP) // err + RET diff --git a/src/pkg/syscall/asm_linux_amd64.s b/src/pkg/syscall/asm_linux_amd64.s index fdc233ca5..1a1fdb06a 100644 --- a/src/pkg/syscall/asm_linux_amd64.s +++ b/src/pkg/syscall/asm_linux_amd64.s @@ -108,7 +108,7 @@ ok2: TEXT ·Gettimeofday(SB),7,$0 MOVQ 8(SP), DI MOVQ $0, SI - MOVQ $0xffffffffff600000, AX + MOVQ runtime·__vdso_gettimeofday_sym(SB), AX CALL AX CMPQ AX, $0xfffffffffffff001 @@ -122,7 +122,7 @@ ok7: TEXT ·Time(SB),7,$0 MOVQ 8(SP), DI - MOVQ $0xffffffffff600400, AX + MOVQ runtime·__vdso_time_sym(SB), AX CALL AX MOVQ AX, 16(SP) // tt MOVQ $0, 24(SP) // errno diff --git a/src/pkg/syscall/asm_netbsd_arm.s b/src/pkg/syscall/asm_netbsd_arm.s new file mode 100644 index 000000000..e42da334a --- /dev/null +++ b/src/pkg/syscall/asm_netbsd_arm.s @@ -0,0 +1,124 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +// System call support for ARM, NetBSD +// + +// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32); +// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32); +// func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32) + +TEXT ·Syscall(SB),7,$0 + BL runtime·entersyscall(SB) + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + SWI $0 // syscall + MOVW $0, R2 + BCS error + MOVW R0, 16(FP) // r1 + MOVW R1, 20(FP) // r2 + MOVW R2, 24(FP) // err + BL runtime·exitsyscall(SB) + RET +error: + MOVW $-1, R3 + MOVW R3, 16(FP) // r1 + MOVW R2, 20(FP) // r2 + MOVW R0, 24(FP) // err + BL runtime·exitsyscall(SB) + RET + +TEXT ·Syscall6(SB),7,$0 + BL runtime·entersyscall(SB) + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + MOVW R13, R4 + MOVW $16(FP), R13 // a4 to a6 are passed on stack + SWI $0 // syscall + MOVW R4, R13 + MOVW $0, R2 + BCS error6 + MOVW R0, 28(FP) // r1 + MOVW R1, 32(FP) // r2 + MOVW R2, 36(FP) // err + BL runtime·exitsyscall(SB) + RET +error6: + MOVW $-1, R3 + MOVW R3, 28(FP) // r1 + MOVW R2, 32(FP) // r2 + MOVW R0, 36(FP) // err + BL runtime·exitsyscall(SB) + RET + +TEXT ·Syscall9(SB),7,$0 + BL runtime·entersyscall(SB) + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + MOVW R13, R4 + MOVW $16(FP), R13 // a4 to a9 are passed on stack + SWI $0 // syscall + MOVW R4, R13 + MOVW $0, R2 + BCS error9 + MOVW R0, 40(FP) // r1 + MOVW R1, 44(FP) // r2 + MOVW R2, 48(FP) // err + BL runtime·exitsyscall(SB) + RET +error9: + MOVW $-1, R3 + MOVW R3, 40(FP) // r1 + MOVW R2, 44(FP) // r2 + MOVW R0, 48(FP) // err + BL runtime·exitsyscall(SB) + RET + +TEXT ·RawSyscall(SB),7,$0 + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + SWI $0 // syscall + MOVW $0, R2 + BCS errorr + MOVW R0, 16(FP) // r1 + MOVW R1, 20(FP) // r2 + MOVW R2, 24(FP) // err + RET +errorr: + MOVW $-1, R3 + MOVW R3, 16(FP) // r1 + MOVW R2, 20(FP) // r2 + MOVW R0, 24(FP) // err + RET + +TEXT ·RawSyscall6(SB),7,$0 + MOVW 0(FP), R0 // sigcall num + MOVW 4(FP), R1 // a1 + MOVW 8(FP), R2 // a2 + MOVW 12(FP), R3 // a3 + MOVW R13, R4 + MOVW $16(FP), R13 // a4 to a9 are passed on stack + SWI $0 // syscall + MOVW R4, R13 + MOVW $0, R2 + BCS errorr6 + MOVW R0, 28(FP) // r1 + MOVW R1, 32(FP) // r2 + MOVW R2, 36(FP) // err + RET +errorr6: + MOVW $-1, R3 + MOVW R3, 28(FP) // r1 + MOVW R2, 32(FP) // r2 + MOVW R0, 36(FP) // err + RET diff --git a/src/pkg/syscall/asm_plan9_386.s b/src/pkg/syscall/asm_plan9_386.s index 86ebedccc..2ba137608 100644 --- a/src/pkg/syscall/asm_plan9_386.s +++ b/src/pkg/syscall/asm_plan9_386.s @@ -149,3 +149,11 @@ copyresult6: MOVSL MOVSL RET + +//func exit(code int) +// Import runtime·exit for cleanly exiting. +TEXT ·exit(SB),7,$4 + MOVL code+0(FP), AX + MOVL AX, 0(SP) + CALL runtime·exit(SB) + RET diff --git a/src/pkg/syscall/asm_plan9_amd64.s b/src/pkg/syscall/asm_plan9_amd64.s new file mode 100644 index 000000000..e363b743c --- /dev/null +++ b/src/pkg/syscall/asm_plan9_amd64.s @@ -0,0 +1,165 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +// System call support for Plan 9 +// + +//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string) +//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string) +//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) +//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) + +// Trap # in BP, args on stack above caller pc. +// NxM requires that Plan 9 system calls be +// marked with $0x8000 in AX. +TEXT ·Syscall(SB),7,$0 + CALL runtime·entersyscall(SB) + MOVQ $0x8000, AX // for NxM + MOVQ 8(SP), BP // syscall entry + // slide args down on top of system call number + LEAQ 16(SP), SI + LEAQ 8(SP), DI + CLD + MOVSQ + MOVSQ + MOVSQ + SYSCALL + MOVQ AX, r1+40(SP) + MOVQ $0, r2+48(SP) + CMPL AX, $-1 + JNE ok3 + + SUBQ $16, SP + CALL syscall·errstr(SB) + MOVQ SP, SI + ADDQ $16, SP + JMP copyresult3 + +ok3: + LEAQ runtime·emptystring(SB), SI + +copyresult3: + LEAQ err+56(SP), DI + + CLD + MOVSQ + MOVSQ + + CALL runtime·exitsyscall(SB) + RET + +TEXT ·Syscall6(SB),7,$0 + CALL runtime·entersyscall(SB) + MOVQ $0x8000, AX // for NxM + MOVQ 8(SP), BP // syscall entry + // slide args down on top of system call number + LEAQ 16(SP), SI + LEAQ 8(SP), DI + CLD + MOVSQ + MOVSQ + MOVSQ + MOVSQ + MOVSQ + MOVSQ + SYSCALL + MOVQ AX, r1+64(SP) + MOVQ $0, r2+72(SP) + CMPL AX, $-1 + JNE ok4 + + SUBQ $16, SP + CALL syscall·errstr(SB) + MOVQ SP, SI + ADDQ $16, SP + JMP copyresult4 + +ok4: + LEAQ runtime·emptystring(SB), SI + +copyresult4: + LEAQ err+80(SP), DI + + CLD + MOVSQ + MOVSQ + + CALL runtime·exitsyscall(SB) + RET + +TEXT ·RawSyscall(SB),7,$0 + MOVQ $0x8000, AX // for NxM + MOVQ 8(SP), BP // syscall entry + // slide args down on top of system call number + LEAQ 16(SP), SI + LEAQ 8(SP), DI + CLD + MOVSQ + MOVSQ + MOVSQ + SYSCALL + MOVQ AX, r1+40(SP) + MOVQ AX, r2+48(SP) + MOVQ AX, err+56(SP) + RET + +TEXT ·RawSyscall6(SB),7,$0 + MOVQ $0x8000, AX // for NxM + MOVQ 8(SP), BP // syscall entry + // slide args down on top of system call number + LEAQ 16(SP), SI + LEAQ 8(SP), DI + CLD + MOVSQ + MOVSQ + MOVSQ + MOVSQ + MOVSQ + MOVSQ + SYSCALL + MOVQ AX, r1+64(SP) + MOVQ AX, r2+72(SP) + MOVQ AX, err+80(SP) + RET + +#define SYS_SEEK 39 /* from zsysnum_plan9_amd64.go */ + +//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string) +TEXT ·seek(SB),7,$0 + LEAQ newoffset+40(SP), AX + MOVQ AX, placeholder+8(SP) + + MOVQ $0x8000, AX // for NxM + MOVQ $SYS_SEEK, BP // syscall entry + SYSCALL + + CMPL AX, $-1 + JNE ok6 + MOVQ $-1, newoffset+40(SP) + + SUBQ $16, SP + CALL syscall·errstr(SB) + MOVQ SP, SI + ADDQ $16, SP + JMP copyresult6 + +ok6: + LEAQ runtime·emptystring(SB), SI + +copyresult6: + LEAQ err+48(SP), DI + + CLD + MOVSQ + MOVSQ + RET + +//func exit(code int) +// Import runtime·exit for cleanly exiting. +TEXT ·exit(SB),7,$8 + MOVQ code+0(FP), AX + MOVQ AX, 0(SP) + CALL runtime·exit(SB) + RET diff --git a/src/pkg/syscall/asm_windows_386.s b/src/pkg/syscall/asm_windows_386.s index a7b95643d..8b52fa985 100644 --- a/src/pkg/syscall/asm_windows_386.s +++ b/src/pkg/syscall/asm_windows_386.s @@ -3,5 +3,5 @@ // license that can be found in the LICENSE file. // -// System calls for 386, Windows are implemented in ../runtime/windows/syscall.goc +// System calls for 386, Windows are implemented in ../runtime/syscall_windows.goc // diff --git a/src/pkg/syscall/asm_windows_amd64.s b/src/pkg/syscall/asm_windows_amd64.s index 8b38710c7..5813404d1 100644 --- a/src/pkg/syscall/asm_windows_amd64.s +++ b/src/pkg/syscall/asm_windows_amd64.s @@ -3,5 +3,5 @@ // license that can be found in the LICENSE file. // -// System calls for amd64, Windows are implemented in ../runtime/windows/syscall.goc +// System calls for amd64, Windows are implemented in ../runtime/syscall_windows.goc // diff --git a/src/pkg/syscall/creds_test.go b/src/pkg/syscall/creds_test.go new file mode 100644 index 000000000..b1894c66b --- /dev/null +++ b/src/pkg/syscall/creds_test.go @@ -0,0 +1,113 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package syscall_test + +import ( + "bytes" + "net" + "os" + "syscall" + "testing" +) + +// TestSCMCredentials tests the sending and receiving of credentials +// (PID, UID, GID) in an ancillary message between two UNIX +// sockets. The SO_PASSCRED socket option is enabled on the sending +// socket for this to work. +func TestSCMCredentials(t *testing.T) { + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) + if err != nil { + t.Fatalf("Socketpair: %v", err) + } + defer syscall.Close(fds[0]) + defer syscall.Close(fds[1]) + + err = syscall.SetsockoptInt(fds[0], syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1) + if err != nil { + t.Fatalf("SetsockoptInt: %v", err) + } + + srvFile := os.NewFile(uintptr(fds[0]), "server") + defer srvFile.Close() + srv, err := net.FileConn(srvFile) + if err != nil { + t.Errorf("FileConn: %v", err) + return + } + defer srv.Close() + + cliFile := os.NewFile(uintptr(fds[1]), "client") + defer cliFile.Close() + cli, err := net.FileConn(cliFile) + if err != nil { + t.Errorf("FileConn: %v", err) + return + } + defer cli.Close() + + var ucred syscall.Ucred + if os.Getuid() != 0 { + ucred.Pid = int32(os.Getpid()) + ucred.Uid = 0 + ucred.Gid = 0 + oob := syscall.UnixCredentials(&ucred) + _, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil) + if err.(*net.OpError).Err != syscall.EPERM { + t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err) + } + } + + ucred.Pid = int32(os.Getpid()) + ucred.Uid = uint32(os.Getuid()) + ucred.Gid = uint32(os.Getgid()) + oob := syscall.UnixCredentials(&ucred) + + // this is going to send a dummy byte + n, oobn, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil) + if err != nil { + t.Fatalf("WriteMsgUnix: %v", err) + } + if n != 0 { + t.Fatalf("WriteMsgUnix n = %d, want 0", n) + } + if oobn != len(oob) { + t.Fatalf("WriteMsgUnix oobn = %d, want %d", oobn, len(oob)) + } + + oob2 := make([]byte, 10*len(oob)) + n, oobn2, flags, _, err := srv.(*net.UnixConn).ReadMsgUnix(nil, oob2) + if err != nil { + t.Fatalf("ReadMsgUnix: %v", err) + } + if flags != 0 { + t.Fatalf("ReadMsgUnix flags = 0x%x, want 0", flags) + } + if n != 1 { + t.Fatalf("ReadMsgUnix n = %d, want 1 (dummy byte)", n) + } + if oobn2 != oobn { + // without SO_PASSCRED set on the socket, ReadMsgUnix will + // return zero oob bytes + t.Fatalf("ReadMsgUnix oobn = %d, want %d", oobn2, oobn) + } + oob2 = oob2[:oobn2] + if !bytes.Equal(oob, oob2) { + t.Fatal("ReadMsgUnix oob bytes don't match") + } + + scm, err := syscall.ParseSocketControlMessage(oob2) + if err != nil { + t.Fatalf("ParseSocketControlMessage: %v", err) + } + newUcred, err := syscall.ParseUnixCredentials(&scm[0]) + if err != nil { + t.Fatalf("ParseUnixCredentials: %v", err) + } + if *newUcred != ucred { + t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred) + } +} diff --git a/src/pkg/syscall/dir_plan9.go b/src/pkg/syscall/dir_plan9.go new file mode 100644 index 000000000..b7ab4cd10 --- /dev/null +++ b/src/pkg/syscall/dir_plan9.go @@ -0,0 +1,205 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Plan 9 directory marshalling. See intro(5). + +package syscall + +import "errors" + +var ( + ErrShortStat = errors.New("stat buffer too short") + ErrBadStat = errors.New("malformed stat buffer") +) + +// A Qid represents a 9P server's unique identification for a file. +type Qid struct { + Path uint64 // the file server's unique identification for the file + Vers uint32 // version number for given Path + Type uint8 // the type of the file (syscall.QTDIR for example) +} + +// A Dir contains the metadata for a file. +type Dir struct { + // system-modified data + Type uint16 // server type + Dev uint32 // server subtype + + // file data + Qid Qid // unique id from server + Mode uint32 // permissions + Atime uint32 // last read time + Mtime uint32 // last write time + Length int64 // file length + Name string // last element of path + Uid string // owner name + Gid string // group name + Muid string // last modifier name +} + +var nullDir = Dir{ + Type: ^uint16(0), + Dev: ^uint32(0), + Qid: Qid{ + Path: ^uint64(0), + Vers: ^uint32(0), + Type: ^uint8(0), + }, + Mode: ^uint32(0), + Atime: ^uint32(0), + Mtime: ^uint32(0), + Length: ^int64(0), +} + +// Null assigns special "don't touch" values to members of d to +// avoid modifiying them during syscall.Wstat. +func (d *Dir) Null() { *d = nullDir } + +// Marshal encodes a 9P stat message corresponding to d into b +// +// If there isn't enough space in b for a stat message, ErrShortStat is returned. +func (d *Dir) Marshal(b []byte) (n int, err error) { + n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid) + if n > len(b) { + return n, ErrShortStat + } + + b = pbit16(b, uint16(n)-2) + b = pbit16(b, d.Type) + b = pbit32(b, d.Dev) + b = pbit8(b, d.Qid.Type) + b = pbit32(b, d.Qid.Vers) + b = pbit64(b, d.Qid.Path) + b = pbit32(b, d.Mode) + b = pbit32(b, d.Atime) + b = pbit32(b, d.Mtime) + b = pbit64(b, uint64(d.Length)) + b = pstring(b, d.Name) + b = pstring(b, d.Uid) + b = pstring(b, d.Gid) + b = pstring(b, d.Muid) + + return n, nil +} + +// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir. +// +// If b is too small to hold a valid stat message, ErrShortStat is returned. +// +// If the stat message itself is invalid, ErrBadStat is returned. +func UnmarshalDir(b []byte) (*Dir, error) { + if len(b) < STATFIXLEN { + return nil, ErrShortStat + } + size, buf := gbit16(b) + if len(b) != int(size)+2 { + return nil, ErrBadStat + } + b = buf + + var d Dir + d.Type, b = gbit16(b) + d.Dev, b = gbit32(b) + d.Qid.Type, b = gbit8(b) + d.Qid.Vers, b = gbit32(b) + d.Qid.Path, b = gbit64(b) + d.Mode, b = gbit32(b) + d.Atime, b = gbit32(b) + d.Mtime, b = gbit32(b) + + n, b := gbit64(b) + d.Length = int64(n) + + var ok bool + if d.Name, b, ok = gstring(b); !ok { + return nil, ErrBadStat + } + if d.Uid, b, ok = gstring(b); !ok { + return nil, ErrBadStat + } + if d.Gid, b, ok = gstring(b); !ok { + return nil, ErrBadStat + } + if d.Muid, b, ok = gstring(b); !ok { + return nil, ErrBadStat + } + + return &d, nil +} + +// pbit8 copies the 8-bit number v to b and returns the remaining slice of b. +func pbit8(b []byte, v uint8) []byte { + b[0] = byte(v) + return b[1:] +} + +// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b. +func pbit16(b []byte, v uint16) []byte { + b[0] = byte(v) + b[1] = byte(v >> 8) + return b[2:] +} + +// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b. +func pbit32(b []byte, v uint32) []byte { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + return b[4:] +} + +// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b. +func pbit64(b []byte, v uint64) []byte { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) + return b[8:] +} + +// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and +// returning the remaining slice of b.. +func pstring(b []byte, s string) []byte { + b = pbit16(b, uint16(len(s))) + n := copy(b, s) + return b[n:] +} + +// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b. +func gbit8(b []byte) (uint8, []byte) { + return uint8(b[0]), b[1:] +} + +// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b. +func gbit16(b []byte) (uint16, []byte) { + return uint16(b[0]) | uint16(b[1])<<8, b[2:] +} + +// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b. +func gbit32(b []byte) (uint32, []byte) { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] +} + +// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b. +func gbit64(b []byte) (uint64, []byte) { + lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24 + return uint64(lo) | uint64(hi)<<32, b[8:] +} + +// gstring reads a string from b, prefixed with a 16-bit length in little-endian order. +// It returns the string with the remaining slice of b and a boolean. If the length is +// greater than the number of bytes in b, the boolean will be false. +func gstring(b []byte) (string, []byte, bool) { + n, b := gbit16(b) + if int(n) > len(b) { + return "", b, false + } + return string(b[:n]), b[n:], true +} diff --git a/src/pkg/syscall/dll_windows.go b/src/pkg/syscall/dll_windows.go index 2a45991dd..d29e9921c 100644 --- a/src/pkg/syscall/dll_windows.go +++ b/src/pkg/syscall/dll_windows.go @@ -6,6 +6,8 @@ package syscall import ( "sync" + "sync/atomic" + "unsafe" ) // DLLError describes reasons for DLL load failures. @@ -17,13 +19,13 @@ type DLLError struct { func (e *DLLError) Error() string { return e.Msg } -// Implemented in ../runtime/windows/syscall.goc. +// Implemented in ../runtime/syscall_windows.goc. func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno) func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno) -func loadlibrary(filename *uint16) (handle, err Errno) +func loadlibrary(filename *uint16) (handle uintptr, err Errno) func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) // A DLL implements access to a single DLL. @@ -34,7 +36,11 @@ type DLL struct { // LoadDLL loads DLL file into memory. func LoadDLL(name string) (dll *DLL, err error) { - h, e := loadlibrary(StringToUTF16Ptr(name)) + namep, err := UTF16PtrFromString(name) + if err != nil { + return nil, err + } + h, e := loadlibrary(namep) if e != 0 { return nil, &DLLError{ Err: e, @@ -61,7 +67,11 @@ func MustLoadDLL(name string) *DLL { // FindProc searches DLL d for procedure named name and returns *Proc // if found. It returns an error if search fails. func (d *DLL) FindProc(name string) (proc *Proc, err error) { - a, e := getprocaddress(uintptr(d.Handle), StringBytePtr(name)) + namep, err := BytePtrFromString(name) + if err != nil { + return nil, err + } + a, e := getprocaddress(uintptr(d.Handle), namep) if e != 0 { return nil, &DLLError{ Err: e, @@ -104,8 +114,14 @@ func (p *Proc) Addr() uintptr { return p.addr } -// Call executes procedure p with arguments a. -func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, err error) { +// Call executes procedure p with arguments a. It will panic, if more then 15 arguments +// are supplied. +// +// The returned error is always non-nil, constructed from the result of GetLastError. +// Callers must inspect the primary return value to decide whether an error occurred +// (according to the semantics of the specific function being called) before consulting +// the error. The error will be guaranteed to contain syscall.Errno. +func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { switch len(a) { case 0: return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0) @@ -158,7 +174,9 @@ type LazyDLL struct { // Load loads DLL file d.Name into memory. It returns an error if fails. // Load will not try to load DLL, if it is already loaded into memory. func (d *LazyDLL) Load() error { - if d.dll == nil { + // Non-racy version of: + // if d.dll == nil { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil { d.mu.Lock() defer d.mu.Unlock() if d.dll == nil { @@ -166,7 +184,9 @@ func (d *LazyDLL) Load() error { if e != nil { return e } - d.dll = dll + // Non-racy version of: + // d.dll = dll + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll)) } } return nil @@ -209,7 +229,9 @@ type LazyProc struct { // an error if search fails. Find will not search procedure, // if it is already found and loaded into memory. func (p *LazyProc) Find() error { - if p.proc == nil { + // Non-racy version of: + // if p.proc == nil { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil { p.mu.Lock() defer p.mu.Unlock() if p.proc == nil { @@ -221,7 +243,9 @@ func (p *LazyProc) Find() error { if e != nil { return e } - p.proc = proc + // Non-racy version of: + // p.proc = proc + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc)) } } return nil @@ -242,8 +266,14 @@ func (p *LazyProc) Addr() uintptr { return p.proc.Addr() } -// Call executes procedure p with arguments a. -func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, err error) { +// Call executes procedure p with arguments a. It will panic, if more then 15 arguments +// are supplied. +// +// The returned error is always non-nil, constructed from the result of GetLastError. +// Callers must inspect the primary return value to decide whether an error occurred +// (according to the semantics of the specific function being called) before consulting +// the error. The error will be guaranteed to contain syscall.Errno. +func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { p.mustFind() return p.proc.Call(a...) } diff --git a/src/pkg/syscall/env_plan9.go b/src/pkg/syscall/env_plan9.go index 2848d9b32..9587ab5af 100644 --- a/src/pkg/syscall/env_plan9.go +++ b/src/pkg/syscall/env_plan9.go @@ -12,14 +12,20 @@ import ( ) var ( - // envOnce guards initialization by copyenv, which populates env. + // envOnce guards copyenv, which populates env. envOnce sync.Once - // envLock guards env. + // envLock guards env and envs. envLock sync.RWMutex // env maps from an environment variable to its value. - env map[string]string + env = make(map[string]string) + + // envs contains elements of env in the form "key=value". + envs []string + + errZeroLengthKey = errors.New("zero length key") + errShortWrite = errors.New("i/o count too small") ) func readenv(key string) (string, error) { @@ -47,12 +53,18 @@ func writeenv(key, value string) error { return err } defer Close(fd) - _, err = Write(fd, []byte(value)) - return err + b := []byte(value) + n, err := Write(fd, b) + if err != nil { + return err + } + if n != len(b) { + return errShortWrite + } + return nil } func copyenv() { - env = make(map[string]string) fd, err := Open("/env", O_RDONLY) if err != nil { return @@ -62,17 +74,20 @@ func copyenv() { if err != nil { return } + envs = make([]string, len(files)) + i := 0 for _, key := range files { v, err := readenv(key) if err != nil { continue } env[key] = v + envs[i] = key + "=" + v + i++ } } func Getenv(key string) (value string, found bool) { - envOnce.Do(copyenv) if len(key) == 0 { return "", false } @@ -80,17 +95,21 @@ func Getenv(key string) (value string, found bool) { envLock.RLock() defer envLock.RUnlock() - v, ok := env[key] - if !ok { + if v, ok := env[key]; ok { + return v, true + } + v, err := readenv(key) + if err != nil { return "", false } + env[key] = v + envs = append(envs, key+"="+v) return v, true } func Setenv(key, value string) error { - envOnce.Do(copyenv) if len(key) == 0 { - return errors.New("zero length key") + return errZeroLengthKey } envLock.Lock() @@ -101,28 +120,23 @@ func Setenv(key, value string) error { return err } env[key] = value + envs = append(envs, key+"="+value) return nil } func Clearenv() { - envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv - envLock.Lock() defer envLock.Unlock() env = make(map[string]string) + envs = []string{} RawSyscall(SYS_RFORK, RFCENVG, 0, 0) } func Environ() []string { - envOnce.Do(copyenv) envLock.RLock() defer envLock.RUnlock() - a := make([]string, len(env)) - i := 0 - for k, v := range env { - a[i] = k + "=" + v - i++ - } - return a + + envOnce.Do(copyenv) + return append([]string(nil), envs...) } diff --git a/src/pkg/syscall/env_unix.go b/src/pkg/syscall/env_unix.go index 8b1868c27..8573d79c7 100644 --- a/src/pkg/syscall/env_unix.go +++ b/src/pkg/syscall/env_unix.go @@ -71,6 +71,16 @@ func Setenv(key, value string) error { if len(key) == 0 { return EINVAL } + for i := 0; i < len(key); i++ { + if key[i] == '=' || key[i] == 0 { + return EINVAL + } + } + for i := 0; i < len(value); i++ { + if value[i] == 0 { + return EINVAL + } + } envLock.Lock() defer envLock.Unlock() diff --git a/src/pkg/syscall/env_windows.go b/src/pkg/syscall/env_windows.go index 8308f10a2..39bd5022e 100644 --- a/src/pkg/syscall/env_windows.go +++ b/src/pkg/syscall/env_windows.go @@ -12,14 +12,18 @@ import ( ) func Getenv(key string) (value string, found bool) { + keyp, err := UTF16PtrFromString(key) + if err != nil { + return "", false + } b := make([]uint16, 100) - n, e := GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b))) + n, e := GetEnvironmentVariable(keyp, &b[0], uint32(len(b))) if n == 0 && e == ERROR_ENVVAR_NOT_FOUND { return "", false } if n > uint32(len(b)) { b = make([]uint16, n) - n, e = GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b))) + n, e = GetEnvironmentVariable(keyp, &b[0], uint32(len(b))) if n > uint32(len(b)) { n = 0 } @@ -32,10 +36,18 @@ func Getenv(key string) (value string, found bool) { func Setenv(key, value string) error { var v *uint16 + var err error if len(value) > 0 { - v = StringToUTF16Ptr(value) + v, err = UTF16PtrFromString(value) + if err != nil { + return err + } + } + keyp, err := UTF16PtrFromString(key) + if err != nil { + return err } - e := SetEnvironmentVariable(StringToUTF16Ptr(key), v) + e := SetEnvironmentVariable(keyp, v) if e != nil { return e } diff --git a/src/pkg/syscall/exec_bsd.go b/src/pkg/syscall/exec_bsd.go index 9c3af5ec9..318cbc060 100644 --- a/src/pkg/syscall/exec_bsd.go +++ b/src/pkg/syscall/exec_bsd.go @@ -221,3 +221,17 @@ childerror: // and this shuts up the compiler. panic("unreached") } + +// Try to open a pipe with O_CLOEXEC set on both file descriptors. +func forkExecPipe(p []int) error { + err := Pipe(p) + if err != nil { + return err + } + _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC) + if err != nil { + return err + } + _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC) + return err +} diff --git a/src/pkg/syscall/exec_linux.go b/src/pkg/syscall/exec_linux.go index 70f3e6217..a8dc672b8 100644 --- a/src/pkg/syscall/exec_linux.go +++ b/src/pkg/syscall/exec_linux.go @@ -16,8 +16,9 @@ type SysProcAttr struct { Ptrace bool // Enable tracing. Setsid bool // Create session. Setpgid bool // Set process group ID to new pid (SYSV setpgrp) - Setctty bool // Set controlling terminal to fd 0 + Setctty bool // Set controlling terminal to fd Ctty (only meaningful if Setsid is set) Noctty bool // Detach fd 0 from controlling terminal + Ctty int // Controlling TTY fd (Linux only) Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only) } @@ -206,9 +207,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } - // Make fd 0 the tty - if sys.Setctty { - _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCSCTTY), 0) + // Set the controlling TTY to Ctty + if sys.Setctty && sys.Ctty >= 0 { + _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0) if err1 != 0 { goto childerror } @@ -232,3 +233,20 @@ childerror: // and this shuts up the compiler. panic("unreached") } + +// Try to open a pipe with O_CLOEXEC set on both file descriptors. +func forkExecPipe(p []int) (err error) { + err = Pipe2(p, O_CLOEXEC) + // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it + // might not be implemented. + if err == ENOSYS { + if err = Pipe(p); err != nil { + return + } + if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil { + return + } + _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC) + } + return +} diff --git a/src/pkg/syscall/exec_plan9.go b/src/pkg/syscall/exec_plan9.go index 7e4e180fa..ebd57f3e3 100644 --- a/src/pkg/syscall/exec_plan9.go +++ b/src/pkg/syscall/exec_plan9.go @@ -7,6 +7,7 @@ package syscall import ( + "runtime" "sync" "unsafe" ) @@ -60,8 +61,9 @@ import ( var ForkLock sync.RWMutex -// Convert array of string to array -// of NUL-terminated byte pointer. +// StringSlicePtr is deprecated. Use SlicePtrFromStrings instead. +// If any string contains a NUL byte this function panics instead +// of returning an error. func StringSlicePtr(ss []string) []*byte { bb := make([]*byte, len(ss)+1) for i := 0; i < len(ss); i++ { @@ -71,81 +73,81 @@ func StringSlicePtr(ss []string) []*byte { return bb } -// gbit16 reads a 16-bit numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit16(b []byte) (uint16, []byte) { - return uint16(b[0]) | uint16(b[1])<<8, b[2:] -} - -// gstring reads a string from a 9P protocol message stored in b, -// returning the value as a Go string and the remaining slice of b. -func gstring(b []byte) (string, []byte) { - n, b := gbit16(b) - return string(b[0:n]), b[n:] +// SlicePtrFromStrings converts a slice of strings to a slice of +// pointers to NUL-terminated byte slices. If any string contains +// a NUL byte, it returns (nil, EINVAL). +func SlicePtrFromStrings(ss []string) ([]*byte, error) { + var err error + bb := make([]*byte, len(ss)+1) + for i := 0; i < len(ss); i++ { + bb[i], err = BytePtrFromString(ss[i]) + if err != nil { + return nil, err + } + } + bb[len(ss)] = nil + return bb, nil } // readdirnames returns the names of files inside the directory represented by dirfd. func readdirnames(dirfd int) (names []string, err error) { - result := make([]string, 0, 100) + names = make([]string, 0, 100) var buf [STATMAX]byte for { n, e := Read(dirfd, buf[:]) if e != nil { - return []string{}, e + return nil, e } if n == 0 { break } - for i := 0; i < n; { m, _ := gbit16(buf[i:]) m += 2 if m < STATFIXLEN { - return []string{}, NewError("malformed stat buffer") + return nil, ErrBadStat } - name, _ := gstring(buf[i+41:]) - result = append(result, name) - + s, _, ok := gstring(buf[i+41:]) + if !ok { + return nil, ErrBadStat + } + names = append(names, s) i += int(m) } } - return []string{}, nil + return } // readdupdevice returns a list of currently opened fds (excluding stdin, stdout, stderr) from the dup device #d. // ForkLock should be write locked before calling, so that no new fds would be created while the fd list is being read. func readdupdevice() (fds []int, err error) { dupdevfd, err := Open("#d", O_RDONLY) - if err != nil { return } defer Close(dupdevfd) - fileNames, err := readdirnames(dupdevfd) + names, err := readdirnames(dupdevfd) if err != nil { return } - fds = make([]int, 0, len(fileNames)>>1) - for _, fdstr := range fileNames { - if l := len(fdstr); l > 2 && fdstr[l-3] == 'c' && fdstr[l-2] == 't' && fdstr[l-1] == 'l' { + fds = make([]int, 0, len(names)/2) + for _, name := range names { + if n := len(name); n > 3 && name[n-3:n] == "ctl" { continue } - - fd := int(atoi([]byte(fdstr))) - - if fd == 0 || fd == 1 || fd == 2 || fd == dupdevfd { + fd := int(atoi([]byte(name))) + switch fd { + case 0, 1, 2, dupdevfd: continue } - fds = append(fds, fd) } - - return fds[0:len(fds)], nil + return } var startupFds []int @@ -196,7 +198,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv|rflag), 0, 0) if r1 != 0 { - if int(r1) == -1 { + if int32(r1) == -1 { return 0, NewError(errstr()) } // parent; return PID @@ -208,7 +210,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at // Close fds we don't need. for i = 0; i < len(fdsToClose); i++ { r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(fdsToClose[i]), 0, 0) - if int(r1) == -1 { + if int32(r1) == -1 { goto childerror } } @@ -218,7 +220,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at for i = 0; i < len(envv); i++ { r1, _, _ = RawSyscall(SYS_CREATE, uintptr(unsafe.Pointer(envv[i].name)), uintptr(O_WRONLY), uintptr(0666)) - if int(r1) == -1 { + if int32(r1) == -1 { goto childerror } @@ -227,13 +229,13 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at r1, _, _ = RawSyscall6(SYS_PWRITE, uintptr(envfd), uintptr(unsafe.Pointer(envv[i].value)), uintptr(envv[i].nvalue), ^uintptr(0), ^uintptr(0), 0) - if int(r1) == -1 || int(r1) != envv[i].nvalue { + if int32(r1) == -1 || int(r1) != envv[i].nvalue { goto childerror } r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(envfd), 0, 0) - if int(r1) == -1 { + if int32(r1) == -1 { goto childerror } } @@ -242,7 +244,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at // Chdir if dir != nil { r1, _, _ = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0) - if int(r1) == -1 { + if int32(r1) == -1 { goto childerror } } @@ -252,7 +254,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at nextfd = int(len(fd)) if pipe < nextfd { r1, _, _ = RawSyscall(SYS_DUP, uintptr(pipe), uintptr(nextfd), 0) - if int(r1) == -1 { + if int32(r1) == -1 { goto childerror } pipe = nextfd @@ -261,7 +263,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at for i = 0; i < len(fd); i++ { if fd[i] >= 0 && fd[i] < int(i) { r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(nextfd), 0) - if int(r1) == -1 { + if int32(r1) == -1 { goto childerror } @@ -282,12 +284,17 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at if fd[i] == int(i) { continue } - r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(i), 0) - if int(r1) == -1 { + if int32(r1) == -1 { goto childerror } - RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0) + } + + // Pass 3: close fd[i] if it was moved in the previous pass. + for i = 0; i < len(fd); i++ { + if fd[i] >= 0 && fd[i] != int(i) { + RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0) + } } // Time to exec. @@ -374,12 +381,21 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) p[1] = -1 // Convert args to C form. - argv0p := StringBytePtr(argv0) - argvp := StringSlicePtr(argv) + argv0p, err := BytePtrFromString(argv0) + if err != nil { + return 0, err + } + argvp, err := SlicePtrFromStrings(argv) + if err != nil { + return 0, err + } var dir *byte if attr.Dir != "" { - dir = StringBytePtr(attr.Dir) + dir, err = BytePtrFromString(attr.Dir) + if err != nil { + return 0, err + } } var envvParsed []envItem if attr.Env != nil { @@ -390,7 +406,13 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) i++ } - envvParsed = append(envvParsed, envItem{StringBytePtr("/env/" + v[:i]), StringBytePtr(v[i+1:]), len(v) - i}) + envname, err := BytePtrFromString("/env/" + v[:i]) + if err != nil { + return 0, err + } + envvalue := make([]byte, len(v)-i) + copy(envvalue, v[i+1:]) + envvParsed = append(envvParsed, envItem{envname, &envvalue[0], len(v) - i}) } } @@ -400,44 +422,37 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) // get a list of open fds, excluding stdin,stdout and stderr that need to be closed in the child. // no new fds can be created while we hold the ForkLock for writing. openFds, e := readdupdevice() - if e != nil { ForkLock.Unlock() return 0, e } fdsToClose := make([]int, 0, len(openFds)) - // exclude fds opened from startup from the list of fds to be closed. for _, fd := range openFds { - isReserved := false - for _, reservedFd := range startupFds { - if fd == reservedFd { - isReserved = true + doClose := true + + // exclude files opened at startup. + for _, sfd := range startupFds { + if fd == sfd { + doClose = false break } } - if !isReserved { - fdsToClose = append(fdsToClose, fd) - } - } - - // exclude fds requested by the caller from the list of fds to be closed. - for _, fd := range openFds { - isReserved := false - for _, reservedFd := range attr.Files { - if fd == int(reservedFd) { - isReserved = true + // exclude files explicitly requested by the caller. + for _, rfd := range attr.Files { + if fd == int(rfd) { + doClose = false break } } - if !isReserved { + if doClose { fdsToClose = append(fdsToClose, fd) } } - // Allocate child status pipe close on exec. + // Allocate child status pipe close on exec. e = cexecPipe(p[:]) if e != nil { @@ -480,14 +495,75 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) return pid, nil } +type waitErr struct { + Waitmsg + err error +} + +var procs struct { + sync.Mutex + waits map[int]chan *waitErr +} + +// startProcess starts a new goroutine, tied to the OS +// thread, which runs the process and subsequently waits +// for it to finish, communicating the process stats back +// to any goroutines that may have been waiting on it. +// +// Such a dedicated goroutine is needed because on +// Plan 9, only the parent thread can wait for a child, +// whereas goroutines tend to jump OS threads (e.g., +// between starting a process and running Wait(), the +// goroutine may have been rescheduled). +func startProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) { + type forkRet struct { + pid int + err error + } + + forkc := make(chan forkRet, 1) + go func() { + runtime.LockOSThread() + var ret forkRet + + ret.pid, ret.err = forkExec(argv0, argv, attr) + // If fork fails there is nothing to wait for. + if ret.err != nil || ret.pid == 0 { + forkc <- ret + return + } + + waitc := make(chan *waitErr, 1) + + // Mark that the process is running. + procs.Lock() + if procs.waits == nil { + procs.waits = make(map[int]chan *waitErr) + } + procs.waits[ret.pid] = waitc + procs.Unlock() + + forkc <- ret + + var w waitErr + for w.err == nil && w.Pid != ret.pid { + w.err = Await(&w.Waitmsg) + } + waitc <- &w + close(waitc) + }() + ret := <-forkc + return ret.pid, ret.err +} + // Combination of fork and exec, careful to be thread safe. func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) { - return forkExec(argv0, argv, attr) + return startProcess(argv0, argv, attr) } // StartProcess wraps ForkExec for package os. func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { - pid, err = forkExec(argv0, argv, attr) + pid, err = startProcess(argv0, argv, attr) return pid, 0, err } @@ -495,7 +571,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle func Exec(argv0 string, argv []string, envv []string) (err error) { if envv != nil { r1, _, _ := RawSyscall(SYS_RFORK, RFCENVG, 0, 0) - if int(r1) == -1 { + if int32(r1) == -1 { return NewError(errstr()) } @@ -519,10 +595,50 @@ func Exec(argv0 string, argv []string, envv []string) (err error) { } } + argv0p, err := BytePtrFromString(argv0) + if err != nil { + return err + } + argvp, err := SlicePtrFromStrings(argv) + if err != nil { + return err + } _, _, e1 := Syscall(SYS_EXEC, - uintptr(unsafe.Pointer(StringBytePtr(argv0))), - uintptr(unsafe.Pointer(&StringSlicePtr(argv)[0])), + uintptr(unsafe.Pointer(argv0p)), + uintptr(unsafe.Pointer(&argvp[0])), 0) return e1 } + +// WaitProcess waits until the pid of a +// running process is found in the queue of +// wait messages. It is used in conjunction +// with ForkExec/StartProcess to wait for a +// running process to exit. +func WaitProcess(pid int, w *Waitmsg) (err error) { + procs.Lock() + ch := procs.waits[pid] + procs.Unlock() + + var wmsg *waitErr + if ch != nil { + wmsg = <-ch + procs.Lock() + if procs.waits[pid] == ch { + delete(procs.waits, pid) + } + procs.Unlock() + } + if wmsg == nil { + // ch was missing or ch is closed + return NewError("process not found") + } + if wmsg.err != nil { + return wmsg.err + } + if w != nil { + *w = wmsg.Waitmsg + } + return nil +} diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go index dfaa0374a..01b76c106 100644 --- a/src/pkg/syscall/exec_unix.go +++ b/src/pkg/syscall/exec_unix.go @@ -63,8 +63,9 @@ import ( var ForkLock sync.RWMutex -// Convert array of string to array -// of NUL-terminated byte pointer. +// StringSlicePtr is deprecated. Use SlicePtrFromStrings instead. +// If any string contains a NUL byte this function panics instead +// of returning an error. func StringSlicePtr(ss []string) []*byte { bb := make([]*byte, len(ss)+1) for i := 0; i < len(ss); i++ { @@ -74,6 +75,22 @@ func StringSlicePtr(ss []string) []*byte { return bb } +// SlicePtrFromStrings converts a slice of strings to a slice of +// pointers to NUL-terminated byte slices. If any string contains +// a NUL byte, it returns (nil, EINVAL). +func SlicePtrFromStrings(ss []string) ([]*byte, error) { + var err error + bb := make([]*byte, len(ss)+1) + for i := 0; i < len(ss); i++ { + bb[i], err = BytePtrFromString(ss[i]) + if err != nil { + return nil, err + } + } + bb[len(ss)] = nil + return bb, nil +} + func CloseOnExec(fd int) { fcntl(fd, F_SETFD, FD_CLOEXEC) } func SetNonblock(fd int, nonblocking bool) (err error) { @@ -128,9 +145,18 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) p[1] = -1 // Convert args to C form. - argv0p := StringBytePtr(argv0) - argvp := StringSlicePtr(argv) - envvp := StringSlicePtr(attr.Env) + argv0p, err := BytePtrFromString(argv0) + if err != nil { + return 0, err + } + argvp, err := SlicePtrFromStrings(argv) + if err != nil { + return 0, err + } + envvp, err := SlicePtrFromStrings(attr.Env) + if err != nil { + return 0, err + } if runtime.GOOS == "freebsd" && len(argv[0]) > len(argv0) { argvp[0] = argv0p @@ -138,11 +164,17 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) var chroot *byte if sys.Chroot != "" { - chroot = StringBytePtr(sys.Chroot) + chroot, err = BytePtrFromString(sys.Chroot) + if err != nil { + return 0, err + } } var dir *byte if attr.Dir != "" { - dir = StringBytePtr(attr.Dir) + dir, err = BytePtrFromString(attr.Dir) + if err != nil { + return 0, err + } } // Acquire the fork lock so that no other threads @@ -151,13 +183,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) ForkLock.Lock() // Allocate child status pipe close on exec. - if err = Pipe(p[0:]); err != nil { - goto error - } - if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil { - goto error - } - if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != nil { + if err = forkExecPipe(p[:]); err != nil { goto error } @@ -171,7 +197,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) // Read child error status from pipe. Close(p[1]) - n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) + n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) Close(p[0]) if err != nil || n != 0 { if n == int(unsafe.Sizeof(err1)) { @@ -215,9 +241,21 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle // Ordinary exec. func Exec(argv0 string, argv []string, envv []string) (err error) { + argv0p, err := BytePtrFromString(argv0) + if err != nil { + return err + } + argvp, err := SlicePtrFromStrings(argv) + if err != nil { + return err + } + envvp, err := SlicePtrFromStrings(envv) + if err != nil { + return err + } _, _, err1 := RawSyscall(SYS_EXECVE, - uintptr(unsafe.Pointer(StringBytePtr(argv0))), - uintptr(unsafe.Pointer(&StringSlicePtr(argv)[0])), - uintptr(unsafe.Pointer(&StringSlicePtr(envv)[0]))) + uintptr(unsafe.Pointer(argv0p)), + uintptr(unsafe.Pointer(&argvp[0])), + uintptr(unsafe.Pointer(&envvp[0]))) return Errno(err1) } diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go index 4dc4d059d..82abc0715 100644 --- a/src/pkg/syscall/exec_windows.go +++ b/src/pkg/syscall/exec_windows.go @@ -132,7 +132,10 @@ func SetNonblock(fd Handle, nonblocking bool) (err error) { // getFullPath retrieves the full path of the specified file. // Just a wrapper for Windows GetFullPathName api. func getFullPath(name string) (path string, err error) { - p := StringToUTF16Ptr(name) + p, err := UTF16PtrFromString(name) + if err != nil { + return "", err + } buf := make([]uint16, 100) n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil) if err != nil { @@ -225,8 +228,9 @@ type ProcAttr struct { } type SysProcAttr struct { - HideWindow bool - CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess + HideWindow bool + CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess + CreationFlags uint32 } var zeroProcAttr ProcAttr @@ -261,7 +265,10 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle return 0, 0, err } } - argv0p := StringToUTF16Ptr(argv0) + argv0p, err := UTF16PtrFromString(argv0) + if err != nil { + return 0, 0, err + } var cmdline string // Windows CreateProcess takes the command line as a single string: @@ -275,12 +282,18 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle var argvp *uint16 if len(cmdline) != 0 { - argvp = StringToUTF16Ptr(cmdline) + argvp, err = UTF16PtrFromString(cmdline) + if err != nil { + return 0, 0, err + } } var dirp *uint16 if len(attr.Dir) != 0 { - dirp = StringToUTF16Ptr(attr.Dir) + dirp, err = UTF16PtrFromString(attr.Dir) + if err != nil { + return 0, 0, err + } } // Acquire the fork lock so that no other threads @@ -313,7 +326,8 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle pi := new(ProcessInformation) - err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi) + flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT + err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi) if err != nil { return 0, 0, err } diff --git a/src/pkg/syscall/mkall.sh b/src/pkg/syscall/mkall.sh index 9e2d98f0d..3900a93ab 100755 --- a/src/pkg/syscall/mkall.sh +++ b/src/pkg/syscall/mkall.sh @@ -87,7 +87,8 @@ case "$1" in -syscalls) for i in zsyscall*go do - sed 1q $i | sed 's;^// ;;' | sh | gofmt >_$i && mv _$i $i + sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i + rm _$i done exit 0 ;; @@ -131,6 +132,12 @@ freebsd_amd64) mksysnum="curl -s 'http://svn.freebsd.org/base/head/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; +freebsd_arm) + mkerrors="$mkerrors" + mksyscall="./mksyscall.pl -l32 -arm" + mksysnum="curl -s 'http://svn.freebsd.org/base/head/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl" + mktypes="GOARCH=$GOARCH go tool cgo -godefs" + ;; linux_386) mkerrors="$mkerrors -m32" mksyscall="./mksyscall.pl -l32" @@ -138,8 +145,13 @@ linux_386) mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; linux_amd64) + unistd_h=$(ls -1 /usr/include/asm/unistd_64.h /usr/include/x86_64-linux-gnu/asm/unistd_64.h 2>/dev/null | head -1) + if [ "$unistd_h" = "" ]; then + echo >&2 cannot find unistd_64.h + exit 1 + fi mkerrors="$mkerrors -m64" - mksysnum="./mksysnum_linux.pl /usr/include/asm/unistd_64.h" + mksysnum="./mksysnum_linux.pl $unistd_h" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; linux_arm) diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh index 9aacec29c..e8abb6774 100755 --- a/src/pkg/syscall/mkerrors.sh +++ b/src/pkg/syscall/mkerrors.sh @@ -83,6 +83,10 @@ includes_Linux=' #include #include #include + +#ifndef MSG_FASTOPEN +#define MSG_FASTOPEN 0x20000000 +#endif ' includes_NetBSD=' @@ -104,6 +108,9 @@ includes_NetBSD=' #include #include #include + +// Needed since refers to it... +#define schedppq 1 ' includes_OpenBSD=' @@ -174,22 +181,37 @@ ccflags="$@" $2 !~ /^EQUIV_/ && $2 !~ /^EXPR_/ && $2 ~ /^E[A-Z0-9_]+$/ || + $2 ~ /^B[0-9_]+$/ || + $2 ~ /^V[A-Z0-9]+$/ || + $2 ~ /^CS[A-Z0-9]/ || + $2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ || + $2 ~ /^IGN/ || + $2 ~ /^IX(ON|ANY|OFF)$/ || + $2 ~ /^IN(LCR|PCK)$/ || + $2 ~ /(^FLU?SH)|(FLU?SH$)/ || + $2 ~ /^C(LOCAL|READ)$/ || + $2 == "BRKINT" || + $2 == "HUPCL" || + $2 == "PENDIN" || + $2 == "TOSTOP" || + $2 ~ /^PAR/ || $2 ~ /^SIG[^_]/ || + $2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ || $2 ~ /^IN_/ || $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ || $2 == "SOMAXCONN" || $2 == "NAME_MAX" || $2 == "IFNAMSIZ" || - $2 == "CTL_NET" || - $2 == "CTL_MAXNAME" || + $2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ || + $2 ~ /^SYSCTL_VERS/ || $2 ~ /^(MS|MNT)_/ || $2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ || $2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ || $2 ~ /^LINUX_REBOOT_CMD_/ || $2 ~ /^LINUX_REBOOT_MAGIC[12]$/ || $2 !~ "NLA_TYPE_MASK" && - $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ || + $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ || $2 ~ /^SIOC/ || $2 ~ /^TIOC/ || $2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ || diff --git a/src/pkg/syscall/mksyscall.pl b/src/pkg/syscall/mksyscall.pl index 09949688e..df47958ea 100755 --- a/src/pkg/syscall/mksyscall.pl +++ b/src/pkg/syscall/mksyscall.pl @@ -110,6 +110,16 @@ while(<>) { my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : ""; $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl; + # Check if err return available + my $errvar = ""; + foreach my $p (@out) { + my ($name, $type) = parseparam($p); + if($type eq "error") { + $errvar = $name; + last; + } + } + # Prepare arguments to Syscall. my @args = (); my $n = 0; @@ -117,8 +127,18 @@ while(<>) { my ($name, $type) = parseparam($p); if($type =~ /^\*/) { push @args, "uintptr(unsafe.Pointer($name))"; + } elsif($type eq "string" && $errvar ne "") { + $text .= "\tvar _p$n *byte\n"; + $text .= "\t_p$n, $errvar = BytePtrFromString($name)\n"; + $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n"; + push @args, "uintptr(unsafe.Pointer(_p$n))"; + $n++; } elsif($type eq "string") { - push @args, "uintptr(unsafe.Pointer(StringBytePtr($name)))"; + print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n"; + $text .= "\tvar _p$n *byte\n"; + $text .= "\t_p$n, _ = BytePtrFromString($name)\n"; + push @args, "uintptr(unsafe.Pointer(_p$n))"; + $n++; } elsif($type =~ /^\[\](.*)/) { # Convert slice into pointer, length. # Have to be careful not to take address of &a[0] if len == 0: @@ -237,7 +257,7 @@ while(<>) { $text .= $body; if ($plan9 && $ret[2] eq "e1") { - $text .= "\tif int(r0) == -1 {\n"; + $text .= "\tif int32(r0) == -1 {\n"; $text .= "\t\terr = e1\n"; $text .= "\t}\n"; } elsif ($do_errno) { diff --git a/src/pkg/syscall/mksyscall_windows.pl b/src/pkg/syscall/mksyscall_windows.pl index 1fb51125b..0e015cb70 100755 --- a/src/pkg/syscall/mksyscall_windows.pl +++ b/src/pkg/syscall/mksyscall_windows.pl @@ -118,7 +118,8 @@ while(<>) { } # Decide which version of api is used: ascii or unicode. - my $strconvfunc = $sysname !~ /W$/ ? "StringBytePtr" : "StringToUTF16Ptr"; + my $strconvfunc = $sysname !~ /W$/ ? "BytePtrFromString" : "UTF16PtrFromString"; + my $strconvtype = $sysname !~ /W$/ ? "*byte" : "*uint16"; # Winapi proc address variable. $vars .= "\t$sysvarname = $modvname.NewProc(\"$sysname\")\n"; @@ -133,6 +134,16 @@ while(<>) { } $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out; + # Check if err return available + my $errvar = ""; + foreach my $p (@out) { + my ($name, $type) = parseparam($p); + if($type eq "error") { + $errvar = $name; + last; + } + } + # Prepare arguments to Syscall. my @args = (); my $n = 0; @@ -141,8 +152,18 @@ while(<>) { my ($name, $type) = parseparam($p); if($type =~ /^\*/) { push @args, "uintptr(unsafe.Pointer($name))"; + } elsif($type eq "string" && $errvar ne "") { + $text .= "\tvar _p$n $strconvtype\n"; + $text .= "\t_p$n, $errvar = $strconvfunc($name)\n"; + $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n"; + push @args, "uintptr(unsafe.Pointer(_p$n))"; + $n++; } elsif($type eq "string") { - push @args, "uintptr(unsafe.Pointer($strconvfunc($name)))"; + print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n"; + $text .= "\tvar _p$n $strconvtype\n"; + $text .= "\t_p$n, _ = $strconvfunc($name)\n"; + push @args, "uintptr(unsafe.Pointer(_p$n))"; + $n++; } elsif($type =~ /^\[\](.*)/) { # Convert slice into pointer, length. # Have to be careful not to take address of &a[0] if len == 0: @@ -245,7 +266,7 @@ while(<>) { $failexpr = "!$name"; } elsif($name eq "err") { $ret[$i] = "r1"; - $failexpr = "int(r1) $failcond"; + $failexpr = "r1 $failcond"; } else { $failexpr = "$name $failcond"; } diff --git a/src/pkg/syscall/netlink_linux.go b/src/pkg/syscall/netlink_linux.go index dc0f68470..49550ea2f 100644 --- a/src/pkg/syscall/netlink_linux.go +++ b/src/pkg/syscall/netlink_linux.go @@ -6,9 +6,7 @@ package syscall -import ( - "unsafe" -) +import "unsafe" // Round the length of a netlink message up to align it properly. func nlmAlignOf(msglen int) int { @@ -21,8 +19,8 @@ func rtaAlignOf(attrlen int) int { return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1) } -// NetlinkRouteRequest represents the request message to receive -// routing and link states from the kernel. +// NetlinkRouteRequest represents a request message to receive routing +// and link states from the kernel. type NetlinkRouteRequest struct { Header NlMsghdr Data RtGenmsg @@ -30,29 +28,18 @@ type NetlinkRouteRequest struct { func (rr *NetlinkRouteRequest) toWireFormat() []byte { b := make([]byte, rr.Header.Len) - b[0] = byte(rr.Header.Len) - b[1] = byte(rr.Header.Len >> 8) - b[2] = byte(rr.Header.Len >> 16) - b[3] = byte(rr.Header.Len >> 24) - b[4] = byte(rr.Header.Type) - b[5] = byte(rr.Header.Type >> 8) - b[6] = byte(rr.Header.Flags) - b[7] = byte(rr.Header.Flags >> 8) - b[8] = byte(rr.Header.Seq) - b[9] = byte(rr.Header.Seq >> 8) - b[10] = byte(rr.Header.Seq >> 16) - b[11] = byte(rr.Header.Seq >> 24) - b[12] = byte(rr.Header.Pid) - b[13] = byte(rr.Header.Pid >> 8) - b[14] = byte(rr.Header.Pid >> 16) - b[15] = byte(rr.Header.Pid >> 24) + *(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len + *(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type + *(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags + *(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq + *(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid b[16] = byte(rr.Data.Family) return b } func newNetlinkRouteRequest(proto, seq, family int) []byte { rr := &NetlinkRouteRequest{} - rr.Header.Len = NLMSG_HDRLEN + SizeofRtGenmsg + rr.Header.Len = uint32(NLMSG_HDRLEN + SizeofRtGenmsg) rr.Header.Type = uint16(proto) rr.Header.Flags = NLM_F_DUMP | NLM_F_REQUEST rr.Header.Seq = uint32(seq) @@ -60,167 +47,131 @@ func newNetlinkRouteRequest(proto, seq, family int) []byte { return rr.toWireFormat() } -// NetlinkRIB returns routing information base, as known as RIB, -// which consists of network facility information, states and -// parameters. +// NetlinkRIB returns routing information base, as known as RIB, which +// consists of network facility information, states and parameters. func NetlinkRIB(proto, family int) ([]byte, error) { - var ( - lsanl SockaddrNetlink - tab []byte - ) - - s, e := Socket(AF_NETLINK, SOCK_RAW, 0) - if e != nil { - return nil, e + s, err := Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) + if err != nil { + return nil, err } defer Close(s) - - lsanl.Family = AF_NETLINK - e = Bind(s, &lsanl) - if e != nil { - return nil, e + lsa := &SockaddrNetlink{Family: AF_NETLINK} + if err := Bind(s, lsa); err != nil { + return nil, err } - - seq := 1 - wb := newNetlinkRouteRequest(proto, seq, family) - e = Sendto(s, wb, 0, &lsanl) - if e != nil { - return nil, e + wb := newNetlinkRouteRequest(proto, 1, family) + if err := Sendto(s, wb, 0, lsa); err != nil { + return nil, err } - + var tab []byte +done: for { - var ( - rb []byte - nr int - lsa Sockaddr - ) - - rb = make([]byte, Getpagesize()) - nr, _, e = Recvfrom(s, rb, 0) - if e != nil { - return nil, e + rb := make([]byte, Getpagesize()) + nr, _, err := Recvfrom(s, rb, 0) + if err != nil { + return nil, err } if nr < NLMSG_HDRLEN { return nil, EINVAL } rb = rb[:nr] tab = append(tab, rb...) - - msgs, _ := ParseNetlinkMessage(rb) + msgs, err := ParseNetlinkMessage(rb) + if err != nil { + return nil, err + } for _, m := range msgs { - if lsa, e = Getsockname(s); e != nil { - return nil, e + lsa, err := Getsockname(s) + if err != nil { + return nil, err } switch v := lsa.(type) { case *SockaddrNetlink: - if m.Header.Seq != uint32(seq) || m.Header.Pid != v.Pid { + if m.Header.Seq != 1 || m.Header.Pid != v.Pid { return nil, EINVAL } default: return nil, EINVAL } if m.Header.Type == NLMSG_DONE { - goto done + break done } if m.Header.Type == NLMSG_ERROR { return nil, EINVAL } } } - -done: return tab, nil } -// NetlinkMessage represents the netlink message. +// NetlinkMessage represents a netlink message. type NetlinkMessage struct { Header NlMsghdr Data []byte } -// ParseNetlinkMessage parses buf as netlink messages and returns -// the slice containing the NetlinkMessage structs. -func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, error) { - var ( - h *NlMsghdr - dbuf []byte - dlen int - e error - msgs []NetlinkMessage - ) - - for len(buf) >= NLMSG_HDRLEN { - h, dbuf, dlen, e = netlinkMessageHeaderAndData(buf) - if e != nil { - break +// ParseNetlinkMessage parses b as an array of netlink messages and +// returns the slice containing the NetlinkMessage structures. +func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) { + var msgs []NetlinkMessage + for len(b) >= NLMSG_HDRLEN { + h, dbuf, dlen, err := netlinkMessageHeaderAndData(b) + if err != nil { + return nil, err } - m := NetlinkMessage{} - m.Header = *h - m.Data = dbuf[:h.Len-NLMSG_HDRLEN] + m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]} msgs = append(msgs, m) - buf = buf[dlen:] + b = b[dlen:] } - - return msgs, e + return msgs, nil } -func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, error) { - h := (*NlMsghdr)(unsafe.Pointer(&buf[0])) - if h.Len < NLMSG_HDRLEN || int(h.Len) > len(buf) { +func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) { + h := (*NlMsghdr)(unsafe.Pointer(&b[0])) + if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(b) { return nil, nil, 0, EINVAL } - return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil + return h, b[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil } -// NetlinkRouteAttr represents the netlink route attribute. +// NetlinkRouteAttr represents a netlink route attribute. type NetlinkRouteAttr struct { Attr RtAttr Value []byte } -// ParseNetlinkRouteAttr parses msg's payload as netlink route -// attributes and returns the slice containing the NetlinkRouteAttr -// structs. -func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, error) { - var ( - buf []byte - a *RtAttr - alen int - vbuf []byte - e error - attrs []NetlinkRouteAttr - ) - - switch msg.Header.Type { +// ParseNetlinkRouteAttr parses m's payload as an array of netlink +// route attributes and returns the slice containing the +// NetlinkRouteAttr structures. +func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) { + var b []byte + switch m.Header.Type { case RTM_NEWLINK, RTM_DELLINK: - buf = msg.Data[SizeofIfInfomsg:] + b = m.Data[SizeofIfInfomsg:] case RTM_NEWADDR, RTM_DELADDR: - buf = msg.Data[SizeofIfAddrmsg:] + b = m.Data[SizeofIfAddrmsg:] case RTM_NEWROUTE, RTM_DELROUTE: - buf = msg.Data[SizeofRtMsg:] + b = m.Data[SizeofRtMsg:] default: return nil, EINVAL } - - for len(buf) >= SizeofRtAttr { - a, vbuf, alen, e = netlinkRouteAttrAndValue(buf) - if e != nil { - break + var attrs []NetlinkRouteAttr + for len(b) >= SizeofRtAttr { + a, vbuf, alen, err := netlinkRouteAttrAndValue(b) + if err != nil { + return nil, err } - ra := NetlinkRouteAttr{} - ra.Attr = *a - ra.Value = vbuf[:a.Len-SizeofRtAttr] + ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]} attrs = append(attrs, ra) - buf = buf[alen:] + b = b[alen:] } - return attrs, nil } -func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, error) { - h := (*RtAttr)(unsafe.Pointer(&buf[0])) - if h.Len < SizeofRtAttr || int(h.Len) > len(buf) { +func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) { + a := (*RtAttr)(unsafe.Pointer(&b[0])) + if int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) { return nil, nil, 0, EINVAL } - return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), nil + return a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil } diff --git a/src/pkg/syscall/passfd_test.go b/src/pkg/syscall/passfd_test.go new file mode 100644 index 000000000..079c9303b --- /dev/null +++ b/src/pkg/syscall/passfd_test.go @@ -0,0 +1,201 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin freebsd netbsd openbsd + +package syscall_test + +import ( + "flag" + "fmt" + "io/ioutil" + "net" + "os" + "os/exec" + "runtime" + "syscall" + "testing" + "time" +) + +// TestPassFD tests passing a file descriptor over a Unix socket. +// +// This test involved both a parent and child process. The parent +// process is invoked as a normal test, with "go test", which then +// runs the child process by running the current test binary with args +// "-test.run=^TestPassFD$" and an environment variable used to signal +// that the test should become the child process instead. +func TestPassFD(t *testing.T) { + if runtime.GOOS == "openbsd" { + t.Skip("issue 4956") + } + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + passFDChild() + return + } + + tempDir, err := ioutil.TempDir("", "TestPassFD") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) + if err != nil { + t.Fatalf("Socketpair: %v", err) + } + defer syscall.Close(fds[0]) + defer syscall.Close(fds[1]) + writeFile := os.NewFile(uintptr(fds[0]), "child-writes") + readFile := os.NewFile(uintptr(fds[1]), "parent-reads") + defer writeFile.Close() + defer readFile.Close() + + cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir) + cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...) + cmd.ExtraFiles = []*os.File{writeFile} + + out, err := cmd.CombinedOutput() + if len(out) > 0 || err != nil { + t.Fatalf("child process: %q, %v", out, err) + } + + c, err := net.FileConn(readFile) + if err != nil { + t.Fatalf("FileConn: %v", err) + } + defer c.Close() + + uc, ok := c.(*net.UnixConn) + if !ok { + t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c) + } + + buf := make([]byte, 32) // expect 1 byte + oob := make([]byte, 32) // expect 24 bytes + closeUnix := time.AfterFunc(5*time.Second, func() { + t.Logf("timeout reading from unix socket") + uc.Close() + }) + _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) + closeUnix.Stop() + + scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + t.Fatalf("ParseSocketControlMessage: %v", err) + } + if len(scms) != 1 { + t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms) + } + scm := scms[0] + gotFds, err := syscall.ParseUnixRights(&scm) + if err != nil { + t.Fatalf("syscall.ParseUnixRights: %v", err) + } + if len(gotFds) != 1 { + t.Fatalf("wanted 1 fd; got %#v", gotFds) + } + + f := os.NewFile(uintptr(gotFds[0]), "fd-from-child") + defer f.Close() + + got, err := ioutil.ReadAll(f) + want := "Hello from child process!\n" + if string(got) != want { + t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want) + } +} + +// passFDChild is the child process used by TestPassFD. +func passFDChild() { + defer os.Exit(0) + + // Look for our fd. It should be fd 3, but we work around an fd leak + // bug here (http://golang.org/issue/2603) to let it be elsewhere. + var uc *net.UnixConn + for fd := uintptr(3); fd <= 10; fd++ { + f := os.NewFile(fd, "unix-conn") + var ok bool + netc, _ := net.FileConn(f) + uc, ok = netc.(*net.UnixConn) + if ok { + break + } + } + if uc == nil { + fmt.Println("failed to find unix fd") + return + } + + // Make a file f to send to our parent process on uc. + // We make it in tempDir, which our parent will clean up. + flag.Parse() + tempDir := flag.Arg(0) + f, err := ioutil.TempFile(tempDir, "") + if err != nil { + fmt.Printf("TempFile: %v", err) + return + } + + f.Write([]byte("Hello from child process!\n")) + f.Seek(0, 0) + + rights := syscall.UnixRights(int(f.Fd())) + dummyByte := []byte("x") + n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil) + if err != nil { + fmt.Printf("WriteMsgUnix: %v", err) + return + } + if n != 1 || oobn != len(rights) { + fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights)) + return + } +} + +// TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage, +// and ParseUnixRights are able to successfully round-trip lists of file descriptors. +func TestUnixRightsRoundtrip(t *testing.T) { + testCases := [...][][]int{ + {{42}}, + {{1, 2}}, + {{3, 4, 5}}, + {{}}, + {{1, 2}, {3, 4, 5}, {}, {7}}, + } + for _, testCase := range testCases { + b := []byte{} + var n int + for _, fds := range testCase { + // Last assignment to n wins + n = len(b) + syscall.CmsgLen(4*len(fds)) + b = append(b, syscall.UnixRights(fds...)...) + } + // Truncate b + b = b[:n] + + scms, err := syscall.ParseSocketControlMessage(b) + if err != nil { + t.Fatalf("ParseSocketControlMessage: %v", err) + } + if len(scms) != len(testCase) { + t.Fatalf("expected %v SocketControlMessage; got scms = %#v", len(testCase), scms) + } + for i, scm := range scms { + gotFds, err := syscall.ParseUnixRights(&scm) + if err != nil { + t.Fatalf("ParseUnixRights: %v", err) + } + wantFds := testCase[i] + if len(gotFds) != len(wantFds) { + t.Fatalf("expected %v fds, got %#v", len(wantFds), gotFds) + } + for j, fd := range gotFds { + if fd != wantFds[j] { + t.Fatalf("expected fd %v, got %v", wantFds[j], fd) + } + } + } + } +} diff --git a/src/pkg/syscall/race.go b/src/pkg/syscall/race.go new file mode 100644 index 000000000..81778846f --- /dev/null +++ b/src/pkg/syscall/race.go @@ -0,0 +1,22 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race + +package syscall + +import ( + "runtime" + "unsafe" +) + +const raceenabled = true + +func raceAcquire(addr unsafe.Pointer) { + runtime.RaceAcquire(addr) +} + +func raceReleaseMerge(addr unsafe.Pointer) { + runtime.RaceReleaseMerge(addr) +} diff --git a/src/pkg/syscall/race0.go b/src/pkg/syscall/race0.go new file mode 100644 index 000000000..e94fb47af --- /dev/null +++ b/src/pkg/syscall/race0.go @@ -0,0 +1,19 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !race + +package syscall + +import ( + "unsafe" +) + +const raceenabled = false + +func raceAcquire(addr unsafe.Pointer) { +} + +func raceReleaseMerge(addr unsafe.Pointer) { +} diff --git a/src/pkg/syscall/route_bsd.go b/src/pkg/syscall/route_bsd.go index e17d976b1..62c5ce1a3 100644 --- a/src/pkg/syscall/route_bsd.go +++ b/src/pkg/syscall/route_bsd.go @@ -8,9 +8,7 @@ package syscall -import ( - "unsafe" -) +import "unsafe" // Round the length of a raw sockaddr up to align it properly. func rsaAlignOf(salen int) int { @@ -31,7 +29,6 @@ func rsaAlignOf(salen int) int { // parameters. func RouteRIB(facility, param int) ([]byte, error) { mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)} - // Find size. n := uintptr(0) if err := sysctl(mib, nil, &n, nil, 0); err != nil { @@ -40,12 +37,10 @@ func RouteRIB(facility, param int) ([]byte, error) { if n == 0 { return nil, nil } - tab := make([]byte, n) if err := sysctl(mib, &tab[0], &n, nil, 0); err != nil { return nil, err } - return tab[:n], nil } @@ -76,13 +71,12 @@ func (m *RouteMessage) sockaddr() []Sockaddr { af int sas [4]Sockaddr ) - - buf := m.Data[:] + b := m.Data[:] for i := uint(0); i < RTAX_MAX; i++ { if m.Header.Addrs&rtaRtMask&(1< 0 && j < int(rsa4.Len)-int(unsafe.Offsetof(rsa4.Addr)); j++ { sa.Addr[j] = rsa4.Addr[j] } sas[i] = sa case AF_INET6: - rsa6 := (*RawSockaddrInet6)(unsafe.Pointer(&buf[0])) + rsa6 := (*RawSockaddrInet6)(unsafe.Pointer(&b[0])) sa := new(SockaddrInet6) for j := 0; rsa6.Len > 0 && j < int(rsa6.Len)-int(unsafe.Offsetof(rsa6.Addr)); j++ { sa.Addr[j] = rsa6.Addr[j] @@ -111,9 +105,8 @@ func (m *RouteMessage) sockaddr() []Sockaddr { sas[i] = sa } } - buf = buf[rsaAlignOf(int(rsa.Len)):] + b = b[rsaAlignOf(int(rsa.Len)):] } - return sas[:] } @@ -148,13 +141,12 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { if m.Header.Addrs&rtaIfaMask == 0 { return nil } - - buf := m.Data[:] + b := m.Data[:] for i := uint(0); i < RTAX_MAX; i++ { if m.Header.Addrs&rtaIfaMask&(1<= anyMessageLen { - any := (*anyMessage)(unsafe.Pointer(&buf[0])) +// ParseRoutingMessage parses b as routing messages and returns the +// slice containing the RoutingMessage interfaces. +func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) { + for len(b) >= anyMessageLen { + any := (*anyMessage)(unsafe.Pointer(&b[0])) if any.Version != RTM_VERSION { return nil, EINVAL } - msgs = append(msgs, any.toRoutingMessage(buf)) - buf = buf[any.Msglen:] + msgs = append(msgs, any.toRoutingMessage(b)) + b = b[any.Msglen:] } return msgs, nil } diff --git a/src/pkg/syscall/route_darwin.go b/src/pkg/syscall/route_darwin.go index 410e70a13..ad2790723 100644 --- a/src/pkg/syscall/route_darwin.go +++ b/src/pkg/syscall/route_darwin.go @@ -6,36 +6,22 @@ package syscall -import ( - "unsafe" -) +import "unsafe" -func (any *anyMessage) toRoutingMessage(buf []byte) RoutingMessage { +func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage { switch any.Type { case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE: p := (*RouteMessage)(unsafe.Pointer(any)) - rtm := &RouteMessage{} - rtm.Header = p.Header - rtm.Data = buf[SizeofRtMsghdr:any.Msglen] - return rtm + return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]} case RTM_IFINFO: p := (*InterfaceMessage)(unsafe.Pointer(any)) - ifm := &InterfaceMessage{} - ifm.Header = p.Header - ifm.Data = buf[SizeofIfMsghdr:any.Msglen] - return ifm + return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]} case RTM_NEWADDR, RTM_DELADDR: p := (*InterfaceAddrMessage)(unsafe.Pointer(any)) - ifam := &InterfaceAddrMessage{} - ifam.Header = p.Header - ifam.Data = buf[SizeofIfaMsghdr:any.Msglen] - return ifam + return &InterfaceAddrMessage{Header: p.Header, Data: b[SizeofIfaMsghdr:any.Msglen]} case RTM_NEWMADDR2, RTM_DELMADDR: p := (*InterfaceMulticastAddrMessage)(unsafe.Pointer(any)) - ifmam := &InterfaceMulticastAddrMessage{} - ifmam.Header = p.Header - ifmam.Data = buf[SizeofIfmaMsghdr2:any.Msglen] - return ifmam + return &InterfaceMulticastAddrMessage{Header: p.Header, Data: b[SizeofIfmaMsghdr2:any.Msglen]} } return nil } @@ -53,13 +39,12 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { if m.Header.Addrs&rtaIfmaMask == 0 { return nil } - - buf := m.Data[:] + b := m.Data[:] for i := uint(0); i < RTAX_MAX; i++ { if m.Header.Addrs&rtaIfmaMask&(1< 0 { - sys = StringToUTF16Ptr(system) + sys, e = UTF16PtrFromString(system) + if e != nil { + return nil, "", 0, e + } } db := make([]uint16, 50) dn := uint32(len(db)) b := make([]byte, 50) n := uint32(len(b)) sid = (*SID)(unsafe.Pointer(&b[0])) - e := LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType) + e = LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType) if e != nil { if e != ERROR_INSUFFICIENT_BUFFER { return nil, "", 0, e @@ -170,7 +183,10 @@ func (sid *SID) Copy() (*SID, error) { func (sid *SID) LookupAccount(system string) (account, domain string, accType uint32, err error) { var sys *uint16 if len(system) > 0 { - sys = StringToUTF16Ptr(system) + sys, err = UTF16PtrFromString(system) + if err != nil { + return "", "", 0, err + } } b := make([]uint16, 50) n := uint32(len(b)) diff --git a/src/pkg/syscall/sockcmsg_linux.go b/src/pkg/syscall/sockcmsg_linux.go index 0b4caa1d0..a2e26a1f4 100644 --- a/src/pkg/syscall/sockcmsg_linux.go +++ b/src/pkg/syscall/sockcmsg_linux.go @@ -6,33 +6,31 @@ package syscall -import ( - "unsafe" -) +import "unsafe" // UnixCredentials encodes credentials into a socket control message // for sending to another process. This can be used for // authentication. func UnixCredentials(ucred *Ucred) []byte { - buf := make([]byte, CmsgSpace(SizeofUcred)) - cmsg := (*Cmsghdr)(unsafe.Pointer(&buf[0])) - cmsg.Level = SOL_SOCKET - cmsg.Type = SCM_CREDENTIALS - cmsg.SetLen(CmsgLen(SizeofUcred)) - *((*Ucred)(cmsgData(cmsg))) = *ucred - return buf + b := make([]byte, CmsgSpace(SizeofUcred)) + h := (*Cmsghdr)(unsafe.Pointer(&b[0])) + h.Level = SOL_SOCKET + h.Type = SCM_CREDENTIALS + h.SetLen(CmsgLen(SizeofUcred)) + *((*Ucred)(cmsgData(h))) = *ucred + return b } // ParseUnixCredentials decodes a socket control message that contains // credentials in a Ucred structure. To receive such a message, the // SO_PASSCRED option must be enabled on the socket. -func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, error) { - if msg.Header.Level != SOL_SOCKET { +func ParseUnixCredentials(m *SocketControlMessage) (*Ucred, error) { + if m.Header.Level != SOL_SOCKET { return nil, EINVAL } - if msg.Header.Type != SCM_CREDENTIALS { + if m.Header.Type != SCM_CREDENTIALS { return nil, EINVAL } - ucred := *(*Ucred)(unsafe.Pointer(&msg.Data[0])) + ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0])) return &ucred, nil } diff --git a/src/pkg/syscall/sockcmsg_unix.go b/src/pkg/syscall/sockcmsg_unix.go index d279decb6..bc7cf2098 100644 --- a/src/pkg/syscall/sockcmsg_unix.go +++ b/src/pkg/syscall/sockcmsg_unix.go @@ -8,11 +8,9 @@ package syscall -import ( - "unsafe" -) +import "unsafe" -// Round the length of a raw sockaddr up to align it propery. +// Round the length of a raw sockaddr up to align it properly. func cmsgAlignOf(salen int) int { salign := sizeofPtr // NOTE: It seems like 64-bit Darwin kernel still requires 32-bit @@ -20,9 +18,6 @@ func cmsgAlignOf(salen int) int { if darwinAMD64 { salign = 4 } - if salen == 0 { - return salign - } return (salen + salign - 1) & ^(salign - 1) } @@ -38,77 +33,70 @@ func CmsgSpace(datalen int) int { return cmsgAlignOf(SizeofCmsghdr) + cmsgAlignOf(datalen) } -func cmsgData(cmsg *Cmsghdr) unsafe.Pointer { - return unsafe.Pointer(uintptr(unsafe.Pointer(cmsg)) + SizeofCmsghdr) +func cmsgData(h *Cmsghdr) unsafe.Pointer { + return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr))) } +// SocketControlMessage represents a socket control message. type SocketControlMessage struct { Header Cmsghdr Data []byte } -func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, error) { - var ( - h *Cmsghdr - dbuf []byte - e error - cmsgs []SocketControlMessage - ) - - for len(buf) >= CmsgLen(0) { - h, dbuf, e = socketControlMessageHeaderAndData(buf) - if e != nil { - break +// ParseSocketControlMessage parses b as an array of socket control +// messages. +func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) { + var msgs []SocketControlMessage + i := 0 + for i+CmsgLen(0) <= len(b) { + h, dbuf, err := socketControlMessageHeaderAndData(b[i:]) + if err != nil { + return nil, err } - m := SocketControlMessage{} - m.Header = *h - m.Data = dbuf[:int(h.Len)-cmsgAlignOf(SizeofCmsghdr)] - cmsgs = append(cmsgs, m) - buf = buf[cmsgAlignOf(int(h.Len)):] + m := SocketControlMessage{Header: *h, Data: dbuf} + msgs = append(msgs, m) + i += cmsgAlignOf(int(h.Len)) } - - return cmsgs, e + return msgs, nil } -func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, error) { - h := (*Cmsghdr)(unsafe.Pointer(&buf[0])) - if h.Len < SizeofCmsghdr || int(h.Len) > len(buf) { +func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) { + h := (*Cmsghdr)(unsafe.Pointer(&b[0])) + if h.Len < SizeofCmsghdr || int(h.Len) > len(b) { return nil, nil, EINVAL } - return h, buf[cmsgAlignOf(SizeofCmsghdr):], nil + return h, b[cmsgAlignOf(SizeofCmsghdr):h.Len], nil } // UnixRights encodes a set of open file descriptors into a socket // control message for sending to another process. func UnixRights(fds ...int) []byte { datalen := len(fds) * 4 - buf := make([]byte, CmsgSpace(datalen)) - cmsg := (*Cmsghdr)(unsafe.Pointer(&buf[0])) - cmsg.Level = SOL_SOCKET - cmsg.Type = SCM_RIGHTS - cmsg.SetLen(CmsgLen(datalen)) - - data := uintptr(cmsgData(cmsg)) + b := make([]byte, CmsgSpace(datalen)) + h := (*Cmsghdr)(unsafe.Pointer(&b[0])) + h.Level = SOL_SOCKET + h.Type = SCM_RIGHTS + h.SetLen(CmsgLen(datalen)) + data := uintptr(cmsgData(h)) for _, fd := range fds { *(*int32)(unsafe.Pointer(data)) = int32(fd) data += 4 } - - return buf + return b } // ParseUnixRights decodes a socket control message that contains an // integer array of open file descriptors from another process. -func ParseUnixRights(msg *SocketControlMessage) ([]int, error) { - if msg.Header.Level != SOL_SOCKET { +func ParseUnixRights(m *SocketControlMessage) ([]int, error) { + if m.Header.Level != SOL_SOCKET { return nil, EINVAL } - if msg.Header.Type != SCM_RIGHTS { + if m.Header.Type != SCM_RIGHTS { return nil, EINVAL } - fds := make([]int, len(msg.Data)>>2) - for i, j := 0, 0; i < len(msg.Data); i += 4 { - fds[j] = int(*(*int32)(unsafe.Pointer(&msg.Data[i]))) + fds := make([]int, len(m.Data)>>2) + for i, j := 0, 0; i < len(m.Data); i += 4 { + fds[j] = int(*(*int32)(unsafe.Pointer(&m.Data[i]))) j++ } return fds, nil diff --git a/src/pkg/syscall/syscall.go b/src/pkg/syscall/syscall.go index 335559fc3..f7473fd5a 100644 --- a/src/pkg/syscall/syscall.go +++ b/src/pkg/syscall/syscall.go @@ -3,10 +3,15 @@ // license that can be found in the LICENSE file. // Package syscall contains an interface to the low-level operating system -// primitives. The details vary depending on the underlying system. -// Its primary use is inside other packages that provide a more portable -// interface to the system, such as "os", "time" and "net". Use those -// packages rather than this one if you can. +// primitives. The details vary depending on the underlying system, and +// by default, godoc will display the syscall documentation for the current +// system. If you want godoc to display syscall documentation for another +// system, set $GOOS and $GOARCH to the desired system. For example, if +// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS +// to freebsd and $GOARCH to arm. +// The primary use of syscall is inside other packages that provide a more +// portable interface to the system, such as "os", "time" and "net". Use +// those packages rather than this one if you can. // For details of the functions and data types in this package consult // the manuals for the appropriate operating system. // These calls return err == nil to indicate success; otherwise @@ -14,18 +19,47 @@ // On most systems, that error has type syscall.Errno. package syscall -// StringByteSlice returns a NUL-terminated slice of bytes -// containing the text of s. +// StringByteSlice is deprecated. Use ByteSliceFromString instead. +// If s contains a NUL byte this function panics instead of +// returning an error. func StringByteSlice(s string) []byte { + a, err := ByteSliceFromString(s) + if err != nil { + panic("syscall: string with NUL passed to StringByteSlice") + } + return a +} + +// ByteSliceFromString returns a NUL-terminated slice of bytes +// containing the text of s. If s contains a NUL byte at any +// location, it returns (nil, EINVAL). +func ByteSliceFromString(s string) ([]byte, error) { + for i := 0; i < len(s); i++ { + if s[i] == 0 { + return nil, EINVAL + } + } a := make([]byte, len(s)+1) copy(a, s) - return a + return a, nil } -// StringBytePtr returns a pointer to a NUL-terminated array of bytes -// containing the text of s. +// StringBytePtr is deprecated. Use BytePtrFromString instead. +// If s contains a NUL byte this function panics instead of +// returning an error. func StringBytePtr(s string) *byte { return &StringByteSlice(s)[0] } +// BytePtrFromString returns a pointer to a NUL-terminated array of +// bytes containing the text of s. If s contains a NUL byte at any +// location, it returns (nil, EINVAL). +func BytePtrFromString(s string) (*byte, error) { + a, err := ByteSliceFromString(s) + if err != nil { + return nil, err + } + return &a[0], nil +} + // Single-word zero for use when we need a valid pointer to 0 bytes. // See mksyscall.pl. var _zero uintptr diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go index c1a822aa1..a1e0d153f 100644 --- a/src/pkg/syscall/syscall_bsd.go +++ b/src/pkg/syscall/syscall_bsd.go @@ -20,6 +20,7 @@ import ( /* * Pseudo-system calls */ + // The const provides a compile-time constant so clients // can adjust to whether there is a working Getwd and avoid // even linking this function into the binary. See ../os/getwd.go. @@ -304,6 +305,14 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) { if err != nil { return } + if runtime.GOOS == "darwin" && len == 0 { + // Accepted socket has no address. + // This is likely due to a bug in xnu kernels, + // where instead of ECONNABORTED error socket + // is accepted, but has no address. + Close(nfd) + return 0, nil, ECONNABORTED + } sa, err = anyToSockaddr(&rsa) if err != nil { Close(nfd) @@ -354,10 +363,15 @@ func Socket(domain, typ, proto int) (fd int, err error) { return } -//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (err error) +//sysnb socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) func Socketpair(domain, typ, proto int) (fd [2]int, err error) { - err = socketpair(domain, typ, proto, &fd) + var fdx [2]int32 + err = socketpair(domain, typ, proto, &fdx) + if err == nil { + fd[0] = int(fdx[0]) + fd[1] = int(fdx[1]) + } return } @@ -437,7 +451,9 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil { return } - from, err = anyToSockaddr(&rsa) + if rsa.Addr.Family != AF_UNSPEC { + from, err = anyToSockaddr(&rsa) + } return } @@ -553,16 +569,7 @@ func Sysctl(name string) (value string, err error) { return "", err } if n == 0 { - // TODO(jsing): Remove after OpenBSD 5.2 release. - // Work around a bug that was fixed after OpenBSD 5.0. - // The length for kern.hostname and kern.domainname is always - // returned as 0 when a nil value is passed for oldp. - if runtime.GOOS == "openbsd" && (name == "kern.hostname" || name == "kern.domainname") { - // MAXHOSTNAMELEN - n = 256 - } else { - return "", nil - } + return "", nil } // Read into buffer of that size. @@ -605,6 +612,21 @@ func Utimes(path string, tv []Timeval) (err error) { return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))) } +func UtimesNano(path string, ts []Timespec) error { + // TODO: The BSDs can do utimensat with SYS_UTIMENSAT but it + // isn't supported by darwin so this uses utimes instead + if len(ts) != 2 { + return EINVAL + } + // Not as efficient as it could be because Timespec and + // Timeval have different types in the different OSes + tv := [2]Timeval{ + NsecToTimeval(TimespecToNsec(ts[0])), + NsecToTimeval(TimespecToNsec(ts[1])), + } + return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))) +} + //sys futimes(fd int, timeval *[2]Timeval) (err error) func Futimes(fd int, tv []Timeval) (err error) { if len(tv) != 2 { diff --git a/src/pkg/syscall/syscall_darwin.go b/src/pkg/syscall/syscall_darwin.go index c712f94d4..7fe406aaa 100644 --- a/src/pkg/syscall/syscall_darwin.go +++ b/src/pkg/syscall/syscall_darwin.go @@ -41,7 +41,10 @@ func nametomib(name string) (mib []_C_int, err error) { n := uintptr(CTL_MAXNAME) * siz p := (*byte)(unsafe.Pointer(&buf[0])) - bytes := StringByteSlice(name) + bytes, err := ByteSliceFromString(name) + if err != nil { + return nil, err + } // Magic sysctl: "setting" 0.3 to a string name // lets you read back the array of integers form. @@ -94,7 +97,7 @@ func Pipe(p []int) (err error) { } // TODO -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { return -1, ENOSYS } @@ -159,7 +162,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1) //sys Pathconf(path string, name int) (val int, err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error) //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) -//sys Read(fd int, p []byte) (n int, err error) +//sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) //sys Revoke(path string) (err error) @@ -188,11 +191,11 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1) //sys Undelete(path string) (err error) //sys Unlink(path string) (err error) //sys Unmount(path string, flags int) (err error) -//sys Write(fd int, p []byte) (n int, err error) +//sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) -//sys read(fd int, buf *byte, nbuf int) (n int, err error) -//sys write(fd int, buf *byte, nbuf int) (n int, err error) +//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ +//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE /* * Unimplemented @@ -331,7 +334,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1) // __pthread_canceled // __semwait_signal // Proc_info -// Sendfile +// sendfile // Stat64_extended // Lstat64_extended // Fstat64_extended diff --git a/src/pkg/syscall/syscall_freebsd.go b/src/pkg/syscall/syscall_freebsd.go index 6556ea8de..c7ffe223e 100644 --- a/src/pkg/syscall/syscall_freebsd.go +++ b/src/pkg/syscall/syscall_freebsd.go @@ -41,7 +41,10 @@ func nametomib(name string) (mib []_C_int, err error) { n := uintptr(CTL_MAXNAME) * siz p := (*byte)(unsafe.Pointer(&buf[0])) - bytes := StringByteSlice(name) + bytes, err := ByteSliceFromString(name) + if err != nil { + return nil, err + } // Magic sysctl: "setting" 0.3 to a string name // lets you read back the array of integers form. @@ -89,11 +92,6 @@ func Pipe(p []int) (err error) { return } -// TODO -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { - return -1, ENOSYS -} - func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, error) { var value IPMreqn vallen := _Socklen(SizeofIPMreqn) @@ -160,7 +158,7 @@ func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) { //sys Pathconf(path string, name int) (val int, err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error) //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) -//sys Read(fd int, p []byte) (n int, err error) +//sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) //sys Revoke(path string) (err error) @@ -188,11 +186,11 @@ func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) { //sys Undelete(path string) (err error) //sys Unlink(path string) (err error) //sys Unmount(path string, flags int) (err error) -//sys Write(fd int, p []byte) (n int, err error) +//sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) -//sys read(fd int, buf *byte, nbuf int) (n int, err error) -//sys write(fd int, buf *byte, nbuf int) (n int, err error) +//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ +//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE /* * Unimplemented @@ -331,7 +329,6 @@ func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) { // __pthread_canceled // __semwait_signal // Proc_info -// Sendfile // Stat64_extended // Lstat64_extended // Fstat64_extended diff --git a/src/pkg/syscall/syscall_freebsd_386.go b/src/pkg/syscall/syscall_freebsd_386.go index fa322c596..ebd3d4c9c 100644 --- a/src/pkg/syscall/syscall_freebsd_386.go +++ b/src/pkg/syscall/syscall_freebsd_386.go @@ -4,6 +4,8 @@ package syscall +import "unsafe" + func Getpagesize() int { return 4096 } func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } @@ -41,4 +43,16 @@ func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + var writtenOut uint64 = 0 + _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) + + written = int(writtenOut) + + if e1 != 0 { + err = e1 + } + return +} + func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // sic diff --git a/src/pkg/syscall/syscall_freebsd_amd64.go b/src/pkg/syscall/syscall_freebsd_amd64.go index 8c1ddf6db..70c2ffb03 100644 --- a/src/pkg/syscall/syscall_freebsd_amd64.go +++ b/src/pkg/syscall/syscall_freebsd_amd64.go @@ -4,6 +4,8 @@ package syscall +import "unsafe" + func Getpagesize() int { return 4096 } func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } @@ -40,3 +42,17 @@ func (msghdr *Msghdr) SetControllen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } + +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + var writtenOut uint64 = 0 + _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) + + written = int(writtenOut) + + if e1 != 0 { + err = e1 + } + return +} + +func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) diff --git a/src/pkg/syscall/syscall_freebsd_arm.go b/src/pkg/syscall/syscall_freebsd_arm.go new file mode 100644 index 000000000..ab72871dd --- /dev/null +++ b/src/pkg/syscall/syscall_freebsd_arm.go @@ -0,0 +1,58 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +import "unsafe" + +func Getpagesize() int { return 4096 } + +func TimespecToNsec(ts Timespec) int64 { return ts.Sec*1e9 + int64(ts.Nsec) } + +func NsecToTimespec(nsec int64) (ts Timespec) { + ts.Sec = nsec / 1e9 + ts.Nsec = int32(nsec % 1e9) + return +} + +func TimevalToNsec(tv Timeval) int64 { return tv.Sec*1e9 + int64(tv.Usec)*1e3 } + +func NsecToTimeval(nsec int64) (tv Timeval) { + nsec += 999 // round up to microsecond + tv.Usec = int32(nsec % 1e9 / 1e3) + tv.Sec = nsec / 1e9 + return +} + +func SetKevent(k *Kevent_t, fd, mode, flags int) { + k.Ident = uint32(fd) + k.Filter = int16(mode) + k.Flags = uint16(flags) +} + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint32(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} + +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + var writtenOut uint64 = 0 + _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) + + written = int(writtenOut) + + if e1 != 0 { + err = e1 + } + return +} + +func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // sic diff --git a/src/pkg/syscall/syscall_linux.go b/src/pkg/syscall/syscall_linux.go index 89fb68192..4f458be73 100644 --- a/src/pkg/syscall/syscall_linux.go +++ b/src/pkg/syscall/syscall_linux.go @@ -18,16 +18,19 @@ import "unsafe" */ //sys open(path string, mode int, perm uint32) (fd int, err error) + func Open(path string, mode int, perm uint32) (fd int, err error) { return open(path, mode|O_LARGEFILE, perm) } //sys openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) + func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { return openat(dirfd, path, flags|O_LARGEFILE, mode) } //sysnb pipe(p *[2]_C_int) (err error) + func Pipe(p []int) (err error) { if len(p) != 2 { return EINVAL @@ -39,7 +42,21 @@ func Pipe(p []int) (err error) { return } +//sysnb pipe2(p *[2]_C_int, flags int) (err error) + +func Pipe2(p []int, flags int) (err error) { + if len(p) != 2 { + return EINVAL + } + var pp [2]_C_int + err = pipe2(&pp, flags) + p[0] = int(pp[0]) + p[1] = int(pp[1]) + return +} + //sys utimes(path string, times *[2]Timeval) (err error) + func Utimes(path string, tv []Timeval) (err error) { if len(tv) != 2 { return EINVAL @@ -47,12 +64,37 @@ func Utimes(path string, tv []Timeval) (err error) { return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))) } +//sys utimensat(dirfd int, path string, times *[2]Timespec) (err error) + +func UtimesNano(path string, ts []Timespec) (err error) { + if len(ts) != 2 { + return EINVAL + } + err = utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0]))) + if err != ENOSYS { + return err + } + // If the utimensat syscall isn't available (utimensat was added to Linux + // in 2.6.22, Released, 8 July 2007) then fall back to utimes + var tv [2]Timeval + for i := 0; i < 2; i++ { + tv[i].Sec = ts[i].Sec + tv[i].Usec = ts[i].Nsec / 1000 + } + return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))) +} + //sys futimesat(dirfd int, path *byte, times *[2]Timeval) (err error) + func Futimesat(dirfd int, path string, tv []Timeval) (err error) { if len(tv) != 2 { return EINVAL } - return futimesat(dirfd, StringBytePtr(path), (*[2]Timeval)(unsafe.Pointer(&tv[0]))) + pathp, err := BytePtrFromString(path) + if err != nil { + return err + } + return futimesat(dirfd, pathp, (*[2]Timeval)(unsafe.Pointer(&tv[0]))) } func Futimes(fd int, tv []Timeval) (err error) { @@ -64,6 +106,7 @@ func Futimes(fd int, tv []Timeval) (err error) { const ImplementsGetwd = true //sys Getcwd(buf []byte) (n int, err error) + func Getwd() (wd string, err error) { var buf [PathMax]byte n, err := Getcwd(buf[0:]) @@ -173,6 +216,7 @@ func (w WaitStatus) TrapCause() int { } //sys wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) + func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { var status _C_int wpid, err = wait4(pid, &status, options, rusage) @@ -244,7 +288,7 @@ type SockaddrUnix struct { func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, error) { name := sa.Name n := len(name) - if n >= len(sa.raw.Path) || n == 0 { + if n >= len(sa.raw.Path) { return 0, 0, EINVAL } sa.raw.Family = AF_UNIX @@ -252,7 +296,10 @@ func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, error) { sa.raw.Path[i] = int8(name[i]) } // length is family (uint16), name, NUL. - sl := 2 + _Socklen(n) + 1 + sl := _Socklen(2) + if n > 0 { + sl += _Socklen(n) + 1 + } if sa.raw.Path[0] == '@' { sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. @@ -392,6 +439,21 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) { return } +func Accept4(fd int, flags int) (nfd int, sa Sockaddr, err error) { + var rsa RawSockaddrAny + var len _Socklen = SizeofSockaddrAny + nfd, err = accept4(fd, &rsa, &len, flags) + if err != nil { + return + } + sa, err = anyToSockaddr(&rsa) + if err != nil { + Close(nfd) + nfd = 0 + } + return +} + func Getsockname(fd int) (sa Sockaddr, err error) { var rsa RawSockaddrAny var len _Socklen = SizeofSockaddrAny @@ -435,7 +497,12 @@ func Socket(domain, typ, proto int) (fd int, err error) { } func Socketpair(domain, typ, proto int) (fd [2]int, err error) { - err = socketpair(domain, typ, proto, &fd) + var fdx [2]int32 + err = socketpair(domain, typ, proto, &fdx) + if err == nil { + fd[0] = int(fdx[0]) + fd[1] = int(fdx[1]) + } return } @@ -473,6 +540,13 @@ func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) { return &value, err } +func GetsockoptUcred(fd, level, opt int) (*Ucred, error) { + var value Ucred + vallen := _Socklen(SizeofUcred) + err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) + return &value, err +} + func SetsockoptInt(fd, level, opt int, value int) (err error) { var n = int32(value) return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4) @@ -512,7 +586,9 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil { return } - from, err = anyToSockaddr(&rsa) + if rsa.Addr.Family != AF_UNSPEC { + from, err = anyToSockaddr(&rsa) + } return } @@ -731,6 +807,10 @@ func PtraceCont(pid int, signal int) (err error) { return ptrace(PTRACE_CONT, pid, 0, uintptr(signal)) } +func PtraceSyscall(pid int, signal int) (err error) { + return ptrace(PTRACE_SYSCALL, pid, 0, uintptr(signal)) +} + func PtraceSingleStep(pid int) (err error) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) } func PtraceAttach(pid int) (err error) { return ptrace(PTRACE_ATTACH, pid, 0, 0) } @@ -738,6 +818,7 @@ func PtraceAttach(pid int) (err error) { return ptrace(PTRACE_ATTACH, pid, 0, 0) func PtraceDetach(pid int) (err error) { return ptrace(PTRACE_DETACH, pid, 0, 0) } //sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) + func Reboot(cmd int) (err error) { return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "") } @@ -777,13 +858,18 @@ func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, } //sys mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) + func Mount(source string, target string, fstype string, flags uintptr, data string) (err error) { // Certain file systems get rather angry and EINVAL if you give // them an empty string of data, rather than NULL. if data == "" { return mount(source, target, fstype, flags, nil) } - return mount(source, target, fstype, flags, StringBytePtr(data)) + datap, err := BytePtrFromString(data) + if err != nil { + return err + } + return mount(source, target, fstype, flags, datap) } // Sendto @@ -823,9 +909,9 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri //sysnb Getpgrp() (pid int) //sysnb Getpid() (pid int) //sysnb Getppid() (ppid int) -//sysnb Getrlimit(resource int, rlim *Rlimit) (err error) //sysnb Getrusage(who int, rusage *Rusage) (err error) //sysnb Gettid() (tid int) +//sys Getxattr(path string, attr string, dest []byte) (sz int, err error) //sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) //sysnb InotifyInit() (fd int, err error) //sysnb InotifyInit1(flags int) (fd int, err error) @@ -833,6 +919,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri //sysnb Kill(pid int, sig Signal) (err error) //sys Klogctl(typ int, buf []byte) (n int, err error) = SYS_SYSLOG //sys Link(oldpath string, newpath string) (err error) +//sys Listxattr(path string, dest []byte) (sz int, err error) //sys Mkdir(path string, mode uint32) (err error) //sys Mkdirat(dirfd int, path string, mode uint32) (err error) //sys Mknod(path string, mode uint32, dev int) (err error) @@ -840,18 +927,20 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Pause() (err error) //sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT -//sys Read(fd int, p []byte) (n int, err error) +//sysnb prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) = SYS_PRLIMIT64 +//sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) +//sys Removexattr(path string, attr string) (err error) //sys Rename(oldpath string, newpath string) (err error) //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Rmdir(path string) (err error) //sys Setdomainname(p []byte) (err error) //sys Sethostname(p []byte) (err error) //sysnb Setpgid(pid int, pgid int) (err error) -//sysnb Setrlimit(resource int, rlim *Rlimit) (err error) //sysnb Setsid() (pid int, err error) //sysnb Settimeofday(tv *Timeval) (err error) //sysnb Setuid(uid int) (err error) +//sys Setxattr(path string, attr string, data []byte, flags int) (err error) //sys Symlink(oldpath string, newpath string) (err error) //sys Sync() //sysnb Sysinfo(info *Sysinfo_t) (err error) @@ -866,10 +955,10 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri //sys Unshare(flags int) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error) //sys Utime(path string, buf *Utimbuf) (err error) -//sys Write(fd int, p []byte) (n int, err error) +//sys write(fd int, p []byte) (n int, err error) //sys exitThread(code int) (err error) = SYS_EXIT -//sys read(fd int, p *byte, np int) (n int, err error) -//sys write(fd int, p *byte, np int) (n int, err error) +//sys readlen(fd int, p *byte, np int) (n int, err error) = SYS_READ +//sys writelen(fd int, p *byte, np int) (n int, err error) = SYS_WRITE // mmap varies by architecture; see syscall_linux_*.go. //sys munmap(addr uintptr, length uintptr) (err error) @@ -931,7 +1020,6 @@ func Munmap(b []byte) (err error) { // Getitimer // Getpmsg // Getpriority -// Getxattr // IoCancel // IoDestroy // IoGetevents @@ -943,7 +1031,6 @@ func Munmap(b []byte) (err error) { // KexecLoad // Keyctl // Lgetxattr -// Listxattr // Llistxattr // LookupDcookie // Lremovexattr @@ -981,7 +1068,6 @@ func Munmap(b []byte) (err error) { // Readahead // Readv // RemapFilePages -// Removexattr // RequestKey // RestartSyscall // RtSigaction @@ -1010,7 +1096,6 @@ func Munmap(b []byte) (err error) { // SetThreadArea // SetTidAddress // Setpriority -// Setxattr // Shmat // Shmctl // Shmdt @@ -1036,5 +1121,4 @@ func Munmap(b []byte) (err error) { // Vmsplice // Vserver // Waitid -// Writev // _Sysctl diff --git a/src/pkg/syscall/syscall_linux_386.go b/src/pkg/syscall/syscall_linux_386.go index b833db8a7..a0ded43dc 100644 --- a/src/pkg/syscall/syscall_linux_386.go +++ b/src/pkg/syscall/syscall_linux_386.go @@ -41,7 +41,7 @@ func NsecToTimeval(nsec int64) (tv Timeval) { //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 //sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 -//sys Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 +//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 //sys Setfsgid(gid int) (err error) = SYS_SETFSGID32 //sys Setfsuid(uid int) (err error) = SYS_SETFSUID32 //sysnb Setgid(gid int) (err error) = SYS_SETGID32 @@ -67,6 +67,69 @@ func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int6 return mmap2(addr, length, prot, flags, fd, page) } +type rlimit32 struct { + Cur uint32 + Max uint32 +} + +//sysnb getrlimit(resource int, rlim *rlimit32) (err error) = SYS_GETRLIMIT + +const rlimInf32 = ^uint32(0) +const rlimInf64 = ^uint64(0) + +func Getrlimit(resource int, rlim *Rlimit) (err error) { + err = prlimit(0, resource, rlim, nil) + if err != ENOSYS { + return err + } + + rl := rlimit32{} + err = getrlimit(resource, &rl) + if err != nil { + return + } + + if rl.Cur == rlimInf32 { + rlim.Cur = rlimInf64 + } else { + rlim.Cur = uint64(rl.Cur) + } + + if rl.Max == rlimInf32 { + rlim.Max = rlimInf64 + } else { + rlim.Max = uint64(rl.Max) + } + return +} + +//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT + +func Setrlimit(resource int, rlim *Rlimit) (err error) { + err = prlimit(0, resource, nil, rlim) + if err != ENOSYS { + return err + } + + rl := rlimit32{} + if rlim.Cur == rlimInf64 { + rl.Cur = rlimInf32 + } else if rlim.Cur < uint64(rlimInf32) { + rl.Cur = uint32(rlim.Cur) + } else { + return EINVAL + } + if rlim.Max == rlimInf64 { + rl.Max = rlimInf32 + } else if rlim.Max < uint64(rlimInf32) { + rl.Max = uint32(rlim.Max) + } else { + return EINVAL + } + + return setrlimit(resource, &rl) +} + // Underlying system call writes to newoffset via pointer. // Implemented in assembly to avoid allocation. func Seek(fd int, offset int64, whence int) (newoffset int64, err error) @@ -101,6 +164,9 @@ const ( _GETSOCKOPT = 15 _SENDMSG = 16 _RECVMSG = 17 + _ACCEPT4 = 18 + _RECVMMSG = 19 + _SENDMMSG = 20 ) func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err Errno) @@ -114,6 +180,14 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { return } +func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { + fd, e := socketcall(_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) + if e != 0 { + err = e + } + return +} + func getsockname(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { _, e := rawsocketcall(_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0) if e != 0 { @@ -130,7 +204,7 @@ func getpeername(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { return } -func socketpair(domain int, typ int, flags int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, flags int, fd *[2]int32) (err error) { _, e := rawsocketcall(_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd)), 0, 0) if e != 0 { err = e @@ -243,7 +317,11 @@ func Fstatfs(fd int, buf *Statfs_t) (err error) { } func Statfs(path string, buf *Statfs_t) (err error) { - _, _, e := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf))) + pathp, err := BytePtrFromString(path) + if err != nil { + return err + } + _, _, e := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(pathp)), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf))) if e != 0 { err = e } diff --git a/src/pkg/syscall/syscall_linux_amd64.go b/src/pkg/syscall/syscall_linux_amd64.go index 18b36b78e..f4b73b20e 100644 --- a/src/pkg/syscall/syscall_linux_amd64.go +++ b/src/pkg/syscall/syscall_linux_amd64.go @@ -12,6 +12,7 @@ package syscall //sysnb Getegid() (egid int) //sysnb Geteuid() (euid int) //sysnb Getgid() (gid int) +//sysnb Getrlimit(resource int, rlim *Rlimit) (err error) //sysnb Getuid() (uid int) //sys Ioperm(from int, num int, on int) (err error) //sys Iopl(level int) (err error) @@ -22,13 +23,14 @@ package syscall //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) -//sys Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) +//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) //sys Setfsgid(gid int) (err error) //sys Setfsuid(uid int) (err error) //sysnb Setgid(gid int) (err error) //sysnb Setregid(rgid int, egid int) (err error) //sysnb Setresgid(rgid int, egid int, sgid int) (err error) //sysnb Setresuid(ruid int, euid int, suid int) (err error) +//sysnb Setrlimit(resource int, rlim *Rlimit) (err error) //sysnb Setreuid(ruid int, euid int) (err error) //sys Shutdown(fd int, how int) (err error) //sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) @@ -37,6 +39,7 @@ package syscall //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) //sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) +//sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr uintptr, addrlen _Socklen) (err error) //sys connect(s int, addr uintptr, addrlen _Socklen) (err error) //sysnb getgroups(n int, list *_Gid_t) (nn int, err error) @@ -44,7 +47,7 @@ package syscall //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) //sys setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (err error) //sysnb socket(domain int, typ int, proto int) (fd int, err error) -//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (err error) +//sysnb socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) //sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) //sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) diff --git a/src/pkg/syscall/syscall_linux_arm.go b/src/pkg/syscall/syscall_linux_arm.go index 2f2907945..7839d5288 100644 --- a/src/pkg/syscall/syscall_linux_arm.go +++ b/src/pkg/syscall/syscall_linux_arm.go @@ -4,6 +4,8 @@ package syscall +import "unsafe" + func Getpagesize() int { return 4096 } func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } @@ -26,6 +28,7 @@ func NsecToTimeval(nsec int64) (tv Timeval) { func Seek(fd int, offset int64, whence int) (newoffset int64, err error) //sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) +//sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr uintptr, addrlen _Socklen) (err error) //sys connect(s int, addr uintptr, addrlen _Socklen) (err error) //sysnb getgroups(n int, list *_Gid_t) (nn int, err error) = SYS_GETGROUPS32 @@ -37,7 +40,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) //sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) //sys sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error) -//sysnb socketpair(domain int, typ int, flags int, fd *[2]int) (err error) +//sysnb socketpair(domain int, typ int, flags int, fd *[2]int32) (err error) //sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) //sys sendmsg(s int, msg *Msghdr, flags int) (err error) @@ -46,7 +49,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) //sys Chown(path string, uid int, gid int) (err error) = SYS_CHOWN32 //sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32 //sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64 -//sys Fstatfs(fd int, buf *Statfs_t) (err error) = SYS_FSTATFS64 //sysnb Getegid() (egid int) = SYS_GETEGID32 //sysnb Geteuid() (euid int) = SYS_GETEUID32 //sysnb Getgid() (gid int) = SYS_GETGID32 @@ -54,7 +56,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) //sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32 //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 -//sys Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 +//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT //sys Setfsgid(gid int) (err error) = SYS_SETFSGID32 //sys Setfsuid(uid int) (err error) = SYS_SETFSUID32 @@ -66,7 +68,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) //sys Shutdown(fd int, how int) (err error) //sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error) //sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64 -//sys Statfs(path string, buf *Statfs_t) (err error) = SYS_STATFS64 // Vsyscalls on amd64. //sysnb Gettimeofday(tv *Timeval) (err error) @@ -79,6 +80,26 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) //sys mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) +func Fstatfs(fd int, buf *Statfs_t) (err error) { + _, _, e := Syscall(SYS_FSTATFS64, uintptr(fd), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf))) + if e != 0 { + err = e + } + return +} + +func Statfs(path string, buf *Statfs_t) (err error) { + pathp, err := BytePtrFromString(path) + if err != nil { + return err + } + _, _, e := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(pathp)), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf))) + if e != 0 { + err = e + } + return +} + func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) { page := uintptr(offset / 4096) if offset != int64(page)*4096 { @@ -87,10 +108,72 @@ func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int6 return mmap2(addr, length, prot, flags, fd, page) } -// TODO(kaib): add support for tracing -func (r *PtraceRegs) PC() uint64 { return 0 } +type rlimit32 struct { + Cur uint32 + Max uint32 +} + +//sysnb getrlimit(resource int, rlim *rlimit32) (err error) = SYS_GETRLIMIT + +const rlimInf32 = ^uint32(0) +const rlimInf64 = ^uint64(0) + +func Getrlimit(resource int, rlim *Rlimit) (err error) { + err = prlimit(0, resource, rlim, nil) + if err != ENOSYS { + return err + } + + rl := rlimit32{} + err = getrlimit(resource, &rl) + if err != nil { + return + } + + if rl.Cur == rlimInf32 { + rlim.Cur = rlimInf64 + } else { + rlim.Cur = uint64(rl.Cur) + } + + if rl.Max == rlimInf32 { + rlim.Max = rlimInf64 + } else { + rlim.Max = uint64(rl.Max) + } + return +} + +//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT + +func Setrlimit(resource int, rlim *Rlimit) (err error) { + err = prlimit(0, resource, nil, rlim) + if err != ENOSYS { + return err + } + + rl := rlimit32{} + if rlim.Cur == rlimInf64 { + rl.Cur = rlimInf32 + } else if rlim.Cur < uint64(rlimInf32) { + rl.Cur = uint32(rlim.Cur) + } else { + return EINVAL + } + if rlim.Max == rlimInf64 { + rl.Max = rlimInf32 + } else if rlim.Max < uint64(rlimInf32) { + rl.Max = uint32(rlim.Max) + } else { + return EINVAL + } + + return setrlimit(resource, &rl) +} + +func (r *PtraceRegs) PC() uint64 { return uint64(r.Uregs[15]) } -func (r *PtraceRegs) SetPC(pc uint64) {} +func (r *PtraceRegs) SetPC(pc uint64) { r.Uregs[15] = uint32(pc) } func (iov *Iovec) SetLen(length int) { iov.Len = uint32(length) diff --git a/src/pkg/syscall/syscall_netbsd.go b/src/pkg/syscall/syscall_netbsd.go index 3f6d16f56..b0fa1112d 100644 --- a/src/pkg/syscall/syscall_netbsd.go +++ b/src/pkg/syscall/syscall_netbsd.go @@ -22,14 +22,72 @@ type SockaddrDatalink struct { Nlen uint8 Alen uint8 Slen uint8 - Data [24]int8 + Data [12]int8 raw RawSockaddrDatalink } func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) +func sysctlNodes(mib []_C_int) (nodes []Sysctlnode, err error) { + var olen uintptr + + // Get a list of all sysctl nodes below the given MIB by performing + // a sysctl for the given MIB with CTL_QUERY appended. + mib = append(mib, CTL_QUERY) + qnode := Sysctlnode{Flags: SYSCTL_VERS_1} + qp := (*byte)(unsafe.Pointer(&qnode)) + sz := unsafe.Sizeof(qnode) + if err = sysctl(mib, nil, &olen, qp, sz); err != nil { + return nil, err + } + + // Now that we know the size, get the actual nodes. + nodes = make([]Sysctlnode, olen/sz) + np := (*byte)(unsafe.Pointer(&nodes[0])) + if err = sysctl(mib, np, &olen, qp, sz); err != nil { + return nil, err + } + + return nodes, nil +} + func nametomib(name string) (mib []_C_int, err error) { - return nil, EINVAL + + // Split name into components. + var parts []string + last := 0 + for i := 0; i < len(name); i++ { + if name[i] == '.' { + parts = append(parts, name[last:i]) + last = i + 1 + } + } + parts = append(parts, name[last:]) + + // Discover the nodes and construct the MIB OID. + for partno, part := range parts { + nodes, err := sysctlNodes(mib) + if err != nil { + return nil, err + } + for _, node := range nodes { + n := make([]byte, 0) + for i := range node.Name { + if node.Name[i] != 0 { + n = append(n, byte(node.Name[i])) + } + } + if string(n) == part { + mib = append(mib, _C_int(node.Num)) + break + } + } + if len(mib) != partno+1 { + return nil, EINVAL + } + } + + return mib, nil } // ParseDirent parses up to max directory entries in buf, @@ -60,15 +118,12 @@ func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, return origlen - len(buf), count, names } -//sysnb pipe2(p *[2]_C_int, flags _C_int) (err error) +//sysnb pipe() (fd1 int, fd2 int, err error) func Pipe(p []int) (err error) { if len(p) != 2 { return EINVAL } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + p[0], p[1], err = pipe() return } @@ -78,7 +133,7 @@ func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { } // TODO -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { return -1, ENOSYS } @@ -133,7 +188,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys Pathconf(path string, name int) (val int, err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error) //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) -//sys Read(fd int, p []byte) (n int, err error) +//sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) //sys Revoke(path string) (err error) @@ -158,11 +213,11 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys Umask(newmask int) (oldmask int) //sys Unlink(path string) (err error) //sys Unmount(path string, flags int) (err error) -//sys Write(fd int, p []byte) (n int, err error) +//sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) -//sys read(fd int, buf *byte, nbuf int) (n int, err error) -//sys write(fd int, buf *byte, nbuf int) (n int, err error) +//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ +//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE /* * Unimplemented diff --git a/src/pkg/syscall/syscall_netbsd_386.go b/src/pkg/syscall/syscall_netbsd_386.go index 3c4c693c9..2dbff07f1 100644 --- a/src/pkg/syscall/syscall_netbsd_386.go +++ b/src/pkg/syscall/syscall_netbsd_386.go @@ -9,7 +9,7 @@ func Getpagesize() int { return 4096 } func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int32(nsec / 1e9) + ts.Sec = int64(nsec / 1e9) ts.Nsec = int32(nsec % 1e9) return } @@ -19,14 +19,14 @@ func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec) func NsecToTimeval(nsec int64) (tv Timeval) { nsec += 999 // round up to microsecond tv.Usec = int32(nsec % 1e9 / 1e3) - tv.Sec = int32(nsec / 1e9) + tv.Sec = int64(nsec / 1e9) return } func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Ident = uint32(fd) - k.Filter = int16(mode) - k.Flags = uint16(flags) + k.Filter = uint32(mode) + k.Flags = uint32(flags) } func (iov *Iovec) SetLen(length int) { diff --git a/src/pkg/syscall/syscall_netbsd_amd64.go b/src/pkg/syscall/syscall_netbsd_amd64.go index 17485b12c..5784db99a 100644 --- a/src/pkg/syscall/syscall_netbsd_amd64.go +++ b/src/pkg/syscall/syscall_netbsd_amd64.go @@ -9,8 +9,8 @@ func Getpagesize() int { return 4096 } func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } func NsecToTimespec(nsec int64) (ts Timespec) { - ts.Sec = int32(nsec / 1e9) - ts.Nsec = nsec % 1e9 + ts.Sec = int64(nsec / 1e9) + ts.Nsec = int64(nsec % 1e9) return } @@ -18,15 +18,15 @@ func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec) func NsecToTimeval(nsec int64) (tv Timeval) { nsec += 999 // round up to microsecond - tv.Usec = nsec % 1e9 / 1e3 + tv.Usec = int32(nsec % 1e9 / 1e3) tv.Sec = int64(nsec / 1e9) return } func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Ident = uint64(fd) - k.Filter = int16(mode) - k.Flags = uint16(flags) + k.Filter = uint32(mode) + k.Flags = uint32(flags) } func (iov *Iovec) SetLen(length int) { diff --git a/src/pkg/syscall/syscall_netbsd_arm.go b/src/pkg/syscall/syscall_netbsd_arm.go new file mode 100644 index 000000000..659698aac --- /dev/null +++ b/src/pkg/syscall/syscall_netbsd_arm.go @@ -0,0 +1,42 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +func Getpagesize() int { return 4096 } + +func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } + +func NsecToTimespec(nsec int64) (ts Timespec) { + ts.Sec = int64(nsec / 1e9) + ts.Nsec = int32(nsec % 1e9) + return +} + +func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 } + +func NsecToTimeval(nsec int64) (tv Timeval) { + nsec += 999 // round up to microsecond + tv.Usec = int32(nsec % 1e9 / 1e3) + tv.Sec = int64(nsec / 1e9) + return +} + +func SetKevent(k *Kevent_t, fd, mode, flags int) { + k.Ident = uint32(fd) + k.Filter = uint32(mode) + k.Flags = uint32(flags) +} + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint32(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} diff --git a/src/pkg/syscall/syscall_openbsd.go b/src/pkg/syscall/syscall_openbsd.go index 7cd119108..a146dca2c 100644 --- a/src/pkg/syscall/syscall_openbsd.go +++ b/src/pkg/syscall/syscall_openbsd.go @@ -91,7 +91,7 @@ func Pipe(p []int) (err error) { } // TODO -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { return -1, ENOSYS } @@ -149,7 +149,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys Pathconf(path string, name int) (val int, err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error) //sys Pwrite(fd int, p []byte, offset int64) (n int, err error) -//sys Read(fd int, p []byte) (n int, err error) +//sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) //sys Revoke(path string) (err error) @@ -176,11 +176,11 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys Umask(newmask int) (oldmask int) //sys Unlink(path string) (err error) //sys Unmount(path string, flags int) (err error) -//sys Write(fd int, p []byte) (n int, err error) +//sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) -//sys read(fd int, buf *byte, nbuf int) (n int, err error) -//sys write(fd int, buf *byte, nbuf int) (n int, err error) +//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ +//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE /* * Unimplemented diff --git a/src/pkg/syscall/syscall_plan9.go b/src/pkg/syscall/syscall_plan9.go index 122a96f26..bc2505758 100644 --- a/src/pkg/syscall/syscall_plan9.go +++ b/src/pkg/syscall/syscall_plan9.go @@ -23,13 +23,28 @@ func (e ErrorString) Error() string { return string(e) } // NewError converts s to an ErrorString, which satisfies the Error interface. func NewError(s string) error { return ErrorString(s) } +func (e ErrorString) Temporary() bool { + return e == EINTR || e == EMFILE || e.Timeout() +} + +func (e ErrorString) Timeout() bool { + return e == EBUSY || e == ETIMEDOUT +} + +// A Note is a string describing a process note. +// It implements the os.Signal interface. +type Note string + +func (n Note) Signal() {} + +func (n Note) String() string { + return string(n) +} + var ( Stdin = 0 Stdout = 1 Stderr = 2 - - EAFNOSUPPORT = NewError("address family not supported by protocol") - EISDIR = NewError("file is a directory") ) // For testing: clients can set this flag to force @@ -67,25 +82,10 @@ func errstr() string { return cstring(buf[:]) } -func Getpagesize() int { return 4096 } +// Implemented in assembly to import from runtime. +func exit(int) -//sys exits(msg *byte) -func Exits(msg *string) { - if msg == nil { - exits(nil) - } - - exits(StringBytePtr(*msg)) -} - -func Exit(code int) { - if code == 0 { - Exits(nil) - } - - msg := itoa(code) - Exits(&msg) -} +func Exit(code int) { exit(code) } func readnum(path string) (uint, error) { var b [12]byte @@ -120,13 +120,22 @@ func Getppid() (ppid int) { } func Read(fd int, p []byte) (n int, err error) { - return Pread(fd, p, -1) + n, err = Pread(fd, p, -1) + if raceenabled && err == nil { + raceAcquire(unsafe.Pointer(&ioSync)) + } + return } func Write(fd int, p []byte) (n int, err error) { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } return Pwrite(fd, p, -1) } +var ioSync int64 + func Getwd() (wd string, err error) { fd, e := Open(".", O_RDONLY) @@ -240,19 +249,27 @@ func Await(w *Waitmsg) (err error) { } func Unmount(name, old string) (err error) { - oldp := uintptr(unsafe.Pointer(StringBytePtr(old))) + oldp, err := BytePtrFromString(old) + if err != nil { + return err + } + oldptr := uintptr(unsafe.Pointer(oldp)) var r0 uintptr var e ErrorString // bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted. if name == "" { - r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldp, 0) + r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0) } else { - r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(name))), oldp, 0) + namep, err := BytePtrFromString(name) + if err != nil { + return err + } + r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0) } - if int(r0) == -1 { + if int32(r0) == -1 { err = e } return @@ -300,29 +317,12 @@ func DecodeBintime(b []byte) (nsec int64, err error) { return } -func Gettimeofday(tv *Timeval) (err error) { - // TODO(paulzhol): - // avoid reopening a file descriptor for /dev/bintime on each call, - // use lower-level calls to avoid allocation. - - var b [8]byte - var nsec int64 - - fd, e := Open("/dev/bintime", O_RDONLY) +func Gettimeofday(tv *Timeval) error { + nsec, e := nanotime() if e != nil { return e } - defer Close(fd) - - if _, e = Pread(fd, b[:], 0); e != nil { - return e - } - - if nsec, e = DecodeBintime(b[:]); e != nil { - return e - } *tv = NsecToTimeval(nsec) - return e } @@ -335,14 +335,6 @@ func Getgroups() (gids []int, err error) { return make([]int, 0), nil } -type Signal int - -func (s Signal) Signal() {} - -func (s Signal) String() string { - return "" -} - //sys Dup(oldfd int, newfd int) (fd int, err error) //sys Open(path string, mode int) (fd int, err error) //sys Create(path string, mode int, perm uint32) (fd int, err error) diff --git a/src/pkg/syscall/syscall_plan9_386.go b/src/pkg/syscall/syscall_plan9_386.go index e82b540b4..7357e0ba6 100644 --- a/src/pkg/syscall/syscall_plan9_386.go +++ b/src/pkg/syscall/syscall_plan9_386.go @@ -3,3 +3,30 @@ // license that can be found in the LICENSE file. package syscall + +func Getpagesize() int { return 0x1000 } + +func nanotime() (nsec int64, err error) { + // TODO(paulzhol): + // avoid reopening a file descriptor for /dev/bintime on each call, + // use lower-level calls to avoid allocation. + + var b [8]byte + nsec = -1 + + fd, err := Open("/dev/bintime", O_RDONLY) + if err != nil { + return + } + defer Close(fd) + + if _, err = Pread(fd, b[:], 0); err != nil { + return + } + + if nsec, err = DecodeBintime(b[:]); err != nil { + return -1, err + } + + return +} diff --git a/src/pkg/syscall/syscall_plan9_amd64.go b/src/pkg/syscall/syscall_plan9_amd64.go new file mode 100644 index 000000000..9387db3e5 --- /dev/null +++ b/src/pkg/syscall/syscall_plan9_amd64.go @@ -0,0 +1,14 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +func Getpagesize() int { return 0x200000 } + +// Used by Gettimeofday, which expects +// an error return value. +func nanotime() (int64, error) { + r1, _, _ := RawSyscall(SYS_NANOTIME, 0, 0, 0) + return int64(r1), nil +} diff --git a/src/pkg/syscall/syscall_unix.go b/src/pkg/syscall/syscall_unix.go index d4e02f68a..fee1fc491 100644 --- a/src/pkg/syscall/syscall_unix.go +++ b/src/pkg/syscall/syscall_unix.go @@ -127,3 +127,27 @@ func (s Signal) String() string { } return "signal " + itoa(int(s)) } + +func Read(fd int, p []byte) (n int, err error) { + n, err = read(fd, p) + if raceenabled && err == nil { + raceAcquire(unsafe.Pointer(&ioSync)) + } + return +} + +func Write(fd int, p []byte) (n int, err error) { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + return write(fd, p) +} + +func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + return sendfile(outfd, infd, offset, count) +} + +var ioSync int64 diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index 6b544f1d4..d7c3265a1 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -7,6 +7,8 @@ package syscall import ( + errorspkg "errors" + "sync" "unicode/utf16" "unsafe" ) @@ -15,46 +17,28 @@ type Handle uintptr const InvalidHandle = ^Handle(0) -/* - -small demo to detect version of windows you are running: - -package main - -import ( - "syscall" -) - -func abort(funcname string, err error) { - panic(funcname + " failed: " + err.Error()) -} - -func print_version(v uint32) { - major := byte(v) - minor := uint8(v >> 8) - build := uint16(v >> 16) - print("windows version ", major, ".", minor, " (Build ", build, ")\n") -} - -func main() { - h, err := syscall.LoadLibrary("kernel32.dll") - if err != nil { - abort("LoadLibrary", err) - } - defer syscall.FreeLibrary(h) - proc, err := syscall.GetProcAddress(h, "GetVersion") +// StringToUTF16 is deprecated. Use UTF16FromString instead. +// If s contains a NUL byte this function panics instead of +// returning an error. +func StringToUTF16(s string) []uint16 { + a, err := UTF16FromString(s) if err != nil { - abort("GetProcAddress", err) + panic("syscall: string with NUL passed to StringToUTF16") } - r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0) - print_version(uint32(r)) + return a } -*/ - -// StringToUTF16 returns the UTF-16 encoding of the UTF-8 string s, -// with a terminating NUL added. -func StringToUTF16(s string) []uint16 { return utf16.Encode([]rune(s + "\x00")) } +// UTF16FromString returns the UTF-16 encoding of the UTF-8 string +// s, with a terminating NUL added. If s contains a NUL byte at any +// location, it returns (nil, EINVAL). +func UTF16FromString(s string) ([]uint16, error) { + for i := 0; i < len(s); i++ { + if s[i] == 0 { + return nil, EINVAL + } + } + return utf16.Encode([]rune(s + "\x00")), nil +} // UTF16ToString returns the UTF-8 encoding of the UTF-16 sequence s, // with a terminating NUL removed. @@ -68,10 +52,22 @@ func UTF16ToString(s []uint16) string { return string(utf16.Decode(s)) } -// StringToUTF16Ptr returns pointer to the UTF-16 encoding of -// the UTF-8 string s, with a terminating NUL added. +// StringToUTF16Ptr is deprecated. Use UTF16PtrFromString instead. +// If s contains a NUL byte this function panics instead of +// returning an error. func StringToUTF16Ptr(s string) *uint16 { return &StringToUTF16(s)[0] } +// UTF16PtrFromString returns pointer to the UTF-16 encoding of +// the UTF-8 string s, with a terminating NUL added. If s +// contains a NUL byte at any location, it returns (nil, EINVAL). +func UTF16PtrFromString(s string) (*uint16, error) { + a, err := UTF16FromString(s) + if err != nil { + return nil, err + } + return &a[0], nil +} + func Getpagesize() int { return 4096 } // Errno is the Windows error number. @@ -90,9 +86,10 @@ func (e Errno) Error() string { b := make([]uint16, 300) n, err := FormatMessage(flags, 0, uint32(e), langid(LANG_ENGLISH, SUBLANG_ENGLISH_US), b, nil) if err != nil { - // TODO(brainman): Call FormatMessage again asking for "native" error message. - // http://code.google.com/p/go/issues/detail?id=3376 must be resolved first. - return "winapi error #" + itoa(int(e)) + n, err = FormatMessage(flags, 0, uint32(e), 0, b, nil) + if err != nil { + return "winapi error #" + itoa(int(e)) + } } // trim terminating \r and \n for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- { @@ -111,7 +108,7 @@ func (e Errno) Timeout() bool { // Converts a Go function to a function pointer conforming // to the stdcall calling convention. This is useful when // interoperating with Windows code requiring callbacks. -// Implemented in ../runtime/windows/syscall.goc +// Implemented in ../runtime/syscall_windows.goc func NewCallback(fn interface{}) uintptr // windows api calls @@ -147,6 +144,7 @@ func NewCallback(fn interface{}) uintptr //sys GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error) //sys PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error) //sys CancelIo(s Handle) (err error) +//sys CancelIoEx(s Handle, o *Overlapped) (err error) //sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) = CreateProcessW //sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err error) //sys TerminateProcess(handle Handle, exitcode uint32) (err error) @@ -201,7 +199,10 @@ func NewCallback(fn interface{}) uintptr //sys RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW //sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW //sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW -//sys getCurrentProcessId() (pid uint32) = kernel32.getCurrentProcessId +//sys getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId +//sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode +//sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW +//sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW // syscall interface implementation for other packages @@ -218,6 +219,10 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) { if len(path) == 0 { return InvalidHandle, ERROR_FILE_NOT_FOUND } + pathp, err := UTF16PtrFromString(path) + if err != nil { + return InvalidHandle, err + } var access uint32 switch mode & (O_RDONLY | O_WRONLY | O_RDWR) { case O_RDONLY: @@ -252,7 +257,7 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) { default: createmode = OPEN_EXISTING } - h, e := CreateFile(StringToUTF16Ptr(path), access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0) + h, e := CreateFile(pathp, access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0) return h, e } @@ -266,10 +271,16 @@ func Read(fd Handle, p []byte) (n int, err error) { } return 0, e } + if raceenabled { + raceAcquire(unsafe.Pointer(&ioSync)) + } return int(done), nil } func Write(fd Handle, p []byte) (n int, err error) { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } var done uint32 e := WriteFile(fd, p, &done, nil) if e != nil { @@ -278,6 +289,8 @@ func Write(fd Handle, p []byte) (n int, err error) { return int(done), nil } +var ioSync int64 + func Seek(fd Handle, offset int64, whence int) (newoffset int64, err error) { var w uint32 switch whence { @@ -330,24 +343,46 @@ func Getwd() (wd string, err error) { } func Chdir(path string) (err error) { - return SetCurrentDirectory(&StringToUTF16(path)[0]) + pathp, err := UTF16PtrFromString(path) + if err != nil { + return err + } + return SetCurrentDirectory(pathp) } func Mkdir(path string, mode uint32) (err error) { - return CreateDirectory(&StringToUTF16(path)[0], nil) + pathp, err := UTF16PtrFromString(path) + if err != nil { + return err + } + return CreateDirectory(pathp, nil) } func Rmdir(path string) (err error) { - return RemoveDirectory(&StringToUTF16(path)[0]) + pathp, err := UTF16PtrFromString(path) + if err != nil { + return err + } + return RemoveDirectory(pathp) } func Unlink(path string) (err error) { - return DeleteFile(&StringToUTF16(path)[0]) + pathp, err := UTF16PtrFromString(path) + if err != nil { + return err + } + return DeleteFile(pathp) } func Rename(oldpath, newpath string) (err error) { - from := &StringToUTF16(oldpath)[0] - to := &StringToUTF16(newpath)[0] + from, err := UTF16PtrFromString(oldpath) + if err != nil { + return err + } + to, err := UTF16PtrFromString(newpath) + if err != nil { + return err + } return MoveFile(from, to) } @@ -403,7 +438,11 @@ func Utimes(path string, tv []Timeval) (err error) { if len(tv) != 2 { return EINVAL } - h, e := CreateFile(StringToUTF16Ptr(path), + pathp, e := UTF16PtrFromString(path) + if e != nil { + return e + } + h, e := CreateFile(pathp, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) if e != nil { @@ -415,6 +454,26 @@ func Utimes(path string, tv []Timeval) (err error) { return SetFileTime(h, nil, &a, &w) } +func UtimesNano(path string, ts []Timespec) (err error) { + if len(ts) != 2 { + return EINVAL + } + pathp, e := UTF16PtrFromString(path) + if e != nil { + return e + } + h, e := CreateFile(pathp, + FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, nil, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) + if e != nil { + return e + } + defer Close(h) + a := NsecToFiletime(TimespecToNsec(ts[0])) + w := NsecToFiletime(TimespecToNsec(ts[1])) + return SetFileTime(h, nil, &a, &w) +} + func Fsync(fd Handle) (err error) { return FlushFileBuffers(fd) } @@ -423,7 +482,10 @@ func Chmod(path string, mode uint32) (err error) { if mode == 0 { return EINVAL } - p := StringToUTF16Ptr(path) + p, e := UTF16PtrFromString(path) + if e != nil { + return e + } attrs, e := GetFileAttributes(p) if e != nil { return e @@ -436,32 +498,41 @@ func Chmod(path string, mode uint32) (err error) { return SetFileAttributes(p, attrs) } +func LoadCancelIoEx() error { + return procCancelIoEx.Find() +} + // net api calls +const socket_error = uintptr(^uint32(0)) + //sys WSAStartup(verreq uint32, data *WSAData) (sockerr error) = ws2_32.WSAStartup -//sys WSACleanup() (err error) [failretval==-1] = ws2_32.WSACleanup -//sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) [failretval==-1] = ws2_32.WSAIoctl +//sys WSACleanup() (err error) [failretval==socket_error] = ws2_32.WSACleanup +//sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) [failretval==socket_error] = ws2_32.WSAIoctl //sys socket(af int32, typ int32, protocol int32) (handle Handle, err error) [failretval==InvalidHandle] = ws2_32.socket -//sys Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) [failretval==-1] = ws2_32.setsockopt -//sys bind(s Handle, name uintptr, namelen int32) (err error) [failretval==-1] = ws2_32.bind -//sys connect(s Handle, name uintptr, namelen int32) (err error) [failretval==-1] = ws2_32.connect -//sys getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) [failretval==-1] = ws2_32.getsockname -//sys getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) [failretval==-1] = ws2_32.getpeername -//sys listen(s Handle, backlog int32) (err error) [failretval==-1] = ws2_32.listen -//sys shutdown(s Handle, how int32) (err error) [failretval==-1] = ws2_32.shutdown -//sys Closesocket(s Handle) (err error) [failretval==-1] = ws2_32.closesocket +//sys Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) [failretval==socket_error] = ws2_32.setsockopt +//sys Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) [failretval==socket_error] = ws2_32.getsockopt +//sys bind(s Handle, name uintptr, namelen int32) (err error) [failretval==socket_error] = ws2_32.bind +//sys connect(s Handle, name uintptr, namelen int32) (err error) [failretval==socket_error] = ws2_32.connect +//sys getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) [failretval==socket_error] = ws2_32.getsockname +//sys getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) [failretval==socket_error] = ws2_32.getpeername +//sys listen(s Handle, backlog int32) (err error) [failretval==socket_error] = ws2_32.listen +//sys shutdown(s Handle, how int32) (err error) [failretval==socket_error] = ws2_32.shutdown +//sys Closesocket(s Handle) (err error) [failretval==socket_error] = ws2_32.closesocket //sys AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (err error) = mswsock.AcceptEx //sys GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) = mswsock.GetAcceptExSockaddrs -//sys WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==-1] = ws2_32.WSARecv -//sys WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==-1] = ws2_32.WSASend -//sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==-1] = ws2_32.WSARecvFrom -//sys WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==-1] = ws2_32.WSASendTo +//sys WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSARecv +//sys WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSASend +//sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSARecvFrom +//sys WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSASendTo //sys GetHostByName(name string) (h *Hostent, err error) [failretval==nil] = ws2_32.gethostbyname //sys GetServByName(name string, proto string) (s *Servent, err error) [failretval==nil] = ws2_32.getservbyname //sys Ntohs(netshort uint16) (u uint16) = ws2_32.ntohs //sys GetProtoByName(name string) (p *Protoent, err error) [failretval==nil] = ws2_32.getprotobyname //sys DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) = dnsapi.DnsQuery_W //sys DnsRecordListFree(rl *DNSRecord, freetype uint32) = dnsapi.DnsRecordListFree +//sys GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) = ws2_32.GetAddrInfoW +//sys FreeAddrInfoW(addrinfo *AddrinfoW) = ws2_32.FreeAddrInfoW //sys GetIfEntry(pIfRow *MibIfRow) (errcode error) = iphlpapi.GetIfEntry //sys GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) = iphlpapi.GetAdaptersInfo @@ -476,6 +547,14 @@ type RawSockaddrInet4 struct { Zero [8]uint8 } +type RawSockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + type RawSockaddr struct { Family uint16 Data [14]int8 @@ -514,11 +593,22 @@ type SockaddrInet6 struct { Port int ZoneId uint32 Addr [16]byte + raw RawSockaddrInet6 } func (sa *SockaddrInet6) sockaddr() (uintptr, int32, error) { - // TODO(brainman): implement SockaddrInet6.sockaddr() - return 0, 0, EWINDOWS + if sa.Port < 0 || sa.Port > 0xFFFF { + return 0, 0, EINVAL + } + sa.raw.Family = AF_INET6 + p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)) + p[0] = byte(sa.Port >> 8) + p[1] = byte(sa.Port) + sa.raw.Scope_id = sa.ZoneId + for i := 0; i < len(sa.Addr); i++ { + sa.raw.Addr[i] = sa.Addr[i] + } + return uintptr(unsafe.Pointer(&sa.raw)), int32(unsafe.Sizeof(sa.raw)), nil } type SockaddrUnix struct { @@ -546,7 +636,15 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) { return sa, nil case AF_INET6: - return nil, EWINDOWS + pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa)) + sa := new(SockaddrInet6) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) + sa.ZoneId = pp.Scope_id + for i := 0; i < len(sa.Addr); i++ { + sa.Addr[i] = pp.Addr[i] + } + return sa, nil } return nil, EAFNOSUPPORT } @@ -613,6 +711,60 @@ func WSASendto(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32 return WSASendTo(s, bufs, bufcnt, sent, flags, (*RawSockaddrAny)(unsafe.Pointer(rsa)), l, overlapped, croutine) } +func LoadGetAddrInfo() error { + return procGetAddrInfoW.Find() +} + +var connectExFunc struct { + once sync.Once + addr uintptr + err error +} + +func LoadConnectEx() error { + connectExFunc.once.Do(func() { + var s Handle + s, connectExFunc.err = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) + if connectExFunc.err != nil { + return + } + defer CloseHandle(s) + var n uint32 + connectExFunc.err = WSAIoctl(s, + SIO_GET_EXTENSION_FUNCTION_POINTER, + (*byte)(unsafe.Pointer(&WSAID_CONNECTEX)), + uint32(unsafe.Sizeof(WSAID_CONNECTEX)), + (*byte)(unsafe.Pointer(&connectExFunc.addr)), + uint32(unsafe.Sizeof(connectExFunc.addr)), + &n, nil, 0) + }) + return connectExFunc.err +} + +func connectEx(s Handle, name uintptr, namelen int32, sendBuf *byte, sendDataLen uint32, bytesSent *uint32, overlapped *Overlapped) (err error) { + r1, _, e1 := Syscall9(connectExFunc.addr, 7, uintptr(s), uintptr(name), uintptr(namelen), uintptr(unsafe.Pointer(sendBuf)), uintptr(sendDataLen), uintptr(unsafe.Pointer(bytesSent)), uintptr(unsafe.Pointer(overlapped)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func ConnectEx(fd Handle, sa Sockaddr, sendBuf *byte, sendDataLen uint32, bytesSent *uint32, overlapped *Overlapped) error { + err := LoadConnectEx() + if err != nil { + return errorspkg.New("failed to find ConnectEx: " + err.Error()) + } + ptr, n, err := sa.sockaddr() + if err != nil { + return err + } + return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped) +} + // Invented structures to support what package os expects. type Rusage struct { CreationTime Filetime @@ -650,6 +802,14 @@ type Timespec struct { Nsec int64 } +func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } + +func NsecToTimespec(nsec int64) (ts Timespec) { + ts.Sec = nsec / 1e9 + ts.Nsec = nsec % 1e9 + return +} + // TODO(brainman): fix all needed for net func Accept(fd Handle) (nfd Handle, sa Sockaddr, err error) { return 0, nil, EWINDOWS } @@ -659,11 +819,23 @@ func Recvfrom(fd Handle, p []byte, flags int) (n int, from Sockaddr, err error) func Sendto(fd Handle, p []byte, flags int, to Sockaddr) (err error) { return EWINDOWS } func SetsockoptTimeval(fd Handle, level, opt int, tv *Timeval) (err error) { return EWINDOWS } +// The Linger struct is wrong but we only noticed after Go 1. +// sysLinger is the real system call structure. + +// BUG(brainman): The definition of Linger is not appropriate for direct use +// with Setsockopt and Getsockopt. +// Use SetsockoptLinger instead. + type Linger struct { Onoff int32 Linger int32 } +type sysLinger struct { + Onoff uint16 + Linger uint16 +} + type IPMreq struct { Multiaddr [4]byte /* in_addr */ Interface [4]byte /* in_addr */ @@ -674,8 +846,13 @@ type IPv6Mreq struct { Interface uint32 } -func GetsockoptInt(fd Handle, level, opt int) (int, error) { return -1, EWINDOWS } -func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) { return EWINDOWS } +func GetsockoptInt(fd Handle, level, opt int) (int, error) { return -1, EWINDOWS } + +func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) { + sys := sysLinger{Onoff: uint16(l.Onoff), Linger: uint16(l.Linger)} + return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&sys)), int32(unsafe.Sizeof(sys))) +} + func SetsockoptInet4Addr(fd Handle, level, opt int, value [4]byte) (err error) { return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&value[0])), 4) } diff --git a/src/pkg/syscall/syscall_windows_test.go b/src/pkg/syscall/syscall_windows_test.go index 79cd8f869..86842f2ad 100644 --- a/src/pkg/syscall/syscall_windows_test.go +++ b/src/pkg/syscall/syscall_windows_test.go @@ -35,7 +35,8 @@ func TestWin32finddata(t *testing.T) { var want byte = 2 // it is unlikely to have this character in the filename x := X{got: want} - h, err := syscall.FindFirstFile(syscall.StringToUTF16Ptr(path), &(x.fd)) + pathp, _ := syscall.UTF16PtrFromString(path) + h, err := syscall.FindFirstFile(pathp, &(x.fd)) if err != nil { t.Fatalf("FindFirstFile failed: %v", err) } @@ -48,3 +49,24 @@ func TestWin32finddata(t *testing.T) { t.Fatalf("memory corruption: want=%d got=%d", want, x.got) } } + +func abort(funcname string, err error) { + panic(funcname + " failed: " + err.Error()) +} + +func ExampleLoadLibrary() { + h, err := syscall.LoadLibrary("kernel32.dll") + if err != nil { + abort("LoadLibrary", err) + } + defer syscall.FreeLibrary(h) + proc, err := syscall.GetProcAddress(h, "GetVersion") + if err != nil { + abort("GetProcAddress", err) + } + r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0) + major := byte(r) + minor := uint8(r >> 8) + build := uint16(r >> 16) + print("windows version ", major, ".", minor, " (Build ", build, ")\n") +} diff --git a/src/pkg/syscall/types_darwin.go b/src/pkg/syscall/types_darwin.go index b9c65e3c7..098bbff6f 100644 --- a/src/pkg/syscall/types_darwin.go +++ b/src/pkg/syscall/types_darwin.go @@ -20,6 +20,7 @@ package syscall #include #include #include +#include #include #include #include @@ -149,6 +150,8 @@ type Msghdr C.struct_msghdr type Cmsghdr C.struct_cmsghdr +type Inet4Pktinfo C.struct_in_pktinfo + type Inet6Pktinfo C.struct_in6_pktinfo const ( @@ -162,6 +165,7 @@ const ( SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq SizeofMsghdr = C.sizeof_struct_msghdr SizeofCmsghdr = C.sizeof_struct_cmsghdr + SizeofInet4Pktinfo = C.sizeof_struct_in_pktinfo SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo ) @@ -226,3 +230,7 @@ type BpfProgram C.struct_bpf_program type BpfInsn C.struct_bpf_insn type BpfHdr C.struct_bpf_hdr + +// Terminal handling + +type Termios C.struct_termios diff --git a/src/pkg/syscall/types_freebsd.go b/src/pkg/syscall/types_freebsd.go index 6e1dd4310..7d4923d5a 100644 --- a/src/pkg/syscall/types_freebsd.go +++ b/src/pkg/syscall/types_freebsd.go @@ -193,12 +193,13 @@ type FdSet C.fd_set // Routing and interface messages const ( - SizeofIfMsghdr = C.sizeof_struct_if_msghdr - SizeofIfData = C.sizeof_struct_if_data - SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr - SizeofIfmaMsghdr = C.sizeof_struct_ifma_msghdr - SizeofRtMsghdr = C.sizeof_struct_rt_msghdr - SizeofRtMetrics = C.sizeof_struct_rt_metrics + SizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfData = C.sizeof_struct_if_data + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofIfmaMsghdr = C.sizeof_struct_ifma_msghdr + SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics ) type IfMsghdr C.struct_if_msghdr @@ -209,6 +210,8 @@ type IfaMsghdr C.struct_ifa_msghdr type IfmaMsghdr C.struct_ifma_msghdr +type IfAnnounceMsghdr C.struct_if_announcemsghdr + type RtMsghdr C.struct_rt_msghdr type RtMetrics C.struct_rt_metrics diff --git a/src/pkg/syscall/types_linux.go b/src/pkg/syscall/types_linux.go index 0030960b5..1514cbc95 100644 --- a/src/pkg/syscall/types_linux.go +++ b/src/pkg/syscall/types_linux.go @@ -73,7 +73,7 @@ struct sockaddr_any { char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; }; -// copied from /usr/include/linux/un.h +// copied from /usr/include/linux/un.h struct my_sockaddr_un { sa_family_t sun_family; #ifdef __ARM_EABI__ @@ -195,6 +195,8 @@ type Inet6Pktinfo C.struct_in6_pktinfo type Ucred C.struct_ucred +type TCPInfo C.struct_tcp_info + const ( SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 @@ -211,86 +213,103 @@ const ( SizeofInet4Pktinfo = C.sizeof_struct_in_pktinfo SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo SizeofUcred = C.sizeof_struct_ucred + SizeofTCPInfo = C.sizeof_struct_tcp_info ) // Netlink routing and interface messages const ( - IFA_UNSPEC = C.IFA_UNSPEC - IFA_ADDRESS = C.IFA_ADDRESS - IFA_LOCAL = C.IFA_LOCAL - IFA_LABEL = C.IFA_LABEL - IFA_BROADCAST = C.IFA_BROADCAST - IFA_ANYCAST = C.IFA_ANYCAST - IFA_CACHEINFO = C.IFA_CACHEINFO - IFA_MULTICAST = C.IFA_MULTICAST - IFLA_UNSPEC = C.IFLA_UNSPEC - IFLA_ADDRESS = C.IFLA_ADDRESS - IFLA_BROADCAST = C.IFLA_BROADCAST - IFLA_IFNAME = C.IFLA_IFNAME - IFLA_MTU = C.IFLA_MTU - IFLA_LINK = C.IFLA_LINK - IFLA_QDISC = C.IFLA_QDISC - IFLA_STATS = C.IFLA_STATS - IFLA_COST = C.IFLA_COST - IFLA_PRIORITY = C.IFLA_PRIORITY - IFLA_MASTER = C.IFLA_MASTER - IFLA_WIRELESS = C.IFLA_WIRELESS - IFLA_PROTINFO = C.IFLA_PROTINFO - IFLA_TXQLEN = C.IFLA_TXQLEN - IFLA_MAP = C.IFLA_MAP - IFLA_WEIGHT = C.IFLA_WEIGHT - IFLA_OPERSTATE = C.IFLA_OPERSTATE - IFLA_LINKMODE = C.IFLA_LINKMODE - IFLA_LINKINFO = C.IFLA_LINKINFO - IFLA_NET_NS_PID = C.IFLA_NET_NS_PID - IFLA_IFALIAS = C.IFLA_IFALIAS - IFLA_MAX = C.IFLA_MAX - RT_SCOPE_UNIVERSE = C.RT_SCOPE_UNIVERSE - RT_SCOPE_SITE = C.RT_SCOPE_SITE - RT_SCOPE_LINK = C.RT_SCOPE_LINK - RT_SCOPE_HOST = C.RT_SCOPE_HOST - RT_SCOPE_NOWHERE = C.RT_SCOPE_NOWHERE - RT_TABLE_UNSPEC = C.RT_TABLE_UNSPEC - RT_TABLE_COMPAT = C.RT_TABLE_COMPAT - RT_TABLE_DEFAULT = C.RT_TABLE_DEFAULT - RT_TABLE_MAIN = C.RT_TABLE_MAIN - RT_TABLE_LOCAL = C.RT_TABLE_LOCAL - RT_TABLE_MAX = C.RT_TABLE_MAX - RTA_UNSPEC = C.RTA_UNSPEC - RTA_DST = C.RTA_DST - RTA_SRC = C.RTA_SRC - RTA_IIF = C.RTA_IIF - RTA_OIF = C.RTA_OIF - RTA_GATEWAY = C.RTA_GATEWAY - RTA_PRIORITY = C.RTA_PRIORITY - RTA_PREFSRC = C.RTA_PREFSRC - RTA_METRICS = C.RTA_METRICS - RTA_MULTIPATH = C.RTA_MULTIPATH - RTA_FLOW = C.RTA_FLOW - RTA_CACHEINFO = C.RTA_CACHEINFO - RTA_TABLE = C.RTA_TABLE - RTN_UNSPEC = C.RTN_UNSPEC - RTN_UNICAST = C.RTN_UNICAST - RTN_LOCAL = C.RTN_LOCAL - RTN_BROADCAST = C.RTN_BROADCAST - RTN_ANYCAST = C.RTN_ANYCAST - RTN_MULTICAST = C.RTN_MULTICAST - RTN_BLACKHOLE = C.RTN_BLACKHOLE - RTN_UNREACHABLE = C.RTN_UNREACHABLE - RTN_PROHIBIT = C.RTN_PROHIBIT - RTN_THROW = C.RTN_THROW - RTN_NAT = C.RTN_NAT - RTN_XRESOLVE = C.RTN_XRESOLVE - SizeofNlMsghdr = C.sizeof_struct_nlmsghdr - SizeofNlMsgerr = C.sizeof_struct_nlmsgerr - SizeofRtGenmsg = C.sizeof_struct_rtgenmsg - SizeofNlAttr = C.sizeof_struct_nlattr - SizeofRtAttr = C.sizeof_struct_rtattr - SizeofIfInfomsg = C.sizeof_struct_ifinfomsg - SizeofIfAddrmsg = C.sizeof_struct_ifaddrmsg - SizeofRtMsg = C.sizeof_struct_rtmsg - SizeofRtNexthop = C.sizeof_struct_rtnexthop + IFA_UNSPEC = C.IFA_UNSPEC + IFA_ADDRESS = C.IFA_ADDRESS + IFA_LOCAL = C.IFA_LOCAL + IFA_LABEL = C.IFA_LABEL + IFA_BROADCAST = C.IFA_BROADCAST + IFA_ANYCAST = C.IFA_ANYCAST + IFA_CACHEINFO = C.IFA_CACHEINFO + IFA_MULTICAST = C.IFA_MULTICAST + IFLA_UNSPEC = C.IFLA_UNSPEC + IFLA_ADDRESS = C.IFLA_ADDRESS + IFLA_BROADCAST = C.IFLA_BROADCAST + IFLA_IFNAME = C.IFLA_IFNAME + IFLA_MTU = C.IFLA_MTU + IFLA_LINK = C.IFLA_LINK + IFLA_QDISC = C.IFLA_QDISC + IFLA_STATS = C.IFLA_STATS + IFLA_COST = C.IFLA_COST + IFLA_PRIORITY = C.IFLA_PRIORITY + IFLA_MASTER = C.IFLA_MASTER + IFLA_WIRELESS = C.IFLA_WIRELESS + IFLA_PROTINFO = C.IFLA_PROTINFO + IFLA_TXQLEN = C.IFLA_TXQLEN + IFLA_MAP = C.IFLA_MAP + IFLA_WEIGHT = C.IFLA_WEIGHT + IFLA_OPERSTATE = C.IFLA_OPERSTATE + IFLA_LINKMODE = C.IFLA_LINKMODE + IFLA_LINKINFO = C.IFLA_LINKINFO + IFLA_NET_NS_PID = C.IFLA_NET_NS_PID + IFLA_IFALIAS = C.IFLA_IFALIAS + IFLA_MAX = C.IFLA_MAX + RT_SCOPE_UNIVERSE = C.RT_SCOPE_UNIVERSE + RT_SCOPE_SITE = C.RT_SCOPE_SITE + RT_SCOPE_LINK = C.RT_SCOPE_LINK + RT_SCOPE_HOST = C.RT_SCOPE_HOST + RT_SCOPE_NOWHERE = C.RT_SCOPE_NOWHERE + RT_TABLE_UNSPEC = C.RT_TABLE_UNSPEC + RT_TABLE_COMPAT = C.RT_TABLE_COMPAT + RT_TABLE_DEFAULT = C.RT_TABLE_DEFAULT + RT_TABLE_MAIN = C.RT_TABLE_MAIN + RT_TABLE_LOCAL = C.RT_TABLE_LOCAL + RT_TABLE_MAX = C.RT_TABLE_MAX + RTA_UNSPEC = C.RTA_UNSPEC + RTA_DST = C.RTA_DST + RTA_SRC = C.RTA_SRC + RTA_IIF = C.RTA_IIF + RTA_OIF = C.RTA_OIF + RTA_GATEWAY = C.RTA_GATEWAY + RTA_PRIORITY = C.RTA_PRIORITY + RTA_PREFSRC = C.RTA_PREFSRC + RTA_METRICS = C.RTA_METRICS + RTA_MULTIPATH = C.RTA_MULTIPATH + RTA_FLOW = C.RTA_FLOW + RTA_CACHEINFO = C.RTA_CACHEINFO + RTA_TABLE = C.RTA_TABLE + RTN_UNSPEC = C.RTN_UNSPEC + RTN_UNICAST = C.RTN_UNICAST + RTN_LOCAL = C.RTN_LOCAL + RTN_BROADCAST = C.RTN_BROADCAST + RTN_ANYCAST = C.RTN_ANYCAST + RTN_MULTICAST = C.RTN_MULTICAST + RTN_BLACKHOLE = C.RTN_BLACKHOLE + RTN_UNREACHABLE = C.RTN_UNREACHABLE + RTN_PROHIBIT = C.RTN_PROHIBIT + RTN_THROW = C.RTN_THROW + RTN_NAT = C.RTN_NAT + RTN_XRESOLVE = C.RTN_XRESOLVE + RTNLGRP_NONE = C.RTNLGRP_NONE + RTNLGRP_LINK = C.RTNLGRP_LINK + RTNLGRP_NOTIFY = C.RTNLGRP_NOTIFY + RTNLGRP_NEIGH = C.RTNLGRP_NEIGH + RTNLGRP_TC = C.RTNLGRP_TC + RTNLGRP_IPV4_IFADDR = C.RTNLGRP_IPV4_IFADDR + RTNLGRP_IPV4_MROUTE = C.RTNLGRP_IPV4_MROUTE + RTNLGRP_IPV4_ROUTE = C.RTNLGRP_IPV4_ROUTE + RTNLGRP_IPV4_RULE = C.RTNLGRP_IPV4_RULE + RTNLGRP_IPV6_IFADDR = C.RTNLGRP_IPV6_IFADDR + RTNLGRP_IPV6_MROUTE = C.RTNLGRP_IPV6_MROUTE + RTNLGRP_IPV6_ROUTE = C.RTNLGRP_IPV6_ROUTE + RTNLGRP_IPV6_IFINFO = C.RTNLGRP_IPV6_IFINFO + RTNLGRP_IPV6_PREFIX = C.RTNLGRP_IPV6_PREFIX + RTNLGRP_IPV6_RULE = C.RTNLGRP_IPV6_RULE + RTNLGRP_ND_USEROPT = C.RTNLGRP_ND_USEROPT + SizeofNlMsghdr = C.sizeof_struct_nlmsghdr + SizeofNlMsgerr = C.sizeof_struct_nlmsgerr + SizeofRtGenmsg = C.sizeof_struct_rtgenmsg + SizeofNlAttr = C.sizeof_struct_nlattr + SizeofRtAttr = C.sizeof_struct_rtattr + SizeofIfInfomsg = C.sizeof_struct_ifinfomsg + SizeofIfAddrmsg = C.sizeof_struct_ifaddrmsg + SizeofRtMsg = C.sizeof_struct_rtmsg + SizeofRtNexthop = C.sizeof_struct_rtnexthop ) type NlMsghdr C.struct_nlmsghdr @@ -345,6 +364,10 @@ type Ustat_t C.struct_ustat type EpollEvent C.struct_my_epoll_event +const ( + _AT_FDCWD = C.AT_FDCWD +) + // Terminal handling type Termios C.struct_termios diff --git a/src/pkg/syscall/types_netbsd.go b/src/pkg/syscall/types_netbsd.go index 518688833..4906a99ef 100644 --- a/src/pkg/syscall/types_netbsd.go +++ b/src/pkg/syscall/types_netbsd.go @@ -31,6 +31,7 @@ package syscall #include #include #include +#include #include #include #include @@ -97,23 +98,6 @@ type _Gid_t C.gid_t // Files -const ( // Directory mode bits - S_IFMT = C.S_IFMT - S_IFIFO = C.S_IFIFO - S_IFCHR = C.S_IFCHR - S_IFDIR = C.S_IFDIR - S_IFBLK = C.S_IFBLK - S_IFREG = C.S_IFREG - S_IFLNK = C.S_IFLNK - S_IFSOCK = C.S_IFSOCK - S_ISUID = C.S_ISUID - S_ISGID = C.S_ISGID - S_ISVTX = C.S_ISVTX - S_IRUSR = C.S_IRUSR - S_IWUSR = C.S_IWUSR - S_IXUSR = C.S_IXUSR -) - type Stat_t C.struct_stat type Statfs_t C.struct_statfs @@ -187,11 +171,12 @@ type FdSet C.fd_set // Routing and interface messages const ( - SizeofIfMsghdr = C.sizeof_struct_if_msghdr - SizeofIfData = C.sizeof_struct_if_data - SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr - SizeofRtMsghdr = C.sizeof_struct_rt_msghdr - SizeofRtMetrics = C.sizeof_struct_rt_metrics + SizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfData = C.sizeof_struct_if_data + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics ) type IfMsghdr C.struct_if_msghdr @@ -200,6 +185,8 @@ type IfData C.struct_if_data type IfaMsghdr C.struct_ifa_msghdr +type IfAnnounceMsghdr C.struct_if_announcemsghdr + type RtMsghdr C.struct_rt_msghdr type RtMetrics C.struct_rt_metrics @@ -227,3 +214,7 @@ type BpfInsn C.struct_bpf_insn type BpfHdr C.struct_bpf_hdr type BpfTimeval C.struct_bpf_timeval + +// Sysctl + +type Sysctlnode C.struct_sysctlnode diff --git a/src/pkg/syscall/types_openbsd.go b/src/pkg/syscall/types_openbsd.go index 518688833..2430a166b 100644 --- a/src/pkg/syscall/types_openbsd.go +++ b/src/pkg/syscall/types_openbsd.go @@ -187,11 +187,12 @@ type FdSet C.fd_set // Routing and interface messages const ( - SizeofIfMsghdr = C.sizeof_struct_if_msghdr - SizeofIfData = C.sizeof_struct_if_data - SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr - SizeofRtMsghdr = C.sizeof_struct_rt_msghdr - SizeofRtMetrics = C.sizeof_struct_rt_metrics + SizeofIfMsghdr = C.sizeof_struct_if_msghdr + SizeofIfData = C.sizeof_struct_if_data + SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr + SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr + SizeofRtMsghdr = C.sizeof_struct_rt_msghdr + SizeofRtMetrics = C.sizeof_struct_rt_metrics ) type IfMsghdr C.struct_if_msghdr @@ -200,6 +201,8 @@ type IfData C.struct_if_data type IfaMsghdr C.struct_ifa_msghdr +type IfAnnounceMsghdr C.struct_if_announcemsghdr + type RtMsghdr C.struct_rt_msghdr type RtMetrics C.struct_rt_metrics diff --git a/src/pkg/syscall/zerrors_darwin_386.go b/src/pkg/syscall/zerrors_darwin_386.go index 09d7b0fe9..cdb860543 100644 --- a/src/pkg/syscall/zerrors_darwin_386.go +++ b/src/pkg/syscall/zerrors_darwin_386.go @@ -43,6 +43,29 @@ const ( AF_SYSTEM = 0x20 AF_UNIX = 0x1 AF_UNSPEC = 0x0 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B9600 = 0x2580 BIOCFLUSH = 0x20004268 BIOCGBLEN = 0x40044266 BIOCGDLT = 0x4004426a @@ -108,6 +131,20 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x8000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0x14 + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a CTL_MAXNAME = 0xc CTL_NET = 0x4 DLT_APPLE_IP_OVER_IEEE1394 = 0x8a @@ -186,6 +223,7 @@ const ( EXTPROC = 0x800 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 + FLUSHO = 0x800000 F_ADDFILESIGS = 0x3d F_ADDSIGS = 0x3b F_ALLOCATEALL = 0x4 @@ -233,6 +271,10 @@ const ( F_VOLPOSMODE = 0x4 F_WRITEBOOTSTRAP = 0x2f F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 IFF_ALLMULTI = 0x200 IFF_ALTPHYS = 0x4000 IFF_BROADCAST = 0x2 @@ -318,6 +360,12 @@ const ( IFT_X25DDN = 0x4 IFT_X25PLE = 0x28 IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 IN_CLASSA_HOST = 0xffffff IN_CLASSA_MAX = 0x80 IN_CLASSA_NET = 0xff000000 @@ -560,6 +608,12 @@ const ( IP_TRAFFIC_MGT_BACKGROUND = 0x41 IP_TTL = 0x4 IP_UNBLOCK_SOURCE = 0x49 + ISIG = 0x80 + ISTRIP = 0x20 + IUTF8 = 0x4000 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 LOCK_EX = 0x2 LOCK_NB = 0x4 LOCK_SH = 0x1 @@ -619,6 +673,7 @@ const ( NET_RT_MAXID = 0xa NET_RT_STAT = 0x4 NET_RT_TRASH = 0x5 + NOFLSH = 0x80000000 NOTE_ABSOLUTE = 0x8 NOTE_ATTRIB = 0x8 NOTE_CHILD = 0x4 @@ -655,6 +710,14 @@ const ( NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000 NOTE_VM_PRESSURE_TERMINATE = 0x40000000 NOTE_WRITE = 0x2 + OCRNL = 0x10 + OFDEL = 0x20000 + OFILL = 0x80 + ONLCR = 0x2 + ONLRET = 0x40 + ONOCR = 0x20 + ONOEOT = 0x8 + OPOST = 0x1 O_ACCMODE = 0x3 O_ALERT = 0x20000000 O_APPEND = 0x8 @@ -679,6 +742,10 @@ const ( O_SYNC = 0x80 O_TRUNC = 0x400 O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 PROT_EXEC = 0x4 PROT_NONE = 0x0 PROT_READ = 0x1 @@ -937,6 +1004,9 @@ const ( S_IXGRP = 0x8 S_IXOTH = 0x1 S_IXUSR = 0x40 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 TCP_CONNECTIONTIMEOUT = 0x20 TCP_KEEPALIVE = 0x10 TCP_MAXHLEN = 0x3c @@ -953,6 +1023,7 @@ const ( TCP_NOPUSH = 0x4 TCP_RXT_CONNDROPTIME = 0x80 TCP_RXT_FINDROP = 0x100 + TCSAFLUSH = 0x2 TIOCCBRK = 0x2000747a TIOCCDTR = 0x20007478 TIOCCONS = 0x80047462 @@ -1022,6 +1093,28 @@ const ( TIOCSWINSZ = 0x80087467 TIOCTIMESTAMP = 0x40087459 TIOCUCNTL = 0x80047466 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VT0 = 0x0 + VT1 = 0x10000 + VTDLY = 0x10000 + VTIME = 0x11 + VWERASE = 0x4 WCONTINUED = 0x10 WCOREFLAG = 0x80 WEXITED = 0x4 diff --git a/src/pkg/syscall/zerrors_darwin_amd64.go b/src/pkg/syscall/zerrors_darwin_amd64.go index 7fa5cfcd2..8ffcbcc04 100644 --- a/src/pkg/syscall/zerrors_darwin_amd64.go +++ b/src/pkg/syscall/zerrors_darwin_amd64.go @@ -43,6 +43,29 @@ const ( AF_SYSTEM = 0x20 AF_UNIX = 0x1 AF_UNSPEC = 0x0 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B9600 = 0x2580 BIOCFLUSH = 0x20004268 BIOCGBLEN = 0x40044266 BIOCGDLT = 0x4004426a @@ -108,6 +131,20 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x8000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0x14 + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a CTL_MAXNAME = 0xc CTL_NET = 0x4 DLT_APPLE_IP_OVER_IEEE1394 = 0x8a @@ -186,6 +223,7 @@ const ( EXTPROC = 0x800 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 + FLUSHO = 0x800000 F_ADDFILESIGS = 0x3d F_ADDSIGS = 0x3b F_ALLOCATEALL = 0x4 @@ -233,6 +271,10 @@ const ( F_VOLPOSMODE = 0x4 F_WRITEBOOTSTRAP = 0x2f F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 IFF_ALLMULTI = 0x200 IFF_ALTPHYS = 0x4000 IFF_BROADCAST = 0x2 @@ -318,6 +360,12 @@ const ( IFT_X25DDN = 0x4 IFT_X25PLE = 0x28 IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 IN_CLASSA_HOST = 0xffffff IN_CLASSA_MAX = 0x80 IN_CLASSA_NET = 0xff000000 @@ -560,6 +608,12 @@ const ( IP_TRAFFIC_MGT_BACKGROUND = 0x41 IP_TTL = 0x4 IP_UNBLOCK_SOURCE = 0x49 + ISIG = 0x80 + ISTRIP = 0x20 + IUTF8 = 0x4000 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 LOCK_EX = 0x2 LOCK_NB = 0x4 LOCK_SH = 0x1 @@ -619,6 +673,7 @@ const ( NET_RT_MAXID = 0xa NET_RT_STAT = 0x4 NET_RT_TRASH = 0x5 + NOFLSH = 0x80000000 NOTE_ABSOLUTE = 0x8 NOTE_ATTRIB = 0x8 NOTE_CHILD = 0x4 @@ -655,6 +710,14 @@ const ( NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000 NOTE_VM_PRESSURE_TERMINATE = 0x40000000 NOTE_WRITE = 0x2 + OCRNL = 0x10 + OFDEL = 0x20000 + OFILL = 0x80 + ONLCR = 0x2 + ONLRET = 0x40 + ONOCR = 0x20 + ONOEOT = 0x8 + OPOST = 0x1 O_ACCMODE = 0x3 O_ALERT = 0x20000000 O_APPEND = 0x8 @@ -679,6 +742,10 @@ const ( O_SYNC = 0x80 O_TRUNC = 0x400 O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 PROT_EXEC = 0x4 PROT_NONE = 0x0 PROT_READ = 0x1 @@ -937,6 +1004,9 @@ const ( S_IXGRP = 0x8 S_IXOTH = 0x1 S_IXUSR = 0x40 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 TCP_CONNECTIONTIMEOUT = 0x20 TCP_KEEPALIVE = 0x10 TCP_MAXHLEN = 0x3c @@ -953,6 +1023,7 @@ const ( TCP_NOPUSH = 0x4 TCP_RXT_CONNDROPTIME = 0x80 TCP_RXT_FINDROP = 0x100 + TCSAFLUSH = 0x2 TIOCCBRK = 0x2000747a TIOCCDTR = 0x20007478 TIOCCONS = 0x80047462 @@ -1022,6 +1093,28 @@ const ( TIOCSWINSZ = 0x80087467 TIOCTIMESTAMP = 0x40107459 TIOCUCNTL = 0x80047466 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VT0 = 0x0 + VT1 = 0x10000 + VTDLY = 0x10000 + VTIME = 0x11 + VWERASE = 0x4 WCONTINUED = 0x10 WCOREFLAG = 0x80 WEXITED = 0x4 diff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.go index cc00f0dcf..9cf82f90e 100644 --- a/src/pkg/syscall/zerrors_freebsd_386.go +++ b/src/pkg/syscall/zerrors_freebsd_386.go @@ -369,6 +369,8 @@ const ( F_UNLCK = 0x2 F_UNLCKSYS = 0x4 F_WRLCK = 0x3 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 IFF_ALLMULTI = 0x200 IFF_ALTPHYS = 0x4000 IFF_BROADCAST = 0x2 diff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.go index b7b27b56f..732570659 100644 --- a/src/pkg/syscall/zerrors_freebsd_amd64.go +++ b/src/pkg/syscall/zerrors_freebsd_amd64.go @@ -369,6 +369,8 @@ const ( F_UNLCK = 0x2 F_UNLCKSYS = 0x4 F_WRLCK = 0x3 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 IFF_ALLMULTI = 0x200 IFF_ALTPHYS = 0x4000 IFF_BROADCAST = 0x2 diff --git a/src/pkg/syscall/zerrors_freebsd_arm.go b/src/pkg/syscall/zerrors_freebsd_arm.go new file mode 100644 index 000000000..92cade265 --- /dev/null +++ b/src/pkg/syscall/zerrors_freebsd_arm.go @@ -0,0 +1,1652 @@ +// mkerrors.sh +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs -- _const.go + +package syscall + +const ( + //O_CLOEXEC = 0x100000 + AF_APPLETALK = 0x10 + AF_ARP = 0x23 + AF_ATM = 0x1e + AF_BLUETOOTH = 0x24 + AF_CCITT = 0xa + AF_CHAOS = 0x5 + AF_CNT = 0x15 + AF_COIP = 0x14 + AF_DATAKIT = 0x9 + AF_DECnet = 0xc + AF_DLI = 0xd + AF_E164 = 0x1a + AF_ECMA = 0x8 + AF_HYLINK = 0xf + AF_IEEE80211 = 0x25 + AF_IMPLINK = 0x3 + AF_INET = 0x2 + AF_INET6 = 0x1c + AF_IPX = 0x17 + AF_ISDN = 0x1a + AF_ISO = 0x7 + AF_LAT = 0xe + AF_LINK = 0x12 + AF_LOCAL = 0x1 + AF_MAX = 0x26 + AF_NATM = 0x1d + AF_NETBIOS = 0x6 + AF_NETGRAPH = 0x20 + AF_OSI = 0x7 + AF_PUP = 0x4 + AF_ROUTE = 0x11 + AF_SCLUSTER = 0x22 + AF_SIP = 0x18 + AF_SLOW = 0x21 + AF_SNA = 0xb + AF_UNIX = 0x1 + AF_UNSPEC = 0x0 + AF_VENDOR00 = 0x27 + AF_VENDOR01 = 0x29 + AF_VENDOR02 = 0x2b + AF_VENDOR03 = 0x2d + AF_VENDOR04 = 0x2f + AF_VENDOR05 = 0x31 + AF_VENDOR06 = 0x33 + AF_VENDOR07 = 0x35 + AF_VENDOR08 = 0x37 + AF_VENDOR09 = 0x39 + AF_VENDOR10 = 0x3b + AF_VENDOR11 = 0x3d + AF_VENDOR12 = 0x3f + AF_VENDOR13 = 0x41 + AF_VENDOR14 = 0x43 + AF_VENDOR15 = 0x45 + AF_VENDOR16 = 0x47 + AF_VENDOR17 = 0x49 + AF_VENDOR18 = 0x4b + AF_VENDOR19 = 0x4d + AF_VENDOR20 = 0x4f + AF_VENDOR21 = 0x51 + AF_VENDOR22 = 0x53 + AF_VENDOR23 = 0x55 + AF_VENDOR24 = 0x57 + AF_VENDOR25 = 0x59 + AF_VENDOR26 = 0x5b + AF_VENDOR27 = 0x5d + AF_VENDOR28 = 0x5f + AF_VENDOR29 = 0x61 + AF_VENDOR30 = 0x63 + AF_VENDOR31 = 0x65 + AF_VENDOR32 = 0x67 + AF_VENDOR33 = 0x69 + AF_VENDOR34 = 0x6b + AF_VENDOR35 = 0x6d + AF_VENDOR36 = 0x6f + AF_VENDOR37 = 0x71 + AF_VENDOR38 = 0x73 + AF_VENDOR39 = 0x75 + AF_VENDOR40 = 0x77 + AF_VENDOR41 = 0x79 + AF_VENDOR42 = 0x7b + AF_VENDOR43 = 0x7d + AF_VENDOR44 = 0x7f + AF_VENDOR45 = 0x81 + AF_VENDOR46 = 0x83 + AF_VENDOR47 = 0x85 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B460800 = 0x70800 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B921600 = 0xe1000 + B9600 = 0x2580 + BIOCFEEDBACK = 0x8004427c + BIOCFLUSH = 0x20004268 + BIOCGBLEN = 0x40044266 + BIOCGDIRECTION = 0x40044276 + BIOCGDLT = 0x4004426a + BIOCGDLTLIST = 0xc0084279 + BIOCGETBUFMODE = 0x4004427d + BIOCGETIF = 0x4020426b + BIOCGETZMAX = 0x4004427f + BIOCGHDRCMPLT = 0x40044274 + BIOCGRSIG = 0x40044272 + BIOCGRTIMEOUT = 0x400c426e + BIOCGSEESENT = 0x40044276 + BIOCGSTATS = 0x4008426f + BIOCGTSTAMP = 0x40044283 + BIOCIMMEDIATE = 0x80044270 + BIOCLOCK = 0x2000427a + BIOCPROMISC = 0x20004269 + BIOCROTZBUF = 0x400c4280 + BIOCSBLEN = 0xc0044266 + BIOCSDIRECTION = 0x80044277 + BIOCSDLT = 0x80044278 + BIOCSETBUFMODE = 0x8004427e + BIOCSETF = 0x80084267 + BIOCSETFNR = 0x80084282 + BIOCSETIF = 0x8020426c + BIOCSETWF = 0x8008427b + BIOCSETZBUF = 0x800c4281 + BIOCSHDRCMPLT = 0x80044275 + BIOCSRSIG = 0x80044273 + BIOCSRTIMEOUT = 0x800c426d + BIOCSSEESENT = 0x80044277 + BIOCSTSTAMP = 0x80044284 + BIOCVERSION = 0x40044271 + BPF_A = 0x10 + BPF_ABS = 0x20 + BPF_ADD = 0x0 + BPF_ALIGNMENT = 0x4 + BPF_ALU = 0x4 + BPF_AND = 0x50 + BPF_B = 0x10 + BPF_BUFMODE_BUFFER = 0x1 + BPF_BUFMODE_ZBUF = 0x2 + BPF_DIV = 0x30 + BPF_H = 0x8 + BPF_IMM = 0x0 + BPF_IND = 0x40 + BPF_JA = 0x0 + BPF_JEQ = 0x10 + BPF_JGE = 0x30 + BPF_JGT = 0x20 + BPF_JMP = 0x5 + BPF_JSET = 0x40 + BPF_K = 0x0 + BPF_LD = 0x0 + BPF_LDX = 0x1 + BPF_LEN = 0x80 + BPF_LSH = 0x60 + BPF_MAJOR_VERSION = 0x1 + BPF_MAXBUFSIZE = 0x80000 + BPF_MAXINSNS = 0x200 + BPF_MEM = 0x60 + BPF_MEMWORDS = 0x10 + BPF_MINBUFSIZE = 0x20 + BPF_MINOR_VERSION = 0x1 + BPF_MISC = 0x7 + BPF_MSH = 0xa0 + BPF_MUL = 0x20 + BPF_NEG = 0x80 + BPF_OR = 0x40 + BPF_RELEASE = 0x30bb6 + BPF_RET = 0x6 + BPF_RSH = 0x70 + BPF_ST = 0x2 + BPF_STX = 0x3 + BPF_SUB = 0x10 + BPF_TAX = 0x0 + BPF_TXA = 0x80 + BPF_T_BINTIME = 0x2 + BPF_T_BINTIME_FAST = 0x102 + BPF_T_BINTIME_MONOTONIC = 0x202 + BPF_T_BINTIME_MONOTONIC_FAST = 0x302 + BPF_T_FAST = 0x100 + BPF_T_FLAG_MASK = 0x300 + BPF_T_FORMAT_MASK = 0x3 + BPF_T_MICROTIME = 0x0 + BPF_T_MICROTIME_FAST = 0x100 + BPF_T_MICROTIME_MONOTONIC = 0x200 + BPF_T_MICROTIME_MONOTONIC_FAST = 0x300 + BPF_T_MONOTONIC = 0x200 + BPF_T_MONOTONIC_FAST = 0x300 + BPF_T_NANOTIME = 0x1 + BPF_T_NANOTIME_FAST = 0x101 + BPF_T_NANOTIME_MONOTONIC = 0x201 + BPF_T_NANOTIME_MONOTONIC_FAST = 0x301 + BPF_T_NONE = 0x3 + BPF_T_NORMAL = 0x0 + BPF_W = 0x0 + BPF_X = 0x8 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x8000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0x14 + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a + CTL_MAXNAME = 0x18 + CTL_NET = 0x4 + DLT_A429 = 0xb8 + DLT_A653_ICM = 0xb9 + DLT_AIRONET_HEADER = 0x78 + DLT_AOS = 0xde + DLT_APPLE_IP_OVER_IEEE1394 = 0x8a + DLT_ARCNET = 0x7 + DLT_ARCNET_LINUX = 0x81 + DLT_ATM_CLIP = 0x13 + DLT_ATM_RFC1483 = 0xb + DLT_AURORA = 0x7e + DLT_AX25 = 0x3 + DLT_AX25_KISS = 0xca + DLT_BACNET_MS_TP = 0xa5 + DLT_BLUETOOTH_HCI_H4 = 0xbb + DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9 + DLT_CAN20B = 0xbe + DLT_CAN_SOCKETCAN = 0xe3 + DLT_CHAOS = 0x5 + DLT_CHDLC = 0x68 + DLT_CISCO_IOS = 0x76 + DLT_C_HDLC = 0x68 + DLT_C_HDLC_WITH_DIR = 0xcd + DLT_DBUS = 0xe7 + DLT_DECT = 0xdd + DLT_DOCSIS = 0x8f + DLT_DVB_CI = 0xeb + DLT_ECONET = 0x73 + DLT_EN10MB = 0x1 + DLT_EN3MB = 0x2 + DLT_ENC = 0x6d + DLT_ERF = 0xc5 + DLT_ERF_ETH = 0xaf + DLT_ERF_POS = 0xb0 + DLT_FC_2 = 0xe0 + DLT_FC_2_WITH_FRAME_DELIMS = 0xe1 + DLT_FDDI = 0xa + DLT_FLEXRAY = 0xd2 + DLT_FRELAY = 0x6b + DLT_FRELAY_WITH_DIR = 0xce + DLT_GCOM_SERIAL = 0xad + DLT_GCOM_T1E1 = 0xac + DLT_GPF_F = 0xab + DLT_GPF_T = 0xaa + DLT_GPRS_LLC = 0xa9 + DLT_GSMTAP_ABIS = 0xda + DLT_GSMTAP_UM = 0xd9 + DLT_HHDLC = 0x79 + DLT_IBM_SN = 0x92 + DLT_IBM_SP = 0x91 + DLT_IEEE802 = 0x6 + DLT_IEEE802_11 = 0x69 + DLT_IEEE802_11_RADIO = 0x7f + DLT_IEEE802_11_RADIO_AVS = 0xa3 + DLT_IEEE802_15_4 = 0xc3 + DLT_IEEE802_15_4_LINUX = 0xbf + DLT_IEEE802_15_4_NOFCS = 0xe6 + DLT_IEEE802_15_4_NONASK_PHY = 0xd7 + DLT_IEEE802_16_MAC_CPS = 0xbc + DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 + DLT_IPFILTER = 0x74 + DLT_IPMB = 0xc7 + DLT_IPMB_LINUX = 0xd1 + DLT_IPNET = 0xe2 + DLT_IPOIB = 0xf2 + DLT_IPV4 = 0xe4 + DLT_IPV6 = 0xe5 + DLT_IP_OVER_FC = 0x7a + DLT_JUNIPER_ATM1 = 0x89 + DLT_JUNIPER_ATM2 = 0x87 + DLT_JUNIPER_ATM_CEMIC = 0xee + DLT_JUNIPER_CHDLC = 0xb5 + DLT_JUNIPER_ES = 0x84 + DLT_JUNIPER_ETHER = 0xb2 + DLT_JUNIPER_FIBRECHANNEL = 0xea + DLT_JUNIPER_FRELAY = 0xb4 + DLT_JUNIPER_GGSN = 0x85 + DLT_JUNIPER_ISM = 0xc2 + DLT_JUNIPER_MFR = 0x86 + DLT_JUNIPER_MLFR = 0x83 + DLT_JUNIPER_MLPPP = 0x82 + DLT_JUNIPER_MONITOR = 0xa4 + DLT_JUNIPER_PIC_PEER = 0xae + DLT_JUNIPER_PPP = 0xb3 + DLT_JUNIPER_PPPOE = 0xa7 + DLT_JUNIPER_PPPOE_ATM = 0xa8 + DLT_JUNIPER_SERVICES = 0x88 + DLT_JUNIPER_SRX_E2E = 0xe9 + DLT_JUNIPER_ST = 0xc8 + DLT_JUNIPER_VP = 0xb7 + DLT_JUNIPER_VS = 0xe8 + DLT_LAPB_WITH_DIR = 0xcf + DLT_LAPD = 0xcb + DLT_LIN = 0xd4 + DLT_LINUX_EVDEV = 0xd8 + DLT_LINUX_IRDA = 0x90 + DLT_LINUX_LAPD = 0xb1 + DLT_LINUX_PPP_WITHDIRECTION = 0xa6 + DLT_LINUX_SLL = 0x71 + DLT_LOOP = 0x6c + DLT_LTALK = 0x72 + DLT_MATCHING_MAX = 0xf2 + DLT_MATCHING_MIN = 0x68 + DLT_MFR = 0xb6 + DLT_MOST = 0xd3 + DLT_MPLS = 0xdb + DLT_MTP2 = 0x8c + DLT_MTP2_WITH_PHDR = 0x8b + DLT_MTP3 = 0x8d + DLT_MUX27010 = 0xec + DLT_NETANALYZER = 0xf0 + DLT_NETANALYZER_TRANSPARENT = 0xf1 + DLT_NFLOG = 0xef + DLT_NULL = 0x0 + DLT_PCI_EXP = 0x7d + DLT_PFLOG = 0x75 + DLT_PFSYNC = 0x79 + DLT_PPI = 0xc0 + DLT_PPP = 0x9 + DLT_PPP_BSDOS = 0x10 + DLT_PPP_ETHER = 0x33 + DLT_PPP_PPPD = 0xa6 + DLT_PPP_SERIAL = 0x32 + DLT_PPP_WITH_DIR = 0xcc + DLT_PPP_WITH_DIRECTION = 0xa6 + DLT_PRISM_HEADER = 0x77 + DLT_PRONET = 0x4 + DLT_RAIF1 = 0xc6 + DLT_RAW = 0xc + DLT_RIO = 0x7c + DLT_SCCP = 0x8e + DLT_SITA = 0xc4 + DLT_SLIP = 0x8 + DLT_SLIP_BSDOS = 0xf + DLT_STANAG_5066_D_PDU = 0xed + DLT_SUNATM = 0x7b + DLT_SYMANTEC_FIREWALL = 0x63 + DLT_TZSP = 0x80 + DLT_USB = 0xba + DLT_USB_LINUX = 0xbd + DLT_USB_LINUX_MMAPPED = 0xdc + DLT_USER0 = 0x93 + DLT_USER1 = 0x94 + DLT_USER10 = 0x9d + DLT_USER11 = 0x9e + DLT_USER12 = 0x9f + DLT_USER13 = 0xa0 + DLT_USER14 = 0xa1 + DLT_USER15 = 0xa2 + DLT_USER2 = 0x95 + DLT_USER3 = 0x96 + DLT_USER4 = 0x97 + DLT_USER5 = 0x98 + DLT_USER6 = 0x99 + DLT_USER7 = 0x9a + DLT_USER8 = 0x9b + DLT_USER9 = 0x9c + DLT_WIHART = 0xdf + DLT_X2E_SERIAL = 0xd5 + DLT_X2E_XORAYA = 0xd6 + DT_BLK = 0x6 + DT_CHR = 0x2 + DT_DIR = 0x4 + DT_FIFO = 0x1 + DT_LNK = 0xa + DT_REG = 0x8 + DT_SOCK = 0xc + DT_UNKNOWN = 0x0 + DT_WHT = 0xe + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 + EVFILT_AIO = -0x3 + EVFILT_FS = -0x9 + EVFILT_LIO = -0xa + EVFILT_PROC = -0x5 + EVFILT_READ = -0x1 + EVFILT_SIGNAL = -0x6 + EVFILT_SYSCOUNT = 0xb + EVFILT_TIMER = -0x7 + EVFILT_USER = -0xb + EVFILT_VNODE = -0x4 + EVFILT_WRITE = -0x2 + EV_ADD = 0x1 + EV_CLEAR = 0x20 + EV_DELETE = 0x2 + EV_DISABLE = 0x8 + EV_DISPATCH = 0x80 + EV_ENABLE = 0x4 + EV_EOF = 0x8000 + EV_ERROR = 0x4000 + EV_FLAG1 = 0x2000 + EV_ONESHOT = 0x10 + EV_RECEIPT = 0x40 + EV_SYSFLAGS = 0xf000 + EXTA = 0x4b00 + EXTB = 0x9600 + EXTPROC = 0x800 + FD_CLOEXEC = 0x1 + FD_SETSIZE = 0x400 + FLUSHO = 0x800000 + F_CANCEL = 0x5 + F_DUP2FD = 0xa + F_DUP2FD_CLOEXEC = 0x12 + F_DUPFD = 0x0 + F_DUPFD_CLOEXEC = 0x11 + F_GETFD = 0x1 + F_GETFL = 0x3 + F_GETLK = 0xb + F_GETOWN = 0x5 + F_OGETLK = 0x7 + F_OK = 0x0 + F_OSETLK = 0x8 + F_OSETLKW = 0x9 + F_RDAHEAD = 0x10 + F_RDLCK = 0x1 + F_READAHEAD = 0xf + F_SETFD = 0x2 + F_SETFL = 0x4 + F_SETLK = 0xc + F_SETLKW = 0xd + F_SETLK_REMOTE = 0xe + F_SETOWN = 0x6 + F_UNLCK = 0x2 + F_UNLCKSYS = 0x4 + F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 + IFF_ALLMULTI = 0x200 + IFF_ALTPHYS = 0x4000 + IFF_BROADCAST = 0x2 + IFF_CANTCHANGE = 0x218f72 + IFF_CANTCONFIG = 0x10000 + IFF_DEBUG = 0x4 + IFF_DRV_OACTIVE = 0x400 + IFF_DRV_RUNNING = 0x40 + IFF_DYING = 0x200000 + IFF_LINK0 = 0x1000 + IFF_LINK1 = 0x2000 + IFF_LINK2 = 0x4000 + IFF_LOOPBACK = 0x8 + IFF_MONITOR = 0x40000 + IFF_MULTICAST = 0x8000 + IFF_NOARP = 0x80 + IFF_OACTIVE = 0x400 + IFF_POINTOPOINT = 0x10 + IFF_PPROMISC = 0x20000 + IFF_PROMISC = 0x100 + IFF_RENAMING = 0x400000 + IFF_RUNNING = 0x40 + IFF_SIMPLEX = 0x800 + IFF_SMART = 0x20 + IFF_STATICARP = 0x80000 + IFF_UP = 0x1 + IFNAMSIZ = 0x10 + IFT_1822 = 0x2 + IFT_A12MPPSWITCH = 0x82 + IFT_AAL2 = 0xbb + IFT_AAL5 = 0x31 + IFT_ADSL = 0x5e + IFT_AFLANE8023 = 0x3b + IFT_AFLANE8025 = 0x3c + IFT_ARAP = 0x58 + IFT_ARCNET = 0x23 + IFT_ARCNETPLUS = 0x24 + IFT_ASYNC = 0x54 + IFT_ATM = 0x25 + IFT_ATMDXI = 0x69 + IFT_ATMFUNI = 0x6a + IFT_ATMIMA = 0x6b + IFT_ATMLOGICAL = 0x50 + IFT_ATMRADIO = 0xbd + IFT_ATMSUBINTERFACE = 0x86 + IFT_ATMVCIENDPT = 0xc2 + IFT_ATMVIRTUAL = 0x95 + IFT_BGPPOLICYACCOUNTING = 0xa2 + IFT_BRIDGE = 0xd1 + IFT_BSC = 0x53 + IFT_CCTEMUL = 0x3d + IFT_CEPT = 0x13 + IFT_CES = 0x85 + IFT_CHANNEL = 0x46 + IFT_CNR = 0x55 + IFT_COFFEE = 0x84 + IFT_COMPOSITELINK = 0x9b + IFT_DCN = 0x8d + IFT_DIGITALPOWERLINE = 0x8a + IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba + IFT_DLSW = 0x4a + IFT_DOCSCABLEDOWNSTREAM = 0x80 + IFT_DOCSCABLEMACLAYER = 0x7f + IFT_DOCSCABLEUPSTREAM = 0x81 + IFT_DS0 = 0x51 + IFT_DS0BUNDLE = 0x52 + IFT_DS1FDL = 0xaa + IFT_DS3 = 0x1e + IFT_DTM = 0x8c + IFT_DVBASILN = 0xac + IFT_DVBASIOUT = 0xad + IFT_DVBRCCDOWNSTREAM = 0x93 + IFT_DVBRCCMACLAYER = 0x92 + IFT_DVBRCCUPSTREAM = 0x94 + IFT_ENC = 0xf4 + IFT_EON = 0x19 + IFT_EPLRS = 0x57 + IFT_ESCON = 0x49 + IFT_ETHER = 0x6 + IFT_FAITH = 0xf2 + IFT_FAST = 0x7d + IFT_FASTETHER = 0x3e + IFT_FASTETHERFX = 0x45 + IFT_FDDI = 0xf + IFT_FIBRECHANNEL = 0x38 + IFT_FRAMERELAYINTERCONNECT = 0x3a + IFT_FRAMERELAYMPI = 0x5c + IFT_FRDLCIENDPT = 0xc1 + IFT_FRELAY = 0x20 + IFT_FRELAYDCE = 0x2c + IFT_FRF16MFRBUNDLE = 0xa3 + IFT_FRFORWARD = 0x9e + IFT_G703AT2MB = 0x43 + IFT_G703AT64K = 0x42 + IFT_GIF = 0xf0 + IFT_GIGABITETHERNET = 0x75 + IFT_GR303IDT = 0xb2 + IFT_GR303RDT = 0xb1 + IFT_H323GATEKEEPER = 0xa4 + IFT_H323PROXY = 0xa5 + IFT_HDH1822 = 0x3 + IFT_HDLC = 0x76 + IFT_HDSL2 = 0xa8 + IFT_HIPERLAN2 = 0xb7 + IFT_HIPPI = 0x2f + IFT_HIPPIINTERFACE = 0x39 + IFT_HOSTPAD = 0x5a + IFT_HSSI = 0x2e + IFT_HY = 0xe + IFT_IBM370PARCHAN = 0x48 + IFT_IDSL = 0x9a + IFT_IEEE1394 = 0x90 + IFT_IEEE80211 = 0x47 + IFT_IEEE80212 = 0x37 + IFT_IEEE8023ADLAG = 0xa1 + IFT_IFGSN = 0x91 + IFT_IMT = 0xbe + IFT_INFINIBAND = 0xc7 + IFT_INTERLEAVE = 0x7c + IFT_IP = 0x7e + IFT_IPFORWARD = 0x8e + IFT_IPOVERATM = 0x72 + IFT_IPOVERCDLC = 0x6d + IFT_IPOVERCLAW = 0x6e + IFT_IPSWITCH = 0x4e + IFT_IPXIP = 0xf9 + IFT_ISDN = 0x3f + IFT_ISDNBASIC = 0x14 + IFT_ISDNPRIMARY = 0x15 + IFT_ISDNS = 0x4b + IFT_ISDNU = 0x4c + IFT_ISO88022LLC = 0x29 + IFT_ISO88023 = 0x7 + IFT_ISO88024 = 0x8 + IFT_ISO88025 = 0x9 + IFT_ISO88025CRFPINT = 0x62 + IFT_ISO88025DTR = 0x56 + IFT_ISO88025FIBER = 0x73 + IFT_ISO88026 = 0xa + IFT_ISUP = 0xb3 + IFT_L2VLAN = 0x87 + IFT_L3IPVLAN = 0x88 + IFT_L3IPXVLAN = 0x89 + IFT_LAPB = 0x10 + IFT_LAPD = 0x4d + IFT_LAPF = 0x77 + IFT_LOCALTALK = 0x2a + IFT_LOOP = 0x18 + IFT_MEDIAMAILOVERIP = 0x8b + IFT_MFSIGLINK = 0xa7 + IFT_MIOX25 = 0x26 + IFT_MODEM = 0x30 + IFT_MPC = 0x71 + IFT_MPLS = 0xa6 + IFT_MPLSTUNNEL = 0x96 + IFT_MSDSL = 0x8f + IFT_MVL = 0xbf + IFT_MYRINET = 0x63 + IFT_NFAS = 0xaf + IFT_NSIP = 0x1b + IFT_OPTICALCHANNEL = 0xc3 + IFT_OPTICALTRANSPORT = 0xc4 + IFT_OTHER = 0x1 + IFT_P10 = 0xc + IFT_P80 = 0xd + IFT_PARA = 0x22 + IFT_PFLOG = 0xf6 + IFT_PFSYNC = 0xf7 + IFT_PLC = 0xae + IFT_POS = 0xab + IFT_PPP = 0x17 + IFT_PPPMULTILINKBUNDLE = 0x6c + IFT_PROPBWAP2MP = 0xb8 + IFT_PROPCNLS = 0x59 + IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5 + IFT_PROPDOCSWIRELESSMACLAYER = 0xb4 + IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6 + IFT_PROPMUX = 0x36 + IFT_PROPVIRTUAL = 0x35 + IFT_PROPWIRELESSP2P = 0x9d + IFT_PTPSERIAL = 0x16 + IFT_PVC = 0xf1 + IFT_QLLC = 0x44 + IFT_RADIOMAC = 0xbc + IFT_RADSL = 0x5f + IFT_REACHDSL = 0xc0 + IFT_RFC1483 = 0x9f + IFT_RS232 = 0x21 + IFT_RSRB = 0x4f + IFT_SDLC = 0x11 + IFT_SDSL = 0x60 + IFT_SHDSL = 0xa9 + IFT_SIP = 0x1f + IFT_SLIP = 0x1c + IFT_SMDSDXI = 0x2b + IFT_SMDSICIP = 0x34 + IFT_SONET = 0x27 + IFT_SONETOVERHEADCHANNEL = 0xb9 + IFT_SONETPATH = 0x32 + IFT_SONETVT = 0x33 + IFT_SRP = 0x97 + IFT_SS7SIGLINK = 0x9c + IFT_STACKTOSTACK = 0x6f + IFT_STARLAN = 0xb + IFT_STF = 0xd7 + IFT_T1 = 0x12 + IFT_TDLC = 0x74 + IFT_TERMPAD = 0x5b + IFT_TR008 = 0xb0 + IFT_TRANSPHDLC = 0x7b + IFT_TUNNEL = 0x83 + IFT_ULTRA = 0x1d + IFT_USB = 0xa0 + IFT_V11 = 0x40 + IFT_V35 = 0x2d + IFT_V36 = 0x41 + IFT_V37 = 0x78 + IFT_VDSL = 0x61 + IFT_VIRTUALIPADDRESS = 0x70 + IFT_VOICEEM = 0x64 + IFT_VOICEENCAP = 0x67 + IFT_VOICEFXO = 0x65 + IFT_VOICEFXS = 0x66 + IFT_VOICEOVERATM = 0x98 + IFT_VOICEOVERFRAMERELAY = 0x99 + IFT_VOICEOVERIP = 0x68 + IFT_X213 = 0x5d + IFT_X25 = 0x5 + IFT_X25DDN = 0x4 + IFT_X25HUNTGROUP = 0x7a + IFT_X25MLP = 0x79 + IFT_X25PLE = 0x28 + IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 + IN_CLASSA_HOST = 0xffffff + IN_CLASSA_MAX = 0x80 + IN_CLASSA_NET = 0xff000000 + IN_CLASSA_NSHIFT = 0x18 + IN_CLASSB_HOST = 0xffff + IN_CLASSB_MAX = 0x10000 + IN_CLASSB_NET = 0xffff0000 + IN_CLASSB_NSHIFT = 0x10 + IN_CLASSC_HOST = 0xff + IN_CLASSC_NET = 0xffffff00 + IN_CLASSC_NSHIFT = 0x8 + IN_CLASSD_HOST = 0xfffffff + IN_CLASSD_NET = 0xf0000000 + IN_CLASSD_NSHIFT = 0x1c + IN_LOOPBACKNET = 0x7f + IN_RFC3021_MASK = 0xfffffffe + IPPROTO_3PC = 0x22 + IPPROTO_ADFS = 0x44 + IPPROTO_AH = 0x33 + IPPROTO_AHIP = 0x3d + IPPROTO_APES = 0x63 + IPPROTO_ARGUS = 0xd + IPPROTO_AX25 = 0x5d + IPPROTO_BHA = 0x31 + IPPROTO_BLT = 0x1e + IPPROTO_BRSATMON = 0x4c + IPPROTO_CARP = 0x70 + IPPROTO_CFTP = 0x3e + IPPROTO_CHAOS = 0x10 + IPPROTO_CMTP = 0x26 + IPPROTO_CPHB = 0x49 + IPPROTO_CPNX = 0x48 + IPPROTO_DDP = 0x25 + IPPROTO_DGP = 0x56 + IPPROTO_DIVERT = 0x102 + IPPROTO_DONE = 0x101 + IPPROTO_DSTOPTS = 0x3c + IPPROTO_EGP = 0x8 + IPPROTO_EMCON = 0xe + IPPROTO_ENCAP = 0x62 + IPPROTO_EON = 0x50 + IPPROTO_ESP = 0x32 + IPPROTO_ETHERIP = 0x61 + IPPROTO_FRAGMENT = 0x2c + IPPROTO_GGP = 0x3 + IPPROTO_GMTP = 0x64 + IPPROTO_GRE = 0x2f + IPPROTO_HELLO = 0x3f + IPPROTO_HMP = 0x14 + IPPROTO_HOPOPTS = 0x0 + IPPROTO_ICMP = 0x1 + IPPROTO_ICMPV6 = 0x3a + IPPROTO_IDP = 0x16 + IPPROTO_IDPR = 0x23 + IPPROTO_IDRP = 0x2d + IPPROTO_IGMP = 0x2 + IPPROTO_IGP = 0x55 + IPPROTO_IGRP = 0x58 + IPPROTO_IL = 0x28 + IPPROTO_INLSP = 0x34 + IPPROTO_INP = 0x20 + IPPROTO_IP = 0x0 + IPPROTO_IPCOMP = 0x6c + IPPROTO_IPCV = 0x47 + IPPROTO_IPEIP = 0x5e + IPPROTO_IPIP = 0x4 + IPPROTO_IPPC = 0x43 + IPPROTO_IPV4 = 0x4 + IPPROTO_IPV6 = 0x29 + IPPROTO_IRTP = 0x1c + IPPROTO_KRYPTOLAN = 0x41 + IPPROTO_LARP = 0x5b + IPPROTO_LEAF1 = 0x19 + IPPROTO_LEAF2 = 0x1a + IPPROTO_MAX = 0x100 + IPPROTO_MAXID = 0x34 + IPPROTO_MEAS = 0x13 + IPPROTO_MH = 0x87 + IPPROTO_MHRP = 0x30 + IPPROTO_MICP = 0x5f + IPPROTO_MOBILE = 0x37 + IPPROTO_MPLS = 0x89 + IPPROTO_MTP = 0x5c + IPPROTO_MUX = 0x12 + IPPROTO_ND = 0x4d + IPPROTO_NHRP = 0x36 + IPPROTO_NONE = 0x3b + IPPROTO_NSP = 0x1f + IPPROTO_NVPII = 0xb + IPPROTO_OLD_DIVERT = 0xfe + IPPROTO_OSPFIGP = 0x59 + IPPROTO_PFSYNC = 0xf0 + IPPROTO_PGM = 0x71 + IPPROTO_PIGP = 0x9 + IPPROTO_PIM = 0x67 + IPPROTO_PRM = 0x15 + IPPROTO_PUP = 0xc + IPPROTO_PVP = 0x4b + IPPROTO_RAW = 0xff + IPPROTO_RCCMON = 0xa + IPPROTO_RDP = 0x1b + IPPROTO_ROUTING = 0x2b + IPPROTO_RSVP = 0x2e + IPPROTO_RVD = 0x42 + IPPROTO_SATEXPAK = 0x40 + IPPROTO_SATMON = 0x45 + IPPROTO_SCCSP = 0x60 + IPPROTO_SCTP = 0x84 + IPPROTO_SDRP = 0x2a + IPPROTO_SEND = 0x103 + IPPROTO_SEP = 0x21 + IPPROTO_SKIP = 0x39 + IPPROTO_SPACER = 0x7fff + IPPROTO_SRPC = 0x5a + IPPROTO_ST = 0x7 + IPPROTO_SVMTP = 0x52 + IPPROTO_SWIPE = 0x35 + IPPROTO_TCF = 0x57 + IPPROTO_TCP = 0x6 + IPPROTO_TLSP = 0x38 + IPPROTO_TP = 0x1d + IPPROTO_TPXX = 0x27 + IPPROTO_TRUNK1 = 0x17 + IPPROTO_TRUNK2 = 0x18 + IPPROTO_TTP = 0x54 + IPPROTO_UDP = 0x11 + IPPROTO_VINES = 0x53 + IPPROTO_VISA = 0x46 + IPPROTO_VMTP = 0x51 + IPPROTO_WBEXPAK = 0x4f + IPPROTO_WBMON = 0x4e + IPPROTO_WSN = 0x4a + IPPROTO_XNET = 0xf + IPPROTO_XTP = 0x24 + IPV6_AUTOFLOWLABEL = 0x3b + IPV6_BINDANY = 0x40 + IPV6_BINDV6ONLY = 0x1b + IPV6_CHECKSUM = 0x1a + IPV6_DEFAULT_MULTICAST_HOPS = 0x1 + IPV6_DEFAULT_MULTICAST_LOOP = 0x1 + IPV6_DEFHLIM = 0x40 + IPV6_DONTFRAG = 0x3e + IPV6_DSTOPTS = 0x32 + IPV6_FAITH = 0x1d + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 + IPV6_FRAGTTL = 0x78 + IPV6_FW_ADD = 0x1e + IPV6_FW_DEL = 0x1f + IPV6_FW_FLUSH = 0x20 + IPV6_FW_GET = 0x22 + IPV6_FW_ZERO = 0x21 + IPV6_HLIMDEC = 0x1 + IPV6_HOPLIMIT = 0x2f + IPV6_HOPOPTS = 0x31 + IPV6_IPSEC_POLICY = 0x1c + IPV6_JOIN_GROUP = 0xc + IPV6_LEAVE_GROUP = 0xd + IPV6_MAXHLIM = 0xff + IPV6_MAXOPTHDR = 0x800 + IPV6_MAXPACKET = 0xffff + IPV6_MAX_GROUP_SRC_FILTER = 0x200 + IPV6_MAX_MEMBERSHIPS = 0xfff + IPV6_MAX_SOCK_SRC_FILTER = 0x80 + IPV6_MIN_MEMBERSHIPS = 0x1f + IPV6_MMTU = 0x500 + IPV6_MSFILTER = 0x4a + IPV6_MULTICAST_HOPS = 0xa + IPV6_MULTICAST_IF = 0x9 + IPV6_MULTICAST_LOOP = 0xb + IPV6_NEXTHOP = 0x30 + IPV6_PATHMTU = 0x2c + IPV6_PKTINFO = 0x2e + IPV6_PORTRANGE = 0xe + IPV6_PORTRANGE_DEFAULT = 0x0 + IPV6_PORTRANGE_HIGH = 0x1 + IPV6_PORTRANGE_LOW = 0x2 + IPV6_PREFER_TEMPADDR = 0x3f + IPV6_RECVDSTOPTS = 0x28 + IPV6_RECVHOPLIMIT = 0x25 + IPV6_RECVHOPOPTS = 0x27 + IPV6_RECVPATHMTU = 0x2b + IPV6_RECVPKTINFO = 0x24 + IPV6_RECVRTHDR = 0x26 + IPV6_RECVTCLASS = 0x39 + IPV6_RTHDR = 0x33 + IPV6_RTHDRDSTOPTS = 0x23 + IPV6_RTHDR_LOOSE = 0x0 + IPV6_RTHDR_STRICT = 0x1 + IPV6_RTHDR_TYPE_0 = 0x0 + IPV6_SOCKOPT_RESERVED1 = 0x3 + IPV6_TCLASS = 0x3d + IPV6_UNICAST_HOPS = 0x4 + IPV6_USE_MIN_MTU = 0x2a + IPV6_V6ONLY = 0x1b + IPV6_VERSION = 0x60 + IPV6_VERSION_MASK = 0xf0 + IP_ADD_MEMBERSHIP = 0xc + IP_ADD_SOURCE_MEMBERSHIP = 0x46 + IP_BINDANY = 0x18 + IP_BLOCK_SOURCE = 0x48 + IP_DEFAULT_MULTICAST_LOOP = 0x1 + IP_DEFAULT_MULTICAST_TTL = 0x1 + IP_DF = 0x4000 + IP_DONTFRAG = 0x43 + IP_DROP_MEMBERSHIP = 0xd + IP_DROP_SOURCE_MEMBERSHIP = 0x47 + IP_DUMMYNET3 = 0x31 + IP_DUMMYNET_CONFIGURE = 0x3c + IP_DUMMYNET_DEL = 0x3d + IP_DUMMYNET_FLUSH = 0x3e + IP_DUMMYNET_GET = 0x40 + IP_FAITH = 0x16 + IP_FW3 = 0x30 + IP_FW_ADD = 0x32 + IP_FW_DEL = 0x33 + IP_FW_FLUSH = 0x34 + IP_FW_GET = 0x36 + IP_FW_NAT_CFG = 0x38 + IP_FW_NAT_DEL = 0x39 + IP_FW_NAT_GET_CONFIG = 0x3a + IP_FW_NAT_GET_LOG = 0x3b + IP_FW_RESETLOG = 0x37 + IP_FW_TABLE_ADD = 0x28 + IP_FW_TABLE_DEL = 0x29 + IP_FW_TABLE_FLUSH = 0x2a + IP_FW_TABLE_GETSIZE = 0x2b + IP_FW_TABLE_LIST = 0x2c + IP_FW_ZERO = 0x35 + IP_HDRINCL = 0x2 + IP_IPSEC_POLICY = 0x15 + IP_MAXPACKET = 0xffff + IP_MAX_GROUP_SRC_FILTER = 0x200 + IP_MAX_MEMBERSHIPS = 0xfff + IP_MAX_SOCK_MUTE_FILTER = 0x80 + IP_MAX_SOCK_SRC_FILTER = 0x80 + IP_MAX_SOURCE_FILTER = 0x400 + IP_MF = 0x2000 + IP_MINTTL = 0x42 + IP_MIN_MEMBERSHIPS = 0x1f + IP_MSFILTER = 0x4a + IP_MSS = 0x240 + IP_MULTICAST_IF = 0x9 + IP_MULTICAST_LOOP = 0xb + IP_MULTICAST_TTL = 0xa + IP_MULTICAST_VIF = 0xe + IP_OFFMASK = 0x1fff + IP_ONESBCAST = 0x17 + IP_OPTIONS = 0x1 + IP_PORTRANGE = 0x13 + IP_PORTRANGE_DEFAULT = 0x0 + IP_PORTRANGE_HIGH = 0x1 + IP_PORTRANGE_LOW = 0x2 + IP_RECVDSTADDR = 0x7 + IP_RECVIF = 0x14 + IP_RECVOPTS = 0x5 + IP_RECVRETOPTS = 0x6 + IP_RECVTOS = 0x44 + IP_RECVTTL = 0x41 + IP_RETOPTS = 0x8 + IP_RF = 0x8000 + IP_RSVP_OFF = 0x10 + IP_RSVP_ON = 0xf + IP_RSVP_VIF_OFF = 0x12 + IP_RSVP_VIF_ON = 0x11 + IP_SENDSRCADDR = 0x7 + IP_TOS = 0x3 + IP_TTL = 0x4 + IP_UNBLOCK_SOURCE = 0x49 + ISIG = 0x80 + ISTRIP = 0x20 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 + LOCK_EX = 0x2 + LOCK_NB = 0x4 + LOCK_SH = 0x1 + LOCK_UN = 0x8 + MSG_COMPAT = 0x8000 + MSG_CTRUNC = 0x20 + MSG_DONTROUTE = 0x4 + MSG_DONTWAIT = 0x80 + MSG_EOF = 0x100 + MSG_EOR = 0x8 + MSG_NBIO = 0x4000 + MSG_NOSIGNAL = 0x20000 + MSG_NOTIFICATION = 0x2000 + MSG_OOB = 0x1 + MSG_PEEK = 0x2 + MSG_TRUNC = 0x10 + MSG_WAITALL = 0x40 + NET_RT_DUMP = 0x1 + NET_RT_FLAGS = 0x2 + NET_RT_IFLIST = 0x3 + NET_RT_IFLISTL = 0x5 + NET_RT_IFMALIST = 0x4 + NET_RT_MAXID = 0x6 + NOFLSH = 0x80000000 + NOTE_ATTRIB = 0x8 + NOTE_CHILD = 0x4 + NOTE_DELETE = 0x1 + NOTE_EXEC = 0x20000000 + NOTE_EXIT = 0x80000000 + NOTE_EXTEND = 0x4 + NOTE_FFAND = 0x40000000 + NOTE_FFCOPY = 0xc0000000 + NOTE_FFCTRLMASK = 0xc0000000 + NOTE_FFLAGSMASK = 0xffffff + NOTE_FFNOP = 0x0 + NOTE_FFOR = 0x80000000 + NOTE_FORK = 0x40000000 + NOTE_LINK = 0x10 + NOTE_LOWAT = 0x1 + NOTE_PCTRLMASK = 0xf0000000 + NOTE_PDATAMASK = 0xfffff + NOTE_RENAME = 0x20 + NOTE_REVOKE = 0x40 + NOTE_TRACK = 0x1 + NOTE_TRACKERR = 0x2 + NOTE_TRIGGER = 0x1000000 + NOTE_WRITE = 0x2 + OCRNL = 0x10 + ONLCR = 0x2 + ONLRET = 0x40 + ONOCR = 0x20 + ONOEOT = 0x8 + OPOST = 0x1 + O_ACCMODE = 0x3 + O_APPEND = 0x8 + O_ASYNC = 0x40 + O_CREAT = 0x200 + O_DIRECT = 0x10000 + O_DIRECTORY = 0x20000 + O_EXCL = 0x800 + O_EXEC = 0x40000 + O_EXLOCK = 0x20 + O_FSYNC = 0x80 + O_NDELAY = 0x4 + O_NOCTTY = 0x8000 + O_NOFOLLOW = 0x100 + O_NONBLOCK = 0x4 + O_RDONLY = 0x0 + O_RDWR = 0x2 + O_SHLOCK = 0x10 + O_SYNC = 0x80 + O_TRUNC = 0x400 + O_TTY_INIT = 0x80000 + O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 + RLIMIT_AS = 0xa + RLIMIT_CORE = 0x4 + RLIMIT_CPU = 0x0 + RLIMIT_DATA = 0x2 + RLIMIT_FSIZE = 0x1 + RLIMIT_NOFILE = 0x8 + RLIMIT_STACK = 0x3 + RLIM_INFINITY = 0x7fffffffffffffff + RTAX_AUTHOR = 0x6 + RTAX_BRD = 0x7 + RTAX_DST = 0x0 + RTAX_GATEWAY = 0x1 + RTAX_GENMASK = 0x3 + RTAX_IFA = 0x5 + RTAX_IFP = 0x4 + RTAX_MAX = 0x8 + RTAX_NETMASK = 0x2 + RTA_AUTHOR = 0x40 + RTA_BRD = 0x80 + RTA_DST = 0x1 + RTA_GATEWAY = 0x2 + RTA_GENMASK = 0x8 + RTA_IFA = 0x20 + RTA_IFP = 0x10 + RTA_NETMASK = 0x4 + RTF_BLACKHOLE = 0x1000 + RTF_BROADCAST = 0x400000 + RTF_DONE = 0x40 + RTF_DYNAMIC = 0x10 + RTF_FMASK = 0x1004d808 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_LLDATA = 0x400 + RTF_LLINFO = 0x400 + RTF_LOCAL = 0x200000 + RTF_MODIFIED = 0x20 + RTF_MULTICAST = 0x800000 + RTF_PINNED = 0x100000 + RTF_PRCLONING = 0x10000 + RTF_PROTO1 = 0x8000 + RTF_PROTO2 = 0x4000 + RTF_PROTO3 = 0x40000 + RTF_REJECT = 0x8 + RTF_RNH_LOCKED = 0x40000000 + RTF_STATIC = 0x800 + RTF_STICKY = 0x10000000 + RTF_UP = 0x1 + RTF_XRESOLVE = 0x200 + RTM_ADD = 0x1 + RTM_CHANGE = 0x3 + RTM_DELADDR = 0xd + RTM_DELETE = 0x2 + RTM_DELMADDR = 0x10 + RTM_GET = 0x4 + RTM_IEEE80211 = 0x12 + RTM_IFANNOUNCE = 0x11 + RTM_IFINFO = 0xe + RTM_LOCK = 0x8 + RTM_LOSING = 0x5 + RTM_MISS = 0x7 + RTM_NEWADDR = 0xc + RTM_NEWMADDR = 0xf + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_REDIRECT = 0x6 + RTM_RESOLVE = 0xb + RTM_RTTUNIT = 0xf4240 + RTM_VERSION = 0x5 + RTV_EXPIRE = 0x4 + RTV_HOPCOUNT = 0x2 + RTV_MTU = 0x1 + RTV_RPIPE = 0x8 + RTV_RTT = 0x40 + RTV_RTTVAR = 0x80 + RTV_SPIPE = 0x10 + RTV_SSTHRESH = 0x20 + RTV_WEIGHT = 0x100 + RT_CACHING_CONTEXT = 0x1 + RT_DEFAULT_FIB = 0x0 + RT_NORTREF = 0x2 + RUSAGE_CHILDREN = -0x1 + RUSAGE_SELF = 0x0 + RUSAGE_THREAD = 0x1 + SCM_BINTIME = 0x4 + SCM_CREDS = 0x3 + SCM_RIGHTS = 0x1 + SCM_TIMESTAMP = 0x2 + SHUT_RD = 0x0 + SHUT_RDWR = 0x2 + SHUT_WR = 0x1 + SIOCADDMULTI = 0x80206931 + SIOCADDRT = 0x8030720a + SIOCAIFADDR = 0x8044692b + SIOCAIFGROUP = 0x80246987 + SIOCALIFADDR = 0x8118691b + SIOCATMARK = 0x40047307 + SIOCDELMULTI = 0x80206932 + SIOCDELRT = 0x8030720b + SIOCDIFADDR = 0x80206919 + SIOCDIFGROUP = 0x80246989 + SIOCDIFPHYADDR = 0x80206949 + SIOCDLIFADDR = 0x8118691d + SIOCGDRVSPEC = 0xc01c697b + SIOCGETSGCNT = 0xc0147210 + SIOCGETVIFCNT = 0xc014720f + SIOCGHIWAT = 0x40047301 + SIOCGIFADDR = 0xc0206921 + SIOCGIFBRDADDR = 0xc0206923 + SIOCGIFCAP = 0xc020691f + SIOCGIFCONF = 0xc0086924 + SIOCGIFDESCR = 0xc020692a + SIOCGIFDSTADDR = 0xc0206922 + SIOCGIFFIB = 0xc020695c + SIOCGIFFLAGS = 0xc0206911 + SIOCGIFGENERIC = 0xc020693a + SIOCGIFGMEMB = 0xc024698a + SIOCGIFGROUP = 0xc0246988 + SIOCGIFINDEX = 0xc0206920 + SIOCGIFMAC = 0xc0206926 + SIOCGIFMEDIA = 0xc0286938 + SIOCGIFMETRIC = 0xc0206917 + SIOCGIFMTU = 0xc0206933 + SIOCGIFNETMASK = 0xc0206925 + SIOCGIFPDSTADDR = 0xc0206948 + SIOCGIFPHYS = 0xc0206935 + SIOCGIFPSRCADDR = 0xc0206947 + SIOCGIFSTATUS = 0xc334693b + SIOCGLIFADDR = 0xc118691c + SIOCGLIFPHYADDR = 0xc118694b + SIOCGLOWAT = 0x40047303 + SIOCGPGRP = 0x40047309 + SIOCGPRIVATE_0 = 0xc0206950 + SIOCGPRIVATE_1 = 0xc0206951 + SIOCIFCREATE = 0xc020697a + SIOCIFCREATE2 = 0xc020697c + SIOCIFDESTROY = 0x80206979 + SIOCIFGCLONERS = 0xc00c6978 + SIOCSDRVSPEC = 0x801c697b + SIOCSHIWAT = 0x80047300 + SIOCSIFADDR = 0x8020690c + SIOCSIFBRDADDR = 0x80206913 + SIOCSIFCAP = 0x8020691e + SIOCSIFDESCR = 0x80206929 + SIOCSIFDSTADDR = 0x8020690e + SIOCSIFFIB = 0x8020695d + SIOCSIFFLAGS = 0x80206910 + SIOCSIFGENERIC = 0x80206939 + SIOCSIFLLADDR = 0x8020693c + SIOCSIFMAC = 0x80206927 + SIOCSIFMEDIA = 0xc0206937 + SIOCSIFMETRIC = 0x80206918 + SIOCSIFMTU = 0x80206934 + SIOCSIFNAME = 0x80206928 + SIOCSIFNETMASK = 0x80206916 + SIOCSIFPHYADDR = 0x80446946 + SIOCSIFPHYS = 0x80206936 + SIOCSIFRVNET = 0xc020695b + SIOCSIFVNET = 0xc020695a + SIOCSLIFPHYADDR = 0x8118694a + SIOCSLOWAT = 0x80047302 + SIOCSPGRP = 0x80047308 + SOCK_DGRAM = 0x2 + SOCK_MAXADDRLEN = 0xff + SOCK_RAW = 0x3 + SOCK_RDM = 0x4 + SOCK_SEQPACKET = 0x5 + SOCK_STREAM = 0x1 + SOL_SOCKET = 0xffff + SOMAXCONN = 0x80 + SO_ACCEPTCONN = 0x2 + SO_ACCEPTFILTER = 0x1000 + SO_BINTIME = 0x2000 + SO_BROADCAST = 0x20 + SO_DEBUG = 0x1 + SO_DONTROUTE = 0x10 + SO_ERROR = 0x1007 + SO_KEEPALIVE = 0x8 + SO_LABEL = 0x1009 + SO_LINGER = 0x80 + SO_LISTENINCQLEN = 0x1013 + SO_LISTENQLEN = 0x1012 + SO_LISTENQLIMIT = 0x1011 + SO_NOSIGPIPE = 0x800 + SO_NO_DDP = 0x8000 + SO_NO_OFFLOAD = 0x4000 + SO_OOBINLINE = 0x100 + SO_PEERLABEL = 0x1010 + SO_PROTOCOL = 0x1016 + SO_PROTOTYPE = 0x1016 + SO_RCVBUF = 0x1002 + SO_RCVLOWAT = 0x1004 + SO_RCVTIMEO = 0x1006 + SO_REUSEADDR = 0x4 + SO_REUSEPORT = 0x200 + SO_SETFIB = 0x1014 + SO_SNDBUF = 0x1001 + SO_SNDLOWAT = 0x1003 + SO_SNDTIMEO = 0x1005 + SO_TIMESTAMP = 0x400 + SO_TYPE = 0x1008 + SO_USELOOPBACK = 0x40 + SO_USER_COOKIE = 0x1015 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 + TCP_CA_NAME_MAX = 0x10 + TCP_CONGESTION = 0x40 + TCP_INFO = 0x20 + TCP_KEEPCNT = 0x400 + TCP_KEEPIDLE = 0x100 + TCP_KEEPINIT = 0x80 + TCP_KEEPINTVL = 0x200 + TCP_MAXBURST = 0x4 + TCP_MAXHLEN = 0x3c + TCP_MAXOLEN = 0x28 + TCP_MAXSEG = 0x2 + TCP_MAXWIN = 0xffff + TCP_MAX_SACK = 0x4 + TCP_MAX_WINSHIFT = 0xe + TCP_MD5SIG = 0x10 + TCP_MINMSS = 0xd8 + TCP_MSS = 0x218 + TCP_NODELAY = 0x1 + TCP_NOOPT = 0x8 + TCP_NOPUSH = 0x4 + TCSAFLUSH = 0x2 + TIOCCBRK = 0x2000747a + TIOCCDTR = 0x20007478 + TIOCCONS = 0x80047462 + TIOCDRAIN = 0x2000745e + TIOCEXCL = 0x2000740d + TIOCEXT = 0x80047460 + TIOCFLUSH = 0x80047410 + TIOCGDRAINWAIT = 0x40047456 + TIOCGETA = 0x402c7413 + TIOCGETD = 0x4004741a + TIOCGPGRP = 0x40047477 + TIOCGPTN = 0x4004740f + TIOCGSID = 0x40047463 + TIOCGWINSZ = 0x40087468 + TIOCMBIC = 0x8004746b + TIOCMBIS = 0x8004746c + TIOCMGDTRWAIT = 0x4004745a + TIOCMGET = 0x4004746a + TIOCMSDTRWAIT = 0x8004745b + TIOCMSET = 0x8004746d + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DCD = 0x40 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x20007471 + TIOCNXCL = 0x2000740e + TIOCOUTQ = 0x40047473 + TIOCPKT = 0x80047470 + TIOCPKT_DATA = 0x0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCPTMASTER = 0x2000741c + TIOCSBRK = 0x2000747b + TIOCSCTTY = 0x20007461 + TIOCSDRAINWAIT = 0x80047457 + TIOCSDTR = 0x20007479 + TIOCSETA = 0x802c7414 + TIOCSETAF = 0x802c7416 + TIOCSETAW = 0x802c7415 + TIOCSETD = 0x8004741b + TIOCSIG = 0x2004745f + TIOCSPGRP = 0x80047476 + TIOCSTART = 0x2000746e + TIOCSTAT = 0x20007465 + TIOCSTI = 0x80017472 + TIOCSTOP = 0x2000746f + TIOCSWINSZ = 0x80087467 + TIOCTIMESTAMP = 0x400c7459 + TIOCUCNTL = 0x80047466 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VERASE2 = 0x7 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VTIME = 0x11 + VWERASE = 0x4 + WCONTINUED = 0x4 + WCOREFLAG = 0x80 + WLINUXCLONE = 0x80000000 + WNOHANG = 0x1 + WNOWAIT = 0x8 + WSTOPPED = 0x2 + WUNTRACED = 0x2 +) + +// Errors +const ( + E2BIG = Errno(0x7) + EACCES = Errno(0xd) + EADDRINUSE = Errno(0x30) + EADDRNOTAVAIL = Errno(0x31) + EAFNOSUPPORT = Errno(0x2f) + EAGAIN = Errno(0x23) + EALREADY = Errno(0x25) + EAUTH = Errno(0x50) + EBADF = Errno(0x9) + EBADMSG = Errno(0x59) + EBADRPC = Errno(0x48) + EBUSY = Errno(0x10) + ECANCELED = Errno(0x55) + ECAPMODE = Errno(0x5e) + ECHILD = Errno(0xa) + ECONNABORTED = Errno(0x35) + ECONNREFUSED = Errno(0x3d) + ECONNRESET = Errno(0x36) + EDEADLK = Errno(0xb) + EDESTADDRREQ = Errno(0x27) + EDOM = Errno(0x21) + EDOOFUS = Errno(0x58) + EDQUOT = Errno(0x45) + EEXIST = Errno(0x11) + EFAULT = Errno(0xe) + EFBIG = Errno(0x1b) + EFTYPE = Errno(0x4f) + EHOSTDOWN = Errno(0x40) + EHOSTUNREACH = Errno(0x41) + EIDRM = Errno(0x52) + EILSEQ = Errno(0x56) + EINPROGRESS = Errno(0x24) + EINTR = Errno(0x4) + EINVAL = Errno(0x16) + EIO = Errno(0x5) + EISCONN = Errno(0x38) + EISDIR = Errno(0x15) + ELAST = Errno(0x5e) + ELOOP = Errno(0x3e) + EMFILE = Errno(0x18) + EMLINK = Errno(0x1f) + EMSGSIZE = Errno(0x28) + EMULTIHOP = Errno(0x5a) + ENAMETOOLONG = Errno(0x3f) + ENEEDAUTH = Errno(0x51) + ENETDOWN = Errno(0x32) + ENETRESET = Errno(0x34) + ENETUNREACH = Errno(0x33) + ENFILE = Errno(0x17) + ENOATTR = Errno(0x57) + ENOBUFS = Errno(0x37) + ENODEV = Errno(0x13) + ENOENT = Errno(0x2) + ENOEXEC = Errno(0x8) + ENOLCK = Errno(0x4d) + ENOLINK = Errno(0x5b) + ENOMEM = Errno(0xc) + ENOMSG = Errno(0x53) + ENOPROTOOPT = Errno(0x2a) + ENOSPC = Errno(0x1c) + ENOSYS = Errno(0x4e) + ENOTBLK = Errno(0xf) + ENOTCAPABLE = Errno(0x5d) + ENOTCONN = Errno(0x39) + ENOTDIR = Errno(0x14) + ENOTEMPTY = Errno(0x42) + ENOTSOCK = Errno(0x26) + ENOTSUP = Errno(0x2d) + ENOTTY = Errno(0x19) + ENXIO = Errno(0x6) + EOPNOTSUPP = Errno(0x2d) + EOVERFLOW = Errno(0x54) + EPERM = Errno(0x1) + EPFNOSUPPORT = Errno(0x2e) + EPIPE = Errno(0x20) + EPROCLIM = Errno(0x43) + EPROCUNAVAIL = Errno(0x4c) + EPROGMISMATCH = Errno(0x4b) + EPROGUNAVAIL = Errno(0x4a) + EPROTO = Errno(0x5c) + EPROTONOSUPPORT = Errno(0x2b) + EPROTOTYPE = Errno(0x29) + ERANGE = Errno(0x22) + EREMOTE = Errno(0x47) + EROFS = Errno(0x1e) + ERPCMISMATCH = Errno(0x49) + ESHUTDOWN = Errno(0x3a) + ESOCKTNOSUPPORT = Errno(0x2c) + ESPIPE = Errno(0x1d) + ESRCH = Errno(0x3) + ESTALE = Errno(0x46) + ETIMEDOUT = Errno(0x3c) + ETOOMANYREFS = Errno(0x3b) + ETXTBSY = Errno(0x1a) + EUSERS = Errno(0x44) + EWOULDBLOCK = Errno(0x23) + EXDEV = Errno(0x12) +) + +// Signals +const ( + SIGABRT = Signal(0x6) + SIGALRM = Signal(0xe) + SIGBUS = Signal(0xa) + SIGCHLD = Signal(0x14) + SIGCONT = Signal(0x13) + SIGEMT = Signal(0x7) + SIGFPE = Signal(0x8) + SIGHUP = Signal(0x1) + SIGILL = Signal(0x4) + SIGINFO = Signal(0x1d) + SIGINT = Signal(0x2) + SIGIO = Signal(0x17) + SIGIOT = Signal(0x6) + SIGKILL = Signal(0x9) + SIGLIBRT = Signal(0x21) + SIGLWP = Signal(0x20) + SIGPIPE = Signal(0xd) + SIGPROF = Signal(0x1b) + SIGQUIT = Signal(0x3) + SIGSEGV = Signal(0xb) + SIGSTOP = Signal(0x11) + SIGSYS = Signal(0xc) + SIGTERM = Signal(0xf) + SIGTHR = Signal(0x20) + SIGTRAP = Signal(0x5) + SIGTSTP = Signal(0x12) + SIGTTIN = Signal(0x15) + SIGTTOU = Signal(0x16) + SIGURG = Signal(0x10) + SIGUSR1 = Signal(0x1e) + SIGUSR2 = Signal(0x1f) + SIGVTALRM = Signal(0x1a) + SIGWINCH = Signal(0x1c) + SIGXCPU = Signal(0x18) + SIGXFSZ = Signal(0x19) +) + +// Error table +var errors = [...]string{ + 1: "operation not permitted", + 2: "no such file or directory", + 3: "no such process", + 4: "interrupted system call", + 5: "input/output error", + 6: "device not configured", + 7: "argument list too long", + 8: "exec format error", + 9: "bad file descriptor", + 10: "no child processes", + 11: "resource deadlock avoided", + 12: "cannot allocate memory", + 13: "permission denied", + 14: "bad address", + 15: "block device required", + 16: "device busy", + 17: "file exists", + 18: "cross-device link", + 19: "operation not supported by device", + 20: "not a directory", + 21: "is a directory", + 22: "invalid argument", + 23: "too many open files in system", + 24: "too many open files", + 25: "inappropriate ioctl for device", + 26: "text file busy", + 27: "file too large", + 28: "no space left on device", + 29: "illegal seek", + 30: "read-only file system", + 31: "too many links", + 32: "broken pipe", + 33: "numerical argument out of domain", + 34: "result too large", + 35: "resource temporarily unavailable", + 36: "operation now in progress", + 37: "operation already in progress", + 38: "socket operation on non-socket", + 39: "destination address required", + 40: "message too long", + 41: "protocol wrong type for socket", + 42: "protocol not available", + 43: "protocol not supported", + 44: "socket type not supported", + 45: "operation not supported", + 46: "protocol family not supported", + 47: "address family not supported by protocol family", + 48: "address already in use", + 49: "can't assign requested address", + 50: "network is down", + 51: "network is unreachable", + 52: "network dropped connection on reset", + 53: "software caused connection abort", + 54: "connection reset by peer", + 55: "no buffer space available", + 56: "socket is already connected", + 57: "socket is not connected", + 58: "can't send after socket shutdown", + 59: "too many references: can't splice", + 60: "operation timed out", + 61: "connection refused", + 62: "too many levels of symbolic links", + 63: "file name too long", + 64: "host is down", + 65: "no route to host", + 66: "directory not empty", + 67: "too many processes", + 68: "too many users", + 69: "disc quota exceeded", + 70: "stale NFS file handle", + 71: "too many levels of remote in path", + 72: "RPC struct is bad", + 73: "RPC version wrong", + 74: "RPC prog. not avail", + 75: "program version wrong", + 76: "bad procedure for program", + 77: "no locks available", + 78: "function not implemented", + 79: "inappropriate file type or format", + 80: "authentication error", + 81: "need authenticator", + 82: "identifier removed", + 83: "no message of desired type", + 84: "value too large to be stored in data type", + 85: "operation canceled", + 86: "illegal byte sequence", + 87: "attribute not found", + 88: "programming error", + 89: "bad message", + 90: "multihop attempted", + 91: "link has been severed", + 92: "protocol error", + 93: "capabilities insufficient", + 94: "not permitted in capability mode", +} + +// Signal table +var signals = [...]string{ + 1: "hangup", + 2: "interrupt", + 3: "quit", + 4: "illegal instruction", + 5: "trace/BPT trap", + 6: "abort trap", + 7: "EMT trap", + 8: "floating point exception", + 9: "killed", + 10: "bus error", + 11: "segmentation fault", + 12: "bad system call", + 13: "broken pipe", + 14: "alarm clock", + 15: "terminated", + 16: "urgent I/O condition", + 17: "suspended (signal)", + 18: "suspended", + 19: "continued", + 20: "child exited", + 21: "stopped (tty input)", + 22: "stopped (tty output)", + 23: "I/O possible", + 24: "cputime limit exceeded", + 25: "filesize limit exceeded", + 26: "virtual timer expired", + 27: "profiling timer expired", + 28: "window size changes", + 29: "information request", + 30: "user defined signal 1", + 31: "user defined signal 2", + 32: "unknown signal", + 33: "unknown signal", +} diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go index 065da8f45..a689942b8 100644 --- a/src/pkg/syscall/zerrors_linux_386.go +++ b/src/pkg/syscall/zerrors_linux_386.go @@ -528,6 +528,7 @@ const ( MSG_DONTWAIT = 0x40 MSG_EOR = 0x80 MSG_ERRQUEUE = 0x2000 + MSG_FASTOPEN = 0x20000000 MSG_FIN = 0x200 MSG_MORE = 0x8000 MSG_NOSIGNAL = 0x4000 diff --git a/src/pkg/syscall/zerrors_linux_amd64.go b/src/pkg/syscall/zerrors_linux_amd64.go index 4e4918452..a1ac1773b 100644 --- a/src/pkg/syscall/zerrors_linux_amd64.go +++ b/src/pkg/syscall/zerrors_linux_amd64.go @@ -528,6 +528,7 @@ const ( MSG_DONTWAIT = 0x40 MSG_EOR = 0x80 MSG_ERRQUEUE = 0x2000 + MSG_FASTOPEN = 0x20000000 MSG_FIN = 0x200 MSG_MORE = 0x8000 MSG_NOSIGNAL = 0x4000 diff --git a/src/pkg/syscall/zerrors_linux_arm.go b/src/pkg/syscall/zerrors_linux_arm.go index 9b99cf83f..0730f2140 100644 --- a/src/pkg/syscall/zerrors_linux_arm.go +++ b/src/pkg/syscall/zerrors_linux_arm.go @@ -523,6 +523,7 @@ const ( MSG_DONTWAIT = 0x40 MSG_EOR = 0x80 MSG_ERRQUEUE = 0x2000 + MSG_FASTOPEN = 0x20000000 MSG_FIN = 0x200 MSG_MORE = 0x8000 MSG_NOSIGNAL = 0x4000 diff --git a/src/pkg/syscall/zerrors_netbsd_386.go b/src/pkg/syscall/zerrors_netbsd_386.go index 52dc69a64..68051f9f8 100644 --- a/src/pkg/syscall/zerrors_netbsd_386.go +++ b/src/pkg/syscall/zerrors_netbsd_386.go @@ -8,7 +8,8 @@ package syscall const ( AF_APPLETALK = 0x10 - AF_BLUETOOTH = 0x20 + AF_ARP = 0x1c + AF_BLUETOOTH = 0x1f AF_CCITT = 0xa AF_CHAOS = 0x5 AF_CNT = 0x15 @@ -18,67 +19,93 @@ const ( AF_DLI = 0xd AF_E164 = 0x1a AF_ECMA = 0x8 - AF_ENCAP = 0x1c AF_HYLINK = 0xf + AF_IEEE80211 = 0x20 AF_IMPLINK = 0x3 AF_INET = 0x2 AF_INET6 = 0x18 AF_IPX = 0x17 AF_ISDN = 0x1a AF_ISO = 0x7 - AF_KEY = 0x1e AF_LAT = 0xe AF_LINK = 0x12 AF_LOCAL = 0x1 - AF_MAX = 0x24 + AF_MAX = 0x23 AF_MPLS = 0x21 AF_NATM = 0x1b AF_NS = 0x6 + AF_OROUTE = 0x11 AF_OSI = 0x7 AF_PUP = 0x4 - AF_ROUTE = 0x11 - AF_SIP = 0x1d + AF_ROUTE = 0x22 AF_SNA = 0xb AF_UNIX = 0x1 AF_UNSPEC = 0x0 + ARPHRD_ARCNET = 0x7 ARPHRD_ETHER = 0x1 ARPHRD_FRELAY = 0xf ARPHRD_IEEE1394 = 0x18 ARPHRD_IEEE802 = 0x6 + ARPHRD_STRIP = 0x17 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B460800 = 0x70800 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B921600 = 0xe1000 + B9600 = 0x2580 + BIOCFEEDBACK = 0x8004427d BIOCFLUSH = 0x20004268 BIOCGBLEN = 0x40044266 - BIOCGDIRFILT = 0x4004427c BIOCGDLT = 0x4004426a - BIOCGDLTLIST = 0xc008427b - BIOCGETIF = 0x4020426b - BIOCGFILDROP = 0x40044278 + BIOCGDLTLIST = 0xc0084277 + BIOCGETIF = 0x4090426b + BIOCGFEEDBACK = 0x4004427c BIOCGHDRCMPLT = 0x40044274 - BIOCGRSIG = 0x40044273 - BIOCGRTIMEOUT = 0x4008426e - BIOCGSTATS = 0x4008426f + BIOCGRTIMEOUT = 0x400c427b + BIOCGSEESENT = 0x40044278 + BIOCGSTATS = 0x4080426f + BIOCGSTATSOLD = 0x4008426f BIOCIMMEDIATE = 0x80044270 - BIOCLOCK = 0x20004276 BIOCPROMISC = 0x20004269 BIOCSBLEN = 0xc0044266 - BIOCSDIRFILT = 0x8004427d - BIOCSDLT = 0x8004427a + BIOCSDLT = 0x80044276 BIOCSETF = 0x80084267 - BIOCSETIF = 0x8020426c - BIOCSETWF = 0x80084277 - BIOCSFILDROP = 0x80044279 + BIOCSETIF = 0x8090426c + BIOCSFEEDBACK = 0x8004427d BIOCSHDRCMPLT = 0x80044275 - BIOCSRSIG = 0x80044272 - BIOCSRTIMEOUT = 0x8008426d + BIOCSRTIMEOUT = 0x800c427a + BIOCSSEESENT = 0x80044279 + BIOCSTCPF = 0x80084272 + BIOCSUDPF = 0x80084273 BIOCVERSION = 0x40044271 BPF_A = 0x10 BPF_ABS = 0x20 BPF_ADD = 0x0 BPF_ALIGNMENT = 0x4 + BPF_ALIGNMENT32 = 0x4 BPF_ALU = 0x4 BPF_AND = 0x50 BPF_B = 0x10 - BPF_DIRECTION_IN = 0x1 - BPF_DIRECTION_OUT = 0x2 + BPF_DFLTBUFSIZE = 0x100000 BPF_DIV = 0x30 BPF_H = 0x8 BPF_IMM = 0x0 @@ -95,7 +122,7 @@ const ( BPF_LEN = 0x80 BPF_LSH = 0x60 BPF_MAJOR_VERSION = 0x1 - BPF_MAXBUFSIZE = 0x200000 + BPF_MAXBUFSIZE = 0x1000000 BPF_MAXINSNS = 0x200 BPF_MEM = 0x60 BPF_MEMWORDS = 0x10 @@ -116,31 +143,151 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x8000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0x14 + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a CTL_MAXNAME = 0xc CTL_NET = 0x4 + CTL_QUERY = -0x2 + DIOCBSFLUSH = 0x20006478 + DLT_A429 = 0xb8 + DLT_A653_ICM = 0xb9 + DLT_AIRONET_HEADER = 0x78 + DLT_AOS = 0xde + DLT_APPLE_IP_OVER_IEEE1394 = 0x8a DLT_ARCNET = 0x7 + DLT_ARCNET_LINUX = 0x81 + DLT_ATM_CLIP = 0x13 DLT_ATM_RFC1483 = 0xb + DLT_AURORA = 0x7e DLT_AX25 = 0x3 + DLT_AX25_KISS = 0xca + DLT_BACNET_MS_TP = 0xa5 + DLT_BLUETOOTH_HCI_H4 = 0xbb + DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9 + DLT_CAN20B = 0xbe + DLT_CAN_SOCKETCAN = 0xe3 DLT_CHAOS = 0x5 + DLT_CISCO_IOS = 0x76 + DLT_C_HDLC = 0x68 + DLT_C_HDLC_WITH_DIR = 0xcd + DLT_DECT = 0xdd + DLT_DOCSIS = 0x8f + DLT_ECONET = 0x73 DLT_EN10MB = 0x1 DLT_EN3MB = 0x2 - DLT_ENC = 0xd + DLT_ENC = 0x6d + DLT_ERF = 0xc5 + DLT_ERF_ETH = 0xaf + DLT_ERF_POS = 0xb0 + DLT_FC_2 = 0xe0 + DLT_FC_2_WITH_FRAME_DELIMS = 0xe1 DLT_FDDI = 0xa + DLT_FLEXRAY = 0xd2 + DLT_FRELAY = 0x6b + DLT_FRELAY_WITH_DIR = 0xce + DLT_GCOM_SERIAL = 0xad + DLT_GCOM_T1E1 = 0xac + DLT_GPF_F = 0xab + DLT_GPF_T = 0xaa + DLT_GPRS_LLC = 0xa9 + DLT_GSMTAP_ABIS = 0xda + DLT_GSMTAP_UM = 0xd9 + DLT_HDLC = 0x10 + DLT_HHDLC = 0x79 + DLT_HIPPI = 0xf + DLT_IBM_SN = 0x92 + DLT_IBM_SP = 0x91 DLT_IEEE802 = 0x6 DLT_IEEE802_11 = 0x69 DLT_IEEE802_11_RADIO = 0x7f - DLT_LOOP = 0xc + DLT_IEEE802_11_RADIO_AVS = 0xa3 + DLT_IEEE802_15_4 = 0xc3 + DLT_IEEE802_15_4_LINUX = 0xbf + DLT_IEEE802_15_4_NONASK_PHY = 0xd7 + DLT_IEEE802_16_MAC_CPS = 0xbc + DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 + DLT_IPMB = 0xc7 + DLT_IPMB_LINUX = 0xd1 + DLT_IPNET = 0xe2 + DLT_IPV4 = 0xe4 + DLT_IPV6 = 0xe5 + DLT_IP_OVER_FC = 0x7a + DLT_JUNIPER_ATM1 = 0x89 + DLT_JUNIPER_ATM2 = 0x87 + DLT_JUNIPER_CHDLC = 0xb5 + DLT_JUNIPER_ES = 0x84 + DLT_JUNIPER_ETHER = 0xb2 + DLT_JUNIPER_FRELAY = 0xb4 + DLT_JUNIPER_GGSN = 0x85 + DLT_JUNIPER_ISM = 0xc2 + DLT_JUNIPER_MFR = 0x86 + DLT_JUNIPER_MLFR = 0x83 + DLT_JUNIPER_MLPPP = 0x82 + DLT_JUNIPER_MONITOR = 0xa4 + DLT_JUNIPER_PIC_PEER = 0xae + DLT_JUNIPER_PPP = 0xb3 + DLT_JUNIPER_PPPOE = 0xa7 + DLT_JUNIPER_PPPOE_ATM = 0xa8 + DLT_JUNIPER_SERVICES = 0x88 + DLT_JUNIPER_ST = 0xc8 + DLT_JUNIPER_VP = 0xb7 + DLT_LAPB_WITH_DIR = 0xcf + DLT_LAPD = 0xcb + DLT_LIN = 0xd4 + DLT_LINUX_EVDEV = 0xd8 + DLT_LINUX_IRDA = 0x90 + DLT_LINUX_LAPD = 0xb1 + DLT_LINUX_SLL = 0x71 + DLT_LOOP = 0x6c + DLT_LTALK = 0x72 + DLT_MFR = 0xb6 + DLT_MOST = 0xd3 DLT_MPLS = 0xdb + DLT_MTP2 = 0x8c + DLT_MTP2_WITH_PHDR = 0x8b + DLT_MTP3 = 0x8d DLT_NULL = 0x0 + DLT_PCI_EXP = 0x7d DLT_PFLOG = 0x75 DLT_PFSYNC = 0x12 + DLT_PPI = 0xc0 DLT_PPP = 0x9 - DLT_PPP_BSDOS = 0x10 + DLT_PPP_BSDOS = 0xe DLT_PPP_ETHER = 0x33 + DLT_PPP_PPPD = 0xa6 + DLT_PPP_SERIAL = 0x32 + DLT_PPP_WITH_DIR = 0xcc + DLT_PRISM_HEADER = 0x77 DLT_PRONET = 0x4 - DLT_RAW = 0xe + DLT_RAIF1 = 0xc6 + DLT_RAW = 0xc + DLT_RAWAF_MASK = 0x2240000 + DLT_RIO = 0x7c + DLT_SCCP = 0x8e + DLT_SITA = 0xc4 DLT_SLIP = 0x8 - DLT_SLIP_BSDOS = 0xf + DLT_SLIP_BSDOS = 0xd + DLT_SUNATM = 0x7b + DLT_SYMANTEC_FIREWALL = 0x63 + DLT_TZSP = 0x80 + DLT_USB = 0xba + DLT_USB_LINUX = 0xbd + DLT_USB_LINUX_MMAPPED = 0xdc + DLT_WIHART = 0xdf + DLT_X2E_SERIAL = 0xd5 + DLT_X2E_XORAYA = 0xd6 DT_BLK = 0x6 DT_CHR = 0x2 DT_DIR = 0x4 @@ -149,15 +296,33 @@ const ( DT_REG = 0x8 DT_SOCK = 0xc DT_UNKNOWN = 0x0 - EFER_LMA = 0x400 - EFER_LME = 0x100 - EFER_NXE = 0x800 - EFER_SCE = 0x1 - EMT_TAGOVF = 0x1 - EMUL_ENABLED = 0x1 - EMUL_NATIVE = 0x2 + DT_WHT = 0xe + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 + EMUL_LINUX = 0x1 + EMUL_LINUX32 = 0x5 + EMUL_MAXID = 0x6 + EN_SW_CTL_INF = 0x1000 + EN_SW_CTL_PREC = 0x300 + EN_SW_CTL_ROUND = 0xc00 + EN_SW_DATACHAIN = 0x80 + EN_SW_DENORM = 0x2 + EN_SW_INVOP = 0x1 + EN_SW_OVERFLOW = 0x8 + EN_SW_PRECLOSS = 0x20 + EN_SW_UNDERFLOW = 0x10 + EN_SW_ZERODIV = 0x4 + ETHERCAP_JUMBO_MTU = 0x4 + ETHERCAP_VLAN_HWTAGGING = 0x2 + ETHERCAP_VLAN_MTU = 0x1 ETHERMIN = 0x2e ETHERMTU = 0x5dc + ETHERMTU_JUMBO = 0x2328 ETHERTYPE_8023 = 0x4 ETHERTYPE_AARP = 0x80f3 ETHERTYPE_ACCTON = 0x8390 @@ -165,7 +330,6 @@ const ( ETHERTYPE_ALPHA = 0x814a ETHERTYPE_AMBER = 0x6008 ETHERTYPE_AMOEBA = 0x8145 - ETHERTYPE_AOE = 0x88a2 ETHERTYPE_APOLLO = 0x80f7 ETHERTYPE_APOLLODOMAIN = 0x8019 ETHERTYPE_APPLETALK = 0x809b @@ -236,7 +400,6 @@ const ( ETHERTYPE_LAT = 0x6004 ETHERTYPE_LBACK = 0x9000 ETHERTYPE_LITTLE = 0x8060 - ETHERTYPE_LLDP = 0x88cc ETHERTYPE_LOGICRAFT = 0x8148 ETHERTYPE_LOOPBACK = 0x9000 ETHERTYPE_MATRA = 0x807a @@ -284,7 +447,6 @@ const ( ETHERTYPE_PRIMENTS = 0x7031 ETHERTYPE_PUP = 0x200 ETHERTYPE_PUPAT = 0x200 - ETHERTYPE_QINQ = 0x88a8 ETHERTYPE_RACAL = 0x7030 ETHERTYPE_RATIONAL = 0x8150 ETHERTYPE_RAWFR = 0x6559 @@ -301,7 +463,7 @@ const ( ETHERTYPE_SG_NETGAMES = 0x8014 ETHERTYPE_SG_RESV = 0x8015 ETHERTYPE_SIMNET = 0x5208 - ETHERTYPE_SLOW = 0x8809 + ETHERTYPE_SLOWPROTOCOLS = 0x8809 ETHERTYPE_SNA = 0x80d5 ETHERTYPE_SNMP = 0x814c ETHERTYPE_SONIX = 0xfaf5 @@ -344,24 +506,24 @@ const ( ETHERTYPE_XNSSM = 0x9001 ETHERTYPE_XTP = 0x817d ETHER_ADDR_LEN = 0x6 - ETHER_ALIGN = 0x2 ETHER_CRC_LEN = 0x4 ETHER_CRC_POLY_BE = 0x4c11db6 ETHER_CRC_POLY_LE = 0xedb88320 ETHER_HDR_LEN = 0xe - ETHER_MAX_DIX_LEN = 0x600 ETHER_MAX_LEN = 0x5ee + ETHER_MAX_LEN_JUMBO = 0x233a ETHER_MIN_LEN = 0x40 + ETHER_PPPOE_ENCAP_LEN = 0x8 ETHER_TYPE_LEN = 0x2 ETHER_VLAN_ENCAP_LEN = 0x4 - EVFILT_AIO = -0x3 - EVFILT_PROC = -0x5 - EVFILT_READ = -0x1 - EVFILT_SIGNAL = -0x6 + EVFILT_AIO = 0x2 + EVFILT_PROC = 0x4 + EVFILT_READ = 0x0 + EVFILT_SIGNAL = 0x5 EVFILT_SYSCOUNT = 0x7 - EVFILT_TIMER = -0x7 - EVFILT_VNODE = -0x4 - EVFILT_WRITE = -0x2 + EVFILT_TIMER = 0x6 + EVFILT_VNODE = 0x3 + EVFILT_WRITE = 0x1 EV_ADD = 0x1 EV_CLEAR = 0x20 EV_DELETE = 0x2 @@ -376,25 +538,46 @@ const ( EXTB = 0x9600 EXTPROC = 0x800 FD_CLOEXEC = 0x1 - FD_SETSIZE = 0x400 + FD_SETSIZE = 0x100 + FLUSHO = 0x800000 + F_CLOSEM = 0xa F_DUPFD = 0x0 - F_DUPFD_CLOEXEC = 0xa + F_DUPFD_CLOEXEC = 0xc + F_FSCTL = -0x80000000 + F_FSDIRMASK = 0x70000000 + F_FSIN = 0x10000000 + F_FSINOUT = 0x30000000 + F_FSOUT = 0x20000000 + F_FSPRIV = 0x8000 + F_FSVOID = 0x40000000 F_GETFD = 0x1 F_GETFL = 0x3 F_GETLK = 0x7 + F_GETNOSIGPIPE = 0xd F_GETOWN = 0x5 + F_MAXFD = 0xb + F_OK = 0x0 + F_PARAM_MASK = 0xfff + F_PARAM_MAX = 0xfff F_RDLCK = 0x1 F_SETFD = 0x2 F_SETFL = 0x4 F_SETLK = 0x8 F_SETLKW = 0x9 + F_SETNOSIGPIPE = 0xe F_SETOWN = 0x6 F_UNLCK = 0x2 F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 IFA_ROUTE = 0x1 IFF_ALLMULTI = 0x200 IFF_BROADCAST = 0x2 - IFF_CANTCHANGE = 0x8e52 + IFF_CANTCHANGE = 0x8f52 IFF_DEBUG = 0x4 IFF_LINK0 = 0x1000 IFF_LINK1 = 0x2000 @@ -431,10 +614,9 @@ const ( IFT_ATMVCIENDPT = 0xc2 IFT_ATMVIRTUAL = 0x95 IFT_BGPPOLICYACCOUNTING = 0xa2 - IFT_BLUETOOTH = 0xf8 IFT_BRIDGE = 0xd1 IFT_BSC = 0x53 - IFT_CARP = 0xf7 + IFT_CARP = 0xf8 IFT_CCTEMUL = 0x3d IFT_CEPT = 0x13 IFT_CES = 0x85 @@ -455,19 +637,17 @@ const ( IFT_DS1FDL = 0xaa IFT_DS3 = 0x1e IFT_DTM = 0x8c - IFT_DUMMY = 0xf1 IFT_DVBASILN = 0xac IFT_DVBASIOUT = 0xad IFT_DVBRCCDOWNSTREAM = 0x93 IFT_DVBRCCMACLAYER = 0x92 IFT_DVBRCCUPSTREAM = 0x94 IFT_ECONET = 0xce - IFT_ENC = 0xf4 IFT_EON = 0x19 IFT_EPLRS = 0x57 IFT_ESCON = 0x49 IFT_ETHER = 0x6 - IFT_FAITH = 0xf3 + IFT_FAITH = 0xf2 IFT_FAST = 0x7d IFT_FASTETHER = 0x3e IFT_FASTETHERFX = 0x45 @@ -555,7 +735,6 @@ const ( IFT_P80 = 0xd IFT_PARA = 0x22 IFT_PFLOG = 0xf5 - IFT_PFLOW = 0xf9 IFT_PFSYNC = 0xf6 IFT_PLC = 0xae IFT_PON155 = 0xcf @@ -573,7 +752,7 @@ const ( IFT_PROPVIRTUAL = 0x35 IFT_PROPWIRELESSP2P = 0x9d IFT_PTPSERIAL = 0x16 - IFT_PVC = 0xf2 + IFT_PVC = 0xf1 IFT_Q2931 = 0xc9 IFT_QLLC = 0x44 IFT_RADIOMAC = 0xbc @@ -599,6 +778,7 @@ const ( IFT_SS7SIGLINK = 0x9c IFT_STACKTOSTACK = 0x6f IFT_STARLAN = 0xb + IFT_STF = 0xd7 IFT_T1 = 0x12 IFT_TDLC = 0x74 IFT_TELINK = 0xc8 @@ -633,6 +813,12 @@ const ( IFT_X25MLP = 0x79 IFT_X25PLE = 0x28 IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 IN_CLASSA_HOST = 0xffffff IN_CLASSA_MAX = 0x80 IN_CLASSA_NET = 0xff000000 @@ -648,12 +834,8 @@ const ( IN_CLASSD_NET = 0xf0000000 IN_CLASSD_NSHIFT = 0x1c IN_LOOPBACKNET = 0x7f - IN_RFC3021_HOST = 0x1 - IN_RFC3021_NET = 0xfffffffe - IN_RFC3021_NSHIFT = 0x1f IPPROTO_AH = 0x33 IPPROTO_CARP = 0x70 - IPPROTO_DIVERT = 0x102 IPPROTO_DONE = 0x101 IPPROTO_DSTOPTS = 0x3c IPPROTO_EGP = 0x8 @@ -674,10 +856,10 @@ const ( IPPROTO_IPIP = 0x4 IPPROTO_IPV4 = 0x4 IPPROTO_IPV6 = 0x29 + IPPROTO_IPV6_ICMP = 0x3a IPPROTO_MAX = 0x100 - IPPROTO_MAXID = 0x103 + IPPROTO_MAXID = 0x34 IPPROTO_MOBILE = 0x37 - IPPROTO_MPLS = 0x89 IPPROTO_NONE = 0x3b IPPROTO_PFSYNC = 0xf0 IPPROTO_PIM = 0x67 @@ -688,16 +870,13 @@ const ( IPPROTO_TCP = 0x6 IPPROTO_TP = 0x1d IPPROTO_UDP = 0x11 - IPV6_AUTH_LEVEL = 0x35 - IPV6_AUTOFLOWLABEL = 0x3b + IPPROTO_VRRP = 0x70 IPV6_CHECKSUM = 0x1a IPV6_DEFAULT_MULTICAST_HOPS = 0x1 IPV6_DEFAULT_MULTICAST_LOOP = 0x1 IPV6_DEFHLIM = 0x40 IPV6_DONTFRAG = 0x3e IPV6_DSTOPTS = 0x32 - IPV6_ESP_NETWORK_LEVEL = 0x37 - IPV6_ESP_TRANS_LEVEL = 0x36 IPV6_FAITH = 0x1d IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 @@ -705,7 +884,7 @@ const ( IPV6_HLIMDEC = 0x1 IPV6_HOPLIMIT = 0x2f IPV6_HOPOPTS = 0x31 - IPV6_IPCOMP_LEVEL = 0x3c + IPV6_IPSEC_POLICY = 0x1c IPV6_JOIN_GROUP = 0xc IPV6_LEAVE_GROUP = 0xd IPV6_MAXHLIM = 0xff @@ -715,9 +894,7 @@ const ( IPV6_MULTICAST_IF = 0x9 IPV6_MULTICAST_LOOP = 0xb IPV6_NEXTHOP = 0x30 - IPV6_OPTIONS = 0x1 IPV6_PATHMTU = 0x2c - IPV6_PIPEX = 0x3f IPV6_PKTINFO = 0x2e IPV6_PORTRANGE = 0xe IPV6_PORTRANGE_DEFAULT = 0x0 @@ -730,7 +907,6 @@ const ( IPV6_RECVPKTINFO = 0x24 IPV6_RECVRTHDR = 0x26 IPV6_RECVTCLASS = 0x39 - IPV6_RTABLE = 0x1021 IPV6_RTHDR = 0x33 IPV6_RTHDRDSTOPTS = 0x23 IPV6_RTHDR_LOOSE = 0x0 @@ -744,74 +920,105 @@ const ( IPV6_VERSION = 0x60 IPV6_VERSION_MASK = 0xf0 IP_ADD_MEMBERSHIP = 0xc - IP_AUTH_LEVEL = 0x14 IP_DEFAULT_MULTICAST_LOOP = 0x1 IP_DEFAULT_MULTICAST_TTL = 0x1 IP_DF = 0x4000 IP_DROP_MEMBERSHIP = 0xd - IP_ESP_NETWORK_LEVEL = 0x16 - IP_ESP_TRANS_LEVEL = 0x15 + IP_EF = 0x8000 + IP_ERRORMTU = 0x15 IP_HDRINCL = 0x2 - IP_IPCOMP_LEVEL = 0x1d - IP_IPSEC_LOCAL_AUTH = 0x1b - IP_IPSEC_LOCAL_CRED = 0x19 - IP_IPSEC_LOCAL_ID = 0x17 - IP_IPSEC_REMOTE_AUTH = 0x1c - IP_IPSEC_REMOTE_CRED = 0x1a - IP_IPSEC_REMOTE_ID = 0x18 + IP_IPSEC_POLICY = 0x16 IP_MAXPACKET = 0xffff - IP_MAX_MEMBERSHIPS = 0xfff + IP_MAX_MEMBERSHIPS = 0x14 IP_MF = 0x2000 - IP_MINTTL = 0x20 - IP_MIN_MEMBERSHIPS = 0xf + IP_MINFRAGSIZE = 0x45 + IP_MINTTL = 0x18 IP_MSS = 0x240 IP_MULTICAST_IF = 0x9 IP_MULTICAST_LOOP = 0xb IP_MULTICAST_TTL = 0xa IP_OFFMASK = 0x1fff IP_OPTIONS = 0x1 - IP_PIPEX = 0x22 IP_PORTRANGE = 0x13 IP_PORTRANGE_DEFAULT = 0x0 IP_PORTRANGE_HIGH = 0x1 IP_PORTRANGE_LOW = 0x2 IP_RECVDSTADDR = 0x7 - IP_RECVDSTPORT = 0x21 - IP_RECVIF = 0x1e + IP_RECVIF = 0x14 IP_RECVOPTS = 0x5 IP_RECVRETOPTS = 0x6 - IP_RECVRTABLE = 0x23 - IP_RECVTTL = 0x1f + IP_RECVTTL = 0x17 IP_RETOPTS = 0x8 IP_RF = 0x8000 - IP_RTABLE = 0x1021 IP_TOS = 0x3 IP_TTL = 0x4 - MAP_ET_KVAGUARD = 0x10 + ISIG = 0x80 + ISTRIP = 0x20 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 + LOCK_EX = 0x2 + LOCK_NB = 0x4 + LOCK_SH = 0x1 + LOCK_UN = 0x8 MSG_BCAST = 0x100 + MSG_CMSG_CLOEXEC = 0x800 + MSG_CONTROLMBUF = 0x2000000 MSG_CTRUNC = 0x20 MSG_DONTROUTE = 0x4 MSG_DONTWAIT = 0x80 MSG_EOR = 0x8 + MSG_IOVUSRSPACE = 0x4000000 + MSG_LENUSRSPACE = 0x8000000 MSG_MCAST = 0x200 + MSG_NAMEMBUF = 0x1000000 + MSG_NBIO = 0x1000 + MSG_NOSIGNAL = 0x400 MSG_OOB = 0x1 MSG_PEEK = 0x2 MSG_TRUNC = 0x10 + MSG_USERFLAGS = 0xffffff MSG_WAITALL = 0x40 - NAME_MAX = 0xff + NAME_MAX = 0x1ff NET_RT_DUMP = 0x1 NET_RT_FLAGS = 0x2 - NET_RT_IFLIST = 0x3 + NET_RT_IFLIST = 0x5 NET_RT_MAXID = 0x6 - NET_RT_STATS = 0x4 - NET_RT_TABLE = 0x5 + NET_RT_OIFLIST = 0x4 + NET_RT_OOIFLIST = 0x3 + NOFLSH = 0x80000000 + NOTE_ATTRIB = 0x8 + NOTE_CHILD = 0x4 + NOTE_DELETE = 0x1 + NOTE_EXEC = 0x20000000 + NOTE_EXIT = 0x80000000 + NOTE_EXTEND = 0x4 + NOTE_FORK = 0x40000000 + NOTE_LINK = 0x10 + NOTE_LOWAT = 0x1 + NOTE_PCTRLMASK = 0xf0000000 + NOTE_PDATAMASK = 0xfffff + NOTE_RENAME = 0x20 + NOTE_REVOKE = 0x40 + NOTE_TRACK = 0x1 + NOTE_TRACKERR = 0x2 + NOTE_WRITE = 0x2 + OCRNL = 0x10 + OFIOGETBMAP = 0xc004667a + ONLCR = 0x2 + ONLRET = 0x40 + ONOCR = 0x20 + ONOEOT = 0x8 + OPOST = 0x1 O_ACCMODE = 0x3 + O_ALT_IO = 0x40000 O_APPEND = 0x8 O_ASYNC = 0x40 - O_CLOEXEC = 0x10000 + O_CLOEXEC = 0x400000 O_CREAT = 0x200 - O_DIRECTORY = 0x20000 - O_DSYNC = 0x80 + O_DIRECT = 0x80000 + O_DIRECTORY = 0x200000 + O_DSYNC = 0x10000 O_EXCL = 0x800 O_EXLOCK = 0x20 O_FSYNC = 0x80 @@ -819,13 +1026,27 @@ const ( O_NOCTTY = 0x8000 O_NOFOLLOW = 0x100 O_NONBLOCK = 0x4 + O_NOSIGPIPE = 0x1000000 O_RDONLY = 0x0 O_RDWR = 0x2 - O_RSYNC = 0x80 + O_RSYNC = 0x20000 O_SHLOCK = 0x10 O_SYNC = 0x80 O_TRUNC = 0x400 O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 + PRI_IOFLUSH = 0x7c + RLIMIT_AS = 0xa + RLIMIT_CORE = 0x4 + RLIMIT_CPU = 0x0 + RLIMIT_DATA = 0x2 + RLIMIT_FSIZE = 0x1 + RLIMIT_NOFILE = 0x8 + RLIMIT_STACK = 0x3 + RLIM_INFINITY = 0x7fffffffffffffff RTAX_AUTHOR = 0x6 RTAX_BRD = 0x7 RTAX_DST = 0x0 @@ -833,11 +1054,9 @@ const ( RTAX_GENMASK = 0x3 RTAX_IFA = 0x5 RTAX_IFP = 0x4 - RTAX_LABEL = 0xa - RTAX_MAX = 0xb + RTAX_MAX = 0x9 RTAX_NETMASK = 0x2 - RTAX_SRC = 0x8 - RTAX_SRCMASK = 0x9 + RTAX_TAG = 0x8 RTA_AUTHOR = 0x40 RTA_BRD = 0x80 RTA_DST = 0x1 @@ -845,52 +1064,48 @@ const ( RTA_GENMASK = 0x8 RTA_IFA = 0x20 RTA_IFP = 0x10 - RTA_LABEL = 0x400 RTA_NETMASK = 0x4 - RTA_SRC = 0x100 - RTA_SRCMASK = 0x200 - RTF_ANNOUNCE = 0x4000 + RTA_TAG = 0x100 + RTF_ANNOUNCE = 0x20000 RTF_BLACKHOLE = 0x1000 - RTF_CLONED = 0x10000 + RTF_CLONED = 0x2000 RTF_CLONING = 0x100 RTF_DONE = 0x40 RTF_DYNAMIC = 0x10 - RTF_FMASK = 0x8f808 RTF_GATEWAY = 0x2 RTF_HOST = 0x4 - RTF_JUMBO = 0x80000 RTF_LLINFO = 0x400 RTF_MASK = 0x80 RTF_MODIFIED = 0x20 - RTF_MPATH = 0x40000 - RTF_MPLS = 0x100000 - RTF_PERMANENT_ARP = 0x2000 RTF_PROTO1 = 0x8000 RTF_PROTO2 = 0x4000 - RTF_PROTO3 = 0x2000 RTF_REJECT = 0x8 - RTF_SOURCE = 0x20000 + RTF_SRC = 0x10000 RTF_STATIC = 0x800 - RTF_TUNNEL = 0x100000 RTF_UP = 0x1 - RTF_USETRAILERS = 0x8000 RTF_XRESOLVE = 0x200 RTM_ADD = 0x1 RTM_CHANGE = 0x3 + RTM_CHGADDR = 0x15 RTM_DELADDR = 0xd RTM_DELETE = 0x2 - RTM_DESYNC = 0x10 RTM_GET = 0x4 - RTM_IFANNOUNCE = 0xf - RTM_IFINFO = 0xe + RTM_IEEE80211 = 0x11 + RTM_IFANNOUNCE = 0x10 + RTM_IFINFO = 0x14 + RTM_LLINFO_UPD = 0x13 RTM_LOCK = 0x8 RTM_LOSING = 0x5 - RTM_MAXSIZE = 0x800 RTM_MISS = 0x7 RTM_NEWADDR = 0xc + RTM_OIFINFO = 0xf + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_OOIFINFO = 0xe RTM_REDIRECT = 0x6 RTM_RESOLVE = 0xb RTM_RTTUNIT = 0xf4240 + RTM_SETGATE = 0x12 RTM_VERSION = 0x4 RTV_EXPIRE = 0x4 RTV_HOPCOUNT = 0x2 @@ -900,159 +1115,82 @@ const ( RTV_RTTVAR = 0x80 RTV_SPIPE = 0x10 RTV_SSTHRESH = 0x20 - RT_TABLEID_MAX = 0xff - SCM_CREDS = 0x2 + RUSAGE_CHILDREN = -0x1 + RUSAGE_SELF = 0x0 + SCM_CREDS = 0x4 SCM_RIGHTS = 0x1 - SCM_TIMESTAMP = 0x4 + SCM_TIMESTAMP = 0x8 SHUT_RD = 0x0 SHUT_RDWR = 0x2 SHUT_WR = 0x1 - SIGABRT = 0x6 - SIGALRM = 0xe - SIGBUS = 0xa - SIGCHLD = 0x14 - SIGCONT = 0x13 - SIGEMT = 0x7 - SIGFPE = 0x8 - SIGHUP = 0x1 - SIGILL = 0x4 - SIGINFO = 0x1d - SIGINT = 0x2 - SIGIO = 0x17 - SIGIOT = 0x6 - SIGKILL = 0x9 - SIGPIPE = 0xd - SIGPROF = 0x1b - SIGQUIT = 0x3 - SIGSEGV = 0xb - SIGSTOP = 0x11 - SIGSYS = 0xc - SIGTERM = 0xf - SIGTHR = 0x20 - SIGTRAP = 0x5 - SIGTSTP = 0x12 - SIGTTIN = 0x15 - SIGTTOU = 0x16 - SIGURG = 0x10 - SIGUSR1 = 0x1e - SIGUSR2 = 0x1f - SIGVTALRM = 0x1a - SIGWINCH = 0x1c - SIGXCPU = 0x18 - SIGXFSZ = 0x19 - SIOCADDMULTI = 0x80206931 + SIOCADDMULTI = 0x80906931 + SIOCADDRT = 0x8030720a SIOCAIFADDR = 0x8040691a - SIOCAIFGROUP = 0x80246987 - SIOCALIFADDR = 0x8218691c + SIOCALIFADDR = 0x8118691c SIOCATMARK = 0x40047307 - SIOCBRDGADD = 0x8054693c - SIOCBRDGADDS = 0x80546941 - SIOCBRDGARL = 0x806e694d - SIOCBRDGDADDR = 0x80286947 - SIOCBRDGDEL = 0x8054693d - SIOCBRDGDELS = 0x80546942 - SIOCBRDGFLUSH = 0x80546948 - SIOCBRDGFRL = 0x806e694e - SIOCBRDGGCACHE = 0xc0146941 - SIOCBRDGGFD = 0xc0146952 - SIOCBRDGGHT = 0xc0146951 - SIOCBRDGGIFFLGS = 0xc054693e - SIOCBRDGGMA = 0xc0146953 - SIOCBRDGGPARAM = 0xc0386958 - SIOCBRDGGPRI = 0xc0146950 - SIOCBRDGGRL = 0xc028694f - SIOCBRDGGSIFS = 0xc054693c - SIOCBRDGGTO = 0xc0146946 - SIOCBRDGIFS = 0xc0546942 - SIOCBRDGRTS = 0xc0186943 - SIOCBRDGSADDR = 0xc0286944 - SIOCBRDGSCACHE = 0x80146940 - SIOCBRDGSFD = 0x80146952 - SIOCBRDGSHT = 0x80146951 - SIOCBRDGSIFCOST = 0x80546955 - SIOCBRDGSIFFLGS = 0x8054693f - SIOCBRDGSIFPRIO = 0x80546954 - SIOCBRDGSMA = 0x80146953 - SIOCBRDGSPRI = 0x80146950 - SIOCBRDGSPROTO = 0x8014695a - SIOCBRDGSTO = 0x80146945 - SIOCBRDGSTXHC = 0x80146959 - SIOCDELMULTI = 0x80206932 - SIOCDIFADDR = 0x80206919 - SIOCDIFGROUP = 0x80246989 - SIOCDIFPHYADDR = 0x80206949 - SIOCDLIFADDR = 0x8218691e - SIOCGETKALIVE = 0xc01869a4 - SIOCGETLABEL = 0x8020699a - SIOCGETPFLOW = 0xc02069fe - SIOCGETPFSYNC = 0xc02069f8 + SIOCDELMULTI = 0x80906932 + SIOCDELRT = 0x8030720b + SIOCDIFADDR = 0x80906919 + SIOCDIFPHYADDR = 0x80906949 + SIOCDLIFADDR = 0x8118691e + SIOCGDRVSPEC = 0xc01c697b + SIOCGETPFSYNC = 0xc09069f8 SIOCGETSGCNT = 0xc0147534 SIOCGETVIFCNT = 0xc0147533 - SIOCGETVLANPRIO = 0xc0206990 SIOCGHIWAT = 0x40047301 - SIOCGIFADDR = 0xc0206921 - SIOCGIFASYNCMAP = 0xc020697c - SIOCGIFBRDADDR = 0xc0206923 - SIOCGIFCONF = 0xc0086924 - SIOCGIFDATA = 0xc020691b - SIOCGIFDESCR = 0xc0206981 - SIOCGIFDSTADDR = 0xc0206922 - SIOCGIFFLAGS = 0xc0206911 - SIOCGIFGATTR = 0xc024698b - SIOCGIFGENERIC = 0xc020693a - SIOCGIFGMEMB = 0xc024698a - SIOCGIFGROUP = 0xc0246988 + SIOCGIFADDR = 0xc0906921 + SIOCGIFADDRPREF = 0xc0946920 + SIOCGIFALIAS = 0xc040691b + SIOCGIFBRDADDR = 0xc0906923 + SIOCGIFCAP = 0xc0206976 + SIOCGIFCONF = 0xc0086926 + SIOCGIFDATA = 0xc0946985 + SIOCGIFDLT = 0xc0906977 + SIOCGIFDSTADDR = 0xc0906922 + SIOCGIFFLAGS = 0xc0906911 + SIOCGIFGENERIC = 0xc090693a SIOCGIFMEDIA = 0xc0286936 - SIOCGIFMETRIC = 0xc0206917 - SIOCGIFMTU = 0xc020697e - SIOCGIFNETMASK = 0xc0206925 - SIOCGIFPDSTADDR = 0xc0206948 - SIOCGIFPRIORITY = 0xc020699c - SIOCGIFPSRCADDR = 0xc0206947 - SIOCGIFRDOMAIN = 0xc02069a0 - SIOCGIFRTLABEL = 0xc0206983 - SIOCGIFTIMESLOT = 0xc0206986 - SIOCGIFXFLAGS = 0xc020699e - SIOCGLIFADDR = 0xc218691d - SIOCGLIFPHYADDR = 0xc218694b - SIOCGLIFPHYRTABLE = 0xc02069a2 + SIOCGIFMETRIC = 0xc0906917 + SIOCGIFMTU = 0xc090697e + SIOCGIFNETMASK = 0xc0906925 + SIOCGIFPDSTADDR = 0xc0906948 + SIOCGIFPSRCADDR = 0xc0906947 + SIOCGLIFADDR = 0xc118691d + SIOCGLIFPHYADDR = 0xc118694b + SIOCGLINKSTR = 0xc01c6987 SIOCGLOWAT = 0x40047303 SIOCGPGRP = 0x40047309 - SIOCGVH = 0xc02069f6 - SIOCIFCREATE = 0x8020697a - SIOCIFDESTROY = 0x80206979 + SIOCGVH = 0xc0906983 + SIOCIFCREATE = 0x8090697a + SIOCIFDESTROY = 0x80906979 SIOCIFGCLONERS = 0xc00c6978 - SIOCSETKALIVE = 0x801869a3 - SIOCSETLABEL = 0x80206999 - SIOCSETPFLOW = 0x802069fd - SIOCSETPFSYNC = 0x802069f7 - SIOCSETVLANPRIO = 0x8020698f + SIOCINITIFADDR = 0xc0446984 + SIOCSDRVSPEC = 0x801c697b + SIOCSETPFSYNC = 0x809069f7 SIOCSHIWAT = 0x80047300 - SIOCSIFADDR = 0x8020690c - SIOCSIFASYNCMAP = 0x8020697d - SIOCSIFBRDADDR = 0x80206913 - SIOCSIFDESCR = 0x80206980 - SIOCSIFDSTADDR = 0x8020690e - SIOCSIFFLAGS = 0x80206910 - SIOCSIFGATTR = 0x8024698c - SIOCSIFGENERIC = 0x80206939 - SIOCSIFLLADDR = 0x8020691f - SIOCSIFMEDIA = 0xc0206935 - SIOCSIFMETRIC = 0x80206918 - SIOCSIFMTU = 0x8020697f - SIOCSIFNETMASK = 0x80206916 + SIOCSIFADDR = 0x8090690c + SIOCSIFADDRPREF = 0x8094691f + SIOCSIFBRDADDR = 0x80906913 + SIOCSIFCAP = 0x80206975 + SIOCSIFDSTADDR = 0x8090690e + SIOCSIFFLAGS = 0x80906910 + SIOCSIFGENERIC = 0x80906939 + SIOCSIFMEDIA = 0xc0906935 + SIOCSIFMETRIC = 0x80906918 + SIOCSIFMTU = 0x8090697f + SIOCSIFNETMASK = 0x80906916 SIOCSIFPHYADDR = 0x80406946 - SIOCSIFPRIORITY = 0x8020699b - SIOCSIFRDOMAIN = 0x8020699f - SIOCSIFRTLABEL = 0x80206982 - SIOCSIFTIMESLOT = 0x80206985 - SIOCSIFXFLAGS = 0x8020699d - SIOCSLIFPHYADDR = 0x8218694a - SIOCSLIFPHYRTABLE = 0x802069a1 + SIOCSLIFPHYADDR = 0x8118694a + SIOCSLINKSTR = 0x801c6988 SIOCSLOWAT = 0x80047302 SIOCSPGRP = 0x80047308 - SIOCSVH = 0xc02069f5 + SIOCSVH = 0xc0906982 + SIOCZIFDATA = 0xc0946986 + SOCK_CLOEXEC = 0x10000000 SOCK_DGRAM = 0x2 + SOCK_FLAGS_MASK = 0xf0000000 + SOCK_NONBLOCK = 0x20000000 + SOCK_NOSIGPIPE = 0x40000000 SOCK_RAW = 0x3 SOCK_RDM = 0x4 SOCK_SEQPACKET = 0x5 @@ -1060,63 +1198,107 @@ const ( SOL_SOCKET = 0xffff SOMAXCONN = 0x80 SO_ACCEPTCONN = 0x2 - SO_BINDANY = 0x1000 + SO_ACCEPTFILTER = 0x1000 SO_BROADCAST = 0x20 SO_DEBUG = 0x1 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 - SO_JUMBO = 0x400 SO_KEEPALIVE = 0x8 SO_LINGER = 0x80 - SO_NETPROC = 0x1020 + SO_NOHEADER = 0x100a + SO_NOSIGPIPE = 0x800 SO_OOBINLINE = 0x100 - SO_PEERCRED = 0x1022 + SO_OVERFLOWED = 0x1009 SO_RCVBUF = 0x1002 SO_RCVLOWAT = 0x1004 - SO_RCVTIMEO = 0x1006 + SO_RCVTIMEO = 0x100c SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 - SO_RTABLE = 0x1021 SO_SNDBUF = 0x1001 SO_SNDLOWAT = 0x1003 - SO_SNDTIMEO = 0x1005 - SO_SPLICE = 0x1023 - SO_TIMESTAMP = 0x800 + SO_SNDTIMEO = 0x100b + SO_TIMESTAMP = 0x2000 SO_TYPE = 0x1008 SO_USELOOPBACK = 0x40 + SYSCTL_VERSION = 0x1000000 + SYSCTL_VERS_0 = 0x0 + SYSCTL_VERS_1 = 0x1000000 + SYSCTL_VERS_MASK = 0xff000000 + S_ARCH1 = 0x10000 + S_ARCH2 = 0x20000 + S_BLKSIZE = 0x200 + S_IEXEC = 0x40 + S_IFBLK = 0x6000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFIFO = 0x1000 + S_IFLNK = 0xa000 + S_IFMT = 0xf000 + S_IFREG = 0x8000 + S_IFSOCK = 0xc000 + S_IFWHT = 0xe000 + S_IREAD = 0x100 + S_IRGRP = 0x20 + S_IROTH = 0x4 + S_IRUSR = 0x100 + S_IRWXG = 0x38 + S_IRWXO = 0x7 + S_IRWXU = 0x1c0 + S_ISGID = 0x400 + S_ISTXT = 0x200 + S_ISUID = 0x800 + S_ISVTX = 0x200 + S_IWGRP = 0x10 + S_IWOTH = 0x2 + S_IWRITE = 0x80 + S_IWUSR = 0x80 + S_IXGRP = 0x8 + S_IXOTH = 0x1 + S_IXUSR = 0x40 + S_LOGIN_SET = 0x1 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 + TCP_CONGCTL = 0x20 + TCP_KEEPCNT = 0x6 + TCP_KEEPIDLE = 0x3 + TCP_KEEPINIT = 0x7 + TCP_KEEPINTVL = 0x5 TCP_MAXBURST = 0x4 TCP_MAXSEG = 0x2 TCP_MAXWIN = 0xffff - TCP_MAX_SACK = 0x3 TCP_MAX_WINSHIFT = 0xe - TCP_MD5SIG = 0x4 - TCP_MSS = 0x200 + TCP_MD5SIG = 0x10 + TCP_MINMSS = 0xd8 + TCP_MSS = 0x218 TCP_NODELAY = 0x1 - TCP_NSTATES = 0xb - TCP_SACK_ENABLE = 0x8 + TCSAFLUSH = 0x2 TIOCCBRK = 0x2000747a TIOCCDTR = 0x20007478 TIOCCONS = 0x80047462 + TIOCDCDTIMESTAMP = 0x400c7458 TIOCDRAIN = 0x2000745e TIOCEXCL = 0x2000740d TIOCEXT = 0x80047460 + TIOCFLAG_CDTRCTS = 0x10 TIOCFLAG_CLOCAL = 0x2 TIOCFLAG_CRTSCTS = 0x4 TIOCFLAG_MDMBUF = 0x8 - TIOCFLAG_PPS = 0x10 TIOCFLAG_SOFTCAR = 0x1 TIOCFLUSH = 0x80047410 TIOCGETA = 0x402c7413 TIOCGETD = 0x4004741a TIOCGFLAGS = 0x4004745d + TIOCGLINED = 0x40207442 TIOCGPGRP = 0x40047477 - TIOCGTSTAMP = 0x4008745b + TIOCGQSIZE = 0x40047481 + TIOCGRANTPT = 0x20007447 + TIOCGSID = 0x40047463 + TIOCGSIZE = 0x40087468 TIOCGWINSZ = 0x40087468 TIOCMBIC = 0x8004746b TIOCMBIS = 0x8004746c TIOCMGET = 0x4004746a - TIOCMODG = 0x4004746a - TIOCMODS = 0x8004746d TIOCMSET = 0x8004746d TIOCM_CAR = 0x40 TIOCM_CD = 0x40 @@ -1141,6 +1323,9 @@ const ( TIOCPKT_NOSTOP = 0x10 TIOCPKT_START = 0x8 TIOCPKT_STOP = 0x4 + TIOCPTMGET = 0x40287446 + TIOCPTSNAME = 0x40287448 + TIOCRCVFRAME = 0x80047445 TIOCREMOTE = 0x80047469 TIOCSBRK = 0x2000747b TIOCSCTTY = 0x20007461 @@ -1150,19 +1335,46 @@ const ( TIOCSETAW = 0x802c7415 TIOCSETD = 0x8004741b TIOCSFLAGS = 0x8004745c - TIOCSIG = 0x8004745f + TIOCSIG = 0x2000745f + TIOCSLINED = 0x80207443 TIOCSPGRP = 0x80047476 + TIOCSQSIZE = 0x80047480 + TIOCSSIZE = 0x80087467 TIOCSTART = 0x2000746e TIOCSTAT = 0x80047465 TIOCSTI = 0x80017472 TIOCSTOP = 0x2000746f - TIOCSTSTAMP = 0x8008745a TIOCSWINSZ = 0x80087467 TIOCUCNTL = 0x80047466 + TIOCXMTFRAME = 0x80047444 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VTIME = 0x11 + VWERASE = 0x4 + WALL = 0x8 + WALLSIG = 0x8 WALTSIG = 0x4 - WCONTINUED = 0x8 + WCLONE = 0x4 WCOREFLAG = 0x80 WNOHANG = 0x1 + WNOWAIT = 0x10000 + WNOZOMBIE = 0x20000 + WOPTSCHECKED = 0x40000 WSTOPPED = 0x7f WUNTRACED = 0x2 ) @@ -1178,17 +1390,11 @@ const ( EALREADY = Errno(0x25) EAUTH = Errno(0x50) EBADF = Errno(0x9) + EBADMSG = Errno(0x58) EBADRPC = Errno(0x48) EBUSY = Errno(0x10) - ECANCELED = Errno(0x58) + ECANCELED = Errno(0x57) ECHILD = Errno(0xa) - ECHO = Errno(0x8) - ECHOCTL = Errno(0x40) - ECHOE = Errno(0x2) - ECHOK = Errno(0x4) - ECHOKE = Errno(0x1) - ECHONL = Errno(0x10) - ECHOPRT = Errno(0x20) ECONNABORTED = Errno(0x35) ECONNREFUSED = Errno(0x3d) ECONNRESET = Errno(0x36) @@ -1202,50 +1408,51 @@ const ( EFTYPE = Errno(0x4f) EHOSTDOWN = Errno(0x40) EHOSTUNREACH = Errno(0x41) - EIDRM = Errno(0x59) - EILSEQ = Errno(0x54) + EIDRM = Errno(0x52) + EILSEQ = Errno(0x55) EINPROGRESS = Errno(0x24) EINTR = Errno(0x4) EINVAL = Errno(0x16) EIO = Errno(0x5) - EIPSEC = Errno(0x52) EISCONN = Errno(0x38) EISDIR = Errno(0x15) - ELAST = Errno(0x5b) + ELAST = Errno(0x60) ELOOP = Errno(0x3e) - EMEDIUMTYPE = Errno(0x56) EMFILE = Errno(0x18) EMLINK = Errno(0x1f) EMSGSIZE = Errno(0x28) + EMULTIHOP = Errno(0x5e) ENAMETOOLONG = Errno(0x3f) - ENDRUNDISC = Errno(0x9) ENEEDAUTH = Errno(0x51) ENETDOWN = Errno(0x32) ENETRESET = Errno(0x34) ENETUNREACH = Errno(0x33) ENFILE = Errno(0x17) - ENOATTR = Errno(0x53) + ENOATTR = Errno(0x5d) ENOBUFS = Errno(0x37) + ENODATA = Errno(0x59) ENODEV = Errno(0x13) ENOENT = Errno(0x2) ENOEXEC = Errno(0x8) ENOLCK = Errno(0x4d) - ENOMEDIUM = Errno(0x55) + ENOLINK = Errno(0x5f) ENOMEM = Errno(0xc) - ENOMSG = Errno(0x5a) + ENOMSG = Errno(0x53) ENOPROTOOPT = Errno(0x2a) ENOSPC = Errno(0x1c) + ENOSR = Errno(0x5a) + ENOSTR = Errno(0x5b) ENOSYS = Errno(0x4e) ENOTBLK = Errno(0xf) ENOTCONN = Errno(0x39) ENOTDIR = Errno(0x14) ENOTEMPTY = Errno(0x42) ENOTSOCK = Errno(0x26) - ENOTSUP = Errno(0x5b) + ENOTSUP = Errno(0x56) ENOTTY = Errno(0x19) ENXIO = Errno(0x6) EOPNOTSUPP = Errno(0x2d) - EOVERFLOW = Errno(0x57) + EOVERFLOW = Errno(0x54) EPERM = Errno(0x1) EPFNOSUPPORT = Errno(0x2e) EPIPE = Errno(0x20) @@ -1253,6 +1460,7 @@ const ( EPROCUNAVAIL = Errno(0x4c) EPROGMISMATCH = Errno(0x4b) EPROGUNAVAIL = Errno(0x4a) + EPROTO = Errno(0x60) EPROTONOSUPPORT = Errno(0x2b) EPROTOTYPE = Errno(0x29) ERANGE = Errno(0x22) @@ -1264,6 +1472,7 @@ const ( ESPIPE = Errno(0x1d) ESRCH = Errno(0x3) ESTALE = Errno(0x46) + ETIME = Errno(0x5c) ETIMEDOUT = Errno(0x3c) ETOOMANYREFS = Errno(0x3b) ETXTBSY = Errno(0x1a) @@ -1272,6 +1481,43 @@ const ( EXDEV = Errno(0x12) ) +// Signals +const ( + SIGABRT = Signal(0x6) + SIGALRM = Signal(0xe) + SIGBUS = Signal(0xa) + SIGCHLD = Signal(0x14) + SIGCONT = Signal(0x13) + SIGEMT = Signal(0x7) + SIGFPE = Signal(0x8) + SIGHUP = Signal(0x1) + SIGILL = Signal(0x4) + SIGINFO = Signal(0x1d) + SIGINT = Signal(0x2) + SIGIO = Signal(0x17) + SIGIOT = Signal(0x6) + SIGKILL = Signal(0x9) + SIGPIPE = Signal(0xd) + SIGPROF = Signal(0x1b) + SIGPWR = Signal(0x20) + SIGQUIT = Signal(0x3) + SIGSEGV = Signal(0xb) + SIGSTOP = Signal(0x11) + SIGSYS = Signal(0xc) + SIGTERM = Signal(0xf) + SIGTRAP = Signal(0x5) + SIGTSTP = Signal(0x12) + SIGTTIN = Signal(0x15) + SIGTTOU = Signal(0x16) + SIGURG = Signal(0x10) + SIGUSR1 = Signal(0x1e) + SIGUSR2 = Signal(0x1f) + SIGVTALRM = Signal(0x1a) + SIGWINCH = Signal(0x1c) + SIGXCPU = Signal(0x18) + SIGXFSZ = Signal(0x19) +) + // Error table var errors = [...]string{ 1: "operation not permitted", @@ -1307,7 +1553,7 @@ var errors = [...]string{ 31: "too many links", 32: "broken pipe", 33: "numerical argument out of domain", - 34: "result too large", + 34: "result too large or too small", 35: "resource temporarily unavailable", 36: "operation now in progress", 37: "operation already in progress", @@ -1315,7 +1561,7 @@ var errors = [...]string{ 39: "destination address required", 40: "message too long", 41: "protocol wrong type for socket", - 42: "protocol not available", + 42: "protocol option not available", 43: "protocol not supported", 44: "socket type not supported", 45: "operation not supported", @@ -1355,14 +1601,55 @@ var errors = [...]string{ 79: "inappropriate file type or format", 80: "authentication error", 81: "need authenticator", - 82: "IPsec processing failure", - 83: "attribute not found", - 84: "illegal byte sequence", - 85: "no medium found", - 86: "wrong medium type", - 87: "value too large to be stored in data type", - 88: "operation canceled", - 89: "identifier removed", - 90: "no message of desired type", - 91: "not supported", + 82: "identifier removed", + 83: "no message of desired type", + 84: "value too large to be stored in data type", + 85: "illegal byte sequence", + 86: "not supported", + 87: "operation Canceled", + 88: "bad or Corrupt message", + 89: "no message available", + 90: "no STREAM resources", + 91: "not a STREAM", + 92: "STREAM ioctl timeout", + 93: "attribute not found", + 94: "multihop attempted", + 95: "link has been severed", + 96: "protocol error", +} + +// Signal table +var signals = [...]string{ + 1: "hangup", + 2: "interrupt", + 3: "quit", + 4: "illegal instruction", + 5: "trace/BPT trap", + 6: "abort trap", + 7: "EMT trap", + 8: "floating point exception", + 9: "killed", + 10: "bus error", + 11: "segmentation fault", + 12: "bad system call", + 13: "broken pipe", + 14: "alarm clock", + 15: "terminated", + 16: "urgent I/O condition", + 17: "stopped (signal)", + 18: "stopped", + 19: "continued", + 20: "child exited", + 21: "stopped (tty input)", + 22: "stopped (tty output)", + 23: "I/O possible", + 24: "cputime limit exceeded", + 25: "filesize limit exceeded", + 26: "virtual timer expired", + 27: "profiling timer expired", + 28: "window size changes", + 29: "information request", + 30: "user defined signal 1", + 31: "user defined signal 2", + 32: "power fail/restart", } diff --git a/src/pkg/syscall/zerrors_netbsd_amd64.go b/src/pkg/syscall/zerrors_netbsd_amd64.go index c6367fbbc..f719528a7 100644 --- a/src/pkg/syscall/zerrors_netbsd_amd64.go +++ b/src/pkg/syscall/zerrors_netbsd_amd64.go @@ -8,7 +8,8 @@ package syscall const ( AF_APPLETALK = 0x10 - AF_BLUETOOTH = 0x20 + AF_ARP = 0x1c + AF_BLUETOOTH = 0x1f AF_CCITT = 0xa AF_CHAOS = 0x5 AF_CNT = 0x15 @@ -18,67 +19,93 @@ const ( AF_DLI = 0xd AF_E164 = 0x1a AF_ECMA = 0x8 - AF_ENCAP = 0x1c AF_HYLINK = 0xf + AF_IEEE80211 = 0x20 AF_IMPLINK = 0x3 AF_INET = 0x2 AF_INET6 = 0x18 AF_IPX = 0x17 AF_ISDN = 0x1a AF_ISO = 0x7 - AF_KEY = 0x1e AF_LAT = 0xe AF_LINK = 0x12 AF_LOCAL = 0x1 - AF_MAX = 0x24 + AF_MAX = 0x23 AF_MPLS = 0x21 AF_NATM = 0x1b AF_NS = 0x6 + AF_OROUTE = 0x11 AF_OSI = 0x7 AF_PUP = 0x4 - AF_ROUTE = 0x11 - AF_SIP = 0x1d + AF_ROUTE = 0x22 AF_SNA = 0xb AF_UNIX = 0x1 AF_UNSPEC = 0x0 + ARPHRD_ARCNET = 0x7 ARPHRD_ETHER = 0x1 ARPHRD_FRELAY = 0xf ARPHRD_IEEE1394 = 0x18 ARPHRD_IEEE802 = 0x6 + ARPHRD_STRIP = 0x17 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B460800 = 0x70800 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B921600 = 0xe1000 + B9600 = 0x2580 + BIOCFEEDBACK = 0x8004427d BIOCFLUSH = 0x20004268 BIOCGBLEN = 0x40044266 - BIOCGDIRFILT = 0x4004427c BIOCGDLT = 0x4004426a - BIOCGDLTLIST = 0xc010427b - BIOCGETIF = 0x4020426b - BIOCGFILDROP = 0x40044278 + BIOCGDLTLIST = 0xc0104277 + BIOCGETIF = 0x4090426b + BIOCGFEEDBACK = 0x4004427c BIOCGHDRCMPLT = 0x40044274 - BIOCGRSIG = 0x40044273 - BIOCGRTIMEOUT = 0x4010426e - BIOCGSTATS = 0x4008426f + BIOCGRTIMEOUT = 0x4010427b + BIOCGSEESENT = 0x40044278 + BIOCGSTATS = 0x4080426f + BIOCGSTATSOLD = 0x4008426f BIOCIMMEDIATE = 0x80044270 - BIOCLOCK = 0x20004276 BIOCPROMISC = 0x20004269 BIOCSBLEN = 0xc0044266 - BIOCSDIRFILT = 0x8004427d - BIOCSDLT = 0x8004427a + BIOCSDLT = 0x80044276 BIOCSETF = 0x80104267 - BIOCSETIF = 0x8020426c - BIOCSETWF = 0x80104277 - BIOCSFILDROP = 0x80044279 + BIOCSETIF = 0x8090426c + BIOCSFEEDBACK = 0x8004427d BIOCSHDRCMPLT = 0x80044275 - BIOCSRSIG = 0x80044272 - BIOCSRTIMEOUT = 0x8010426d + BIOCSRTIMEOUT = 0x8010427a + BIOCSSEESENT = 0x80044279 + BIOCSTCPF = 0x80104272 + BIOCSUDPF = 0x80104273 BIOCVERSION = 0x40044271 BPF_A = 0x10 BPF_ABS = 0x20 BPF_ADD = 0x0 - BPF_ALIGNMENT = 0x4 + BPF_ALIGNMENT = 0x8 + BPF_ALIGNMENT32 = 0x4 BPF_ALU = 0x4 BPF_AND = 0x50 BPF_B = 0x10 - BPF_DIRECTION_IN = 0x1 - BPF_DIRECTION_OUT = 0x2 + BPF_DFLTBUFSIZE = 0x100000 BPF_DIV = 0x30 BPF_H = 0x8 BPF_IMM = 0x0 @@ -95,7 +122,7 @@ const ( BPF_LEN = 0x80 BPF_LSH = 0x60 BPF_MAJOR_VERSION = 0x1 - BPF_MAXBUFSIZE = 0x200000 + BPF_MAXBUFSIZE = 0x1000000 BPF_MAXINSNS = 0x200 BPF_MEM = 0x60 BPF_MEMWORDS = 0x10 @@ -116,31 +143,151 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x8000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0x14 + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a CTL_MAXNAME = 0xc CTL_NET = 0x4 + CTL_QUERY = -0x2 + DIOCBSFLUSH = 0x20006478 + DLT_A429 = 0xb8 + DLT_A653_ICM = 0xb9 + DLT_AIRONET_HEADER = 0x78 + DLT_AOS = 0xde + DLT_APPLE_IP_OVER_IEEE1394 = 0x8a DLT_ARCNET = 0x7 + DLT_ARCNET_LINUX = 0x81 + DLT_ATM_CLIP = 0x13 DLT_ATM_RFC1483 = 0xb + DLT_AURORA = 0x7e DLT_AX25 = 0x3 + DLT_AX25_KISS = 0xca + DLT_BACNET_MS_TP = 0xa5 + DLT_BLUETOOTH_HCI_H4 = 0xbb + DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9 + DLT_CAN20B = 0xbe + DLT_CAN_SOCKETCAN = 0xe3 DLT_CHAOS = 0x5 + DLT_CISCO_IOS = 0x76 + DLT_C_HDLC = 0x68 + DLT_C_HDLC_WITH_DIR = 0xcd + DLT_DECT = 0xdd + DLT_DOCSIS = 0x8f + DLT_ECONET = 0x73 DLT_EN10MB = 0x1 DLT_EN3MB = 0x2 - DLT_ENC = 0xd + DLT_ENC = 0x6d + DLT_ERF = 0xc5 + DLT_ERF_ETH = 0xaf + DLT_ERF_POS = 0xb0 + DLT_FC_2 = 0xe0 + DLT_FC_2_WITH_FRAME_DELIMS = 0xe1 DLT_FDDI = 0xa + DLT_FLEXRAY = 0xd2 + DLT_FRELAY = 0x6b + DLT_FRELAY_WITH_DIR = 0xce + DLT_GCOM_SERIAL = 0xad + DLT_GCOM_T1E1 = 0xac + DLT_GPF_F = 0xab + DLT_GPF_T = 0xaa + DLT_GPRS_LLC = 0xa9 + DLT_GSMTAP_ABIS = 0xda + DLT_GSMTAP_UM = 0xd9 + DLT_HDLC = 0x10 + DLT_HHDLC = 0x79 + DLT_HIPPI = 0xf + DLT_IBM_SN = 0x92 + DLT_IBM_SP = 0x91 DLT_IEEE802 = 0x6 DLT_IEEE802_11 = 0x69 DLT_IEEE802_11_RADIO = 0x7f - DLT_LOOP = 0xc + DLT_IEEE802_11_RADIO_AVS = 0xa3 + DLT_IEEE802_15_4 = 0xc3 + DLT_IEEE802_15_4_LINUX = 0xbf + DLT_IEEE802_15_4_NONASK_PHY = 0xd7 + DLT_IEEE802_16_MAC_CPS = 0xbc + DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 + DLT_IPMB = 0xc7 + DLT_IPMB_LINUX = 0xd1 + DLT_IPNET = 0xe2 + DLT_IPV4 = 0xe4 + DLT_IPV6 = 0xe5 + DLT_IP_OVER_FC = 0x7a + DLT_JUNIPER_ATM1 = 0x89 + DLT_JUNIPER_ATM2 = 0x87 + DLT_JUNIPER_CHDLC = 0xb5 + DLT_JUNIPER_ES = 0x84 + DLT_JUNIPER_ETHER = 0xb2 + DLT_JUNIPER_FRELAY = 0xb4 + DLT_JUNIPER_GGSN = 0x85 + DLT_JUNIPER_ISM = 0xc2 + DLT_JUNIPER_MFR = 0x86 + DLT_JUNIPER_MLFR = 0x83 + DLT_JUNIPER_MLPPP = 0x82 + DLT_JUNIPER_MONITOR = 0xa4 + DLT_JUNIPER_PIC_PEER = 0xae + DLT_JUNIPER_PPP = 0xb3 + DLT_JUNIPER_PPPOE = 0xa7 + DLT_JUNIPER_PPPOE_ATM = 0xa8 + DLT_JUNIPER_SERVICES = 0x88 + DLT_JUNIPER_ST = 0xc8 + DLT_JUNIPER_VP = 0xb7 + DLT_LAPB_WITH_DIR = 0xcf + DLT_LAPD = 0xcb + DLT_LIN = 0xd4 + DLT_LINUX_EVDEV = 0xd8 + DLT_LINUX_IRDA = 0x90 + DLT_LINUX_LAPD = 0xb1 + DLT_LINUX_SLL = 0x71 + DLT_LOOP = 0x6c + DLT_LTALK = 0x72 + DLT_MFR = 0xb6 + DLT_MOST = 0xd3 DLT_MPLS = 0xdb + DLT_MTP2 = 0x8c + DLT_MTP2_WITH_PHDR = 0x8b + DLT_MTP3 = 0x8d DLT_NULL = 0x0 + DLT_PCI_EXP = 0x7d DLT_PFLOG = 0x75 DLT_PFSYNC = 0x12 + DLT_PPI = 0xc0 DLT_PPP = 0x9 - DLT_PPP_BSDOS = 0x10 + DLT_PPP_BSDOS = 0xe DLT_PPP_ETHER = 0x33 + DLT_PPP_PPPD = 0xa6 + DLT_PPP_SERIAL = 0x32 + DLT_PPP_WITH_DIR = 0xcc + DLT_PRISM_HEADER = 0x77 DLT_PRONET = 0x4 - DLT_RAW = 0xe + DLT_RAIF1 = 0xc6 + DLT_RAW = 0xc + DLT_RAWAF_MASK = 0x2240000 + DLT_RIO = 0x7c + DLT_SCCP = 0x8e + DLT_SITA = 0xc4 DLT_SLIP = 0x8 - DLT_SLIP_BSDOS = 0xf + DLT_SLIP_BSDOS = 0xd + DLT_SUNATM = 0x7b + DLT_SYMANTEC_FIREWALL = 0x63 + DLT_TZSP = 0x80 + DLT_USB = 0xba + DLT_USB_LINUX = 0xbd + DLT_USB_LINUX_MMAPPED = 0xdc + DLT_WIHART = 0xdf + DLT_X2E_SERIAL = 0xd5 + DLT_X2E_XORAYA = 0xd6 DT_BLK = 0x6 DT_CHR = 0x2 DT_DIR = 0x4 @@ -149,15 +296,23 @@ const ( DT_REG = 0x8 DT_SOCK = 0xc DT_UNKNOWN = 0x0 - EFER_LMA = 0x400 - EFER_LME = 0x100 - EFER_NXE = 0x800 - EFER_SCE = 0x1 - EMT_TAGOVF = 0x1 - EMUL_ENABLED = 0x1 - EMUL_NATIVE = 0x2 + DT_WHT = 0xe + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 + EMUL_LINUX = 0x1 + EMUL_LINUX32 = 0x5 + EMUL_MAXID = 0x6 + ETHERCAP_JUMBO_MTU = 0x4 + ETHERCAP_VLAN_HWTAGGING = 0x2 + ETHERCAP_VLAN_MTU = 0x1 ETHERMIN = 0x2e ETHERMTU = 0x5dc + ETHERMTU_JUMBO = 0x2328 ETHERTYPE_8023 = 0x4 ETHERTYPE_AARP = 0x80f3 ETHERTYPE_ACCTON = 0x8390 @@ -165,7 +320,6 @@ const ( ETHERTYPE_ALPHA = 0x814a ETHERTYPE_AMBER = 0x6008 ETHERTYPE_AMOEBA = 0x8145 - ETHERTYPE_AOE = 0x88a2 ETHERTYPE_APOLLO = 0x80f7 ETHERTYPE_APOLLODOMAIN = 0x8019 ETHERTYPE_APPLETALK = 0x809b @@ -236,7 +390,6 @@ const ( ETHERTYPE_LAT = 0x6004 ETHERTYPE_LBACK = 0x9000 ETHERTYPE_LITTLE = 0x8060 - ETHERTYPE_LLDP = 0x88cc ETHERTYPE_LOGICRAFT = 0x8148 ETHERTYPE_LOOPBACK = 0x9000 ETHERTYPE_MATRA = 0x807a @@ -284,7 +437,6 @@ const ( ETHERTYPE_PRIMENTS = 0x7031 ETHERTYPE_PUP = 0x200 ETHERTYPE_PUPAT = 0x200 - ETHERTYPE_QINQ = 0x88a8 ETHERTYPE_RACAL = 0x7030 ETHERTYPE_RATIONAL = 0x8150 ETHERTYPE_RAWFR = 0x6559 @@ -301,7 +453,7 @@ const ( ETHERTYPE_SG_NETGAMES = 0x8014 ETHERTYPE_SG_RESV = 0x8015 ETHERTYPE_SIMNET = 0x5208 - ETHERTYPE_SLOW = 0x8809 + ETHERTYPE_SLOWPROTOCOLS = 0x8809 ETHERTYPE_SNA = 0x80d5 ETHERTYPE_SNMP = 0x814c ETHERTYPE_SONIX = 0xfaf5 @@ -344,24 +496,24 @@ const ( ETHERTYPE_XNSSM = 0x9001 ETHERTYPE_XTP = 0x817d ETHER_ADDR_LEN = 0x6 - ETHER_ALIGN = 0x2 ETHER_CRC_LEN = 0x4 ETHER_CRC_POLY_BE = 0x4c11db6 ETHER_CRC_POLY_LE = 0xedb88320 ETHER_HDR_LEN = 0xe - ETHER_MAX_DIX_LEN = 0x600 ETHER_MAX_LEN = 0x5ee + ETHER_MAX_LEN_JUMBO = 0x233a ETHER_MIN_LEN = 0x40 + ETHER_PPPOE_ENCAP_LEN = 0x8 ETHER_TYPE_LEN = 0x2 ETHER_VLAN_ENCAP_LEN = 0x4 - EVFILT_AIO = -0x3 - EVFILT_PROC = -0x5 - EVFILT_READ = -0x1 - EVFILT_SIGNAL = -0x6 + EVFILT_AIO = 0x2 + EVFILT_PROC = 0x4 + EVFILT_READ = 0x0 + EVFILT_SIGNAL = 0x5 EVFILT_SYSCOUNT = 0x7 - EVFILT_TIMER = -0x7 - EVFILT_VNODE = -0x4 - EVFILT_WRITE = -0x2 + EVFILT_TIMER = 0x6 + EVFILT_VNODE = 0x3 + EVFILT_WRITE = 0x1 EV_ADD = 0x1 EV_CLEAR = 0x20 EV_DELETE = 0x2 @@ -376,25 +528,46 @@ const ( EXTB = 0x9600 EXTPROC = 0x800 FD_CLOEXEC = 0x1 - FD_SETSIZE = 0x400 + FD_SETSIZE = 0x100 + FLUSHO = 0x800000 + F_CLOSEM = 0xa F_DUPFD = 0x0 - F_DUPFD_CLOEXEC = 0xa + F_DUPFD_CLOEXEC = 0xc + F_FSCTL = -0x80000000 + F_FSDIRMASK = 0x70000000 + F_FSIN = 0x10000000 + F_FSINOUT = 0x30000000 + F_FSOUT = 0x20000000 + F_FSPRIV = 0x8000 + F_FSVOID = 0x40000000 F_GETFD = 0x1 F_GETFL = 0x3 F_GETLK = 0x7 + F_GETNOSIGPIPE = 0xd F_GETOWN = 0x5 + F_MAXFD = 0xb + F_OK = 0x0 + F_PARAM_MASK = 0xfff + F_PARAM_MAX = 0xfff F_RDLCK = 0x1 F_SETFD = 0x2 F_SETFL = 0x4 F_SETLK = 0x8 F_SETLKW = 0x9 + F_SETNOSIGPIPE = 0xe F_SETOWN = 0x6 F_UNLCK = 0x2 F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 IFA_ROUTE = 0x1 IFF_ALLMULTI = 0x200 IFF_BROADCAST = 0x2 - IFF_CANTCHANGE = 0x8e52 + IFF_CANTCHANGE = 0x8f52 IFF_DEBUG = 0x4 IFF_LINK0 = 0x1000 IFF_LINK1 = 0x2000 @@ -431,10 +604,9 @@ const ( IFT_ATMVCIENDPT = 0xc2 IFT_ATMVIRTUAL = 0x95 IFT_BGPPOLICYACCOUNTING = 0xa2 - IFT_BLUETOOTH = 0xf8 IFT_BRIDGE = 0xd1 IFT_BSC = 0x53 - IFT_CARP = 0xf7 + IFT_CARP = 0xf8 IFT_CCTEMUL = 0x3d IFT_CEPT = 0x13 IFT_CES = 0x85 @@ -455,19 +627,17 @@ const ( IFT_DS1FDL = 0xaa IFT_DS3 = 0x1e IFT_DTM = 0x8c - IFT_DUMMY = 0xf1 IFT_DVBASILN = 0xac IFT_DVBASIOUT = 0xad IFT_DVBRCCDOWNSTREAM = 0x93 IFT_DVBRCCMACLAYER = 0x92 IFT_DVBRCCUPSTREAM = 0x94 IFT_ECONET = 0xce - IFT_ENC = 0xf4 IFT_EON = 0x19 IFT_EPLRS = 0x57 IFT_ESCON = 0x49 IFT_ETHER = 0x6 - IFT_FAITH = 0xf3 + IFT_FAITH = 0xf2 IFT_FAST = 0x7d IFT_FASTETHER = 0x3e IFT_FASTETHERFX = 0x45 @@ -555,7 +725,6 @@ const ( IFT_P80 = 0xd IFT_PARA = 0x22 IFT_PFLOG = 0xf5 - IFT_PFLOW = 0xf9 IFT_PFSYNC = 0xf6 IFT_PLC = 0xae IFT_PON155 = 0xcf @@ -573,7 +742,7 @@ const ( IFT_PROPVIRTUAL = 0x35 IFT_PROPWIRELESSP2P = 0x9d IFT_PTPSERIAL = 0x16 - IFT_PVC = 0xf2 + IFT_PVC = 0xf1 IFT_Q2931 = 0xc9 IFT_QLLC = 0x44 IFT_RADIOMAC = 0xbc @@ -599,6 +768,7 @@ const ( IFT_SS7SIGLINK = 0x9c IFT_STACKTOSTACK = 0x6f IFT_STARLAN = 0xb + IFT_STF = 0xd7 IFT_T1 = 0x12 IFT_TDLC = 0x74 IFT_TELINK = 0xc8 @@ -633,6 +803,12 @@ const ( IFT_X25MLP = 0x79 IFT_X25PLE = 0x28 IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 IN_CLASSA_HOST = 0xffffff IN_CLASSA_MAX = 0x80 IN_CLASSA_NET = 0xff000000 @@ -648,12 +824,8 @@ const ( IN_CLASSD_NET = 0xf0000000 IN_CLASSD_NSHIFT = 0x1c IN_LOOPBACKNET = 0x7f - IN_RFC3021_HOST = 0x1 - IN_RFC3021_NET = 0xfffffffe - IN_RFC3021_NSHIFT = 0x1f IPPROTO_AH = 0x33 IPPROTO_CARP = 0x70 - IPPROTO_DIVERT = 0x102 IPPROTO_DONE = 0x101 IPPROTO_DSTOPTS = 0x3c IPPROTO_EGP = 0x8 @@ -674,10 +846,10 @@ const ( IPPROTO_IPIP = 0x4 IPPROTO_IPV4 = 0x4 IPPROTO_IPV6 = 0x29 + IPPROTO_IPV6_ICMP = 0x3a IPPROTO_MAX = 0x100 - IPPROTO_MAXID = 0x103 + IPPROTO_MAXID = 0x34 IPPROTO_MOBILE = 0x37 - IPPROTO_MPLS = 0x89 IPPROTO_NONE = 0x3b IPPROTO_PFSYNC = 0xf0 IPPROTO_PIM = 0x67 @@ -688,16 +860,13 @@ const ( IPPROTO_TCP = 0x6 IPPROTO_TP = 0x1d IPPROTO_UDP = 0x11 - IPV6_AUTH_LEVEL = 0x35 - IPV6_AUTOFLOWLABEL = 0x3b + IPPROTO_VRRP = 0x70 IPV6_CHECKSUM = 0x1a IPV6_DEFAULT_MULTICAST_HOPS = 0x1 IPV6_DEFAULT_MULTICAST_LOOP = 0x1 IPV6_DEFHLIM = 0x40 IPV6_DONTFRAG = 0x3e IPV6_DSTOPTS = 0x32 - IPV6_ESP_NETWORK_LEVEL = 0x37 - IPV6_ESP_TRANS_LEVEL = 0x36 IPV6_FAITH = 0x1d IPV6_FLOWINFO_MASK = 0xffffff0f IPV6_FLOWLABEL_MASK = 0xffff0f00 @@ -705,7 +874,7 @@ const ( IPV6_HLIMDEC = 0x1 IPV6_HOPLIMIT = 0x2f IPV6_HOPOPTS = 0x31 - IPV6_IPCOMP_LEVEL = 0x3c + IPV6_IPSEC_POLICY = 0x1c IPV6_JOIN_GROUP = 0xc IPV6_LEAVE_GROUP = 0xd IPV6_MAXHLIM = 0xff @@ -715,9 +884,7 @@ const ( IPV6_MULTICAST_IF = 0x9 IPV6_MULTICAST_LOOP = 0xb IPV6_NEXTHOP = 0x30 - IPV6_OPTIONS = 0x1 IPV6_PATHMTU = 0x2c - IPV6_PIPEX = 0x3f IPV6_PKTINFO = 0x2e IPV6_PORTRANGE = 0xe IPV6_PORTRANGE_DEFAULT = 0x0 @@ -730,7 +897,6 @@ const ( IPV6_RECVPKTINFO = 0x24 IPV6_RECVRTHDR = 0x26 IPV6_RECVTCLASS = 0x39 - IPV6_RTABLE = 0x1021 IPV6_RTHDR = 0x33 IPV6_RTHDRDSTOPTS = 0x23 IPV6_RTHDR_LOOSE = 0x0 @@ -744,74 +910,105 @@ const ( IPV6_VERSION = 0x60 IPV6_VERSION_MASK = 0xf0 IP_ADD_MEMBERSHIP = 0xc - IP_AUTH_LEVEL = 0x14 IP_DEFAULT_MULTICAST_LOOP = 0x1 IP_DEFAULT_MULTICAST_TTL = 0x1 IP_DF = 0x4000 IP_DROP_MEMBERSHIP = 0xd - IP_ESP_NETWORK_LEVEL = 0x16 - IP_ESP_TRANS_LEVEL = 0x15 + IP_EF = 0x8000 + IP_ERRORMTU = 0x15 IP_HDRINCL = 0x2 - IP_IPCOMP_LEVEL = 0x1d - IP_IPSEC_LOCAL_AUTH = 0x1b - IP_IPSEC_LOCAL_CRED = 0x19 - IP_IPSEC_LOCAL_ID = 0x17 - IP_IPSEC_REMOTE_AUTH = 0x1c - IP_IPSEC_REMOTE_CRED = 0x1a - IP_IPSEC_REMOTE_ID = 0x18 + IP_IPSEC_POLICY = 0x16 IP_MAXPACKET = 0xffff - IP_MAX_MEMBERSHIPS = 0xfff + IP_MAX_MEMBERSHIPS = 0x14 IP_MF = 0x2000 - IP_MINTTL = 0x20 - IP_MIN_MEMBERSHIPS = 0xf + IP_MINFRAGSIZE = 0x45 + IP_MINTTL = 0x18 IP_MSS = 0x240 IP_MULTICAST_IF = 0x9 IP_MULTICAST_LOOP = 0xb IP_MULTICAST_TTL = 0xa IP_OFFMASK = 0x1fff IP_OPTIONS = 0x1 - IP_PIPEX = 0x22 IP_PORTRANGE = 0x13 IP_PORTRANGE_DEFAULT = 0x0 IP_PORTRANGE_HIGH = 0x1 IP_PORTRANGE_LOW = 0x2 IP_RECVDSTADDR = 0x7 - IP_RECVDSTPORT = 0x21 - IP_RECVIF = 0x1e + IP_RECVIF = 0x14 IP_RECVOPTS = 0x5 IP_RECVRETOPTS = 0x6 - IP_RECVRTABLE = 0x23 - IP_RECVTTL = 0x1f + IP_RECVTTL = 0x17 IP_RETOPTS = 0x8 IP_RF = 0x8000 - IP_RTABLE = 0x1021 IP_TOS = 0x3 IP_TTL = 0x4 - MAP_ET_KVAGUARD = 0x10 + ISIG = 0x80 + ISTRIP = 0x20 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 + LOCK_EX = 0x2 + LOCK_NB = 0x4 + LOCK_SH = 0x1 + LOCK_UN = 0x8 MSG_BCAST = 0x100 + MSG_CMSG_CLOEXEC = 0x800 + MSG_CONTROLMBUF = 0x2000000 MSG_CTRUNC = 0x20 MSG_DONTROUTE = 0x4 MSG_DONTWAIT = 0x80 MSG_EOR = 0x8 + MSG_IOVUSRSPACE = 0x4000000 + MSG_LENUSRSPACE = 0x8000000 MSG_MCAST = 0x200 + MSG_NAMEMBUF = 0x1000000 + MSG_NBIO = 0x1000 + MSG_NOSIGNAL = 0x400 MSG_OOB = 0x1 MSG_PEEK = 0x2 MSG_TRUNC = 0x10 + MSG_USERFLAGS = 0xffffff MSG_WAITALL = 0x40 - NAME_MAX = 0xff + NAME_MAX = 0x1ff NET_RT_DUMP = 0x1 NET_RT_FLAGS = 0x2 - NET_RT_IFLIST = 0x3 + NET_RT_IFLIST = 0x5 NET_RT_MAXID = 0x6 - NET_RT_STATS = 0x4 - NET_RT_TABLE = 0x5 + NET_RT_OIFLIST = 0x4 + NET_RT_OOIFLIST = 0x3 + NOFLSH = 0x80000000 + NOTE_ATTRIB = 0x8 + NOTE_CHILD = 0x4 + NOTE_DELETE = 0x1 + NOTE_EXEC = 0x20000000 + NOTE_EXIT = 0x80000000 + NOTE_EXTEND = 0x4 + NOTE_FORK = 0x40000000 + NOTE_LINK = 0x10 + NOTE_LOWAT = 0x1 + NOTE_PCTRLMASK = 0xf0000000 + NOTE_PDATAMASK = 0xfffff + NOTE_RENAME = 0x20 + NOTE_REVOKE = 0x40 + NOTE_TRACK = 0x1 + NOTE_TRACKERR = 0x2 + NOTE_WRITE = 0x2 + OCRNL = 0x10 + OFIOGETBMAP = 0xc004667a + ONLCR = 0x2 + ONLRET = 0x40 + ONOCR = 0x20 + ONOEOT = 0x8 + OPOST = 0x1 O_ACCMODE = 0x3 + O_ALT_IO = 0x40000 O_APPEND = 0x8 O_ASYNC = 0x40 - O_CLOEXEC = 0x10000 + O_CLOEXEC = 0x400000 O_CREAT = 0x200 - O_DIRECTORY = 0x20000 - O_DSYNC = 0x80 + O_DIRECT = 0x80000 + O_DIRECTORY = 0x200000 + O_DSYNC = 0x10000 O_EXCL = 0x800 O_EXLOCK = 0x20 O_FSYNC = 0x80 @@ -819,13 +1016,27 @@ const ( O_NOCTTY = 0x8000 O_NOFOLLOW = 0x100 O_NONBLOCK = 0x4 + O_NOSIGPIPE = 0x1000000 O_RDONLY = 0x0 O_RDWR = 0x2 - O_RSYNC = 0x80 + O_RSYNC = 0x20000 O_SHLOCK = 0x10 O_SYNC = 0x80 O_TRUNC = 0x400 O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 + PRI_IOFLUSH = 0x7c + RLIMIT_AS = 0xa + RLIMIT_CORE = 0x4 + RLIMIT_CPU = 0x0 + RLIMIT_DATA = 0x2 + RLIMIT_FSIZE = 0x1 + RLIMIT_NOFILE = 0x8 + RLIMIT_STACK = 0x3 + RLIM_INFINITY = 0x7fffffffffffffff RTAX_AUTHOR = 0x6 RTAX_BRD = 0x7 RTAX_DST = 0x0 @@ -833,11 +1044,9 @@ const ( RTAX_GENMASK = 0x3 RTAX_IFA = 0x5 RTAX_IFP = 0x4 - RTAX_LABEL = 0xa - RTAX_MAX = 0xb + RTAX_MAX = 0x9 RTAX_NETMASK = 0x2 - RTAX_SRC = 0x8 - RTAX_SRCMASK = 0x9 + RTAX_TAG = 0x8 RTA_AUTHOR = 0x40 RTA_BRD = 0x80 RTA_DST = 0x1 @@ -845,52 +1054,48 @@ const ( RTA_GENMASK = 0x8 RTA_IFA = 0x20 RTA_IFP = 0x10 - RTA_LABEL = 0x400 RTA_NETMASK = 0x4 - RTA_SRC = 0x100 - RTA_SRCMASK = 0x200 - RTF_ANNOUNCE = 0x4000 + RTA_TAG = 0x100 + RTF_ANNOUNCE = 0x20000 RTF_BLACKHOLE = 0x1000 - RTF_CLONED = 0x10000 + RTF_CLONED = 0x2000 RTF_CLONING = 0x100 RTF_DONE = 0x40 RTF_DYNAMIC = 0x10 - RTF_FMASK = 0x8f808 RTF_GATEWAY = 0x2 RTF_HOST = 0x4 - RTF_JUMBO = 0x80000 RTF_LLINFO = 0x400 RTF_MASK = 0x80 RTF_MODIFIED = 0x20 - RTF_MPATH = 0x40000 - RTF_MPLS = 0x100000 - RTF_PERMANENT_ARP = 0x2000 RTF_PROTO1 = 0x8000 RTF_PROTO2 = 0x4000 - RTF_PROTO3 = 0x2000 RTF_REJECT = 0x8 - RTF_SOURCE = 0x20000 + RTF_SRC = 0x10000 RTF_STATIC = 0x800 - RTF_TUNNEL = 0x100000 RTF_UP = 0x1 - RTF_USETRAILERS = 0x8000 RTF_XRESOLVE = 0x200 RTM_ADD = 0x1 RTM_CHANGE = 0x3 + RTM_CHGADDR = 0x15 RTM_DELADDR = 0xd RTM_DELETE = 0x2 - RTM_DESYNC = 0x10 RTM_GET = 0x4 - RTM_IFANNOUNCE = 0xf - RTM_IFINFO = 0xe + RTM_IEEE80211 = 0x11 + RTM_IFANNOUNCE = 0x10 + RTM_IFINFO = 0x14 + RTM_LLINFO_UPD = 0x13 RTM_LOCK = 0x8 RTM_LOSING = 0x5 - RTM_MAXSIZE = 0x800 RTM_MISS = 0x7 RTM_NEWADDR = 0xc + RTM_OIFINFO = 0xf + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_OOIFINFO = 0xe RTM_REDIRECT = 0x6 RTM_RESOLVE = 0xb RTM_RTTUNIT = 0xf4240 + RTM_SETGATE = 0x12 RTM_VERSION = 0x4 RTV_EXPIRE = 0x4 RTV_HOPCOUNT = 0x2 @@ -900,159 +1105,82 @@ const ( RTV_RTTVAR = 0x80 RTV_SPIPE = 0x10 RTV_SSTHRESH = 0x20 - RT_TABLEID_MAX = 0xff - SCM_CREDS = 0x2 + RUSAGE_CHILDREN = -0x1 + RUSAGE_SELF = 0x0 + SCM_CREDS = 0x4 SCM_RIGHTS = 0x1 - SCM_TIMESTAMP = 0x4 + SCM_TIMESTAMP = 0x8 SHUT_RD = 0x0 SHUT_RDWR = 0x2 SHUT_WR = 0x1 - SIGABRT = 0x6 - SIGALRM = 0xe - SIGBUS = 0xa - SIGCHLD = 0x14 - SIGCONT = 0x13 - SIGEMT = 0x7 - SIGFPE = 0x8 - SIGHUP = 0x1 - SIGILL = 0x4 - SIGINFO = 0x1d - SIGINT = 0x2 - SIGIO = 0x17 - SIGIOT = 0x6 - SIGKILL = 0x9 - SIGPIPE = 0xd - SIGPROF = 0x1b - SIGQUIT = 0x3 - SIGSEGV = 0xb - SIGSTOP = 0x11 - SIGSYS = 0xc - SIGTERM = 0xf - SIGTHR = 0x20 - SIGTRAP = 0x5 - SIGTSTP = 0x12 - SIGTTIN = 0x15 - SIGTTOU = 0x16 - SIGURG = 0x10 - SIGUSR1 = 0x1e - SIGUSR2 = 0x1f - SIGVTALRM = 0x1a - SIGWINCH = 0x1c - SIGXCPU = 0x18 - SIGXFSZ = 0x19 - SIOCADDMULTI = 0x80206931 + SIOCADDMULTI = 0x80906931 + SIOCADDRT = 0x8038720a SIOCAIFADDR = 0x8040691a - SIOCAIFGROUP = 0x80286987 - SIOCALIFADDR = 0x8218691c + SIOCALIFADDR = 0x8118691c SIOCATMARK = 0x40047307 - SIOCBRDGADD = 0x8058693c - SIOCBRDGADDS = 0x80586941 - SIOCBRDGARL = 0x806e694d - SIOCBRDGDADDR = 0x80286947 - SIOCBRDGDEL = 0x8058693d - SIOCBRDGDELS = 0x80586942 - SIOCBRDGFLUSH = 0x80586948 - SIOCBRDGFRL = 0x806e694e - SIOCBRDGGCACHE = 0xc0146941 - SIOCBRDGGFD = 0xc0146952 - SIOCBRDGGHT = 0xc0146951 - SIOCBRDGGIFFLGS = 0xc058693e - SIOCBRDGGMA = 0xc0146953 - SIOCBRDGGPARAM = 0xc0406958 - SIOCBRDGGPRI = 0xc0146950 - SIOCBRDGGRL = 0xc030694f - SIOCBRDGGSIFS = 0xc058693c - SIOCBRDGGTO = 0xc0146946 - SIOCBRDGIFS = 0xc0586942 - SIOCBRDGRTS = 0xc0206943 - SIOCBRDGSADDR = 0xc0286944 - SIOCBRDGSCACHE = 0x80146940 - SIOCBRDGSFD = 0x80146952 - SIOCBRDGSHT = 0x80146951 - SIOCBRDGSIFCOST = 0x80586955 - SIOCBRDGSIFFLGS = 0x8058693f - SIOCBRDGSIFPRIO = 0x80586954 - SIOCBRDGSMA = 0x80146953 - SIOCBRDGSPRI = 0x80146950 - SIOCBRDGSPROTO = 0x8014695a - SIOCBRDGSTO = 0x80146945 - SIOCBRDGSTXHC = 0x80146959 - SIOCDELMULTI = 0x80206932 - SIOCDIFADDR = 0x80206919 - SIOCDIFGROUP = 0x80286989 - SIOCDIFPHYADDR = 0x80206949 - SIOCDLIFADDR = 0x8218691e - SIOCGETKALIVE = 0xc01869a4 - SIOCGETLABEL = 0x8020699a - SIOCGETPFLOW = 0xc02069fe - SIOCGETPFSYNC = 0xc02069f8 + SIOCDELMULTI = 0x80906932 + SIOCDELRT = 0x8038720b + SIOCDIFADDR = 0x80906919 + SIOCDIFPHYADDR = 0x80906949 + SIOCDLIFADDR = 0x8118691e + SIOCGDRVSPEC = 0xc028697b + SIOCGETPFSYNC = 0xc09069f8 SIOCGETSGCNT = 0xc0207534 SIOCGETVIFCNT = 0xc0287533 - SIOCGETVLANPRIO = 0xc0206990 SIOCGHIWAT = 0x40047301 - SIOCGIFADDR = 0xc0206921 - SIOCGIFASYNCMAP = 0xc020697c - SIOCGIFBRDADDR = 0xc0206923 - SIOCGIFCONF = 0xc0106924 - SIOCGIFDATA = 0xc020691b - SIOCGIFDESCR = 0xc0206981 - SIOCGIFDSTADDR = 0xc0206922 - SIOCGIFFLAGS = 0xc0206911 - SIOCGIFGATTR = 0xc028698b - SIOCGIFGENERIC = 0xc020693a - SIOCGIFGMEMB = 0xc028698a - SIOCGIFGROUP = 0xc0286988 + SIOCGIFADDR = 0xc0906921 + SIOCGIFADDRPREF = 0xc0986920 + SIOCGIFALIAS = 0xc040691b + SIOCGIFBRDADDR = 0xc0906923 + SIOCGIFCAP = 0xc0206976 + SIOCGIFCONF = 0xc0106926 + SIOCGIFDATA = 0xc0986985 + SIOCGIFDLT = 0xc0906977 + SIOCGIFDSTADDR = 0xc0906922 + SIOCGIFFLAGS = 0xc0906911 + SIOCGIFGENERIC = 0xc090693a SIOCGIFMEDIA = 0xc0306936 - SIOCGIFMETRIC = 0xc0206917 - SIOCGIFMTU = 0xc020697e - SIOCGIFNETMASK = 0xc0206925 - SIOCGIFPDSTADDR = 0xc0206948 - SIOCGIFPRIORITY = 0xc020699c - SIOCGIFPSRCADDR = 0xc0206947 - SIOCGIFRDOMAIN = 0xc02069a0 - SIOCGIFRTLABEL = 0xc0206983 - SIOCGIFTIMESLOT = 0xc0206986 - SIOCGIFXFLAGS = 0xc020699e - SIOCGLIFADDR = 0xc218691d - SIOCGLIFPHYADDR = 0xc218694b - SIOCGLIFPHYRTABLE = 0xc02069a2 + SIOCGIFMETRIC = 0xc0906917 + SIOCGIFMTU = 0xc090697e + SIOCGIFNETMASK = 0xc0906925 + SIOCGIFPDSTADDR = 0xc0906948 + SIOCGIFPSRCADDR = 0xc0906947 + SIOCGLIFADDR = 0xc118691d + SIOCGLIFPHYADDR = 0xc118694b + SIOCGLINKSTR = 0xc0286987 SIOCGLOWAT = 0x40047303 SIOCGPGRP = 0x40047309 - SIOCGVH = 0xc02069f6 - SIOCIFCREATE = 0x8020697a - SIOCIFDESTROY = 0x80206979 + SIOCGVH = 0xc0906983 + SIOCIFCREATE = 0x8090697a + SIOCIFDESTROY = 0x80906979 SIOCIFGCLONERS = 0xc0106978 - SIOCSETKALIVE = 0x801869a3 - SIOCSETLABEL = 0x80206999 - SIOCSETPFLOW = 0x802069fd - SIOCSETPFSYNC = 0x802069f7 - SIOCSETVLANPRIO = 0x8020698f + SIOCINITIFADDR = 0xc0706984 + SIOCSDRVSPEC = 0x8028697b + SIOCSETPFSYNC = 0x809069f7 SIOCSHIWAT = 0x80047300 - SIOCSIFADDR = 0x8020690c - SIOCSIFASYNCMAP = 0x8020697d - SIOCSIFBRDADDR = 0x80206913 - SIOCSIFDESCR = 0x80206980 - SIOCSIFDSTADDR = 0x8020690e - SIOCSIFFLAGS = 0x80206910 - SIOCSIFGATTR = 0x8028698c - SIOCSIFGENERIC = 0x80206939 - SIOCSIFLLADDR = 0x8020691f - SIOCSIFMEDIA = 0xc0206935 - SIOCSIFMETRIC = 0x80206918 - SIOCSIFMTU = 0x8020697f - SIOCSIFNETMASK = 0x80206916 + SIOCSIFADDR = 0x8090690c + SIOCSIFADDRPREF = 0x8098691f + SIOCSIFBRDADDR = 0x80906913 + SIOCSIFCAP = 0x80206975 + SIOCSIFDSTADDR = 0x8090690e + SIOCSIFFLAGS = 0x80906910 + SIOCSIFGENERIC = 0x80906939 + SIOCSIFMEDIA = 0xc0906935 + SIOCSIFMETRIC = 0x80906918 + SIOCSIFMTU = 0x8090697f + SIOCSIFNETMASK = 0x80906916 SIOCSIFPHYADDR = 0x80406946 - SIOCSIFPRIORITY = 0x8020699b - SIOCSIFRDOMAIN = 0x8020699f - SIOCSIFRTLABEL = 0x80206982 - SIOCSIFTIMESLOT = 0x80206985 - SIOCSIFXFLAGS = 0x8020699d - SIOCSLIFPHYADDR = 0x8218694a - SIOCSLIFPHYRTABLE = 0x802069a1 + SIOCSLIFPHYADDR = 0x8118694a + SIOCSLINKSTR = 0x80286988 SIOCSLOWAT = 0x80047302 SIOCSPGRP = 0x80047308 - SIOCSVH = 0xc02069f5 + SIOCSVH = 0xc0906982 + SIOCZIFDATA = 0xc0986986 + SOCK_CLOEXEC = 0x10000000 SOCK_DGRAM = 0x2 + SOCK_FLAGS_MASK = 0xf0000000 + SOCK_NONBLOCK = 0x20000000 + SOCK_NOSIGPIPE = 0x40000000 SOCK_RAW = 0x3 SOCK_RDM = 0x4 SOCK_SEQPACKET = 0x5 @@ -1060,63 +1188,107 @@ const ( SOL_SOCKET = 0xffff SOMAXCONN = 0x80 SO_ACCEPTCONN = 0x2 - SO_BINDANY = 0x1000 + SO_ACCEPTFILTER = 0x1000 SO_BROADCAST = 0x20 SO_DEBUG = 0x1 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 - SO_JUMBO = 0x400 SO_KEEPALIVE = 0x8 SO_LINGER = 0x80 - SO_NETPROC = 0x1020 + SO_NOHEADER = 0x100a + SO_NOSIGPIPE = 0x800 SO_OOBINLINE = 0x100 - SO_PEERCRED = 0x1022 + SO_OVERFLOWED = 0x1009 SO_RCVBUF = 0x1002 SO_RCVLOWAT = 0x1004 - SO_RCVTIMEO = 0x1006 + SO_RCVTIMEO = 0x100c SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 - SO_RTABLE = 0x1021 SO_SNDBUF = 0x1001 SO_SNDLOWAT = 0x1003 - SO_SNDTIMEO = 0x1005 - SO_SPLICE = 0x1023 - SO_TIMESTAMP = 0x800 + SO_SNDTIMEO = 0x100b + SO_TIMESTAMP = 0x2000 SO_TYPE = 0x1008 SO_USELOOPBACK = 0x40 + SYSCTL_VERSION = 0x1000000 + SYSCTL_VERS_0 = 0x0 + SYSCTL_VERS_1 = 0x1000000 + SYSCTL_VERS_MASK = 0xff000000 + S_ARCH1 = 0x10000 + S_ARCH2 = 0x20000 + S_BLKSIZE = 0x200 + S_IEXEC = 0x40 + S_IFBLK = 0x6000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFIFO = 0x1000 + S_IFLNK = 0xa000 + S_IFMT = 0xf000 + S_IFREG = 0x8000 + S_IFSOCK = 0xc000 + S_IFWHT = 0xe000 + S_IREAD = 0x100 + S_IRGRP = 0x20 + S_IROTH = 0x4 + S_IRUSR = 0x100 + S_IRWXG = 0x38 + S_IRWXO = 0x7 + S_IRWXU = 0x1c0 + S_ISGID = 0x400 + S_ISTXT = 0x200 + S_ISUID = 0x800 + S_ISVTX = 0x200 + S_IWGRP = 0x10 + S_IWOTH = 0x2 + S_IWRITE = 0x80 + S_IWUSR = 0x80 + S_IXGRP = 0x8 + S_IXOTH = 0x1 + S_IXUSR = 0x40 + S_LOGIN_SET = 0x1 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 + TCP_CONGCTL = 0x20 + TCP_KEEPCNT = 0x6 + TCP_KEEPIDLE = 0x3 + TCP_KEEPINIT = 0x7 + TCP_KEEPINTVL = 0x5 TCP_MAXBURST = 0x4 TCP_MAXSEG = 0x2 TCP_MAXWIN = 0xffff - TCP_MAX_SACK = 0x3 TCP_MAX_WINSHIFT = 0xe - TCP_MD5SIG = 0x4 - TCP_MSS = 0x200 + TCP_MD5SIG = 0x10 + TCP_MINMSS = 0xd8 + TCP_MSS = 0x218 TCP_NODELAY = 0x1 - TCP_NSTATES = 0xb - TCP_SACK_ENABLE = 0x8 + TCSAFLUSH = 0x2 TIOCCBRK = 0x2000747a TIOCCDTR = 0x20007478 TIOCCONS = 0x80047462 + TIOCDCDTIMESTAMP = 0x40107458 TIOCDRAIN = 0x2000745e TIOCEXCL = 0x2000740d TIOCEXT = 0x80047460 + TIOCFLAG_CDTRCTS = 0x10 TIOCFLAG_CLOCAL = 0x2 TIOCFLAG_CRTSCTS = 0x4 TIOCFLAG_MDMBUF = 0x8 - TIOCFLAG_PPS = 0x10 TIOCFLAG_SOFTCAR = 0x1 TIOCFLUSH = 0x80047410 TIOCGETA = 0x402c7413 TIOCGETD = 0x4004741a TIOCGFLAGS = 0x4004745d + TIOCGLINED = 0x40207442 TIOCGPGRP = 0x40047477 - TIOCGTSTAMP = 0x4010745b + TIOCGQSIZE = 0x40047481 + TIOCGRANTPT = 0x20007447 + TIOCGSID = 0x40047463 + TIOCGSIZE = 0x40087468 TIOCGWINSZ = 0x40087468 TIOCMBIC = 0x8004746b TIOCMBIS = 0x8004746c TIOCMGET = 0x4004746a - TIOCMODG = 0x4004746a - TIOCMODS = 0x8004746d TIOCMSET = 0x8004746d TIOCM_CAR = 0x40 TIOCM_CD = 0x40 @@ -1141,6 +1313,9 @@ const ( TIOCPKT_NOSTOP = 0x10 TIOCPKT_START = 0x8 TIOCPKT_STOP = 0x4 + TIOCPTMGET = 0x40287446 + TIOCPTSNAME = 0x40287448 + TIOCRCVFRAME = 0x80087445 TIOCREMOTE = 0x80047469 TIOCSBRK = 0x2000747b TIOCSCTTY = 0x20007461 @@ -1150,19 +1325,46 @@ const ( TIOCSETAW = 0x802c7415 TIOCSETD = 0x8004741b TIOCSFLAGS = 0x8004745c - TIOCSIG = 0x8004745f + TIOCSIG = 0x2000745f + TIOCSLINED = 0x80207443 TIOCSPGRP = 0x80047476 + TIOCSQSIZE = 0x80047480 + TIOCSSIZE = 0x80087467 TIOCSTART = 0x2000746e TIOCSTAT = 0x80047465 TIOCSTI = 0x80017472 TIOCSTOP = 0x2000746f - TIOCSTSTAMP = 0x8008745a TIOCSWINSZ = 0x80087467 TIOCUCNTL = 0x80047466 + TIOCXMTFRAME = 0x80087444 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VTIME = 0x11 + VWERASE = 0x4 + WALL = 0x8 + WALLSIG = 0x8 WALTSIG = 0x4 - WCONTINUED = 0x8 + WCLONE = 0x4 WCOREFLAG = 0x80 WNOHANG = 0x1 + WNOWAIT = 0x10000 + WNOZOMBIE = 0x20000 + WOPTSCHECKED = 0x40000 WSTOPPED = 0x7f WUNTRACED = 0x2 ) @@ -1178,17 +1380,11 @@ const ( EALREADY = Errno(0x25) EAUTH = Errno(0x50) EBADF = Errno(0x9) + EBADMSG = Errno(0x58) EBADRPC = Errno(0x48) EBUSY = Errno(0x10) - ECANCELED = Errno(0x58) + ECANCELED = Errno(0x57) ECHILD = Errno(0xa) - ECHO = Errno(0x8) - ECHOCTL = Errno(0x40) - ECHOE = Errno(0x2) - ECHOK = Errno(0x4) - ECHOKE = Errno(0x1) - ECHONL = Errno(0x10) - ECHOPRT = Errno(0x20) ECONNABORTED = Errno(0x35) ECONNREFUSED = Errno(0x3d) ECONNRESET = Errno(0x36) @@ -1202,50 +1398,51 @@ const ( EFTYPE = Errno(0x4f) EHOSTDOWN = Errno(0x40) EHOSTUNREACH = Errno(0x41) - EIDRM = Errno(0x59) - EILSEQ = Errno(0x54) + EIDRM = Errno(0x52) + EILSEQ = Errno(0x55) EINPROGRESS = Errno(0x24) EINTR = Errno(0x4) EINVAL = Errno(0x16) EIO = Errno(0x5) - EIPSEC = Errno(0x52) EISCONN = Errno(0x38) EISDIR = Errno(0x15) - ELAST = Errno(0x5b) + ELAST = Errno(0x60) ELOOP = Errno(0x3e) - EMEDIUMTYPE = Errno(0x56) EMFILE = Errno(0x18) EMLINK = Errno(0x1f) EMSGSIZE = Errno(0x28) + EMULTIHOP = Errno(0x5e) ENAMETOOLONG = Errno(0x3f) - ENDRUNDISC = Errno(0x9) ENEEDAUTH = Errno(0x51) ENETDOWN = Errno(0x32) ENETRESET = Errno(0x34) ENETUNREACH = Errno(0x33) ENFILE = Errno(0x17) - ENOATTR = Errno(0x53) + ENOATTR = Errno(0x5d) ENOBUFS = Errno(0x37) + ENODATA = Errno(0x59) ENODEV = Errno(0x13) ENOENT = Errno(0x2) ENOEXEC = Errno(0x8) ENOLCK = Errno(0x4d) - ENOMEDIUM = Errno(0x55) + ENOLINK = Errno(0x5f) ENOMEM = Errno(0xc) - ENOMSG = Errno(0x5a) + ENOMSG = Errno(0x53) ENOPROTOOPT = Errno(0x2a) ENOSPC = Errno(0x1c) + ENOSR = Errno(0x5a) + ENOSTR = Errno(0x5b) ENOSYS = Errno(0x4e) ENOTBLK = Errno(0xf) ENOTCONN = Errno(0x39) ENOTDIR = Errno(0x14) ENOTEMPTY = Errno(0x42) ENOTSOCK = Errno(0x26) - ENOTSUP = Errno(0x5b) + ENOTSUP = Errno(0x56) ENOTTY = Errno(0x19) ENXIO = Errno(0x6) EOPNOTSUPP = Errno(0x2d) - EOVERFLOW = Errno(0x57) + EOVERFLOW = Errno(0x54) EPERM = Errno(0x1) EPFNOSUPPORT = Errno(0x2e) EPIPE = Errno(0x20) @@ -1253,6 +1450,7 @@ const ( EPROCUNAVAIL = Errno(0x4c) EPROGMISMATCH = Errno(0x4b) EPROGUNAVAIL = Errno(0x4a) + EPROTO = Errno(0x60) EPROTONOSUPPORT = Errno(0x2b) EPROTOTYPE = Errno(0x29) ERANGE = Errno(0x22) @@ -1264,6 +1462,7 @@ const ( ESPIPE = Errno(0x1d) ESRCH = Errno(0x3) ESTALE = Errno(0x46) + ETIME = Errno(0x5c) ETIMEDOUT = Errno(0x3c) ETOOMANYREFS = Errno(0x3b) ETXTBSY = Errno(0x1a) @@ -1272,6 +1471,43 @@ const ( EXDEV = Errno(0x12) ) +// Signals +const ( + SIGABRT = Signal(0x6) + SIGALRM = Signal(0xe) + SIGBUS = Signal(0xa) + SIGCHLD = Signal(0x14) + SIGCONT = Signal(0x13) + SIGEMT = Signal(0x7) + SIGFPE = Signal(0x8) + SIGHUP = Signal(0x1) + SIGILL = Signal(0x4) + SIGINFO = Signal(0x1d) + SIGINT = Signal(0x2) + SIGIO = Signal(0x17) + SIGIOT = Signal(0x6) + SIGKILL = Signal(0x9) + SIGPIPE = Signal(0xd) + SIGPROF = Signal(0x1b) + SIGPWR = Signal(0x20) + SIGQUIT = Signal(0x3) + SIGSEGV = Signal(0xb) + SIGSTOP = Signal(0x11) + SIGSYS = Signal(0xc) + SIGTERM = Signal(0xf) + SIGTRAP = Signal(0x5) + SIGTSTP = Signal(0x12) + SIGTTIN = Signal(0x15) + SIGTTOU = Signal(0x16) + SIGURG = Signal(0x10) + SIGUSR1 = Signal(0x1e) + SIGUSR2 = Signal(0x1f) + SIGVTALRM = Signal(0x1a) + SIGWINCH = Signal(0x1c) + SIGXCPU = Signal(0x18) + SIGXFSZ = Signal(0x19) +) + // Error table var errors = [...]string{ 1: "operation not permitted", @@ -1307,7 +1543,7 @@ var errors = [...]string{ 31: "too many links", 32: "broken pipe", 33: "numerical argument out of domain", - 34: "result too large", + 34: "result too large or too small", 35: "resource temporarily unavailable", 36: "operation now in progress", 37: "operation already in progress", @@ -1315,7 +1551,7 @@ var errors = [...]string{ 39: "destination address required", 40: "message too long", 41: "protocol wrong type for socket", - 42: "protocol not available", + 42: "protocol option not available", 43: "protocol not supported", 44: "socket type not supported", 45: "operation not supported", @@ -1355,14 +1591,55 @@ var errors = [...]string{ 79: "inappropriate file type or format", 80: "authentication error", 81: "need authenticator", - 82: "IPsec processing failure", - 83: "attribute not found", - 84: "illegal byte sequence", - 85: "no medium found", - 86: "wrong medium type", - 87: "value too large to be stored in data type", - 88: "operation canceled", - 89: "identifier removed", - 90: "no message of desired type", - 91: "not supported", + 82: "identifier removed", + 83: "no message of desired type", + 84: "value too large to be stored in data type", + 85: "illegal byte sequence", + 86: "not supported", + 87: "operation Canceled", + 88: "bad or Corrupt message", + 89: "no message available", + 90: "no STREAM resources", + 91: "not a STREAM", + 92: "STREAM ioctl timeout", + 93: "attribute not found", + 94: "multihop attempted", + 95: "link has been severed", + 96: "protocol error", +} + +// Signal table +var signals = [...]string{ + 1: "hangup", + 2: "interrupt", + 3: "quit", + 4: "illegal instruction", + 5: "trace/BPT trap", + 6: "abort trap", + 7: "EMT trap", + 8: "floating point exception", + 9: "killed", + 10: "bus error", + 11: "segmentation fault", + 12: "bad system call", + 13: "broken pipe", + 14: "alarm clock", + 15: "terminated", + 16: "urgent I/O condition", + 17: "stopped (signal)", + 18: "stopped", + 19: "continued", + 20: "child exited", + 21: "stopped (tty input)", + 22: "stopped (tty output)", + 23: "I/O possible", + 24: "cputime limit exceeded", + 25: "filesize limit exceeded", + 26: "virtual timer expired", + 27: "profiling timer expired", + 28: "window size changes", + 29: "information request", + 30: "user defined signal 1", + 31: "user defined signal 2", + 32: "power fail/restart", } diff --git a/src/pkg/syscall/zerrors_netbsd_arm.go b/src/pkg/syscall/zerrors_netbsd_arm.go new file mode 100644 index 000000000..3899f6ba6 --- /dev/null +++ b/src/pkg/syscall/zerrors_netbsd_arm.go @@ -0,0 +1,1644 @@ +// mkerrors.sh -marm +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs -- -marm _const.go + +package syscall + +const ( + AF_APPLETALK = 0x10 + AF_ARP = 0x1c + AF_BLUETOOTH = 0x1f + AF_CCITT = 0xa + AF_CHAOS = 0x5 + AF_CNT = 0x15 + AF_COIP = 0x14 + AF_DATAKIT = 0x9 + AF_DECnet = 0xc + AF_DLI = 0xd + AF_E164 = 0x1a + AF_ECMA = 0x8 + AF_HYLINK = 0xf + AF_IEEE80211 = 0x20 + AF_IMPLINK = 0x3 + AF_INET = 0x2 + AF_INET6 = 0x18 + AF_IPX = 0x17 + AF_ISDN = 0x1a + AF_ISO = 0x7 + AF_LAT = 0xe + AF_LINK = 0x12 + AF_LOCAL = 0x1 + AF_MAX = 0x23 + AF_MPLS = 0x21 + AF_NATM = 0x1b + AF_NS = 0x6 + AF_OROUTE = 0x11 + AF_OSI = 0x7 + AF_PUP = 0x4 + AF_ROUTE = 0x22 + AF_SNA = 0xb + AF_UNIX = 0x1 + AF_UNSPEC = 0x0 + ARPHRD_ARCNET = 0x7 + ARPHRD_ETHER = 0x1 + ARPHRD_FRELAY = 0xf + ARPHRD_IEEE1394 = 0x18 + ARPHRD_IEEE802 = 0x6 + ARPHRD_STRIP = 0x17 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B460800 = 0x70800 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B921600 = 0xe1000 + B9600 = 0x2580 + BIOCFEEDBACK = 0x8004427d + BIOCFLUSH = 0x20004268 + BIOCGBLEN = 0x40044266 + BIOCGDLT = 0x4004426a + BIOCGDLTLIST = 0xc0084277 + BIOCGETIF = 0x4090426b + BIOCGFEEDBACK = 0x4004427c + BIOCGHDRCMPLT = 0x40044274 + BIOCGRTIMEOUT = 0x400c427b + BIOCGSEESENT = 0x40044278 + BIOCGSTATS = 0x4080426f + BIOCGSTATSOLD = 0x4008426f + BIOCIMMEDIATE = 0x80044270 + BIOCPROMISC = 0x20004269 + BIOCSBLEN = 0xc0044266 + BIOCSDLT = 0x80044276 + BIOCSETF = 0x80084267 + BIOCSETIF = 0x8090426c + BIOCSFEEDBACK = 0x8004427d + BIOCSHDRCMPLT = 0x80044275 + BIOCSRTIMEOUT = 0x800c427a + BIOCSSEESENT = 0x80044279 + BIOCSTCPF = 0x80084272 + BIOCSUDPF = 0x80084273 + BIOCVERSION = 0x40044271 + BPF_A = 0x10 + BPF_ABS = 0x20 + BPF_ADD = 0x0 + BPF_ALIGNMENT = 0x4 + BPF_ALIGNMENT32 = 0x4 + BPF_ALU = 0x4 + BPF_AND = 0x50 + BPF_B = 0x10 + BPF_DFLTBUFSIZE = 0x100000 + BPF_DIV = 0x30 + BPF_H = 0x8 + BPF_IMM = 0x0 + BPF_IND = 0x40 + BPF_JA = 0x0 + BPF_JEQ = 0x10 + BPF_JGE = 0x30 + BPF_JGT = 0x20 + BPF_JMP = 0x5 + BPF_JSET = 0x40 + BPF_K = 0x0 + BPF_LD = 0x0 + BPF_LDX = 0x1 + BPF_LEN = 0x80 + BPF_LSH = 0x60 + BPF_MAJOR_VERSION = 0x1 + BPF_MAXBUFSIZE = 0x1000000 + BPF_MAXINSNS = 0x200 + BPF_MEM = 0x60 + BPF_MEMWORDS = 0x10 + BPF_MINBUFSIZE = 0x20 + BPF_MINOR_VERSION = 0x1 + BPF_MISC = 0x7 + BPF_MSH = 0xa0 + BPF_MUL = 0x20 + BPF_NEG = 0x80 + BPF_OR = 0x40 + BPF_RELEASE = 0x30bb6 + BPF_RET = 0x6 + BPF_RSH = 0x70 + BPF_ST = 0x2 + BPF_STX = 0x3 + BPF_SUB = 0x10 + BPF_TAX = 0x0 + BPF_TXA = 0x80 + BPF_W = 0x0 + BPF_X = 0x8 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x8000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0x14 + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a + CTL_MAXNAME = 0xc + CTL_NET = 0x4 + CTL_QUERY = -0x2 + DIOCBSFLUSH = 0x20006478 + DLT_A429 = 0xb8 + DLT_A653_ICM = 0xb9 + DLT_AIRONET_HEADER = 0x78 + DLT_AOS = 0xde + DLT_APPLE_IP_OVER_IEEE1394 = 0x8a + DLT_ARCNET = 0x7 + DLT_ARCNET_LINUX = 0x81 + DLT_ATM_CLIP = 0x13 + DLT_ATM_RFC1483 = 0xb + DLT_AURORA = 0x7e + DLT_AX25 = 0x3 + DLT_AX25_KISS = 0xca + DLT_BACNET_MS_TP = 0xa5 + DLT_BLUETOOTH_HCI_H4 = 0xbb + DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9 + DLT_CAN20B = 0xbe + DLT_CAN_SOCKETCAN = 0xe3 + DLT_CHAOS = 0x5 + DLT_CISCO_IOS = 0x76 + DLT_C_HDLC = 0x68 + DLT_C_HDLC_WITH_DIR = 0xcd + DLT_DECT = 0xdd + DLT_DOCSIS = 0x8f + DLT_ECONET = 0x73 + DLT_EN10MB = 0x1 + DLT_EN3MB = 0x2 + DLT_ENC = 0x6d + DLT_ERF = 0xc5 + DLT_ERF_ETH = 0xaf + DLT_ERF_POS = 0xb0 + DLT_FC_2 = 0xe0 + DLT_FC_2_WITH_FRAME_DELIMS = 0xe1 + DLT_FDDI = 0xa + DLT_FLEXRAY = 0xd2 + DLT_FRELAY = 0x6b + DLT_FRELAY_WITH_DIR = 0xce + DLT_GCOM_SERIAL = 0xad + DLT_GCOM_T1E1 = 0xac + DLT_GPF_F = 0xab + DLT_GPF_T = 0xaa + DLT_GPRS_LLC = 0xa9 + DLT_GSMTAP_ABIS = 0xda + DLT_GSMTAP_UM = 0xd9 + DLT_HDLC = 0x10 + DLT_HHDLC = 0x79 + DLT_HIPPI = 0xf + DLT_IBM_SN = 0x92 + DLT_IBM_SP = 0x91 + DLT_IEEE802 = 0x6 + DLT_IEEE802_11 = 0x69 + DLT_IEEE802_11_RADIO = 0x7f + DLT_IEEE802_11_RADIO_AVS = 0xa3 + DLT_IEEE802_15_4 = 0xc3 + DLT_IEEE802_15_4_LINUX = 0xbf + DLT_IEEE802_15_4_NONASK_PHY = 0xd7 + DLT_IEEE802_16_MAC_CPS = 0xbc + DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 + DLT_IPMB = 0xc7 + DLT_IPMB_LINUX = 0xd1 + DLT_IPNET = 0xe2 + DLT_IPV4 = 0xe4 + DLT_IPV6 = 0xe5 + DLT_IP_OVER_FC = 0x7a + DLT_JUNIPER_ATM1 = 0x89 + DLT_JUNIPER_ATM2 = 0x87 + DLT_JUNIPER_CHDLC = 0xb5 + DLT_JUNIPER_ES = 0x84 + DLT_JUNIPER_ETHER = 0xb2 + DLT_JUNIPER_FRELAY = 0xb4 + DLT_JUNIPER_GGSN = 0x85 + DLT_JUNIPER_ISM = 0xc2 + DLT_JUNIPER_MFR = 0x86 + DLT_JUNIPER_MLFR = 0x83 + DLT_JUNIPER_MLPPP = 0x82 + DLT_JUNIPER_MONITOR = 0xa4 + DLT_JUNIPER_PIC_PEER = 0xae + DLT_JUNIPER_PPP = 0xb3 + DLT_JUNIPER_PPPOE = 0xa7 + DLT_JUNIPER_PPPOE_ATM = 0xa8 + DLT_JUNIPER_SERVICES = 0x88 + DLT_JUNIPER_ST = 0xc8 + DLT_JUNIPER_VP = 0xb7 + DLT_LAPB_WITH_DIR = 0xcf + DLT_LAPD = 0xcb + DLT_LIN = 0xd4 + DLT_LINUX_EVDEV = 0xd8 + DLT_LINUX_IRDA = 0x90 + DLT_LINUX_LAPD = 0xb1 + DLT_LINUX_SLL = 0x71 + DLT_LOOP = 0x6c + DLT_LTALK = 0x72 + DLT_MFR = 0xb6 + DLT_MOST = 0xd3 + DLT_MPLS = 0xdb + DLT_MTP2 = 0x8c + DLT_MTP2_WITH_PHDR = 0x8b + DLT_MTP3 = 0x8d + DLT_NULL = 0x0 + DLT_PCI_EXP = 0x7d + DLT_PFLOG = 0x75 + DLT_PFSYNC = 0x12 + DLT_PPI = 0xc0 + DLT_PPP = 0x9 + DLT_PPP_BSDOS = 0xe + DLT_PPP_ETHER = 0x33 + DLT_PPP_PPPD = 0xa6 + DLT_PPP_SERIAL = 0x32 + DLT_PPP_WITH_DIR = 0xcc + DLT_PRISM_HEADER = 0x77 + DLT_PRONET = 0x4 + DLT_RAIF1 = 0xc6 + DLT_RAW = 0xc + DLT_RAWAF_MASK = 0x2240000 + DLT_RIO = 0x7c + DLT_SCCP = 0x8e + DLT_SITA = 0xc4 + DLT_SLIP = 0x8 + DLT_SLIP_BSDOS = 0xd + DLT_SUNATM = 0x7b + DLT_SYMANTEC_FIREWALL = 0x63 + DLT_TZSP = 0x80 + DLT_USB = 0xba + DLT_USB_LINUX = 0xbd + DLT_USB_LINUX_MMAPPED = 0xdc + DLT_WIHART = 0xdf + DLT_X2E_SERIAL = 0xd5 + DLT_X2E_XORAYA = 0xd6 + DT_BLK = 0x6 + DT_CHR = 0x2 + DT_DIR = 0x4 + DT_FIFO = 0x1 + DT_LNK = 0xa + DT_REG = 0x8 + DT_SOCK = 0xc + DT_UNKNOWN = 0x0 + DT_WHT = 0xe + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 + EMUL_LINUX = 0x1 + EMUL_LINUX32 = 0x5 + EMUL_MAXID = 0x6 + ETHERCAP_JUMBO_MTU = 0x4 + ETHERCAP_VLAN_HWTAGGING = 0x2 + ETHERCAP_VLAN_MTU = 0x1 + ETHERMIN = 0x2e + ETHERMTU = 0x5dc + ETHERMTU_JUMBO = 0x2328 + ETHERTYPE_8023 = 0x4 + ETHERTYPE_AARP = 0x80f3 + ETHERTYPE_ACCTON = 0x8390 + ETHERTYPE_AEONIC = 0x8036 + ETHERTYPE_ALPHA = 0x814a + ETHERTYPE_AMBER = 0x6008 + ETHERTYPE_AMOEBA = 0x8145 + ETHERTYPE_APOLLO = 0x80f7 + ETHERTYPE_APOLLODOMAIN = 0x8019 + ETHERTYPE_APPLETALK = 0x809b + ETHERTYPE_APPLITEK = 0x80c7 + ETHERTYPE_ARGONAUT = 0x803a + ETHERTYPE_ARP = 0x806 + ETHERTYPE_AT = 0x809b + ETHERTYPE_ATALK = 0x809b + ETHERTYPE_ATOMIC = 0x86df + ETHERTYPE_ATT = 0x8069 + ETHERTYPE_ATTSTANFORD = 0x8008 + ETHERTYPE_AUTOPHON = 0x806a + ETHERTYPE_AXIS = 0x8856 + ETHERTYPE_BCLOOP = 0x9003 + ETHERTYPE_BOFL = 0x8102 + ETHERTYPE_CABLETRON = 0x7034 + ETHERTYPE_CHAOS = 0x804 + ETHERTYPE_COMDESIGN = 0x806c + ETHERTYPE_COMPUGRAPHIC = 0x806d + ETHERTYPE_COUNTERPOINT = 0x8062 + ETHERTYPE_CRONUS = 0x8004 + ETHERTYPE_CRONUSVLN = 0x8003 + ETHERTYPE_DCA = 0x1234 + ETHERTYPE_DDE = 0x807b + ETHERTYPE_DEBNI = 0xaaaa + ETHERTYPE_DECAM = 0x8048 + ETHERTYPE_DECCUST = 0x6006 + ETHERTYPE_DECDIAG = 0x6005 + ETHERTYPE_DECDNS = 0x803c + ETHERTYPE_DECDTS = 0x803e + ETHERTYPE_DECEXPER = 0x6000 + ETHERTYPE_DECLAST = 0x8041 + ETHERTYPE_DECLTM = 0x803f + ETHERTYPE_DECMUMPS = 0x6009 + ETHERTYPE_DECNETBIOS = 0x8040 + ETHERTYPE_DELTACON = 0x86de + ETHERTYPE_DIDDLE = 0x4321 + ETHERTYPE_DLOG1 = 0x660 + ETHERTYPE_DLOG2 = 0x661 + ETHERTYPE_DN = 0x6003 + ETHERTYPE_DOGFIGHT = 0x1989 + ETHERTYPE_DSMD = 0x8039 + ETHERTYPE_ECMA = 0x803 + ETHERTYPE_ENCRYPT = 0x803d + ETHERTYPE_ES = 0x805d + ETHERTYPE_EXCELAN = 0x8010 + ETHERTYPE_EXPERDATA = 0x8049 + ETHERTYPE_FLIP = 0x8146 + ETHERTYPE_FLOWCONTROL = 0x8808 + ETHERTYPE_FRARP = 0x808 + ETHERTYPE_GENDYN = 0x8068 + ETHERTYPE_HAYES = 0x8130 + ETHERTYPE_HIPPI_FP = 0x8180 + ETHERTYPE_HITACHI = 0x8820 + ETHERTYPE_HP = 0x8005 + ETHERTYPE_IEEEPUP = 0xa00 + ETHERTYPE_IEEEPUPAT = 0xa01 + ETHERTYPE_IMLBL = 0x4c42 + ETHERTYPE_IMLBLDIAG = 0x424c + ETHERTYPE_IP = 0x800 + ETHERTYPE_IPAS = 0x876c + ETHERTYPE_IPV6 = 0x86dd + ETHERTYPE_IPX = 0x8137 + ETHERTYPE_IPXNEW = 0x8037 + ETHERTYPE_KALPANA = 0x8582 + ETHERTYPE_LANBRIDGE = 0x8038 + ETHERTYPE_LANPROBE = 0x8888 + ETHERTYPE_LAT = 0x6004 + ETHERTYPE_LBACK = 0x9000 + ETHERTYPE_LITTLE = 0x8060 + ETHERTYPE_LOGICRAFT = 0x8148 + ETHERTYPE_LOOPBACK = 0x9000 + ETHERTYPE_MATRA = 0x807a + ETHERTYPE_MAX = 0xffff + ETHERTYPE_MERIT = 0x807c + ETHERTYPE_MICP = 0x873a + ETHERTYPE_MOPDL = 0x6001 + ETHERTYPE_MOPRC = 0x6002 + ETHERTYPE_MOTOROLA = 0x818d + ETHERTYPE_MPLS = 0x8847 + ETHERTYPE_MPLS_MCAST = 0x8848 + ETHERTYPE_MUMPS = 0x813f + ETHERTYPE_NBPCC = 0x3c04 + ETHERTYPE_NBPCLAIM = 0x3c09 + ETHERTYPE_NBPCLREQ = 0x3c05 + ETHERTYPE_NBPCLRSP = 0x3c06 + ETHERTYPE_NBPCREQ = 0x3c02 + ETHERTYPE_NBPCRSP = 0x3c03 + ETHERTYPE_NBPDG = 0x3c07 + ETHERTYPE_NBPDGB = 0x3c08 + ETHERTYPE_NBPDLTE = 0x3c0a + ETHERTYPE_NBPRAR = 0x3c0c + ETHERTYPE_NBPRAS = 0x3c0b + ETHERTYPE_NBPRST = 0x3c0d + ETHERTYPE_NBPSCD = 0x3c01 + ETHERTYPE_NBPVCD = 0x3c00 + ETHERTYPE_NBS = 0x802 + ETHERTYPE_NCD = 0x8149 + ETHERTYPE_NESTAR = 0x8006 + ETHERTYPE_NETBEUI = 0x8191 + ETHERTYPE_NOVELL = 0x8138 + ETHERTYPE_NS = 0x600 + ETHERTYPE_NSAT = 0x601 + ETHERTYPE_NSCOMPAT = 0x807 + ETHERTYPE_NTRAILER = 0x10 + ETHERTYPE_OS9 = 0x7007 + ETHERTYPE_OS9NET = 0x7009 + ETHERTYPE_PACER = 0x80c6 + ETHERTYPE_PAE = 0x888e + ETHERTYPE_PCS = 0x4242 + ETHERTYPE_PLANNING = 0x8044 + ETHERTYPE_PPP = 0x880b + ETHERTYPE_PPPOE = 0x8864 + ETHERTYPE_PPPOEDISC = 0x8863 + ETHERTYPE_PRIMENTS = 0x7031 + ETHERTYPE_PUP = 0x200 + ETHERTYPE_PUPAT = 0x200 + ETHERTYPE_RACAL = 0x7030 + ETHERTYPE_RATIONAL = 0x8150 + ETHERTYPE_RAWFR = 0x6559 + ETHERTYPE_RCL = 0x1995 + ETHERTYPE_RDP = 0x8739 + ETHERTYPE_RETIX = 0x80f2 + ETHERTYPE_REVARP = 0x8035 + ETHERTYPE_SCA = 0x6007 + ETHERTYPE_SECTRA = 0x86db + ETHERTYPE_SECUREDATA = 0x876d + ETHERTYPE_SGITW = 0x817e + ETHERTYPE_SG_BOUNCE = 0x8016 + ETHERTYPE_SG_DIAG = 0x8013 + ETHERTYPE_SG_NETGAMES = 0x8014 + ETHERTYPE_SG_RESV = 0x8015 + ETHERTYPE_SIMNET = 0x5208 + ETHERTYPE_SLOWPROTOCOLS = 0x8809 + ETHERTYPE_SNA = 0x80d5 + ETHERTYPE_SNMP = 0x814c + ETHERTYPE_SONIX = 0xfaf5 + ETHERTYPE_SPIDER = 0x809f + ETHERTYPE_SPRITE = 0x500 + ETHERTYPE_STP = 0x8181 + ETHERTYPE_TALARIS = 0x812b + ETHERTYPE_TALARISMC = 0x852b + ETHERTYPE_TCPCOMP = 0x876b + ETHERTYPE_TCPSM = 0x9002 + ETHERTYPE_TEC = 0x814f + ETHERTYPE_TIGAN = 0x802f + ETHERTYPE_TRAIL = 0x1000 + ETHERTYPE_TRANSETHER = 0x6558 + ETHERTYPE_TYMSHARE = 0x802e + ETHERTYPE_UBBST = 0x7005 + ETHERTYPE_UBDEBUG = 0x900 + ETHERTYPE_UBDIAGLOOP = 0x7002 + ETHERTYPE_UBDL = 0x7000 + ETHERTYPE_UBNIU = 0x7001 + ETHERTYPE_UBNMC = 0x7003 + ETHERTYPE_VALID = 0x1600 + ETHERTYPE_VARIAN = 0x80dd + ETHERTYPE_VAXELN = 0x803b + ETHERTYPE_VEECO = 0x8067 + ETHERTYPE_VEXP = 0x805b + ETHERTYPE_VGLAB = 0x8131 + ETHERTYPE_VINES = 0xbad + ETHERTYPE_VINESECHO = 0xbaf + ETHERTYPE_VINESLOOP = 0xbae + ETHERTYPE_VITAL = 0xff00 + ETHERTYPE_VLAN = 0x8100 + ETHERTYPE_VLTLMAN = 0x8080 + ETHERTYPE_VPROD = 0x805c + ETHERTYPE_VURESERVED = 0x8147 + ETHERTYPE_WATERLOO = 0x8130 + ETHERTYPE_WELLFLEET = 0x8103 + ETHERTYPE_X25 = 0x805 + ETHERTYPE_X75 = 0x801 + ETHERTYPE_XNSSM = 0x9001 + ETHERTYPE_XTP = 0x817d + ETHER_ADDR_LEN = 0x6 + ETHER_CRC_LEN = 0x4 + ETHER_CRC_POLY_BE = 0x4c11db6 + ETHER_CRC_POLY_LE = 0xedb88320 + ETHER_HDR_LEN = 0xe + ETHER_MAX_LEN = 0x5ee + ETHER_MAX_LEN_JUMBO = 0x233a + ETHER_MIN_LEN = 0x40 + ETHER_PPPOE_ENCAP_LEN = 0x8 + ETHER_TYPE_LEN = 0x2 + ETHER_VLAN_ENCAP_LEN = 0x4 + EVFILT_AIO = 0x2 + EVFILT_PROC = 0x4 + EVFILT_READ = 0x0 + EVFILT_SIGNAL = 0x5 + EVFILT_SYSCOUNT = 0x7 + EVFILT_TIMER = 0x6 + EVFILT_VNODE = 0x3 + EVFILT_WRITE = 0x1 + EV_ADD = 0x1 + EV_CLEAR = 0x20 + EV_DELETE = 0x2 + EV_DISABLE = 0x8 + EV_ENABLE = 0x4 + EV_EOF = 0x8000 + EV_ERROR = 0x4000 + EV_FLAG1 = 0x2000 + EV_ONESHOT = 0x10 + EV_SYSFLAGS = 0xf000 + EXTA = 0x4b00 + EXTB = 0x9600 + EXTPROC = 0x800 + FD_CLOEXEC = 0x1 + FD_SETSIZE = 0x100 + FLUSHO = 0x800000 + F_CLOSEM = 0xa + F_DUPFD = 0x0 + F_DUPFD_CLOEXEC = 0xc + F_FSCTL = -0x80000000 + F_FSDIRMASK = 0x70000000 + F_FSIN = 0x10000000 + F_FSINOUT = 0x30000000 + F_FSOUT = 0x20000000 + F_FSPRIV = 0x8000 + F_FSVOID = 0x40000000 + F_GETFD = 0x1 + F_GETFL = 0x3 + F_GETLK = 0x7 + F_GETNOSIGPIPE = 0xd + F_GETOWN = 0x5 + F_MAXFD = 0xb + F_OK = 0x0 + F_PARAM_MASK = 0xfff + F_PARAM_MAX = 0xfff + F_RDLCK = 0x1 + F_SETFD = 0x2 + F_SETFL = 0x4 + F_SETLK = 0x8 + F_SETLKW = 0x9 + F_SETNOSIGPIPE = 0xe + F_SETOWN = 0x6 + F_UNLCK = 0x2 + F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 + IFA_ROUTE = 0x1 + IFF_ALLMULTI = 0x200 + IFF_BROADCAST = 0x2 + IFF_CANTCHANGE = 0x8f52 + IFF_DEBUG = 0x4 + IFF_LINK0 = 0x1000 + IFF_LINK1 = 0x2000 + IFF_LINK2 = 0x4000 + IFF_LOOPBACK = 0x8 + IFF_MULTICAST = 0x8000 + IFF_NOARP = 0x80 + IFF_NOTRAILERS = 0x20 + IFF_OACTIVE = 0x400 + IFF_POINTOPOINT = 0x10 + IFF_PROMISC = 0x100 + IFF_RUNNING = 0x40 + IFF_SIMPLEX = 0x800 + IFF_UP = 0x1 + IFNAMSIZ = 0x10 + IFT_1822 = 0x2 + IFT_A12MPPSWITCH = 0x82 + IFT_AAL2 = 0xbb + IFT_AAL5 = 0x31 + IFT_ADSL = 0x5e + IFT_AFLANE8023 = 0x3b + IFT_AFLANE8025 = 0x3c + IFT_ARAP = 0x58 + IFT_ARCNET = 0x23 + IFT_ARCNETPLUS = 0x24 + IFT_ASYNC = 0x54 + IFT_ATM = 0x25 + IFT_ATMDXI = 0x69 + IFT_ATMFUNI = 0x6a + IFT_ATMIMA = 0x6b + IFT_ATMLOGICAL = 0x50 + IFT_ATMRADIO = 0xbd + IFT_ATMSUBINTERFACE = 0x86 + IFT_ATMVCIENDPT = 0xc2 + IFT_ATMVIRTUAL = 0x95 + IFT_BGPPOLICYACCOUNTING = 0xa2 + IFT_BRIDGE = 0xd1 + IFT_BSC = 0x53 + IFT_CARP = 0xf8 + IFT_CCTEMUL = 0x3d + IFT_CEPT = 0x13 + IFT_CES = 0x85 + IFT_CHANNEL = 0x46 + IFT_CNR = 0x55 + IFT_COFFEE = 0x84 + IFT_COMPOSITELINK = 0x9b + IFT_DCN = 0x8d + IFT_DIGITALPOWERLINE = 0x8a + IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba + IFT_DLSW = 0x4a + IFT_DOCSCABLEDOWNSTREAM = 0x80 + IFT_DOCSCABLEMACLAYER = 0x7f + IFT_DOCSCABLEUPSTREAM = 0x81 + IFT_DOCSCABLEUPSTREAMCHANNEL = 0xcd + IFT_DS0 = 0x51 + IFT_DS0BUNDLE = 0x52 + IFT_DS1FDL = 0xaa + IFT_DS3 = 0x1e + IFT_DTM = 0x8c + IFT_DVBASILN = 0xac + IFT_DVBASIOUT = 0xad + IFT_DVBRCCDOWNSTREAM = 0x93 + IFT_DVBRCCMACLAYER = 0x92 + IFT_DVBRCCUPSTREAM = 0x94 + IFT_ECONET = 0xce + IFT_EON = 0x19 + IFT_EPLRS = 0x57 + IFT_ESCON = 0x49 + IFT_ETHER = 0x6 + IFT_FAITH = 0xf2 + IFT_FAST = 0x7d + IFT_FASTETHER = 0x3e + IFT_FASTETHERFX = 0x45 + IFT_FDDI = 0xf + IFT_FIBRECHANNEL = 0x38 + IFT_FRAMERELAYINTERCONNECT = 0x3a + IFT_FRAMERELAYMPI = 0x5c + IFT_FRDLCIENDPT = 0xc1 + IFT_FRELAY = 0x20 + IFT_FRELAYDCE = 0x2c + IFT_FRF16MFRBUNDLE = 0xa3 + IFT_FRFORWARD = 0x9e + IFT_G703AT2MB = 0x43 + IFT_G703AT64K = 0x42 + IFT_GIF = 0xf0 + IFT_GIGABITETHERNET = 0x75 + IFT_GR303IDT = 0xb2 + IFT_GR303RDT = 0xb1 + IFT_H323GATEKEEPER = 0xa4 + IFT_H323PROXY = 0xa5 + IFT_HDH1822 = 0x3 + IFT_HDLC = 0x76 + IFT_HDSL2 = 0xa8 + IFT_HIPERLAN2 = 0xb7 + IFT_HIPPI = 0x2f + IFT_HIPPIINTERFACE = 0x39 + IFT_HOSTPAD = 0x5a + IFT_HSSI = 0x2e + IFT_HY = 0xe + IFT_IBM370PARCHAN = 0x48 + IFT_IDSL = 0x9a + IFT_IEEE1394 = 0x90 + IFT_IEEE80211 = 0x47 + IFT_IEEE80212 = 0x37 + IFT_IEEE8023ADLAG = 0xa1 + IFT_IFGSN = 0x91 + IFT_IMT = 0xbe + IFT_INFINIBAND = 0xc7 + IFT_INTERLEAVE = 0x7c + IFT_IP = 0x7e + IFT_IPFORWARD = 0x8e + IFT_IPOVERATM = 0x72 + IFT_IPOVERCDLC = 0x6d + IFT_IPOVERCLAW = 0x6e + IFT_IPSWITCH = 0x4e + IFT_ISDN = 0x3f + IFT_ISDNBASIC = 0x14 + IFT_ISDNPRIMARY = 0x15 + IFT_ISDNS = 0x4b + IFT_ISDNU = 0x4c + IFT_ISO88022LLC = 0x29 + IFT_ISO88023 = 0x7 + IFT_ISO88024 = 0x8 + IFT_ISO88025 = 0x9 + IFT_ISO88025CRFPINT = 0x62 + IFT_ISO88025DTR = 0x56 + IFT_ISO88025FIBER = 0x73 + IFT_ISO88026 = 0xa + IFT_ISUP = 0xb3 + IFT_L2VLAN = 0x87 + IFT_L3IPVLAN = 0x88 + IFT_L3IPXVLAN = 0x89 + IFT_LAPB = 0x10 + IFT_LAPD = 0x4d + IFT_LAPF = 0x77 + IFT_LINEGROUP = 0xd2 + IFT_LOCALTALK = 0x2a + IFT_LOOP = 0x18 + IFT_MEDIAMAILOVERIP = 0x8b + IFT_MFSIGLINK = 0xa7 + IFT_MIOX25 = 0x26 + IFT_MODEM = 0x30 + IFT_MPC = 0x71 + IFT_MPLS = 0xa6 + IFT_MPLSTUNNEL = 0x96 + IFT_MSDSL = 0x8f + IFT_MVL = 0xbf + IFT_MYRINET = 0x63 + IFT_NFAS = 0xaf + IFT_NSIP = 0x1b + IFT_OPTICALCHANNEL = 0xc3 + IFT_OPTICALTRANSPORT = 0xc4 + IFT_OTHER = 0x1 + IFT_P10 = 0xc + IFT_P80 = 0xd + IFT_PARA = 0x22 + IFT_PFLOG = 0xf5 + IFT_PFSYNC = 0xf6 + IFT_PLC = 0xae + IFT_PON155 = 0xcf + IFT_PON622 = 0xd0 + IFT_POS = 0xab + IFT_PPP = 0x17 + IFT_PPPMULTILINKBUNDLE = 0x6c + IFT_PROPATM = 0xc5 + IFT_PROPBWAP2MP = 0xb8 + IFT_PROPCNLS = 0x59 + IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5 + IFT_PROPDOCSWIRELESSMACLAYER = 0xb4 + IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6 + IFT_PROPMUX = 0x36 + IFT_PROPVIRTUAL = 0x35 + IFT_PROPWIRELESSP2P = 0x9d + IFT_PTPSERIAL = 0x16 + IFT_PVC = 0xf1 + IFT_Q2931 = 0xc9 + IFT_QLLC = 0x44 + IFT_RADIOMAC = 0xbc + IFT_RADSL = 0x5f + IFT_REACHDSL = 0xc0 + IFT_RFC1483 = 0x9f + IFT_RS232 = 0x21 + IFT_RSRB = 0x4f + IFT_SDLC = 0x11 + IFT_SDSL = 0x60 + IFT_SHDSL = 0xa9 + IFT_SIP = 0x1f + IFT_SIPSIG = 0xcc + IFT_SIPTG = 0xcb + IFT_SLIP = 0x1c + IFT_SMDSDXI = 0x2b + IFT_SMDSICIP = 0x34 + IFT_SONET = 0x27 + IFT_SONETOVERHEADCHANNEL = 0xb9 + IFT_SONETPATH = 0x32 + IFT_SONETVT = 0x33 + IFT_SRP = 0x97 + IFT_SS7SIGLINK = 0x9c + IFT_STACKTOSTACK = 0x6f + IFT_STARLAN = 0xb + IFT_STF = 0xd7 + IFT_T1 = 0x12 + IFT_TDLC = 0x74 + IFT_TELINK = 0xc8 + IFT_TERMPAD = 0x5b + IFT_TR008 = 0xb0 + IFT_TRANSPHDLC = 0x7b + IFT_TUNNEL = 0x83 + IFT_ULTRA = 0x1d + IFT_USB = 0xa0 + IFT_V11 = 0x40 + IFT_V35 = 0x2d + IFT_V36 = 0x41 + IFT_V37 = 0x78 + IFT_VDSL = 0x61 + IFT_VIRTUALIPADDRESS = 0x70 + IFT_VIRTUALTG = 0xca + IFT_VOICEDID = 0xd5 + IFT_VOICEEM = 0x64 + IFT_VOICEEMFGD = 0xd3 + IFT_VOICEENCAP = 0x67 + IFT_VOICEFGDEANA = 0xd4 + IFT_VOICEFXO = 0x65 + IFT_VOICEFXS = 0x66 + IFT_VOICEOVERATM = 0x98 + IFT_VOICEOVERCABLE = 0xc6 + IFT_VOICEOVERFRAMERELAY = 0x99 + IFT_VOICEOVERIP = 0x68 + IFT_X213 = 0x5d + IFT_X25 = 0x5 + IFT_X25DDN = 0x4 + IFT_X25HUNTGROUP = 0x7a + IFT_X25MLP = 0x79 + IFT_X25PLE = 0x28 + IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 + IN_CLASSA_HOST = 0xffffff + IN_CLASSA_MAX = 0x80 + IN_CLASSA_NET = 0xff000000 + IN_CLASSA_NSHIFT = 0x18 + IN_CLASSB_HOST = 0xffff + IN_CLASSB_MAX = 0x10000 + IN_CLASSB_NET = 0xffff0000 + IN_CLASSB_NSHIFT = 0x10 + IN_CLASSC_HOST = 0xff + IN_CLASSC_NET = 0xffffff00 + IN_CLASSC_NSHIFT = 0x8 + IN_CLASSD_HOST = 0xfffffff + IN_CLASSD_NET = 0xf0000000 + IN_CLASSD_NSHIFT = 0x1c + IN_LOOPBACKNET = 0x7f + IPPROTO_AH = 0x33 + IPPROTO_CARP = 0x70 + IPPROTO_DONE = 0x101 + IPPROTO_DSTOPTS = 0x3c + IPPROTO_EGP = 0x8 + IPPROTO_ENCAP = 0x62 + IPPROTO_EON = 0x50 + IPPROTO_ESP = 0x32 + IPPROTO_ETHERIP = 0x61 + IPPROTO_FRAGMENT = 0x2c + IPPROTO_GGP = 0x3 + IPPROTO_GRE = 0x2f + IPPROTO_HOPOPTS = 0x0 + IPPROTO_ICMP = 0x1 + IPPROTO_ICMPV6 = 0x3a + IPPROTO_IDP = 0x16 + IPPROTO_IGMP = 0x2 + IPPROTO_IP = 0x0 + IPPROTO_IPCOMP = 0x6c + IPPROTO_IPIP = 0x4 + IPPROTO_IPV4 = 0x4 + IPPROTO_IPV6 = 0x29 + IPPROTO_IPV6_ICMP = 0x3a + IPPROTO_MAX = 0x100 + IPPROTO_MAXID = 0x34 + IPPROTO_MOBILE = 0x37 + IPPROTO_NONE = 0x3b + IPPROTO_PFSYNC = 0xf0 + IPPROTO_PIM = 0x67 + IPPROTO_PUP = 0xc + IPPROTO_RAW = 0xff + IPPROTO_ROUTING = 0x2b + IPPROTO_RSVP = 0x2e + IPPROTO_TCP = 0x6 + IPPROTO_TP = 0x1d + IPPROTO_UDP = 0x11 + IPPROTO_VRRP = 0x70 + IPV6_CHECKSUM = 0x1a + IPV6_DEFAULT_MULTICAST_HOPS = 0x1 + IPV6_DEFAULT_MULTICAST_LOOP = 0x1 + IPV6_DEFHLIM = 0x40 + IPV6_DONTFRAG = 0x3e + IPV6_DSTOPTS = 0x32 + IPV6_FAITH = 0x1d + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 + IPV6_FRAGTTL = 0x78 + IPV6_HLIMDEC = 0x1 + IPV6_HOPLIMIT = 0x2f + IPV6_HOPOPTS = 0x31 + IPV6_IPSEC_POLICY = 0x1c + IPV6_JOIN_GROUP = 0xc + IPV6_LEAVE_GROUP = 0xd + IPV6_MAXHLIM = 0xff + IPV6_MAXPACKET = 0xffff + IPV6_MMTU = 0x500 + IPV6_MULTICAST_HOPS = 0xa + IPV6_MULTICAST_IF = 0x9 + IPV6_MULTICAST_LOOP = 0xb + IPV6_NEXTHOP = 0x30 + IPV6_PATHMTU = 0x2c + IPV6_PKTINFO = 0x2e + IPV6_PORTRANGE = 0xe + IPV6_PORTRANGE_DEFAULT = 0x0 + IPV6_PORTRANGE_HIGH = 0x1 + IPV6_PORTRANGE_LOW = 0x2 + IPV6_RECVDSTOPTS = 0x28 + IPV6_RECVHOPLIMIT = 0x25 + IPV6_RECVHOPOPTS = 0x27 + IPV6_RECVPATHMTU = 0x2b + IPV6_RECVPKTINFO = 0x24 + IPV6_RECVRTHDR = 0x26 + IPV6_RECVTCLASS = 0x39 + IPV6_RTHDR = 0x33 + IPV6_RTHDRDSTOPTS = 0x23 + IPV6_RTHDR_LOOSE = 0x0 + IPV6_RTHDR_STRICT = 0x1 + IPV6_RTHDR_TYPE_0 = 0x0 + IPV6_SOCKOPT_RESERVED1 = 0x3 + IPV6_TCLASS = 0x3d + IPV6_UNICAST_HOPS = 0x4 + IPV6_USE_MIN_MTU = 0x2a + IPV6_V6ONLY = 0x1b + IPV6_VERSION = 0x60 + IPV6_VERSION_MASK = 0xf0 + IP_ADD_MEMBERSHIP = 0xc + IP_DEFAULT_MULTICAST_LOOP = 0x1 + IP_DEFAULT_MULTICAST_TTL = 0x1 + IP_DF = 0x4000 + IP_DROP_MEMBERSHIP = 0xd + IP_EF = 0x8000 + IP_ERRORMTU = 0x15 + IP_HDRINCL = 0x2 + IP_IPSEC_POLICY = 0x16 + IP_MAXPACKET = 0xffff + IP_MAX_MEMBERSHIPS = 0x14 + IP_MF = 0x2000 + IP_MINFRAGSIZE = 0x45 + IP_MINTTL = 0x18 + IP_MSS = 0x240 + IP_MULTICAST_IF = 0x9 + IP_MULTICAST_LOOP = 0xb + IP_MULTICAST_TTL = 0xa + IP_OFFMASK = 0x1fff + IP_OPTIONS = 0x1 + IP_PORTRANGE = 0x13 + IP_PORTRANGE_DEFAULT = 0x0 + IP_PORTRANGE_HIGH = 0x1 + IP_PORTRANGE_LOW = 0x2 + IP_RECVDSTADDR = 0x7 + IP_RECVIF = 0x14 + IP_RECVOPTS = 0x5 + IP_RECVRETOPTS = 0x6 + IP_RECVTTL = 0x17 + IP_RETOPTS = 0x8 + IP_RF = 0x8000 + IP_TOS = 0x3 + IP_TTL = 0x4 + ISIG = 0x80 + ISTRIP = 0x20 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 + LOCK_EX = 0x2 + LOCK_NB = 0x4 + LOCK_SH = 0x1 + LOCK_UN = 0x8 + MSG_BCAST = 0x100 + MSG_CMSG_CLOEXEC = 0x800 + MSG_CONTROLMBUF = 0x2000000 + MSG_CTRUNC = 0x20 + MSG_DONTROUTE = 0x4 + MSG_DONTWAIT = 0x80 + MSG_EOR = 0x8 + MSG_IOVUSRSPACE = 0x4000000 + MSG_LENUSRSPACE = 0x8000000 + MSG_MCAST = 0x200 + MSG_NAMEMBUF = 0x1000000 + MSG_NBIO = 0x1000 + MSG_NOSIGNAL = 0x400 + MSG_OOB = 0x1 + MSG_PEEK = 0x2 + MSG_TRUNC = 0x10 + MSG_USERFLAGS = 0xffffff + MSG_WAITALL = 0x40 + NAME_MAX = 0x1ff + NET_RT_DUMP = 0x1 + NET_RT_FLAGS = 0x2 + NET_RT_IFLIST = 0x5 + NET_RT_MAXID = 0x6 + NET_RT_OIFLIST = 0x4 + NET_RT_OOIFLIST = 0x3 + NOFLSH = 0x80000000 + NOTE_ATTRIB = 0x8 + NOTE_CHILD = 0x4 + NOTE_DELETE = 0x1 + NOTE_EXEC = 0x20000000 + NOTE_EXIT = 0x80000000 + NOTE_EXTEND = 0x4 + NOTE_FORK = 0x40000000 + NOTE_LINK = 0x10 + NOTE_LOWAT = 0x1 + NOTE_PCTRLMASK = 0xf0000000 + NOTE_PDATAMASK = 0xfffff + NOTE_RENAME = 0x20 + NOTE_REVOKE = 0x40 + NOTE_TRACK = 0x1 + NOTE_TRACKERR = 0x2 + NOTE_WRITE = 0x2 + OCRNL = 0x10 + OFIOGETBMAP = 0xc004667a + ONLCR = 0x2 + ONLRET = 0x40 + ONOCR = 0x20 + ONOEOT = 0x8 + OPOST = 0x1 + O_ACCMODE = 0x3 + O_ALT_IO = 0x40000 + O_APPEND = 0x8 + O_ASYNC = 0x40 + O_CLOEXEC = 0x400000 + O_CREAT = 0x200 + O_DIRECT = 0x80000 + O_DIRECTORY = 0x200000 + O_DSYNC = 0x10000 + O_EXCL = 0x800 + O_EXLOCK = 0x20 + O_FSYNC = 0x80 + O_NDELAY = 0x4 + O_NOCTTY = 0x8000 + O_NOFOLLOW = 0x100 + O_NONBLOCK = 0x4 + O_NOSIGPIPE = 0x1000000 + O_RDONLY = 0x0 + O_RDWR = 0x2 + O_RSYNC = 0x20000 + O_SHLOCK = 0x10 + O_SYNC = 0x80 + O_TRUNC = 0x400 + O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 + PRI_IOFLUSH = 0x7c + RLIMIT_AS = 0xa + RLIMIT_CORE = 0x4 + RLIMIT_CPU = 0x0 + RLIMIT_DATA = 0x2 + RLIMIT_FSIZE = 0x1 + RLIMIT_NOFILE = 0x8 + RLIMIT_STACK = 0x3 + RLIM_INFINITY = 0x7fffffffffffffff + RTAX_AUTHOR = 0x6 + RTAX_BRD = 0x7 + RTAX_DST = 0x0 + RTAX_GATEWAY = 0x1 + RTAX_GENMASK = 0x3 + RTAX_IFA = 0x5 + RTAX_IFP = 0x4 + RTAX_MAX = 0x9 + RTAX_NETMASK = 0x2 + RTAX_TAG = 0x8 + RTA_AUTHOR = 0x40 + RTA_BRD = 0x80 + RTA_DST = 0x1 + RTA_GATEWAY = 0x2 + RTA_GENMASK = 0x8 + RTA_IFA = 0x20 + RTA_IFP = 0x10 + RTA_NETMASK = 0x4 + RTA_TAG = 0x100 + RTF_ANNOUNCE = 0x20000 + RTF_BLACKHOLE = 0x1000 + RTF_CLONED = 0x2000 + RTF_CLONING = 0x100 + RTF_DONE = 0x40 + RTF_DYNAMIC = 0x10 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_LLINFO = 0x400 + RTF_MASK = 0x80 + RTF_MODIFIED = 0x20 + RTF_PROTO1 = 0x8000 + RTF_PROTO2 = 0x4000 + RTF_REJECT = 0x8 + RTF_SRC = 0x10000 + RTF_STATIC = 0x800 + RTF_UP = 0x1 + RTF_XRESOLVE = 0x200 + RTM_ADD = 0x1 + RTM_CHANGE = 0x3 + RTM_CHGADDR = 0x15 + RTM_DELADDR = 0xd + RTM_DELETE = 0x2 + RTM_GET = 0x4 + RTM_IEEE80211 = 0x11 + RTM_IFANNOUNCE = 0x10 + RTM_IFINFO = 0x14 + RTM_LLINFO_UPD = 0x13 + RTM_LOCK = 0x8 + RTM_LOSING = 0x5 + RTM_MISS = 0x7 + RTM_NEWADDR = 0xc + RTM_OIFINFO = 0xf + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_OOIFINFO = 0xe + RTM_REDIRECT = 0x6 + RTM_RESOLVE = 0xb + RTM_RTTUNIT = 0xf4240 + RTM_SETGATE = 0x12 + RTM_VERSION = 0x4 + RTV_EXPIRE = 0x4 + RTV_HOPCOUNT = 0x2 + RTV_MTU = 0x1 + RTV_RPIPE = 0x8 + RTV_RTT = 0x40 + RTV_RTTVAR = 0x80 + RTV_SPIPE = 0x10 + RTV_SSTHRESH = 0x20 + RUSAGE_CHILDREN = -0x1 + RUSAGE_SELF = 0x0 + SCM_CREDS = 0x4 + SCM_RIGHTS = 0x1 + SCM_TIMESTAMP = 0x8 + SHUT_RD = 0x0 + SHUT_RDWR = 0x2 + SHUT_WR = 0x1 + SIOCADDMULTI = 0x80906931 + SIOCADDRT = 0x8030720a + SIOCAIFADDR = 0x8040691a + SIOCALIFADDR = 0x8118691c + SIOCATMARK = 0x40047307 + SIOCDELMULTI = 0x80906932 + SIOCDELRT = 0x8030720b + SIOCDIFADDR = 0x80906919 + SIOCDIFPHYADDR = 0x80906949 + SIOCDLIFADDR = 0x8118691e + SIOCGDRVSPEC = 0xc01c697b + SIOCGETPFSYNC = 0xc09069f8 + SIOCGETSGCNT = 0xc0147534 + SIOCGETVIFCNT = 0xc0147533 + SIOCGHIWAT = 0x40047301 + SIOCGIFADDR = 0xc0906921 + SIOCGIFADDRPREF = 0xc0946920 + SIOCGIFALIAS = 0xc040691b + SIOCGIFBRDADDR = 0xc0906923 + SIOCGIFCAP = 0xc0206976 + SIOCGIFCONF = 0xc0086926 + SIOCGIFDATA = 0xc0946985 + SIOCGIFDLT = 0xc0906977 + SIOCGIFDSTADDR = 0xc0906922 + SIOCGIFFLAGS = 0xc0906911 + SIOCGIFGENERIC = 0xc090693a + SIOCGIFMEDIA = 0xc0286936 + SIOCGIFMETRIC = 0xc0906917 + SIOCGIFMTU = 0xc090697e + SIOCGIFNETMASK = 0xc0906925 + SIOCGIFPDSTADDR = 0xc0906948 + SIOCGIFPSRCADDR = 0xc0906947 + SIOCGLIFADDR = 0xc118691d + SIOCGLIFPHYADDR = 0xc118694b + SIOCGLINKSTR = 0xc01c6987 + SIOCGLOWAT = 0x40047303 + SIOCGPGRP = 0x40047309 + SIOCGVH = 0xc0906983 + SIOCIFCREATE = 0x8090697a + SIOCIFDESTROY = 0x80906979 + SIOCIFGCLONERS = 0xc00c6978 + SIOCINITIFADDR = 0xc0446984 + SIOCSDRVSPEC = 0x801c697b + SIOCSETPFSYNC = 0x809069f7 + SIOCSHIWAT = 0x80047300 + SIOCSIFADDR = 0x8090690c + SIOCSIFADDRPREF = 0x8094691f + SIOCSIFBRDADDR = 0x80906913 + SIOCSIFCAP = 0x80206975 + SIOCSIFDSTADDR = 0x8090690e + SIOCSIFFLAGS = 0x80906910 + SIOCSIFGENERIC = 0x80906939 + SIOCSIFMEDIA = 0xc0906935 + SIOCSIFMETRIC = 0x80906918 + SIOCSIFMTU = 0x8090697f + SIOCSIFNETMASK = 0x80906916 + SIOCSIFPHYADDR = 0x80406946 + SIOCSLIFPHYADDR = 0x8118694a + SIOCSLINKSTR = 0x801c6988 + SIOCSLOWAT = 0x80047302 + SIOCSPGRP = 0x80047308 + SIOCSVH = 0xc0906982 + SIOCZIFDATA = 0xc0946986 + SOCK_CLOEXEC = 0x10000000 + SOCK_DGRAM = 0x2 + SOCK_FLAGS_MASK = 0xf0000000 + SOCK_NONBLOCK = 0x20000000 + SOCK_NOSIGPIPE = 0x40000000 + SOCK_RAW = 0x3 + SOCK_RDM = 0x4 + SOCK_SEQPACKET = 0x5 + SOCK_STREAM = 0x1 + SOL_SOCKET = 0xffff + SOMAXCONN = 0x80 + SO_ACCEPTCONN = 0x2 + SO_ACCEPTFILTER = 0x1000 + SO_BROADCAST = 0x20 + SO_DEBUG = 0x1 + SO_DONTROUTE = 0x10 + SO_ERROR = 0x1007 + SO_KEEPALIVE = 0x8 + SO_LINGER = 0x80 + SO_NOHEADER = 0x100a + SO_NOSIGPIPE = 0x800 + SO_OOBINLINE = 0x100 + SO_OVERFLOWED = 0x1009 + SO_RCVBUF = 0x1002 + SO_RCVLOWAT = 0x1004 + SO_RCVTIMEO = 0x100c + SO_REUSEADDR = 0x4 + SO_REUSEPORT = 0x200 + SO_SNDBUF = 0x1001 + SO_SNDLOWAT = 0x1003 + SO_SNDTIMEO = 0x100b + SO_TIMESTAMP = 0x2000 + SO_TYPE = 0x1008 + SO_USELOOPBACK = 0x40 + SYSCTL_VERSION = 0x1000000 + SYSCTL_VERS_0 = 0x0 + SYSCTL_VERS_1 = 0x1000000 + SYSCTL_VERS_MASK = 0xff000000 + S_ARCH1 = 0x10000 + S_ARCH2 = 0x20000 + S_BLKSIZE = 0x200 + S_IEXEC = 0x40 + S_IFBLK = 0x6000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFIFO = 0x1000 + S_IFLNK = 0xa000 + S_IFMT = 0xf000 + S_IFREG = 0x8000 + S_IFSOCK = 0xc000 + S_IFWHT = 0xe000 + S_IREAD = 0x100 + S_IRGRP = 0x20 + S_IROTH = 0x4 + S_IRUSR = 0x100 + S_IRWXG = 0x38 + S_IRWXO = 0x7 + S_IRWXU = 0x1c0 + S_ISGID = 0x400 + S_ISTXT = 0x200 + S_ISUID = 0x800 + S_ISVTX = 0x200 + S_IWGRP = 0x10 + S_IWOTH = 0x2 + S_IWRITE = 0x80 + S_IWUSR = 0x80 + S_IXGRP = 0x8 + S_IXOTH = 0x1 + S_IXUSR = 0x40 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 + TCP_CONGCTL = 0x20 + TCP_KEEPCNT = 0x6 + TCP_KEEPIDLE = 0x3 + TCP_KEEPINIT = 0x7 + TCP_KEEPINTVL = 0x5 + TCP_MAXBURST = 0x4 + TCP_MAXSEG = 0x2 + TCP_MAXWIN = 0xffff + TCP_MAX_WINSHIFT = 0xe + TCP_MD5SIG = 0x10 + TCP_MINMSS = 0xd8 + TCP_MSS = 0x218 + TCP_NODELAY = 0x1 + TCSAFLUSH = 0x2 + TIOCCBRK = 0x2000747a + TIOCCDTR = 0x20007478 + TIOCCONS = 0x80047462 + TIOCDCDTIMESTAMP = 0x400c7458 + TIOCDRAIN = 0x2000745e + TIOCEXCL = 0x2000740d + TIOCEXT = 0x80047460 + TIOCFLAG_CDTRCTS = 0x10 + TIOCFLAG_CLOCAL = 0x2 + TIOCFLAG_CRTSCTS = 0x4 + TIOCFLAG_MDMBUF = 0x8 + TIOCFLAG_SOFTCAR = 0x1 + TIOCFLUSH = 0x80047410 + TIOCGETA = 0x402c7413 + TIOCGETD = 0x4004741a + TIOCGFLAGS = 0x4004745d + TIOCGLINED = 0x40207442 + TIOCGPGRP = 0x40047477 + TIOCGQSIZE = 0x40047481 + TIOCGRANTPT = 0x20007447 + TIOCGSID = 0x40047463 + TIOCGSIZE = 0x40087468 + TIOCGWINSZ = 0x40087468 + TIOCMBIC = 0x8004746b + TIOCMBIS = 0x8004746c + TIOCMGET = 0x4004746a + TIOCMSET = 0x8004746d + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x20007471 + TIOCNXCL = 0x2000740e + TIOCOUTQ = 0x40047473 + TIOCPKT = 0x80047470 + TIOCPKT_DATA = 0x0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCPTMGET = 0x48087446 + TIOCPTSNAME = 0x48087448 + TIOCRCVFRAME = 0x80047445 + TIOCREMOTE = 0x80047469 + TIOCSBRK = 0x2000747b + TIOCSCTTY = 0x20007461 + TIOCSDTR = 0x20007479 + TIOCSETA = 0x802c7414 + TIOCSETAF = 0x802c7416 + TIOCSETAW = 0x802c7415 + TIOCSETD = 0x8004741b + TIOCSFLAGS = 0x8004745c + TIOCSIG = 0x2000745f + TIOCSLINED = 0x80207443 + TIOCSPGRP = 0x80047476 + TIOCSQSIZE = 0x80047480 + TIOCSSIZE = 0x80087467 + TIOCSTART = 0x2000746e + TIOCSTAT = 0x80047465 + TIOCSTI = 0x80017472 + TIOCSTOP = 0x2000746f + TIOCSWINSZ = 0x80087467 + TIOCUCNTL = 0x80047466 + TIOCXMTFRAME = 0x80047444 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VTIME = 0x11 + VWERASE = 0x4 + WALL = 0x8 + WALLSIG = 0x8 + WALTSIG = 0x4 + WCLONE = 0x4 + WCOREFLAG = 0x80 + WNOHANG = 0x1 + WNOWAIT = 0x10000 + WNOZOMBIE = 0x20000 + WOPTSCHECKED = 0x40000 + WSTOPPED = 0x7f + WUNTRACED = 0x2 +) + +// Errors +const ( + E2BIG = Errno(0x7) + EACCES = Errno(0xd) + EADDRINUSE = Errno(0x30) + EADDRNOTAVAIL = Errno(0x31) + EAFNOSUPPORT = Errno(0x2f) + EAGAIN = Errno(0x23) + EALREADY = Errno(0x25) + EAUTH = Errno(0x50) + EBADF = Errno(0x9) + EBADMSG = Errno(0x58) + EBADRPC = Errno(0x48) + EBUSY = Errno(0x10) + ECANCELED = Errno(0x57) + ECHILD = Errno(0xa) + ECONNABORTED = Errno(0x35) + ECONNREFUSED = Errno(0x3d) + ECONNRESET = Errno(0x36) + EDEADLK = Errno(0xb) + EDESTADDRREQ = Errno(0x27) + EDOM = Errno(0x21) + EDQUOT = Errno(0x45) + EEXIST = Errno(0x11) + EFAULT = Errno(0xe) + EFBIG = Errno(0x1b) + EFTYPE = Errno(0x4f) + EHOSTDOWN = Errno(0x40) + EHOSTUNREACH = Errno(0x41) + EIDRM = Errno(0x52) + EILSEQ = Errno(0x55) + EINPROGRESS = Errno(0x24) + EINTR = Errno(0x4) + EINVAL = Errno(0x16) + EIO = Errno(0x5) + EISCONN = Errno(0x38) + EISDIR = Errno(0x15) + ELAST = Errno(0x60) + ELOOP = Errno(0x3e) + EMFILE = Errno(0x18) + EMLINK = Errno(0x1f) + EMSGSIZE = Errno(0x28) + EMULTIHOP = Errno(0x5e) + ENAMETOOLONG = Errno(0x3f) + ENEEDAUTH = Errno(0x51) + ENETDOWN = Errno(0x32) + ENETRESET = Errno(0x34) + ENETUNREACH = Errno(0x33) + ENFILE = Errno(0x17) + ENOATTR = Errno(0x5d) + ENOBUFS = Errno(0x37) + ENODATA = Errno(0x59) + ENODEV = Errno(0x13) + ENOENT = Errno(0x2) + ENOEXEC = Errno(0x8) + ENOLCK = Errno(0x4d) + ENOLINK = Errno(0x5f) + ENOMEM = Errno(0xc) + ENOMSG = Errno(0x53) + ENOPROTOOPT = Errno(0x2a) + ENOSPC = Errno(0x1c) + ENOSR = Errno(0x5a) + ENOSTR = Errno(0x5b) + ENOSYS = Errno(0x4e) + ENOTBLK = Errno(0xf) + ENOTCONN = Errno(0x39) + ENOTDIR = Errno(0x14) + ENOTEMPTY = Errno(0x42) + ENOTSOCK = Errno(0x26) + ENOTSUP = Errno(0x56) + ENOTTY = Errno(0x19) + ENXIO = Errno(0x6) + EOPNOTSUPP = Errno(0x2d) + EOVERFLOW = Errno(0x54) + EPERM = Errno(0x1) + EPFNOSUPPORT = Errno(0x2e) + EPIPE = Errno(0x20) + EPROCLIM = Errno(0x43) + EPROCUNAVAIL = Errno(0x4c) + EPROGMISMATCH = Errno(0x4b) + EPROGUNAVAIL = Errno(0x4a) + EPROTO = Errno(0x60) + EPROTONOSUPPORT = Errno(0x2b) + EPROTOTYPE = Errno(0x29) + ERANGE = Errno(0x22) + EREMOTE = Errno(0x47) + EROFS = Errno(0x1e) + ERPCMISMATCH = Errno(0x49) + ESHUTDOWN = Errno(0x3a) + ESOCKTNOSUPPORT = Errno(0x2c) + ESPIPE = Errno(0x1d) + ESRCH = Errno(0x3) + ESTALE = Errno(0x46) + ETIME = Errno(0x5c) + ETIMEDOUT = Errno(0x3c) + ETOOMANYREFS = Errno(0x3b) + ETXTBSY = Errno(0x1a) + EUSERS = Errno(0x44) + EWOULDBLOCK = Errno(0x23) + EXDEV = Errno(0x12) +) + +// Signals +const ( + SIGABRT = Signal(0x6) + SIGALRM = Signal(0xe) + SIGBUS = Signal(0xa) + SIGCHLD = Signal(0x14) + SIGCONT = Signal(0x13) + SIGEMT = Signal(0x7) + SIGFPE = Signal(0x8) + SIGHUP = Signal(0x1) + SIGILL = Signal(0x4) + SIGINFO = Signal(0x1d) + SIGINT = Signal(0x2) + SIGIO = Signal(0x17) + SIGIOT = Signal(0x6) + SIGKILL = Signal(0x9) + SIGPIPE = Signal(0xd) + SIGPROF = Signal(0x1b) + SIGPWR = Signal(0x20) + SIGQUIT = Signal(0x3) + SIGSEGV = Signal(0xb) + SIGSTOP = Signal(0x11) + SIGSYS = Signal(0xc) + SIGTERM = Signal(0xf) + SIGTRAP = Signal(0x5) + SIGTSTP = Signal(0x12) + SIGTTIN = Signal(0x15) + SIGTTOU = Signal(0x16) + SIGURG = Signal(0x10) + SIGUSR1 = Signal(0x1e) + SIGUSR2 = Signal(0x1f) + SIGVTALRM = Signal(0x1a) + SIGWINCH = Signal(0x1c) + SIGXCPU = Signal(0x18) + SIGXFSZ = Signal(0x19) +) + +// Error table +var errors = [...]string{ + 1: "operation not permitted", + 2: "no such file or directory", + 3: "no such process", + 4: "interrupted system call", + 5: "input/output error", + 6: "device not configured", + 7: "argument list too long", + 8: "exec format error", + 9: "bad file descriptor", + 10: "no child processes", + 11: "resource deadlock avoided", + 12: "cannot allocate memory", + 13: "permission denied", + 14: "bad address", + 15: "block device required", + 16: "device busy", + 17: "file exists", + 18: "cross-device link", + 19: "operation not supported by device", + 20: "not a directory", + 21: "is a directory", + 22: "invalid argument", + 23: "too many open files in system", + 24: "too many open files", + 25: "inappropriate ioctl for device", + 26: "text file busy", + 27: "file too large", + 28: "no space left on device", + 29: "illegal seek", + 30: "read-only file system", + 31: "too many links", + 32: "broken pipe", + 33: "numerical argument out of domain", + 34: "result too large or too small", + 35: "resource temporarily unavailable", + 36: "operation now in progress", + 37: "operation already in progress", + 38: "socket operation on non-socket", + 39: "destination address required", + 40: "message too long", + 41: "protocol wrong type for socket", + 42: "protocol option not available", + 43: "protocol not supported", + 44: "socket type not supported", + 45: "operation not supported", + 46: "protocol family not supported", + 47: "address family not supported by protocol family", + 48: "address already in use", + 49: "can't assign requested address", + 50: "network is down", + 51: "network is unreachable", + 52: "network dropped connection on reset", + 53: "software caused connection abort", + 54: "connection reset by peer", + 55: "no buffer space available", + 56: "socket is already connected", + 57: "socket is not connected", + 58: "can't send after socket shutdown", + 59: "too many references: can't splice", + 60: "connection timed out", + 61: "connection refused", + 62: "too many levels of symbolic links", + 63: "file name too long", + 64: "host is down", + 65: "no route to host", + 66: "directory not empty", + 67: "too many processes", + 68: "too many users", + 69: "disc quota exceeded", + 70: "stale NFS file handle", + 71: "too many levels of remote in path", + 72: "RPC struct is bad", + 73: "RPC version wrong", + 74: "RPC prog. not avail", + 75: "program version wrong", + 76: "bad procedure for program", + 77: "no locks available", + 78: "function not implemented", + 79: "inappropriate file type or format", + 80: "authentication error", + 81: "need authenticator", + 82: "identifier removed", + 83: "no message of desired type", + 84: "value too large to be stored in data type", + 85: "illegal byte sequence", + 86: "not supported", + 87: "operation Canceled", + 88: "bad or Corrupt message", + 89: "no message available", + 90: "no STREAM resources", + 91: "not a STREAM", + 92: "STREAM ioctl timeout", + 93: "attribute not found", + 94: "multihop attempted", + 95: "link has been severed", + 96: "protocol error", +} + +// Signal table +var signals = [...]string{ + 1: "hangup", + 2: "interrupt", + 3: "quit", + 4: "illegal instruction", + 5: "trace/BPT trap", + 6: "abort trap", + 7: "EMT trap", + 8: "floating point exception", + 9: "killed", + 10: "bus error", + 11: "segmentation fault", + 12: "bad system call", + 13: "broken pipe", + 14: "alarm clock", + 15: "terminated", + 16: "urgent I/O condition", + 17: "stopped (signal)", + 18: "stopped", + 19: "continued", + 20: "child exited", + 21: "stopped (tty input)", + 22: "stopped (tty output)", + 23: "I/O possible", + 24: "cputime limit exceeded", + 25: "filesize limit exceeded", + 26: "virtual timer expired", + 27: "profiling timer expired", + 28: "window size changes", + 29: "information request", + 30: "user defined signal 1", + 31: "user defined signal 2", + 32: "power fail/restart", +} diff --git a/src/pkg/syscall/zerrors_openbsd_386.go b/src/pkg/syscall/zerrors_openbsd_386.go index 11074d5f5..5330d15b1 100644 --- a/src/pkg/syscall/zerrors_openbsd_386.go +++ b/src/pkg/syscall/zerrors_openbsd_386.go @@ -45,6 +45,29 @@ const ( ARPHRD_FRELAY = 0xf ARPHRD_IEEE1394 = 0x18 ARPHRD_IEEE802 = 0x6 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B9600 = 0x2580 BIOCFLUSH = 0x20004268 BIOCGBLEN = 0x40044266 BIOCGDIRFILT = 0x4004427c @@ -116,12 +139,28 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x8000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0xff + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a CTL_MAXNAME = 0xc CTL_NET = 0x4 + DIOCOSFPFLUSH = 0x2000444e DLT_ARCNET = 0x7 DLT_ATM_RFC1483 = 0xb DLT_AX25 = 0x3 DLT_CHAOS = 0x5 + DLT_C_HDLC = 0x68 DLT_EN10MB = 0x1 DLT_EN3MB = 0x2 DLT_ENC = 0xd @@ -137,6 +176,7 @@ const ( DLT_PPP = 0x9 DLT_PPP_BSDOS = 0x10 DLT_PPP_ETHER = 0x33 + DLT_PPP_SERIAL = 0x32 DLT_PRONET = 0x4 DLT_RAW = 0xe DLT_SLIP = 0x8 @@ -156,10 +196,6 @@ const ( ECHOKE = 0x1 ECHONL = 0x10 ECHOPRT = 0x20 - EFER_LMA = 0x400 - EFER_LME = 0x100 - EFER_NXE = 0x800 - EFER_SCE = 0x1 EMT_TAGOVF = 0x1 EMUL_ENABLED = 0x1 EMUL_NATIVE = 0x2 @@ -385,6 +421,7 @@ const ( EXTPROC = 0x800 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 + FLUSHO = 0x800000 F_DUPFD = 0x0 F_DUPFD_CLOEXEC = 0xa F_GETFD = 0x1 @@ -400,6 +437,12 @@ const ( F_SETOWN = 0x6 F_UNLCK = 0x2 F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 IFA_ROUTE = 0x1 IFF_ALLMULTI = 0x200 IFF_BROADCAST = 0x2 @@ -642,6 +685,12 @@ const ( IFT_X25MLP = 0x79 IFT_X25PLE = 0x28 IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 IN_CLASSA_HOST = 0xffffff IN_CLASSA_MAX = 0x80 IN_CLASSA_NET = 0xff000000 @@ -762,6 +811,7 @@ const ( IP_ESP_TRANS_LEVEL = 0x15 IP_HDRINCL = 0x2 IP_IPCOMP_LEVEL = 0x1d + IP_IPSECFLOWINFO = 0x24 IP_IPSEC_LOCAL_AUTH = 0x1b IP_IPSEC_LOCAL_CRED = 0x19 IP_IPSEC_LOCAL_ID = 0x17 @@ -796,17 +846,23 @@ const ( IP_RTABLE = 0x1021 IP_TOS = 0x3 IP_TTL = 0x4 + ISIG = 0x80 + ISTRIP = 0x20 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 + LCNT_OVERLOAD_FLUSH = 0x6 LOCK_EX = 0x2 LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 - MAP_ET_KVAGUARD = 0x10 MSG_BCAST = 0x100 MSG_CTRUNC = 0x20 MSG_DONTROUTE = 0x4 MSG_DONTWAIT = 0x80 MSG_EOR = 0x8 MSG_MCAST = 0x200 + MSG_NOSIGNAL = 0x400 MSG_OOB = 0x1 MSG_PEEK = 0x2 MSG_TRUNC = 0x10 @@ -818,6 +874,7 @@ const ( NET_RT_MAXID = 0x6 NET_RT_STATS = 0x4 NET_RT_TABLE = 0x5 + NOFLSH = 0x80000000 NOTE_ATTRIB = 0x8 NOTE_CHILD = 0x4 NOTE_DELETE = 0x1 @@ -836,6 +893,12 @@ const ( NOTE_TRACKERR = 0x2 NOTE_TRUNCATE = 0x80 NOTE_WRITE = 0x2 + OCRNL = 0x10 + ONLCR = 0x2 + ONLRET = 0x80 + ONOCR = 0x40 + ONOEOT = 0x8 + OPOST = 0x1 O_ACCMODE = 0x3 O_APPEND = 0x8 O_ASYNC = 0x40 @@ -857,6 +920,12 @@ const ( O_SYNC = 0x80 O_TRUNC = 0x400 O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 + PF_FLUSH = 0x1 + PT_MASK = 0x3ff000 RLIMIT_CORE = 0x4 RLIMIT_CPU = 0x0 RLIMIT_DATA = 0x2 @@ -893,10 +962,9 @@ const ( RTF_CLONING = 0x100 RTF_DONE = 0x40 RTF_DYNAMIC = 0x10 - RTF_FMASK = 0x8f808 + RTF_FMASK = 0xf808 RTF_GATEWAY = 0x2 RTF_HOST = 0x4 - RTF_JUMBO = 0x80000 RTF_LLINFO = 0x400 RTF_MASK = 0x80 RTF_MODIFIED = 0x20 @@ -942,7 +1010,6 @@ const ( RUSAGE_CHILDREN = -0x1 RUSAGE_SELF = 0x0 RUSAGE_THREAD = 0x1 - SCM_CREDS = 0x2 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x4 SHUT_RD = 0x0 @@ -996,7 +1063,6 @@ const ( SIOCGETPFSYNC = 0xc02069f8 SIOCGETSGCNT = 0xc0147534 SIOCGETVIFCNT = 0xc0147533 - SIOCGETVLANPRIO = 0xc0206990 SIOCGHIWAT = 0x40047301 SIOCGIFADDR = 0xc0206921 SIOCGIFASYNCMAP = 0xc020697c @@ -1034,7 +1100,6 @@ const ( SIOCSETLABEL = 0x80206999 SIOCSETPFLOW = 0x802069fd SIOCSETPFSYNC = 0x802069f7 - SIOCSETVLANPRIO = 0x8020698f SIOCSHIWAT = 0x80047300 SIOCSIFADDR = 0x8020690c SIOCSIFASYNCMAP = 0x8020697d @@ -1073,7 +1138,6 @@ const ( SO_DEBUG = 0x1 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 - SO_JUMBO = 0x400 SO_KEEPALIVE = 0x8 SO_LINGER = 0x80 SO_NETPROC = 0x1020 @@ -1092,6 +1156,9 @@ const ( SO_TIMESTAMP = 0x800 SO_TYPE = 0x1008 SO_USELOOPBACK = 0x40 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 TCP_MAXBURST = 0x4 TCP_MAXSEG = 0x2 TCP_MAXWIN = 0xffff @@ -1102,6 +1169,7 @@ const ( TCP_NODELAY = 0x1 TCP_NSTATES = 0xb TCP_SACK_ENABLE = 0x8 + TCSAFLUSH = 0x2 TIOCCBRK = 0x2000747a TIOCCDTR = 0x20007478 TIOCCONS = 0x80047462 @@ -1167,6 +1235,25 @@ const ( TIOCSTSTAMP = 0x8008745a TIOCSWINSZ = 0x80087467 TIOCUCNTL = 0x80047466 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VTIME = 0x11 + VWERASE = 0x4 WALTSIG = 0x4 WCONTINUED = 0x8 WCOREFLAG = 0x80 diff --git a/src/pkg/syscall/zerrors_openbsd_amd64.go b/src/pkg/syscall/zerrors_openbsd_amd64.go index 91ea06553..5a7edd341 100644 --- a/src/pkg/syscall/zerrors_openbsd_amd64.go +++ b/src/pkg/syscall/zerrors_openbsd_amd64.go @@ -45,6 +45,29 @@ const ( ARPHRD_FRELAY = 0xf ARPHRD_IEEE1394 = 0x18 ARPHRD_IEEE802 = 0x6 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B9600 = 0x2580 BIOCFLUSH = 0x20004268 BIOCGBLEN = 0x40044266 BIOCGDIRFILT = 0x4004427c @@ -116,12 +139,30 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 + BRKINT = 0x2 + CCR0_FLUSH = 0x10 + CFLUSH = 0xf + CLOCAL = 0x8000 + CPUID_CFLUSH = 0x80000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0xff + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a CTL_MAXNAME = 0xc CTL_NET = 0x4 + DIOCOSFPFLUSH = 0x2000444e DLT_ARCNET = 0x7 DLT_ATM_RFC1483 = 0xb DLT_AX25 = 0x3 DLT_CHAOS = 0x5 + DLT_C_HDLC = 0x68 DLT_EN10MB = 0x1 DLT_EN3MB = 0x2 DLT_ENC = 0xd @@ -137,6 +178,7 @@ const ( DLT_PPP = 0x9 DLT_PPP_BSDOS = 0x10 DLT_PPP_ETHER = 0x33 + DLT_PPP_SERIAL = 0x32 DLT_PRONET = 0x4 DLT_RAW = 0xe DLT_SLIP = 0x8 @@ -385,6 +427,7 @@ const ( EXTPROC = 0x800 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 + FLUSHO = 0x800000 F_DUPFD = 0x0 F_DUPFD_CLOEXEC = 0xa F_GETFD = 0x1 @@ -400,6 +443,12 @@ const ( F_SETOWN = 0x6 F_UNLCK = 0x2 F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 IFA_ROUTE = 0x1 IFF_ALLMULTI = 0x200 IFF_BROADCAST = 0x2 @@ -642,6 +691,12 @@ const ( IFT_X25MLP = 0x79 IFT_X25PLE = 0x28 IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 IN_CLASSA_HOST = 0xffffff IN_CLASSA_MAX = 0x80 IN_CLASSA_NET = 0xff000000 @@ -762,6 +817,7 @@ const ( IP_ESP_TRANS_LEVEL = 0x15 IP_HDRINCL = 0x2 IP_IPCOMP_LEVEL = 0x1d + IP_IPSECFLOWINFO = 0x24 IP_IPSEC_LOCAL_AUTH = 0x1b IP_IPSEC_LOCAL_CRED = 0x19 IP_IPSEC_LOCAL_ID = 0x17 @@ -796,17 +852,23 @@ const ( IP_RTABLE = 0x1021 IP_TOS = 0x3 IP_TTL = 0x4 + ISIG = 0x80 + ISTRIP = 0x20 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 + LCNT_OVERLOAD_FLUSH = 0x6 LOCK_EX = 0x2 LOCK_NB = 0x4 LOCK_SH = 0x1 LOCK_UN = 0x8 - MAP_ET_KVAGUARD = 0x10 MSG_BCAST = 0x100 MSG_CTRUNC = 0x20 MSG_DONTROUTE = 0x4 MSG_DONTWAIT = 0x80 MSG_EOR = 0x8 MSG_MCAST = 0x200 + MSG_NOSIGNAL = 0x400 MSG_OOB = 0x1 MSG_PEEK = 0x2 MSG_TRUNC = 0x10 @@ -818,6 +880,7 @@ const ( NET_RT_MAXID = 0x6 NET_RT_STATS = 0x4 NET_RT_TABLE = 0x5 + NOFLSH = 0x80000000 NOTE_ATTRIB = 0x8 NOTE_CHILD = 0x4 NOTE_DELETE = 0x1 @@ -836,6 +899,12 @@ const ( NOTE_TRACKERR = 0x2 NOTE_TRUNCATE = 0x80 NOTE_WRITE = 0x2 + OCRNL = 0x10 + ONLCR = 0x2 + ONLRET = 0x80 + ONOCR = 0x40 + ONOEOT = 0x8 + OPOST = 0x1 O_ACCMODE = 0x3 O_APPEND = 0x8 O_ASYNC = 0x40 @@ -857,6 +926,12 @@ const ( O_SYNC = 0x80 O_TRUNC = 0x400 O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 + PF_FLUSH = 0x1 + PMC5_PIPELINE_FLUSH = 0x15 RLIMIT_CORE = 0x4 RLIMIT_CPU = 0x0 RLIMIT_DATA = 0x2 @@ -893,10 +968,9 @@ const ( RTF_CLONING = 0x100 RTF_DONE = 0x40 RTF_DYNAMIC = 0x10 - RTF_FMASK = 0x8f808 + RTF_FMASK = 0xf808 RTF_GATEWAY = 0x2 RTF_HOST = 0x4 - RTF_JUMBO = 0x80000 RTF_LLINFO = 0x400 RTF_MASK = 0x80 RTF_MODIFIED = 0x20 @@ -942,7 +1016,6 @@ const ( RUSAGE_CHILDREN = -0x1 RUSAGE_SELF = 0x0 RUSAGE_THREAD = 0x1 - SCM_CREDS = 0x2 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x4 SHUT_RD = 0x0 @@ -996,7 +1069,6 @@ const ( SIOCGETPFSYNC = 0xc02069f8 SIOCGETSGCNT = 0xc0207534 SIOCGETVIFCNT = 0xc0287533 - SIOCGETVLANPRIO = 0xc0206990 SIOCGHIWAT = 0x40047301 SIOCGIFADDR = 0xc0206921 SIOCGIFASYNCMAP = 0xc020697c @@ -1034,7 +1106,6 @@ const ( SIOCSETLABEL = 0x80206999 SIOCSETPFLOW = 0x802069fd SIOCSETPFSYNC = 0x802069f7 - SIOCSETVLANPRIO = 0x8020698f SIOCSHIWAT = 0x80047300 SIOCSIFADDR = 0x8020690c SIOCSIFASYNCMAP = 0x8020697d @@ -1073,7 +1144,6 @@ const ( SO_DEBUG = 0x1 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 - SO_JUMBO = 0x400 SO_KEEPALIVE = 0x8 SO_LINGER = 0x80 SO_NETPROC = 0x1020 @@ -1092,6 +1162,9 @@ const ( SO_TIMESTAMP = 0x800 SO_TYPE = 0x1008 SO_USELOOPBACK = 0x40 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 TCP_MAXBURST = 0x4 TCP_MAXSEG = 0x2 TCP_MAXWIN = 0xffff @@ -1102,6 +1175,7 @@ const ( TCP_NODELAY = 0x1 TCP_NSTATES = 0xb TCP_SACK_ENABLE = 0x8 + TCSAFLUSH = 0x2 TIOCCBRK = 0x2000747a TIOCCDTR = 0x20007478 TIOCCONS = 0x80047462 @@ -1167,6 +1241,25 @@ const ( TIOCSTSTAMP = 0x8008745a TIOCSWINSZ = 0x80087467 TIOCUCNTL = 0x80047466 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VTIME = 0x11 + VWERASE = 0x4 WALTSIG = 0x4 WCONTINUED = 0x8 WCOREFLAG = 0x80 diff --git a/src/pkg/syscall/zerrors_plan9_386.go b/src/pkg/syscall/zerrors_plan9_386.go index e7c993c0f..ede3d6a32 100644 --- a/src/pkg/syscall/zerrors_plan9_386.go +++ b/src/pkg/syscall/zerrors_plan9_386.go @@ -4,8 +4,6 @@ package syscall -import "errors" - // Constants const ( // Invented values to support what package os expects. @@ -24,19 +22,27 @@ const ( S_IFREG = 0x8000 S_IFLNK = 0xa000 S_IFSOCK = 0xc000 - - SIGINT = Signal(0x2) - SIGKILL = Signal(0x9) ) // Errors var ( - EINVAL = errors.New("bad arg in system call") - ENOTDIR = errors.New("not a directory") - ENOENT = errors.New("file does not exist") - EEXIST = errors.New("file already exists") - EIO = errors.New("i/o error") - ENAMETOOLONG = errors.New("file name too long") - EPERM = errors.New("permission denied") - EPLAN9 = errors.New("not supported by plan 9") + EINVAL = NewError("bad arg in system call") + ENOTDIR = NewError("not a directory") + EISDIR = NewError("file is a directory") + ENOENT = NewError("file does not exist") + EEXIST = NewError("file already exists") + EMFILE = NewError("no free file descriptors") + EIO = NewError("i/o error") + ENAMETOOLONG = NewError("file name too long") + EINTR = NewError("interrupted") + EPERM = NewError("permission denied") + EBUSY = NewError("no free devices") + ETIMEDOUT = NewError("connection timed out") + EPLAN9 = NewError("not supported by plan 9") + + // The following errors do not correspond to any + // Plan 9 system messages. Invented to support + // what package os and others expect. + EACCES = NewError("access permission denied") + EAFNOSUPPORT = NewError("address family not supported by protocol") ) diff --git a/src/pkg/syscall/zerrors_plan9_amd64.go b/src/pkg/syscall/zerrors_plan9_amd64.go new file mode 100644 index 000000000..ede3d6a32 --- /dev/null +++ b/src/pkg/syscall/zerrors_plan9_amd64.go @@ -0,0 +1,48 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +// Constants +const ( + // Invented values to support what package os expects. + O_CREAT = 0x02000 + O_APPEND = 0x00400 + O_NOCTTY = 0x00000 + O_NONBLOCK = 0x00000 + O_SYNC = 0x00000 + O_ASYNC = 0x00000 + + S_IFMT = 0x1f000 + S_IFIFO = 0x1000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFBLK = 0x6000 + S_IFREG = 0x8000 + S_IFLNK = 0xa000 + S_IFSOCK = 0xc000 +) + +// Errors +var ( + EINVAL = NewError("bad arg in system call") + ENOTDIR = NewError("not a directory") + EISDIR = NewError("file is a directory") + ENOENT = NewError("file does not exist") + EEXIST = NewError("file already exists") + EMFILE = NewError("no free file descriptors") + EIO = NewError("i/o error") + ENAMETOOLONG = NewError("file name too long") + EINTR = NewError("interrupted") + EPERM = NewError("permission denied") + EBUSY = NewError("no free devices") + ETIMEDOUT = NewError("connection timed out") + EPLAN9 = NewError("not supported by plan 9") + + // The following errors do not correspond to any + // Plan 9 system messages. Invented to support + // what package os and others expect. + EACCES = NewError("access permission denied") + EAFNOSUPPORT = NewError("address family not supported by protocol") +) diff --git a/src/pkg/syscall/zsyscall_darwin_386.go b/src/pkg/syscall/zsyscall_darwin_386.go index b952ac47c..2f99b7f6c 100644 --- a/src/pkg/syscall/zsyscall_darwin_386.go +++ b/src/pkg/syscall/zsyscall_darwin_386.go @@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func utimes(path string, timeval *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) if e1 != 0 { err = e1 } @@ -286,7 +291,12 @@ func kill(pid int, signum int, posix int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -306,7 +316,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -316,7 +331,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -326,7 +346,12 @@ func Chflags(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -336,7 +361,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -346,7 +376,12 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -387,7 +422,17 @@ func Dup2(from int, to int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Exchangedata(path1 string, path2 string, options int) (err error) { - _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(StringBytePtr(path1))), uintptr(unsafe.Pointer(StringBytePtr(path2))), uintptr(options)) + var _p0 *byte + _p0, err = BytePtrFromString(path1) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(path2) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options)) if e1 != 0 { err = e1 } @@ -414,7 +459,12 @@ func Fchdir(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -675,7 +725,12 @@ func Kqueue() (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -685,7 +740,17 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -705,7 +770,12 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -715,7 +785,12 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -725,7 +800,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkfifo(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -735,7 +815,12 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -745,7 +830,12 @@ func Mknod(path string, mode uint32, dev int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -756,7 +846,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Pathconf(path string, name int) (val int, err error) { - r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) val = int(r0) if e1 != 0 { err = e1 @@ -800,7 +895,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -818,13 +913,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -835,7 +935,17 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rename(from string, to string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -845,7 +955,12 @@ func Rename(from string, to string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Revoke(path string) (err error) { - _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -855,7 +970,12 @@ func Revoke(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -916,7 +1036,12 @@ func Setgid(gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setlogin(name string) (err error) { - _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1017,7 +1142,12 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1027,7 +1157,12 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Statfs(path string, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1037,7 +1172,17 @@ func Statfs(path string, stat *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -1057,7 +1202,12 @@ func Sync() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), uintptr(length>>32)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32)) if e1 != 0 { err = e1 } @@ -1075,7 +1225,12 @@ func Umask(newmask int) (oldmask int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Undelete(path string) (err error) { - _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1085,7 +1240,12 @@ func Undelete(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1095,7 +1255,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -1104,7 +1269,7 @@ func Unmount(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1142,7 +1307,7 @@ func munmap(addr uintptr, length uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, buf *byte, nbuf int) (n int, err error) { +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { @@ -1153,7 +1318,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, buf *byte, nbuf int) (n int, err error) { +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_darwin_amd64.go b/src/pkg/syscall/zsyscall_darwin_amd64.go index 104ee5513..0f08df4c5 100644 --- a/src/pkg/syscall/zsyscall_darwin_amd64.go +++ b/src/pkg/syscall/zsyscall_darwin_amd64.go @@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func utimes(path string, timeval *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) if e1 != 0 { err = e1 } @@ -286,7 +291,12 @@ func kill(pid int, signum int, posix int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -306,7 +316,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -316,7 +331,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -326,7 +346,12 @@ func Chflags(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -336,7 +361,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -346,7 +376,12 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -387,7 +422,17 @@ func Dup2(from int, to int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Exchangedata(path1 string, path2 string, options int) (err error) { - _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(StringBytePtr(path1))), uintptr(unsafe.Pointer(StringBytePtr(path2))), uintptr(options)) + var _p0 *byte + _p0, err = BytePtrFromString(path1) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(path2) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options)) if e1 != 0 { err = e1 } @@ -414,7 +459,12 @@ func Fchdir(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -675,7 +725,12 @@ func Kqueue() (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -685,7 +740,17 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -705,7 +770,12 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -715,7 +785,12 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -725,7 +800,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkfifo(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -735,7 +815,12 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -745,7 +830,12 @@ func Mknod(path string, mode uint32, dev int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -756,7 +846,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Pathconf(path string, name int) (val int, err error) { - r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) val = int(r0) if e1 != 0 { err = e1 @@ -800,7 +895,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -818,13 +913,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -835,7 +935,17 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rename(from string, to string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -845,7 +955,12 @@ func Rename(from string, to string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Revoke(path string) (err error) { - _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -855,7 +970,12 @@ func Revoke(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -916,7 +1036,12 @@ func Setgid(gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setlogin(name string) (err error) { - _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1017,7 +1142,12 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1027,7 +1157,12 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Statfs(path string, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1037,7 +1172,17 @@ func Statfs(path string, stat *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -1057,7 +1202,12 @@ func Sync() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0) if e1 != 0 { err = e1 } @@ -1075,7 +1225,12 @@ func Umask(newmask int) (oldmask int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Undelete(path string) (err error) { - _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1085,7 +1240,12 @@ func Undelete(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1095,7 +1255,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -1104,7 +1269,7 @@ func Unmount(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1142,7 +1307,7 @@ func munmap(addr uintptr, length uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, buf *byte, nbuf int) (n int, err error) { +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { @@ -1153,7 +1318,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, buf *byte, nbuf int) (n int, err error) { +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_freebsd_386.go b/src/pkg/syscall/zsyscall_freebsd_386.go index 455575467..5ca0bcb16 100644 --- a/src/pkg/syscall/zsyscall_freebsd_386.go +++ b/src/pkg/syscall/zsyscall_freebsd_386.go @@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func utimes(path string, timeval *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) if e1 != 0 { err = e1 } @@ -266,7 +271,12 @@ func pipe() (r int, w int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -286,7 +296,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -296,7 +311,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -306,7 +326,12 @@ func Chflags(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -316,7 +341,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -326,7 +356,12 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -384,7 +419,12 @@ func Fchdir(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -665,7 +705,12 @@ func Kqueue() (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -675,7 +720,17 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -695,7 +750,12 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -705,7 +765,12 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -715,7 +780,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkfifo(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -725,7 +795,12 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -745,7 +820,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -756,7 +836,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Pathconf(path string, name int) (val int, err error) { - r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) val = int(r0) if e1 != 0 { err = e1 @@ -800,7 +885,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -818,13 +903,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -835,7 +925,17 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rename(from string, to string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -845,7 +945,12 @@ func Rename(from string, to string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Revoke(path string) (err error) { - _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -855,7 +960,12 @@ func Revoke(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -916,7 +1026,12 @@ func Setgid(gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setlogin(name string) (err error) { - _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1007,7 +1122,12 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1017,7 +1137,12 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Statfs(path string, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1027,7 +1152,17 @@ func Statfs(path string, stat *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -1047,7 +1182,12 @@ func Sync() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), uintptr(length>>32)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32)) if e1 != 0 { err = e1 } @@ -1065,7 +1205,12 @@ func Umask(newmask int) (oldmask int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Undelete(path string) (err error) { - _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1075,7 +1220,12 @@ func Undelete(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1085,7 +1235,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -1094,7 +1249,7 @@ func Unmount(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1132,7 +1287,7 @@ func munmap(addr uintptr, length uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, buf *byte, nbuf int) (n int, err error) { +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { @@ -1143,7 +1298,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, buf *byte, nbuf int) (n int, err error) { +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_freebsd_amd64.go b/src/pkg/syscall/zsyscall_freebsd_amd64.go index 33b71b9eb..260677473 100644 --- a/src/pkg/syscall/zsyscall_freebsd_amd64.go +++ b/src/pkg/syscall/zsyscall_freebsd_amd64.go @@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func utimes(path string, timeval *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) if e1 != 0 { err = e1 } @@ -266,7 +271,12 @@ func pipe() (r int, w int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -286,7 +296,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -296,7 +311,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -306,7 +326,12 @@ func Chflags(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -316,7 +341,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -326,7 +356,12 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -384,7 +419,12 @@ func Fchdir(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -665,7 +705,12 @@ func Kqueue() (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -675,7 +720,17 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -695,7 +750,12 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -705,7 +765,12 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -715,7 +780,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkfifo(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -725,7 +795,12 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -745,7 +820,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -756,7 +836,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Pathconf(path string, name int) (val int, err error) { - r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) val = int(r0) if e1 != 0 { err = e1 @@ -800,7 +885,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -818,13 +903,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -835,7 +925,17 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rename(from string, to string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -845,7 +945,12 @@ func Rename(from string, to string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Revoke(path string) (err error) { - _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -855,7 +960,12 @@ func Revoke(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -916,7 +1026,12 @@ func Setgid(gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setlogin(name string) (err error) { - _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1007,7 +1122,12 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1017,7 +1137,12 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Statfs(path string, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1027,7 +1152,17 @@ func Statfs(path string, stat *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -1047,7 +1182,12 @@ func Sync() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0) if e1 != 0 { err = e1 } @@ -1065,7 +1205,12 @@ func Umask(newmask int) (oldmask int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Undelete(path string) (err error) { - _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1075,7 +1220,12 @@ func Undelete(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1085,7 +1235,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -1094,7 +1249,7 @@ func Unmount(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1132,7 +1287,7 @@ func munmap(addr uintptr, length uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, buf *byte, nbuf int) (n int, err error) { +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { @@ -1143,7 +1298,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, buf *byte, nbuf int) (n int, err error) { +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_freebsd_arm.go b/src/pkg/syscall/zsyscall_freebsd_arm.go new file mode 100644 index 000000000..925b83fbb --- /dev/null +++ b/src/pkg/syscall/zsyscall_freebsd_arm.go @@ -0,0 +1,1308 @@ +// mksyscall.pl -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import "unsafe" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getgroups(ngid int, gid *_Gid_t) (n int, err error) { + r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setgroups(ngid int, gid *_Gid_t) (err error) { + _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) { + r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0) + wpid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { + r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func bind(s int, addr uintptr, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func connect(s int, addr uintptr, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socket(domain int, typ int, proto int) (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) { + _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (err error) { + _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Shutdown(s int, how int) (err error) { + _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { + _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (err error) { + _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, err error) { + r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimes(path string, timeval *[2]Timeval) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func futimes(fd int, timeval *[2]Timeval) (err error) { + _, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntl(fd int, cmd int, arg int) (val int, err error) { + r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg)) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pipe() (r int, w int, err error) { + r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) + r = int(r0) + w = int(r1) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Access(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { + _, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chflags(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chmod(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chown(path string, uid int, gid int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chroot(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Close(fd int) (err error) { + _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup(fd int) (nfd int, err error) { + r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0) + nfd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup2(from int, to int) (err error) { + _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Exit(code int) { + Syscall(SYS_EXIT, uintptr(code), 0, 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchdir(fd int) (err error) { + _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchflags(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchmod(fd int, mode uint32) (err error) { + _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchown(fd int, uid int, gid int) (err error) { + _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Flock(fd int, how int) (err error) { + _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fpathconf(fd int, name int) (val int, err error) { + r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstat(fd int, stat *Stat_t) (err error) { + _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstatfs(fd int, stat *Statfs_t) (err error) { + _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fsync(fd int) (err error) { + _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Ftruncate(fd int, length int64) (err error) { + _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), uintptr(length>>32)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getdtablesize() (size int) { + r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0) + size = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getegid() (egid int) { + r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0) + egid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Geteuid() (uid int) { + r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), uintptr(len(buf)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getgid() (gid int) { + r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgid(pid int) (pgid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0) + pgid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgrp() (pgrp int) { + r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0) + pgrp = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpid() (pid int) { + r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) + pid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getppid() (ppid int) { + r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0) + ppid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpriority(which int, who int) (prio int, err error) { + r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0) + prio = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrlimit(which int, lim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrusage(who int, rusage *Rusage) (err error) { + _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getsid(pid int) (sid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0) + sid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Gettimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getuid() (uid int) { + r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Issetugid() (tainted bool) { + r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0) + tainted = bool(r0 != 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Kill(pid int, signum Signal) (err error) { + _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Kqueue() (fd int, err error) { + r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Lchown(path string, uid int, gid int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Link(path string, link string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listen(s int, backlog int) (err error) { + _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Lstat(path string, stat *Stat_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkdir(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkfifo(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mknod(path string, mode uint32, dev int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Nanosleep(time *Timespec, leftover *Timespec) (err error) { + _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Open(path string, mode int, perm uint32) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pathconf(path string, name int) (val int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pread(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Readlink(path string, buf []byte) (n int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(buf) > 0 { + _p1 = unsafe.Pointer(&buf[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Rename(from string, to string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Revoke(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Rmdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { + r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(whence), 0, 0) + newoffset = int64(int64(r1)<<32 | int64(r0)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) { + _, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setegid(egid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seteuid(euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setgid(gid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setlogin(name string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpgid(pid int, pgid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpriority(which int, who int, prio int) (err error) { + _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setregid(rgid int, egid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setreuid(ruid int, euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setrlimit(which int, lim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setsid() (pid int, err error) { + r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) + pid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Settimeofday(tp *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setuid(uid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Stat(path string, stat *Stat_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Statfs(path string, stat *Statfs_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Symlink(path string, link string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Sync() (err error) { + _, _, e1 := Syscall(SYS_SYNC, 0, 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Truncate(path string, length int64) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Umask(newmask int) (oldmask int) { + r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0) + oldmask = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Undelete(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unlink(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unmount(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) { + r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos), uintptr(pos>>32), 0, 0) + ret = uintptr(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func munmap(addr uintptr, length uintptr) (err error) { + _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func read(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func write(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} diff --git a/src/pkg/syscall/zsyscall_linux_386.go b/src/pkg/syscall/zsyscall_linux_386.go index 1752fe462..3f0470c98 100644 --- a/src/pkg/syscall/zsyscall_linux_386.go +++ b/src/pkg/syscall/zsyscall_linux_386.go @@ -8,7 +8,12 @@ import "unsafe" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -19,7 +24,12 @@ func open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { - r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), uintptr(mode), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0) fd = int(r0) if e1 != 0 { err = e1 @@ -39,8 +49,38 @@ func pipe(p *[2]_C_int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimes(path string, times *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimensat(dirfd int, path string, times *[2]Timespec) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times))) if e1 != 0 { err = e1 } @@ -98,7 +138,12 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) { - _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(arg) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -108,7 +153,22 @@ func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) { - _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(data)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(source) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(target) + if err != nil { + return + } + var _p2 *byte + _p2, err = BytePtrFromString(fstype) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0) if e1 != 0 { err = e1 } @@ -118,7 +178,12 @@ func mount(source string, target string, fstype string, flags uintptr, data *byt // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -128,7 +193,12 @@ func Access(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Acct(path string) (err error) { - _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -149,7 +219,12 @@ func Adjtimex(buf *Timex) (state int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -159,7 +234,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -169,7 +249,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -189,7 +274,12 @@ func Close(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Creat(path string, mode uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) fd = int(r0) if e1 != 0 { err = e1 @@ -277,7 +367,12 @@ func Exit(code int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) if e1 != 0 { err = e1 } @@ -317,7 +412,12 @@ func Fchmod(fd int, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) if e1 != 0 { err = e1 } @@ -327,7 +427,12 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -429,16 +534,6 @@ func Getppid() (ppid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Getrlimit(resource int, rlim *Rlimit) (err error) { - _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) - if e1 != 0 { - err = e1 - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getrusage(who int, rusage *Rusage) (err error) { _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) if e1 != 0 { @@ -457,8 +552,40 @@ func Gettid() (tid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Getxattr(path string, attr string, dest []byte) (sz int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p2 unsafe.Pointer + if len(dest) > 0 { + _p2 = unsafe.Pointer(&dest[0]) + } else { + _p2 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0) + sz = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) { - r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(StringBytePtr(pathname))), uintptr(mask)) + var _p0 *byte + _p0, err = BytePtrFromString(pathname) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask)) watchdesc = int(r0) if e1 != 0 { err = e1 @@ -529,7 +656,39 @@ func Klogctl(typ int, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listxattr(path string, dest []byte) (sz int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(dest) > 0 { + _p1 = unsafe.Pointer(&dest[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest))) + sz = int(r0) if e1 != 0 { err = e1 } @@ -539,7 +698,12 @@ func Link(oldpath string, newpath string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -549,7 +713,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdirat(dirfd int, path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode)) if e1 != 0 { err = e1 } @@ -559,7 +728,12 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -569,7 +743,12 @@ func Mknod(path string, mode uint32, dev int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) if e1 != 0 { err = e1 } @@ -599,7 +778,17 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func PivotRoot(newroot string, putold string) (err error) { - _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(StringBytePtr(newroot))), uintptr(unsafe.Pointer(StringBytePtr(putold))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(newroot) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(putold) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -608,7 +797,17 @@ func PivotRoot(newroot string, putold string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { + _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -626,13 +825,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -642,8 +846,38 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Removexattr(path string, attr string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Rename(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -653,7 +887,17 @@ func Rename(oldpath string, newpath string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) { - _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(newdirfd), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0) if e1 != 0 { err = e1 } @@ -663,7 +907,12 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -714,8 +963,9 @@ func Setpgid(pid int, pgid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setrlimit(resource int, rlim *Rlimit) (err error) { - _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) +func Setsid() (pid int, err error) { + r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) + pid = int(r0) if e1 != 0 { err = e1 } @@ -724,9 +974,8 @@ func Setrlimit(resource int, rlim *Rlimit) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setsid() (pid int, err error) { - r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) - pid = int(r0) +func Settimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) if e1 != 0 { err = e1 } @@ -735,8 +984,8 @@ func Setsid() (pid int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Settimeofday(tv *Timeval) (err error) { - _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) +func Setuid(uid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) if e1 != 0 { err = e1 } @@ -745,8 +994,24 @@ func Settimeofday(tv *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setuid(uid int) (err error) { - _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) +func Setxattr(path string, attr string, data []byte, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p2 unsafe.Pointer + if len(data) > 0 { + _p2 = unsafe.Pointer(&data[0]) + } else { + _p2 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -756,7 +1021,17 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -833,7 +1108,12 @@ func Uname(buf *Utsname) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -843,7 +1123,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlinkat(dirfd int, path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), 0) if e1 != 0 { err = e1 } @@ -853,7 +1138,12 @@ func Unlinkat(dirfd int, path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(target string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(target) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -883,7 +1173,12 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Utime(path string, buf *Utimbuf) (err error) { - _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0) if e1 != 0 { err = e1 } @@ -892,7 +1187,7 @@ func Utime(path string, buf *Utimbuf) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -919,7 +1214,7 @@ func exitThread(code int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, p *byte, np int) (n int, err error) { +func readlen(fd int, p *byte, np int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)) n = int(r0) if e1 != 0 { @@ -930,7 +1225,7 @@ func read(fd int, p *byte, np int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, p *byte, np int) (n int, err error) { +func writelen(fd int, p *byte, np int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)) n = int(r0) if e1 != 0 { @@ -1036,7 +1331,12 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1128,7 +1428,12 @@ func Iopl(level int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1138,7 +1443,12 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1181,7 +1491,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { r0, _, e1 := Syscall6(SYS_SENDFILE64, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0) written = int(r0) if e1 != 0 { @@ -1274,7 +1584,12 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1294,7 +1609,12 @@ func SyncFileRange(fd int, off int64, n int64, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall(SYS_TRUNCATE64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), uintptr(length>>32)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE64, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32)) if e1 != 0 { err = e1 } @@ -1346,6 +1666,26 @@ func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getrlimit(resource int, rlim *rlimit32) (err error) { + _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setrlimit(resource int, rlim *rlimit32) (err error) { + _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Gettimeofday(tv *Timeval) (err error) { _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_linux_amd64.go b/src/pkg/syscall/zsyscall_linux_amd64.go index d27346f7f..43f24e773 100644 --- a/src/pkg/syscall/zsyscall_linux_amd64.go +++ b/src/pkg/syscall/zsyscall_linux_amd64.go @@ -8,7 +8,12 @@ import "unsafe" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -19,7 +24,12 @@ func open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { - r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), uintptr(mode), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0) fd = int(r0) if e1 != 0 { err = e1 @@ -39,8 +49,38 @@ func pipe(p *[2]_C_int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimes(path string, times *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimensat(dirfd int, path string, times *[2]Timespec) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times))) if e1 != 0 { err = e1 } @@ -98,7 +138,12 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) { - _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(arg) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -108,7 +153,22 @@ func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) { - _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(data)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(source) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(target) + if err != nil { + return + } + var _p2 *byte + _p2, err = BytePtrFromString(fstype) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0) if e1 != 0 { err = e1 } @@ -118,7 +178,12 @@ func mount(source string, target string, fstype string, flags uintptr, data *byt // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -128,7 +193,12 @@ func Access(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Acct(path string) (err error) { - _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -149,7 +219,12 @@ func Adjtimex(buf *Timex) (state int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -159,7 +234,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -169,7 +249,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -189,7 +274,12 @@ func Close(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Creat(path string, mode uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) fd = int(r0) if e1 != 0 { err = e1 @@ -277,7 +367,12 @@ func Exit(code int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) if e1 != 0 { err = e1 } @@ -317,7 +412,12 @@ func Fchmod(fd int, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) if e1 != 0 { err = e1 } @@ -327,7 +427,12 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -429,16 +534,6 @@ func Getppid() (ppid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Getrlimit(resource int, rlim *Rlimit) (err error) { - _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) - if e1 != 0 { - err = e1 - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getrusage(who int, rusage *Rusage) (err error) { _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) if e1 != 0 { @@ -457,8 +552,40 @@ func Gettid() (tid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Getxattr(path string, attr string, dest []byte) (sz int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p2 unsafe.Pointer + if len(dest) > 0 { + _p2 = unsafe.Pointer(&dest[0]) + } else { + _p2 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0) + sz = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) { - r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(StringBytePtr(pathname))), uintptr(mask)) + var _p0 *byte + _p0, err = BytePtrFromString(pathname) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask)) watchdesc = int(r0) if e1 != 0 { err = e1 @@ -529,7 +656,39 @@ func Klogctl(typ int, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listxattr(path string, dest []byte) (sz int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(dest) > 0 { + _p1 = unsafe.Pointer(&dest[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest))) + sz = int(r0) if e1 != 0 { err = e1 } @@ -539,7 +698,12 @@ func Link(oldpath string, newpath string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -549,7 +713,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdirat(dirfd int, path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode)) if e1 != 0 { err = e1 } @@ -559,7 +728,12 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -569,7 +743,12 @@ func Mknod(path string, mode uint32, dev int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) if e1 != 0 { err = e1 } @@ -599,7 +778,17 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func PivotRoot(newroot string, putold string) (err error) { - _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(StringBytePtr(newroot))), uintptr(unsafe.Pointer(StringBytePtr(putold))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(newroot) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(putold) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -608,7 +797,17 @@ func PivotRoot(newroot string, putold string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { + _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -626,13 +825,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -642,8 +846,38 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Removexattr(path string, attr string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Rename(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -653,7 +887,17 @@ func Rename(oldpath string, newpath string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) { - _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(newdirfd), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0) if e1 != 0 { err = e1 } @@ -663,7 +907,12 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -714,8 +963,9 @@ func Setpgid(pid int, pgid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setrlimit(resource int, rlim *Rlimit) (err error) { - _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) +func Setsid() (pid int, err error) { + r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) + pid = int(r0) if e1 != 0 { err = e1 } @@ -724,9 +974,8 @@ func Setrlimit(resource int, rlim *Rlimit) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setsid() (pid int, err error) { - r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) - pid = int(r0) +func Settimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) if e1 != 0 { err = e1 } @@ -735,8 +984,8 @@ func Setsid() (pid int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Settimeofday(tv *Timeval) (err error) { - _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) +func Setuid(uid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) if e1 != 0 { err = e1 } @@ -745,8 +994,24 @@ func Settimeofday(tv *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setuid(uid int) (err error) { - _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) +func Setxattr(path string, attr string, data []byte, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p2 unsafe.Pointer + if len(data) > 0 { + _p2 = unsafe.Pointer(&data[0]) + } else { + _p2 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -756,7 +1021,17 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -833,7 +1108,12 @@ func Uname(buf *Utsname) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -843,7 +1123,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlinkat(dirfd int, path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), 0) if e1 != 0 { err = e1 } @@ -853,7 +1138,12 @@ func Unlinkat(dirfd int, path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(target string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(target) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -883,7 +1173,12 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Utime(path string, buf *Utimbuf) (err error) { - _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0) if e1 != 0 { err = e1 } @@ -892,7 +1187,7 @@ func Utime(path string, buf *Utimbuf) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -919,7 +1214,7 @@ func exitThread(code int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, p *byte, np int) (n int, err error) { +func readlen(fd int, p *byte, np int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)) n = int(r0) if e1 != 0 { @@ -930,7 +1225,7 @@ func read(fd int, p *byte, np int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, p *byte, np int) (n int, err error) { +func writelen(fd int, p *byte, np int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)) n = int(r0) if e1 != 0 { @@ -1036,7 +1331,12 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1109,6 +1409,16 @@ func Getgid() (gid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Getrlimit(resource int, rlim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Getuid() (uid int) { r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0) uid = int(r0) @@ -1138,7 +1448,12 @@ func Iopl(level int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1158,7 +1473,12 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1223,7 +1543,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { r0, _, e1 := Syscall6(SYS_SENDFILE, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0) written = int(r0) if e1 != 0 { @@ -1294,6 +1614,16 @@ func Setresuid(ruid int, euid int, suid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Setrlimit(resource int, rlim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Setreuid(ruid int, euid int) (err error) { _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0) if e1 != 0 { @@ -1326,7 +1656,12 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1336,7 +1671,12 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Statfs(path string, buf *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0) if e1 != 0 { err = e1 } @@ -1356,7 +1696,12 @@ func SyncFileRange(fd int, off int64, n int64, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0) if e1 != 0 { err = e1 } @@ -1376,6 +1721,17 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { + r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func bind(s int, addr uintptr, addrlen _Socklen) (err error) { _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) if e1 != 0 { @@ -1448,7 +1804,7 @@ func socket(domain int, typ int, proto int) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 diff --git a/src/pkg/syscall/zsyscall_linux_arm.go b/src/pkg/syscall/zsyscall_linux_arm.go index b1a59a69e..804821eb3 100644 --- a/src/pkg/syscall/zsyscall_linux_arm.go +++ b/src/pkg/syscall/zsyscall_linux_arm.go @@ -8,7 +8,12 @@ import "unsafe" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -19,7 +24,12 @@ func open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { - r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), uintptr(mode), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0) fd = int(r0) if e1 != 0 { err = e1 @@ -39,8 +49,38 @@ func pipe(p *[2]_C_int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimes(path string, times *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimensat(dirfd int, path string, times *[2]Timespec) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times))) if e1 != 0 { err = e1 } @@ -98,7 +138,12 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) { - _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(arg) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -108,7 +153,22 @@ func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) { - _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(data)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(source) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(target) + if err != nil { + return + } + var _p2 *byte + _p2, err = BytePtrFromString(fstype) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0) if e1 != 0 { err = e1 } @@ -118,7 +178,12 @@ func mount(source string, target string, fstype string, flags uintptr, data *byt // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -128,7 +193,12 @@ func Access(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Acct(path string) (err error) { - _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -149,7 +219,12 @@ func Adjtimex(buf *Timex) (state int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -159,7 +234,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -169,7 +249,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -189,7 +274,12 @@ func Close(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Creat(path string, mode uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) fd = int(r0) if e1 != 0 { err = e1 @@ -277,7 +367,12 @@ func Exit(code int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) if e1 != 0 { err = e1 } @@ -317,7 +412,12 @@ func Fchmod(fd int, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) if e1 != 0 { err = e1 } @@ -327,7 +427,12 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { - _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -429,16 +534,6 @@ func Getppid() (ppid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Getrlimit(resource int, rlim *Rlimit) (err error) { - _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) - if e1 != 0 { - err = e1 - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getrusage(who int, rusage *Rusage) (err error) { _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) if e1 != 0 { @@ -457,8 +552,40 @@ func Gettid() (tid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Getxattr(path string, attr string, dest []byte) (sz int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p2 unsafe.Pointer + if len(dest) > 0 { + _p2 = unsafe.Pointer(&dest[0]) + } else { + _p2 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0) + sz = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) { - r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(StringBytePtr(pathname))), uintptr(mask)) + var _p0 *byte + _p0, err = BytePtrFromString(pathname) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask)) watchdesc = int(r0) if e1 != 0 { err = e1 @@ -529,7 +656,39 @@ func Klogctl(typ int, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listxattr(path string, dest []byte) (sz int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(dest) > 0 { + _p1 = unsafe.Pointer(&dest[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest))) + sz = int(r0) if e1 != 0 { err = e1 } @@ -539,7 +698,12 @@ func Link(oldpath string, newpath string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -549,7 +713,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdirat(dirfd int, path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode)) if e1 != 0 { err = e1 } @@ -559,7 +728,12 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -569,7 +743,12 @@ func Mknod(path string, mode uint32, dev int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) if e1 != 0 { err = e1 } @@ -599,7 +778,17 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func PivotRoot(newroot string, putold string) (err error) { - _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(StringBytePtr(newroot))), uintptr(unsafe.Pointer(StringBytePtr(putold))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(newroot) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(putold) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -608,7 +797,17 @@ func PivotRoot(newroot string, putold string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { + _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -626,13 +825,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -642,8 +846,38 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Removexattr(path string, attr string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Rename(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -653,7 +887,17 @@ func Rename(oldpath string, newpath string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) { - _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(newdirfd), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0) if e1 != 0 { err = e1 } @@ -663,7 +907,12 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -714,8 +963,9 @@ func Setpgid(pid int, pgid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setrlimit(resource int, rlim *Rlimit) (err error) { - _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) +func Setsid() (pid int, err error) { + r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) + pid = int(r0) if e1 != 0 { err = e1 } @@ -724,9 +974,8 @@ func Setrlimit(resource int, rlim *Rlimit) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setsid() (pid int, err error) { - r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) - pid = int(r0) +func Settimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) if e1 != 0 { err = e1 } @@ -735,8 +984,8 @@ func Setsid() (pid int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Settimeofday(tv *Timeval) (err error) { - _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) +func Setuid(uid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) if e1 != 0 { err = e1 } @@ -745,8 +994,24 @@ func Settimeofday(tv *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Setuid(uid int) (err error) { - _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) +func Setxattr(path string, attr string, data []byte, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p2 unsafe.Pointer + if len(data) > 0 { + _p2 = unsafe.Pointer(&data[0]) + } else { + _p2 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -756,7 +1021,17 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(oldpath string, newpath string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -833,7 +1108,12 @@ func Uname(buf *Utsname) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -843,7 +1123,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlinkat(dirfd int, path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), 0) if e1 != 0 { err = e1 } @@ -853,7 +1138,12 @@ func Unlinkat(dirfd int, path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(target string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(target) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -883,7 +1173,12 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Utime(path string, buf *Utimbuf) (err error) { - _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0) if e1 != 0 { err = e1 } @@ -892,7 +1187,7 @@ func Utime(path string, buf *Utimbuf) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -919,7 +1214,7 @@ func exitThread(code int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, p *byte, np int) (n int, err error) { +func readlen(fd int, p *byte, np int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)) n = int(r0) if e1 != 0 { @@ -930,7 +1225,7 @@ func read(fd int, p *byte, np int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, p *byte, np int) (n int, err error) { +func writelen(fd int, p *byte, np int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)) n = int(r0) if e1 != 0 { @@ -1046,6 +1341,17 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { + r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func bind(s int, addr uintptr, addrlen _Socklen) (err error) { _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) if e1 != 0 { @@ -1171,7 +1477,7 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err err // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, flags int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, flags int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -1203,7 +1509,12 @@ func sendmsg(s int, msg *Msghdr, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1232,16 +1543,6 @@ func Fstat(fd int, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Fstatfs(fd int, buf *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(buf)), 0) - if e1 != 0 { - err = e1 - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getegid() (egid int) { r0, _, _ := RawSyscall(SYS_GETEGID32, 0, 0, 0) egid = int(r0) @@ -1275,7 +1576,12 @@ func Getuid() (uid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -1295,7 +1601,12 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1304,7 +1615,7 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { r0, _, e1 := Syscall6(SYS_SENDFILE64, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0) written = int(r0) if e1 != 0 { @@ -1418,17 +1729,12 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = e1 + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func Statfs(path string, buf *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0) + _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1493,7 +1799,12 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall6(SYS_TRUNCATE64, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length), uintptr(length>>32), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_TRUNCATE64, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0) if e1 != 0 { err = e1 } @@ -1520,3 +1831,23 @@ func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getrlimit(resource int, rlim *rlimit32) (err error) { + _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setrlimit(resource int, rlim *rlimit32) (err error) { + _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0) + if e1 != 0 { + err = e1 + } + return +} diff --git a/src/pkg/syscall/zsyscall_netbsd_386.go b/src/pkg/syscall/zsyscall_netbsd_386.go index 6155a1687..10ac072db 100644 --- a/src/pkg/syscall/zsyscall_netbsd_386.go +++ b/src/pkg/syscall/zsyscall_netbsd_386.go @@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func utimes(path string, timeval *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) if e1 != 0 { err = e1 } @@ -253,8 +258,10 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags _C_int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) +func pipe() (fd1 int, fd2 int, err error) { + r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) + fd1 = int(r0) + fd2 = int(r1) if e1 != 0 { err = e1 } @@ -281,7 +288,12 @@ func getdents(fd int, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -301,7 +313,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -311,7 +328,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -321,7 +343,12 @@ func Chflags(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -331,7 +358,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -341,7 +373,12 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -399,7 +436,12 @@ func Fchdir(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -606,7 +648,7 @@ func Issetugid() (tainted bool) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Kill(pid int, signum int) (err error) { +func Kill(pid int, signum Signal) (err error) { _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0) if e1 != 0 { err = e1 @@ -628,7 +670,12 @@ func Kqueue() (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -638,7 +685,17 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -658,7 +715,12 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -668,7 +730,12 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -678,7 +745,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkfifo(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -688,7 +760,12 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -708,7 +785,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -719,7 +801,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Pathconf(path string, name int) (val int, err error) { - r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) val = int(r0) if e1 != 0 { err = e1 @@ -763,7 +850,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -781,13 +868,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -798,7 +890,17 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rename(from string, to string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -808,7 +910,12 @@ func Rename(from string, to string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Revoke(path string) (err error) { - _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -818,7 +925,12 @@ func Revoke(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -960,7 +1072,12 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -970,7 +1087,17 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -990,7 +1117,12 @@ func Sync() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length), uintptr(length>>32), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0) if e1 != 0 { err = e1 } @@ -1008,7 +1140,12 @@ func Umask(newmask int) (oldmask int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1018,7 +1155,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -1027,7 +1169,7 @@ func Unmount(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1065,7 +1207,7 @@ func munmap(addr uintptr, length uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, buf *byte, nbuf int) (n int, err error) { +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { @@ -1076,7 +1218,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, buf *byte, nbuf int) (n int, err error) { +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_netbsd_amd64.go b/src/pkg/syscall/zsyscall_netbsd_amd64.go index d68cd86d8..f10dc0bf4 100644 --- a/src/pkg/syscall/zsyscall_netbsd_amd64.go +++ b/src/pkg/syscall/zsyscall_netbsd_amd64.go @@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func utimes(path string, timeval *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) if e1 != 0 { err = e1 } @@ -253,8 +258,10 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags _C_int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) +func pipe() (fd1 int, fd2 int, err error) { + r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) + fd1 = int(r0) + fd2 = int(r1) if e1 != 0 { err = e1 } @@ -281,7 +288,12 @@ func getdents(fd int, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -301,7 +313,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -311,7 +328,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -321,7 +343,12 @@ func Chflags(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -331,7 +358,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -341,7 +373,12 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -399,7 +436,12 @@ func Fchdir(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -606,7 +648,7 @@ func Issetugid() (tainted bool) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Kill(pid int, signum int) (err error) { +func Kill(pid int, signum Signal) (err error) { _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0) if e1 != 0 { err = e1 @@ -628,7 +670,12 @@ func Kqueue() (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -638,7 +685,17 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -658,7 +715,12 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -668,7 +730,12 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -678,7 +745,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkfifo(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -688,7 +760,12 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -708,7 +785,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -719,7 +801,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Pathconf(path string, name int) (val int, err error) { - r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) val = int(r0) if e1 != 0 { err = e1 @@ -763,7 +850,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -781,13 +868,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -798,7 +890,17 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rename(from string, to string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -808,7 +910,12 @@ func Rename(from string, to string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Revoke(path string) (err error) { - _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -818,7 +925,12 @@ func Revoke(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -960,7 +1072,12 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -970,7 +1087,17 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -990,7 +1117,12 @@ func Sync() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length)) if e1 != 0 { err = e1 } @@ -1008,7 +1140,12 @@ func Umask(newmask int) (oldmask int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1018,7 +1155,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -1027,7 +1169,7 @@ func Unmount(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1065,7 +1207,7 @@ func munmap(addr uintptr, length uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, buf *byte, nbuf int) (n int, err error) { +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { @@ -1076,7 +1218,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, buf *byte, nbuf int) (n int, err error) { +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_netbsd_arm.go b/src/pkg/syscall/zsyscall_netbsd_arm.go new file mode 100644 index 000000000..763a906e5 --- /dev/null +++ b/src/pkg/syscall/zsyscall_netbsd_arm.go @@ -0,0 +1,1228 @@ +// mksyscall.pl -l32 -netbsd syscall_bsd.go syscall_netbsd.go syscall_netbsd_arm.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import "unsafe" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getgroups(ngid int, gid *_Gid_t) (n int, err error) { + r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setgroups(ngid int, gid *_Gid_t) (err error) { + _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) { + r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0) + wpid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { + r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func bind(s int, addr uintptr, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func connect(s int, addr uintptr, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socket(domain int, typ int, proto int) (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) { + _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (err error) { + _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Shutdown(s int, how int) (err error) { + _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { + _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (err error) { + _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, err error) { + r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimes(path string, timeval *[2]Timeval) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func futimes(fd int, timeval *[2]Timeval) (err error) { + _, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntl(fd int, cmd int, arg int) (val int, err error) { + r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg)) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pipe() (fd1 int, fd2 int, err error) { + r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) + fd1 = int(r0) + fd2 = int(r1) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getdents(fd int, buf []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_GETDENTS, uintptr(fd), uintptr(_p0), uintptr(len(buf))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Access(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { + _, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chflags(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chmod(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chown(path string, uid int, gid int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chroot(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Close(fd int) (err error) { + _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup(fd int) (nfd int, err error) { + r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0) + nfd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup2(from int, to int) (err error) { + _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Exit(code int) { + Syscall(SYS_EXIT, uintptr(code), 0, 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchdir(fd int) (err error) { + _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchflags(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchmod(fd int, mode uint32) (err error) { + _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchown(fd int, uid int, gid int) (err error) { + _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Flock(fd int, how int) (err error) { + _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fpathconf(fd int, name int) (val int, err error) { + r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstat(fd int, stat *Stat_t) (err error) { + _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fsync(fd int) (err error) { + _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Ftruncate(fd int, length int64) (err error) { + _, _, e1 := Syscall6(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length), uintptr(length>>32), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getegid() (egid int) { + r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0) + egid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Geteuid() (uid int) { + r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getgid() (gid int) { + r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgid(pid int) (pgid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0) + pgid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgrp() (pgrp int) { + r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0) + pgrp = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpid() (pid int) { + r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) + pid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getppid() (ppid int) { + r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0) + ppid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpriority(which int, who int) (prio int, err error) { + r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0) + prio = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrlimit(which int, lim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrusage(who int, rusage *Rusage) (err error) { + _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getsid(pid int) (sid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0) + sid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Gettimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getuid() (uid int) { + r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Issetugid() (tainted bool) { + r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0) + tainted = bool(r0 != 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Kill(pid int, signum Signal) (err error) { + _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Kqueue() (fd int, err error) { + r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Lchown(path string, uid int, gid int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Link(path string, link string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listen(s int, backlog int) (err error) { + _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Lstat(path string, stat *Stat_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkdir(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkfifo(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mknod(path string, mode uint32, dev int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Nanosleep(time *Timespec, leftover *Timespec) (err error) { + _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Open(path string, mode int, perm uint32) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pathconf(path string, name int) (val int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pread(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func read(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Readlink(path string, buf []byte) (n int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(buf) > 0 { + _p1 = unsafe.Pointer(&buf[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Rename(from string, to string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Revoke(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Rmdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { + r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(whence), 0) + newoffset = int64(int64(r1)<<32 | int64(r0)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) { + _, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setegid(egid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seteuid(euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setgid(gid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpgid(pid int, pgid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpriority(which int, who int, prio int) (err error) { + _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setregid(rgid int, egid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setreuid(ruid int, euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setrlimit(which int, lim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setsid() (pid int, err error) { + r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) + pid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Settimeofday(tp *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setuid(uid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Stat(path string, stat *Stat_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Symlink(path string, link string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Sync() (err error) { + _, _, e1 := Syscall(SYS_SYNC, 0, 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Truncate(path string, length int64) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Umask(newmask int) (oldmask int) { + r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0) + oldmask = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unlink(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unmount(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func write(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) { + r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), 0, uintptr(pos), uintptr(pos>>32), 0) + ret = uintptr(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func munmap(addr uintptr, length uintptr) (err error) { + _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} diff --git a/src/pkg/syscall/zsyscall_openbsd_386.go b/src/pkg/syscall/zsyscall_openbsd_386.go index c88492329..60e907bd2 100644 --- a/src/pkg/syscall/zsyscall_openbsd_386.go +++ b/src/pkg/syscall/zsyscall_openbsd_386.go @@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func utimes(path string, timeval *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) if e1 != 0 { err = e1 } @@ -264,7 +269,12 @@ func pipe(p *[2]_C_int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -284,7 +294,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -294,7 +309,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -304,7 +324,12 @@ func Chflags(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -314,7 +339,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -324,7 +354,12 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -382,7 +417,12 @@ func Fchdir(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -655,7 +695,12 @@ func Kqueue() (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -665,7 +710,17 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -685,7 +740,12 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -695,7 +755,12 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -705,7 +770,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkfifo(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -715,7 +785,12 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -735,7 +810,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -746,7 +826,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Pathconf(path string, name int) (val int, err error) { - r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) val = int(r0) if e1 != 0 { err = e1 @@ -790,7 +875,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -808,13 +893,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -825,7 +915,17 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rename(from string, to string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -835,7 +935,12 @@ func Rename(from string, to string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Revoke(path string) (err error) { - _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -845,7 +950,12 @@ func Revoke(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -906,7 +1016,12 @@ func Setgid(gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setlogin(name string) (err error) { - _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -997,7 +1112,12 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1007,7 +1127,12 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Statfs(path string, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1017,7 +1142,17 @@ func Statfs(path string, stat *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -1037,7 +1172,12 @@ func Sync() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length), uintptr(length>>32), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0) if e1 != 0 { err = e1 } @@ -1055,7 +1195,12 @@ func Umask(newmask int) (oldmask int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1065,7 +1210,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -1074,7 +1224,7 @@ func Unmount(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1112,7 +1262,7 @@ func munmap(addr uintptr, length uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, buf *byte, nbuf int) (n int, err error) { +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { @@ -1123,7 +1273,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, buf *byte, nbuf int) (n int, err error) { +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_openbsd_amd64.go b/src/pkg/syscall/zsyscall_openbsd_amd64.go index 7775646c4..1403dd7e7 100644 --- a/src/pkg/syscall/zsyscall_openbsd_amd64.go +++ b/src/pkg/syscall/zsyscall_openbsd_amd64.go @@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) { +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) if e1 != 0 { err = e1 @@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func utimes(path string, timeval *[2]Timeval) (err error) { - _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) if e1 != 0 { err = e1 } @@ -264,7 +269,12 @@ func pipe(p *[2]_C_int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Access(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -284,7 +294,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -294,7 +309,12 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -304,7 +324,12 @@ func Chflags(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chmod(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -314,7 +339,12 @@ func Chmod(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -324,7 +354,12 @@ func Chown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chroot(path string) (err error) { - _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -382,7 +417,12 @@ func Fchdir(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Fchflags(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -655,7 +695,12 @@ func Kqueue() (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lchown(path string, uid int, gid int) (err error) { - _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) if e1 != 0 { err = e1 } @@ -665,7 +710,17 @@ func Lchown(path string, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Link(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -685,7 +740,12 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Lstat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -695,7 +755,12 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkdir(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -705,7 +770,12 @@ func Mkdir(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mkfifo(path string, mode uint32) (err error) { - _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) if e1 != 0 { err = e1 } @@ -715,7 +785,12 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mknod(path string, mode uint32, dev int) (err error) { - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) if e1 != 0 { err = e1 } @@ -735,7 +810,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if e1 != 0 { err = e1 @@ -746,7 +826,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Pathconf(path string, name int) (val int, err error) { - r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) val = int(r0) if e1 != 0 { err = e1 @@ -790,7 +875,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Read(fd int, p []byte) (n int, err error) { +func read(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -808,13 +893,18 @@ func Read(fd int, p []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Readlink(path string, buf []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + _p1 = unsafe.Pointer(&buf[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf))) + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) n = int(r0) if e1 != 0 { err = e1 @@ -825,7 +915,17 @@ func Readlink(path string, buf []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rename(from string, to string) (err error) { - _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -835,7 +935,12 @@ func Rename(from string, to string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Revoke(path string) (err error) { - _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -845,7 +950,12 @@ func Revoke(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Rmdir(path string) (err error) { - _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -906,7 +1016,12 @@ func Setgid(gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Setlogin(name string) (err error) { - _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -997,7 +1112,12 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1007,7 +1127,12 @@ func Stat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Statfs(path string, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = e1 } @@ -1017,7 +1142,17 @@ func Statfs(path string, stat *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Symlink(path string, link string) (err error) { - _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) if e1 != 0 { err = e1 } @@ -1037,7 +1172,12 @@ func Sync() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Truncate(path string, length int64) (err error) { - _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length)) if e1 != 0 { err = e1 } @@ -1055,7 +1195,12 @@ func Umask(newmask int) (oldmask int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unlink(path string) (err error) { - _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) if e1 != 0 { err = e1 } @@ -1065,7 +1210,12 @@ func Unlink(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Unmount(path string, flags int) (err error) { - _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) if e1 != 0 { err = e1 } @@ -1074,7 +1224,7 @@ func Unmount(path string, flags int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Write(fd int, p []byte) (n int, err error) { +func write(fd int, p []byte) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1112,7 +1262,7 @@ func munmap(addr uintptr, length uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func read(fd int, buf *byte, nbuf int) (n int, err error) { +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { @@ -1123,7 +1273,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func write(fd int, buf *byte, nbuf int) (n int, err error) { +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) n = int(r0) if e1 != 0 { diff --git a/src/pkg/syscall/zsyscall_plan9_386.go b/src/pkg/syscall/zsyscall_plan9_386.go index 913c28fa2..5ffa0e439 100644 --- a/src/pkg/syscall/zsyscall_plan9_386.go +++ b/src/pkg/syscall/zsyscall_plan9_386.go @@ -7,13 +7,6 @@ import "unsafe" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func exits(msg *byte) { - Syscall(SYS_EXITS, uintptr(unsafe.Pointer(msg)), 0, 0) - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func fd2path(fd int, buf []byte) (err error) { var _p0 unsafe.Pointer if len(buf) > 0 { @@ -69,7 +62,12 @@ func Dup(oldfd int, newfd int) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Open(path string, mode int) (fd int, err error) { - r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) fd = int(r0) if int(r0) == -1 { err = e1 @@ -80,7 +78,12 @@ func Open(path string, mode int) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Create(path string, mode int, perm uint32) (fd int, err error) { - r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) fd = int(r0) if int(r0) == -1 { err = e1 @@ -91,7 +94,12 @@ func Create(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Remove(path string) (err error) { - r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) if int(r0) == -1 { err = e1 } @@ -145,7 +153,12 @@ func Close(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Chdir(path string) (err error) { - r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) if int(r0) == -1 { err = e1 } @@ -155,7 +168,17 @@ func Chdir(path string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Bind(name string, old string, flag int) (err error) { - r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(old))), uintptr(flag)) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(old) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) if int(r0) == -1 { err = e1 } @@ -165,7 +188,17 @@ func Bind(name string, old string, flag int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Mount(fd int, afd int, old string, flag int, aname string) (err error) { - r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(StringBytePtr(old))), uintptr(flag), uintptr(unsafe.Pointer(StringBytePtr(aname))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(old) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(aname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) if int(r0) == -1 { err = e1 } @@ -175,13 +208,18 @@ func Mount(fd int, afd int, old string, flag int, aname string) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Stat(path string, edir []byte) (n int, err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(edir) > 0 { - _p0 = unsafe.Pointer(&edir[0]) + _p1 = unsafe.Pointer(&edir[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(edir))) + r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) n = int(r0) if int(r0) == -1 { err = e1 @@ -209,13 +247,18 @@ func Fstat(fd int, edir []byte) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT func Wstat(path string, edir []byte) (err error) { - var _p0 unsafe.Pointer + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer if len(edir) > 0 { - _p0 = unsafe.Pointer(&edir[0]) + _p1 = unsafe.Pointer(&edir[0]) } else { - _p0 = unsafe.Pointer(&_zero) + _p1 = unsafe.Pointer(&_zero) } - r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(edir))) + r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) if int(r0) == -1 { err = e1 } diff --git a/src/pkg/syscall/zsyscall_plan9_amd64.go b/src/pkg/syscall/zsyscall_plan9_amd64.go new file mode 100644 index 000000000..c64533f49 --- /dev/null +++ b/src/pkg/syscall/zsyscall_plan9_amd64.go @@ -0,0 +1,282 @@ +// mksyscall.pl -l32 -plan9 syscall_plan9.go syscall_plan9_amd64.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import "unsafe" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fd2path(fd int, buf []byte) (err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf))) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pipe(p *[2]_C_int) (err error) { + r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func await(s []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(s) > 0 { + _p0 = unsafe.Pointer(&s[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup(oldfd int, newfd int) (fd int, err error) { + r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0) + fd = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Open(path string, mode int) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + fd = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Create(path string, mode int, perm uint32) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) + fd = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Remove(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pread(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Close(fd int) (err error) { + r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Bind(name string, old string, flag int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(old) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mount(fd int, afd int, old string, flag int, aname string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(old) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(aname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Stat(path string, edir []byte) (n int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(edir) > 0 { + _p1 = unsafe.Pointer(&edir[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstat(fd int, edir []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(edir) > 0 { + _p0 = unsafe.Pointer(&edir[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) + n = int(r0) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Wstat(path string, edir []byte) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(edir) > 0 { + _p1 = unsafe.Pointer(&edir[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir))) + if int32(r0) == -1 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fwstat(fd int, edir []byte) (err error) { + var _p0 unsafe.Pointer + if len(edir) > 0 { + _p0 = unsafe.Pointer(&edir[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir))) + if int32(r0) == -1 { + err = e1 + } + return +} diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index 3022ce2e9..e5c48488b 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -49,6 +49,7 @@ var ( procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus") procCancelIo = modkernel32.NewProc("CancelIo") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") procCreateProcessW = modkernel32.NewProc("CreateProcessW") procOpenProcess = modkernel32.NewProc("OpenProcess") procTerminateProcess = modkernel32.NewProc("TerminateProcess") @@ -103,11 +104,16 @@ var ( procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW") procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW") procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW") + procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") + procGetConsoleMode = modkernel32.NewProc("GetConsoleMode") + procWriteConsoleW = modkernel32.NewProc("WriteConsoleW") + procReadConsoleW = modkernel32.NewProc("ReadConsoleW") procWSAStartup = modws2_32.NewProc("WSAStartup") procWSACleanup = modws2_32.NewProc("WSACleanup") procWSAIoctl = modws2_32.NewProc("WSAIoctl") procsocket = modws2_32.NewProc("socket") procsetsockopt = modws2_32.NewProc("setsockopt") + procgetsockopt = modws2_32.NewProc("getsockopt") procbind = modws2_32.NewProc("bind") procconnect = modws2_32.NewProc("connect") procgetsockname = modws2_32.NewProc("getsockname") @@ -127,6 +133,8 @@ var ( procgetprotobyname = modws2_32.NewProc("getprotobyname") procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W") procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") + procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW") + procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procTranslateNameW = modsecur32.NewProc("TranslateNameW") @@ -142,7 +150,6 @@ var ( procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken") procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation") procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW") - procgetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") ) func GetLastError() (lasterr error) { @@ -154,7 +161,12 @@ func GetLastError() (lasterr error) { } func LoadLibrary(libname string) (handle Handle, err error) { - r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(StringToUTF16Ptr(libname))), 0, 0) + var _p0 *uint16 + _p0, err = UTF16PtrFromString(libname) + if err != nil { + return + } + r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0) handle = Handle(r0) if handle == 0 { if e1 != 0 { @@ -168,7 +180,7 @@ func LoadLibrary(libname string) (handle Handle, err error) { func FreeLibrary(handle Handle) (err error) { r1, _, e1 := Syscall(procFreeLibrary.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -179,7 +191,12 @@ func FreeLibrary(handle Handle) (err error) { } func GetProcAddress(module Handle, procname string) (proc uintptr, err error) { - r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(procname) + if err != nil { + return + } + r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(_p0)), 0) proc = uintptr(r0) if proc == 0 { if e1 != 0 { @@ -245,7 +262,7 @@ func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) ( _p0 = &buf[0] } r1, _, e1 := Syscall6(procReadFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -261,7 +278,7 @@ func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) _p0 = &buf[0] } r1, _, e1 := Syscall6(procWriteFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -286,7 +303,7 @@ func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence func CloseHandle(handle Handle) (err error) { r1, _, e1 := Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -324,7 +341,7 @@ func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err erro func findNextFile1(handle Handle, data *win32finddata1) (err error) { r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -336,7 +353,7 @@ func findNextFile1(handle Handle, data *win32finddata1) (err error) { func FindClose(handle Handle) (err error) { r1, _, e1 := Syscall(procFindClose.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -348,7 +365,7 @@ func FindClose(handle Handle) (err error) { func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (err error) { r1, _, e1 := Syscall(procGetFileInformationByHandle.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -373,7 +390,7 @@ func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) { func SetCurrentDirectory(path *uint16) (err error) { r1, _, e1 := Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -385,7 +402,7 @@ func SetCurrentDirectory(path *uint16) (err error) { func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) { r1, _, e1 := Syscall(procCreateDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -397,7 +414,7 @@ func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) { func RemoveDirectory(path *uint16) (err error) { r1, _, e1 := Syscall(procRemoveDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -409,7 +426,7 @@ func RemoveDirectory(path *uint16) (err error) { func DeleteFile(path *uint16) (err error) { r1, _, e1 := Syscall(procDeleteFileW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -421,7 +438,7 @@ func DeleteFile(path *uint16) (err error) { func MoveFile(from *uint16, to *uint16) (err error) { r1, _, e1 := Syscall(procMoveFileW.Addr(), 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -433,7 +450,7 @@ func MoveFile(from *uint16, to *uint16) (err error) { func GetComputerName(buf *uint16, n *uint32) (err error) { r1, _, e1 := Syscall(procGetComputerNameW.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -445,7 +462,7 @@ func GetComputerName(buf *uint16, n *uint32) (err error) { func SetEndOfFile(handle Handle) (err error) { r1, _, e1 := Syscall(procSetEndOfFile.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -488,7 +505,7 @@ func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, thre func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error) { r1, _, e1 := Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -500,7 +517,7 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error) { r1, _, e1 := Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -512,7 +529,19 @@ func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlap func CancelIo(s Handle) (err error) { r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0) - if int(r1) == 0 { + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func CancelIoEx(s Handle, o *Overlapped) (err error) { + r1, _, e1 := Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0) + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -530,7 +559,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA _p0 = 0 } r1, _, e1 := Syscall12(procCreateProcessW.Addr(), 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -561,7 +590,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err func TerminateProcess(handle Handle, exitcode uint32) (err error) { r1, _, e1 := Syscall(procTerminateProcess.Addr(), 2, uintptr(handle), uintptr(exitcode), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -573,7 +602,7 @@ func TerminateProcess(handle Handle, exitcode uint32) (err error) { func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) { r1, _, e1 := Syscall(procGetExitCodeProcess.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -585,7 +614,7 @@ func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) { func GetStartupInfo(startupInfo *StartupInfo) (err error) { r1, _, e1 := Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -610,7 +639,7 @@ func GetCurrentProcess() (pseudoHandle Handle, err error) { func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error) { r1, _, e1 := Syscall6(procGetProcessTimes.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -628,7 +657,7 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP _p0 = 0 } r1, _, e1 := Syscall9(procDuplicateHandle.Addr(), 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -666,7 +695,7 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) { func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (err error) { r1, _, e1 := Syscall6(procCreatePipe.Addr(), 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -691,7 +720,7 @@ func GetFileType(filehandle Handle) (n uint32, err error) { func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (err error) { r1, _, e1 := Syscall6(procCryptAcquireContextW.Addr(), 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -703,7 +732,7 @@ func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16 func CryptReleaseContext(provhandle Handle, flags uint32) (err error) { r1, _, e1 := Syscall(procCryptReleaseContext.Addr(), 2, uintptr(provhandle), uintptr(flags), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -715,7 +744,7 @@ func CryptReleaseContext(provhandle Handle, flags uint32) (err error) { func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) { r1, _, e1 := Syscall(procCryptGenRandom.Addr(), 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -740,7 +769,7 @@ func GetEnvironmentStrings() (envs *uint16, err error) { func FreeEnvironmentStrings(envs *uint16) (err error) { r1, _, e1 := Syscall(procFreeEnvironmentStringsW.Addr(), 1, uintptr(unsafe.Pointer(envs)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -765,7 +794,7 @@ func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32 func SetEnvironmentVariable(name *uint16, value *uint16) (err error) { r1, _, e1 := Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -777,7 +806,7 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (err error) { func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) { r1, _, e1 := Syscall6(procSetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -802,7 +831,7 @@ func GetFileAttributes(name *uint16) (attrs uint32, err error) { func SetFileAttributes(name *uint16, attrs uint32) (err error) { r1, _, e1 := Syscall(procSetFileAttributesW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -814,7 +843,7 @@ func SetFileAttributes(name *uint16, attrs uint32) (err error) { func GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) { r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -858,7 +887,7 @@ func LocalFree(hmem Handle) (handle Handle, err error) { func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) { r1, _, e1 := Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags)) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -870,7 +899,7 @@ func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) func FlushFileBuffers(handle Handle) (err error) { r1, _, e1 := Syscall(procFlushFileBuffers.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -947,7 +976,7 @@ func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow ui func UnmapViewOfFile(addr uintptr) (err error) { r1, _, e1 := Syscall(procUnmapViewOfFile.Addr(), 1, uintptr(addr), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -959,7 +988,7 @@ func UnmapViewOfFile(addr uintptr) (err error) { func FlushViewOfFile(addr uintptr, length uintptr) (err error) { r1, _, e1 := Syscall(procFlushViewOfFile.Addr(), 2, uintptr(addr), uintptr(length), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -971,7 +1000,7 @@ func FlushViewOfFile(addr uintptr, length uintptr) (err error) { func VirtualLock(addr uintptr, length uintptr) (err error) { r1, _, e1 := Syscall(procVirtualLock.Addr(), 2, uintptr(addr), uintptr(length), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -983,7 +1012,7 @@ func VirtualLock(addr uintptr, length uintptr) (err error) { func VirtualUnlock(addr uintptr, length uintptr) (err error) { r1, _, e1 := Syscall(procVirtualUnlock.Addr(), 2, uintptr(addr), uintptr(length), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -995,7 +1024,7 @@ func VirtualUnlock(addr uintptr, length uintptr) (err error) { func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (err error) { r1, _, e1 := Syscall9(procTransmitFile.Addr(), 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1013,7 +1042,7 @@ func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree _p0 = 0 } r1, _, e1 := Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1064,7 +1093,7 @@ func CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (contex func CertAddCertificateContextToStore(store Handle, certContext *CertContext, addDisposition uint32, storeContext **CertContext) (err error) { r1, _, e1 := Syscall6(procCertAddCertificateContextToStore.Addr(), 4, uintptr(store), uintptr(unsafe.Pointer(certContext)), uintptr(addDisposition), uintptr(unsafe.Pointer(storeContext)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1076,7 +1105,7 @@ func CertAddCertificateContextToStore(store Handle, certContext *CertContext, ad func CertCloseStore(store Handle, flags uint32) (err error) { r1, _, e1 := Syscall(procCertCloseStore.Addr(), 2, uintptr(store), uintptr(flags), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1088,7 +1117,7 @@ func CertCloseStore(store Handle, flags uint32) (err error) { func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, additionalStore Handle, para *CertChainPara, flags uint32, reserved uintptr, chainCtx **CertChainContext) (err error) { r1, _, e1 := Syscall9(procCertGetCertificateChain.Addr(), 8, uintptr(engine), uintptr(unsafe.Pointer(leaf)), uintptr(unsafe.Pointer(time)), uintptr(additionalStore), uintptr(unsafe.Pointer(para)), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(chainCtx)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1118,7 +1147,7 @@ func CertCreateCertificateContext(certEncodingType uint32, certEncoded *byte, en func CertFreeCertificateContext(ctx *CertContext) (err error) { r1, _, e1 := Syscall(procCertFreeCertificateContext.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1130,7 +1159,7 @@ func CertFreeCertificateContext(ctx *CertContext) (err error) { func CertVerifyCertificateChainPolicy(policyOID uintptr, chain *CertChainContext, para *CertChainPolicyPara, status *CertChainPolicyStatus) (err error) { r1, _, e1 := Syscall6(procCertVerifyCertificateChainPolicy.Addr(), 4, uintptr(policyOID), uintptr(unsafe.Pointer(chain)), uintptr(unsafe.Pointer(para)), uintptr(unsafe.Pointer(status)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1180,6 +1209,48 @@ func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32 return } +func getCurrentProcessId() (pid uint32) { + r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0) + pid = uint32(r0) + return +} + +func GetConsoleMode(console Handle, mode *uint32) (err error) { + r1, _, e1 := Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) { + r1, _, e1 := Syscall6(procWriteConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(towrite), uintptr(unsafe.Pointer(written)), uintptr(unsafe.Pointer(reserved)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) { + r1, _, e1 := Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { r0, _, _ := Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) if r0 != 0 { @@ -1190,7 +1261,7 @@ func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { func WSACleanup() (err error) { r1, _, e1 := Syscall(procWSACleanup.Addr(), 0, 0, 0, 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1202,7 +1273,7 @@ func WSACleanup() (err error) { func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) { r1, _, e1 := Syscall9(procWSAIoctl.Addr(), 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine)) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1227,7 +1298,19 @@ func socket(af int32, typ int32, protocol int32) (handle Handle, err error) { func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) { r1, _, e1 := Syscall6(procsetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0) - if int(r1) == -1 { + if r1 == socket_error { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) { + r1, _, e1 := Syscall6(procgetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(unsafe.Pointer(optlen)), 0) + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1239,7 +1322,7 @@ func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32 func bind(s Handle, name uintptr, namelen int32) (err error) { r1, _, e1 := Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1251,7 +1334,7 @@ func bind(s Handle, name uintptr, namelen int32) (err error) { func connect(s Handle, name uintptr, namelen int32) (err error) { r1, _, e1 := Syscall(procconnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1263,7 +1346,7 @@ func connect(s Handle, name uintptr, namelen int32) (err error) { func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { r1, _, e1 := Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1275,7 +1358,7 @@ func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { r1, _, e1 := Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1287,7 +1370,7 @@ func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { func listen(s Handle, backlog int32) (err error) { r1, _, e1 := Syscall(proclisten.Addr(), 2, uintptr(s), uintptr(backlog), 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1299,7 +1382,7 @@ func listen(s Handle, backlog int32) (err error) { func shutdown(s Handle, how int32) (err error) { r1, _, e1 := Syscall(procshutdown.Addr(), 2, uintptr(s), uintptr(how), 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1311,7 +1394,7 @@ func shutdown(s Handle, how int32) (err error) { func Closesocket(s Handle) (err error) { r1, _, e1 := Syscall(procclosesocket.Addr(), 1, uintptr(s), 0, 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1323,7 +1406,7 @@ func Closesocket(s Handle) (err error) { func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (err error) { r1, _, e1 := Syscall9(procAcceptEx.Addr(), 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1340,7 +1423,7 @@ func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1352,7 +1435,7 @@ func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32 func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := Syscall9(procWSASend.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1364,7 +1447,7 @@ func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := Syscall9(procWSARecvFrom.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1376,7 +1459,7 @@ func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *ui func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1387,7 +1470,12 @@ func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32 } func GetHostByName(name string) (h *Hostent, err error) { - r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0) h = (*Hostent)(unsafe.Pointer(r0)) if h == nil { if e1 != 0 { @@ -1400,7 +1488,17 @@ func GetHostByName(name string) (h *Hostent, err error) { } func GetServByName(name string, proto string) (s *Servent, err error) { - r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(proto))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(proto) + if err != nil { + return + } + r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) s = (*Servent)(unsafe.Pointer(r0)) if s == nil { if e1 != 0 { @@ -1419,7 +1517,12 @@ func Ntohs(netshort uint16) (u uint16) { } func GetProtoByName(name string) (p *Protoent, err error) { - r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0) p = (*Protoent)(unsafe.Pointer(r0)) if p == nil { if e1 != 0 { @@ -1432,7 +1535,12 @@ func GetProtoByName(name string) (p *Protoent, err error) { } func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) { - r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(StringToUTF16Ptr(name))), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr))) + var _p0 *uint16 + _p0, status = UTF16PtrFromString(name) + if status != nil { + return + } + r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(_p0)), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr))) if r0 != 0 { status = Errno(r0) } @@ -1444,6 +1552,19 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) { return } +func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) { + r0, _, _ := Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0) + if r0 != 0 { + sockerr = Errno(r0) + } + return +} + +func FreeAddrInfoW(addrinfo *AddrinfoW) { + Syscall(procFreeAddrInfoW.Addr(), 1, uintptr(unsafe.Pointer(addrinfo)), 0, 0) + return +} + func GetIfEntry(pIfRow *MibIfRow) (errcode error) { r0, _, _ := Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0) if r0 != 0 { @@ -1462,7 +1583,7 @@ func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) { func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) { r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0) - if int(r1)&0xff == 0 { + if r1&0xff == 0 { if e1 != 0 { err = error(e1) } else { @@ -1474,7 +1595,7 @@ func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) { r1, _, e1 := Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize))) - if int(r1)&0xff == 0 { + if r1&0xff == 0 { if e1 != 0 { err = error(e1) } else { @@ -1502,7 +1623,7 @@ func NetApiBufferFree(buf *byte) (neterr error) { func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) { r1, _, e1 := Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1514,7 +1635,7 @@ func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint3 func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) { r1, _, e1 := Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1526,7 +1647,7 @@ func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) { r1, _, e1 := Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(stringSid)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1538,7 +1659,7 @@ func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) { func ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) { r1, _, e1 := Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(stringSid)), uintptr(unsafe.Pointer(sid)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1556,7 +1677,7 @@ func GetLengthSid(sid *SID) (len uint32) { func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) { r1, _, e1 := Syscall(procCopySid.Addr(), 3, uintptr(destSidLen), uintptr(unsafe.Pointer(destSid)), uintptr(unsafe.Pointer(srcSid))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1568,7 +1689,7 @@ func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) { func OpenProcessToken(h Handle, access uint32, token *Token) (err error) { r1, _, e1 := Syscall(procOpenProcessToken.Addr(), 3, uintptr(h), uintptr(access), uintptr(unsafe.Pointer(token))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1580,7 +1701,7 @@ func OpenProcessToken(h Handle, access uint32, token *Token) (err error) { func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) { r1, _, e1 := Syscall6(procGetTokenInformation.Addr(), 5, uintptr(t), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), uintptr(unsafe.Pointer(returnedLen)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1592,7 +1713,7 @@ func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) { r1, _, e1 := Syscall(procGetUserProfileDirectoryW.Addr(), 3, uintptr(t), uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1601,9 +1722,3 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) { } return } - -func getCurrentProcessId() (pid uint32) { - r0, _, _ := Syscall(procgetCurrentProcessId.Addr(), 0, 0, 0, 0) - pid = uint32(r0) - return -} diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go index ea0000c32..465b509ae 100644 --- a/src/pkg/syscall/zsyscall_windows_amd64.go +++ b/src/pkg/syscall/zsyscall_windows_amd64.go @@ -49,6 +49,7 @@ var ( procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus") procCancelIo = modkernel32.NewProc("CancelIo") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") procCreateProcessW = modkernel32.NewProc("CreateProcessW") procOpenProcess = modkernel32.NewProc("OpenProcess") procTerminateProcess = modkernel32.NewProc("TerminateProcess") @@ -103,11 +104,16 @@ var ( procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW") procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW") procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW") + procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") + procGetConsoleMode = modkernel32.NewProc("GetConsoleMode") + procWriteConsoleW = modkernel32.NewProc("WriteConsoleW") + procReadConsoleW = modkernel32.NewProc("ReadConsoleW") procWSAStartup = modws2_32.NewProc("WSAStartup") procWSACleanup = modws2_32.NewProc("WSACleanup") procWSAIoctl = modws2_32.NewProc("WSAIoctl") procsocket = modws2_32.NewProc("socket") procsetsockopt = modws2_32.NewProc("setsockopt") + procgetsockopt = modws2_32.NewProc("getsockopt") procbind = modws2_32.NewProc("bind") procconnect = modws2_32.NewProc("connect") procgetsockname = modws2_32.NewProc("getsockname") @@ -127,6 +133,8 @@ var ( procgetprotobyname = modws2_32.NewProc("getprotobyname") procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W") procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") + procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW") + procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procTranslateNameW = modsecur32.NewProc("TranslateNameW") @@ -142,7 +150,6 @@ var ( procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken") procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation") procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW") - procgetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") ) func GetLastError() (lasterr error) { @@ -154,7 +161,12 @@ func GetLastError() (lasterr error) { } func LoadLibrary(libname string) (handle Handle, err error) { - r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(StringToUTF16Ptr(libname))), 0, 0) + var _p0 *uint16 + _p0, err = UTF16PtrFromString(libname) + if err != nil { + return + } + r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0) handle = Handle(r0) if handle == 0 { if e1 != 0 { @@ -168,7 +180,7 @@ func LoadLibrary(libname string) (handle Handle, err error) { func FreeLibrary(handle Handle) (err error) { r1, _, e1 := Syscall(procFreeLibrary.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -179,7 +191,12 @@ func FreeLibrary(handle Handle) (err error) { } func GetProcAddress(module Handle, procname string) (proc uintptr, err error) { - r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(procname) + if err != nil { + return + } + r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(_p0)), 0) proc = uintptr(r0) if proc == 0 { if e1 != 0 { @@ -245,7 +262,7 @@ func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) ( _p0 = &buf[0] } r1, _, e1 := Syscall6(procReadFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -261,7 +278,7 @@ func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) _p0 = &buf[0] } r1, _, e1 := Syscall6(procWriteFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -286,7 +303,7 @@ func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence func CloseHandle(handle Handle) (err error) { r1, _, e1 := Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -324,7 +341,7 @@ func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err erro func findNextFile1(handle Handle, data *win32finddata1) (err error) { r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -336,7 +353,7 @@ func findNextFile1(handle Handle, data *win32finddata1) (err error) { func FindClose(handle Handle) (err error) { r1, _, e1 := Syscall(procFindClose.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -348,7 +365,7 @@ func FindClose(handle Handle) (err error) { func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (err error) { r1, _, e1 := Syscall(procGetFileInformationByHandle.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -373,7 +390,7 @@ func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) { func SetCurrentDirectory(path *uint16) (err error) { r1, _, e1 := Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -385,7 +402,7 @@ func SetCurrentDirectory(path *uint16) (err error) { func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) { r1, _, e1 := Syscall(procCreateDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -397,7 +414,7 @@ func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) { func RemoveDirectory(path *uint16) (err error) { r1, _, e1 := Syscall(procRemoveDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -409,7 +426,7 @@ func RemoveDirectory(path *uint16) (err error) { func DeleteFile(path *uint16) (err error) { r1, _, e1 := Syscall(procDeleteFileW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -421,7 +438,7 @@ func DeleteFile(path *uint16) (err error) { func MoveFile(from *uint16, to *uint16) (err error) { r1, _, e1 := Syscall(procMoveFileW.Addr(), 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -433,7 +450,7 @@ func MoveFile(from *uint16, to *uint16) (err error) { func GetComputerName(buf *uint16, n *uint32) (err error) { r1, _, e1 := Syscall(procGetComputerNameW.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -445,7 +462,7 @@ func GetComputerName(buf *uint16, n *uint32) (err error) { func SetEndOfFile(handle Handle) (err error) { r1, _, e1 := Syscall(procSetEndOfFile.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -488,7 +505,7 @@ func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, thre func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error) { r1, _, e1 := Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -500,7 +517,7 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error) { r1, _, e1 := Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -512,7 +529,19 @@ func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlap func CancelIo(s Handle) (err error) { r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0) - if int(r1) == 0 { + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func CancelIoEx(s Handle, o *Overlapped) (err error) { + r1, _, e1 := Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0) + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -530,7 +559,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA _p0 = 0 } r1, _, e1 := Syscall12(procCreateProcessW.Addr(), 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -561,7 +590,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err func TerminateProcess(handle Handle, exitcode uint32) (err error) { r1, _, e1 := Syscall(procTerminateProcess.Addr(), 2, uintptr(handle), uintptr(exitcode), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -573,7 +602,7 @@ func TerminateProcess(handle Handle, exitcode uint32) (err error) { func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) { r1, _, e1 := Syscall(procGetExitCodeProcess.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -585,7 +614,7 @@ func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) { func GetStartupInfo(startupInfo *StartupInfo) (err error) { r1, _, e1 := Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -610,7 +639,7 @@ func GetCurrentProcess() (pseudoHandle Handle, err error) { func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error) { r1, _, e1 := Syscall6(procGetProcessTimes.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -628,7 +657,7 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP _p0 = 0 } r1, _, e1 := Syscall9(procDuplicateHandle.Addr(), 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -666,7 +695,7 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) { func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (err error) { r1, _, e1 := Syscall6(procCreatePipe.Addr(), 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -691,7 +720,7 @@ func GetFileType(filehandle Handle) (n uint32, err error) { func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (err error) { r1, _, e1 := Syscall6(procCryptAcquireContextW.Addr(), 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -703,7 +732,7 @@ func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16 func CryptReleaseContext(provhandle Handle, flags uint32) (err error) { r1, _, e1 := Syscall(procCryptReleaseContext.Addr(), 2, uintptr(provhandle), uintptr(flags), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -715,7 +744,7 @@ func CryptReleaseContext(provhandle Handle, flags uint32) (err error) { func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) { r1, _, e1 := Syscall(procCryptGenRandom.Addr(), 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -740,7 +769,7 @@ func GetEnvironmentStrings() (envs *uint16, err error) { func FreeEnvironmentStrings(envs *uint16) (err error) { r1, _, e1 := Syscall(procFreeEnvironmentStringsW.Addr(), 1, uintptr(unsafe.Pointer(envs)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -765,7 +794,7 @@ func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32 func SetEnvironmentVariable(name *uint16, value *uint16) (err error) { r1, _, e1 := Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -777,7 +806,7 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (err error) { func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) { r1, _, e1 := Syscall6(procSetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -802,7 +831,7 @@ func GetFileAttributes(name *uint16) (attrs uint32, err error) { func SetFileAttributes(name *uint16, attrs uint32) (err error) { r1, _, e1 := Syscall(procSetFileAttributesW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -814,7 +843,7 @@ func SetFileAttributes(name *uint16, attrs uint32) (err error) { func GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) { r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -858,7 +887,7 @@ func LocalFree(hmem Handle) (handle Handle, err error) { func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) { r1, _, e1 := Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags)) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -870,7 +899,7 @@ func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) func FlushFileBuffers(handle Handle) (err error) { r1, _, e1 := Syscall(procFlushFileBuffers.Addr(), 1, uintptr(handle), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -947,7 +976,7 @@ func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow ui func UnmapViewOfFile(addr uintptr) (err error) { r1, _, e1 := Syscall(procUnmapViewOfFile.Addr(), 1, uintptr(addr), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -959,7 +988,7 @@ func UnmapViewOfFile(addr uintptr) (err error) { func FlushViewOfFile(addr uintptr, length uintptr) (err error) { r1, _, e1 := Syscall(procFlushViewOfFile.Addr(), 2, uintptr(addr), uintptr(length), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -971,7 +1000,7 @@ func FlushViewOfFile(addr uintptr, length uintptr) (err error) { func VirtualLock(addr uintptr, length uintptr) (err error) { r1, _, e1 := Syscall(procVirtualLock.Addr(), 2, uintptr(addr), uintptr(length), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -983,7 +1012,7 @@ func VirtualLock(addr uintptr, length uintptr) (err error) { func VirtualUnlock(addr uintptr, length uintptr) (err error) { r1, _, e1 := Syscall(procVirtualUnlock.Addr(), 2, uintptr(addr), uintptr(length), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -995,7 +1024,7 @@ func VirtualUnlock(addr uintptr, length uintptr) (err error) { func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (err error) { r1, _, e1 := Syscall9(procTransmitFile.Addr(), 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1013,7 +1042,7 @@ func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree _p0 = 0 } r1, _, e1 := Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1064,7 +1093,7 @@ func CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (contex func CertAddCertificateContextToStore(store Handle, certContext *CertContext, addDisposition uint32, storeContext **CertContext) (err error) { r1, _, e1 := Syscall6(procCertAddCertificateContextToStore.Addr(), 4, uintptr(store), uintptr(unsafe.Pointer(certContext)), uintptr(addDisposition), uintptr(unsafe.Pointer(storeContext)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1076,7 +1105,7 @@ func CertAddCertificateContextToStore(store Handle, certContext *CertContext, ad func CertCloseStore(store Handle, flags uint32) (err error) { r1, _, e1 := Syscall(procCertCloseStore.Addr(), 2, uintptr(store), uintptr(flags), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1088,7 +1117,7 @@ func CertCloseStore(store Handle, flags uint32) (err error) { func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, additionalStore Handle, para *CertChainPara, flags uint32, reserved uintptr, chainCtx **CertChainContext) (err error) { r1, _, e1 := Syscall9(procCertGetCertificateChain.Addr(), 8, uintptr(engine), uintptr(unsafe.Pointer(leaf)), uintptr(unsafe.Pointer(time)), uintptr(additionalStore), uintptr(unsafe.Pointer(para)), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(chainCtx)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1118,7 +1147,7 @@ func CertCreateCertificateContext(certEncodingType uint32, certEncoded *byte, en func CertFreeCertificateContext(ctx *CertContext) (err error) { r1, _, e1 := Syscall(procCertFreeCertificateContext.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1130,7 +1159,7 @@ func CertFreeCertificateContext(ctx *CertContext) (err error) { func CertVerifyCertificateChainPolicy(policyOID uintptr, chain *CertChainContext, para *CertChainPolicyPara, status *CertChainPolicyStatus) (err error) { r1, _, e1 := Syscall6(procCertVerifyCertificateChainPolicy.Addr(), 4, uintptr(policyOID), uintptr(unsafe.Pointer(chain)), uintptr(unsafe.Pointer(para)), uintptr(unsafe.Pointer(status)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1180,6 +1209,48 @@ func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32 return } +func getCurrentProcessId() (pid uint32) { + r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0) + pid = uint32(r0) + return +} + +func GetConsoleMode(console Handle, mode *uint32) (err error) { + r1, _, e1 := Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) { + r1, _, e1 := Syscall6(procWriteConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(towrite), uintptr(unsafe.Pointer(written)), uintptr(unsafe.Pointer(reserved)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) { + r1, _, e1 := Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { r0, _, _ := Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) if r0 != 0 { @@ -1190,7 +1261,7 @@ func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { func WSACleanup() (err error) { r1, _, e1 := Syscall(procWSACleanup.Addr(), 0, 0, 0, 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1202,7 +1273,7 @@ func WSACleanup() (err error) { func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) { r1, _, e1 := Syscall9(procWSAIoctl.Addr(), 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine)) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1227,7 +1298,19 @@ func socket(af int32, typ int32, protocol int32) (handle Handle, err error) { func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) { r1, _, e1 := Syscall6(procsetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0) - if int(r1) == -1 { + if r1 == socket_error { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + +func Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) { + r1, _, e1 := Syscall6(procgetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(unsafe.Pointer(optlen)), 0) + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1239,7 +1322,7 @@ func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32 func bind(s Handle, name uintptr, namelen int32) (err error) { r1, _, e1 := Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1251,7 +1334,7 @@ func bind(s Handle, name uintptr, namelen int32) (err error) { func connect(s Handle, name uintptr, namelen int32) (err error) { r1, _, e1 := Syscall(procconnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1263,7 +1346,7 @@ func connect(s Handle, name uintptr, namelen int32) (err error) { func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { r1, _, e1 := Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1275,7 +1358,7 @@ func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { r1, _, e1 := Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1287,7 +1370,7 @@ func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) { func listen(s Handle, backlog int32) (err error) { r1, _, e1 := Syscall(proclisten.Addr(), 2, uintptr(s), uintptr(backlog), 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1299,7 +1382,7 @@ func listen(s Handle, backlog int32) (err error) { func shutdown(s Handle, how int32) (err error) { r1, _, e1 := Syscall(procshutdown.Addr(), 2, uintptr(s), uintptr(how), 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1311,7 +1394,7 @@ func shutdown(s Handle, how int32) (err error) { func Closesocket(s Handle) (err error) { r1, _, e1 := Syscall(procclosesocket.Addr(), 1, uintptr(s), 0, 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1323,7 +1406,7 @@ func Closesocket(s Handle) (err error) { func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (err error) { r1, _, e1 := Syscall9(procAcceptEx.Addr(), 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1340,7 +1423,7 @@ func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1352,7 +1435,7 @@ func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32 func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := Syscall9(procWSASend.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1364,7 +1447,7 @@ func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := Syscall9(procWSARecvFrom.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1376,7 +1459,7 @@ func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *ui func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) { r1, _, e1 := Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) - if int(r1) == -1 { + if r1 == socket_error { if e1 != 0 { err = error(e1) } else { @@ -1387,7 +1470,12 @@ func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32 } func GetHostByName(name string) (h *Hostent, err error) { - r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0) h = (*Hostent)(unsafe.Pointer(r0)) if h == nil { if e1 != 0 { @@ -1400,7 +1488,17 @@ func GetHostByName(name string) (h *Hostent, err error) { } func GetServByName(name string, proto string) (s *Servent, err error) { - r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(proto))), 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(proto) + if err != nil { + return + } + r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) s = (*Servent)(unsafe.Pointer(r0)) if s == nil { if e1 != 0 { @@ -1419,7 +1517,12 @@ func Ntohs(netshort uint16) (u uint16) { } func GetProtoByName(name string) (p *Protoent, err error) { - r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0) p = (*Protoent)(unsafe.Pointer(r0)) if p == nil { if e1 != 0 { @@ -1432,7 +1535,12 @@ func GetProtoByName(name string) (p *Protoent, err error) { } func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) { - r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(StringToUTF16Ptr(name))), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr))) + var _p0 *uint16 + _p0, status = UTF16PtrFromString(name) + if status != nil { + return + } + r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(_p0)), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr))) if r0 != 0 { status = Errno(r0) } @@ -1444,6 +1552,19 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) { return } +func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) { + r0, _, _ := Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0) + if r0 != 0 { + sockerr = Errno(r0) + } + return +} + +func FreeAddrInfoW(addrinfo *AddrinfoW) { + Syscall(procFreeAddrInfoW.Addr(), 1, uintptr(unsafe.Pointer(addrinfo)), 0, 0) + return +} + func GetIfEntry(pIfRow *MibIfRow) (errcode error) { r0, _, _ := Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0) if r0 != 0 { @@ -1462,7 +1583,7 @@ func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) { func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) { r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0) - if int(r1)&0xff == 0 { + if r1&0xff == 0 { if e1 != 0 { err = error(e1) } else { @@ -1474,7 +1595,7 @@ func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) { r1, _, e1 := Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize))) - if int(r1)&0xff == 0 { + if r1&0xff == 0 { if e1 != 0 { err = error(e1) } else { @@ -1502,7 +1623,7 @@ func NetApiBufferFree(buf *byte) (neterr error) { func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) { r1, _, e1 := Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1514,7 +1635,7 @@ func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint3 func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) { r1, _, e1 := Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1526,7 +1647,7 @@ func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) { r1, _, e1 := Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(stringSid)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1538,7 +1659,7 @@ func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) { func ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) { r1, _, e1 := Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(stringSid)), uintptr(unsafe.Pointer(sid)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1556,7 +1677,7 @@ func GetLengthSid(sid *SID) (len uint32) { func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) { r1, _, e1 := Syscall(procCopySid.Addr(), 3, uintptr(destSidLen), uintptr(unsafe.Pointer(destSid)), uintptr(unsafe.Pointer(srcSid))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1568,7 +1689,7 @@ func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) { func OpenProcessToken(h Handle, access uint32, token *Token) (err error) { r1, _, e1 := Syscall(procOpenProcessToken.Addr(), 3, uintptr(h), uintptr(access), uintptr(unsafe.Pointer(token))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1580,7 +1701,7 @@ func OpenProcessToken(h Handle, access uint32, token *Token) (err error) { func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) { r1, _, e1 := Syscall6(procGetTokenInformation.Addr(), 5, uintptr(t), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), uintptr(unsafe.Pointer(returnedLen)), 0) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1592,7 +1713,7 @@ func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) { r1, _, e1 := Syscall(procGetUserProfileDirectoryW.Addr(), 3, uintptr(t), uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen))) - if int(r1) == 0 { + if r1 == 0 { if e1 != 0 { err = error(e1) } else { @@ -1601,9 +1722,3 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) { } return } - -func getCurrentProcessId() (pid uint32) { - r0, _, _ := Syscall(procgetCurrentProcessId.Addr(), 0, 0, 0, 0) - pid = uint32(r0) - return -} diff --git a/src/pkg/syscall/zsysctl_openbsd.go b/src/pkg/syscall/zsysctl_openbsd.go index fb1342290..a5914f3f0 100644 --- a/src/pkg/syscall/zsysctl_openbsd.go +++ b/src/pkg/syscall/zsysctl_openbsd.go @@ -64,6 +64,7 @@ var sysctlMib = []mibentry{ {"kern.maxlocksperuid", []_C_int{1, 70}}, {"kern.maxpartitions", []_C_int{1, 23}}, {"kern.maxproc", []_C_int{1, 6}}, + {"kern.maxthread", []_C_int{1, 25}}, {"kern.maxvnodes", []_C_int{1, 5}}, {"kern.mbstat", []_C_int{1, 59}}, {"kern.msgbuf", []_C_int{1, 48}}, @@ -75,6 +76,7 @@ var sysctlMib = []mibentry{ {"kern.nosuidcoredump", []_C_int{1, 32}}, {"kern.nprocs", []_C_int{1, 47}}, {"kern.nselcoll", []_C_int{1, 43}}, + {"kern.nthreads", []_C_int{1, 26}}, {"kern.numvnodes", []_C_int{1, 58}}, {"kern.osrelease", []_C_int{1, 2}}, {"kern.osrevision", []_C_int{1, 3}}, diff --git a/src/pkg/syscall/zsysnum_freebsd_386.go b/src/pkg/syscall/zsysnum_freebsd_386.go index e986abac5..5a2bfd119 100644 --- a/src/pkg/syscall/zsysnum_freebsd_386.go +++ b/src/pkg/syscall/zsysnum_freebsd_386.go @@ -1,4 +1,4 @@ -// mksysnum_freebsd.pl +// mksysnum_freebsd.pl // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall diff --git a/src/pkg/syscall/zsysnum_freebsd_amd64.go b/src/pkg/syscall/zsysnum_freebsd_amd64.go index e986abac5..5a2bfd119 100644 --- a/src/pkg/syscall/zsysnum_freebsd_amd64.go +++ b/src/pkg/syscall/zsysnum_freebsd_amd64.go @@ -1,4 +1,4 @@ -// mksysnum_freebsd.pl +// mksysnum_freebsd.pl // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall diff --git a/src/pkg/syscall/zsysnum_freebsd_arm.go b/src/pkg/syscall/zsysnum_freebsd_arm.go new file mode 100644 index 000000000..5a2bfd119 --- /dev/null +++ b/src/pkg/syscall/zsysnum_freebsd_arm.go @@ -0,0 +1,340 @@ +// mksysnum_freebsd.pl +// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT + +package syscall + +const ( + // SYS_NOSYS = 0; // { int nosys(void); } syscall nosys_args int + SYS_EXIT = 1 // { void sys_exit(int rval); } exit \ + SYS_FORK = 2 // { int fork(void); } + SYS_READ = 3 // { ssize_t read(int fd, void *buf, \ + SYS_WRITE = 4 // { ssize_t write(int fd, const void *buf, \ + SYS_OPEN = 5 // { int open(char *path, int flags, int mode); } + SYS_CLOSE = 6 // { int close(int fd); } + SYS_WAIT4 = 7 // { int wait4(int pid, int *status, \ + SYS_LINK = 9 // { int link(char *path, char *link); } + SYS_UNLINK = 10 // { int unlink(char *path); } + SYS_CHDIR = 12 // { int chdir(char *path); } + SYS_FCHDIR = 13 // { int fchdir(int fd); } + SYS_MKNOD = 14 // { int mknod(char *path, int mode, int dev); } + SYS_CHMOD = 15 // { int chmod(char *path, int mode); } + SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); } + SYS_OBREAK = 17 // { int obreak(char *nsize); } break \ + SYS_GETPID = 20 // { pid_t getpid(void); } + SYS_MOUNT = 21 // { int mount(char *type, char *path, \ + SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); } + SYS_SETUID = 23 // { int setuid(uid_t uid); } + SYS_GETUID = 24 // { uid_t getuid(void); } + SYS_GETEUID = 25 // { uid_t geteuid(void); } + SYS_PTRACE = 26 // { int ptrace(int req, pid_t pid, \ + SYS_RECVMSG = 27 // { int recvmsg(int s, struct msghdr *msg, \ + SYS_SENDMSG = 28 // { int sendmsg(int s, struct msghdr *msg, \ + SYS_RECVFROM = 29 // { int recvfrom(int s, caddr_t buf, \ + SYS_ACCEPT = 30 // { int accept(int s, \ + SYS_GETPEERNAME = 31 // { int getpeername(int fdes, \ + SYS_GETSOCKNAME = 32 // { int getsockname(int fdes, \ + SYS_ACCESS = 33 // { int access(char *path, int amode); } + SYS_CHFLAGS = 34 // { int chflags(char *path, int flags); } + SYS_FCHFLAGS = 35 // { int fchflags(int fd, int flags); } + SYS_SYNC = 36 // { int sync(void); } + SYS_KILL = 37 // { int kill(int pid, int signum); } + SYS_GETPPID = 39 // { pid_t getppid(void); } + SYS_DUP = 41 // { int dup(u_int fd); } + SYS_PIPE = 42 // { int pipe(void); } + SYS_GETEGID = 43 // { gid_t getegid(void); } + SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, \ + SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, \ + SYS_GETGID = 47 // { gid_t getgid(void); } + SYS_GETLOGIN = 49 // { int getlogin(char *namebuf, u_int \ + SYS_SETLOGIN = 50 // { int setlogin(char *namebuf); } + SYS_ACCT = 51 // { int acct(char *path); } + SYS_SIGALTSTACK = 53 // { int sigaltstack(stack_t *ss, \ + SYS_IOCTL = 54 // { int ioctl(int fd, u_long com, \ + SYS_REBOOT = 55 // { int reboot(int opt); } + SYS_REVOKE = 56 // { int revoke(char *path); } + SYS_SYMLINK = 57 // { int symlink(char *path, char *link); } + SYS_READLINK = 58 // { ssize_t readlink(char *path, char *buf, \ + SYS_EXECVE = 59 // { int execve(char *fname, char **argv, \ + SYS_UMASK = 60 // { int umask(int newmask); } umask umask_args \ + SYS_CHROOT = 61 // { int chroot(char *path); } + SYS_MSYNC = 65 // { int msync(void *addr, size_t len, \ + SYS_VFORK = 66 // { int vfork(void); } + SYS_SBRK = 69 // { int sbrk(int incr); } + SYS_SSTK = 70 // { int sstk(int incr); } + SYS_OVADVISE = 72 // { int ovadvise(int anom); } vadvise \ + SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); } + SYS_MPROTECT = 74 // { int mprotect(const void *addr, size_t len, \ + SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, \ + SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, \ + SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, \ + SYS_SETGROUPS = 80 // { int setgroups(u_int gidsetsize, \ + SYS_GETPGRP = 81 // { int getpgrp(void); } + SYS_SETPGID = 82 // { int setpgid(int pid, int pgid); } + SYS_SETITIMER = 83 // { int setitimer(u_int which, struct \ + SYS_SWAPON = 85 // { int swapon(char *name); } + SYS_GETITIMER = 86 // { int getitimer(u_int which, \ + SYS_GETDTABLESIZE = 89 // { int getdtablesize(void); } + SYS_DUP2 = 90 // { int dup2(u_int from, u_int to); } + SYS_FCNTL = 92 // { int fcntl(int fd, int cmd, long arg); } + SYS_SELECT = 93 // { int select(int nd, fd_set *in, fd_set *ou, \ + SYS_FSYNC = 95 // { int fsync(int fd); } + SYS_SETPRIORITY = 96 // { int setpriority(int which, int who, \ + SYS_SOCKET = 97 // { int socket(int domain, int type, \ + SYS_CONNECT = 98 // { int connect(int s, caddr_t name, \ + SYS_GETPRIORITY = 100 // { int getpriority(int which, int who); } + SYS_BIND = 104 // { int bind(int s, caddr_t name, \ + SYS_SETSOCKOPT = 105 // { int setsockopt(int s, int level, int name, \ + SYS_LISTEN = 106 // { int listen(int s, int backlog); } + SYS_GETTIMEOFDAY = 116 // { int gettimeofday(struct timeval *tp, \ + SYS_GETRUSAGE = 117 // { int getrusage(int who, \ + SYS_GETSOCKOPT = 118 // { int getsockopt(int s, int level, int name, \ + SYS_READV = 120 // { int readv(int fd, struct iovec *iovp, \ + SYS_WRITEV = 121 // { int writev(int fd, struct iovec *iovp, \ + SYS_SETTIMEOFDAY = 122 // { int settimeofday(struct timeval *tv, \ + SYS_FCHOWN = 123 // { int fchown(int fd, int uid, int gid); } + SYS_FCHMOD = 124 // { int fchmod(int fd, int mode); } + SYS_SETREUID = 126 // { int setreuid(int ruid, int euid); } + SYS_SETREGID = 127 // { int setregid(int rgid, int egid); } + SYS_RENAME = 128 // { int rename(char *from, char *to); } + SYS_FLOCK = 131 // { int flock(int fd, int how); } + SYS_MKFIFO = 132 // { int mkfifo(char *path, int mode); } + SYS_SENDTO = 133 // { int sendto(int s, caddr_t buf, size_t len, \ + SYS_SHUTDOWN = 134 // { int shutdown(int s, int how); } + SYS_SOCKETPAIR = 135 // { int socketpair(int domain, int type, \ + SYS_MKDIR = 136 // { int mkdir(char *path, int mode); } + SYS_RMDIR = 137 // { int rmdir(char *path); } + SYS_UTIMES = 138 // { int utimes(char *path, \ + SYS_ADJTIME = 140 // { int adjtime(struct timeval *delta, \ + SYS_SETSID = 147 // { int setsid(void); } + SYS_QUOTACTL = 148 // { int quotactl(char *path, int cmd, int uid, \ + SYS_LGETFH = 160 // { int lgetfh(char *fname, \ + SYS_GETFH = 161 // { int getfh(char *fname, \ + SYS_SYSARCH = 165 // { int sysarch(int op, char *parms); } + SYS_RTPRIO = 166 // { int rtprio(int function, pid_t pid, \ + SYS_FREEBSD6_PREAD = 173 // { ssize_t freebsd6_pread(int fd, void *buf, \ + SYS_FREEBSD6_PWRITE = 174 // { ssize_t freebsd6_pwrite(int fd, \ + SYS_SETFIB = 175 // { int setfib(int fibnum); } + SYS_NTP_ADJTIME = 176 // { int ntp_adjtime(struct timex *tp); } + SYS_SETGID = 181 // { int setgid(gid_t gid); } + SYS_SETEGID = 182 // { int setegid(gid_t egid); } + SYS_SETEUID = 183 // { int seteuid(uid_t euid); } + SYS_STAT = 188 // { int stat(char *path, struct stat *ub); } + SYS_FSTAT = 189 // { int fstat(int fd, struct stat *sb); } + SYS_LSTAT = 190 // { int lstat(char *path, struct stat *ub); } + SYS_PATHCONF = 191 // { int pathconf(char *path, int name); } + SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); } + SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, \ + SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, \ + SYS_GETDIRENTRIES = 196 // { int getdirentries(int fd, char *buf, \ + SYS_FREEBSD6_MMAP = 197 // { caddr_t freebsd6_mmap(caddr_t addr, \ + SYS_FREEBSD6_LSEEK = 199 // { off_t freebsd6_lseek(int fd, int pad, \ + SYS_FREEBSD6_TRUNCATE = 200 // { int freebsd6_truncate(char *path, int pad, \ + SYS_FREEBSD6_FTRUNCATE = 201 // { int freebsd6_ftruncate(int fd, int pad, \ + SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, \ + SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); } + SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); } + SYS_UNDELETE = 205 // { int undelete(char *path); } + SYS_FUTIMES = 206 // { int futimes(int fd, struct timeval *tptr); } + SYS_GETPGID = 207 // { int getpgid(pid_t pid); } + SYS_POLL = 209 // { int poll(struct pollfd *fds, u_int nfds, \ + SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, \ + SYS_CLOCK_SETTIME = 233 // { int clock_settime( \ + SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, \ + SYS_KTIMER_CREATE = 235 // { int ktimer_create(clockid_t clock_id, \ + SYS_KTIMER_DELETE = 236 // { int ktimer_delete(int timerid); } + SYS_KTIMER_SETTIME = 237 // { int ktimer_settime(int timerid, int flags, \ + SYS_KTIMER_GETTIME = 238 // { int ktimer_gettime(int timerid, struct \ + SYS_KTIMER_GETOVERRUN = 239 // { int ktimer_getoverrun(int timerid); } + SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, \ + SYS_FFCLOCK_GETCOUNTER = 241 // { int ffclock_getcounter(ffcounter *ffcount); } + SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate( \ + SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate( \ + SYS_NTP_GETTIME = 248 // { int ntp_gettime(struct ntptimeval *ntvp); } + SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, \ + SYS_RFORK = 251 // { int rfork(int flags); } + SYS_OPENBSD_POLL = 252 // { int openbsd_poll(struct pollfd *fds, \ + SYS_ISSETUGID = 253 // { int issetugid(void); } + SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); } + SYS_GETDENTS = 272 // { int getdents(int fd, char *buf, \ + SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); } + SYS_LUTIMES = 276 // { int lutimes(char *path, \ + SYS_NSTAT = 278 // { int nstat(char *path, struct nstat *ub); } + SYS_NFSTAT = 279 // { int nfstat(int fd, struct nstat *sb); } + SYS_NLSTAT = 280 // { int nlstat(char *path, struct nstat *ub); } + SYS_PREADV = 289 // { ssize_t preadv(int fd, struct iovec *iovp, \ + SYS_PWRITEV = 290 // { ssize_t pwritev(int fd, struct iovec *iovp, \ + SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, \ + SYS_FHSTAT = 299 // { int fhstat(const struct fhandle *u_fhp, \ + SYS_MODNEXT = 300 // { int modnext(int modid); } + SYS_MODSTAT = 301 // { int modstat(int modid, \ + SYS_MODFNEXT = 302 // { int modfnext(int modid); } + SYS_MODFIND = 303 // { int modfind(const char *name); } + SYS_KLDLOAD = 304 // { int kldload(const char *file); } + SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); } + SYS_KLDFIND = 306 // { int kldfind(const char *file); } + SYS_KLDNEXT = 307 // { int kldnext(int fileid); } + SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct \ + SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); } + SYS_GETSID = 310 // { int getsid(pid_t pid); } + SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, \ + SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, \ + SYS_YIELD = 321 // { int yield(void); } + SYS_MLOCKALL = 324 // { int mlockall(int how); } + SYS_MUNLOCKALL = 325 // { int munlockall(void); } + SYS___GETCWD = 326 // { int __getcwd(u_char *buf, u_int buflen); } + SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, \ + SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct \ + SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int \ + SYS_SCHED_GETSCHEDULER = 330 // { int sched_getscheduler (pid_t pid); } + SYS_SCHED_YIELD = 331 // { int sched_yield (void); } + SYS_SCHED_GET_PRIORITY_MAX = 332 // { int sched_get_priority_max (int policy); } + SYS_SCHED_GET_PRIORITY_MIN = 333 // { int sched_get_priority_min (int policy); } + SYS_SCHED_RR_GET_INTERVAL = 334 // { int sched_rr_get_interval (pid_t pid, \ + SYS_UTRACE = 335 // { int utrace(const void *addr, size_t len); } + SYS_KLDSYM = 337 // { int kldsym(int fileid, int cmd, \ + SYS_JAIL = 338 // { int jail(struct jail *jail); } + SYS_SIGPROCMASK = 340 // { int sigprocmask(int how, \ + SYS_SIGSUSPEND = 341 // { int sigsuspend(const sigset_t *sigmask); } + SYS_SIGPENDING = 343 // { int sigpending(sigset_t *set); } + SYS_SIGTIMEDWAIT = 345 // { int sigtimedwait(const sigset_t *set, \ + SYS_SIGWAITINFO = 346 // { int sigwaitinfo(const sigset_t *set, \ + SYS___ACL_GET_FILE = 347 // { int __acl_get_file(const char *path, \ + SYS___ACL_SET_FILE = 348 // { int __acl_set_file(const char *path, \ + SYS___ACL_GET_FD = 349 // { int __acl_get_fd(int filedes, \ + SYS___ACL_SET_FD = 350 // { int __acl_set_fd(int filedes, \ + SYS___ACL_DELETE_FILE = 351 // { int __acl_delete_file(const char *path, \ + SYS___ACL_DELETE_FD = 352 // { int __acl_delete_fd(int filedes, \ + SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, \ + SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, \ + SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, \ + SYS_EXTATTR_SET_FILE = 356 // { int extattr_set_file( \ + SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file( \ + SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, \ + SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, \ + SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, \ + SYS_KQUEUE = 362 // { int kqueue(void); } + SYS_KEVENT = 363 // { int kevent(int fd, \ + SYS_EXTATTR_SET_FD = 371 // { int extattr_set_fd(int fd, \ + SYS_EXTATTR_GET_FD = 372 // { ssize_t extattr_get_fd(int fd, \ + SYS_EXTATTR_DELETE_FD = 373 // { int extattr_delete_fd(int fd, \ + SYS___SETUGID = 374 // { int __setugid(int flag); } + SYS_EACCESS = 376 // { int eaccess(char *path, int amode); } + SYS_NMOUNT = 378 // { int nmount(struct iovec *iovp, \ + SYS___MAC_GET_PROC = 384 // { int __mac_get_proc(struct mac *mac_p); } + SYS___MAC_SET_PROC = 385 // { int __mac_set_proc(struct mac *mac_p); } + SYS___MAC_GET_FD = 386 // { int __mac_get_fd(int fd, \ + SYS___MAC_GET_FILE = 387 // { int __mac_get_file(const char *path_p, \ + SYS___MAC_SET_FD = 388 // { int __mac_set_fd(int fd, \ + SYS___MAC_SET_FILE = 389 // { int __mac_set_file(const char *path_p, \ + SYS_KENV = 390 // { int kenv(int what, const char *name, \ + SYS_LCHFLAGS = 391 // { int lchflags(const char *path, int flags); } + SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, \ + SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, \ + SYS_MAC_SYSCALL = 394 // { int mac_syscall(const char *policy, \ + SYS_GETFSSTAT = 395 // { int getfsstat(struct statfs *buf, \ + SYS_STATFS = 396 // { int statfs(char *path, \ + SYS_FSTATFS = 397 // { int fstatfs(int fd, struct statfs *buf); } + SYS_FHSTATFS = 398 // { int fhstatfs(const struct fhandle *u_fhp, \ + SYS___MAC_GET_PID = 409 // { int __mac_get_pid(pid_t pid, \ + SYS___MAC_GET_LINK = 410 // { int __mac_get_link(const char *path_p, \ + SYS___MAC_SET_LINK = 411 // { int __mac_set_link(const char *path_p, \ + SYS_EXTATTR_SET_LINK = 412 // { int extattr_set_link( \ + SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link( \ + SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link( \ + SYS___MAC_EXECVE = 415 // { int __mac_execve(char *fname, char **argv, \ + SYS_SIGACTION = 416 // { int sigaction(int sig, \ + SYS_SIGRETURN = 417 // { int sigreturn( \ + SYS_GETCONTEXT = 421 // { int getcontext(struct __ucontext *ucp); } + SYS_SETCONTEXT = 422 // { int setcontext( \ + SYS_SWAPCONTEXT = 423 // { int swapcontext(struct __ucontext *oucp, \ + SYS_SWAPOFF = 424 // { int swapoff(const char *name); } + SYS___ACL_GET_LINK = 425 // { int __acl_get_link(const char *path, \ + SYS___ACL_SET_LINK = 426 // { int __acl_set_link(const char *path, \ + SYS___ACL_DELETE_LINK = 427 // { int __acl_delete_link(const char *path, \ + SYS___ACL_ACLCHECK_LINK = 428 // { int __acl_aclcheck_link(const char *path, \ + SYS_SIGWAIT = 429 // { int sigwait(const sigset_t *set, \ + SYS_THR_CREATE = 430 // { int thr_create(ucontext_t *ctx, long *id, \ + SYS_THR_EXIT = 431 // { void thr_exit(long *state); } + SYS_THR_SELF = 432 // { int thr_self(long *id); } + SYS_THR_KILL = 433 // { int thr_kill(long id, int sig); } + SYS__UMTX_LOCK = 434 // { int _umtx_lock(struct umtx *umtx); } + SYS__UMTX_UNLOCK = 435 // { int _umtx_unlock(struct umtx *umtx); } + SYS_JAIL_ATTACH = 436 // { int jail_attach(int jid); } + SYS_EXTATTR_LIST_FD = 437 // { ssize_t extattr_list_fd(int fd, \ + SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file( \ + SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link( \ + SYS_THR_SUSPEND = 442 // { int thr_suspend( \ + SYS_THR_WAKE = 443 // { int thr_wake(long id); } + SYS_KLDUNLOADF = 444 // { int kldunloadf(int fileid, int flags); } + SYS_AUDIT = 445 // { int audit(const void *record, \ + SYS_AUDITON = 446 // { int auditon(int cmd, void *data, \ + SYS_GETAUID = 447 // { int getauid(uid_t *auid); } + SYS_SETAUID = 448 // { int setauid(uid_t *auid); } + SYS_GETAUDIT = 449 // { int getaudit(struct auditinfo *auditinfo); } + SYS_SETAUDIT = 450 // { int setaudit(struct auditinfo *auditinfo); } + SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr( \ + SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr( \ + SYS_AUDITCTL = 453 // { int auditctl(char *path); } + SYS__UMTX_OP = 454 // { int _umtx_op(void *obj, int op, \ + SYS_THR_NEW = 455 // { int thr_new(struct thr_param *param, \ + SYS_SIGQUEUE = 456 // { int sigqueue(pid_t pid, int signum, void *value); } + SYS_ABORT2 = 463 // { int abort2(const char *why, int nargs, void **args); } + SYS_THR_SET_NAME = 464 // { int thr_set_name(long id, const char *name); } + SYS_RTPRIO_THREAD = 466 // { int rtprio_thread(int function, \ + SYS_SCTP_PEELOFF = 471 // { int sctp_peeloff(int sd, uint32_t name); } + SYS_SCTP_GENERIC_SENDMSG = 472 // { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, \ + SYS_SCTP_GENERIC_SENDMSG_IOV = 473 // { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, \ + SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, \ + SYS_PREAD = 475 // { ssize_t pread(int fd, void *buf, \ + SYS_PWRITE = 476 // { ssize_t pwrite(int fd, const void *buf, \ + SYS_MMAP = 477 // { caddr_t mmap(caddr_t addr, size_t len, \ + SYS_LSEEK = 478 // { off_t lseek(int fd, off_t offset, \ + SYS_TRUNCATE = 479 // { int truncate(char *path, off_t length); } + SYS_FTRUNCATE = 480 // { int ftruncate(int fd, off_t length); } + SYS_THR_KILL2 = 481 // { int thr_kill2(pid_t pid, long id, int sig); } + SYS_SHM_OPEN = 482 // { int shm_open(const char *path, int flags, \ + SYS_SHM_UNLINK = 483 // { int shm_unlink(const char *path); } + SYS_CPUSET = 484 // { int cpuset(cpusetid_t *setid); } + SYS_CPUSET_SETID = 485 // { int cpuset_setid(cpuwhich_t which, id_t id, \ + SYS_CPUSET_GETID = 486 // { int cpuset_getid(cpulevel_t level, \ + SYS_CPUSET_GETAFFINITY = 487 // { int cpuset_getaffinity(cpulevel_t level, \ + SYS_CPUSET_SETAFFINITY = 488 // { int cpuset_setaffinity(cpulevel_t level, \ + SYS_FACCESSAT = 489 // { int faccessat(int fd, char *path, int amode, \ + SYS_FCHMODAT = 490 // { int fchmodat(int fd, char *path, mode_t mode, \ + SYS_FCHOWNAT = 491 // { int fchownat(int fd, char *path, uid_t uid, \ + SYS_FEXECVE = 492 // { int fexecve(int fd, char **argv, \ + SYS_FSTATAT = 493 // { int fstatat(int fd, char *path, \ + SYS_FUTIMESAT = 494 // { int futimesat(int fd, char *path, \ + SYS_LINKAT = 495 // { int linkat(int fd1, char *path1, int fd2, \ + SYS_MKDIRAT = 496 // { int mkdirat(int fd, char *path, mode_t mode); } + SYS_MKFIFOAT = 497 // { int mkfifoat(int fd, char *path, mode_t mode); } + SYS_MKNODAT = 498 // { int mknodat(int fd, char *path, mode_t mode, \ + SYS_OPENAT = 499 // { int openat(int fd, char *path, int flag, \ + SYS_READLINKAT = 500 // { int readlinkat(int fd, char *path, char *buf, \ + SYS_RENAMEAT = 501 // { int renameat(int oldfd, char *old, int newfd, \ + SYS_SYMLINKAT = 502 // { int symlinkat(char *path1, int fd, \ + SYS_UNLINKAT = 503 // { int unlinkat(int fd, char *path, int flag); } + SYS_POSIX_OPENPT = 504 // { int posix_openpt(int flags); } + SYS_JAIL_GET = 506 // { int jail_get(struct iovec *iovp, \ + SYS_JAIL_SET = 507 // { int jail_set(struct iovec *iovp, \ + SYS_JAIL_REMOVE = 508 // { int jail_remove(int jid); } + SYS_CLOSEFROM = 509 // { int closefrom(int lowfd); } + SYS_LPATHCONF = 513 // { int lpathconf(char *path, int name); } + SYS_CAP_NEW = 514 // { int cap_new(int fd, u_int64_t rights); } + SYS_CAP_GETRIGHTS = 515 // { int cap_getrights(int fd, \ + SYS_CAP_ENTER = 516 // { int cap_enter(void); } + SYS_CAP_GETMODE = 517 // { int cap_getmode(u_int *modep); } + SYS_PDFORK = 518 // { int pdfork(int *fdp, int flags); } + SYS_PDKILL = 519 // { int pdkill(int fd, int signum); } + SYS_PDGETPID = 520 // { int pdgetpid(int fd, pid_t *pidp); } + SYS_PSELECT = 522 // { int pselect(int nd, fd_set *in, \ + SYS_GETLOGINCLASS = 523 // { int getloginclass(char *namebuf, \ + SYS_SETLOGINCLASS = 524 // { int setloginclass(const char *namebuf); } + SYS_RCTL_GET_RACCT = 525 // { int rctl_get_racct(const void *inbufp, \ + SYS_RCTL_GET_RULES = 526 // { int rctl_get_rules(const void *inbufp, \ + SYS_RCTL_GET_LIMITS = 527 // { int rctl_get_limits(const void *inbufp, \ + SYS_RCTL_ADD_RULE = 528 // { int rctl_add_rule(const void *inbufp, \ + SYS_RCTL_REMOVE_RULE = 529 // { int rctl_remove_rule(const void *inbufp, \ + SYS_POSIX_FALLOCATE = 530 // { int posix_fallocate(int fd, \ + SYS_POSIX_FADVISE = 531 // { int posix_fadvise(int fd, off_t offset, \ +) diff --git a/src/pkg/syscall/zsysnum_linux_arm.go b/src/pkg/syscall/zsysnum_linux_arm.go index 7f5d9498c..7068e4e21 100644 --- a/src/pkg/syscall/zsysnum_linux_arm.go +++ b/src/pkg/syscall/zsysnum_linux_arm.go @@ -1,4 +1,4 @@ -// mksysnum_linux.pl +// mksysnum_linux.pl // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall diff --git a/src/pkg/syscall/zsysnum_netbsd_386.go b/src/pkg/syscall/zsysnum_netbsd_386.go index e9a0bcfb1..c57096523 100644 --- a/src/pkg/syscall/zsysnum_netbsd_386.go +++ b/src/pkg/syscall/zsysnum_netbsd_386.go @@ -1,4 +1,4 @@ -// mksysnum_netbsd.pl +// mksysnum_netbsd.pl // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall @@ -163,12 +163,6 @@ const ( SYS__LWP_SETNAME = 323 // { int|sys||_lwp_setname(lwpid_t target, const char *name); } SYS__LWP_GETNAME = 324 // { int|sys||_lwp_getname(lwpid_t target, char *name, size_t len); } SYS__LWP_CTL = 325 // { int|sys||_lwp_ctl(int features, struct lwpctl **address); } - SYS_SA_REGISTER = 330 // { int|sys||sa_register(sa_upcall_t new, sa_upcall_t *old, int flags, ssize_t stackinfo_offset); } - SYS_SA_STACKS = 331 // { int|sys||sa_stacks(int num, stack_t *stacks); } - SYS_SA_ENABLE = 332 // { int|sys||sa_enable(void); } - SYS_SA_SETCONCURRENCY = 333 // { int|sys||sa_setconcurrency(int concurrency); } - SYS_SA_YIELD = 334 // { int|sys||sa_yield(void); } - SYS_SA_PREEMPT = 335 // { int|sys||sa_preempt(int sa_id); } SYS___SIGACTION_SIGTRAMP = 340 // { int|sys||__sigaction_sigtramp(int signum, const struct sigaction *nsa, struct sigaction *osa, const void *tramp, int vers); } SYS_PMC_GET_INFO = 341 // { int|sys||pmc_get_info(int ctr, int op, void *args); } SYS_PMC_CONTROL = 342 // { int|sys||pmc_control(int ctr, int op, void *args); } @@ -250,7 +244,6 @@ const ( SYS_WAIT4 = 449 // { int|sys|50|wait4(pid_t pid, int *status, int options, struct rusage *rusage); } SYS_MKNOD = 450 // { int|sys|50|mknod(const char *path, mode_t mode, dev_t dev); } SYS_FHSTAT = 451 // { int|sys|50|fhstat(const void *fhp, size_t fh_size, struct stat *sb); } - SYS_QUOTACTL = 452 // { int|sys|50|quotactl(const char *path, struct plistref *pref); } SYS_PIPE2 = 453 // { int|sys||pipe2(int *fildes, int flags); } SYS_DUP3 = 454 // { int|sys||dup3(int from, int to, int flags); } SYS_KQUEUE1 = 455 // { int|sys||kqueue1(int flags); } @@ -271,4 +264,8 @@ const ( SYS_SYMLINKAT = 470 // { int|sys||symlinkat(const char *path1, int fd, const char *path2); } SYS_UNLINKAT = 471 // { int|sys||unlinkat(int fd, const char *path, int flag); } SYS_FUTIMENS = 472 // { int|sys||futimens(int fd, const struct timespec *tptr); } + SYS___QUOTACTL = 473 // { int|sys||__quotactl(const char *path, struct quotactl_args *args); } + SYS_POSIX_SPAWN = 474 // { int|sys||posix_spawn(pid_t *pid, const char *path, const struct posix_spawn_file_actions *file_actions, const struct posix_spawnattr *attrp, char *const *argv, char *const *envp); } + SYS_RECVMMSG = 475 // { int|sys||recvmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags, struct timespec *timeout); } + SYS_SENDMMSG = 476 // { int|sys||sendmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags); } ) diff --git a/src/pkg/syscall/zsysnum_netbsd_amd64.go b/src/pkg/syscall/zsysnum_netbsd_amd64.go index e9a0bcfb1..c57096523 100644 --- a/src/pkg/syscall/zsysnum_netbsd_amd64.go +++ b/src/pkg/syscall/zsysnum_netbsd_amd64.go @@ -1,4 +1,4 @@ -// mksysnum_netbsd.pl +// mksysnum_netbsd.pl // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall @@ -163,12 +163,6 @@ const ( SYS__LWP_SETNAME = 323 // { int|sys||_lwp_setname(lwpid_t target, const char *name); } SYS__LWP_GETNAME = 324 // { int|sys||_lwp_getname(lwpid_t target, char *name, size_t len); } SYS__LWP_CTL = 325 // { int|sys||_lwp_ctl(int features, struct lwpctl **address); } - SYS_SA_REGISTER = 330 // { int|sys||sa_register(sa_upcall_t new, sa_upcall_t *old, int flags, ssize_t stackinfo_offset); } - SYS_SA_STACKS = 331 // { int|sys||sa_stacks(int num, stack_t *stacks); } - SYS_SA_ENABLE = 332 // { int|sys||sa_enable(void); } - SYS_SA_SETCONCURRENCY = 333 // { int|sys||sa_setconcurrency(int concurrency); } - SYS_SA_YIELD = 334 // { int|sys||sa_yield(void); } - SYS_SA_PREEMPT = 335 // { int|sys||sa_preempt(int sa_id); } SYS___SIGACTION_SIGTRAMP = 340 // { int|sys||__sigaction_sigtramp(int signum, const struct sigaction *nsa, struct sigaction *osa, const void *tramp, int vers); } SYS_PMC_GET_INFO = 341 // { int|sys||pmc_get_info(int ctr, int op, void *args); } SYS_PMC_CONTROL = 342 // { int|sys||pmc_control(int ctr, int op, void *args); } @@ -250,7 +244,6 @@ const ( SYS_WAIT4 = 449 // { int|sys|50|wait4(pid_t pid, int *status, int options, struct rusage *rusage); } SYS_MKNOD = 450 // { int|sys|50|mknod(const char *path, mode_t mode, dev_t dev); } SYS_FHSTAT = 451 // { int|sys|50|fhstat(const void *fhp, size_t fh_size, struct stat *sb); } - SYS_QUOTACTL = 452 // { int|sys|50|quotactl(const char *path, struct plistref *pref); } SYS_PIPE2 = 453 // { int|sys||pipe2(int *fildes, int flags); } SYS_DUP3 = 454 // { int|sys||dup3(int from, int to, int flags); } SYS_KQUEUE1 = 455 // { int|sys||kqueue1(int flags); } @@ -271,4 +264,8 @@ const ( SYS_SYMLINKAT = 470 // { int|sys||symlinkat(const char *path1, int fd, const char *path2); } SYS_UNLINKAT = 471 // { int|sys||unlinkat(int fd, const char *path, int flag); } SYS_FUTIMENS = 472 // { int|sys||futimens(int fd, const struct timespec *tptr); } + SYS___QUOTACTL = 473 // { int|sys||__quotactl(const char *path, struct quotactl_args *args); } + SYS_POSIX_SPAWN = 474 // { int|sys||posix_spawn(pid_t *pid, const char *path, const struct posix_spawn_file_actions *file_actions, const struct posix_spawnattr *attrp, char *const *argv, char *const *envp); } + SYS_RECVMMSG = 475 // { int|sys||recvmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags, struct timespec *timeout); } + SYS_SENDMMSG = 476 // { int|sys||sendmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags); } ) diff --git a/src/pkg/syscall/zsysnum_netbsd_arm.go b/src/pkg/syscall/zsysnum_netbsd_arm.go new file mode 100644 index 000000000..c57096523 --- /dev/null +++ b/src/pkg/syscall/zsysnum_netbsd_arm.go @@ -0,0 +1,271 @@ +// mksysnum_netbsd.pl +// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT + +package syscall + +const ( + SYS_EXIT = 1 // { void|sys||exit(int rval); } + SYS_FORK = 2 // { int|sys||fork(void); } + SYS_READ = 3 // { ssize_t|sys||read(int fd, void *buf, size_t nbyte); } + SYS_WRITE = 4 // { ssize_t|sys||write(int fd, const void *buf, size_t nbyte); } + SYS_OPEN = 5 // { int|sys||open(const char *path, int flags, ... mode_t mode); } + SYS_CLOSE = 6 // { int|sys||close(int fd); } + SYS_LINK = 9 // { int|sys||link(const char *path, const char *link); } + SYS_UNLINK = 10 // { int|sys||unlink(const char *path); } + SYS_CHDIR = 12 // { int|sys||chdir(const char *path); } + SYS_FCHDIR = 13 // { int|sys||fchdir(int fd); } + SYS_CHMOD = 15 // { int|sys||chmod(const char *path, mode_t mode); } + SYS_CHOWN = 16 // { int|sys||chown(const char *path, uid_t uid, gid_t gid); } + SYS_BREAK = 17 // { int|sys||obreak(char *nsize); } + SYS_GETPID = 20 // { pid_t|sys||getpid_with_ppid(void); } + SYS_UNMOUNT = 22 // { int|sys||unmount(const char *path, int flags); } + SYS_SETUID = 23 // { int|sys||setuid(uid_t uid); } + SYS_GETUID = 24 // { uid_t|sys||getuid_with_euid(void); } + SYS_GETEUID = 25 // { uid_t|sys||geteuid(void); } + SYS_PTRACE = 26 // { int|sys||ptrace(int req, pid_t pid, void *addr, int data); } + SYS_RECVMSG = 27 // { ssize_t|sys||recvmsg(int s, struct msghdr *msg, int flags); } + SYS_SENDMSG = 28 // { ssize_t|sys||sendmsg(int s, const struct msghdr *msg, int flags); } + SYS_RECVFROM = 29 // { ssize_t|sys||recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlenaddr); } + SYS_ACCEPT = 30 // { int|sys||accept(int s, struct sockaddr *name, socklen_t *anamelen); } + SYS_GETPEERNAME = 31 // { int|sys||getpeername(int fdes, struct sockaddr *asa, socklen_t *alen); } + SYS_GETSOCKNAME = 32 // { int|sys||getsockname(int fdes, struct sockaddr *asa, socklen_t *alen); } + SYS_ACCESS = 33 // { int|sys||access(const char *path, int flags); } + SYS_CHFLAGS = 34 // { int|sys||chflags(const char *path, u_long flags); } + SYS_FCHFLAGS = 35 // { int|sys||fchflags(int fd, u_long flags); } + SYS_SYNC = 36 // { void|sys||sync(void); } + SYS_KILL = 37 // { int|sys||kill(pid_t pid, int signum); } + SYS_GETPPID = 39 // { pid_t|sys||getppid(void); } + SYS_DUP = 41 // { int|sys||dup(int fd); } + SYS_PIPE = 42 // { int|sys||pipe(void); } + SYS_GETEGID = 43 // { gid_t|sys||getegid(void); } + SYS_PROFIL = 44 // { int|sys||profil(char *samples, size_t size, u_long offset, u_int scale); } + SYS_KTRACE = 45 // { int|sys||ktrace(const char *fname, int ops, int facs, pid_t pid); } + SYS_GETGID = 47 // { gid_t|sys||getgid_with_egid(void); } + SYS___GETLOGIN = 49 // { int|sys||__getlogin(char *namebuf, size_t namelen); } + SYS___SETLOGIN = 50 // { int|sys||__setlogin(const char *namebuf); } + SYS_ACCT = 51 // { int|sys||acct(const char *path); } + SYS_IOCTL = 54 // { int|sys||ioctl(int fd, u_long com, ... void *data); } + SYS_REVOKE = 56 // { int|sys||revoke(const char *path); } + SYS_SYMLINK = 57 // { int|sys||symlink(const char *path, const char *link); } + SYS_READLINK = 58 // { ssize_t|sys||readlink(const char *path, char *buf, size_t count); } + SYS_EXECVE = 59 // { int|sys||execve(const char *path, char * const *argp, char * const *envp); } + SYS_UMASK = 60 // { mode_t|sys||umask(mode_t newmask); } + SYS_CHROOT = 61 // { int|sys||chroot(const char *path); } + SYS_VFORK = 66 // { int|sys||vfork(void); } + SYS_SBRK = 69 // { int|sys||sbrk(intptr_t incr); } + SYS_SSTK = 70 // { int|sys||sstk(int incr); } + SYS_VADVISE = 72 // { int|sys||ovadvise(int anom); } + SYS_MUNMAP = 73 // { int|sys||munmap(void *addr, size_t len); } + SYS_MPROTECT = 74 // { int|sys||mprotect(void *addr, size_t len, int prot); } + SYS_MADVISE = 75 // { int|sys||madvise(void *addr, size_t len, int behav); } + SYS_MINCORE = 78 // { int|sys||mincore(void *addr, size_t len, char *vec); } + SYS_GETGROUPS = 79 // { int|sys||getgroups(int gidsetsize, gid_t *gidset); } + SYS_SETGROUPS = 80 // { int|sys||setgroups(int gidsetsize, const gid_t *gidset); } + SYS_GETPGRP = 81 // { int|sys||getpgrp(void); } + SYS_SETPGID = 82 // { int|sys||setpgid(pid_t pid, pid_t pgid); } + SYS_DUP2 = 90 // { int|sys||dup2(int from, int to); } + SYS_FCNTL = 92 // { int|sys||fcntl(int fd, int cmd, ... void *arg); } + SYS_FSYNC = 95 // { int|sys||fsync(int fd); } + SYS_SETPRIORITY = 96 // { int|sys||setpriority(int which, id_t who, int prio); } + SYS_CONNECT = 98 // { int|sys||connect(int s, const struct sockaddr *name, socklen_t namelen); } + SYS_GETPRIORITY = 100 // { int|sys||getpriority(int which, id_t who); } + SYS_BIND = 104 // { int|sys||bind(int s, const struct sockaddr *name, socklen_t namelen); } + SYS_SETSOCKOPT = 105 // { int|sys||setsockopt(int s, int level, int name, const void *val, socklen_t valsize); } + SYS_LISTEN = 106 // { int|sys||listen(int s, int backlog); } + SYS_GETSOCKOPT = 118 // { int|sys||getsockopt(int s, int level, int name, void *val, socklen_t *avalsize); } + SYS_READV = 120 // { ssize_t|sys||readv(int fd, const struct iovec *iovp, int iovcnt); } + SYS_WRITEV = 121 // { ssize_t|sys||writev(int fd, const struct iovec *iovp, int iovcnt); } + SYS_FCHOWN = 123 // { int|sys||fchown(int fd, uid_t uid, gid_t gid); } + SYS_FCHMOD = 124 // { int|sys||fchmod(int fd, mode_t mode); } + SYS_SETREUID = 126 // { int|sys||setreuid(uid_t ruid, uid_t euid); } + SYS_SETREGID = 127 // { int|sys||setregid(gid_t rgid, gid_t egid); } + SYS_RENAME = 128 // { int|sys||rename(const char *from, const char *to); } + SYS_FLOCK = 131 // { int|sys||flock(int fd, int how); } + SYS_MKFIFO = 132 // { int|sys||mkfifo(const char *path, mode_t mode); } + SYS_SENDTO = 133 // { ssize_t|sys||sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); } + SYS_SHUTDOWN = 134 // { int|sys||shutdown(int s, int how); } + SYS_SOCKETPAIR = 135 // { int|sys||socketpair(int domain, int type, int protocol, int *rsv); } + SYS_MKDIR = 136 // { int|sys||mkdir(const char *path, mode_t mode); } + SYS_RMDIR = 137 // { int|sys||rmdir(const char *path); } + SYS_SETSID = 147 // { int|sys||setsid(void); } + SYS_SYSARCH = 165 // { int|sys||sysarch(int op, void *parms); } + SYS_PREAD = 173 // { ssize_t|sys||pread(int fd, void *buf, size_t nbyte, int PAD, off_t offset); } + SYS_PWRITE = 174 // { ssize_t|sys||pwrite(int fd, const void *buf, size_t nbyte, int PAD, off_t offset); } + SYS_NTP_ADJTIME = 176 // { int|sys||ntp_adjtime(struct timex *tp); } + SYS_SETGID = 181 // { int|sys||setgid(gid_t gid); } + SYS_SETEGID = 182 // { int|sys||setegid(gid_t egid); } + SYS_SETEUID = 183 // { int|sys||seteuid(uid_t euid); } + SYS_PATHCONF = 191 // { long|sys||pathconf(const char *path, int name); } + SYS_FPATHCONF = 192 // { long|sys||fpathconf(int fd, int name); } + SYS_GETRLIMIT = 194 // { int|sys||getrlimit(int which, struct rlimit *rlp); } + SYS_SETRLIMIT = 195 // { int|sys||setrlimit(int which, const struct rlimit *rlp); } + SYS_MMAP = 197 // { void *|sys||mmap(void *addr, size_t len, int prot, int flags, int fd, long PAD, off_t pos); } + SYS_LSEEK = 199 // { off_t|sys||lseek(int fd, int PAD, off_t offset, int whence); } + SYS_TRUNCATE = 200 // { int|sys||truncate(const char *path, int PAD, off_t length); } + SYS_FTRUNCATE = 201 // { int|sys||ftruncate(int fd, int PAD, off_t length); } + SYS___SYSCTL = 202 // { int|sys||__sysctl(const int *name, u_int namelen, void *old, size_t *oldlenp, const void *new, size_t newlen); } + SYS_MLOCK = 203 // { int|sys||mlock(const void *addr, size_t len); } + SYS_MUNLOCK = 204 // { int|sys||munlock(const void *addr, size_t len); } + SYS_UNDELETE = 205 // { int|sys||undelete(const char *path); } + SYS_GETPGID = 207 // { pid_t|sys||getpgid(pid_t pid); } + SYS_REBOOT = 208 // { int|sys||reboot(int opt, char *bootstr); } + SYS_POLL = 209 // { int|sys||poll(struct pollfd *fds, u_int nfds, int timeout); } + SYS_SEMGET = 221 // { int|sys||semget(key_t key, int nsems, int semflg); } + SYS_SEMOP = 222 // { int|sys||semop(int semid, struct sembuf *sops, size_t nsops); } + SYS_SEMCONFIG = 223 // { int|sys||semconfig(int flag); } + SYS_MSGGET = 225 // { int|sys||msgget(key_t key, int msgflg); } + SYS_MSGSND = 226 // { int|sys||msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); } + SYS_MSGRCV = 227 // { ssize_t|sys||msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } + SYS_SHMAT = 228 // { void *|sys||shmat(int shmid, const void *shmaddr, int shmflg); } + SYS_SHMDT = 230 // { int|sys||shmdt(const void *shmaddr); } + SYS_SHMGET = 231 // { int|sys||shmget(key_t key, size_t size, int shmflg); } + SYS_TIMER_CREATE = 235 // { int|sys||timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid); } + SYS_TIMER_DELETE = 236 // { int|sys||timer_delete(timer_t timerid); } + SYS_TIMER_GETOVERRUN = 239 // { int|sys||timer_getoverrun(timer_t timerid); } + SYS_FDATASYNC = 241 // { int|sys||fdatasync(int fd); } + SYS_MLOCKALL = 242 // { int|sys||mlockall(int flags); } + SYS_MUNLOCKALL = 243 // { int|sys||munlockall(void); } + SYS_SIGQUEUEINFO = 245 // { int|sys||sigqueueinfo(pid_t pid, const siginfo_t *info); } + SYS_MODCTL = 246 // { int|sys||modctl(int cmd, void *arg); } + SYS___POSIX_RENAME = 270 // { int|sys||__posix_rename(const char *from, const char *to); } + SYS_SWAPCTL = 271 // { int|sys||swapctl(int cmd, void *arg, int misc); } + SYS_MINHERIT = 273 // { int|sys||minherit(void *addr, size_t len, int inherit); } + SYS_LCHMOD = 274 // { int|sys||lchmod(const char *path, mode_t mode); } + SYS_LCHOWN = 275 // { int|sys||lchown(const char *path, uid_t uid, gid_t gid); } + SYS___POSIX_CHOWN = 283 // { int|sys||__posix_chown(const char *path, uid_t uid, gid_t gid); } + SYS___POSIX_FCHOWN = 284 // { int|sys||__posix_fchown(int fd, uid_t uid, gid_t gid); } + SYS___POSIX_LCHOWN = 285 // { int|sys||__posix_lchown(const char *path, uid_t uid, gid_t gid); } + SYS_GETSID = 286 // { pid_t|sys||getsid(pid_t pid); } + SYS___CLONE = 287 // { pid_t|sys||__clone(int flags, void *stack); } + SYS_FKTRACE = 288 // { int|sys||fktrace(int fd, int ops, int facs, pid_t pid); } + SYS_PREADV = 289 // { ssize_t|sys||preadv(int fd, const struct iovec *iovp, int iovcnt, int PAD, off_t offset); } + SYS_PWRITEV = 290 // { ssize_t|sys||pwritev(int fd, const struct iovec *iovp, int iovcnt, int PAD, off_t offset); } + SYS___GETCWD = 296 // { int|sys||__getcwd(char *bufp, size_t length); } + SYS_FCHROOT = 297 // { int|sys||fchroot(int fd); } + SYS_LCHFLAGS = 304 // { int|sys||lchflags(const char *path, u_long flags); } + SYS_ISSETUGID = 305 // { int|sys||issetugid(void); } + SYS_UTRACE = 306 // { int|sys||utrace(const char *label, void *addr, size_t len); } + SYS_GETCONTEXT = 307 // { int|sys||getcontext(struct __ucontext *ucp); } + SYS_SETCONTEXT = 308 // { int|sys||setcontext(const struct __ucontext *ucp); } + SYS__LWP_CREATE = 309 // { int|sys||_lwp_create(const struct __ucontext *ucp, u_long flags, lwpid_t *new_lwp); } + SYS__LWP_EXIT = 310 // { int|sys||_lwp_exit(void); } + SYS__LWP_SELF = 311 // { lwpid_t|sys||_lwp_self(void); } + SYS__LWP_WAIT = 312 // { int|sys||_lwp_wait(lwpid_t wait_for, lwpid_t *departed); } + SYS__LWP_SUSPEND = 313 // { int|sys||_lwp_suspend(lwpid_t target); } + SYS__LWP_CONTINUE = 314 // { int|sys||_lwp_continue(lwpid_t target); } + SYS__LWP_WAKEUP = 315 // { int|sys||_lwp_wakeup(lwpid_t target); } + SYS__LWP_GETPRIVATE = 316 // { void *|sys||_lwp_getprivate(void); } + SYS__LWP_SETPRIVATE = 317 // { void|sys||_lwp_setprivate(void *ptr); } + SYS__LWP_KILL = 318 // { int|sys||_lwp_kill(lwpid_t target, int signo); } + SYS__LWP_DETACH = 319 // { int|sys||_lwp_detach(lwpid_t target); } + SYS__LWP_UNPARK = 321 // { int|sys||_lwp_unpark(lwpid_t target, const void *hint); } + SYS__LWP_UNPARK_ALL = 322 // { ssize_t|sys||_lwp_unpark_all(const lwpid_t *targets, size_t ntargets, const void *hint); } + SYS__LWP_SETNAME = 323 // { int|sys||_lwp_setname(lwpid_t target, const char *name); } + SYS__LWP_GETNAME = 324 // { int|sys||_lwp_getname(lwpid_t target, char *name, size_t len); } + SYS__LWP_CTL = 325 // { int|sys||_lwp_ctl(int features, struct lwpctl **address); } + SYS___SIGACTION_SIGTRAMP = 340 // { int|sys||__sigaction_sigtramp(int signum, const struct sigaction *nsa, struct sigaction *osa, const void *tramp, int vers); } + SYS_PMC_GET_INFO = 341 // { int|sys||pmc_get_info(int ctr, int op, void *args); } + SYS_PMC_CONTROL = 342 // { int|sys||pmc_control(int ctr, int op, void *args); } + SYS_RASCTL = 343 // { int|sys||rasctl(void *addr, size_t len, int op); } + SYS_KQUEUE = 344 // { int|sys||kqueue(void); } + SYS__SCHED_SETPARAM = 346 // { int|sys||_sched_setparam(pid_t pid, lwpid_t lid, int policy, const struct sched_param *params); } + SYS__SCHED_GETPARAM = 347 // { int|sys||_sched_getparam(pid_t pid, lwpid_t lid, int *policy, struct sched_param *params); } + SYS__SCHED_SETAFFINITY = 348 // { int|sys||_sched_setaffinity(pid_t pid, lwpid_t lid, size_t size, const cpuset_t *cpuset); } + SYS__SCHED_GETAFFINITY = 349 // { int|sys||_sched_getaffinity(pid_t pid, lwpid_t lid, size_t size, cpuset_t *cpuset); } + SYS_SCHED_YIELD = 350 // { int|sys||sched_yield(void); } + SYS_FSYNC_RANGE = 354 // { int|sys||fsync_range(int fd, int flags, off_t start, off_t length); } + SYS_UUIDGEN = 355 // { int|sys||uuidgen(struct uuid *store, int count); } + SYS_GETVFSSTAT = 356 // { int|sys||getvfsstat(struct statvfs *buf, size_t bufsize, int flags); } + SYS_STATVFS1 = 357 // { int|sys||statvfs1(const char *path, struct statvfs *buf, int flags); } + SYS_FSTATVFS1 = 358 // { int|sys||fstatvfs1(int fd, struct statvfs *buf, int flags); } + SYS_EXTATTRCTL = 360 // { int|sys||extattrctl(const char *path, int cmd, const char *filename, int attrnamespace, const char *attrname); } + SYS_EXTATTR_SET_FILE = 361 // { int|sys||extattr_set_file(const char *path, int attrnamespace, const char *attrname, const void *data, size_t nbytes); } + SYS_EXTATTR_GET_FILE = 362 // { ssize_t|sys||extattr_get_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_FILE = 363 // { int|sys||extattr_delete_file(const char *path, int attrnamespace, const char *attrname); } + SYS_EXTATTR_SET_FD = 364 // { int|sys||extattr_set_fd(int fd, int attrnamespace, const char *attrname, const void *data, size_t nbytes); } + SYS_EXTATTR_GET_FD = 365 // { ssize_t|sys||extattr_get_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_FD = 366 // { int|sys||extattr_delete_fd(int fd, int attrnamespace, const char *attrname); } + SYS_EXTATTR_SET_LINK = 367 // { int|sys||extattr_set_link(const char *path, int attrnamespace, const char *attrname, const void *data, size_t nbytes); } + SYS_EXTATTR_GET_LINK = 368 // { ssize_t|sys||extattr_get_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_LINK = 369 // { int|sys||extattr_delete_link(const char *path, int attrnamespace, const char *attrname); } + SYS_EXTATTR_LIST_FD = 370 // { ssize_t|sys||extattr_list_fd(int fd, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_FILE = 371 // { ssize_t|sys||extattr_list_file(const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_LINK = 372 // { ssize_t|sys||extattr_list_link(const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_SETXATTR = 375 // { int|sys||setxattr(const char *path, const char *name, const void *value, size_t size, int flags); } + SYS_LSETXATTR = 376 // { int|sys||lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags); } + SYS_FSETXATTR = 377 // { int|sys||fsetxattr(int fd, const char *name, const void *value, size_t size, int flags); } + SYS_GETXATTR = 378 // { int|sys||getxattr(const char *path, const char *name, void *value, size_t size); } + SYS_LGETXATTR = 379 // { int|sys||lgetxattr(const char *path, const char *name, void *value, size_t size); } + SYS_FGETXATTR = 380 // { int|sys||fgetxattr(int fd, const char *name, void *value, size_t size); } + SYS_LISTXATTR = 381 // { int|sys||listxattr(const char *path, char *list, size_t size); } + SYS_LLISTXATTR = 382 // { int|sys||llistxattr(const char *path, char *list, size_t size); } + SYS_FLISTXATTR = 383 // { int|sys||flistxattr(int fd, char *list, size_t size); } + SYS_REMOVEXATTR = 384 // { int|sys||removexattr(const char *path, const char *name); } + SYS_LREMOVEXATTR = 385 // { int|sys||lremovexattr(const char *path, const char *name); } + SYS_FREMOVEXATTR = 386 // { int|sys||fremovexattr(int fd, const char *name); } + SYS_GETDENTS = 390 // { int|sys|30|getdents(int fd, char *buf, size_t count); } + SYS_SOCKET = 394 // { int|sys|30|socket(int domain, int type, int protocol); } + SYS_GETFH = 395 // { int|sys|30|getfh(const char *fname, void *fhp, size_t *fh_size); } + SYS_MOUNT = 410 // { int|sys|50|mount(const char *type, const char *path, int flags, void *data, size_t data_len); } + SYS_MREMAP = 411 // { void *|sys||mremap(void *old_address, size_t old_size, void *new_address, size_t new_size, int flags); } + SYS_PSET_CREATE = 412 // { int|sys||pset_create(psetid_t *psid); } + SYS_PSET_DESTROY = 413 // { int|sys||pset_destroy(psetid_t psid); } + SYS_PSET_ASSIGN = 414 // { int|sys||pset_assign(psetid_t psid, cpuid_t cpuid, psetid_t *opsid); } + SYS__PSET_BIND = 415 // { int|sys||_pset_bind(idtype_t idtype, id_t first_id, id_t second_id, psetid_t psid, psetid_t *opsid); } + SYS_POSIX_FADVISE = 416 // { int|sys|50|posix_fadvise(int fd, int PAD, off_t offset, off_t len, int advice); } + SYS_SELECT = 417 // { int|sys|50|select(int nd, fd_set *in, fd_set *ou, fd_set *ex, struct timeval *tv); } + SYS_GETTIMEOFDAY = 418 // { int|sys|50|gettimeofday(struct timeval *tp, void *tzp); } + SYS_SETTIMEOFDAY = 419 // { int|sys|50|settimeofday(const struct timeval *tv, const void *tzp); } + SYS_UTIMES = 420 // { int|sys|50|utimes(const char *path, const struct timeval *tptr); } + SYS_ADJTIME = 421 // { int|sys|50|adjtime(const struct timeval *delta, struct timeval *olddelta); } + SYS_FUTIMES = 423 // { int|sys|50|futimes(int fd, const struct timeval *tptr); } + SYS_LUTIMES = 424 // { int|sys|50|lutimes(const char *path, const struct timeval *tptr); } + SYS_SETITIMER = 425 // { int|sys|50|setitimer(int which, const struct itimerval *itv, struct itimerval *oitv); } + SYS_GETITIMER = 426 // { int|sys|50|getitimer(int which, struct itimerval *itv); } + SYS_CLOCK_GETTIME = 427 // { int|sys|50|clock_gettime(clockid_t clock_id, struct timespec *tp); } + SYS_CLOCK_SETTIME = 428 // { int|sys|50|clock_settime(clockid_t clock_id, const struct timespec *tp); } + SYS_CLOCK_GETRES = 429 // { int|sys|50|clock_getres(clockid_t clock_id, struct timespec *tp); } + SYS_NANOSLEEP = 430 // { int|sys|50|nanosleep(const struct timespec *rqtp, struct timespec *rmtp); } + SYS___SIGTIMEDWAIT = 431 // { int|sys|50|__sigtimedwait(const sigset_t *set, siginfo_t *info, struct timespec *timeout); } + SYS__LWP_PARK = 434 // { int|sys|50|_lwp_park(const struct timespec *ts, lwpid_t unpark, const void *hint, const void *unparkhint); } + SYS_KEVENT = 435 // { int|sys|50|kevent(int fd, const struct kevent *changelist, size_t nchanges, struct kevent *eventlist, size_t nevents, const struct timespec *timeout); } + SYS_PSELECT = 436 // { int|sys|50|pselect(int nd, fd_set *in, fd_set *ou, fd_set *ex, const struct timespec *ts, const sigset_t *mask); } + SYS_POLLTS = 437 // { int|sys|50|pollts(struct pollfd *fds, u_int nfds, const struct timespec *ts, const sigset_t *mask); } + SYS_STAT = 439 // { int|sys|50|stat(const char *path, struct stat *ub); } + SYS_FSTAT = 440 // { int|sys|50|fstat(int fd, struct stat *sb); } + SYS_LSTAT = 441 // { int|sys|50|lstat(const char *path, struct stat *ub); } + SYS___SEMCTL = 442 // { int|sys|50|__semctl(int semid, int semnum, int cmd, ... union __semun *arg); } + SYS_SHMCTL = 443 // { int|sys|50|shmctl(int shmid, int cmd, struct shmid_ds *buf); } + SYS_MSGCTL = 444 // { int|sys|50|msgctl(int msqid, int cmd, struct msqid_ds *buf); } + SYS_GETRUSAGE = 445 // { int|sys|50|getrusage(int who, struct rusage *rusage); } + SYS_TIMER_SETTIME = 446 // { int|sys|50|timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue); } + SYS_TIMER_GETTIME = 447 // { int|sys|50|timer_gettime(timer_t timerid, struct itimerspec *value); } + SYS_NTP_GETTIME = 448 // { int|sys|50|ntp_gettime(struct ntptimeval *ntvp); } + SYS_WAIT4 = 449 // { int|sys|50|wait4(pid_t pid, int *status, int options, struct rusage *rusage); } + SYS_MKNOD = 450 // { int|sys|50|mknod(const char *path, mode_t mode, dev_t dev); } + SYS_FHSTAT = 451 // { int|sys|50|fhstat(const void *fhp, size_t fh_size, struct stat *sb); } + SYS_PIPE2 = 453 // { int|sys||pipe2(int *fildes, int flags); } + SYS_DUP3 = 454 // { int|sys||dup3(int from, int to, int flags); } + SYS_KQUEUE1 = 455 // { int|sys||kqueue1(int flags); } + SYS_PACCEPT = 456 // { int|sys||paccept(int s, struct sockaddr *name, socklen_t *anamelen, const sigset_t *mask, int flags); } + SYS_LINKAT = 457 // { int|sys||linkat(int fd1, const char *name1, int fd2, const char *name2, int flags); } + SYS_RENAMEAT = 458 // { int|sys||renameat(int fromfd, const char *from, int tofd, const char *to); } + SYS_MKFIFOAT = 459 // { int|sys||mkfifoat(int fd, const char *path, mode_t mode); } + SYS_MKNODAT = 460 // { int|sys||mknodat(int fd, const char *path, mode_t mode, uint32_t dev); } + SYS_MKDIRAT = 461 // { int|sys||mkdirat(int fd, const char *path, mode_t mode); } + SYS_FACCESSAT = 462 // { int|sys||faccessat(int fd, const char *path, int amode, int flag); } + SYS_FCHMODAT = 463 // { int|sys||fchmodat(int fd, const char *path, mode_t mode, int flag); } + SYS_FCHOWNAT = 464 // { int|sys||fchownat(int fd, const char *path, uid_t owner, gid_t group, int flag); } + SYS_FEXECVE = 465 // { int|sys||fexecve(int fd, char * const *argp, char * const *envp); } + SYS_FSTATAT = 466 // { int|sys||fstatat(int fd, const char *path, struct stat *buf, int flag); } + SYS_UTIMENSAT = 467 // { int|sys||utimensat(int fd, const char *path, const struct timespec *tptr, int flag); } + SYS_OPENAT = 468 // { int|sys||openat(int fd, const char *path, int oflags, ... mode_t mode); } + SYS_READLINKAT = 469 // { int|sys||readlinkat(int fd, const char *path, char *buf, size_t bufsize); } + SYS_SYMLINKAT = 470 // { int|sys||symlinkat(const char *path1, int fd, const char *path2); } + SYS_UNLINKAT = 471 // { int|sys||unlinkat(int fd, const char *path, int flag); } + SYS_FUTIMENS = 472 // { int|sys||futimens(int fd, const struct timespec *tptr); } + SYS___QUOTACTL = 473 // { int|sys||__quotactl(const char *path, struct quotactl_args *args); } + SYS_POSIX_SPAWN = 474 // { int|sys||posix_spawn(pid_t *pid, const char *path, const struct posix_spawn_file_actions *file_actions, const struct posix_spawnattr *attrp, char *const *argv, char *const *envp); } + SYS_RECVMMSG = 475 // { int|sys||recvmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags, struct timespec *timeout); } + SYS_SENDMMSG = 476 // { int|sys||sendmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags); } +) diff --git a/src/pkg/syscall/zsysnum_openbsd_386.go b/src/pkg/syscall/zsysnum_openbsd_386.go index 1d97fe0db..82c98b90e 100644 --- a/src/pkg/syscall/zsysnum_openbsd_386.go +++ b/src/pkg/syscall/zsysnum_openbsd_386.go @@ -1,4 +1,4 @@ -// mksysnum_openbsd.pl +// mksysnum_openbsd.pl // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall @@ -11,6 +11,7 @@ const ( SYS_OPEN = 5 // { int sys_open(const char *path, \ SYS_CLOSE = 6 // { int sys_close(int fd); } SYS_WAIT4 = 7 // { pid_t sys_wait4(pid_t pid, int *status, int options, \ + SYS___TFORK = 8 // { int sys___tfork(const struct __tfork *param, \ SYS_LINK = 9 // { int sys_link(const char *path, const char *link); } SYS_UNLINK = 10 // { int sys_unlink(const char *path); } SYS_CHDIR = 12 // { int sys_chdir(const char *path); } @@ -19,6 +20,7 @@ const ( SYS_CHMOD = 15 // { int sys_chmod(const char *path, mode_t mode); } SYS_CHOWN = 16 // { int sys_chown(const char *path, uid_t uid, \ SYS_OBREAK = 17 // { int sys_obreak(char *nsize); } break + SYS_GETDTABLECOUNT = 18 // { int sys_getdtablecount(void); } SYS_GETPID = 20 // { pid_t sys_getpid(void); } SYS_MOUNT = 21 // { int sys_mount(const char *type, const char *path, \ SYS_UNMOUNT = 22 // { int sys_unmount(const char *path, int flags); } @@ -111,10 +113,6 @@ const ( SYS_SETGID = 181 // { int sys_setgid(gid_t gid); } SYS_SETEGID = 182 // { int sys_setegid(gid_t egid); } SYS_SETEUID = 183 // { int sys_seteuid(uid_t euid); } - LFS_BMAPV = 184 // { int lfs_bmapv(fsid_t *fsidp, \ - LFS_MARKV = 185 // { int lfs_markv(fsid_t *fsidp, \ - LFS_SEGCLEAN = 186 // { int lfs_segclean(fsid_t *fsidp, u_long segment); } - LFS_SEGWAIT = 187 // { int lfs_segwait(fsid_t *fsidp, struct timeval *tv); } SYS_PATHCONF = 191 // { long sys_pathconf(const char *path, int name); } SYS_FPATHCONF = 192 // { long sys_fpathconf(int fd, int name); } SYS_SWAPCTL = 193 // { int sys_swapctl(int cmd, const void *arg, int misc); } @@ -129,7 +127,6 @@ const ( SYS_MUNLOCK = 204 // { int sys_munlock(const void *addr, size_t len); } SYS_FUTIMES = 206 // { int sys_futimes(int fd, \ SYS_GETPGID = 207 // { pid_t sys_getpgid(pid_t pid); } - SYS_NNPFSPIOCTL = 208 // { int sys_nnpfspioctl(int operation, char *a_pathP, \ SYS_SEMGET = 221 // { int sys_semget(key_t key, int nsems, int semflg); } SYS_MSGGET = 225 // { int sys_msgget(key_t key, int msgflg); } SYS_MSGSND = 226 // { int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, \ @@ -141,7 +138,6 @@ const ( SYS_CLOCK_GETRES = 234 // { int sys_clock_getres(clockid_t clock_id, \ SYS_NANOSLEEP = 240 // { int sys_nanosleep(const struct timespec *rqtp, \ SYS_MINHERIT = 250 // { int sys_minherit(void *addr, size_t len, \ - SYS_RFORK = 251 // { int sys_rfork(int flags); } SYS_POLL = 252 // { int sys_poll(struct pollfd *fds, \ SYS_ISSETUGID = 253 // { int sys_issetugid(void); } SYS_LCHOWN = 254 // { int sys_lchown(const char *path, uid_t uid, gid_t gid); } @@ -201,7 +197,6 @@ const ( SYS_UNLINKAT = 325 // { int sys_unlinkat(int fd, const char *path, \ SYS_UTIMENSAT = 326 // { int sys_utimensat(int fd, const char *path, \ SYS_FUTIMENS = 327 // { int sys_futimens(int fd, \ - SYS___TFORK = 328 // { int sys___tfork(struct __tfork *param); } SYS___SET_TCB = 329 // { void sys___set_tcb(void *tcb); } SYS___GET_TCB = 330 // { void *sys___get_tcb(void); } ) diff --git a/src/pkg/syscall/zsysnum_openbsd_amd64.go b/src/pkg/syscall/zsysnum_openbsd_amd64.go index 1d97fe0db..82c98b90e 100644 --- a/src/pkg/syscall/zsysnum_openbsd_amd64.go +++ b/src/pkg/syscall/zsysnum_openbsd_amd64.go @@ -1,4 +1,4 @@ -// mksysnum_openbsd.pl +// mksysnum_openbsd.pl // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall @@ -11,6 +11,7 @@ const ( SYS_OPEN = 5 // { int sys_open(const char *path, \ SYS_CLOSE = 6 // { int sys_close(int fd); } SYS_WAIT4 = 7 // { pid_t sys_wait4(pid_t pid, int *status, int options, \ + SYS___TFORK = 8 // { int sys___tfork(const struct __tfork *param, \ SYS_LINK = 9 // { int sys_link(const char *path, const char *link); } SYS_UNLINK = 10 // { int sys_unlink(const char *path); } SYS_CHDIR = 12 // { int sys_chdir(const char *path); } @@ -19,6 +20,7 @@ const ( SYS_CHMOD = 15 // { int sys_chmod(const char *path, mode_t mode); } SYS_CHOWN = 16 // { int sys_chown(const char *path, uid_t uid, \ SYS_OBREAK = 17 // { int sys_obreak(char *nsize); } break + SYS_GETDTABLECOUNT = 18 // { int sys_getdtablecount(void); } SYS_GETPID = 20 // { pid_t sys_getpid(void); } SYS_MOUNT = 21 // { int sys_mount(const char *type, const char *path, \ SYS_UNMOUNT = 22 // { int sys_unmount(const char *path, int flags); } @@ -111,10 +113,6 @@ const ( SYS_SETGID = 181 // { int sys_setgid(gid_t gid); } SYS_SETEGID = 182 // { int sys_setegid(gid_t egid); } SYS_SETEUID = 183 // { int sys_seteuid(uid_t euid); } - LFS_BMAPV = 184 // { int lfs_bmapv(fsid_t *fsidp, \ - LFS_MARKV = 185 // { int lfs_markv(fsid_t *fsidp, \ - LFS_SEGCLEAN = 186 // { int lfs_segclean(fsid_t *fsidp, u_long segment); } - LFS_SEGWAIT = 187 // { int lfs_segwait(fsid_t *fsidp, struct timeval *tv); } SYS_PATHCONF = 191 // { long sys_pathconf(const char *path, int name); } SYS_FPATHCONF = 192 // { long sys_fpathconf(int fd, int name); } SYS_SWAPCTL = 193 // { int sys_swapctl(int cmd, const void *arg, int misc); } @@ -129,7 +127,6 @@ const ( SYS_MUNLOCK = 204 // { int sys_munlock(const void *addr, size_t len); } SYS_FUTIMES = 206 // { int sys_futimes(int fd, \ SYS_GETPGID = 207 // { pid_t sys_getpgid(pid_t pid); } - SYS_NNPFSPIOCTL = 208 // { int sys_nnpfspioctl(int operation, char *a_pathP, \ SYS_SEMGET = 221 // { int sys_semget(key_t key, int nsems, int semflg); } SYS_MSGGET = 225 // { int sys_msgget(key_t key, int msgflg); } SYS_MSGSND = 226 // { int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, \ @@ -141,7 +138,6 @@ const ( SYS_CLOCK_GETRES = 234 // { int sys_clock_getres(clockid_t clock_id, \ SYS_NANOSLEEP = 240 // { int sys_nanosleep(const struct timespec *rqtp, \ SYS_MINHERIT = 250 // { int sys_minherit(void *addr, size_t len, \ - SYS_RFORK = 251 // { int sys_rfork(int flags); } SYS_POLL = 252 // { int sys_poll(struct pollfd *fds, \ SYS_ISSETUGID = 253 // { int sys_issetugid(void); } SYS_LCHOWN = 254 // { int sys_lchown(const char *path, uid_t uid, gid_t gid); } @@ -201,7 +197,6 @@ const ( SYS_UNLINKAT = 325 // { int sys_unlinkat(int fd, const char *path, \ SYS_UTIMENSAT = 326 // { int sys_utimensat(int fd, const char *path, \ SYS_FUTIMENS = 327 // { int sys_futimens(int fd, \ - SYS___TFORK = 328 // { int sys___tfork(struct __tfork *param); } SYS___SET_TCB = 329 // { void sys___set_tcb(void *tcb); } SYS___GET_TCB = 330 // { void *sys___get_tcb(void); } ) diff --git a/src/pkg/syscall/zsysnum_plan9_amd64.go b/src/pkg/syscall/zsysnum_plan9_amd64.go new file mode 100644 index 000000000..c03864647 --- /dev/null +++ b/src/pkg/syscall/zsysnum_plan9_amd64.go @@ -0,0 +1,48 @@ +// mksysnum_plan9.sh /media/sys/src/libc/9syscall/sys.h +// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT + +package syscall + +const ( + SYS_SYSR1 = 0 + SYS_BIND = 2 + SYS_CHDIR = 3 + SYS_CLOSE = 4 + SYS_DUP = 5 + SYS_ALARM = 6 + SYS_EXEC = 7 + SYS_EXITS = 8 + SYS_FAUTH = 10 + SYS_SEGBRK = 12 + SYS_OPEN = 14 + SYS_OSEEK = 16 + SYS_SLEEP = 17 + SYS_RFORK = 19 + SYS_PIPE = 21 + SYS_CREATE = 22 + SYS_FD2PATH = 23 + SYS_BRK_ = 24 + SYS_REMOVE = 25 + SYS_NOTIFY = 28 + SYS_NOTED = 29 + SYS_SEGATTACH = 30 + SYS_SEGDETACH = 31 + SYS_SEGFREE = 32 + SYS_SEGFLUSH = 33 + SYS_RENDEZVOUS = 34 + SYS_UNMOUNT = 35 + SYS_SEMACQUIRE = 37 + SYS_SEMRELEASE = 38 + SYS_SEEK = 39 + SYS_FVERSION = 40 + SYS_ERRSTR = 41 + SYS_STAT = 42 + SYS_FSTAT = 43 + SYS_WSTAT = 44 + SYS_FWSTAT = 45 + SYS_MOUNT = 46 + SYS_AWAIT = 47 + SYS_PREAD = 50 + SYS_PWRITE = 51 + SYS_NANOTIME = 60 +) diff --git a/src/pkg/syscall/ztypes_darwin_386.go b/src/pkg/syscall/ztypes_darwin_386.go index 566b02195..71346fbc1 100644 --- a/src/pkg/syscall/ztypes_darwin_386.go +++ b/src/pkg/syscall/ztypes_darwin_386.go @@ -226,6 +226,12 @@ type Cmsghdr struct { Type int32 } +type Inet4Pktinfo struct { + Ifindex uint32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + type Inet6Pktinfo struct { Addr [16]byte /* in6_addr */ Ifindex uint32 @@ -242,6 +248,7 @@ const ( SizeofIPv6Mreq = 0x14 SizeofMsghdr = 0x1c SizeofCmsghdr = 0xc + SizeofInet4Pktinfo = 0xc SizeofInet6Pktinfo = 0x14 ) @@ -416,3 +423,13 @@ type BpfHdr struct { Hdrlen uint16 Pad_cgo_0 [2]byte } + +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} diff --git a/src/pkg/syscall/ztypes_darwin_amd64.go b/src/pkg/syscall/ztypes_darwin_amd64.go index 2c6531158..f0809fe4a 100644 --- a/src/pkg/syscall/ztypes_darwin_amd64.go +++ b/src/pkg/syscall/ztypes_darwin_amd64.go @@ -234,6 +234,12 @@ type Cmsghdr struct { Type int32 } +type Inet4Pktinfo struct { + Ifindex uint32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + type Inet6Pktinfo struct { Addr [16]byte /* in6_addr */ Ifindex uint32 @@ -250,6 +256,7 @@ const ( SizeofIPv6Mreq = 0x14 SizeofMsghdr = 0x30 SizeofCmsghdr = 0xc + SizeofInet4Pktinfo = 0xc SizeofInet6Pktinfo = 0x14 ) @@ -425,3 +432,14 @@ type BpfHdr struct { Hdrlen uint16 Pad_cgo_0 [2]byte } + +type Termios struct { + Iflag uint64 + Oflag uint64 + Cflag uint64 + Lflag uint64 + Cc [20]uint8 + Pad_cgo_0 [4]byte + Ispeed uint64 + Ospeed uint64 +} diff --git a/src/pkg/syscall/ztypes_freebsd_386.go b/src/pkg/syscall/ztypes_freebsd_386.go index 4149948c5..89de58e53 100644 --- a/src/pkg/syscall/ztypes_freebsd_386.go +++ b/src/pkg/syscall/ztypes_freebsd_386.go @@ -271,12 +271,13 @@ type FdSet struct { } const ( - SizeofIfMsghdr = 0x60 - SizeofIfData = 0x50 - SizeofIfaMsghdr = 0x14 - SizeofIfmaMsghdr = 0x10 - SizeofRtMsghdr = 0x5c - SizeofRtMetrics = 0x38 + SizeofIfMsghdr = 0x60 + SizeofIfData = 0x50 + SizeofIfaMsghdr = 0x14 + SizeofIfmaMsghdr = 0x10 + SizeofIfAnnounceMsghdr = 0x18 + SizeofRtMsghdr = 0x5c + SizeofRtMetrics = 0x38 ) type IfMsghdr struct { @@ -339,6 +340,15 @@ type IfmaMsghdr struct { Pad_cgo_0 [2]byte } +type IfAnnounceMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Name [16]int8 + What uint16 +} + type RtMsghdr struct { Msglen uint16 Version uint8 diff --git a/src/pkg/syscall/ztypes_freebsd_amd64.go b/src/pkg/syscall/ztypes_freebsd_amd64.go index 80c12f607..d5e87682d 100644 --- a/src/pkg/syscall/ztypes_freebsd_amd64.go +++ b/src/pkg/syscall/ztypes_freebsd_amd64.go @@ -273,12 +273,13 @@ type FdSet struct { } const ( - SizeofIfMsghdr = 0xa8 - SizeofIfData = 0x98 - SizeofIfaMsghdr = 0x14 - SizeofIfmaMsghdr = 0x10 - SizeofRtMsghdr = 0x98 - SizeofRtMetrics = 0x70 + SizeofIfMsghdr = 0xa8 + SizeofIfData = 0x98 + SizeofIfaMsghdr = 0x14 + SizeofIfmaMsghdr = 0x10 + SizeofIfAnnounceMsghdr = 0x18 + SizeofRtMsghdr = 0x98 + SizeofRtMetrics = 0x70 ) type IfMsghdr struct { @@ -341,6 +342,15 @@ type IfmaMsghdr struct { Pad_cgo_0 [2]byte } +type IfAnnounceMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Name [16]int8 + What uint16 +} + type RtMsghdr struct { Msglen uint16 Version uint8 diff --git a/src/pkg/syscall/ztypes_freebsd_arm.go b/src/pkg/syscall/ztypes_freebsd_arm.go new file mode 100644 index 000000000..4f67a39e6 --- /dev/null +++ b/src/pkg/syscall/ztypes_freebsd_arm.go @@ -0,0 +1,436 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package syscall + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int32 +} + +type Timeval struct { + Sec int64 + Usec int32 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur int64 + Max int64 +} + +type _Gid_t uint32 + +const ( + O_CLOEXEC = 0 // not supported +) + +const ( + S_IFMT = 0xf000 + S_IFIFO = 0x1000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFBLK = 0x6000 + S_IFREG = 0x8000 + S_IFLNK = 0xa000 + S_IFSOCK = 0xc000 + S_ISUID = 0x800 + S_ISGID = 0x400 + S_ISVTX = 0x200 + S_IRUSR = 0x100 + S_IWUSR = 0x80 + S_IXUSR = 0x40 +) + +type Stat_t struct { + Dev uint32 + Ino uint32 + Mode uint16 + Nlink uint16 + Uid uint32 + Gid uint32 + Rdev uint32 + Atimespec Timespec + Mtimespec Timespec + Ctimespec Timespec + Size int64 + Blocks int64 + Blksize uint32 + Flags uint32 + Gen uint32 + Lspare int32 + Birthtimespec Timespec + Pad_cgo_0 [4]byte +} + +type Statfs_t struct { + Version uint32 + Type uint32 + Flags uint64 + Bsize uint64 + Iosize uint64 + Blocks uint64 + Bfree uint64 + Bavail int64 + Files uint64 + Ffree int64 + Syncwrites uint64 + Asyncwrites uint64 + Syncreads uint64 + Asyncreads uint64 + Spare [10]uint64 + Namemax uint32 + Owner uint32 + Fsid Fsid + Charspare [80]int8 + Fstypename [16]int8 + Mntfromname [88]int8 + Mntonname [88]int8 +} + +type Flock_t struct { + Start int64 + Len int64 + Pid int32 + Type int16 + Whence int16 + Sysid int32 +} + +type Dirent struct { + Fileno uint32 + Reclen uint16 + Type uint8 + Namlen uint8 + Name [256]int8 +} + +type Fsid struct { + Val [2]int32 +} + +type RawSockaddrInet4 struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type RawSockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type RawSockaddrUnix struct { + Len uint8 + Family uint8 + Path [104]int8 + Pad_cgo_0 [2]byte +} + +type RawSockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [46]int8 + Pad_cgo_0 [2]byte +} + +type RawSockaddr struct { + Len uint8 + Family uint8 + Data [14]int8 +} + +type RawSockaddrAny struct { + Addr RawSockaddr + Pad [92]int8 +} + +type _Socklen uint32 + +type Linger struct { + Onoff int32 + Linger int32 +} + +type Iovec struct { + Base *byte + Len uint32 +} + +type IPMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type IPMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type IPv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type Msghdr struct { + Name *byte + Namelen uint32 + Iov *Iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type Cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type Inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +const ( + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6c + SizeofSockaddrDatalink = 0x38 + SizeofLinger = 0x8 + SizeofIPMreq = 0x8 + SizeofIPMreqn = 0xc + SizeofIPv6Mreq = 0x14 + SizeofMsghdr = 0x1c + SizeofCmsghdr = 0xc + SizeofInet6Pktinfo = 0x14 +) + +const ( + PTRACE_TRACEME = 0x0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 +) + +type Kevent_t struct { + Ident uint32 + Filter int16 + Flags uint16 + Fflags uint32 + Data int32 + Udata *byte +} + +type FdSet struct { + X__fds_bits [32]uint32 +} + +const ( + SizeofIfMsghdr = 0x68 + SizeofIfData = 0x58 + SizeofIfaMsghdr = 0x14 + SizeofIfmaMsghdr = 0x10 + SizeofIfAnnounceMsghdr = 0x18 + SizeofRtMsghdr = 0x5c + SizeofRtMetrics = 0x38 +) + +type IfMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte + Data IfData +} + +type IfData struct { + Type uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Link_state uint8 + Spare_char1 uint8 //Vhid uint8 + Spare_char2 uint8 + Datalen uint8 + Mtu uint32 + Metric uint32 + Baudrate uint32 + Ipackets uint32 + Ierrors uint32 + Opackets uint32 + Oerrors uint32 + Collisions uint32 + Ibytes uint32 + Obytes uint32 + Imcasts uint32 + Omcasts uint32 + Iqdrops uint32 + Noproto uint32 + Hwassist uint32 + Epoch int64 + Lastchange Timeval +} + +type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte + Metric int32 +} + +type IfmaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte +} + +type IfAnnounceMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Name [16]int8 + What uint16 +} + +type RtMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_cgo_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Fmask int32 + Inits uint32 + Rmx RtMetrics +} + +type RtMetrics struct { + Locks uint32 + Mtu uint32 + Hopcount uint32 + Expire uint32 + Recvpipe uint32 + Sendpipe uint32 + Ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Pksent uint32 + Weight uint32 + Filler [3]uint32 +} + +const ( + SizeofBpfVersion = 0x4 + SizeofBpfStat = 0x8 + SizeofBpfZbuf = 0xc + SizeofBpfProgram = 0x8 + SizeofBpfInsn = 0x8 + SizeofBpfHdr = 0x18 + SizeofBpfZbufHeader = 0x20 +) + +type BpfVersion struct { + Major uint16 + Minor uint16 +} + +type BpfStat struct { + Recv uint32 + Drop uint32 +} + +type BpfZbuf struct { + Bufa *byte + Bufb *byte + Buflen uint32 +} + +type BpfProgram struct { + Len uint32 + Insns *BpfInsn +} + +type BpfInsn struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +type BpfHdr struct { + Tstamp Timeval + Caplen uint32 + Datalen uint32 + Hdrlen uint16 + Pad_cgo_0 [2]byte +} + +type BpfZbufHeader struct { + Kernel_gen uint32 + Kernel_len uint32 + User_gen uint32 + X_bzh_pad [5]uint32 +} diff --git a/src/pkg/syscall/ztypes_linux_386.go b/src/pkg/syscall/ztypes_linux_386.go index fcead2f97..e1c30b649 100644 --- a/src/pkg/syscall/ztypes_linux_386.go +++ b/src/pkg/syscall/ztypes_linux_386.go @@ -231,7 +231,7 @@ type Cmsghdr struct { Len uint32 Level int32 Type int32 - X__cmsg_data [0]byte + X__cmsg_data [0]uint8 } type Inet4Pktinfo struct { @@ -251,6 +251,40 @@ type Ucred struct { Gid uint32 } +type TCPInfo struct { + State uint8 + Ca_state uint8 + Retransmits uint8 + Probes uint8 + Backoff uint8 + Options uint8 + Pad_cgo_0 [2]byte + Rto uint32 + Ato uint32 + Snd_mss uint32 + Rcv_mss uint32 + Unacked uint32 + Sacked uint32 + Lost uint32 + Retrans uint32 + Fackets uint32 + Last_data_sent uint32 + Last_ack_sent uint32 + Last_data_recv uint32 + Last_ack_recv uint32 + Pmtu uint32 + Rcv_ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Advmss uint32 + Reordering uint32 + Rcv_rtt uint32 + Rcv_space uint32 + Total_retrans uint32 +} + const ( SizeofSockaddrInet4 = 0x10 SizeofSockaddrInet6 = 0x1c @@ -267,84 +301,101 @@ const ( SizeofInet4Pktinfo = 0xc SizeofInet6Pktinfo = 0x14 SizeofUcred = 0xc + SizeofTCPInfo = 0x68 ) const ( - IFA_UNSPEC = 0x0 - IFA_ADDRESS = 0x1 - IFA_LOCAL = 0x2 - IFA_LABEL = 0x3 - IFA_BROADCAST = 0x4 - IFA_ANYCAST = 0x5 - IFA_CACHEINFO = 0x6 - IFA_MULTICAST = 0x7 - IFLA_UNSPEC = 0x0 - IFLA_ADDRESS = 0x1 - IFLA_BROADCAST = 0x2 - IFLA_IFNAME = 0x3 - IFLA_MTU = 0x4 - IFLA_LINK = 0x5 - IFLA_QDISC = 0x6 - IFLA_STATS = 0x7 - IFLA_COST = 0x8 - IFLA_PRIORITY = 0x9 - IFLA_MASTER = 0xa - IFLA_WIRELESS = 0xb - IFLA_PROTINFO = 0xc - IFLA_TXQLEN = 0xd - IFLA_MAP = 0xe - IFLA_WEIGHT = 0xf - IFLA_OPERSTATE = 0x10 - IFLA_LINKMODE = 0x11 - IFLA_LINKINFO = 0x12 - IFLA_NET_NS_PID = 0x13 - IFLA_IFALIAS = 0x14 - IFLA_MAX = 0x1c - RT_SCOPE_UNIVERSE = 0x0 - RT_SCOPE_SITE = 0xc8 - RT_SCOPE_LINK = 0xfd - RT_SCOPE_HOST = 0xfe - RT_SCOPE_NOWHERE = 0xff - RT_TABLE_UNSPEC = 0x0 - RT_TABLE_COMPAT = 0xfc - RT_TABLE_DEFAULT = 0xfd - RT_TABLE_MAIN = 0xfe - RT_TABLE_LOCAL = 0xff - RT_TABLE_MAX = 0xffffffff - RTA_UNSPEC = 0x0 - RTA_DST = 0x1 - RTA_SRC = 0x2 - RTA_IIF = 0x3 - RTA_OIF = 0x4 - RTA_GATEWAY = 0x5 - RTA_PRIORITY = 0x6 - RTA_PREFSRC = 0x7 - RTA_METRICS = 0x8 - RTA_MULTIPATH = 0x9 - RTA_FLOW = 0xb - RTA_CACHEINFO = 0xc - RTA_TABLE = 0xf - RTN_UNSPEC = 0x0 - RTN_UNICAST = 0x1 - RTN_LOCAL = 0x2 - RTN_BROADCAST = 0x3 - RTN_ANYCAST = 0x4 - RTN_MULTICAST = 0x5 - RTN_BLACKHOLE = 0x6 - RTN_UNREACHABLE = 0x7 - RTN_PROHIBIT = 0x8 - RTN_THROW = 0x9 - RTN_NAT = 0xa - RTN_XRESOLVE = 0xb - SizeofNlMsghdr = 0x10 - SizeofNlMsgerr = 0x14 - SizeofRtGenmsg = 0x1 - SizeofNlAttr = 0x4 - SizeofRtAttr = 0x4 - SizeofIfInfomsg = 0x10 - SizeofIfAddrmsg = 0x8 - SizeofRtMsg = 0xc - SizeofRtNexthop = 0x8 + IFA_UNSPEC = 0x0 + IFA_ADDRESS = 0x1 + IFA_LOCAL = 0x2 + IFA_LABEL = 0x3 + IFA_BROADCAST = 0x4 + IFA_ANYCAST = 0x5 + IFA_CACHEINFO = 0x6 + IFA_MULTICAST = 0x7 + IFLA_UNSPEC = 0x0 + IFLA_ADDRESS = 0x1 + IFLA_BROADCAST = 0x2 + IFLA_IFNAME = 0x3 + IFLA_MTU = 0x4 + IFLA_LINK = 0x5 + IFLA_QDISC = 0x6 + IFLA_STATS = 0x7 + IFLA_COST = 0x8 + IFLA_PRIORITY = 0x9 + IFLA_MASTER = 0xa + IFLA_WIRELESS = 0xb + IFLA_PROTINFO = 0xc + IFLA_TXQLEN = 0xd + IFLA_MAP = 0xe + IFLA_WEIGHT = 0xf + IFLA_OPERSTATE = 0x10 + IFLA_LINKMODE = 0x11 + IFLA_LINKINFO = 0x12 + IFLA_NET_NS_PID = 0x13 + IFLA_IFALIAS = 0x14 + IFLA_MAX = 0x1d + RT_SCOPE_UNIVERSE = 0x0 + RT_SCOPE_SITE = 0xc8 + RT_SCOPE_LINK = 0xfd + RT_SCOPE_HOST = 0xfe + RT_SCOPE_NOWHERE = 0xff + RT_TABLE_UNSPEC = 0x0 + RT_TABLE_COMPAT = 0xfc + RT_TABLE_DEFAULT = 0xfd + RT_TABLE_MAIN = 0xfe + RT_TABLE_LOCAL = 0xff + RT_TABLE_MAX = 0xffffffff + RTA_UNSPEC = 0x0 + RTA_DST = 0x1 + RTA_SRC = 0x2 + RTA_IIF = 0x3 + RTA_OIF = 0x4 + RTA_GATEWAY = 0x5 + RTA_PRIORITY = 0x6 + RTA_PREFSRC = 0x7 + RTA_METRICS = 0x8 + RTA_MULTIPATH = 0x9 + RTA_FLOW = 0xb + RTA_CACHEINFO = 0xc + RTA_TABLE = 0xf + RTN_UNSPEC = 0x0 + RTN_UNICAST = 0x1 + RTN_LOCAL = 0x2 + RTN_BROADCAST = 0x3 + RTN_ANYCAST = 0x4 + RTN_MULTICAST = 0x5 + RTN_BLACKHOLE = 0x6 + RTN_UNREACHABLE = 0x7 + RTN_PROHIBIT = 0x8 + RTN_THROW = 0x9 + RTN_NAT = 0xa + RTN_XRESOLVE = 0xb + RTNLGRP_NONE = 0x0 + RTNLGRP_LINK = 0x1 + RTNLGRP_NOTIFY = 0x2 + RTNLGRP_NEIGH = 0x3 + RTNLGRP_TC = 0x4 + RTNLGRP_IPV4_IFADDR = 0x5 + RTNLGRP_IPV4_MROUTE = 0x6 + RTNLGRP_IPV4_ROUTE = 0x7 + RTNLGRP_IPV4_RULE = 0x8 + RTNLGRP_IPV6_IFADDR = 0x9 + RTNLGRP_IPV6_MROUTE = 0xa + RTNLGRP_IPV6_ROUTE = 0xb + RTNLGRP_IPV6_IFINFO = 0xc + RTNLGRP_IPV6_PREFIX = 0x12 + RTNLGRP_IPV6_RULE = 0x13 + RTNLGRP_ND_USEROPT = 0x14 + SizeofNlMsghdr = 0x10 + SizeofNlMsgerr = 0x14 + SizeofRtGenmsg = 0x1 + SizeofNlAttr = 0x4 + SizeofRtAttr = 0x4 + SizeofIfInfomsg = 0x10 + SizeofIfAddrmsg = 0x8 + SizeofRtMsg = 0xc + SizeofRtNexthop = 0x8 ) type NlMsghdr struct { @@ -433,7 +484,7 @@ type InotifyEvent struct { Mask uint32 Cookie uint32 Len uint32 - Name [0]byte + Name [0]uint8 } const SizeofInotifyEvent = 0x10 @@ -501,6 +552,10 @@ type EpollEvent struct { Pad int32 } +const ( + _AT_FDCWD = -0x64 +) + type Termios struct { Iflag uint32 Oflag uint32 diff --git a/src/pkg/syscall/ztypes_linux_amd64.go b/src/pkg/syscall/ztypes_linux_amd64.go index d5583a7c2..5800c3c6c 100644 --- a/src/pkg/syscall/ztypes_linux_amd64.go +++ b/src/pkg/syscall/ztypes_linux_amd64.go @@ -233,7 +233,7 @@ type Cmsghdr struct { Len uint64 Level int32 Type int32 - X__cmsg_data [0]byte + X__cmsg_data [0]uint8 } type Inet4Pktinfo struct { @@ -253,6 +253,40 @@ type Ucred struct { Gid uint32 } +type TCPInfo struct { + State uint8 + Ca_state uint8 + Retransmits uint8 + Probes uint8 + Backoff uint8 + Options uint8 + Pad_cgo_0 [2]byte + Rto uint32 + Ato uint32 + Snd_mss uint32 + Rcv_mss uint32 + Unacked uint32 + Sacked uint32 + Lost uint32 + Retrans uint32 + Fackets uint32 + Last_data_sent uint32 + Last_ack_sent uint32 + Last_data_recv uint32 + Last_ack_recv uint32 + Pmtu uint32 + Rcv_ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Advmss uint32 + Reordering uint32 + Rcv_rtt uint32 + Rcv_space uint32 + Total_retrans uint32 +} + const ( SizeofSockaddrInet4 = 0x10 SizeofSockaddrInet6 = 0x1c @@ -269,84 +303,101 @@ const ( SizeofInet4Pktinfo = 0xc SizeofInet6Pktinfo = 0x14 SizeofUcred = 0xc + SizeofTCPInfo = 0x68 ) const ( - IFA_UNSPEC = 0x0 - IFA_ADDRESS = 0x1 - IFA_LOCAL = 0x2 - IFA_LABEL = 0x3 - IFA_BROADCAST = 0x4 - IFA_ANYCAST = 0x5 - IFA_CACHEINFO = 0x6 - IFA_MULTICAST = 0x7 - IFLA_UNSPEC = 0x0 - IFLA_ADDRESS = 0x1 - IFLA_BROADCAST = 0x2 - IFLA_IFNAME = 0x3 - IFLA_MTU = 0x4 - IFLA_LINK = 0x5 - IFLA_QDISC = 0x6 - IFLA_STATS = 0x7 - IFLA_COST = 0x8 - IFLA_PRIORITY = 0x9 - IFLA_MASTER = 0xa - IFLA_WIRELESS = 0xb - IFLA_PROTINFO = 0xc - IFLA_TXQLEN = 0xd - IFLA_MAP = 0xe - IFLA_WEIGHT = 0xf - IFLA_OPERSTATE = 0x10 - IFLA_LINKMODE = 0x11 - IFLA_LINKINFO = 0x12 - IFLA_NET_NS_PID = 0x13 - IFLA_IFALIAS = 0x14 - IFLA_MAX = 0x1c - RT_SCOPE_UNIVERSE = 0x0 - RT_SCOPE_SITE = 0xc8 - RT_SCOPE_LINK = 0xfd - RT_SCOPE_HOST = 0xfe - RT_SCOPE_NOWHERE = 0xff - RT_TABLE_UNSPEC = 0x0 - RT_TABLE_COMPAT = 0xfc - RT_TABLE_DEFAULT = 0xfd - RT_TABLE_MAIN = 0xfe - RT_TABLE_LOCAL = 0xff - RT_TABLE_MAX = 0xffffffff - RTA_UNSPEC = 0x0 - RTA_DST = 0x1 - RTA_SRC = 0x2 - RTA_IIF = 0x3 - RTA_OIF = 0x4 - RTA_GATEWAY = 0x5 - RTA_PRIORITY = 0x6 - RTA_PREFSRC = 0x7 - RTA_METRICS = 0x8 - RTA_MULTIPATH = 0x9 - RTA_FLOW = 0xb - RTA_CACHEINFO = 0xc - RTA_TABLE = 0xf - RTN_UNSPEC = 0x0 - RTN_UNICAST = 0x1 - RTN_LOCAL = 0x2 - RTN_BROADCAST = 0x3 - RTN_ANYCAST = 0x4 - RTN_MULTICAST = 0x5 - RTN_BLACKHOLE = 0x6 - RTN_UNREACHABLE = 0x7 - RTN_PROHIBIT = 0x8 - RTN_THROW = 0x9 - RTN_NAT = 0xa - RTN_XRESOLVE = 0xb - SizeofNlMsghdr = 0x10 - SizeofNlMsgerr = 0x14 - SizeofRtGenmsg = 0x1 - SizeofNlAttr = 0x4 - SizeofRtAttr = 0x4 - SizeofIfInfomsg = 0x10 - SizeofIfAddrmsg = 0x8 - SizeofRtMsg = 0xc - SizeofRtNexthop = 0x8 + IFA_UNSPEC = 0x0 + IFA_ADDRESS = 0x1 + IFA_LOCAL = 0x2 + IFA_LABEL = 0x3 + IFA_BROADCAST = 0x4 + IFA_ANYCAST = 0x5 + IFA_CACHEINFO = 0x6 + IFA_MULTICAST = 0x7 + IFLA_UNSPEC = 0x0 + IFLA_ADDRESS = 0x1 + IFLA_BROADCAST = 0x2 + IFLA_IFNAME = 0x3 + IFLA_MTU = 0x4 + IFLA_LINK = 0x5 + IFLA_QDISC = 0x6 + IFLA_STATS = 0x7 + IFLA_COST = 0x8 + IFLA_PRIORITY = 0x9 + IFLA_MASTER = 0xa + IFLA_WIRELESS = 0xb + IFLA_PROTINFO = 0xc + IFLA_TXQLEN = 0xd + IFLA_MAP = 0xe + IFLA_WEIGHT = 0xf + IFLA_OPERSTATE = 0x10 + IFLA_LINKMODE = 0x11 + IFLA_LINKINFO = 0x12 + IFLA_NET_NS_PID = 0x13 + IFLA_IFALIAS = 0x14 + IFLA_MAX = 0x1d + RT_SCOPE_UNIVERSE = 0x0 + RT_SCOPE_SITE = 0xc8 + RT_SCOPE_LINK = 0xfd + RT_SCOPE_HOST = 0xfe + RT_SCOPE_NOWHERE = 0xff + RT_TABLE_UNSPEC = 0x0 + RT_TABLE_COMPAT = 0xfc + RT_TABLE_DEFAULT = 0xfd + RT_TABLE_MAIN = 0xfe + RT_TABLE_LOCAL = 0xff + RT_TABLE_MAX = 0xffffffff + RTA_UNSPEC = 0x0 + RTA_DST = 0x1 + RTA_SRC = 0x2 + RTA_IIF = 0x3 + RTA_OIF = 0x4 + RTA_GATEWAY = 0x5 + RTA_PRIORITY = 0x6 + RTA_PREFSRC = 0x7 + RTA_METRICS = 0x8 + RTA_MULTIPATH = 0x9 + RTA_FLOW = 0xb + RTA_CACHEINFO = 0xc + RTA_TABLE = 0xf + RTN_UNSPEC = 0x0 + RTN_UNICAST = 0x1 + RTN_LOCAL = 0x2 + RTN_BROADCAST = 0x3 + RTN_ANYCAST = 0x4 + RTN_MULTICAST = 0x5 + RTN_BLACKHOLE = 0x6 + RTN_UNREACHABLE = 0x7 + RTN_PROHIBIT = 0x8 + RTN_THROW = 0x9 + RTN_NAT = 0xa + RTN_XRESOLVE = 0xb + RTNLGRP_NONE = 0x0 + RTNLGRP_LINK = 0x1 + RTNLGRP_NOTIFY = 0x2 + RTNLGRP_NEIGH = 0x3 + RTNLGRP_TC = 0x4 + RTNLGRP_IPV4_IFADDR = 0x5 + RTNLGRP_IPV4_MROUTE = 0x6 + RTNLGRP_IPV4_ROUTE = 0x7 + RTNLGRP_IPV4_RULE = 0x8 + RTNLGRP_IPV6_IFADDR = 0x9 + RTNLGRP_IPV6_MROUTE = 0xa + RTNLGRP_IPV6_ROUTE = 0xb + RTNLGRP_IPV6_IFINFO = 0xc + RTNLGRP_IPV6_PREFIX = 0x12 + RTNLGRP_IPV6_RULE = 0x13 + RTNLGRP_ND_USEROPT = 0x14 + SizeofNlMsghdr = 0x10 + SizeofNlMsgerr = 0x14 + SizeofRtGenmsg = 0x1 + SizeofNlAttr = 0x4 + SizeofRtAttr = 0x4 + SizeofIfInfomsg = 0x10 + SizeofIfAddrmsg = 0x8 + SizeofRtMsg = 0xc + SizeofRtNexthop = 0x8 ) type NlMsghdr struct { @@ -435,7 +486,7 @@ type InotifyEvent struct { Mask uint32 Cookie uint32 Len uint32 - Name [0]byte + Name [0]uint8 } const SizeofInotifyEvent = 0x10 @@ -517,6 +568,10 @@ type EpollEvent struct { Pad int32 } +const ( + _AT_FDCWD = -0x64 +) + type Termios struct { Iflag uint32 Oflag uint32 diff --git a/src/pkg/syscall/ztypes_linux_arm.go b/src/pkg/syscall/ztypes_linux_arm.go index cd680c3ad..4a81d340c 100644 --- a/src/pkg/syscall/ztypes_linux_arm.go +++ b/src/pkg/syscall/ztypes_linux_arm.go @@ -253,6 +253,40 @@ type Ucred struct { Gid uint32 } +type TCPInfo struct { + State uint8 + Ca_state uint8 + Retransmits uint8 + Probes uint8 + Backoff uint8 + Options uint8 + Pad_cgo_0 [2]byte + Rto uint32 + Ato uint32 + Snd_mss uint32 + Rcv_mss uint32 + Unacked uint32 + Sacked uint32 + Lost uint32 + Retrans uint32 + Fackets uint32 + Last_data_sent uint32 + Last_ack_sent uint32 + Last_data_recv uint32 + Last_ack_recv uint32 + Pmtu uint32 + Rcv_ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Advmss uint32 + Reordering uint32 + Rcv_rtt uint32 + Rcv_space uint32 + Total_retrans uint32 +} + const ( SizeofSockaddrInet4 = 0x10 SizeofSockaddrInet6 = 0x1c @@ -269,84 +303,101 @@ const ( SizeofInet4Pktinfo = 0xc SizeofInet6Pktinfo = 0x14 SizeofUcred = 0xc + SizeofTCPInfo = 0x68 ) const ( - IFA_UNSPEC = 0x0 - IFA_ADDRESS = 0x1 - IFA_LOCAL = 0x2 - IFA_LABEL = 0x3 - IFA_BROADCAST = 0x4 - IFA_ANYCAST = 0x5 - IFA_CACHEINFO = 0x6 - IFA_MULTICAST = 0x7 - IFLA_UNSPEC = 0x0 - IFLA_ADDRESS = 0x1 - IFLA_BROADCAST = 0x2 - IFLA_IFNAME = 0x3 - IFLA_MTU = 0x4 - IFLA_LINK = 0x5 - IFLA_QDISC = 0x6 - IFLA_STATS = 0x7 - IFLA_COST = 0x8 - IFLA_PRIORITY = 0x9 - IFLA_MASTER = 0xa - IFLA_WIRELESS = 0xb - IFLA_PROTINFO = 0xc - IFLA_TXQLEN = 0xd - IFLA_MAP = 0xe - IFLA_WEIGHT = 0xf - IFLA_OPERSTATE = 0x10 - IFLA_LINKMODE = 0x11 - IFLA_LINKINFO = 0x12 - IFLA_NET_NS_PID = 0x13 - IFLA_IFALIAS = 0x14 - IFLA_MAX = 0x1c - RT_SCOPE_UNIVERSE = 0x0 - RT_SCOPE_SITE = 0xc8 - RT_SCOPE_LINK = 0xfd - RT_SCOPE_HOST = 0xfe - RT_SCOPE_NOWHERE = 0xff - RT_TABLE_UNSPEC = 0x0 - RT_TABLE_COMPAT = 0xfc - RT_TABLE_DEFAULT = 0xfd - RT_TABLE_MAIN = 0xfe - RT_TABLE_LOCAL = 0xff - RT_TABLE_MAX = 0xffffffff - RTA_UNSPEC = 0x0 - RTA_DST = 0x1 - RTA_SRC = 0x2 - RTA_IIF = 0x3 - RTA_OIF = 0x4 - RTA_GATEWAY = 0x5 - RTA_PRIORITY = 0x6 - RTA_PREFSRC = 0x7 - RTA_METRICS = 0x8 - RTA_MULTIPATH = 0x9 - RTA_FLOW = 0xb - RTA_CACHEINFO = 0xc - RTA_TABLE = 0xf - RTN_UNSPEC = 0x0 - RTN_UNICAST = 0x1 - RTN_LOCAL = 0x2 - RTN_BROADCAST = 0x3 - RTN_ANYCAST = 0x4 - RTN_MULTICAST = 0x5 - RTN_BLACKHOLE = 0x6 - RTN_UNREACHABLE = 0x7 - RTN_PROHIBIT = 0x8 - RTN_THROW = 0x9 - RTN_NAT = 0xa - RTN_XRESOLVE = 0xb - SizeofNlMsghdr = 0x10 - SizeofNlMsgerr = 0x14 - SizeofRtGenmsg = 0x1 - SizeofNlAttr = 0x4 - SizeofRtAttr = 0x4 - SizeofIfInfomsg = 0x10 - SizeofIfAddrmsg = 0x8 - SizeofRtMsg = 0xc - SizeofRtNexthop = 0x8 + IFA_UNSPEC = 0x0 + IFA_ADDRESS = 0x1 + IFA_LOCAL = 0x2 + IFA_LABEL = 0x3 + IFA_BROADCAST = 0x4 + IFA_ANYCAST = 0x5 + IFA_CACHEINFO = 0x6 + IFA_MULTICAST = 0x7 + IFLA_UNSPEC = 0x0 + IFLA_ADDRESS = 0x1 + IFLA_BROADCAST = 0x2 + IFLA_IFNAME = 0x3 + IFLA_MTU = 0x4 + IFLA_LINK = 0x5 + IFLA_QDISC = 0x6 + IFLA_STATS = 0x7 + IFLA_COST = 0x8 + IFLA_PRIORITY = 0x9 + IFLA_MASTER = 0xa + IFLA_WIRELESS = 0xb + IFLA_PROTINFO = 0xc + IFLA_TXQLEN = 0xd + IFLA_MAP = 0xe + IFLA_WEIGHT = 0xf + IFLA_OPERSTATE = 0x10 + IFLA_LINKMODE = 0x11 + IFLA_LINKINFO = 0x12 + IFLA_NET_NS_PID = 0x13 + IFLA_IFALIAS = 0x14 + IFLA_MAX = 0x1d + RT_SCOPE_UNIVERSE = 0x0 + RT_SCOPE_SITE = 0xc8 + RT_SCOPE_LINK = 0xfd + RT_SCOPE_HOST = 0xfe + RT_SCOPE_NOWHERE = 0xff + RT_TABLE_UNSPEC = 0x0 + RT_TABLE_COMPAT = 0xfc + RT_TABLE_DEFAULT = 0xfd + RT_TABLE_MAIN = 0xfe + RT_TABLE_LOCAL = 0xff + RT_TABLE_MAX = 0xffffffff + RTA_UNSPEC = 0x0 + RTA_DST = 0x1 + RTA_SRC = 0x2 + RTA_IIF = 0x3 + RTA_OIF = 0x4 + RTA_GATEWAY = 0x5 + RTA_PRIORITY = 0x6 + RTA_PREFSRC = 0x7 + RTA_METRICS = 0x8 + RTA_MULTIPATH = 0x9 + RTA_FLOW = 0xb + RTA_CACHEINFO = 0xc + RTA_TABLE = 0xf + RTN_UNSPEC = 0x0 + RTN_UNICAST = 0x1 + RTN_LOCAL = 0x2 + RTN_BROADCAST = 0x3 + RTN_ANYCAST = 0x4 + RTN_MULTICAST = 0x5 + RTN_BLACKHOLE = 0x6 + RTN_UNREACHABLE = 0x7 + RTN_PROHIBIT = 0x8 + RTN_THROW = 0x9 + RTN_NAT = 0xa + RTN_XRESOLVE = 0xb + RTNLGRP_NONE = 0x0 + RTNLGRP_LINK = 0x1 + RTNLGRP_NOTIFY = 0x2 + RTNLGRP_NEIGH = 0x3 + RTNLGRP_TC = 0x4 + RTNLGRP_IPV4_IFADDR = 0x5 + RTNLGRP_IPV4_MROUTE = 0x6 + RTNLGRP_IPV4_ROUTE = 0x7 + RTNLGRP_IPV4_RULE = 0x8 + RTNLGRP_IPV6_IFADDR = 0x9 + RTNLGRP_IPV6_MROUTE = 0xa + RTNLGRP_IPV6_ROUTE = 0xb + RTNLGRP_IPV6_IFINFO = 0xc + RTNLGRP_IPV6_PREFIX = 0x12 + RTNLGRP_IPV6_RULE = 0x13 + RTNLGRP_ND_USEROPT = 0x14 + SizeofNlMsghdr = 0x10 + SizeofNlMsgerr = 0x14 + SizeofRtGenmsg = 0x1 + SizeofNlAttr = 0x4 + SizeofRtAttr = 0x4 + SizeofIfInfomsg = 0x10 + SizeofIfAddrmsg = 0x8 + SizeofRtMsg = 0xc + SizeofRtNexthop = 0x8 ) type NlMsghdr struct { @@ -488,6 +539,10 @@ type EpollEvent struct { Pad int32 } +const ( + _AT_FDCWD = -0x64 +) + type Termios struct { Iflag uint32 Oflag uint32 diff --git a/src/pkg/syscall/ztypes_netbsd_386.go b/src/pkg/syscall/ztypes_netbsd_386.go index ef9f33aeb..dd9cf221a 100644 --- a/src/pkg/syscall/ztypes_netbsd_386.go +++ b/src/pkg/syscall/ztypes_netbsd_386.go @@ -19,12 +19,12 @@ type ( ) type Timespec struct { - Sec int32 + Sec int64 Nsec int32 } type Timeval struct { - Sec int32 + Sec int64 Usec int32 } @@ -54,69 +54,27 @@ type Rlimit struct { type _Gid_t uint32 -const ( - S_IFMT = 0xf000 - S_IFIFO = 0x1000 - S_IFCHR = 0x2000 - S_IFDIR = 0x4000 - S_IFBLK = 0x6000 - S_IFREG = 0x8000 - S_IFLNK = 0xa000 - S_IFSOCK = 0xc000 - S_ISUID = 0x800 - S_ISGID = 0x400 - S_ISVTX = 0x200 - S_IRUSR = 0x100 - S_IWUSR = 0x80 - S_IXUSR = 0x40 -) - type Stat_t struct { - Dev int32 - Ino uint32 - Mode uint32 - Nlink uint32 - Uid uint32 - Gid uint32 - Rdev int32 - Lspare0 int32 - Atim Timespec - Mtim Timespec - Ctim Timespec - Size int64 - Blocks int64 - Blksize uint32 - Flags uint32 - Gen uint32 - Lspare1 int32 - X__st_birthtim Timespec - Qspare [2]int64 -} - -type Statfs_t struct { - F_flags uint32 - F_bsize uint32 - F_iosize uint32 - F_blocks uint64 - F_bfree uint64 - F_bavail int64 - F_files uint64 - F_ffree uint64 - F_favail int64 - F_syncwrites uint64 - F_syncreads uint64 - F_asyncwrites uint64 - F_asyncreads uint64 - F_fsid Fsid - F_namemax uint32 - F_owner uint32 - F_ctime uint32 - F_spare [3]uint32 - F_fstypename [16]int8 - F_mntonname [90]int8 - F_mntfromname [90]int8 - Mount_info [160]byte -} + Dev uint64 + Mode uint32 + Ino uint64 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint64 + Atimespec Timespec + Mtimespec Timespec + Ctimespec Timespec + Birthtimespec Timespec + Size int64 + Blocks int64 + Blksize uint32 + Flags uint32 + Gen uint32 + Spare [2]uint32 +} + +type Statfs_t [0]byte type Flock_t struct { Start int64 @@ -127,15 +85,16 @@ type Flock_t struct { } type Dirent struct { - Fileno uint64 - Reclen uint16 - Namlen uint16 - Type uint8 - Name [512]int8 + Fileno uint64 + Reclen uint16 + Namlen uint16 + Type uint8 + Name [512]int8 + Pad_cgo_0 [3]byte } type Fsid struct { - Val [2]int32 + X__fsid_val [2]int32 } type RawSockaddrInet4 struct { @@ -169,7 +128,7 @@ type RawSockaddrDatalink struct { Nlen uint8 Alen uint8 Slen uint8 - Data [24]int8 + Data [12]int8 } type RawSockaddr struct { @@ -209,7 +168,7 @@ type Msghdr struct { Name *byte Namelen uint32 Iov *Iovec - Iovlen uint32 + Iovlen int32 Control *byte Controllen uint32 Flags int32 @@ -231,7 +190,7 @@ const ( SizeofSockaddrInet6 = 0x1c SizeofSockaddrAny = 0x6c SizeofSockaddrUnix = 0x6a - SizeofSockaddrDatalink = 0x20 + SizeofSockaddrDatalink = 0x14 SizeofLinger = 0x8 SizeofIPMreq = 0x8 SizeofIPv6Mreq = 0x14 @@ -248,123 +207,116 @@ const ( type Kevent_t struct { Ident uint32 - Filter int16 - Flags uint16 + Filter uint32 + Flags uint32 Fflags uint32 - Data int32 - Udata *byte + Data int64 + Udata int32 } type FdSet struct { - Bits [32]int32 + Bits [8]uint32 } const ( - SizeofIfMsghdr = 0xe8 - SizeofIfData = 0xd0 - SizeofIfaMsghdr = 0x18 - SizeofRtMsghdr = 0x58 - SizeofRtMetrics = 0x30 + SizeofIfMsghdr = 0x98 + SizeofIfData = 0x84 + SizeofIfaMsghdr = 0x18 + SizeofIfAnnounceMsghdr = 0x18 + SizeofRtMsghdr = 0x78 + SizeofRtMetrics = 0x50 ) type IfMsghdr struct { - Msglen uint16 - Version uint8 - Type uint8 - Hdrlen uint16 - Index uint16 - Tableid uint16 - Pad1 uint8 - Pad2 uint8 - Addrs int32 - Flags int32 - Xflags int32 - Data IfData + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte + Data IfData + Pad_cgo_1 [4]byte } type IfData struct { - Type uint8 - Addrlen uint8 - Hdrlen uint8 - Link_state uint8 - Mtu uint32 - Metric uint32 - Pad uint32 - Baudrate uint64 - Ipackets uint64 - Ierrors uint64 - Opackets uint64 - Oerrors uint64 - Collisions uint64 - Ibytes uint64 - Obytes uint64 - Imcasts uint64 - Omcasts uint64 - Iqdrops uint64 - Noproto uint64 - Capabilities uint32 - Lastchange Timeval - Mclpool [7]Mclpool + Type uint8 + Addrlen uint8 + Hdrlen uint8 + Pad_cgo_0 [1]byte + Link_state int32 + Mtu uint64 + Metric uint64 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Noproto uint64 + Lastchange Timespec } type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Metric int32 + Index uint16 + Pad_cgo_0 [6]byte +} + +type IfAnnounceMsghdr struct { Msglen uint16 Version uint8 Type uint8 - Hdrlen uint16 Index uint16 - Tableid uint16 - Pad1 uint8 - Pad2 uint8 - Addrs int32 - Flags int32 - Metric int32 + Name [16]int8 + What uint16 } type RtMsghdr struct { - Msglen uint16 - Version uint8 - Type uint8 - Hdrlen uint16 - Index uint16 - Tableid uint16 - Priority uint8 - Mpls uint8 - Addrs int32 - Flags int32 - Fmask int32 - Pid int32 - Seq int32 - Errno int32 - Inits uint32 - Rmx RtMetrics + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_cgo_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Use int32 + Inits int32 + Pad_cgo_1 [4]byte + Rmx RtMetrics } type RtMetrics struct { - Pksent uint64 - Locks uint32 - Mtu uint32 - Expire uint32 - Refcnt uint32 - Hopcount uint32 - Recvpipe uint32 - Sendpipe uint32 - Ssthresh uint32 - Rtt uint32 - Rttvar uint32 -} - -type Mclpool struct { - Grown uint32 - Alive uint16 - Hwm uint16 - Cwm uint16 - Lwm uint16 + Locks uint64 + Mtu uint64 + Hopcount uint64 + Recvpipe uint64 + Sendpipe uint64 + Ssthresh uint64 + Rtt uint64 + Rttvar uint64 + Expire int64 + Pksent int64 } +type Mclpool [0]byte + const ( SizeofBpfVersion = 0x4 - SizeofBpfStat = 0x8 + SizeofBpfStat = 0x80 SizeofBpfProgram = 0x8 SizeofBpfInsn = 0x8 SizeofBpfHdr = 0x14 @@ -376,8 +328,10 @@ type BpfVersion struct { } type BpfStat struct { - Recv uint32 - Drop uint32 + Recv uint64 + Drop uint64 + Capt uint64 + Padding [13]uint64 } type BpfProgram struct { @@ -401,6 +355,19 @@ type BpfHdr struct { } type BpfTimeval struct { - Sec uint32 - Usec uint32 + Sec int32 + Usec int32 +} + +type Sysctlnode struct { + Flags uint32 + Num int32 + Name [32]int8 + Ver uint32 + X__rsvd uint32 + Un [16]byte + X_sysctl_size [8]byte + X_sysctl_func [8]byte + X_sysctl_parent [8]byte + X_sysctl_desc [8]byte } diff --git a/src/pkg/syscall/ztypes_netbsd_amd64.go b/src/pkg/syscall/ztypes_netbsd_amd64.go index 3979d54de..89d1ef816 100644 --- a/src/pkg/syscall/ztypes_netbsd_amd64.go +++ b/src/pkg/syscall/ztypes_netbsd_amd64.go @@ -19,14 +19,14 @@ type ( ) type Timespec struct { - Sec int32 - Pad_cgo_0 [4]byte - Nsec int64 + Sec int64 + Nsec int64 } type Timeval struct { - Sec int64 - Usec int64 + Sec int64 + Usec int32 + Pad_cgo_0 [4]byte } type Rusage struct { @@ -55,71 +55,30 @@ type Rlimit struct { type _Gid_t uint32 -const ( - S_IFMT = 0xf000 - S_IFIFO = 0x1000 - S_IFCHR = 0x2000 - S_IFDIR = 0x4000 - S_IFBLK = 0x6000 - S_IFREG = 0x8000 - S_IFLNK = 0xa000 - S_IFSOCK = 0xc000 - S_ISUID = 0x800 - S_ISGID = 0x400 - S_ISVTX = 0x200 - S_IRUSR = 0x100 - S_IWUSR = 0x80 - S_IXUSR = 0x40 -) - type Stat_t struct { - Dev int32 - Ino uint32 - Mode uint32 - Nlink uint32 - Uid uint32 - Gid uint32 - Rdev int32 - Lspare0 int32 - Atim Timespec - Mtim Timespec - Ctim Timespec - Size int64 - Blocks int64 - Blksize uint32 - Flags uint32 - Gen uint32 - Lspare1 int32 - X__st_birthtim Timespec - Qspare [2]int64 -} - -type Statfs_t struct { - F_flags uint32 - F_bsize uint32 - F_iosize uint32 + Dev uint64 + Mode uint32 Pad_cgo_0 [4]byte - F_blocks uint64 - F_bfree uint64 - F_bavail int64 - F_files uint64 - F_ffree uint64 - F_favail int64 - F_syncwrites uint64 - F_syncreads uint64 - F_asyncwrites uint64 - F_asyncreads uint64 - F_fsid Fsid - F_namemax uint32 - F_owner uint32 - F_ctime uint32 - F_spare [3]uint32 - F_fstypename [16]int8 - F_mntonname [90]int8 - F_mntfromname [90]int8 + Ino uint64 + Nlink uint32 + Uid uint32 + Gid uint32 Pad_cgo_1 [4]byte - Mount_info [160]byte -} + Rdev uint64 + Atimespec Timespec + Mtimespec Timespec + Ctimespec Timespec + Birthtimespec Timespec + Size int64 + Blocks int64 + Blksize uint32 + Flags uint32 + Gen uint32 + Spare [2]uint32 + Pad_cgo_2 [4]byte +} + +type Statfs_t [0]byte type Flock_t struct { Start int64 @@ -130,15 +89,16 @@ type Flock_t struct { } type Dirent struct { - Fileno uint64 - Reclen uint16 - Namlen uint16 - Type uint8 - Name [512]int8 + Fileno uint64 + Reclen uint16 + Namlen uint16 + Type uint8 + Name [512]int8 + Pad_cgo_0 [3]byte } type Fsid struct { - Val [2]int32 + X__fsid_val [2]int32 } type RawSockaddrInet4 struct { @@ -172,7 +132,7 @@ type RawSockaddrDatalink struct { Nlen uint8 Alen uint8 Slen uint8 - Data [24]int8 + Data [12]int8 } type RawSockaddr struct { @@ -213,7 +173,7 @@ type Msghdr struct { Namelen uint32 Pad_cgo_0 [4]byte Iov *Iovec - Iovlen uint32 + Iovlen int32 Pad_cgo_1 [4]byte Control *byte Controllen uint32 @@ -236,7 +196,7 @@ const ( SizeofSockaddrInet6 = 0x1c SizeofSockaddrAny = 0x6c SizeofSockaddrUnix = 0x6a - SizeofSockaddrDatalink = 0x20 + SizeofSockaddrDatalink = 0x14 SizeofLinger = 0x8 SizeofIPMreq = 0x8 SizeofIPv6Mreq = 0x14 @@ -252,49 +212,47 @@ const ( ) type Kevent_t struct { - Ident uint64 - Filter int16 - Flags uint16 - Fflags uint32 - Data int32 - Udata *byte + Ident uint64 + Filter uint32 + Flags uint32 + Fflags uint32 + Pad_cgo_0 [4]byte + Data int64 + Udata int64 } type FdSet struct { - Bits [32]int32 + Bits [8]uint32 } const ( - SizeofIfMsghdr = 0xf0 - SizeofIfData = 0xd8 - SizeofIfaMsghdr = 0x18 - SizeofRtMsghdr = 0x58 - SizeofRtMetrics = 0x30 + SizeofIfMsghdr = 0x98 + SizeofIfData = 0x88 + SizeofIfaMsghdr = 0x18 + SizeofIfAnnounceMsghdr = 0x18 + SizeofRtMsghdr = 0x78 + SizeofRtMetrics = 0x50 ) type IfMsghdr struct { - Msglen uint16 - Version uint8 - Type uint8 - Hdrlen uint16 - Index uint16 - Tableid uint16 - Pad1 uint8 - Pad2 uint8 - Addrs int32 - Flags int32 - Xflags int32 - Data IfData + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte + Data IfData } type IfData struct { Type uint8 Addrlen uint8 Hdrlen uint8 - Link_state uint8 - Mtu uint32 - Metric uint32 - Pad uint32 + Pad_cgo_0 [1]byte + Link_state int32 + Mtu uint64 + Metric uint64 Baudrate uint64 Ipackets uint64 Ierrors uint64 @@ -307,72 +265,67 @@ type IfData struct { Omcasts uint64 Iqdrops uint64 Noproto uint64 - Lastchange Timeval - Mclpool [7]Mclpool - Pad_cgo_0 [4]byte + Lastchange Timespec } type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Metric int32 + Index uint16 + Pad_cgo_0 [6]byte +} + +type IfAnnounceMsghdr struct { Msglen uint16 Version uint8 Type uint8 - Hdrlen uint16 Index uint16 - Tableid uint16 - Pad1 uint8 - Pad2 uint8 - Addrs int32 - Flags int32 - Metric int32 + Name [16]int8 + What uint16 } type RtMsghdr struct { - Msglen uint16 - Version uint8 - Type uint8 - Hdrlen uint16 - Index uint16 - Tableid uint16 - Priority uint8 - Mpls uint8 - Addrs int32 - Flags int32 - Fmask int32 - Pid int32 - Seq int32 - Errno int32 - Inits uint32 - Rmx RtMetrics + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_cgo_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Use int32 + Inits int32 + Pad_cgo_1 [4]byte + Rmx RtMetrics } type RtMetrics struct { - Pksent uint64 - Locks uint32 - Mtu uint32 - Expire uint32 - Refcnt uint32 - Hopcount uint32 - Recvpipe uint32 - Sendpipe uint32 - Ssthresh uint32 - Rtt uint32 - Rttvar uint32 -} - -type Mclpool struct { - Grown uint32 - Alive uint16 - Hwm uint16 - Cwm uint16 - Lwm uint16 + Locks uint64 + Mtu uint64 + Hopcount uint64 + Recvpipe uint64 + Sendpipe uint64 + Ssthresh uint64 + Rtt uint64 + Rttvar uint64 + Expire int64 + Pksent int64 } +type Mclpool [0]byte + const ( SizeofBpfVersion = 0x4 - SizeofBpfStat = 0x8 + SizeofBpfStat = 0x80 SizeofBpfProgram = 0x10 SizeofBpfInsn = 0x8 - SizeofBpfHdr = 0x14 + SizeofBpfHdr = 0x20 ) type BpfVersion struct { @@ -381,8 +334,10 @@ type BpfVersion struct { } type BpfStat struct { - Recv uint32 - Drop uint32 + Recv uint64 + Drop uint64 + Capt uint64 + Padding [13]uint64 } type BpfProgram struct { @@ -403,10 +358,23 @@ type BpfHdr struct { Caplen uint32 Datalen uint32 Hdrlen uint16 - Pad_cgo_0 [2]byte + Pad_cgo_0 [6]byte } type BpfTimeval struct { - Sec uint32 - Usec uint32 + Sec int64 + Usec int64 +} + +type Sysctlnode struct { + Flags uint32 + Num int32 + Name [32]int8 + Ver uint32 + X__rsvd uint32 + Un [16]byte + X_sysctl_size [8]byte + X_sysctl_func [8]byte + X_sysctl_parent [8]byte + X_sysctl_desc [8]byte } diff --git a/src/pkg/syscall/ztypes_netbsd_arm.go b/src/pkg/syscall/ztypes_netbsd_arm.go new file mode 100644 index 000000000..dd9cf221a --- /dev/null +++ b/src/pkg/syscall/ztypes_netbsd_arm.go @@ -0,0 +1,373 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_netbsd.go + +package syscall + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int32 +} + +type Timeval struct { + Sec int64 + Usec int32 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type _Gid_t uint32 + +type Stat_t struct { + Dev uint64 + Mode uint32 + Ino uint64 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint64 + Atimespec Timespec + Mtimespec Timespec + Ctimespec Timespec + Birthtimespec Timespec + Size int64 + Blocks int64 + Blksize uint32 + Flags uint32 + Gen uint32 + Spare [2]uint32 +} + +type Statfs_t [0]byte + +type Flock_t struct { + Start int64 + Len int64 + Pid int32 + Type int16 + Whence int16 +} + +type Dirent struct { + Fileno uint64 + Reclen uint16 + Namlen uint16 + Type uint8 + Name [512]int8 + Pad_cgo_0 [3]byte +} + +type Fsid struct { + X__fsid_val [2]int32 +} + +type RawSockaddrInet4 struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type RawSockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type RawSockaddrUnix struct { + Len uint8 + Family uint8 + Path [104]int8 +} + +type RawSockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [12]int8 +} + +type RawSockaddr struct { + Len uint8 + Family uint8 + Data [14]int8 +} + +type RawSockaddrAny struct { + Addr RawSockaddr + Pad [92]int8 +} + +type _Socklen uint32 + +type Linger struct { + Onoff int32 + Linger int32 +} + +type Iovec struct { + Base *byte + Len uint32 +} + +type IPMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type IPv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type Msghdr struct { + Name *byte + Namelen uint32 + Iov *Iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type Cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type Inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +const ( + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x14 + SizeofLinger = 0x8 + SizeofIPMreq = 0x8 + SizeofIPv6Mreq = 0x14 + SizeofMsghdr = 0x1c + SizeofCmsghdr = 0xc + SizeofInet6Pktinfo = 0x14 +) + +const ( + PTRACE_TRACEME = 0x0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 +) + +type Kevent_t struct { + Ident uint32 + Filter uint32 + Flags uint32 + Fflags uint32 + Data int64 + Udata int32 +} + +type FdSet struct { + Bits [8]uint32 +} + +const ( + SizeofIfMsghdr = 0x98 + SizeofIfData = 0x84 + SizeofIfaMsghdr = 0x18 + SizeofIfAnnounceMsghdr = 0x18 + SizeofRtMsghdr = 0x78 + SizeofRtMetrics = 0x50 +) + +type IfMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte + Data IfData + Pad_cgo_1 [4]byte +} + +type IfData struct { + Type uint8 + Addrlen uint8 + Hdrlen uint8 + Pad_cgo_0 [1]byte + Link_state int32 + Mtu uint64 + Metric uint64 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Noproto uint64 + Lastchange Timespec +} + +type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Metric int32 + Index uint16 + Pad_cgo_0 [6]byte +} + +type IfAnnounceMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Name [16]int8 + What uint16 +} + +type RtMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_cgo_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Use int32 + Inits int32 + Pad_cgo_1 [4]byte + Rmx RtMetrics +} + +type RtMetrics struct { + Locks uint64 + Mtu uint64 + Hopcount uint64 + Recvpipe uint64 + Sendpipe uint64 + Ssthresh uint64 + Rtt uint64 + Rttvar uint64 + Expire int64 + Pksent int64 +} + +type Mclpool [0]byte + +const ( + SizeofBpfVersion = 0x4 + SizeofBpfStat = 0x80 + SizeofBpfProgram = 0x8 + SizeofBpfInsn = 0x8 + SizeofBpfHdr = 0x14 +) + +type BpfVersion struct { + Major uint16 + Minor uint16 +} + +type BpfStat struct { + Recv uint64 + Drop uint64 + Capt uint64 + Padding [13]uint64 +} + +type BpfProgram struct { + Len uint32 + Insns *BpfInsn +} + +type BpfInsn struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +type BpfHdr struct { + Tstamp BpfTimeval + Caplen uint32 + Datalen uint32 + Hdrlen uint16 + Pad_cgo_0 [2]byte +} + +type BpfTimeval struct { + Sec int32 + Usec int32 +} + +type Sysctlnode struct { + Flags uint32 + Num int32 + Name [32]int8 + Ver uint32 + X__rsvd uint32 + Un [16]byte + X_sysctl_size [8]byte + X_sysctl_func [8]byte + X_sysctl_parent [8]byte + X_sysctl_desc [8]byte +} diff --git a/src/pkg/syscall/ztypes_openbsd_386.go b/src/pkg/syscall/ztypes_openbsd_386.go index 6c21e39b0..528e92c85 100644 --- a/src/pkg/syscall/ztypes_openbsd_386.go +++ b/src/pkg/syscall/ztypes_openbsd_386.go @@ -260,11 +260,12 @@ type FdSet struct { } const ( - SizeofIfMsghdr = 0xe4 - SizeofIfData = 0xcc - SizeofIfaMsghdr = 0x18 - SizeofRtMsghdr = 0x58 - SizeofRtMetrics = 0x30 + SizeofIfMsghdr = 0xe8 + SizeofIfData = 0xd0 + SizeofIfaMsghdr = 0x18 + SizeofIfAnnounceMsghdr = 0x1a + SizeofRtMsghdr = 0x58 + SizeofRtMetrics = 0x30 ) type IfMsghdr struct { @@ -283,27 +284,28 @@ type IfMsghdr struct { } type IfData struct { - Type uint8 - Addrlen uint8 - Hdrlen uint8 - Link_state uint8 - Mtu uint32 - Metric uint32 - Pad uint32 - Baudrate uint64 - Ipackets uint64 - Ierrors uint64 - Opackets uint64 - Oerrors uint64 - Collisions uint64 - Ibytes uint64 - Obytes uint64 - Imcasts uint64 - Omcasts uint64 - Iqdrops uint64 - Noproto uint64 - Lastchange Timeval - Mclpool [7]Mclpool + Type uint8 + Addrlen uint8 + Hdrlen uint8 + Link_state uint8 + Mtu uint32 + Metric uint32 + Pad uint32 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Noproto uint64 + Capabilities uint32 + Lastchange Timeval + Mclpool [7]Mclpool } type IfaMsghdr struct { @@ -320,6 +322,16 @@ type IfaMsghdr struct { Metric int32 } +type IfAnnounceMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Hdrlen uint16 + Index uint16 + What uint16 + Name [16]int8 +} + type RtMsghdr struct { Msglen uint16 Version uint8 diff --git a/src/pkg/syscall/ztypes_openbsd_amd64.go b/src/pkg/syscall/ztypes_openbsd_amd64.go index 4d01578fc..4b9393054 100644 --- a/src/pkg/syscall/ztypes_openbsd_amd64.go +++ b/src/pkg/syscall/ztypes_openbsd_amd64.go @@ -265,11 +265,12 @@ type FdSet struct { } const ( - SizeofIfMsghdr = 0xf0 - SizeofIfData = 0xd8 - SizeofIfaMsghdr = 0x18 - SizeofRtMsghdr = 0x58 - SizeofRtMetrics = 0x30 + SizeofIfMsghdr = 0xf8 + SizeofIfData = 0xe0 + SizeofIfaMsghdr = 0x18 + SizeofIfAnnounceMsghdr = 0x1a + SizeofRtMsghdr = 0x58 + SizeofRtMetrics = 0x30 ) type IfMsghdr struct { @@ -288,28 +289,30 @@ type IfMsghdr struct { } type IfData struct { - Type uint8 - Addrlen uint8 - Hdrlen uint8 - Link_state uint8 - Mtu uint32 - Metric uint32 - Pad uint32 - Baudrate uint64 - Ipackets uint64 - Ierrors uint64 - Opackets uint64 - Oerrors uint64 - Collisions uint64 - Ibytes uint64 - Obytes uint64 - Imcasts uint64 - Omcasts uint64 - Iqdrops uint64 - Noproto uint64 - Lastchange Timeval - Mclpool [7]Mclpool - Pad_cgo_0 [4]byte + Type uint8 + Addrlen uint8 + Hdrlen uint8 + Link_state uint8 + Mtu uint32 + Metric uint32 + Pad uint32 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Noproto uint64 + Capabilities uint32 + Pad_cgo_0 [4]byte + Lastchange Timeval + Mclpool [7]Mclpool + Pad_cgo_1 [4]byte } type IfaMsghdr struct { @@ -326,6 +329,16 @@ type IfaMsghdr struct { Metric int32 } +type IfAnnounceMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Hdrlen uint16 + Index uint16 + What uint16 + Name [16]int8 +} + type RtMsghdr struct { Msglen uint16 Version uint8 diff --git a/src/pkg/syscall/ztypes_plan9_amd64.go b/src/pkg/syscall/ztypes_plan9_amd64.go new file mode 100644 index 000000000..3e3a8d1f3 --- /dev/null +++ b/src/pkg/syscall/ztypes_plan9_amd64.go @@ -0,0 +1,75 @@ +// godefs -gsyscall -f -m32 types_plan9.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + O_RDONLY = 0 + O_WRONLY = 0x1 + O_RDWR = 0x2 + O_TRUNC = 0x10 + O_CLOEXEC = 0x20 + O_EXCL = 0x1000 + STATMAX = 0xffff + ERRMAX = 0x80 + MORDER = 0x3 + MREPL = 0 + MBEFORE = 0x1 + MAFTER = 0x2 + MCREATE = 0x4 + MCACHE = 0x10 + MMASK = 0x17 + RFNAMEG = 0x1 + RFENVG = 0x2 + RFFDG = 0x4 + RFNOTEG = 0x8 + RFPROC = 0x10 + RFMEM = 0x20 + RFNOWAIT = 0x40 + RFCNAMEG = 0x400 + RFCENVG = 0x800 + RFCFDG = 0x1000 + RFREND = 0x2000 + RFNOMNT = 0x4000 + QTDIR = 0x80 + QTAPPEND = 0x40 + QTEXCL = 0x20 + QTMOUNT = 0x10 + QTAUTH = 0x8 + QTTMP = 0x4 + QTFILE = 0 + DMDIR = 0x80000000 + DMAPPEND = 0x40000000 + DMEXCL = 0x20000000 + DMMOUNT = 0x10000000 + DMAUTH = 0x8000000 + DMTMP = 0x4000000 + DMREAD = 0x4 + DMWRITE = 0x2 + DMEXEC = 0x1 + STATFIXLEN = 0x31 +) + +// Types + +type _C_int int32 + +type Prof struct { + Pp *[0]byte /* sPlink */ + Next *[0]byte /* sPlink */ + Last *[0]byte /* sPlink */ + First *[0]byte /* sPlink */ + Pid uint32 + What uint32 +} + +type Tos struct { + Prof Prof + Cyclefreq uint64 + Kcycles int64 + Pcycles int64 + Pid uint32 + Clock uint32 +} diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go index b2dc12e1a..a2006f803 100644 --- a/src/pkg/syscall/ztypes_windows.go +++ b/src/pkg/syscall/ztypes_windows.go @@ -20,6 +20,7 @@ const ( ERROR_ENVVAR_NOT_FOUND Errno = 203 ERROR_OPERATION_ABORTED Errno = 995 ERROR_IO_PENDING Errno = 997 + ERROR_NOT_FOUND Errno = 1168 ) const ( @@ -146,6 +147,7 @@ const ( WAIT_OBJECT_0 = 0x00000000 WAIT_FAILED = 0xFFFFFFFF + CREATE_NEW_PROCESS_GROUP = 0x00000200 CREATE_UNICODE_ENVIRONMENT = 0x00000400 PROCESS_QUERY_INFORMATION = 0x00000400 @@ -162,6 +164,9 @@ const ( FILE_MAP_WRITE = 0x02 FILE_MAP_READ = 0x04 FILE_MAP_EXECUTE = 0x20 + + CTRL_C_EVENT = 0 + CTRL_BREAK_EVENT = 1 ) const ( @@ -372,11 +377,9 @@ func copyFindData(dst *Win32finddata, src *win32finddata1) { dst.Reserved0 = src.Reserved0 dst.Reserved1 = src.Reserved1 - // The src is 1 element shorter than dst. Zero that last one. + // The src is 1 element bigger than dst, but it must be NUL. copy(dst.FileName[:], src.FileName[:]) - dst.FileName[len(dst.FileName)-1] = 0 copy(dst.AlternateFileName[:], src.AlternateFileName[:]) - src.AlternateFileName[len(dst.AlternateFileName)-1] = 0 } type ByHandleFileInformation struct { @@ -493,15 +496,22 @@ const ( IPPROTO_TCP = 6 IPPROTO_UDP = 17 - SOL_SOCKET = 0xffff - SO_REUSEADDR = 4 - SO_KEEPALIVE = 8 - SO_DONTROUTE = 16 - SO_BROADCAST = 32 - SO_LINGER = 128 - SO_RCVBUF = 0x1002 - SO_SNDBUF = 0x1001 - SO_UPDATE_ACCEPT_CONTEXT = 0x700b + SOL_SOCKET = 0xffff + SO_REUSEADDR = 4 + SO_KEEPALIVE = 8 + SO_DONTROUTE = 16 + SO_BROADCAST = 32 + SO_LINGER = 128 + SO_RCVBUF = 0x1002 + SO_SNDBUF = 0x1001 + SO_UPDATE_ACCEPT_CONTEXT = 0x700b + SO_UPDATE_CONNECT_CONTEXT = 0x7010 + + IOC_OUT = 0x40000000 + IOC_IN = 0x80000000 + IOC_INOUT = IOC_IN | IOC_OUT + IOC_WS2 = 0x08000000 + SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_INOUT | IOC_WS2 | 6 // cf. http://support.microsoft.com/default.aspx?scid=kb;en-us;257460 @@ -921,3 +931,34 @@ const ( REG_DWORD = REG_DWORD_LITTLE_ENDIAN REG_QWORD = REG_QWORD_LITTLE_ENDIAN ) + +type AddrinfoW struct { + Flags int32 + Family int32 + Socktype int32 + Protocol int32 + Addrlen uintptr + Canonname *uint16 + Addr uintptr + Next *AddrinfoW +} + +const ( + AI_PASSIVE = 1 + AI_CANONNAME = 2 + AI_NUMERICHOST = 4 +) + +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +var WSAID_CONNECTEX = GUID{ + 0x25a207b9, + 0xddf3, + 0x4660, + [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}, +} diff --git a/src/pkg/testing/allocs.go b/src/pkg/testing/allocs.go new file mode 100644 index 000000000..d142a330b --- /dev/null +++ b/src/pkg/testing/allocs.go @@ -0,0 +1,41 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testing + +import ( + "runtime" +) + +// AllocsPerRun returns the average number of allocations during calls to f. +// +// To compute the number of allocations, the function will first be run once as +// a warm-up. The average number of allocations over the specified number of +// runs will then be measured and returned. +// +// AllocsPerRun sets GOMAXPROCS to 1 during its measurement and will restore +// it before returning. +func AllocsPerRun(runs int, f func()) (avg float64) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + + // Warm up the function + f() + + // Measure the starting statistics + var memstats runtime.MemStats + runtime.ReadMemStats(&memstats) + mallocs := 0 - memstats.Mallocs + + // Run the function the specified number of times + for i := 0; i < runs; i++ { + f() + } + + // Read the final statistics + runtime.ReadMemStats(&memstats) + mallocs += memstats.Mallocs + + // Average the mallocs over the runs (not counting the warm-up) + return float64(mallocs) / float64(runs) +} diff --git a/src/pkg/testing/benchmark.go b/src/pkg/testing/benchmark.go index 41290594e..25fb2d619 100644 --- a/src/pkg/testing/benchmark.go +++ b/src/pkg/testing/benchmark.go @@ -9,11 +9,19 @@ import ( "fmt" "os" "runtime" + "sync" "time" ) var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run") -var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds") +var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark") +var benchmarkMemory = flag.Bool("test.benchmem", false, "print memory allocations for benchmarks") + +// Global lock to ensure only one benchmark runs at a time. +var benchmarkLock sync.Mutex + +// Used for every benchmark for measuring memory. +var memStats runtime.MemStats // An internal type but exported because it is cross-package; part of the implementation // of the "go test" command. @@ -26,11 +34,18 @@ type InternalBenchmark struct { // timing and to specify the number of iterations to run. type B struct { common - N int - benchmark InternalBenchmark - bytes int64 - timerOn bool - result BenchmarkResult + N int + benchmark InternalBenchmark + bytes int64 + timerOn bool + showAllocResult bool + result BenchmarkResult + // The initial states of memStats.Mallocs and memStats.TotalAlloc. + startAllocs uint64 + startBytes uint64 + // The net total of this test after being run. + netAllocs uint64 + netBytes uint64 } // StartTimer starts timing a test. This function is called automatically @@ -38,6 +53,9 @@ type B struct { // a call to StopTimer. func (b *B) StartTimer() { if !b.timerOn { + runtime.ReadMemStats(&memStats) + b.startAllocs = memStats.Mallocs + b.startBytes = memStats.TotalAlloc b.start = time.Now() b.timerOn = true } @@ -49,6 +67,9 @@ func (b *B) StartTimer() { func (b *B) StopTimer() { if b.timerOn { b.duration += time.Now().Sub(b.start) + runtime.ReadMemStats(&memStats) + b.netAllocs += memStats.Mallocs - b.startAllocs + b.netBytes += memStats.TotalAlloc - b.startBytes b.timerOn = false } } @@ -57,15 +78,27 @@ func (b *B) StopTimer() { // It does not affect whether the timer is running. func (b *B) ResetTimer() { if b.timerOn { + runtime.ReadMemStats(&memStats) + b.startAllocs = memStats.Mallocs + b.startBytes = memStats.TotalAlloc b.start = time.Now() } b.duration = 0 + b.netAllocs = 0 + b.netBytes = 0 } // SetBytes records the number of bytes processed in a single operation. // If this is called, the benchmark will report ns/op and MB/s. func (b *B) SetBytes(n int64) { b.bytes = n } +// ReportAllocs enables malloc statistics for this benchmark. +// It is equivalent to setting -test.benchmem, but it only affects the +// benchmark function that calls ReportAllocs. +func (b *B) ReportAllocs() { + b.showAllocResult = true +} + func (b *B) nsPerOp() int64 { if b.N <= 0 { return 0 @@ -75,6 +108,8 @@ func (b *B) nsPerOp() int64 { // runN runs a single benchmark for the specified number of iterations. func (b *B) runN(n int) { + benchmarkLock.Lock() + defer benchmarkLock.Unlock() // Try to get a comparable environment for each run // by clearing garbage from previous runs. runtime.GC() @@ -151,7 +186,7 @@ func (b *B) launch() { b.runN(n) // Run the benchmark for at least the specified amount of time. - d := time.Duration(*benchTime * float64(time.Second)) + d := *benchTime for !b.failed && b.duration < d && n < 1e9 { last := n // Predict iterations/sec. @@ -168,14 +203,16 @@ func (b *B) launch() { n = roundUp(n) b.runN(n) } - b.result = BenchmarkResult{b.N, b.duration, b.bytes} + b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes} } // The results of a benchmark run. type BenchmarkResult struct { - N int // The number of iterations. - T time.Duration // The total time taken. - Bytes int64 // Bytes processed in one iteration. + N int // The number of iterations. + T time.Duration // The total time taken. + Bytes int64 // Bytes processed in one iteration. + MemAllocs uint64 // The total number of memory allocations. + MemBytes uint64 // The total number of bytes allocated. } func (r BenchmarkResult) NsPerOp() int64 { @@ -192,6 +229,20 @@ func (r BenchmarkResult) mbPerSec() float64 { return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds() } +func (r BenchmarkResult) AllocsPerOp() int64 { + if r.N <= 0 { + return 0 + } + return int64(r.MemAllocs) / int64(r.N) +} + +func (r BenchmarkResult) AllocedBytesPerOp() int64 { + if r.N <= 0 { + return 0 + } + return int64(r.MemBytes) / int64(r.N) +} + func (r BenchmarkResult) String() string { mbs := r.mbPerSec() mb := "" @@ -212,6 +263,11 @@ func (r BenchmarkResult) String() string { return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb) } +func (r BenchmarkResult) MemString() string { + return fmt.Sprintf("%8d B/op\t%8d allocs/op", + r.AllocedBytesPerOp(), r.AllocsPerOp()) +} + // An internal function but exported because it is cross-package; part of the implementation // of the "go test" command. func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) { @@ -249,7 +305,11 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ fmt.Printf("--- FAIL: %s\n%s", benchName, b.output) continue } - fmt.Printf("%v\n", r) + results := r.String() + if *benchmarkMemory || b.showAllocResult { + results += "\t" + r.MemString() + } + fmt.Println(results) // Unlike with tests, we ignore the -chatty flag and always print output for // benchmarks since the output generation time will skew the results. if len(b.output) > 0 { diff --git a/src/pkg/testing/example.go b/src/pkg/testing/example.go index 671c79876..828c2d3ed 100644 --- a/src/pkg/testing/example.go +++ b/src/pkg/testing/example.go @@ -24,8 +24,6 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int var eg InternalExample - stdout, stderr := os.Stdout, os.Stderr - for _, eg = range examples { matched, err := matchString(*match, eg.Name) if err != nil { @@ -35,48 +33,68 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int if !matched { continue } - if *chatty { - fmt.Printf("=== RUN: %s\n", eg.Name) + if !runExample(eg) { + ok = false } + } + + return +} + +func runExample(eg InternalExample) (ok bool) { + if *chatty { + fmt.Printf("=== RUN: %s\n", eg.Name) + } - // capture stdout and stderr - r, w, err := os.Pipe() + // Capture stdout. + stdout := os.Stdout + r, w, err := os.Pipe() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Stdout = w + outC := make(chan string) + go func() { + buf := new(bytes.Buffer) + _, err := io.Copy(buf, r) + r.Close() if err != nil { - fmt.Fprintln(os.Stderr, err) + fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err) os.Exit(1) } - os.Stdout, os.Stderr = w, w - outC := make(chan string) - go func() { - buf := new(bytes.Buffer) - _, err := io.Copy(buf, r) - if err != nil { - fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err) - os.Exit(1) - } - outC <- buf.String() - }() + outC <- buf.String() + }() + + start := time.Now() + ok = true - // run example - t0 := time.Now() - eg.F() - dt := time.Now().Sub(t0) + // Clean up in a deferred call so we can recover if the example panics. + defer func() { + d := time.Now().Sub(start) - // close pipe, restore stdout/stderr, get output + // Close pipe, restore stdout, get output. w.Close() - os.Stdout, os.Stderr = stdout, stderr + os.Stdout = stdout out := <-outC - // report any errors - tstr := fmt.Sprintf("(%.2f seconds)", dt.Seconds()) - if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e { - fmt.Printf("--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n", - eg.Name, tstr, g, e) + var fail string + err := recover() + if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e && err == nil { + fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", g, e) + } + if fail != "" || err != nil { + fmt.Printf("--- FAIL: %s (%v)\n%s", eg.Name, d, fail) ok = false } else if *chatty { - fmt.Printf("--- PASS: %s %s\n", eg.Name, tstr) + fmt.Printf("--- PASS: %s (%v)\n", eg.Name, d) } - } + if err != nil { + panic(err) + } + }() + // Run example. + eg.F() return } diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go index f59ce8ed6..86cd46c29 100644 --- a/src/pkg/testing/testing.go +++ b/src/pkg/testing/testing.go @@ -10,10 +10,21 @@ // [a-z]) and serves to identify the test routine. // These TestXxx routines should be declared within the package they are testing. // +// Tests and benchmarks may be skipped if not applicable like this: +// func TestTimeConsuming(t *testing.T) { +// if testing.Short() { +// t.Skip("skipping test in short mode.") +// } +// ... +// } +// // Functions of the form // func BenchmarkXxx(*testing.B) // are considered benchmarks, and are executed by the "go test" command when -// the -test.bench flag is provided. +// the -test.bench flag is provided. Benchmarks are run sequentially. +// +// For a description of the testing flags, see +// http://golang.org/cmd/go/#Description_of_testing_flags. // // A sample benchmark function looks like this: // func BenchmarkHello(b *testing.B) { @@ -22,26 +33,26 @@ // } // } // +// The benchmark function must run the target code b.N times. // The benchmark package will vary b.N until the benchmark function lasts // long enough to be timed reliably. The output -// testing.BenchmarkHello 10000000 282 ns/op +// BenchmarkHello 10000000 282 ns/op // means that the loop ran 10000000 times at a speed of 282 ns per loop. // // If a benchmark needs some expensive setup before running, the timer -// may be stopped: +// may be reset: // func BenchmarkBigLen(b *testing.B) { -// b.StopTimer() // big := NewBig() -// b.StartTimer() +// b.ResetTimer() // for i := 0; i < b.N; i++ { // big.Len() // } // } // // The package also runs and verifies example code. Example functions may -// include a concluding comment that begins with "Output:" and is compared with -// the standard output of the function when the tests are run, as in these -// examples of an example: +// include a concluding line comment that begins with "Output:" and is compared with +// the standard output of the function when the tests are run. (The comparison +// ignores leading and trailing space.) These are examples of an example: // // func ExampleHello() { // fmt.Println("hello") @@ -79,6 +90,7 @@ package testing import ( + "bytes" "flag" "fmt" "os" @@ -86,6 +98,7 @@ import ( "runtime/pprof" "strconv" "strings" + "sync" "time" ) @@ -98,14 +111,16 @@ var ( short = flag.Bool("test.short", false, "run smaller test suite to save time") // Report as tests are run; default is silent for success. - chatty = flag.Bool("test.v", false, "verbose: print additional output") - match = flag.String("test.run", "", "regular expression to select tests and examples to run") - memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution") - memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate") - cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution") - timeout = flag.Duration("test.timeout", 0, "if positive, sets an aggregate time limit for all tests") - cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test") - parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism") + chatty = flag.Bool("test.v", false, "verbose: print additional output") + match = flag.String("test.run", "", "regular expression to select tests and examples to run") + memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution") + memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate") + cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution") + blockProfile = flag.String("test.blockprofile", "", "write a goroutine blocking profile to the named file after execution") + blockProfileRate = flag.Int("test.blockprofilerate", 1, "if >= 0, calls runtime.SetBlockProfileRate()") + timeout = flag.Duration("test.timeout", 0, "if positive, sets an aggregate time limit for all tests") + cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test") + parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism") haveExamples bool // are there examples? @@ -115,8 +130,11 @@ var ( // common holds the elements common between T and B and // captures common methods such as Errorf. type common struct { - output []byte // Output generated by test or benchmark. - failed bool // Test or benchmark has failed. + mu sync.RWMutex // guards output and failed + output []byte // Output generated by test or benchmark. + failed bool // Test or benchmark has failed. + skipped bool // Test of benchmark has been skipped. + start time.Time // Time test or benchmark started duration time.Duration self interface{} // To be sent on signal channel when done. @@ -128,37 +146,43 @@ func Short() bool { return *short } -// decorate inserts the final newline if needed and indentation tabs for formatting. -// If addFileLine is true, it also prefixes the string with the file and line of the call site. -func decorate(s string, addFileLine bool) string { - if addFileLine { - _, file, line, ok := runtime.Caller(3) // decorate + log + public function. - if ok { - // Truncate file name at last file name separator. - if index := strings.LastIndex(file, "/"); index >= 0 { - file = file[index+1:] - } else if index = strings.LastIndex(file, "\\"); index >= 0 { - file = file[index+1:] - } - } else { - file = "???" - line = 1 +// Verbose reports whether the -test.v flag is set. +func Verbose() bool { + return *chatty +} + +// decorate prefixes the string with the file and line of the call site +// and inserts the final newline if needed and indentation tabs for formatting. +func decorate(s string) string { + _, file, line, ok := runtime.Caller(3) // decorate + log + public function. + if ok { + // Truncate file name at last file name separator. + if index := strings.LastIndex(file, "/"); index >= 0 { + file = file[index+1:] + } else if index = strings.LastIndex(file, "\\"); index >= 0 { + file = file[index+1:] } - s = fmt.Sprintf("%s:%d: %s", file, line, s) + } else { + file = "???" + line = 1 } - s = "\t" + s // Every line is indented at least one tab. - n := len(s) - if n > 0 && s[n-1] != '\n' { - s += "\n" - n++ + buf := new(bytes.Buffer) + // Every line is indented at least one tab. + buf.WriteByte('\t') + fmt.Fprintf(buf, "%s:%d: ", file, line) + lines := strings.Split(s, "\n") + if l := len(lines); l > 1 && lines[l-1] == "" { + lines = lines[:l-1] } - for i := 0; i < n-1; i++ { // -1 to avoid final newline - if s[i] == '\n' { + for i, line := range lines { + if i > 0 { // Second and subsequent lines are indented an extra tab. - return s[0:i+1] + "\t" + decorate(s[i+1:n], false) + buf.WriteString("\n\t\t") } + buf.WriteString(line) } - return s + buf.WriteByte('\n') + return buf.String() } // T is a type passed to Test functions to manage test state and support formatted test logs. @@ -170,13 +194,25 @@ type T struct { } // Fail marks the function as having failed but continues execution. -func (c *common) Fail() { c.failed = true } +func (c *common) Fail() { + c.mu.Lock() + defer c.mu.Unlock() + c.failed = true +} -// Failed returns whether the function has failed. -func (c *common) Failed() bool { return c.failed } +// Failed reports whether the function has failed. +func (c *common) Failed() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.failed +} // FailNow marks the function as having failed and stops its execution. // Execution will continue at the next test or benchmark. +// FailNow must be called from the goroutine running the +// test or benchmark function, not from other goroutines +// created during the test. Calling FailNow does not stop +// those other goroutines. func (c *common) FailNow() { c.Fail() @@ -204,43 +240,80 @@ func (c *common) FailNow() { // log generates the output. It's always at the same stack depth. func (c *common) log(s string) { - c.output = append(c.output, decorate(s, true)...) + c.mu.Lock() + defer c.mu.Unlock() + c.output = append(c.output, decorate(s)...) } -// Log formats its arguments using default formatting, analogous to Println(), +// Log formats its arguments using default formatting, analogous to Println, // and records the text in the error log. func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) } -// Logf formats its arguments according to the format, analogous to Printf(), +// Logf formats its arguments according to the format, analogous to Printf, // and records the text in the error log. func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) } -// Error is equivalent to Log() followed by Fail(). +// Error is equivalent to Log followed by Fail. func (c *common) Error(args ...interface{}) { c.log(fmt.Sprintln(args...)) c.Fail() } -// Errorf is equivalent to Logf() followed by Fail(). +// Errorf is equivalent to Logf followed by Fail. func (c *common) Errorf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) c.Fail() } -// Fatal is equivalent to Log() followed by FailNow(). +// Fatal is equivalent to Log followed by FailNow. func (c *common) Fatal(args ...interface{}) { c.log(fmt.Sprintln(args...)) c.FailNow() } -// Fatalf is equivalent to Logf() followed by FailNow(). +// Fatalf is equivalent to Logf followed by FailNow. func (c *common) Fatalf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) c.FailNow() } -// Parallel signals that this test is to be run in parallel with (and only with) -// other parallel tests in this CPU group. +// Skip is equivalent to Log followed by SkipNow. +func (c *common) Skip(args ...interface{}) { + c.log(fmt.Sprintln(args...)) + c.SkipNow() +} + +// Skipf is equivalent to Logf followed by SkipNow. +func (c *common) Skipf(format string, args ...interface{}) { + c.log(fmt.Sprintf(format, args...)) + c.SkipNow() +} + +// SkipNow marks the test as having been skipped and stops its execution. +// Execution will continue at the next test or benchmark. See also FailNow. +// SkipNow must be called from the goroutine running the test, not from +// other goroutines created during the test. Calling SkipNow does not stop +// those other goroutines. +func (c *common) SkipNow() { + c.skip() + runtime.Goexit() +} + +func (c *common) skip() { + c.mu.Lock() + defer c.mu.Unlock() + c.skipped = true +} + +// Skipped reports whether the test was skipped. +func (c *common) Skipped() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.skipped +} + +// Parallel signals that this test is to be run in parallel with (and only with) +// other parallel tests. func (t *T) Parallel() { t.signal <- (*T)(nil) // Release main testing loop <-t.startParallel // Wait for serial tests to finish @@ -257,7 +330,7 @@ func tRunner(t *T, test *InternalTest) { t.start = time.Now() // When this goroutine is done, either because test.F(t) - // returned normally or because a test failure triggered + // returned normally or because a test failure triggered // a call to runtime.Goexit, record the duration and send // a signal saying that the test is done. defer func() { @@ -297,10 +370,14 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, func (t *T) report() { tstr := fmt.Sprintf("(%.2f seconds)", t.duration.Seconds()) format := "--- %s: %s %s\n%s" - if t.failed { + if t.Failed() { fmt.Printf(format, "FAIL", t.name, tstr, t.output) } else if *chatty { - fmt.Printf(format, "PASS", t.name, tstr, t.output) + if t.Skipped() { + fmt.Printf(format, "SKIP", t.name, tstr, t.output) + } else { + fmt.Printf(format, "PASS", t.name, tstr, t.output) + } } } @@ -356,7 +433,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT continue } t.report() - ok = ok && !out.failed + ok = ok && !out.Failed() } running := 0 @@ -369,7 +446,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT } t := (<-collector).(*T) t.report() - ok = ok && !t.failed + ok = ok && !t.Failed() running-- } } @@ -394,7 +471,9 @@ func before() { } // Could save f so after can call f.Close; not worth the effort. } - + if *blockProfile != "" && *blockProfileRate >= 0 { + runtime.SetBlockProfileRate(*blockProfileRate) + } } // after runs after all testing. @@ -413,6 +492,17 @@ func after() { } f.Close() } + if *blockProfile != "" && *blockProfileRate >= 0 { + f, err := os.Create(*blockProfile) + if err != nil { + fmt.Fprintf(os.Stderr, "testing: %s", err) + return + } + if err = pprof.Lookup("block").WriteTo(f, 0); err != nil { + fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *blockProfile, err) + } + f.Close() + } } var timer *time.Timer diff --git a/src/pkg/text/scanner/scanner.go b/src/pkg/text/scanner/scanner.go index 565650edf..e0d86e343 100644 --- a/src/pkg/text/scanner/scanner.go +++ b/src/pkg/text/scanner/scanner.go @@ -5,7 +5,8 @@ // Package scanner provides a scanner and tokenizer for UTF-8-encoded text. // It takes an io.Reader providing the source, which then can be tokenized // through repeated calls to the Scan function. For compatibility with -// existing tools, the NUL character is not allowed. +// existing tools, the NUL character is not allowed. If the first character +// in the source is a UTF-8 encoded byte order mark (BOM), it is discarded. // // By default, a Scanner skips white space and Go comments and recognizes all // literals as defined by the Go language specification. It may be @@ -208,11 +209,6 @@ func (s *Scanner) Init(src io.Reader) *Scanner { return s } -// TODO(gri): The code for next() and the internal scanner state could benefit -// from a rethink. While next() is optimized for the common ASCII -// case, the "corrections" needed for proper position tracking undo -// some of the attempts for fast-path optimization. - // next reads and returns the next Unicode character. It is designed such // that only a minimal amount of work needs to be done in the common ASCII // case (one test to check for both ASCII and end-of-buffer, and one test @@ -316,7 +312,11 @@ func (s *Scanner) Next() rune { // character of the source. func (s *Scanner) Peek() rune { if s.ch < 0 { + // this code is only run for the very first character s.ch = s.next() + if s.ch == '\uFEFF' { + s.ch = s.next() // ignore BOM + } } return s.ch } @@ -389,15 +389,20 @@ func (s *Scanner) scanNumber(ch rune) (rune, rune) { if ch == 'x' || ch == 'X' { // hexadecimal int ch = s.next() + hasMantissa := false for digitVal(ch) < 16 { ch = s.next() + hasMantissa = true + } + if !hasMantissa { + s.error("illegal hexadecimal number") } } else { // octal int or float - seenDecimalDigit := false + has8or9 := false for isDecimal(ch) { if ch > '7' { - seenDecimalDigit = true + has8or9 = true } ch = s.next() } @@ -408,7 +413,7 @@ func (s *Scanner) scanNumber(ch rune) (rune, rune) { return Float, ch } // octal int - if seenDecimalDigit { + if has8or9 { s.error("illegal octal number") } } diff --git a/src/pkg/text/scanner/scanner_test.go b/src/pkg/text/scanner/scanner_test.go index bb3adb55a..496eed4a3 100644 --- a/src/pkg/text/scanner/scanner_test.go +++ b/src/pkg/text/scanner/scanner_test.go @@ -358,8 +358,10 @@ func TestScanSelectedMask(t *testing.T) { } func TestScanNext(t *testing.T) { - s := new(Scanner).Init(bytes.NewBufferString("if a == bcd /* comment */ {\n\ta += c\n} // line comment ending in eof")) - checkTok(t, s, 1, s.Scan(), Ident, "if") + const BOM = '\uFEFF' + BOMs := string(BOM) + s := new(Scanner).Init(bytes.NewBufferString(BOMs + "if a == bcd /* com" + BOMs + "ment */ {\n\ta += c\n}" + BOMs + "// line comment ending in eof")) + checkTok(t, s, 1, s.Scan(), Ident, "if") // the first BOM is ignored checkTok(t, s, 1, s.Scan(), Ident, "a") checkTok(t, s, 1, s.Scan(), '=', "=") checkTok(t, s, 0, s.Next(), '=', "") @@ -372,6 +374,7 @@ func TestScanNext(t *testing.T) { checkTok(t, s, 0, s.Next(), '=', "") checkTok(t, s, 2, s.Scan(), Ident, "c") checkTok(t, s, 3, s.Scan(), '}', "}") + checkTok(t, s, 3, s.Scan(), BOM, BOMs) checkTok(t, s, 3, s.Scan(), -1, "") if s.ErrorCount != 0 { t.Errorf("%d errors", s.ErrorCount) @@ -446,6 +449,9 @@ func TestError(t *testing.T) { testError(t, `"\'"`, "1:3", "illegal char escape", String) testError(t, `01238`, "1:6", "illegal octal number", Int) + testError(t, `01238123`, "1:9", "illegal octal number", Int) + testError(t, `0x`, "1:3", "illegal hexadecimal number", Int) + testError(t, `0xg`, "1:3", "illegal hexadecimal number", Int) testError(t, `'aa'`, "1:4", "illegal char literal", Char) testError(t, `'`, "1:2", "literal not terminated", Char) diff --git a/src/pkg/text/tabwriter/tabwriter.go b/src/pkg/text/tabwriter/tabwriter.go index ce84600d6..722ac8d87 100644 --- a/src/pkg/text/tabwriter/tabwriter.go +++ b/src/pkg/text/tabwriter/tabwriter.go @@ -547,7 +547,7 @@ func (b *Writer) Write(buf []byte) (n int, err error) { } // NewWriter allocates and initializes a new tabwriter.Writer. -// The parameters are the same as for the the Init function. +// The parameters are the same as for the Init function. // func NewWriter(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer { return new(Writer).Init(output, minwidth, tabwidth, padding, padchar, flags) diff --git a/src/pkg/text/template/doc.go b/src/pkg/text/template/doc.go index aa50ab97f..2da339ce8 100644 --- a/src/pkg/text/template/doc.go +++ b/src/pkg/text/template/doc.go @@ -63,16 +63,16 @@ data, defined in detail below. otherwise, T1 is executed. Dot is unaffected. {{range pipeline}} T1 {{end}} - The value of the pipeline must be an array, slice, or map. If - the value of the pipeline has length zero, nothing is output; + The value of the pipeline must be an array, slice, map, or channel. + If the value of the pipeline has length zero, nothing is output; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed. If the value is a map and the keys are of basic type with a defined order ("comparable"), the elements will be visited in sorted key order. {{range pipeline}} T1 {{else}} T0 {{end}} - The value of the pipeline must be an array, slice, or map. If - the value of the pipeline has length zero, dot is unaffected and + The value of the pipeline must be an array, slice, map, or channel. + If the value of the pipeline has length zero, dot is unaffected and T0 is executed; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed. @@ -100,6 +100,7 @@ An argument is a simple value, denoted by one of the following. - A boolean, string, character, integer, floating-point, imaginary or complex constant in Go syntax. These behave like Go's untyped constants, although raw strings may not span newlines. + - The keyword nil, representing an untyped Go nil. - The character '.' (period): . The result is the value of dot. @@ -147,6 +148,10 @@ An argument is a simple value, denoted by one of the following. The result is the value of invoking the function, fun(). The return types and values behave as in methods. Functions and function names are described below. + - A parenthesized instance of one the above, for grouping. The result + may be accessed by a field or map key invocation. + print (.F1 arg1) (.F2 arg2) + (.StructValuedMethod "arg").Field Arguments may evaluate to any type; if they are pointers the implementation automatically indirects to the base type when required. @@ -198,7 +203,7 @@ If a "range" action initializes a variable, the variable is set to the successive elements of the iteration. Also, a "range" may declare two variables, separated by a comma: - $index, $element := pipeline + range $index, $element := pipeline in which case $index and $element are set to the successive values of the array/slice index or map key and element, respectively. Note that if there is @@ -227,6 +232,8 @@ All produce the quoted word "output": {{"output" | printf "%q"}} A function call whose final argument comes from the previous command. + {{printf "%q" (print "out" "put")}} + A parenthesized argument. {{"put" | printf "%s%s" "out" | printf "%q"}} A more elaborate call. {{"output" | printf "%s" | printf "%q"}} diff --git a/src/pkg/text/template/example_test.go b/src/pkg/text/template/example_test.go index ad49514a8..de1d51851 100644 --- a/src/pkg/text/template/example_test.go +++ b/src/pkg/text/template/example_test.go @@ -47,25 +47,25 @@ Josie // Output: // Dear Aunt Mildred, - // + // // It was a pleasure to see you at the wedding. // Thank you for the lovely bone china tea set. - // + // // Best wishes, // Josie - // + // // Dear Uncle John, - // + // // It is a shame you couldn't make it to the wedding. // Thank you for the lovely moleskin pants. - // + // // Best wishes, // Josie - // + // // Dear Cousin Rodney, - // + // // It is a shame you couldn't make it to the wedding. - // + // // Best wishes, // Josie } diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go index aba21ce28..b9c03d8f0 100644 --- a/src/pkg/text/template/exec.go +++ b/src/pkg/text/template/exec.go @@ -20,7 +20,7 @@ import ( type state struct { tmpl *Template wr io.Writer - line int // line number for errors + node parse.Node // current node, for errors vars []variable // push-down stack of variable values. } @@ -63,17 +63,32 @@ func (s *state) varValue(name string) reflect.Value { var zero reflect.Value +// at marks the state to be on node n, for error reporting. +func (s *state) at(node parse.Node) { + s.node = node +} + +// doublePercent returns the string with %'s replaced by %%, if necessary, +// so it can be used safely inside a Printf format string. +func doublePercent(str string) string { + if strings.Contains(str, "%") { + str = strings.Replace(str, "%", "%%", -1) + } + return str +} + // errorf formats the error and terminates processing. func (s *state) errorf(format string, args ...interface{}) { - format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.Name(), s.line, format) + name := doublePercent(s.tmpl.Name()) + if s.node == nil { + format = fmt.Sprintf("template: %s: %s", name, format) + } else { + location, context := s.tmpl.ErrorContext(s.node) + format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format) + } panic(fmt.Errorf(format, args...)) } -// error terminates processing. -func (s *state) error(err error) { - s.errorf("%s", err) -} - // errRecover is the handler that turns panics into returns from the top // level of Parse. func errRecover(errp *error) { @@ -108,7 +123,6 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { state := &state{ tmpl: t, wr: wr, - line: 1, vars: []variable{{"$", value}}, } if t.Tree == nil || t.Root == nil { @@ -120,38 +134,34 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { // Walk functions step through the major pieces of the template structure, // generating output as they go. -func (s *state) walk(dot reflect.Value, n parse.Node) { - switch n := n.(type) { +func (s *state) walk(dot reflect.Value, node parse.Node) { + s.at(node) + switch node := node.(type) { case *parse.ActionNode: - s.line = n.Line // Do not pop variables so they persist until next end. // Also, if the action declares variables, don't print the result. - val := s.evalPipeline(dot, n.Pipe) - if len(n.Pipe.Decl) == 0 { - s.printValue(n, val) + val := s.evalPipeline(dot, node.Pipe) + if len(node.Pipe.Decl) == 0 { + s.printValue(node, val) } case *parse.IfNode: - s.line = n.Line - s.walkIfOrWith(parse.NodeIf, dot, n.Pipe, n.List, n.ElseList) + s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList) case *parse.ListNode: - for _, node := range n.Nodes { + for _, node := range node.Nodes { s.walk(dot, node) } case *parse.RangeNode: - s.line = n.Line - s.walkRange(dot, n) + s.walkRange(dot, node) case *parse.TemplateNode: - s.line = n.Line - s.walkTemplate(dot, n) + s.walkTemplate(dot, node) case *parse.TextNode: - if _, err := s.wr.Write(n.Text); err != nil { - s.error(err) + if _, err := s.wr.Write(node.Text); err != nil { + s.errorf("%s", err) } case *parse.WithNode: - s.line = n.Line - s.walkIfOrWith(parse.NodeWith, dot, n.Pipe, n.List, n.ElseList) + s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList) default: - s.errorf("unknown node: %s", n) + s.errorf("unknown node: %s", node) } } @@ -206,6 +216,7 @@ func isTrue(val reflect.Value) (truth, ok bool) { } func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { + s.at(r) defer s.pop(s.mark()) val, _ := indirect(s.evalPipeline(dot, r.Pipe)) // mark top of stack before any variables in the body are pushed. @@ -266,6 +277,7 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { } func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) { + s.at(t) tmpl := s.tmpl.tmpl[t.Name] if tmpl == nil { s.errorf("template %q not defined", t.Name) @@ -291,6 +303,7 @@ func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value ref if pipe == nil { return } + s.at(pipe) for _, cmd := range pipe.Cmds { value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg. // If the object has type interface{}, dig down one level to the thing inside. @@ -315,18 +328,26 @@ func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final ref switch n := firstWord.(type) { case *parse.FieldNode: return s.evalFieldNode(dot, n, cmd.Args, final) + case *parse.ChainNode: + return s.evalChainNode(dot, n, cmd.Args, final) case *parse.IdentifierNode: // Must be a function. - return s.evalFunction(dot, n.Ident, cmd.Args, final) + return s.evalFunction(dot, n, cmd, cmd.Args, final) + case *parse.PipeNode: + // Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored. + return s.evalPipeline(dot, n) case *parse.VariableNode: return s.evalVariableNode(dot, n, cmd.Args, final) } + s.at(firstWord) s.notAFunction(cmd.Args, final) switch word := firstWord.(type) { case *parse.BoolNode: return reflect.ValueOf(word.True) case *parse.DotNode: return dot + case *parse.NilNode: + s.errorf("nil is not a command") case *parse.NumberNode: return s.idealConstant(word) case *parse.StringNode: @@ -344,6 +365,7 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { // These are ideal constants but we don't know the type // and we have no context. (If it was a method argument, // we'd know what we need.) The syntax guides us to some extent. + s.at(constant) switch { case constant.IsComplex: return reflect.ValueOf(constant.Complex128) // incontrovertible. @@ -362,43 +384,57 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { } func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value { - return s.evalFieldChain(dot, dot, field.Ident, args, final) + s.at(field) + return s.evalFieldChain(dot, dot, field, field.Ident, args, final) +} + +func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value { + s.at(chain) + // (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields. + pipe := s.evalArg(dot, nil, chain.Node) + if len(chain.Field) == 0 { + s.errorf("internal error: no fields in evalChainNode") + } + return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final) } -func (s *state) evalVariableNode(dot reflect.Value, v *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value { +func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value { // $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields. - value := s.varValue(v.Ident[0]) - if len(v.Ident) == 1 { + s.at(variable) + value := s.varValue(variable.Ident[0]) + if len(variable.Ident) == 1 { s.notAFunction(args, final) return value } - return s.evalFieldChain(dot, value, v.Ident[1:], args, final) + return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final) } // evalFieldChain evaluates .X.Y.Z possibly followed by arguments. // dot is the environment in which to evaluate arguments, while // receiver is the value being walked along the chain. -func (s *state) evalFieldChain(dot, receiver reflect.Value, ident []string, args []parse.Node, final reflect.Value) reflect.Value { +func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value { n := len(ident) for i := 0; i < n-1; i++ { - receiver = s.evalField(dot, ident[i], nil, zero, receiver) + receiver = s.evalField(dot, ident[i], node, nil, zero, receiver) } // Now if it's a method, it gets the arguments. - return s.evalField(dot, ident[n-1], args, final, receiver) + return s.evalField(dot, ident[n-1], node, args, final, receiver) } -func (s *state) evalFunction(dot reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value { +func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value { + s.at(node) + name := node.Ident function, ok := findFunction(name, s.tmpl) if !ok { s.errorf("%q is not a defined function", name) } - return s.evalCall(dot, function, name, args, final) + return s.evalCall(dot, function, cmd, name, args, final) } // evalField evaluates an expression like (.Field) or (.Field arg1 arg2). // The 'final' argument represents the return value from the preceding // value of the pipeline, if any. -func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node, final, receiver reflect.Value) reflect.Value { +func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value { if !receiver.IsValid() { return zero } @@ -411,26 +447,31 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node ptr = ptr.Addr() } if method := ptr.MethodByName(fieldName); method.IsValid() { - return s.evalCall(dot, method, fieldName, args, final) + return s.evalCall(dot, method, node, fieldName, args, final) } hasArgs := len(args) > 1 || final.IsValid() - // It's not a method; is it a field of a struct? + // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil. receiver, isNil := indirect(receiver) - if receiver.Kind() == reflect.Struct { + if isNil { + s.errorf("nil pointer evaluating %s.%s", typ, fieldName) + } + switch receiver.Kind() { + case reflect.Struct: tField, ok := receiver.Type().FieldByName(fieldName) if ok { field := receiver.FieldByIndex(tField.Index) - if tField.PkgPath == "" { // field is exported - // If it's a function, we must call it. - if hasArgs { - s.errorf("%s has arguments but cannot be invoked as function", fieldName) - } - return field + if tField.PkgPath != "" { // field is unexported + s.errorf("%s is an unexported field of struct type %s", fieldName, typ) + } + // If it's a function, we must call it. + if hasArgs { + s.errorf("%s has arguments but cannot be invoked as function", fieldName) } + return field } - } - // If it's a map, attempt to use the field name as a key. - if receiver.Kind() == reflect.Map { + s.errorf("%s is not a field of struct type %s", fieldName, typ) + case reflect.Map: + // If it's a map, attempt to use the field name as a key. nameVal := reflect.ValueOf(fieldName) if nameVal.Type().AssignableTo(receiver.Type().Key()) { if hasArgs { @@ -439,9 +480,6 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node return receiver.MapIndex(nameVal) } } - if isNil { - s.errorf("nil pointer evaluating %s.%s", typ, fieldName) - } s.errorf("can't evaluate field %s in type %s", fieldName, typ) panic("not reached") } @@ -454,7 +492,7 @@ var ( // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] // as the function itself. -func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value { +func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value { if args != nil { args = args[1:] // Zeroth arg is function name/node; not passed to function. } @@ -473,7 +511,8 @@ func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node, s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args)) } if !goodFunc(typ) { - s.errorf("can't handle multiple results from method/function %q", name) + // TODO: This could still be a confusing error; maybe goodFunc should provide info. + s.errorf("can't call method/function %q with %d results", name, typ.NumOut()) } // Build the arg list. argv := make([]reflect.Value, numIn) @@ -500,24 +539,31 @@ func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node, result := fun.Call(argv) // If we have an error that is not nil, stop execution and return that error to the caller. if len(result) == 2 && !result[1].IsNil() { + s.at(node) s.errorf("error calling %s: %s", name, result[1].Interface().(error)) } return result[0] } +// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero. +func canBeNil(typ reflect.Type) bool { + switch typ.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return true + } + return false +} + // validateType guarantees that the value is valid and assignable to the type. func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value { if !value.IsValid() { - switch typ.Kind() { - case reflect.Interface, reflect.Ptr, reflect.Chan, reflect.Map, reflect.Slice, reflect.Func: + if typ == nil || canBeNil(typ) { // An untyped nil interface{}. Accept as a proper nil value. - // TODO: Can we delete the other types in this list? Should we? - value = reflect.Zero(typ) - default: - s.errorf("invalid value; expected %s", typ) + return reflect.Zero(typ) } + s.errorf("invalid value; expected %s", typ) } - if !value.Type().AssignableTo(typ) { + if typ != nil && !value.Type().AssignableTo(typ) { if value.Kind() == reflect.Interface && !value.IsNil() { value = value.Elem() if value.Type().AssignableTo(typ) { @@ -542,13 +588,21 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu } func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) switch arg := n.(type) { case *parse.DotNode: return s.validateType(dot, typ) + case *parse.NilNode: + if canBeNil(typ) { + return reflect.Zero(typ) + } + s.errorf("cannot assign nil to %s", typ) case *parse.FieldNode: return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ) case *parse.VariableNode: return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ) + case *parse.PipeNode: + return s.validateType(s.evalPipeline(dot, arg), typ) } switch typ.Kind() { case reflect.Bool: @@ -573,6 +627,7 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle } func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) if n, ok := n.(*parse.BoolNode); ok { value := reflect.New(typ).Elem() value.SetBool(n.True) @@ -583,6 +638,7 @@ func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value { } func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) if n, ok := n.(*parse.StringNode); ok { value := reflect.New(typ).Elem() value.SetString(n.Text) @@ -593,6 +649,7 @@ func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value { } func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) if n, ok := n.(*parse.NumberNode); ok && n.IsInt { value := reflect.New(typ).Elem() value.SetInt(n.Int64) @@ -603,6 +660,7 @@ func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value { } func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) if n, ok := n.(*parse.NumberNode); ok && n.IsUint { value := reflect.New(typ).Elem() value.SetUint(n.Uint64) @@ -613,6 +671,7 @@ func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Valu } func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) if n, ok := n.(*parse.NumberNode); ok && n.IsFloat { value := reflect.New(typ).Elem() value.SetFloat(n.Float64) @@ -633,6 +692,7 @@ func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value { } func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value { + s.at(n) switch n := n.(type) { case *parse.BoolNode: return reflect.ValueOf(n.True) @@ -641,13 +701,18 @@ func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Valu case *parse.FieldNode: return s.evalFieldNode(dot, n, nil, zero) case *parse.IdentifierNode: - return s.evalFunction(dot, n.Ident, nil, zero) + return s.evalFunction(dot, n, n, nil, zero) + case *parse.NilNode: + // NilNode is handled in evalArg, the only place that calls here. + s.errorf("evalEmptyInterface: nil (can't happen)") case *parse.NumberNode: return s.idealConstant(n) case *parse.StringNode: return reflect.ValueOf(n.Text) case *parse.VariableNode: return s.evalVariableNode(dot, n, nil, zero) + case *parse.PipeNode: + return s.evalPipeline(dot, n) } s.errorf("can't handle assignment of %s to empty interface argument", n) panic("not reached") @@ -671,6 +736,7 @@ func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { // printValue writes the textual representation of the value to the output of // the template. func (s *state) printValue(n parse.Node, v reflect.Value) { + s.at(n) if v.Kind() == reflect.Ptr { v, _ = indirect(v) // fmt.Fprint handles nil. } diff --git a/src/pkg/text/template/exec_test.go b/src/pkg/text/template/exec_test.go index f4ae50f0e..683e9ac76 100644 --- a/src/pkg/text/template/exec_test.go +++ b/src/pkg/text/template/exec_test.go @@ -63,8 +63,11 @@ type T struct { BinaryFunc func(string, string) string VariadicFunc func(...string) string VariadicFuncInt func(int, ...string) string + NilOKFunc func(*int) bool // Template to test evaluation of templates. Tmpl *Template + // Unexported field; cannot be accessed by template. + unexported int } type U struct { @@ -125,6 +128,7 @@ var tVal = &T{ BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) }, VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") }, + NilOKFunc: func(s *int) bool { return s == nil }, Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X } @@ -220,6 +224,7 @@ var execTests = []execTest{ // Trivial cases. {"empty", "", "", nil, true}, {"text", "some text", "some text", nil, true}, + {"nil action", "{{nil}}", "", nil, false}, // Ideal constants. {"ideal int", "{{typeOf 3}}", "int", 0, true}, @@ -228,10 +233,12 @@ var execTests = []execTest{ {"ideal complex", "{{typeOf 1i}}", "complex128", 0, true}, {"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true}, {"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false}, + {"ideal nil without type", "{{nil}}", "", 0, false}, // Fields of structs. {".X", "-{{.X}}-", "-x-", tVal, true}, {".U.V", "-{{.U.V}}-", "-v-", tVal, true}, + {".unexported", "{{.unexported}}", "", tVal, false}, // Fields on maps. {"map .one", "{{.MSI.one}}", "1", tVal, true}, @@ -292,7 +299,8 @@ var execTests = []execTest{ {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true}, {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true}, {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true}, - {".Method3(nil)", "-{{.Method3 .MXI.unset}}-", "-Method3: -", tVal, true}, + {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: -", tVal, true}, + {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: -", tVal, true}, {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true}, {"method on chained var", "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", @@ -303,6 +311,8 @@ var execTests = []execTest{ {"chained method on variable", "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", "true", tVal, true}, + {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true}, + {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true}, // Function call builtin. {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true}, @@ -321,14 +331,25 @@ var execTests = []execTest{ {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false}, {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false}, {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false}, + {".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false}, // Pipelines. {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "->-", tVal, true}, + // Parenthesized expressions + {"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true}, + + // Parenthesized expressions with field accesses + {"parens: $ in paren", "{{($).X}}", "x", tVal, true}, + {"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true}, + {"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true}, + {"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true}, + // If. {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true}, + {"if nil", "{{if nil}}TRUE{{end}}", "", tVal, false}, {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, @@ -348,7 +369,8 @@ var execTests = []execTest{ // Print etc. {"print", `{{print "hello, print"}}`, "hello, print", tVal, true}, - {"print", `{{print 1 2 3}}`, "1 2 3", tVal, true}, + {"print 123", `{{print 1 2 3}}`, "1 2 3", tVal, true}, + {"print nil", `{{print nil}}`, "", tVal, true}, {"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true}, {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true}, {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, @@ -387,7 +409,8 @@ var execTests = []execTest{ {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false}, {"map[one]", "{{index .MSI `one`}}", "1", tVal, true}, {"map[two]", "{{index .MSI `two`}}", "2", tVal, true}, - {"map[NO]", "{{index .MSI `XXX`}}", "", tVal, true}, + {"map[NO]", "{{index .MSI `XXX`}}", "0", tVal, true}, + {"map[nil]", "{{index .MSI nil}}", "0", tVal, true}, {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false}, {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true}, @@ -474,6 +497,8 @@ var execTests = []execTest{ // Pipelined arg was not being type-checked. {"bug8a", "{{3|oneArg}}", "", tVal, false}, {"bug8b", "{{4|dddArg 3}}", "", tVal, false}, + // A bug was introduced that broke map lookups for lower-case names. + {"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true}, } func zeroArgs() string { @@ -508,6 +533,29 @@ func vfunc(V, *V) string { return "vfunc" } +func add(args ...int) int { + sum := 0 + for _, x := range args { + sum += x + } + return sum +} + +func echo(arg interface{}) interface{} { + return arg +} + +func makemap(arg ...string) map[string]string { + if len(arg)%2 != 0 { + panic("bad makemap") + } + m := make(map[string]string) + for i := 0; i < len(arg); i += 2 { + m[arg[i]] = arg[i+1] + } + return m +} + func stringer(s fmt.Stringer) string { return s.String() } @@ -515,8 +563,11 @@ func stringer(s fmt.Stringer) string { func testExecute(execTests []execTest, template *Template, t *testing.T) { b := new(bytes.Buffer) funcs := FuncMap{ + "add": add, "count": count, "dddArg": dddArg, + "echo": echo, + "makemap": makemap, "oneArg": oneArg, "typeOf": typeOf, "vfunc": vfunc, @@ -624,6 +675,32 @@ func TestExecuteError(t *testing.T) { } } +const execErrorText = `line 1 +line 2 +line 3 +{{template "one" .}} +{{define "one"}}{{template "two" .}}{{end}} +{{define "two"}}{{template "three" .}}{{end}} +{{define "three"}}{{index "hi" $}}{{end}}` + +// Check that an error from a nested template contains all the relevant information. +func TestExecError(t *testing.T) { + tmpl, err := New("top").Parse(execErrorText) + if err != nil { + t.Fatal("parse error:", err) + } + var b bytes.Buffer + err = tmpl.Execute(&b, 5) // 5 is out of range indexing "hi" + if err == nil { + t.Fatal("expected error") + } + const want = `template: top:7:20: executing "three" at : error calling index: index out of range: 5` + got := err.Error() + if got != want { + t.Errorf("expected\n%q\ngot\n%q", want, got) + } +} + func TestJSEscaping(t *testing.T) { testCases := []struct { in, exp string @@ -734,3 +811,8 @@ func TestTree(t *testing.T) { t.Errorf("expected %q got %q", expect, result) } } + +func TestExecuteOnNewTemplate(t *testing.T) { + // This is issue 3872. + _ = New("Name").Templates() +} diff --git a/src/pkg/text/template/funcs.go b/src/pkg/text/template/funcs.go index 8fbf0ef50..31549dc45 100644 --- a/src/pkg/text/template/funcs.go +++ b/src/pkg/text/template/funcs.go @@ -54,7 +54,7 @@ func addValueFuncs(out map[string]reflect.Value, in FuncMap) { panic("value for " + name + " not a function") } if !goodFunc(v.Type()) { - panic(fmt.Errorf("can't handle multiple results from method/function %q", name)) + panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut())) } out[name] = v } @@ -107,7 +107,7 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) { return nil, fmt.Errorf("index of nil pointer") } switch v.Kind() { - case reflect.Array, reflect.Slice: + case reflect.Array, reflect.Slice, reflect.String: var x int64 switch index.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: @@ -122,16 +122,19 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) { } v = v.Index(int(x)) case reflect.Map: + if !index.IsValid() { + index = reflect.Zero(v.Type().Key()) + } if !index.Type().AssignableTo(v.Type().Key()) { return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) } if x := v.MapIndex(index); x.IsValid() { v = x } else { - v = reflect.Zero(v.Type().Key()) + v = reflect.Zero(v.Type().Elem()) } default: - return nil, fmt.Errorf("can't index item of type %s", index.Type()) + return nil, fmt.Errorf("can't index item of type %s", v.Type()) } } return v.Interface(), nil @@ -154,7 +157,7 @@ func length(item interface{}) (int, error) { // Function invocation -// call returns the result of evaluating the the first argument as a function. +// call returns the result of evaluating the first argument as a function. // The function must return 1 result, or 2 results, the second of which is an error. func call(fn interface{}, args ...interface{}) (interface{}, error) { v := reflect.ValueOf(fn) @@ -187,10 +190,13 @@ func call(fn interface{}, args ...interface{}) (interface{}, error) { } else { argType = dddType } + if !value.IsValid() && canBeNil(argType) { + value = reflect.Zero(argType) + } if !value.Type().AssignableTo(argType) { return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType) } - argv[i] = reflect.ValueOf(arg) + argv[i] = value } result := v.Call(argv) if len(result) == 2 { diff --git a/src/pkg/text/template/parse/lex.go b/src/pkg/text/template/parse/lex.go index 7705c0b88..23c0cf079 100644 --- a/src/pkg/text/template/parse/lex.go +++ b/src/pkg/text/template/parse/lex.go @@ -13,8 +13,9 @@ import ( // item represents a token or text string returned from the scanner. type item struct { - typ itemType - val string + typ itemType // The type of this item. + pos Pos // The starting position, in bytes, of this item in the input string. + val string // The value of this item. } func (i item) String() string { @@ -42,65 +43,32 @@ const ( itemComplex // complex constant (1+2i); imaginary is just a number itemColonEquals // colon-equals (':=') introducing a declaration itemEOF - itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y') - itemIdentifier // alphanumeric identifier + itemField // alphanumeric identifier starting with '.' + itemIdentifier // alphanumeric identifier not starting with '.' itemLeftDelim // left action delimiter + itemLeftParen // '(' inside action itemNumber // simple number, including imaginary itemPipe // pipe symbol itemRawString // raw quoted string (includes quotes) itemRightDelim // right action delimiter + itemRightParen // ')' inside action + itemSpace // run of spaces separating arguments itemString // quoted string (includes quotes) itemText // plain text - itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'. + itemVariable // variable starting with '$', such as '$' or '$1' or '$hello' // Keywords appear after all the rest. itemKeyword // used only to delimit the keywords - itemDot // the cursor, spelled '.'. + itemDot // the cursor, spelled '.' itemDefine // define keyword itemElse // else keyword itemEnd // end keyword itemIf // if keyword + itemNil // the untyped nil constant, easiest to treat as a keyword itemRange // range keyword itemTemplate // template keyword itemWith // with keyword ) -// Make the types prettyprint. -var itemName = map[itemType]string{ - itemError: "error", - itemBool: "bool", - itemChar: "char", - itemCharConstant: "charconst", - itemComplex: "complex", - itemColonEquals: ":=", - itemEOF: "EOF", - itemField: "field", - itemIdentifier: "identifier", - itemLeftDelim: "left delim", - itemNumber: "number", - itemPipe: "pipe", - itemRawString: "raw string", - itemRightDelim: "right delim", - itemString: "string", - itemVariable: "variable", - // keywords - itemDot: ".", - itemDefine: "define", - itemElse: "else", - itemIf: "if", - itemEnd: "end", - itemRange: "range", - itemTemplate: "template", - itemWith: "with", -} - -func (i itemType) String() string { - s := itemName[i] - if s == "" { - return fmt.Sprintf("item%d", int(i)) - } - return s -} - var key = map[string]itemType{ ".": itemDot, "define": itemDefine, @@ -108,6 +76,7 @@ var key = map[string]itemType{ "end": itemEnd, "if": itemIf, "range": itemRange, + "nil": itemNil, "template": itemTemplate, "with": itemWith, } @@ -119,24 +88,27 @@ type stateFn func(*lexer) stateFn // lexer holds the state of the scanner. type lexer struct { - name string // the name of the input; used only for error reports. - input string // the string being scanned. - leftDelim string // start of action. - rightDelim string // end of action. - state stateFn // the next lexing function to enter. - pos int // current position in the input. - start int // start position of this item. - width int // width of last rune read from input. - items chan item // channel of scanned items. + name string // the name of the input; used only for error reports + input string // the string being scanned + leftDelim string // start of action + rightDelim string // end of action + state stateFn // the next lexing function to enter + pos Pos // current position in the input + start Pos // start position of this item + width Pos // width of last rune read from input + lastPos Pos // position of most recent item returned by nextItem + items chan item // channel of scanned items + parenDepth int // nesting depth of ( ) exprs } // next returns the next rune in the input. -func (l *lexer) next() (r rune) { - if l.pos >= len(l.input) { +func (l *lexer) next() rune { + if int(l.pos) >= len(l.input) { l.width = 0 return eof } - r, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) + r, w := utf8.DecodeRuneInString(l.input[l.pos:]) + l.width = Pos(w) l.pos += l.width return r } @@ -155,7 +127,7 @@ func (l *lexer) backup() { // emit passes an item back to the client. func (l *lexer) emit(t itemType) { - l.items <- item{t, l.input[l.start:l.pos]} + l.items <- item{t, l.start, l.input[l.start:l.pos]} l.start = l.pos } @@ -180,30 +152,25 @@ func (l *lexer) acceptRun(valid string) { l.backup() } -// lineNumber reports which line we're on. Doing it this way +// lineNumber reports which line we're on, based on the position of +// the previous item returned by nextItem. Doing it this way // means we don't have to worry about peek double counting. func (l *lexer) lineNumber() int { - return 1 + strings.Count(l.input[:l.pos], "\n") + return 1 + strings.Count(l.input[:l.lastPos], "\n") } -// error returns an error token and terminates the scan by passing +// errorf returns an error token and terminates the scan by passing // back a nil pointer that will be the next state, terminating l.nextItem. func (l *lexer) errorf(format string, args ...interface{}) stateFn { - l.items <- item{itemError, fmt.Sprintf(format, args...)} + l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)} return nil } // nextItem returns the next item from the input. func (l *lexer) nextItem() item { - for { - select { - case item := <-l.items: - return item - default: - l.state = l.state(l) - } - } - panic("not reached") + item := <-l.items + l.lastPos = item.pos + return item } // lex creates a new scanner for the input string. @@ -219,12 +186,19 @@ func lex(name, input, left, right string) *lexer { input: input, leftDelim: left, rightDelim: right, - state: lexText, - items: make(chan item, 2), // Two items of buffering is sufficient for all state functions + items: make(chan item), } + go l.run() return l } +// run runs the state machine for the lexer. +func (l *lexer) run() { + for l.state = lexText; l.state != nil; { + l.state = l.state(l) + } +} + // state functions const ( @@ -257,28 +231,30 @@ func lexText(l *lexer) stateFn { // lexLeftDelim scans the left delimiter, which is known to be present. func lexLeftDelim(l *lexer) stateFn { - if strings.HasPrefix(l.input[l.pos:], l.leftDelim+leftComment) { + l.pos += Pos(len(l.leftDelim)) + if strings.HasPrefix(l.input[l.pos:], leftComment) { return lexComment } - l.pos += len(l.leftDelim) l.emit(itemLeftDelim) + l.parenDepth = 0 return lexInsideAction } // lexComment scans a comment. The left comment marker is known to be present. func lexComment(l *lexer) stateFn { + l.pos += Pos(len(leftComment)) i := strings.Index(l.input[l.pos:], rightComment+l.rightDelim) if i < 0 { return l.errorf("unclosed comment") } - l.pos += i + len(rightComment) + len(l.rightDelim) + l.pos += Pos(i + len(rightComment) + len(l.rightDelim)) l.ignore() return lexText } // lexRightDelim scans the right delimiter, which is known to be present. func lexRightDelim(l *lexer) stateFn { - l.pos += len(l.rightDelim) + l.pos += Pos(len(l.rightDelim)) l.emit(itemRightDelim) return lexText } @@ -286,16 +262,19 @@ func lexRightDelim(l *lexer) stateFn { // lexInsideAction scans the elements inside action delimiters. func lexInsideAction(l *lexer) stateFn { // Either number, quoted string, or identifier. - // Spaces separate and are ignored. + // Spaces separate arguments; runs of spaces turn into itemSpace. // Pipe symbols separate and are emitted. if strings.HasPrefix(l.input[l.pos:], l.rightDelim) { - return lexRightDelim + if l.parenDepth == 0 { + return lexRightDelim + } + return l.errorf("unclosed left paren") } switch r := l.next(); { - case r == eof || r == '\n': + case r == eof || isEndOfLine(r): return l.errorf("unclosed action") case isSpace(r): - l.ignore() + return lexSpace case r == ':': if l.next() != '=' { return l.errorf("expected :=") @@ -308,15 +287,15 @@ func lexInsideAction(l *lexer) stateFn { case r == '`': return lexRawQuote case r == '$': - return lexIdentifier + return lexVariable case r == '\'': return lexChar case r == '.': // special look-ahead for ".field" so we don't break l.backup(). - if l.pos < len(l.input) { + if l.pos < Pos(len(l.input)) { r := l.input[l.pos] if r < '0' || '9' < r { - return lexIdentifier // itemDot comes from the keyword table. + return lexField } } fallthrough // '.' can start a number. @@ -326,6 +305,17 @@ func lexInsideAction(l *lexer) stateFn { case isAlphaNumeric(r): l.backup() return lexIdentifier + case r == '(': + l.emit(itemLeftParen) + l.parenDepth++ + return lexInsideAction + case r == ')': + l.emit(itemRightParen) + l.parenDepth-- + if l.parenDepth < 0 { + return l.errorf("unexpected right paren %#U", r) + } + return lexInsideAction case r <= unicode.MaxASCII && unicode.IsPrint(r): l.emit(itemChar) return lexInsideAction @@ -335,28 +325,34 @@ func lexInsideAction(l *lexer) stateFn { return lexInsideAction } -// lexIdentifier scans an alphanumeric or field. +// lexSpace scans a run of space characters. +// One space has already been seen. +func lexSpace(l *lexer) stateFn { + for isSpace(l.peek()) { + l.next() + } + l.emit(itemSpace) + return lexInsideAction +} + +// lexIdentifier scans an alphanumeric. func lexIdentifier(l *lexer) stateFn { Loop: for { switch r := l.next(); { case isAlphaNumeric(r): // absorb. - case r == '.' && (l.input[l.start] == '.' || l.input[l.start] == '$'): - // field chaining; absorb into one token. default: l.backup() word := l.input[l.start:l.pos] if !l.atTerminator() { - return l.errorf("unexpected character %+U", r) + return l.errorf("bad character %#U", r) } switch { case key[word] > itemKeyword: l.emit(key[word]) case word[0] == '.': l.emit(itemField) - case word[0] == '$': - l.emit(itemVariable) case word == "true", word == "false": l.emit(itemBool) default: @@ -368,17 +364,59 @@ Loop: return lexInsideAction } +// lexField scans a field: .Alphanumeric. +// The . has been scanned. +func lexField(l *lexer) stateFn { + return lexFieldOrVariable(l, itemField) +} + +// lexVariable scans a Variable: $Alphanumeric. +// The $ has been scanned. +func lexVariable(l *lexer) stateFn { + if l.atTerminator() { // Nothing interesting follows -> "$". + l.emit(itemVariable) + return lexInsideAction + } + return lexFieldOrVariable(l, itemVariable) +} + +// lexVariable scans a field or variable: [.$]Alphanumeric. +// The . or $ has been scanned. +func lexFieldOrVariable(l *lexer, typ itemType) stateFn { + if l.atTerminator() { // Nothing interesting follows -> "." or "$". + if typ == itemVariable { + l.emit(itemVariable) + } else { + l.emit(itemDot) + } + return lexInsideAction + } + var r rune + for { + r = l.next() + if !isAlphaNumeric(r) { + l.backup() + break + } + } + if !l.atTerminator() { + return l.errorf("bad character %#U", r) + } + l.emit(typ) + return lexInsideAction +} + // atTerminator reports whether the input is at valid termination character to -// appear after an identifier. Mostly to catch cases like "$x+2" not being -// acceptable without a space, in case we decide one day to implement -// arithmetic. +// appear after an identifier. Breaks .X.Y into two pieces. Also catches cases +// like "$x+2" not being acceptable without a space, in case we decide one +// day to implement arithmetic. func (l *lexer) atTerminator() bool { r := l.peek() - if isSpace(r) { + if isSpace(r) || isEndOfLine(r) { return true } switch r { - case eof, ',', '|', ':': + case eof, '.', ',', '|', ':', ')', '(': return true } // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will @@ -391,7 +429,7 @@ func (l *lexer) atTerminator() bool { } // lexChar scans a character constant. The initial quote is already -// scanned. Syntax checking is done by the parse. +// scanned. Syntax checking is done by the parser. func lexChar(l *lexer) stateFn { Loop: for { @@ -411,7 +449,7 @@ Loop: return lexInsideAction } -// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This +// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This // isn't a perfect number scanner - for instance it accepts "." and "0x0.2" // and "089" - but when it's wrong the input is invalid and the parser (via // strconv) will notice. @@ -420,7 +458,7 @@ func lexNumber(l *lexer) stateFn { return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) } if sign := l.peek(); sign == '+' || sign == '-' { - // Complex: 1+2i. No spaces, must end in 'i'. + // Complex: 1+2i. No spaces, must end in 'i'. if !l.scanNumber() || l.input[l.pos-1] != 'i' { return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) } @@ -494,11 +532,12 @@ Loop: // isSpace reports whether r is a space character. func isSpace(r rune) bool { - switch r { - case ' ', '\t', '\n', '\r': - return true - } - return false + return r == ' ' || r == '\t' +} + +// isEndOfLine reports whether r is an end-of-line character. +func isEndOfLine(r rune) bool { + return r == '\r' || r == '\n' } // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. diff --git a/src/pkg/text/template/parse/lex_test.go b/src/pkg/text/template/parse/lex_test.go index 6ee1b4701..d2264c991 100644 --- a/src/pkg/text/template/parse/lex_test.go +++ b/src/pkg/text/template/parse/lex_test.go @@ -5,10 +5,52 @@ package parse import ( - "reflect" + "fmt" "testing" ) +// Make the types prettyprint. +var itemName = map[itemType]string{ + itemError: "error", + itemBool: "bool", + itemChar: "char", + itemCharConstant: "charconst", + itemComplex: "complex", + itemColonEquals: ":=", + itemEOF: "EOF", + itemField: "field", + itemIdentifier: "identifier", + itemLeftDelim: "left delim", + itemLeftParen: "(", + itemNumber: "number", + itemPipe: "pipe", + itemRawString: "raw string", + itemRightDelim: "right delim", + itemRightParen: ")", + itemSpace: "space", + itemString: "string", + itemVariable: "variable", + + // keywords + itemDot: ".", + itemDefine: "define", + itemElse: "else", + itemIf: "if", + itemEnd: "end", + itemNil: "nil", + itemRange: "range", + itemTemplate: "template", + itemWith: "with", +} + +func (i itemType) String() string { + s := itemName[i] + if s == "" { + return fmt.Sprintf("item%d", int(i)) + } + return s +} + type lexTest struct { name string input string @@ -16,173 +58,265 @@ type lexTest struct { } var ( - tEOF = item{itemEOF, ""} - tLeft = item{itemLeftDelim, "{{"} - tRight = item{itemRightDelim, "}}"} - tRange = item{itemRange, "range"} - tPipe = item{itemPipe, "|"} - tFor = item{itemIdentifier, "for"} - tQuote = item{itemString, `"abc \n\t\" "`} + tEOF = item{itemEOF, 0, ""} + tFor = item{itemIdentifier, 0, "for"} + tLeft = item{itemLeftDelim, 0, "{{"} + tLpar = item{itemLeftParen, 0, "("} + tPipe = item{itemPipe, 0, "|"} + tQuote = item{itemString, 0, `"abc \n\t\" "`} + tRange = item{itemRange, 0, "range"} + tRight = item{itemRightDelim, 0, "}}"} + tRpar = item{itemRightParen, 0, ")"} + tSpace = item{itemSpace, 0, " "} raw = "`" + `abc\n\t\" ` + "`" - tRawQuote = item{itemRawString, raw} + tRawQuote = item{itemRawString, 0, raw} ) var lexTests = []lexTest{ {"empty", "", []item{tEOF}}, - {"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}}, - {"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}}, + {"spaces", " \t\n", []item{{itemText, 0, " \t\n"}, tEOF}}, + {"text", `now is the time`, []item{{itemText, 0, "now is the time"}, tEOF}}, {"text with comment", "hello-{{/* this is a comment */}}-world", []item{ - {itemText, "hello-"}, - {itemText, "-world"}, + {itemText, 0, "hello-"}, + {itemText, 0, "-world"}, tEOF, }}, - {"punctuation", "{{,@%}}", []item{ + {"punctuation", "{{,@% }}", []item{ tLeft, - {itemChar, ","}, - {itemChar, "@"}, - {itemChar, "%"}, + {itemChar, 0, ","}, + {itemChar, 0, "@"}, + {itemChar, 0, "%"}, + tSpace, + tRight, + tEOF, + }}, + {"parens", "{{((3))}}", []item{ + tLeft, + tLpar, + tLpar, + {itemNumber, 0, "3"}, + tRpar, + tRpar, tRight, tEOF, }}, {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}}, - {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}}, + {"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}}, {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}}, {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}}, {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{ tLeft, - {itemNumber, "1"}, - {itemNumber, "02"}, - {itemNumber, "0x14"}, - {itemNumber, "-7.2i"}, - {itemNumber, "1e3"}, - {itemNumber, "+1.2e-4"}, - {itemNumber, "4.2i"}, - {itemComplex, "1+2i"}, + {itemNumber, 0, "1"}, + tSpace, + {itemNumber, 0, "02"}, + tSpace, + {itemNumber, 0, "0x14"}, + tSpace, + {itemNumber, 0, "-7.2i"}, + tSpace, + {itemNumber, 0, "1e3"}, + tSpace, + {itemNumber, 0, "+1.2e-4"}, + tSpace, + {itemNumber, 0, "4.2i"}, + tSpace, + {itemComplex, 0, "1+2i"}, tRight, tEOF, }}, {"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{ tLeft, - {itemCharConstant, `'a'`}, - {itemCharConstant, `'\n'`}, - {itemCharConstant, `'\''`}, - {itemCharConstant, `'\\'`}, - {itemCharConstant, `'\u00FF'`}, - {itemCharConstant, `'\xFF'`}, - {itemCharConstant, `'本'`}, + {itemCharConstant, 0, `'a'`}, + tSpace, + {itemCharConstant, 0, `'\n'`}, + tSpace, + {itemCharConstant, 0, `'\''`}, + tSpace, + {itemCharConstant, 0, `'\\'`}, + tSpace, + {itemCharConstant, 0, `'\u00FF'`}, + tSpace, + {itemCharConstant, 0, `'\xFF'`}, + tSpace, + {itemCharConstant, 0, `'本'`}, tRight, tEOF, }}, {"bools", "{{true false}}", []item{ tLeft, - {itemBool, "true"}, - {itemBool, "false"}, + {itemBool, 0, "true"}, + tSpace, + {itemBool, 0, "false"}, tRight, tEOF, }}, {"dot", "{{.}}", []item{ tLeft, - {itemDot, "."}, + {itemDot, 0, "."}, tRight, tEOF, }}, - {"dots", "{{.x . .2 .x.y}}", []item{ + {"nil", "{{nil}}", []item{ tLeft, - {itemField, ".x"}, - {itemDot, "."}, - {itemNumber, ".2"}, - {itemField, ".x.y"}, + {itemNil, 0, "nil"}, + tRight, + tEOF, + }}, + {"dots", "{{.x . .2 .x.y.z}}", []item{ + tLeft, + {itemField, 0, ".x"}, + tSpace, + {itemDot, 0, "."}, + tSpace, + {itemNumber, 0, ".2"}, + tSpace, + {itemField, 0, ".x"}, + {itemField, 0, ".y"}, + {itemField, 0, ".z"}, tRight, tEOF, }}, {"keywords", "{{range if else end with}}", []item{ tLeft, - {itemRange, "range"}, - {itemIf, "if"}, - {itemElse, "else"}, - {itemEnd, "end"}, - {itemWith, "with"}, + {itemRange, 0, "range"}, + tSpace, + {itemIf, 0, "if"}, + tSpace, + {itemElse, 0, "else"}, + tSpace, + {itemEnd, 0, "end"}, + tSpace, + {itemWith, 0, "with"}, tRight, tEOF, }}, {"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{ tLeft, - {itemVariable, "$c"}, - {itemColonEquals, ":="}, - {itemIdentifier, "printf"}, - {itemVariable, "$"}, - {itemVariable, "$hello"}, - {itemVariable, "$23"}, - {itemVariable, "$"}, - {itemVariable, "$var.Field"}, - {itemField, ".Method"}, + {itemVariable, 0, "$c"}, + tSpace, + {itemColonEquals, 0, ":="}, + tSpace, + {itemIdentifier, 0, "printf"}, + tSpace, + {itemVariable, 0, "$"}, + tSpace, + {itemVariable, 0, "$hello"}, + tSpace, + {itemVariable, 0, "$23"}, + tSpace, + {itemVariable, 0, "$"}, + tSpace, + {itemVariable, 0, "$var"}, + {itemField, 0, ".Field"}, + tSpace, + {itemField, 0, ".Method"}, + tRight, + tEOF, + }}, + {"variable invocation", "{{$x 23}}", []item{ + tLeft, + {itemVariable, 0, "$x"}, + tSpace, + {itemNumber, 0, "23"}, tRight, tEOF, }}, {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{ - {itemText, "intro "}, + {itemText, 0, "intro "}, tLeft, - {itemIdentifier, "echo"}, - {itemIdentifier, "hi"}, - {itemNumber, "1.2"}, + {itemIdentifier, 0, "echo"}, + tSpace, + {itemIdentifier, 0, "hi"}, + tSpace, + {itemNumber, 0, "1.2"}, + tSpace, tPipe, - {itemIdentifier, "noargs"}, + {itemIdentifier, 0, "noargs"}, tPipe, - {itemIdentifier, "args"}, - {itemNumber, "1"}, - {itemString, `"hi"`}, + {itemIdentifier, 0, "args"}, + tSpace, + {itemNumber, 0, "1"}, + tSpace, + {itemString, 0, `"hi"`}, tRight, - {itemText, " outro"}, + {itemText, 0, " outro"}, tEOF, }}, {"declaration", "{{$v := 3}}", []item{ tLeft, - {itemVariable, "$v"}, - {itemColonEquals, ":="}, - {itemNumber, "3"}, + {itemVariable, 0, "$v"}, + tSpace, + {itemColonEquals, 0, ":="}, + tSpace, + {itemNumber, 0, "3"}, tRight, tEOF, }}, {"2 declarations", "{{$v , $w := 3}}", []item{ tLeft, - {itemVariable, "$v"}, - {itemChar, ","}, - {itemVariable, "$w"}, - {itemColonEquals, ":="}, - {itemNumber, "3"}, + {itemVariable, 0, "$v"}, + tSpace, + {itemChar, 0, ","}, + tSpace, + {itemVariable, 0, "$w"}, + tSpace, + {itemColonEquals, 0, ":="}, + tSpace, + {itemNumber, 0, "3"}, + tRight, + tEOF, + }}, + {"field of parenthesized expression", "{{(.X).Y}}", []item{ + tLeft, + tLpar, + {itemField, 0, ".X"}, + tRpar, + {itemField, 0, ".Y"}, tRight, tEOF, }}, // errors {"badchar", "#{{\x01}}", []item{ - {itemText, "#"}, + {itemText, 0, "#"}, tLeft, - {itemError, "unrecognized character in action: U+0001"}, + {itemError, 0, "unrecognized character in action: U+0001"}, }}, {"unclosed action", "{{\n}}", []item{ tLeft, - {itemError, "unclosed action"}, + {itemError, 0, "unclosed action"}, }}, {"EOF in action", "{{range", []item{ tLeft, tRange, - {itemError, "unclosed action"}, + {itemError, 0, "unclosed action"}, }}, {"unclosed quote", "{{\"\n\"}}", []item{ tLeft, - {itemError, "unterminated quoted string"}, + {itemError, 0, "unterminated quoted string"}, }}, {"unclosed raw quote", "{{`xx\n`}}", []item{ tLeft, - {itemError, "unterminated raw quoted string"}, + {itemError, 0, "unterminated raw quoted string"}, }}, {"unclosed char constant", "{{'\n}}", []item{ tLeft, - {itemError, "unterminated character constant"}, + {itemError, 0, "unterminated character constant"}, }}, {"bad number", "{{3k}}", []item{ tLeft, - {itemError, `bad number syntax: "3k"`}, + {itemError, 0, `bad number syntax: "3k"`}, + }}, + {"unclosed paren", "{{(3}}", []item{ + tLeft, + tLpar, + {itemNumber, 0, "3"}, + {itemError, 0, `unclosed left paren`}, + }}, + {"extra right paren", "{{3)}}", []item{ + tLeft, + {itemNumber, 0, "3"}, + tRpar, + {itemError, 0, `unexpected right paren U+0029 ')'`}, }}, // Fixed bugs @@ -198,6 +332,10 @@ var lexTests = []lexTest{ tRight, tEOF, }}, + {"text with bad comment", "hello-{{/*/}}-world", []item{ + {itemText, 0, "hello-"}, + {itemError, 0, `unclosed comment`}, + }}, } // collect gathers the emitted items into a slice. @@ -213,11 +351,29 @@ func collect(t *lexTest, left, right string) (items []item) { return } +func equal(i1, i2 []item, checkPos bool) bool { + if len(i1) != len(i2) { + return false + } + for k := range i1 { + if i1[k].typ != i2[k].typ { + return false + } + if i1[k].val != i2[k].val { + return false + } + if checkPos && i1[k].pos != i2[k].pos { + return false + } + } + return true +} + func TestLex(t *testing.T) { for _, test := range lexTests { items := collect(&test, "", "") - if !reflect.DeepEqual(items, test.items) { - t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) + if !equal(items, test.items, false) { + t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, items, test.items) } } } @@ -226,32 +382,74 @@ func TestLex(t *testing.T) { var lexDelimTests = []lexTest{ {"punctuation", "$$,@%{{}}@@", []item{ tLeftDelim, - {itemChar, ","}, - {itemChar, "@"}, - {itemChar, "%"}, - {itemChar, "{"}, - {itemChar, "{"}, - {itemChar, "}"}, - {itemChar, "}"}, + {itemChar, 0, ","}, + {itemChar, 0, "@"}, + {itemChar, 0, "%"}, + {itemChar, 0, "{"}, + {itemChar, 0, "{"}, + {itemChar, 0, "}"}, + {itemChar, 0, "}"}, tRightDelim, tEOF, }}, {"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}}, - {"for", `$$for @@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}}, + {"for", `$$for@@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}}, {"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}}, {"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}}, } var ( - tLeftDelim = item{itemLeftDelim, "$$"} - tRightDelim = item{itemRightDelim, "@@"} + tLeftDelim = item{itemLeftDelim, 0, "$$"} + tRightDelim = item{itemRightDelim, 0, "@@"} ) func TestDelims(t *testing.T) { for _, test := range lexDelimTests { items := collect(&test, "$$", "@@") - if !reflect.DeepEqual(items, test.items) { + if !equal(items, test.items, false) { + t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) + } + } +} + +var lexPosTests = []lexTest{ + {"empty", "", []item{tEOF}}, + {"punctuation", "{{,@%#}}", []item{ + {itemLeftDelim, 0, "{{"}, + {itemChar, 2, ","}, + {itemChar, 3, "@"}, + {itemChar, 4, "%"}, + {itemChar, 5, "#"}, + {itemRightDelim, 6, "}}"}, + {itemEOF, 8, ""}, + }}, + {"sample", "0123{{hello}}xyz", []item{ + {itemText, 0, "0123"}, + {itemLeftDelim, 4, "{{"}, + {itemIdentifier, 6, "hello"}, + {itemRightDelim, 11, "}}"}, + {itemText, 13, "xyz"}, + {itemEOF, 16, ""}, + }}, +} + +// The other tests don't check position, to make the test cases easier to construct. +// This one does. +func TestPos(t *testing.T) { + for _, test := range lexPosTests { + items := collect(&test, "", "") + if !equal(items, test.items, true) { t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) + if len(items) == len(test.items) { + // Detailed print; avoid item.String() to expose the position value. + for i := range items { + if !equal(items[i:i+1], test.items[i:i+1], true) { + i1 := items[i] + i2 := test.items[i] + t.Errorf("\t#%d: got {%v %d %q} expected {%v %d %q}", i, i1.typ, i1.pos, i1.val, i2.typ, i2.pos, i2.val) + } + } + } } } } diff --git a/src/pkg/text/template/parse/node.go b/src/pkg/text/template/parse/node.go index db645624c..9d0d09eb5 100644 --- a/src/pkg/text/template/parse/node.go +++ b/src/pkg/text/template/parse/node.go @@ -13,7 +13,9 @@ import ( "strings" ) -// A node is an element in the parse tree. The interface is trivial. +// A Node is an element in the parse tree. The interface is trivial. +// The interface contains an unexported method so that only +// types local to this package can satisfy it. type Node interface { Type() NodeType String() string @@ -21,11 +23,27 @@ type Node interface { // To avoid type assertions, some XxxNodes also have specialized // CopyXxx methods that return *XxxNode. Copy() Node + Position() Pos // byte position of start of node in full original input string + // Make sure only functions in this package can create Nodes. + unexported() } // NodeType identifies the type of a parse tree node. type NodeType int +// Pos represents a byte position in the original input text from which +// this template was parsed. +type Pos int + +func (p Pos) Position() Pos { + return p +} + +// unexported keeps Node implementations local to the package. +// All implementations embed Pos, so this takes care of it. +func (Pos) unexported() { +} + // Type returns itself and provides an easy default implementation // for embedding in a Node. Embedded in all non-trivial Nodes. func (t NodeType) Type() NodeType { @@ -34,8 +52,9 @@ func (t NodeType) Type() NodeType { const ( NodeText NodeType = iota // Plain text. - NodeAction // A simple action such as field evaluation. + NodeAction // A non-control action such as a field evaluation. NodeBool // A boolean constant. + NodeChain // A sequence of field accesses. NodeCommand // An element of a pipeline. NodeDot // The cursor, dot. nodeElse // An else action. Not added to tree. @@ -44,6 +63,7 @@ const ( NodeIdentifier // An identifier; always a function name. NodeIf // An if action. NodeList // A list of Nodes. + NodeNil // An untyped nil constant. NodeNumber // A numerical constant. NodePipe // A pipeline of commands. NodeRange // A range action. @@ -58,11 +78,12 @@ const ( // ListNode holds a sequence of nodes. type ListNode struct { NodeType + Pos Nodes []Node // The element nodes in lexical order. } -func newList() *ListNode { - return &ListNode{NodeType: NodeList} +func newList(pos Pos) *ListNode { + return &ListNode{NodeType: NodeList, Pos: pos} } func (l *ListNode) append(n Node) { @@ -81,7 +102,7 @@ func (l *ListNode) CopyList() *ListNode { if l == nil { return l } - n := newList() + n := newList(l.Pos) for _, elem := range l.Nodes { n.append(elem.Copy()) } @@ -95,11 +116,12 @@ func (l *ListNode) Copy() Node { // TextNode holds plain text. type TextNode struct { NodeType + Pos Text []byte // The text; may span newlines. } -func newText(text string) *TextNode { - return &TextNode{NodeType: NodeText, Text: []byte(text)} +func newText(pos Pos, text string) *TextNode { + return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)} } func (t *TextNode) String() string { @@ -113,13 +135,14 @@ func (t *TextNode) Copy() Node { // PipeNode holds a pipeline with optional declaration type PipeNode struct { NodeType - Line int // The line number in the input. + Pos + Line int // The line number in the input (deprecated; kept for compatibility) Decl []*VariableNode // Variable declarations in lexical order. Cmds []*CommandNode // The commands in lexical order. } -func newPipeline(line int, decl []*VariableNode) *PipeNode { - return &PipeNode{NodeType: NodePipe, Line: line, Decl: decl} +func newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode { + return &PipeNode{NodeType: NodePipe, Pos: pos, Line: line, Decl: decl} } func (p *PipeNode) append(command *CommandNode) { @@ -154,7 +177,7 @@ func (p *PipeNode) CopyPipe() *PipeNode { for _, d := range p.Decl { decl = append(decl, d.Copy().(*VariableNode)) } - n := newPipeline(p.Line, decl) + n := newPipeline(p.Pos, p.Line, decl) for _, c := range p.Cmds { n.append(c.Copy().(*CommandNode)) } @@ -167,15 +190,16 @@ func (p *PipeNode) Copy() Node { // ActionNode holds an action (something bounded by delimiters). // Control actions have their own nodes; ActionNode represents simple -// ones such as field evaluations. +// ones such as field evaluations and parenthesized pipelines. type ActionNode struct { NodeType - Line int // The line number in the input. + Pos + Line int // The line number in the input (deprecated; kept for compatibility) Pipe *PipeNode // The pipeline in the action. } -func newAction(line int, pipe *PipeNode) *ActionNode { - return &ActionNode{NodeType: NodeAction, Line: line, Pipe: pipe} +func newAction(pos Pos, line int, pipe *PipeNode) *ActionNode { + return &ActionNode{NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe} } func (a *ActionNode) String() string { @@ -184,18 +208,19 @@ func (a *ActionNode) String() string { } func (a *ActionNode) Copy() Node { - return newAction(a.Line, a.Pipe.CopyPipe()) + return newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) } // CommandNode holds a command (a pipeline inside an evaluating action). type CommandNode struct { NodeType + Pos Args []Node // Arguments in lexical order: Identifier, field, or constant. } -func newCommand() *CommandNode { - return &CommandNode{NodeType: NodeCommand} +func newCommand(pos Pos) *CommandNode { + return &CommandNode{NodeType: NodeCommand, Pos: pos} } func (c *CommandNode) append(arg Node) { @@ -208,6 +233,10 @@ func (c *CommandNode) String() string { if i > 0 { s += " " } + if arg, ok := arg.(*PipeNode); ok { + s += "(" + arg.String() + ")" + continue + } s += arg.String() } return s @@ -217,7 +246,7 @@ func (c *CommandNode) Copy() Node { if c == nil { return c } - n := newCommand() + n := newCommand(c.Pos) for _, c := range c.Args { n.append(c.Copy()) } @@ -227,6 +256,7 @@ func (c *CommandNode) Copy() Node { // IdentifierNode holds an identifier. type IdentifierNode struct { NodeType + Pos Ident string // The identifier's name. } @@ -235,23 +265,32 @@ func NewIdentifier(ident string) *IdentifierNode { return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident} } +// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature. +// Chained for convenience. +// TODO: fix one day? +func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode { + i.Pos = pos + return i +} + func (i *IdentifierNode) String() string { return i.Ident } func (i *IdentifierNode) Copy() Node { - return NewIdentifier(i.Ident) + return NewIdentifier(i.Ident).SetPos(i.Pos) } -// VariableNode holds a list of variable names. The dollar sign is -// part of the name. +// VariableNode holds a list of variable names, possibly with chained field +// accesses. The dollar sign is part of the (first) name. type VariableNode struct { NodeType - Ident []string // Variable names in lexical order. + Pos + Ident []string // Variable name and fields in lexical order. } -func newVariable(ident string) *VariableNode { - return &VariableNode{NodeType: NodeVariable, Ident: strings.Split(ident, ".")} +func newVariable(pos Pos, ident string) *VariableNode { + return &VariableNode{NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")} } func (v *VariableNode) String() string { @@ -266,14 +305,16 @@ func (v *VariableNode) String() string { } func (v *VariableNode) Copy() Node { - return &VariableNode{NodeType: NodeVariable, Ident: append([]string{}, v.Ident...)} + return &VariableNode{NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)} } -// DotNode holds the special identifier '.'. It is represented by a nil pointer. -type DotNode bool +// DotNode holds the special identifier '.'. +type DotNode struct { + Pos +} -func newDot() *DotNode { - return nil +func newDot(pos Pos) *DotNode { + return &DotNode{Pos: pos} } func (d *DotNode) Type() NodeType { @@ -285,7 +326,28 @@ func (d *DotNode) String() string { } func (d *DotNode) Copy() Node { - return newDot() + return newDot(d.Pos) +} + +// NilNode holds the special identifier 'nil' representing an untyped nil constant. +type NilNode struct { + Pos +} + +func newNil(pos Pos) *NilNode { + return &NilNode{Pos: pos} +} + +func (n *NilNode) Type() NodeType { + return NodeNil +} + +func (n *NilNode) String() string { + return "nil" +} + +func (n *NilNode) Copy() Node { + return newNil(n.Pos) } // FieldNode holds a field (identifier starting with '.'). @@ -293,11 +355,12 @@ func (d *DotNode) Copy() Node { // The period is dropped from each ident. type FieldNode struct { NodeType + Pos Ident []string // The identifiers in lexical order. } -func newField(ident string) *FieldNode { - return &FieldNode{NodeType: NodeField, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period +func newField(pos Pos, ident string) *FieldNode { + return &FieldNode{NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period } func (f *FieldNode) String() string { @@ -309,17 +372,59 @@ func (f *FieldNode) String() string { } func (f *FieldNode) Copy() Node { - return &FieldNode{NodeType: NodeField, Ident: append([]string{}, f.Ident...)} + return &FieldNode{NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)} +} + +// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.'). +// The names may be chained ('.x.y'). +// The periods are dropped from each ident. +type ChainNode struct { + NodeType + Pos + Node Node + Field []string // The identifiers in lexical order. +} + +func newChain(pos Pos, node Node) *ChainNode { + return &ChainNode{NodeType: NodeChain, Pos: pos, Node: node} +} + +// Add adds the named field (which should start with a period) to the end of the chain. +func (c *ChainNode) Add(field string) { + if len(field) == 0 || field[0] != '.' { + panic("no dot in field") + } + field = field[1:] // Remove leading dot. + if field == "" { + panic("empty field") + } + c.Field = append(c.Field, field) +} + +func (c *ChainNode) String() string { + s := c.Node.String() + if _, ok := c.Node.(*PipeNode); ok { + s = "(" + s + ")" + } + for _, field := range c.Field { + s += "." + field + } + return s +} + +func (c *ChainNode) Copy() Node { + return &ChainNode{NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)} } // BoolNode holds a boolean constant. type BoolNode struct { NodeType + Pos True bool // The value of the boolean constant. } -func newBool(true bool) *BoolNode { - return &BoolNode{NodeType: NodeBool, True: true} +func newBool(pos Pos, true bool) *BoolNode { + return &BoolNode{NodeType: NodeBool, Pos: pos, True: true} } func (b *BoolNode) String() string { @@ -330,7 +435,7 @@ func (b *BoolNode) String() string { } func (b *BoolNode) Copy() Node { - return newBool(b.True) + return newBool(b.Pos, b.True) } // NumberNode holds a number: signed or unsigned integer, float, or complex. @@ -338,6 +443,7 @@ func (b *BoolNode) Copy() Node { // This simulates in a small amount of code the behavior of Go's ideal constants. type NumberNode struct { NodeType + Pos IsInt bool // Number has an integral value. IsUint bool // Number has an unsigned integral value. IsFloat bool // Number has a floating-point value. @@ -349,8 +455,8 @@ type NumberNode struct { Text string // The original textual representation from the input. } -func newNumber(text string, typ itemType) (*NumberNode, error) { - n := &NumberNode{NodeType: NodeNumber, Text: text} +func newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) { + n := &NumberNode{NodeType: NodeNumber, Pos: pos, Text: text} switch typ { case itemCharConstant: rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0]) @@ -460,12 +566,13 @@ func (n *NumberNode) Copy() Node { // StringNode holds a string constant. The value has been "unquoted". type StringNode struct { NodeType + Pos Quoted string // The original text of the string, with quotes. Text string // The string, after quote processing. } -func newString(orig, text string) *StringNode { - return &StringNode{NodeType: NodeString, Quoted: orig, Text: text} +func newString(pos Pos, orig, text string) *StringNode { + return &StringNode{NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} } func (s *StringNode) String() string { @@ -473,15 +580,17 @@ func (s *StringNode) String() string { } func (s *StringNode) Copy() Node { - return newString(s.Quoted, s.Text) + return newString(s.Pos, s.Quoted, s.Text) } -// endNode represents an {{end}} action. It is represented by a nil pointer. +// endNode represents an {{end}} action. // It does not appear in the final parse tree. -type endNode bool +type endNode struct { + Pos +} -func newEnd() *endNode { - return nil +func newEnd(pos Pos) *endNode { + return &endNode{Pos: pos} } func (e *endNode) Type() NodeType { @@ -493,17 +602,18 @@ func (e *endNode) String() string { } func (e *endNode) Copy() Node { - return newEnd() + return newEnd(e.Pos) } // elseNode represents an {{else}} action. Does not appear in the final tree. type elseNode struct { NodeType - Line int // The line number in the input. + Pos + Line int // The line number in the input (deprecated; kept for compatibility) } -func newElse(line int) *elseNode { - return &elseNode{NodeType: nodeElse, Line: line} +func newElse(pos Pos, line int) *elseNode { + return &elseNode{NodeType: nodeElse, Pos: pos, Line: line} } func (e *elseNode) Type() NodeType { @@ -515,13 +625,14 @@ func (e *elseNode) String() string { } func (e *elseNode) Copy() Node { - return newElse(e.Line) + return newElse(e.Pos, e.Line) } // BranchNode is the common representation of if, range, and with. type BranchNode struct { NodeType - Line int // The line number in the input. + Pos + Line int // The line number in the input (deprecated; kept for compatibility) Pipe *PipeNode // The pipeline to be evaluated. List *ListNode // What to execute if the value is non-empty. ElseList *ListNode // What to execute if the value is empty (nil if absent). @@ -550,12 +661,12 @@ type IfNode struct { BranchNode } -func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { - return &IfNode{BranchNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { + return &IfNode{BranchNode{NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} } func (i *IfNode) Copy() Node { - return newIf(i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) + return newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) } // RangeNode represents a {{range}} action and its commands. @@ -563,12 +674,12 @@ type RangeNode struct { BranchNode } -func newRange(line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { - return &RangeNode{BranchNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { + return &RangeNode{BranchNode{NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} } func (r *RangeNode) Copy() Node { - return newRange(r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) + return newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) } // WithNode represents a {{with}} action and its commands. @@ -576,24 +687,25 @@ type WithNode struct { BranchNode } -func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { - return &WithNode{BranchNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +func newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { + return &WithNode{BranchNode{NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} } func (w *WithNode) Copy() Node { - return newWith(w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) + return newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) } // TemplateNode represents a {{template}} action. type TemplateNode struct { NodeType - Line int // The line number in the input. + Pos + Line int // The line number in the input (deprecated; kept for compatibility) Name string // The name of the template (unquoted). Pipe *PipeNode // The command to evaluate as dot for the template. } -func newTemplate(line int, name string, pipe *PipeNode) *TemplateNode { - return &TemplateNode{NodeType: NodeTemplate, Line: line, Name: name, Pipe: pipe} +func newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode { + return &TemplateNode{NodeType: NodeTemplate, Line: line, Pos: pos, Name: name, Pipe: pipe} } func (t *TemplateNode) String() string { @@ -604,5 +716,5 @@ func (t *TemplateNode) String() string { } func (t *TemplateNode) Copy() Node { - return newTemplate(t.Line, t.Name, t.Pipe.CopyPipe()) + return newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe()) } diff --git a/src/pkg/text/template/parse/parse.go b/src/pkg/text/template/parse/parse.go index c0087b278..c0fb9274a 100644 --- a/src/pkg/text/template/parse/parse.go +++ b/src/pkg/text/template/parse/parse.go @@ -13,17 +13,20 @@ import ( "fmt" "runtime" "strconv" + "strings" "unicode" ) // Tree is the representation of a single parsed template. type Tree struct { - Name string // name of the template represented by the tree. - Root *ListNode // top-level root of the tree. + Name string // name of the template represented by the tree. + ParseName string // name of the top-level template during parsing, for error messages. + Root *ListNode // top-level root of the tree. + text string // text parsed to create the template (or its parent) // Parsing only; cleared after parse. funcs []map[string]interface{} lex *lexer - token [2]item // two-token lookahead for parser. + token [3]item // three-token lookahead for parser. peekCount int vars []string // variables defined at the moment. } @@ -34,7 +37,9 @@ type Tree struct { // empty map is returned with the error. func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) { treeSet = make(map[string]*Tree) - _, err = New(name).Parse(text, leftDelim, rightDelim, treeSet, funcs...) + t := New(name) + t.text = text + _, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...) return } @@ -53,12 +58,21 @@ func (t *Tree) backup() { t.peekCount++ } -// backup2 backs the input stream up two tokens +// backup2 backs the input stream up two tokens. +// The zeroth token is already there. func (t *Tree) backup2(t1 item) { t.token[1] = t1 t.peekCount = 2 } +// backup3 backs the input stream up three tokens +// The zeroth token is already there. +func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back. + t.token[1] = t1 + t.token[2] = t2 + t.peekCount = 3 +} + // peek returns but does not consume the next token. func (t *Tree) peek() item { if t.peekCount > 0 { @@ -69,6 +83,29 @@ func (t *Tree) peek() item { return t.token[0] } +// nextNonSpace returns the next non-space token. +func (t *Tree) nextNonSpace() (token item) { + for { + token = t.next() + if token.typ != itemSpace { + break + } + } + return token +} + +// peekNonSpace returns but does not consume the next non-space token. +func (t *Tree) peekNonSpace() (token item) { + for { + token = t.next() + if token.typ != itemSpace { + break + } + } + t.backup() + return token +} + // Parsing. // New allocates a new parse tree with the given name. @@ -79,10 +116,29 @@ func New(name string, funcs ...map[string]interface{}) *Tree { } } +// ErrorContext returns a textual representation of the location of the node in the input text. +func (t *Tree) ErrorContext(n Node) (location, context string) { + pos := int(n.Position()) + text := t.text[:pos] + byteNum := strings.LastIndex(text, "\n") + if byteNum == -1 { + byteNum = pos // On first line. + } else { + byteNum++ // After the newline. + byteNum = pos - byteNum + } + lineNum := 1 + strings.Count(text, "\n") + context = n.String() + if len(context) > 20 { + context = fmt.Sprintf("%.20s...", context) + } + return fmt.Sprintf("%s:%d:%d", t.ParseName, lineNum, byteNum), context +} + // errorf formats the error and terminates processing. func (t *Tree) errorf(format string, args ...interface{}) { t.Root = nil - format = fmt.Sprintf("template: %s:%d: %s", t.Name, t.lex.lineNumber(), format) + format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format) panic(fmt.Errorf(format, args...)) } @@ -93,18 +149,18 @@ func (t *Tree) error(err error) { // expect consumes the next token and guarantees it has the required type. func (t *Tree) expect(expected itemType, context string) item { - token := t.next() + token := t.nextNonSpace() if token.typ != expected { - t.errorf("expected %s in %s; got %s", expected, context, token) + t.unexpected(token, context) } return token } -// expectEither consumes the next token and guarantees it has one of the required types. +// expectOneOf consumes the next token and guarantees it has one of the required types. func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item { - token := t.next() + token := t.nextNonSpace() if token.typ != expected1 && token.typ != expected2 { - t.errorf("expected %s or %s in %s; got %s", expected1, expected2, context, token) + t.unexpected(token, context) } return token } @@ -169,9 +225,11 @@ func (t *Tree) atEOF() bool { // the template for execution. If either action delimiter string is empty, the // default ("{{" or "}}") is used. Embedded template definitions are added to // the treeSet map. -func (t *Tree) Parse(s, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) { +func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) { defer t.recover(&err) - t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim)) + t.ParseName = t.Name + t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim)) + t.text = text t.parse(treeSet) t.add(treeSet) t.stopParse() @@ -219,12 +277,14 @@ func IsEmptyTree(n Node) bool { // as itemList except it also parses {{define}} actions. // It runs to EOF. func (t *Tree) parse(treeSet map[string]*Tree) (next Node) { - t.Root = newList() + t.Root = newList(t.peek().pos) for t.peek().typ != itemEOF { if t.peek().typ == itemLeftDelim { delim := t.next() - if t.next().typ == itemDefine { + if t.nextNonSpace().typ == itemDefine { newT := New("definition") // name will be updated once we know it. + newT.text = t.text + newT.ParseName = t.ParseName newT.startParse(t.funcs, t.lex) newT.parseDefinition(treeSet) continue @@ -257,16 +317,16 @@ func (t *Tree) parseDefinition(treeSet map[string]*Tree) { if end.Type() != nodeEnd { t.errorf("unexpected %s in %s", end, context) } - t.stopParse() t.add(treeSet) + t.stopParse() } // itemList: // textOrAction* // Terminates at {{end}} or {{else}}, returned separately. func (t *Tree) itemList() (list *ListNode, next Node) { - list = newList() - for t.peek().typ != itemEOF { + list = newList(t.peekNonSpace().pos) + for t.peekNonSpace().typ != itemEOF { n := t.textOrAction() switch n.Type() { case nodeEnd, nodeElse: @@ -281,9 +341,9 @@ func (t *Tree) itemList() (list *ListNode, next Node) { // textOrAction: // text | action func (t *Tree) textOrAction() Node { - switch token := t.next(); token.typ { + switch token := t.nextNonSpace(); token.typ { case itemText: - return newText(token.val) + return newText(token.pos, token.val) case itemLeftDelim: return t.action() default: @@ -298,7 +358,7 @@ func (t *Tree) textOrAction() Node { // Left delim is past. Now get actions. // First word could be a keyword such as range. func (t *Tree) action() (n Node) { - switch token := t.next(); token.typ { + switch token := t.nextNonSpace(); token.typ { case itemElse: return t.elseControl() case itemEnd: @@ -314,24 +374,26 @@ func (t *Tree) action() (n Node) { } t.backup() // Do not pop variables; they persist until "end". - return newAction(t.lex.lineNumber(), t.pipeline("command")) + return newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command")) } // Pipeline: -// field or command -// pipeline "|" pipeline +// declarations? command ('|' command)* func (t *Tree) pipeline(context string) (pipe *PipeNode) { var decl []*VariableNode + pos := t.peekNonSpace().pos // Are there declarations? for { - if v := t.peek(); v.typ == itemVariable { + if v := t.peekNonSpace(); v.typ == itemVariable { t.next() - if next := t.peek(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") { - t.next() - variable := newVariable(v.val) - if len(variable.Ident) != 1 { - t.errorf("illegal variable in declaration: %s", v.val) - } + // Since space is a token, we need 3-token look-ahead here in the worst case: + // in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an + // argument variable rather than a declaration. So remember the token + // adjacent to the variable so we can push it back if necessary. + tokenAfterVariable := t.peek() + if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") { + t.nextNonSpace() + variable := newVariable(v.pos, v.val) decl = append(decl, variable) t.vars = append(t.vars, v.val) if next.typ == itemChar && next.val == "," { @@ -340,22 +402,27 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { } t.errorf("too many declarations in %s", context) } + } else if tokenAfterVariable.typ == itemSpace { + t.backup3(v, tokenAfterVariable) } else { t.backup2(v) } } break } - pipe = newPipeline(t.lex.lineNumber(), decl) + pipe = newPipeline(pos, t.lex.lineNumber(), decl) for { - switch token := t.next(); token.typ { - case itemRightDelim: + switch token := t.nextNonSpace(); token.typ { + case itemRightDelim, itemRightParen: if len(pipe.Cmds) == 0 { t.errorf("missing value for %s", context) } + if token.typ == itemRightParen { + t.backup() + } return case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier, - itemVariable, itemNumber, itemRawString, itemString: + itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen: t.backup() pipe.append(t.command()) default: @@ -365,9 +432,9 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { return } -func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list, elseList *ListNode) { - lineNum = t.lex.lineNumber() +func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { defer t.popVars(len(t.vars)) + line = t.lex.lineNumber() pipe = t.pipeline(context) var next Node list, next = t.itemList() @@ -380,7 +447,7 @@ func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list, } elseList = elseList } - return lineNum, pipe, list, elseList + return pipe.Position(), line, pipe, list, elseList } // If: @@ -411,16 +478,14 @@ func (t *Tree) withControl() Node { // {{end}} // End keyword is past. func (t *Tree) endControl() Node { - t.expect(itemRightDelim, "end") - return newEnd() + return newEnd(t.expect(itemRightDelim, "end").pos) } // Else: // {{else}} // Else keyword is past. func (t *Tree) elseControl() Node { - t.expect(itemRightDelim, "else") - return newElse(t.lex.lineNumber()) + return newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber()) } // Template: @@ -429,7 +494,8 @@ func (t *Tree) elseControl() Node { // to a string. func (t *Tree) templateControl() Node { var name string - switch token := t.next(); token.typ { + token := t.nextNonSpace() + switch token.typ { case itemString, itemRawString: s, err := strconv.Unquote(token.val) if err != nil { @@ -440,57 +506,38 @@ func (t *Tree) templateControl() Node { t.unexpected(token, "template invocation") } var pipe *PipeNode - if t.next().typ != itemRightDelim { + if t.nextNonSpace().typ != itemRightDelim { t.backup() // Do not pop variables; they persist until "end". pipe = t.pipeline("template") } - return newTemplate(t.lex.lineNumber(), name, pipe) + return newTemplate(token.pos, t.lex.lineNumber(), name, pipe) } // command: +// operand (space operand)* // space-separated arguments up to a pipeline character or right delimiter. // we consume the pipe character but leave the right delim to terminate the action. func (t *Tree) command() *CommandNode { - cmd := newCommand() -Loop: + cmd := newCommand(t.peekNonSpace().pos) for { + t.peekNonSpace() // skip leading spaces. + operand := t.operand() + if operand != nil { + cmd.append(operand) + } switch token := t.next(); token.typ { - case itemRightDelim: - t.backup() - break Loop - case itemPipe: - break Loop + case itemSpace: + continue case itemError: t.errorf("%s", token.val) - case itemIdentifier: - if !t.hasFunction(token.val) { - t.errorf("function %q not defined", token.val) - } - cmd.append(NewIdentifier(token.val)) - case itemDot: - cmd.append(newDot()) - case itemVariable: - cmd.append(t.useVar(token.val)) - case itemField: - cmd.append(newField(token.val)) - case itemBool: - cmd.append(newBool(token.val == "true")) - case itemCharConstant, itemComplex, itemNumber: - number, err := newNumber(token.val, token.typ) - if err != nil { - t.error(err) - } - cmd.append(number) - case itemString, itemRawString: - s, err := strconv.Unquote(token.val) - if err != nil { - t.error(err) - } - cmd.append(newString(token.val, s)) + case itemRightDelim, itemRightParen: + t.backup() + case itemPipe: default: - t.unexpected(token, "command") + t.errorf("unexpected %s in operand; missing space?", token) } + break } if len(cmd.Args) == 0 { t.errorf("empty command") @@ -498,6 +545,88 @@ Loop: return cmd } +// operand: +// term .Field* +// An operand is a space-separated component of a command, +// a term possibly followed by field accesses. +// A nil return means the next item is not an operand. +func (t *Tree) operand() Node { + node := t.term() + if node == nil { + return nil + } + if t.peek().typ == itemField { + chain := newChain(t.peek().pos, node) + for t.peek().typ == itemField { + chain.Add(t.next().val) + } + // Compatibility with original API: If the term is of type NodeField + // or NodeVariable, just put more fields on the original. + // Otherwise, keep the Chain node. + // TODO: Switch to Chains always when we can. + switch node.Type() { + case NodeField: + node = newField(chain.Position(), chain.String()) + case NodeVariable: + node = newVariable(chain.Position(), chain.String()) + default: + node = chain + } + } + return node +} + +// term: +// literal (number, string, nil, boolean) +// function (identifier) +// . +// .Field +// $ +// '(' pipeline ')' +// A term is a simple "expression". +// A nil return means the next item is not a term. +func (t *Tree) term() Node { + switch token := t.nextNonSpace(); token.typ { + case itemError: + t.errorf("%s", token.val) + case itemIdentifier: + if !t.hasFunction(token.val) { + t.errorf("function %q not defined", token.val) + } + return NewIdentifier(token.val).SetPos(token.pos) + case itemDot: + return newDot(token.pos) + case itemNil: + return newNil(token.pos) + case itemVariable: + return t.useVar(token.pos, token.val) + case itemField: + return newField(token.pos, token.val) + case itemBool: + return newBool(token.pos, token.val == "true") + case itemCharConstant, itemComplex, itemNumber: + number, err := newNumber(token.pos, token.val, token.typ) + if err != nil { + t.error(err) + } + return number + case itemLeftParen: + pipe := t.pipeline("parenthesized pipeline") + if token := t.next(); token.typ != itemRightParen { + t.errorf("unclosed right paren: unexpected %s", token) + } + return pipe + case itemString, itemRawString: + s, err := strconv.Unquote(token.val) + if err != nil { + t.error(err) + } + return newString(token.pos, token.val, s) + } + t.backup() + return nil +} + // hasFunction reports if a function name exists in the Tree's maps. func (t *Tree) hasFunction(name string) bool { for _, funcMap := range t.funcs { @@ -518,8 +647,8 @@ func (t *Tree) popVars(n int) { // useVar returns a node for a variable reference. It errors if the // variable is not defined. -func (t *Tree) useVar(name string) Node { - v := newVariable(name) +func (t *Tree) useVar(pos Pos, name string) Node { + v := newVariable(pos, name) for _, varName := range t.vars { if varName == v.Ident[0] { return v diff --git a/src/pkg/text/template/parse/parse_test.go b/src/pkg/text/template/parse/parse_test.go index b2e788238..695c76ebf 100644 --- a/src/pkg/text/template/parse/parse_test.go +++ b/src/pkg/text/template/parse/parse_test.go @@ -7,10 +7,11 @@ package parse import ( "flag" "fmt" + "strings" "testing" ) -var debug = flag.Bool("debug", false, "show the errors produced by the tests") +var debug = flag.Bool("debug", false, "show the errors produced by the main tests") type numberTest struct { text string @@ -84,7 +85,7 @@ func TestNumberParse(t *testing.T) { typ = itemComplex } } - n, err := newNumber(test.text, typ) + n, err := newNumber(0, test.text, typ) ok := test.isInt || test.isUint || test.isFloat || test.isComplex if ok && err != nil { t.Errorf("unexpected error for %q: %s", test.text, err) @@ -185,6 +186,10 @@ var parseTests = []parseTest{ `{{.X | .Y}}`}, {"pipeline with decl", "{{$x := .X|.Y}}", noError, `{{$x := .X | .Y}}`}, + {"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError, + `{{.X (.Y .Z) (.A | .B .C) (.E)}}`}, + {"field applied to parentheses", "{{(.Y .Z).Field}}", noError, + `{{(.Y .Z).Field}}`}, {"simple if", "{{if .X}}hello{{end}}", noError, `{{if .X}}"hello"{{end}}`}, {"if with else", "{{if .X}}true{{else}}false{{end}}", noError, @@ -205,8 +210,8 @@ var parseTests = []parseTest{ `{{range $x := .SI}}{{.}}{{end}}`}, {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError, `{{range $x, $y := .SI}}{{.}}{{end}}`}, - {"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError, - `{{range .SI 1 -3.2i true false 'a'}}{{end}}`}, + {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError, + `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`}, {"template", "{{template `x`}}", noError, `{{template "x"}}`}, {"template with arg", "{{template `x` .Y}}", noError, @@ -230,6 +235,9 @@ var parseTests = []parseTest{ {"invalid punctuation", "{{printf 3, 4}}", hasError, ""}, {"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""}, {"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""}, + {"dot applied to parentheses", "{{printf (printf .).}}", hasError, ""}, + {"adjacent args", "{{printf 3`x`}}", hasError, ""}, + {"adjacent args with .", "{{printf `x`.}}", hasError, ""}, // Equals (and other chars) do not assignments make (yet). {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"}, {"bug0b", "{{$x = 1}}{{$x}}", hasError, ""}, @@ -316,3 +324,74 @@ func TestIsEmpty(t *testing.T) { } } } + +// All failures, and the result is a string that must appear in the error message. +var errorTests = []parseTest{ + // Check line numbers are accurate. + {"unclosed1", + "line1\n{{", + hasError, `unclosed1:2: unexpected unclosed action in command`}, + {"unclosed2", + "line1\n{{define `x`}}line2\n{{", + hasError, `unclosed2:3: unexpected unclosed action in command`}, + // Specific errors. + {"function", + "{{foo}}", + hasError, `function "foo" not defined`}, + {"comment", + "{{/*}}", + hasError, `unclosed comment`}, + {"lparen", + "{{.X (1 2 3}}", + hasError, `unclosed left paren`}, + {"rparen", + "{{.X 1 2 3)}}", + hasError, `unexpected ")"`}, + {"space", + "{{`x`3}}", + hasError, `missing space?`}, + {"idchar", + "{{a#}}", + hasError, `'#'`}, + {"charconst", + "{{'a}}", + hasError, `unterminated character constant`}, + {"stringconst", + `{{"a}}`, + hasError, `unterminated quoted string`}, + {"rawstringconst", + "{{`a}}", + hasError, `unterminated raw quoted string`}, + {"number", + "{{0xi}}", + hasError, `number syntax`}, + {"multidefine", + "{{define `a`}}a{{end}}{{define `a`}}b{{end}}", + hasError, `multiple definition of template`}, + {"eof", + "{{range .X}}", + hasError, `unexpected EOF`}, + {"variable", + // Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration. + "{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}", + hasError, `unexpected ":="`}, + {"multidecl", + "{{$a,$b,$c := 23}}", + hasError, `too many declarations`}, + {"undefvar", + "{{$a}}", + hasError, `undefined variable`}, +} + +func TestErrors(t *testing.T) { + for _, test := range errorTests { + _, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree)) + if err == nil { + t.Errorf("%q: expected error", test.name) + continue + } + if !strings.Contains(err.Error(), test.result) { + t.Errorf("%q: error %q does not contain %q", test.name, err, test.result) + } + } +} diff --git a/src/pkg/text/template/template.go b/src/pkg/text/template/template.go index 82fc9e5e3..a2b9062ad 100644 --- a/src/pkg/text/template/template.go +++ b/src/pkg/text/template/template.go @@ -117,6 +117,9 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error // Templates returns a slice of the templates associated with t, including t // itself. func (t *Template) Templates() []*Template { + if t.common == nil { + return nil + } // Return a slice so we don't expose the map. m := make([]*Template, 0, len(t.tmpl)) for _, v := range t.tmpl { diff --git a/src/pkg/time/example_test.go b/src/pkg/time/example_test.go index 944cc789c..8928caaba 100644 --- a/src/pkg/time/example_test.go +++ b/src/pkg/time/example_test.go @@ -56,3 +56,100 @@ func ExampleDate() { fmt.Printf("Go launched at %s\n", t.Local()) // Output: Go launched at 2009-11-10 15:00:00 -0800 PST } + +func ExampleTime_Format() { + const format = "Jan 2, 2006 at 3:04pm (MST)" + t := time.Date(2009, time.November, 10, 15, 0, 0, 0, time.Local) + fmt.Println(t.Format(format)) + fmt.Println(t.UTC().Format(format)) + // Output: + // Nov 10, 2009 at 3:00pm (PST) + // Nov 10, 2009 at 11:00pm (UTC) +} + +func ExampleParse() { + const longForm = "Jan 2, 2006 at 3:04pm (MST)" + t, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)") + fmt.Println(t) + + // Note: without explicit zone, returns time in UTC. + const shortForm = "2006-Jan-02" + t, _ = time.Parse(shortForm, "2013-Feb-03") + fmt.Println(t) + + // Output: + // 2013-02-03 19:54:00 -0800 PST + // 2013-02-03 00:00:00 +0000 UTC +} + +func ExampleParseInLocation() { + loc, _ := time.LoadLocation("Europe/Berlin") + + const longForm = "Jan 2, 2006 at 3:04pm (MST)" + t, _ := time.ParseInLocation(longForm, "Jul 9, 2012 at 5:02am (CEST)", loc) + fmt.Println(t) + + // Note: without explicit zone, returns time in given location. + const shortForm = "2006-Jan-02" + t, _ = time.ParseInLocation(shortForm, "2012-Jul-09", loc) + fmt.Println(t) + + // Output: + // 2012-07-09 05:02:00 +0200 CEST + // 2012-07-09 00:00:00 +0200 CEST +} + +func ExampleTime_Round() { + t := time.Date(0, 0, 0, 12, 15, 30, 918273645, time.UTC) + round := []time.Duration{ + time.Nanosecond, + time.Microsecond, + time.Millisecond, + time.Second, + 2 * time.Second, + time.Minute, + 10 * time.Minute, + time.Hour, + } + + for _, d := range round { + fmt.Printf("t.Round(%6s) = %s\n", d, t.Round(d).Format("15:04:05.999999999")) + } + // Output: + // t.Round( 1ns) = 12:15:30.918273645 + // t.Round( 1us) = 12:15:30.918274 + // t.Round( 1ms) = 12:15:30.918 + // t.Round( 1s) = 12:15:31 + // t.Round( 2s) = 12:15:30 + // t.Round( 1m0s) = 12:16:00 + // t.Round( 10m0s) = 12:20:00 + // t.Round(1h0m0s) = 12:00:00 +} + +func ExampleTime_Truncate() { + t, _ := time.Parse("2006 Jan 02 15:04:05", "2012 Dec 07 12:15:30.918273645") + trunc := []time.Duration{ + time.Nanosecond, + time.Microsecond, + time.Millisecond, + time.Second, + 2 * time.Second, + time.Minute, + 10 * time.Minute, + time.Hour, + } + + for _, d := range trunc { + fmt.Printf("t.Truncate(%6s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999")) + } + + // Output: + // t.Truncate( 1ns) = 12:15:30.918273645 + // t.Truncate( 1us) = 12:15:30.918273 + // t.Truncate( 1ms) = 12:15:30.918 + // t.Truncate( 1s) = 12:15:30 + // t.Truncate( 2s) = 12:15:30 + // t.Truncate( 1m0s) = 12:15:00 + // t.Truncate( 10m0s) = 12:10:00 + // t.Truncate(1h0m0s) = 12:00:00 +} diff --git a/src/pkg/time/export_test.go b/src/pkg/time/export_test.go new file mode 100644 index 000000000..130ca8f7e --- /dev/null +++ b/src/pkg/time/export_test.go @@ -0,0 +1,19 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package time + +import ( + "sync" +) + +func ResetLocalOnceForTest() { + localOnce = sync.Once{} + localLoc = Location{} +} + +func ForceUSPacificForTesting() { + ResetLocalOnceForTest() + localOnce.Do(initTestingZone) +} diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go index efca3a926..817c79a80 100644 --- a/src/pkg/time/format.go +++ b/src/pkg/time/format.go @@ -9,7 +9,7 @@ import "errors" // These are predefined layouts for use in Time.Format. // The standard time used in the layouts is: // Mon Jan 2 15:04:05 MST 2006 -// which is Unix time 1136243045. Since MST is GMT-0700, +// which is Unix time 1136239445. Since MST is GMT-0700, // the standard time can be thought of as // 01/02 03:04:05PM '06 -0700 // To define your own format, write down what the standard time would look @@ -57,63 +57,74 @@ const ( ) const ( - stdLongMonth = "January" - stdMonth = "Jan" - stdNumMonth = "1" - stdZeroMonth = "01" - stdLongWeekDay = "Monday" - stdWeekDay = "Mon" - stdDay = "2" - stdUnderDay = "_2" - stdZeroDay = "02" - stdHour = "15" - stdHour12 = "3" - stdZeroHour12 = "03" - stdMinute = "4" - stdZeroMinute = "04" - stdSecond = "5" - stdZeroSecond = "05" - stdLongYear = "2006" - stdYear = "06" - stdPM = "PM" - stdpm = "pm" - stdTZ = "MST" - stdISO8601TZ = "Z0700" // prints Z for UTC - stdISO8601ColonTZ = "Z07:00" // prints Z for UTC - stdNumTZ = "-0700" // always numeric - stdNumShortTZ = "-07" // always numeric - stdNumColonTZ = "-07:00" // always numeric + _ = iota + stdLongMonth = iota + stdNeedDate // "January" + stdMonth // "Jan" + stdNumMonth // "1" + stdZeroMonth // "01" + stdLongWeekDay // "Monday" + stdWeekDay // "Mon" + stdDay // "2" + stdUnderDay // "_2" + stdZeroDay // "02" + stdHour = iota + stdNeedClock // "15" + stdHour12 // "3" + stdZeroHour12 // "03" + stdMinute // "4" + stdZeroMinute // "04" + stdSecond // "5" + stdZeroSecond // "05" + stdLongYear = iota + stdNeedDate // "2006" + stdYear // "06" + stdPM = iota + stdNeedClock // "PM" + stdpm // "pm" + stdTZ = iota // "MST" + stdISO8601TZ // "Z0700" // prints Z for UTC + stdISO8601ColonTZ // "Z07:00" // prints Z for UTC + stdNumTZ // "-0700" // always numeric + stdNumShortTZ // "-07" // always numeric + stdNumColonTZ // "-07:00" // always numeric + stdFracSecond0 // ".0", ".00", ... , trailing zeros included + stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted + + stdNeedDate = 1 << 8 // need month, day, year + stdNeedClock = 2 << 8 // need hour, minute, second + stdArgShift = 16 // extra argument in high bits, above low stdArgShift + stdMask = 1<= i+7 && layout[i:i+7] == stdLongMonth { - return layout[0:i], stdLongMonth, layout[i+7:] - } - if len(layout) >= i+3 && layout[i:i+3] == stdMonth { + if len(layout) >= i+3 && layout[i:i+3] == "Jan" { + if len(layout) >= i+7 && layout[i:i+7] == "January" { + return layout[0:i], stdLongMonth, layout[i+7:] + } return layout[0:i], stdMonth, layout[i+3:] } case 'M': // Monday, Mon, MST - if len(layout) >= i+6 && layout[i:i+6] == stdLongWeekDay { - return layout[0:i], stdLongWeekDay, layout[i+6:] - } if len(layout) >= i+3 { - if layout[i:i+3] == stdWeekDay { + if layout[i:i+3] == "Mon" { + if len(layout) >= i+6 && layout[i:i+6] == "Monday" { + return layout[0:i], stdLongWeekDay, layout[i+6:] + } return layout[0:i], stdWeekDay, layout[i+3:] } - if layout[i:i+3] == stdTZ { + if layout[i:i+3] == "MST" { return layout[0:i], stdTZ, layout[i+3:] } } case '0': // 01, 02, 03, 04, 05, 06 if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' { - return layout[0:i], layout[i : i+2], layout[i+2:] + return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:] } case '1': // 15, 1 @@ -123,7 +134,7 @@ func nextStdChunk(layout string) (prefix, std, suffix string) { return layout[0:i], stdNumMonth, layout[i+1:] case '2': // 2006, 2 - if len(layout) >= i+4 && layout[i:i+4] == stdLongYear { + if len(layout) >= i+4 && layout[i:i+4] == "2006" { return layout[0:i], stdLongYear, layout[i+4:] } return layout[0:i], stdDay, layout[i+1:] @@ -133,35 +144,41 @@ func nextStdChunk(layout string) (prefix, std, suffix string) { return layout[0:i], stdUnderDay, layout[i+2:] } - case '3', '4', '5': // 3, 4, 5 - return layout[0:i], layout[i : i+1], layout[i+1:] + case '3': + return layout[0:i], stdHour12, layout[i+1:] + + case '4': + return layout[0:i], stdMinute, layout[i+1:] + + case '5': + return layout[0:i], stdSecond, layout[i+1:] case 'P': // PM if len(layout) >= i+2 && layout[i+1] == 'M' { - return layout[0:i], layout[i : i+2], layout[i+2:] + return layout[0:i], stdPM, layout[i+2:] } case 'p': // pm if len(layout) >= i+2 && layout[i+1] == 'm' { - return layout[0:i], layout[i : i+2], layout[i+2:] + return layout[0:i], stdpm, layout[i+2:] } case '-': // -0700, -07:00, -07 - if len(layout) >= i+5 && layout[i:i+5] == stdNumTZ { - return layout[0:i], layout[i : i+5], layout[i+5:] + if len(layout) >= i+5 && layout[i:i+5] == "-0700" { + return layout[0:i], stdNumTZ, layout[i+5:] } - if len(layout) >= i+6 && layout[i:i+6] == stdNumColonTZ { - return layout[0:i], layout[i : i+6], layout[i+6:] + if len(layout) >= i+6 && layout[i:i+6] == "-07:00" { + return layout[0:i], stdNumColonTZ, layout[i+6:] } - if len(layout) >= i+3 && layout[i:i+3] == stdNumShortTZ { - return layout[0:i], layout[i : i+3], layout[i+3:] + if len(layout) >= i+3 && layout[i:i+3] == "-07" { + return layout[0:i], stdNumShortTZ, layout[i+3:] } case 'Z': // Z0700, Z07:00 - if len(layout) >= i+5 && layout[i:i+5] == stdISO8601TZ { - return layout[0:i], layout[i : i+5], layout[i+5:] + if len(layout) >= i+5 && layout[i:i+5] == "Z0700" { + return layout[0:i], stdISO8601TZ, layout[i+5:] } - if len(layout) >= i+6 && layout[i:i+6] == stdISO8601ColonTZ { - return layout[0:i], layout[i : i+6], layout[i+6:] + if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" { + return layout[0:i], stdISO8601ColonTZ, layout[i+6:] } case '.': // .000 or .999 - repeated digits for fractional seconds. if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') { @@ -172,12 +189,17 @@ func nextStdChunk(layout string) (prefix, std, suffix string) { } // String of digits must end here - only fractional second is all digits. if !isDigit(layout, j) { - return layout[0:i], layout[i:j], layout[j:] + std := stdFracSecond0 + if layout[i+1] == '9' { + std = stdFracSecond9 + } + std |= (j - (i + 1)) << stdArgShift + return layout[0:i], std, layout[j:] } } } } - return layout, "", "" + return layout, 0, "" } var longDayNames = []string{ @@ -259,27 +281,36 @@ func lookup(tab []string, val string) (int, string, error) { return -1, val, errBad } +// appendUint appends the decimal form of x to b and returns the result. +// If x is a single-digit number and pad != 0, appendUint inserts the pad byte +// before the digit. // Duplicates functionality in strconv, but avoids dependency. -func itoa(x int) string { +func appendUint(b []byte, x uint, pad byte) []byte { + if x < 10 { + if pad != 0 { + b = append(b, pad) + } + return append(b, byte('0'+x)) + } + if x < 100 { + b = append(b, byte('0'+x/10)) + b = append(b, byte('0'+x%10)) + return b + } + var buf [32]byte n := len(buf) if x == 0 { - return "0" - } - u := uint(x) - if x < 0 { - u = -u - } - for u > 0 { - n-- - buf[n] = byte(u%10 + '0') - u /= 10 + return append(b, '0') } - if x < 0 { + for x >= 10 { n-- - buf[n] = '-' + buf[n] = byte(x%10 + '0') + x /= 10 } - return string(buf[n:]) + n-- + buf[n] = byte(x + '0') + return append(b, buf[n:]...) } // Never printed, just needs to be non-nil for return by atoi. @@ -292,7 +323,8 @@ func atoi(s string) (x int, err error) { neg = true s = s[1:] } - x, rem, err := leadingInt(s) + q, rem, err := leadingInt(s) + x = int(q) if err != nil || rem != "" { return 0, atoiError } @@ -302,37 +334,30 @@ func atoi(s string) (x int, err error) { return x, nil } -func pad(i int, padding string) string { - s := itoa(i) - if i < 10 { - s = padding + s +// formatNano appends a fractional second, as nanoseconds, to b +// and returns the result. +func formatNano(b []byte, nanosec uint, n int, trim bool) []byte { + u := nanosec + var buf [9]byte + for start := len(buf); start > 0; { + start-- + buf[start] = byte(u%10 + '0') + u /= 10 } - return s -} - -func zeroPad(i int) string { return pad(i, "0") } -// formatNano formats a fractional second, as nanoseconds. -func formatNano(nanosec, n int, trim bool) string { - // User might give us bad data. Make sure it's positive and in range. - // They'll get nonsense output but it will have the right format. - s := itoa(int(uint(nanosec) % 1e9)) - // Zero pad left without fmt. - if len(s) < 9 { - s = "000000000"[:9-len(s)] + s - } if n > 9 { n = 9 } if trim { - for n > 0 && s[n-1] == '0' { + for n > 0 && buf[n-1] == '0' { n-- } if n == 0 { - return "" + return b } } - return "." + s[:n] + b = append(b, '.') + return append(b, buf[:n]...) } // String returns the time formatted using the format string @@ -341,16 +366,6 @@ func (t Time) String() string { return t.Format("2006-01-02 15:04:05.999999999 -0700 MST") } -type buffer []byte - -func (b *buffer) WriteString(s string) { - *b = append(*b, s...) -} - -func (b *buffer) String() string { - return string([]byte(*b)) -} - // Format returns a textual representation of the time value formatted // according to layout. The layout defines the format by showing the // representation of the standard time, @@ -361,161 +376,172 @@ func (b *buffer) String() string { // definition of the standard time, see the documentation for ANSIC. func (t Time) Format(layout string) string { var ( + name, offset, abs = t.locabs() + year int = -1 month Month day int hour int = -1 min int sec int - b buffer + + b []byte + buf [64]byte ) + max := len(layout) + 10 + if max <= len(buf) { + b = buf[:0] + } else { + b = make([]byte, 0, max) + } // Each iteration generates one std value. - for { + for layout != "" { prefix, std, suffix := nextStdChunk(layout) - b.WriteString(prefix) - if std == "" { + if prefix != "" { + b = append(b, prefix...) + } + if std == 0 { break } + layout = suffix // Compute year, month, day if needed. - if year < 0 { - // Jan 01 02 2006 - if a, z := std[0], std[len(std)-1]; a == 'J' || a == 'j' || z == '1' || z == '2' || z == '6' { - year, month, day = t.Date() - } + if year < 0 && std&stdNeedDate != 0 { + year, month, day, _ = absDate(abs, true) } // Compute hour, minute, second if needed. - if hour < 0 { - // 03 04 05 15 pm - if z := std[len(std)-1]; z == '3' || z == '4' || z == '5' || z == 'm' || z == 'M' { - hour, min, sec = t.Clock() - } + if hour < 0 && std&stdNeedClock != 0 { + hour, min, sec = absClock(abs) } - var p string - switch std { + switch std & stdMask { case stdYear: - p = zeroPad(year % 100) + y := year + if y < 0 { + y = -y + } + b = appendUint(b, uint(y%100), '0') case stdLongYear: // Pad year to at least 4 digits. - p = itoa(year) + y := year switch { case year <= -1000: - // ok + b = append(b, '-') + y = -y case year <= -100: - p = p[:1] + "0" + p[1:] + b = append(b, "-0"...) + y = -y case year <= -10: - p = p[:1] + "00" + p[1:] + b = append(b, "-00"...) + y = -y case year < 0: - p = p[:1] + "000" + p[1:] + b = append(b, "-000"...) + y = -y case year < 10: - p = "000" + p + b = append(b, "000"...) case year < 100: - p = "00" + p + b = append(b, "00"...) case year < 1000: - p = "0" + p + b = append(b, '0') } + b = appendUint(b, uint(y), 0) case stdMonth: - p = month.String()[:3] + b = append(b, month.String()[:3]...) case stdLongMonth: - p = month.String() + m := month.String() + b = append(b, m...) case stdNumMonth: - p = itoa(int(month)) + b = appendUint(b, uint(month), 0) case stdZeroMonth: - p = zeroPad(int(month)) + b = appendUint(b, uint(month), '0') case stdWeekDay: - p = t.Weekday().String()[:3] + b = append(b, absWeekday(abs).String()[:3]...) case stdLongWeekDay: - p = t.Weekday().String() + s := absWeekday(abs).String() + b = append(b, s...) case stdDay: - p = itoa(day) + b = appendUint(b, uint(day), 0) case stdUnderDay: - p = pad(day, " ") + b = appendUint(b, uint(day), ' ') case stdZeroDay: - p = zeroPad(day) + b = appendUint(b, uint(day), '0') case stdHour: - p = zeroPad(hour) + b = appendUint(b, uint(hour), '0') case stdHour12: // Noon is 12PM, midnight is 12AM. hr := hour % 12 if hr == 0 { hr = 12 } - p = itoa(hr) + b = appendUint(b, uint(hr), 0) case stdZeroHour12: // Noon is 12PM, midnight is 12AM. hr := hour % 12 if hr == 0 { hr = 12 } - p = zeroPad(hr) + b = appendUint(b, uint(hr), '0') case stdMinute: - p = itoa(min) + b = appendUint(b, uint(min), 0) case stdZeroMinute: - p = zeroPad(min) + b = appendUint(b, uint(min), '0') case stdSecond: - p = itoa(sec) + b = appendUint(b, uint(sec), 0) case stdZeroSecond: - p = zeroPad(sec) + b = appendUint(b, uint(sec), '0') case stdPM: if hour >= 12 { - p = "PM" + b = append(b, "PM"...) } else { - p = "AM" + b = append(b, "AM"...) } case stdpm: if hour >= 12 { - p = "pm" + b = append(b, "pm"...) } else { - p = "am" + b = append(b, "am"...) } case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ: // Ugly special case. We cheat and take the "Z" variants // to mean "the time zone as formatted for ISO 8601". - _, offset := t.Zone() - if offset == 0 && std[0] == 'Z' { - p = "Z" + if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ) { + b = append(b, 'Z') break } zone := offset / 60 // convert to minutes if zone < 0 { - p = "-" + b = append(b, '-') zone = -zone } else { - p = "+" + b = append(b, '+') } - p += zeroPad(zone / 60) + b = appendUint(b, uint(zone/60), '0') if std == stdISO8601ColonTZ || std == stdNumColonTZ { - p += ":" + b = append(b, ':') } - p += zeroPad(zone % 60) + b = appendUint(b, uint(zone%60), '0') case stdTZ: - name, offset := t.Zone() if name != "" { - p = name - } else { - // No time zone known for this time, but we must print one. - // Use the -0700 format. - zone := offset / 60 // convert to minutes - if zone < 0 { - p = "-" - zone = -zone - } else { - p = "+" - } - p += zeroPad(zone / 60) - p += zeroPad(zone % 60) + b = append(b, name...) + break } - default: - if len(std) >= 2 && (std[0:2] == ".0" || std[0:2] == ".9") { - p = formatNano(t.Nanosecond(), len(std)-1, std[1] == '9') + // No time zone known for this time, but we must print one. + // Use the -0700 format. + zone := offset / 60 // convert to minutes + if zone < 0 { + b = append(b, '-') + zone = -zone + } else { + b = append(b, '+') } + b = appendUint(b, uint(zone/60), '0') + b = appendUint(b, uint(zone%60), '0') + case stdFracSecond0, stdFracSecond9: + b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9) } - b.WriteString(p) - layout = suffix } - return b.String() + return string(b) } var errBad = errors.New("bad value for field") // placeholder not passed to user @@ -585,14 +611,14 @@ func skip(value, prefix string) (string, error) { for len(prefix) > 0 { if prefix[0] == ' ' { if len(value) > 0 && value[0] != ' ' { - return "", errBad + return value, errBad } prefix = cutspace(prefix) value = cutspace(value) continue } if len(value) == 0 || value[0] != prefix[0] { - return "", errBad + return value, errBad } prefix = prefix[1:] value = value[1:] @@ -611,10 +637,41 @@ func skip(value, prefix string) (string, error) { // // Elements omitted from the value are assumed to be zero or, when // zero is impossible, one, so parsing "3:04pm" returns the time -// corresponding to Jan 1, year 0, 15:04:00 UTC. +// corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is +// 0, this time is before the zero Time). // Years must be in the range 0000..9999. The day of the week is checked // for syntax but it is otherwise ignored. +// +// In the absence of a time zone indicator, Parse returns a time in UTC. +// +// When parsing a time with a zone offset like -0700, if the offset corresponds +// to a time zone used by the current location (Local), then Parse uses that +// location and zone in the returned time. Otherwise it records the time as +// being in a fabricated location with time fixed at the given zone offset. +// +// When parsing a time with a zone abbreviation like MST, if the zone abbreviation +// has a defined offset in the current location, then that offset is used. +// The zone abbreviation "UTC" is recognized as UTC regardless of location. +// If the zone abbreviation is unknown, Parse records the time as being +// in a fabricated location with the given zone abbreviation and a zero offset. +// This choice means that such a time can be parse and reformatted with the +// same layout losslessly, but the exact instant used in the representation will +// differ by the actual zone offset. To avoid such problems, prefer time layouts +// that use a numeric zone offset, or use ParseInLocation. func Parse(layout, value string) (Time, error) { + return parse(layout, value, UTC, Local) +} + +// ParseInLocation is like Parse but differs in two important ways. +// First, in the absence of time zone information, Parse interprets a time as UTC; +// ParseInLocation interprets the time as in the given location. +// Second, when given a zone offset or abbreviation, Parse tries to match it +// against the Local location; ParseInLocation uses the given location. +func ParseInLocation(layout, value string, loc *Location) (Time, error) { + return parse(layout, value, loc, loc) +} + +func parse(layout, value string, defaultLocation, local *Location) (Time, error) { alayout, avalue := layout, value rangeErrString := "" // set if a value is out of range amSet := false // do we need to subtract 12 from the hour for midnight? @@ -638,11 +695,12 @@ func Parse(layout, value string) (Time, error) { for { var err error prefix, std, suffix := nextStdChunk(layout) + stdstr := layout[len(prefix) : len(layout)-len(suffix)] value, err = skip(value, prefix) if err != nil { return Time{}, &ParseError{alayout, avalue, prefix, value, ""} } - if len(std) == 0 { + if std == 0 { if len(value) != 0 { return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + value} } @@ -650,7 +708,7 @@ func Parse(layout, value string) (Time, error) { } layout = suffix var p string - switch std { + switch std & stdMask { case stdYear: if len(value) < 2 { err = errBad @@ -716,7 +774,8 @@ func Parse(layout, value string) (Time, error) { // fractional second in the format? if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) { _, std, _ := nextStdChunk(layout) - if len(std) > 0 && std[0] == '.' && isDigit(std, 1) { + std &= stdMask + if std == stdFracSecond0 || std == stdFracSecond9 { // Fractional second in the layout; proceed normally break } @@ -756,7 +815,7 @@ func Parse(layout, value string) (Time, error) { err = errBad } case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ: - if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' { + if (std == stdISO8601TZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' { value = value[1:] z = UTC break @@ -824,21 +883,37 @@ func Parse(layout, value string) (Time, error) { } // It's a valid format. zoneName = p - default: - if len(value) < len(std) { + + case stdFracSecond0: + // stdFracSecond0 requires the exact number of digits as specified in + // the layout. + ndigit := 1 + (std >> stdArgShift) + if len(value) < ndigit { err = errBad break } - if len(std) >= 2 && std[0:2] == ".0" { - nsec, rangeErrString, err = parseNanoseconds(value, len(std)) - value = value[len(std):] + nsec, rangeErrString, err = parseNanoseconds(value, ndigit) + value = value[ndigit:] + + case stdFracSecond9: + if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] { + // Fractional second omitted. + break + } + // Take any number of digits, even more than asked for, + // because it is what the stdSecond case would do. + i := 0 + for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' { + i++ } + nsec, rangeErrString, err = parseNanoseconds(value, 1+i) + value = value[1+i:] } if rangeErrString != "" { - return Time{}, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"} + return Time{}, &ParseError{alayout, avalue, stdstr, value, ": " + rangeErrString + " out of range"} } if err != nil { - return Time{}, &ParseError{alayout, avalue, std, value, ""} + return Time{}, &ParseError{alayout, avalue, stdstr, value, ""} } } if pmSet && hour < 12 { @@ -847,20 +922,19 @@ func Parse(layout, value string) (Time, error) { hour = 0 } - // TODO: be more aggressive checking day? if z != nil { return Date(year, Month(month), day, hour, min, sec, nsec, z), nil } - t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) if zoneOffset != -1 { + t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) t.sec -= int64(zoneOffset) // Look for local zone with the given offset. // If that zone was in effect at the given time, use it. - name, offset, _, _, _ := Local.lookup(t.sec + internalToUnix) + name, offset, _, _, _ := local.lookup(t.sec + internalToUnix) if offset == zoneOffset && (zoneName == "" || name == zoneName) { - t.loc = Local + t.loc = local return t, nil } @@ -870,16 +944,14 @@ func Parse(layout, value string) (Time, error) { } if zoneName != "" { + t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) // Look for local zone with the given offset. // If that zone was in effect at the given time, use it. - offset, _, ok := Local.lookupName(zoneName) + offset, _, ok := local.lookupName(zoneName, t.sec+internalToUnix) if ok { - name, off, _, _, _ := Local.lookup(t.sec + internalToUnix - int64(offset)) - if name == zoneName && off == offset { - t.sec -= int64(offset) - t.loc = Local - return t, nil - } + t.sec -= int64(offset) + t.loc = local + return t, nil } // Otherwise, create fake zone with unknown offset. @@ -887,8 +959,8 @@ func Parse(layout, value string) (Time, error) { return t, nil } - // Otherwise, fall back to UTC. - return t, nil + // Otherwise, fall back to default. + return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil } func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) { @@ -896,8 +968,7 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err = errBad return } - ns, err = atoi(value[1:nbytes]) - if err != nil { + if ns, err = atoi(value[1:nbytes]); err != nil { return } if ns < 0 || 1e9 <= ns { @@ -917,18 +988,18 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, var errLeadingInt = errors.New("time: bad [0-9]*") // never printed // leadingInt consumes the leading [0-9]* from s. -func leadingInt(s string) (x int, rem string, err error) { +func leadingInt(s string) (x int64, rem string, err error) { i := 0 for ; i < len(s); i++ { c := s[i] if c < '0' || c > '9' { break } - if x >= (1<<31-10)/10 { + if x >= (1<<63-10)/10 { // overflow return 0, "", errLeadingInt } - x = x*10 + int(c) - '0' + x = x*10 + int64(c) - '0' } return x, s[i:], nil } @@ -973,7 +1044,7 @@ func ParseDuration(s string) (Duration, error) { for s != "" { g := float64(0) // this element of the sequence - var x int + var x int64 var err error // The next character must be [0-9.] diff --git a/src/pkg/time/internal_test.go b/src/pkg/time/internal_test.go index b753896d7..918a9f33b 100644 --- a/src/pkg/time/internal_test.go +++ b/src/pkg/time/internal_test.go @@ -6,7 +6,7 @@ package time func init() { // force US/Pacific for time zone tests - localOnce.Do(initTestingZone) + ForceUSPacificForTesting() } var Interrupt = interrupt diff --git a/src/pkg/time/sleep.go b/src/pkg/time/sleep.go index 27820b0ea..591fa27b0 100644 --- a/src/pkg/time/sleep.go +++ b/src/pkg/time/sleep.go @@ -18,10 +18,25 @@ type runtimeTimer struct { i int32 when int64 period int64 - f func(int64, interface{}) + f func(int64, interface{}) // NOTE: must not be closure arg interface{} } +// when is a helper function for setting the 'when' field of a runtimeTimer. +// It returns what the time will be, in nanoseconds, Duration d in the future. +// If d is negative, it is ignored. If the returned value would be less than +// zero because of an overflow, MaxInt64 is returned. +func when(d Duration) int64 { + if d <= 0 { + return nano() + } + t := nano() + int64(d) + if t < 0 { + t = 1<<63 - 1 // math.MaxInt64 + } + return t +} + func startTimer(*runtimeTimer) func stopTimer(*runtimeTimer) bool @@ -35,8 +50,10 @@ type Timer struct { // Stop prevents the Timer from firing. // It returns true if the call stops the timer, false if the timer has already -// expired or stopped. -func (t *Timer) Stop() (ok bool) { +// expired or been stopped. +// Stop does not close the channel, to prevent a read from the channel succeeding +// incorrectly. +func (t *Timer) Stop() bool { return stopTimer(&t.r) } @@ -47,7 +64,7 @@ func NewTimer(d Duration) *Timer { t := &Timer{ C: c, r: runtimeTimer{ - when: nano() + int64(d), + when: when(d), f: sendTime, arg: c, }, @@ -56,6 +73,17 @@ func NewTimer(d Duration) *Timer { return t } +// Reset changes the timer to expire after duration d. +// It returns true if the timer had been active, false if the timer had +// expired or been stopped. +func (t *Timer) Reset(d Duration) bool { + w := when(d) + active := stopTimer(&t.r) + t.r.when = w + startTimer(&t.r) + return active +} + func sendTime(now int64, c interface{}) { // Non-blocking send of time on c. // Used in NewTimer, it cannot block anyway (buffer). @@ -81,7 +109,7 @@ func After(d Duration) <-chan Time { func AfterFunc(d Duration, f func()) *Timer { t := &Timer{ r: runtimeTimer{ - when: nano() + int64(d), + when: when(d), f: goFunc, arg: f, }, diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go index e05773df6..9908e220f 100644 --- a/src/pkg/time/sleep_test.go +++ b/src/pkg/time/sleep_test.go @@ -54,9 +54,10 @@ func TestAfterStress(t *testing.T) { go func() { for atomic.LoadUint32(&stop) == 0 { runtime.GC() - // Need to yield, because otherwise - // the main goroutine will never set the stop flag. - runtime.Gosched() + // Yield so that the OS can wake up the timer thread, + // so that it can generate channel sends for the main goroutine, + // which will eventually set stop = 1 for us. + Sleep(Nanosecond) } }() c := Tick(1) @@ -245,3 +246,70 @@ func TestSleepZeroDeadlock(t *testing.T) { } <-c } + +func testReset(d Duration) error { + t0 := NewTimer(2 * d) + Sleep(d) + if t0.Reset(3*d) != true { + return errors.New("resetting unfired timer returned false") + } + Sleep(2 * d) + select { + case <-t0.C: + return errors.New("timer fired early") + default: + } + Sleep(2 * d) + select { + case <-t0.C: + default: + return errors.New("reset timer did not fire") + } + + if t0.Reset(50*Millisecond) != false { + return errors.New("resetting expired timer returned true") + } + return nil +} + +func TestReset(t *testing.T) { + // We try to run this test with increasingly larger multiples + // until one works so slow, loaded hardware isn't as flaky, + // but without slowing down fast machines unnecessarily. + const unit = 25 * Millisecond + tries := []Duration{ + 1 * unit, + 3 * unit, + 7 * unit, + 15 * unit, + } + var err error + for _, d := range tries { + err = testReset(d) + if err == nil { + t.Logf("passed using duration %v", d) + return + } + } + t.Error(err) +} + +// Test that sleeping for an interval so large it overflows does not +// result in a short sleep duration. +func TestOverflowSleep(t *testing.T) { + const timeout = 25 * Millisecond + const big = Duration(int64(1<<63 - 1)) + select { + case <-After(big): + t.Fatalf("big timeout fired") + case <-After(timeout): + // OK + } + const neg = Duration(-1 << 63) + select { + case <-After(neg): + // OK + case <-After(timeout): + t.Fatalf("negative timeout didn't fire") + } +} diff --git a/src/pkg/time/tick.go b/src/pkg/time/tick.go index 8c6b9bc3b..b92c339c0 100644 --- a/src/pkg/time/tick.go +++ b/src/pkg/time/tick.go @@ -6,7 +6,7 @@ package time import "errors" -// A Ticker holds a synchronous channel that delivers `ticks' of a clock +// A Ticker holds a channel that delivers `ticks' of a clock // at intervals. type Ticker struct { C <-chan Time // The channel on which the ticks are delivered. @@ -39,6 +39,8 @@ func NewTicker(d Duration) *Ticker { } // Stop turns off a ticker. After Stop, no more ticks will be sent. +// Stop does not close the channel, to prevent a read from the channel succeeding +// incorrectly. func (t *Ticker) Stop() { stopTimer(&t.r) } diff --git a/src/pkg/time/time.go b/src/pkg/time/time.go index 2461dac06..d291672af 100644 --- a/src/pkg/time/time.go +++ b/src/pkg/time/time.go @@ -241,10 +241,10 @@ func (t Time) IsZero() bool { // It is called when computing a presentation property like Month or Hour. func (t Time) abs() uint64 { l := t.loc - if l == nil { - l = &utcLoc + // Avoid function calls when possible. + if l == nil || l == &localLoc { + l = l.get() } - // Avoid function call if we hit the local time cache. sec := t.sec + internalToUnix if l != &utcLoc { if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd { @@ -257,6 +257,30 @@ func (t Time) abs() uint64 { return uint64(sec + (unixToInternal + internalToAbsolute)) } +// locabs is a combination of the Zone and abs methods, +// extracting both return values from a single zone lookup. +func (t Time) locabs() (name string, offset int, abs uint64) { + l := t.loc + if l == nil || l == &localLoc { + l = l.get() + } + // Avoid function call if we hit the local time cache. + sec := t.sec + internalToUnix + if l != &utcLoc { + if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd { + name = l.cacheZone.name + offset = l.cacheZone.offset + } else { + name, offset, _, _, _ = l.lookup(sec) + } + sec += int64(offset) + } else { + name = "UTC" + } + abs = uint64(sec + (unixToInternal + internalToAbsolute)) + return +} + // Date returns the year, month, and day in which t occurs. func (t Time) Date() (year int, month Month, day int) { year, month, day, _ = t.date(true) @@ -283,8 +307,13 @@ func (t Time) Day() int { // Weekday returns the day of the week specified by t. func (t Time) Weekday() Weekday { + return absWeekday(t.abs()) +} + +// absWeekday is like Weekday but operates on an absolute time. +func absWeekday(abs uint64) Weekday { // January 1 of the absolute year, like January 1 of 2001, was a Monday. - sec := (t.abs() + uint64(Monday)*secondsPerDay) % secondsPerWeek + sec := (abs + uint64(Monday)*secondsPerDay) % secondsPerWeek return Weekday(int(sec) / secondsPerDay) } @@ -349,7 +378,12 @@ func (t Time) ISOWeek() (year, week int) { // Clock returns the hour, minute, and second within the day specified by t. func (t Time) Clock() (hour, min, sec int) { - sec = int(t.abs() % secondsPerDay) + return absClock(t.abs()) +} + +// absClock is like clock but operates on an absolute time. +func absClock(abs uint64) (hour, min, sec int) { + sec = int(abs % secondsPerDay) hour = sec / secondsPerHour sec -= hour * secondsPerHour min = sec / secondsPerMinute @@ -378,6 +412,13 @@ func (t Time) Nanosecond() int { return int(t.nsec) } +// YearDay returns the day of the year specified by t, in the range [1,365] for non-leap years, +// and [1,366] in leap years. +func (t Time) YearDay() int { + _, _, _, yday := t.date(false) + return yday + 1 +} + // A Duration represents the elapsed time between two instants // as an int64 nanosecond count. The representation limits the // largest representable duration to approximately 290 years. @@ -607,11 +648,16 @@ const ( days1970To2001 = 31*365 + 8 ) -// date computes the year and, only when full=true, +// date computes the year, day of year, and when full=true, // the month and day in which t occurs. func (t Time) date(full bool) (year int, month Month, day int, yday int) { + return absDate(t.abs(), full) +} + +// absDate is like date but operates on an absolute time. +func absDate(abs uint64, full bool) (year int, month Month, day int, yday int) { // Split into time and day. - d := t.abs() / secondsPerDay + d := abs / secondsPerDay // Account for 400 year cycles. n := d / daysPer400Years @@ -987,3 +1033,116 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T return Time{unix + unixToInternal, int32(nsec), loc} } + +// Truncate returns the result of rounding t down to a multiple of d (since the zero time). +// If d <= 0, Truncate returns t unchanged. +func (t Time) Truncate(d Duration) Time { + if d <= 0 { + return t + } + _, r := div(t, d) + return t.Add(-r) +} + +// Round returns the result of rounding t to the nearest multiple of d (since the zero time). +// The rounding behavior for halfway values is to round up. +// If d <= 0, Round returns t unchanged. +func (t Time) Round(d Duration) Time { + if d <= 0 { + return t + } + _, r := div(t, d) + if r+r < d { + return t.Add(-r) + } + return t.Add(d - r) +} + +// div divides t by d and returns the quotient parity and remainder. +// We don't use the quotient parity anymore (round half up instead of round to even) +// but it's still here in case we change our minds. +func div(t Time, d Duration) (qmod2 int, r Duration) { + neg := false + if t.sec < 0 { + // Operate on absolute value. + neg = true + t.sec = -t.sec + t.nsec = -t.nsec + if t.nsec < 0 { + t.nsec += 1e9 + t.sec-- // t.sec >= 1 before the -- so safe + } + } + + switch { + // Special case: 2d divides 1 second. + case d < Second && Second%(d+d) == 0: + qmod2 = int(t.nsec/int32(d)) & 1 + r = Duration(t.nsec % int32(d)) + + // Special case: d is a multiple of 1 second. + case d%Second == 0: + d1 := int64(d / Second) + qmod2 = int(t.sec/d1) & 1 + r = Duration(t.sec%d1)*Second + Duration(t.nsec) + + // General case. + // This could be faster if more cleverness were applied, + // but it's really only here to avoid special case restrictions in the API. + // No one will care about these cases. + default: + // Compute nanoseconds as 128-bit number. + sec := uint64(t.sec) + tmp := (sec >> 32) * 1e9 + u1 := tmp >> 32 + u0 := tmp << 32 + tmp = uint64(sec&0xFFFFFFFF) * 1e9 + u0x, u0 := u0, u0+tmp + if u0 < u0x { + u1++ + } + u0x, u0 = u0, u0+uint64(t.nsec) + if u0 < u0x { + u1++ + } + + // Compute remainder by subtracting r<>63 != 1 { + d1 <<= 1 + } + d0 := uint64(0) + for { + qmod2 = 0 + if u1 > d1 || u1 == d1 && u0 >= d0 { + // subtract + qmod2 = 1 + u0x, u0 = u0, u0-d0 + if u0 > u0x { + u1-- + } + u1 -= d1 + } + if d1 == 0 && d0 == uint64(d) { + break + } + d0 >>= 1 + d0 |= (d1 & 1) << 63 + d1 >>= 1 + } + r = Duration(u0) + } + + if neg && r != 0 { + // If input was negative and not an exact multiple of d, we computed q, r such that + // q*d + r = -t + // But the right answers are given by -(q-1), d-r: + // q*d + r = -t + // -q*d - r = t + // -(q-1)*d + (d - r) = t + qmod2 ^= 1 + r = d - r + } + return +} diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go index 28047804e..4b268f73d 100644 --- a/src/pkg/time/time_test.go +++ b/src/pkg/time/time_test.go @@ -9,6 +9,7 @@ import ( "encoding/gob" "encoding/json" "fmt" + "math/big" "math/rand" "strconv" "strings" @@ -192,6 +193,184 @@ func TestNanosecondsToUTCAndBack(t *testing.T) { } } +// The time routines provide no way to get absolute time +// (seconds since zero), but we need it to compute the right +// answer for bizarre roundings like "to the nearest 3 ns". +// Compute as t - year1 = (t - 1970) + (1970 - 2001) + (2001 - 1). +// t - 1970 is returned by Unix and Nanosecond. +// 1970 - 2001 is -(31*365+8)*86400 = -978307200 seconds. +// 2001 - 1 is 2000*365.2425*86400 = 63113904000 seconds. +const unixToZero = -978307200 + 63113904000 + +// abs returns the absolute time stored in t, as seconds and nanoseconds. +func abs(t Time) (sec, nsec int64) { + unix := t.Unix() + nano := t.Nanosecond() + return unix + unixToZero, int64(nano) +} + +// absString returns abs as a decimal string. +func absString(t Time) string { + sec, nsec := abs(t) + if sec < 0 { + sec = -sec + nsec = -nsec + if nsec < 0 { + nsec += 1e9 + sec-- + } + return fmt.Sprintf("-%d%09d", sec, nsec) + } + return fmt.Sprintf("%d%09d", sec, nsec) +} + +var truncateRoundTests = []struct { + t Time + d Duration +}{ + {Date(-1, January, 1, 12, 15, 30, 5e8, UTC), 3}, + {Date(-1, January, 1, 12, 15, 31, 5e8, UTC), 3}, + {Date(2012, January, 1, 12, 15, 30, 5e8, UTC), Second}, + {Date(2012, January, 1, 12, 15, 31, 5e8, UTC), Second}, +} + +func TestTruncateRound(t *testing.T) { + var ( + bsec = new(big.Int) + bnsec = new(big.Int) + bd = new(big.Int) + bt = new(big.Int) + br = new(big.Int) + bq = new(big.Int) + b1e9 = new(big.Int) + ) + + b1e9.SetInt64(1e9) + + testOne := func(ti, tns, di int64) bool { + t0 := Unix(ti, int64(tns)).UTC() + d := Duration(di) + if d < 0 { + d = -d + } + if d <= 0 { + d = 1 + } + + // Compute bt = absolute nanoseconds. + sec, nsec := abs(t0) + bsec.SetInt64(sec) + bnsec.SetInt64(nsec) + bt.Mul(bsec, b1e9) + bt.Add(bt, bnsec) + + // Compute quotient and remainder mod d. + bd.SetInt64(int64(d)) + bq.DivMod(bt, bd, br) + + // To truncate, subtract remainder. + // br is < d, so it fits in an int64. + r := br.Int64() + t1 := t0.Add(-Duration(r)) + + // Check that time.Truncate works. + if trunc := t0.Truncate(d); trunc != t1 { + t.Errorf("Time.Truncate(%s, %s) = %s, want %s\n"+ + "%v trunc %v =\n%v want\n%v", + t0.Format(RFC3339Nano), d, trunc, t1.Format(RFC3339Nano), + absString(t0), int64(d), absString(trunc), absString(t1)) + return false + } + + // To round, add d back if remainder r > d/2 or r == exactly d/2. + // The commented out code would round half to even instead of up, + // but that makes it time-zone dependent, which is a bit strange. + if r > int64(d)/2 || r+r == int64(d) /*&& bq.Bit(0) == 1*/ { + t1 = t1.Add(Duration(d)) + } + + // Check that time.Round works. + if rnd := t0.Round(d); rnd != t1 { + t.Errorf("Time.Round(%s, %s) = %s, want %s\n"+ + "%v round %v =\n%v want\n%v", + t0.Format(RFC3339Nano), d, rnd, t1.Format(RFC3339Nano), + absString(t0), int64(d), absString(rnd), absString(t1)) + return false + } + return true + } + + // manual test cases + for _, tt := range truncateRoundTests { + testOne(tt.t.Unix(), int64(tt.t.Nanosecond()), int64(tt.d)) + } + + // exhaustive near 0 + for i := 0; i < 100; i++ { + for j := 1; j < 100; j++ { + testOne(unixToZero, int64(i), int64(j)) + testOne(unixToZero, -int64(i), int64(j)) + if t.Failed() { + return + } + } + } + + if t.Failed() { + return + } + + // randomly generated test cases + cfg := &quick.Config{MaxCount: 100000} + if testing.Short() { + cfg.MaxCount = 1000 + } + + // divisors of Second + f1 := func(ti int64, tns int32, logdi int32) bool { + d := Duration(1) + a, b := uint(logdi%9), (logdi>>16)%9 + d <<= a + for i := 0; i < int(b); i++ { + d *= 5 + } + return testOne(ti, int64(tns), int64(d)) + } + quick.Check(f1, cfg) + + // multiples of Second + f2 := func(ti int64, tns int32, di int32) bool { + d := Duration(di) * Second + if d < 0 { + d = -d + } + return testOne(ti, int64(tns), int64(d)) + } + quick.Check(f2, cfg) + + // halfway cases + f3 := func(tns, di int64) bool { + di &= 0xfffffffe + if di == 0 { + di = 2 + } + tns -= tns % di + if tns < 0 { + tns += di / 2 + } else { + tns -= di / 2 + } + return testOne(0, tns, di) + } + quick.Check(f3, cfg) + + // full generality + f4 := func(ti int64, tns int32, di int64) bool { + return testOne(ti, int64(tns), di) + } + quick.Check(f4, cfg) +} + type TimeFormatTest struct { time Time formattedValue string @@ -289,7 +468,7 @@ type ParseTest struct { value string hasTZ bool // contains a time zone hasWD bool // contains a weekday - yearSign int // sign of year + yearSign int // sign of year, -1 indicates the year is not present in the format fracDigits int // number of digits of fractional second } @@ -299,6 +478,7 @@ var parseTests = []ParseTest{ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0}, {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true, 1, 0}, {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1, 0}, + {"RFC1123", RFC1123, "Thu, 04 Feb 2010 22:00:57 PDT", true, true, 1, 0}, {"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57 -0800", true, true, 1, 0}, {"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1, 0}, {"custom: \"2006-01-02 15:04:05-07\"", "2006-01-02 15:04:05-07", "2010-02-04 21:00:57-08", true, false, 1, 0}, @@ -324,6 +504,23 @@ var parseTests = []ParseTest{ // Leading zeros in other places should not be taken as fractional seconds. {"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1}, {"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2}, + + // Accept any number of fractional second digits (including none) for .999... + // In Go 1, .999... was completely ignored in the format, meaning the first two + // cases would succeed, but the next four would not. Go 1.1 accepts all six. + {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0}, + {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0}, + {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4}, + {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4}, + {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9}, + {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9}, + + // issue 4502. + {"", StampNano, "Feb 4 21:00:57.012345678", false, false, -1, 9}, + {"", "Jan _2 15:04:05.999", "Feb 4 21:00:57.012300000", false, false, -1, 4}, + {"", "Jan _2 15:04:05.999", "Feb 4 21:00:57.012345678", false, false, -1, 9}, + {"", "Jan _2 15:04:05.999999999", "Feb 4 21:00:57.0123", false, false, -1, 4}, + {"", "Jan _2 15:04:05.999999999", "Feb 4 21:00:57.012345678", false, false, -1, 9}, } func TestParse(t *testing.T) { @@ -337,6 +534,42 @@ func TestParse(t *testing.T) { } } +func TestParseInSydney(t *testing.T) { + loc, err := LoadLocation("Australia/Sydney") + if err != nil { + t.Fatal(err) + } + + // Check that Parse (and ParseInLocation) understand + // that Feb EST and Aug EST are different time zones in Sydney + // even though both are called EST. + t1, err := ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 EST", loc) + if err != nil { + t.Fatal(err) + } + t2 := Date(2013, February, 1, 00, 00, 00, 0, loc) + if t1 != t2 { + t.Fatalf("ParseInLocation(Feb 01 2013 EST, Sydney) = %v, want %v", t1, t2) + } + _, offset := t1.Zone() + if offset != 11*60*60 { + t.Fatalf("ParseInLocation(Feb 01 2013 EST, Sydney).Zone = _, %d, want _, %d", offset, 11*60*60) + } + + t1, err = ParseInLocation("Jan 02 2006 MST", "Aug 01 2013 EST", loc) + if err != nil { + t.Fatal(err) + } + t2 = Date(2013, August, 1, 00, 00, 00, 0, loc) + if t1 != t2 { + t.Fatalf("ParseInLocation(Aug 01 2013 EST, Sydney) = %v, want %v", t1, t2) + } + _, offset = t1.Zone() + if offset != 10*60*60 { + t.Fatalf("ParseInLocation(Aug 01 2013 EST, Sydney).Zone = _, %d, want _, %d", offset, 10*60*60) + } +} + var rubyTests = []ParseTest{ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0}, // Ignore the time zone in the test. If it parses, it'll be OK. @@ -359,7 +592,7 @@ func TestRubyParse(t *testing.T) { func checkTime(time Time, test *ParseTest, t *testing.T) { // The time should be Thu Feb 4 21:00:57 PST 2010 - if test.yearSign*time.Year() != 2010 { + if test.yearSign >= 0 && test.yearSign*time.Year() != 2010 { t.Errorf("%s: bad year: %d not %d", test.name, time.Year(), 2010) } if time.Month() != February { @@ -440,6 +673,14 @@ var parseErrorTests = []ParseErrorTest{ {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59x01 2010", "cannot parse"}, {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.xxx 2010", "cannot parse"}, {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.-123 2010", "fractional second out of range"}, + // issue 4502. StampNano requires exactly 9 digits of precision. + {StampNano, "Dec 7 11:22:01.000000", `cannot parse ".000000" as ".000000000"`}, + {StampNano, "Dec 7 11:22:01.0000000000", "extra text: 0"}, + // issue 4493. Helpful errors. + {RFC3339, "2006-01-02T15:04:05Z07:00", `parsing time "2006-01-02T15:04:05Z07:00": extra text: 07:00`}, + {RFC3339, "2006-01-02T15:04_abc", `parsing time "2006-01-02T15:04_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as ":"`}, + {RFC3339, "2006-01-02T15:04:05_abc", `parsing time "2006-01-02T15:04:05_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as "Z07:00"`}, + {RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: _abc`}, } func TestParseErrors(t *testing.T) { @@ -601,6 +842,103 @@ func TestISOWeek(t *testing.T) { } } +type YearDayTest struct { + year, month, day int + yday int +} + +// Test YearDay in several different scenarios +// and corner cases +var yearDayTests = []YearDayTest{ + // Non-leap-year tests + {2007, 1, 1, 1}, + {2007, 1, 15, 15}, + {2007, 2, 1, 32}, + {2007, 2, 15, 46}, + {2007, 3, 1, 60}, + {2007, 3, 15, 74}, + {2007, 4, 1, 91}, + {2007, 12, 31, 365}, + + // Leap-year tests + {2008, 1, 1, 1}, + {2008, 1, 15, 15}, + {2008, 2, 1, 32}, + {2008, 2, 15, 46}, + {2008, 3, 1, 61}, + {2008, 3, 15, 75}, + {2008, 4, 1, 92}, + {2008, 12, 31, 366}, + + // Looks like leap-year (but isn't) tests + {1900, 1, 1, 1}, + {1900, 1, 15, 15}, + {1900, 2, 1, 32}, + {1900, 2, 15, 46}, + {1900, 3, 1, 60}, + {1900, 3, 15, 74}, + {1900, 4, 1, 91}, + {1900, 12, 31, 365}, + + // Year one tests (non-leap) + {1, 1, 1, 1}, + {1, 1, 15, 15}, + {1, 2, 1, 32}, + {1, 2, 15, 46}, + {1, 3, 1, 60}, + {1, 3, 15, 74}, + {1, 4, 1, 91}, + {1, 12, 31, 365}, + + // Year minus one tests (non-leap) + {-1, 1, 1, 1}, + {-1, 1, 15, 15}, + {-1, 2, 1, 32}, + {-1, 2, 15, 46}, + {-1, 3, 1, 60}, + {-1, 3, 15, 74}, + {-1, 4, 1, 91}, + {-1, 12, 31, 365}, + + // 400 BC tests (leap-year) + {-400, 1, 1, 1}, + {-400, 1, 15, 15}, + {-400, 2, 1, 32}, + {-400, 2, 15, 46}, + {-400, 3, 1, 61}, + {-400, 3, 15, 75}, + {-400, 4, 1, 92}, + {-400, 12, 31, 366}, + + // Special Cases + + // Gregorian calendar change (no effect) + {1582, 10, 4, 277}, + {1582, 10, 15, 288}, +} + +// Check to see if YearDay is location sensitive +var yearDayLocations = []*Location{ + FixedZone("UTC-8", -8*60*60), + FixedZone("UTC-4", -4*60*60), + UTC, + FixedZone("UTC+4", 4*60*60), + FixedZone("UTC+8", 8*60*60), +} + +func TestYearDay(t *testing.T) { + for _, loc := range yearDayLocations { + for _, ydt := range yearDayTests { + dt := Date(ydt.year, Month(ydt.month), ydt.day, 0, 0, 0, 0, loc) + yday := dt.YearDay() + if yday != ydt.yday { + t.Errorf("got %d, expected %d for %d-%02d-%02d in %v", + yday, ydt.yday, ydt.year, ydt.month, ydt.day, loc) + } + } + } +} + var durationTests = []struct { str string d Duration @@ -741,7 +1079,7 @@ var gobTests = []Time{ Date(0, 1, 2, 3, 4, 5, 6, UTC), Date(7, 8, 9, 10, 11, 12, 13, FixedZone("", 0)), Unix(81985467080890095, 0x76543210), // Time.sec: 0x0123456789ABCDEF - Time{}, // nil location + {}, // nil location Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", 32767*60)), Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", -32768*60)), } @@ -805,7 +1143,7 @@ var jsonTests = []struct { time Time json string }{ - {Date(9999, 4, 12, 23, 20, 50, .52*1e9, UTC), `"9999-04-12T23:20:50.52Z"`}, + {Date(9999, 4, 12, 23, 20, 50, 520*1e6, UTC), `"9999-04-12T23:20:50.52Z"`}, {Date(1996, 12, 19, 16, 39, 57, 0, Local), `"1996-12-19T16:39:57-08:00"`}, {Date(0, 1, 1, 0, 0, 0, 1, FixedZone("", 1*60)), `"0000-01-01T00:00:00.000000001+00:01"`}, } @@ -892,6 +1230,8 @@ var parseDurationTests = []struct { {"-2m3.4s", true, -(2*Minute + 3*Second + 400*Millisecond)}, {"1h2m3s4ms5us6ns", true, 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond}, {"39h9m14.425s", true, 39*Hour + 9*Minute + 14*Second + 425*Millisecond}, + // large value + {"52763797000ns", true, 52763797000 * Nanosecond}, // errors {"", false, 0}, @@ -928,16 +1268,86 @@ func TestParseDurationRoundTrip(t *testing.T) { } } +// golang.org/issue/4622 +func TestLocationRace(t *testing.T) { + ResetLocalOnceForTest() // reset the Once to trigger the race + + c := make(chan string, 1) + go func() { + c <- Now().String() + }() + Now().String() + <-c + Sleep(100 * Millisecond) + + // Back to Los Angeles for subsequent tests: + ForceUSPacificForTesting() +} + +var ( + t Time + u int64 +) + +var mallocTest = []struct { + count int + desc string + fn func() +}{ + {0, `time.Now()`, func() { t = Now() }}, + {0, `time.Now().UnixNano()`, func() { u = Now().UnixNano() }}, +} + +func TestCountMallocs(t *testing.T) { + for _, mt := range mallocTest { + allocs := int(testing.AllocsPerRun(100, mt.fn)) + if allocs > mt.count { + t.Errorf("%s: %d allocs, want %d", mt.desc, allocs, mt.count) + } + } +} + +func TestLoadFixed(t *testing.T) { + // Issue 4064: handle locations without any zone transitions. + loc, err := LoadLocation("Etc/GMT+1") + if err != nil { + t.Fatal(err) + } + + // The tzdata name Etc/GMT+1 uses "east is negative", + // but Go and most other systems use "east is positive". + // So GMT+1 corresponds to -3600 in the Go zone, not +3600. + name, offset := Now().In(loc).Zone() + if name != "GMT+1" || offset != -1*60*60 { + t.Errorf("Now().In(loc).Zone() = %q, %d, want %q, %d", name, offset, "GMT+1", -1*60*60) + } +} + func BenchmarkNow(b *testing.B) { for i := 0; i < b.N; i++ { - Now() + t = Now() + } +} + +func BenchmarkNowUnixNano(b *testing.B) { + for i := 0; i < b.N; i++ { + u = Now().UnixNano() } } func BenchmarkFormat(b *testing.B) { - time := Unix(1265346057, 0) + t := Unix(1265346057, 0) + for i := 0; i < b.N; i++ { + t.Format("Mon Jan 2 15:04:05 2006") + } +} + +func BenchmarkFormatNow(b *testing.B) { + // Like BenchmarkFormat, but easier, because the time zone + // lookup cache is optimized for the present. + t := Now() for i := 0; i < b.N; i++ { - time.Format("Mon Jan 2 15:04:05 2006") + t.Format("Mon Jan 2 15:04:05 2006") } } diff --git a/src/pkg/time/zoneinfo.go b/src/pkg/time/zoneinfo.go index 3c5774404..c44477f47 100644 --- a/src/pkg/time/zoneinfo.go +++ b/src/pkg/time/zoneinfo.go @@ -123,35 +123,58 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start // Not using sort.Search to avoid dependencies. tx := l.tx end = 1<<63 - 1 - for len(tx) > 1 { - m := len(tx) / 2 + lo := 0 + hi := len(tx) + for hi-lo > 1 { + m := lo + (hi-lo)/2 lim := tx[m].when if sec < lim { end = lim - tx = tx[0:m] + hi = m } else { - tx = tx[m:] + lo = m } } - zone := &l.zone[tx[0].index] + zone := &l.zone[tx[lo].index] name = zone.name offset = zone.offset isDST = zone.isDST - start = tx[0].when + start = tx[lo].when // end = maintained during the search return } // lookupName returns information about the time zone with -// the given name (such as "EST"). -func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) { +// the given name (such as "EST") at the given pseudo-Unix time +// (what the given time of day would be in UTC). +func (l *Location) lookupName(name string, unix int64) (offset int, isDST bool, ok bool) { l = l.get() + + // First try for a zone with the right name that was actually + // in effect at the given time. (In Sydney, Australia, both standard + // and daylight-savings time are abbreviated "EST". Using the + // offset helps us pick the right one for the given time. + // It's not perfect: during the backward transition we might pick + // either one.) + for i := range l.zone { + zone := &l.zone[i] + if zone.name == name { + nam, offset, isDST, _, _ := l.lookup(unix - int64(zone.offset)) + if nam == zone.name { + return offset, isDST, true + } + } + } + + // Otherwise fall back to an ordinary name match. for i := range l.zone { zone := &l.zone[i] if zone.name == name { return zone.offset, zone.isDST, true } } + + // Otherwise, give up. return } diff --git a/src/pkg/time/zoneinfo_read.go b/src/pkg/time/zoneinfo_read.go index ebb4205a9..4519c9962 100644 --- a/src/pkg/time/zoneinfo_read.go +++ b/src/pkg/time/zoneinfo_read.go @@ -141,7 +141,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) { if n, ok = zonedata.big4(); !ok { return nil, badData } - zone[i].offset = int(n) + zone[i].offset = int(int32(n)) var b byte if b, ok = zonedata.byte(); !ok { return nil, badData @@ -174,7 +174,13 @@ func loadZoneData(bytes []byte) (l *Location, err error) { } } - // Commited to succeed. + if len(tx) == 0 { + // Build fake transition to cover all time. + // This happens in fixed locations like "Etc/GMT0". + tx = append(tx, zoneTrans{when: -1 << 63, index: 0}) + } + + // Committed to succeed. l = &Location{zone: zone, tx: tx} // Fill in the cache with information about right now, @@ -284,7 +290,7 @@ func loadZoneZip(zipfile, name string) (l *Location, err error) { // 42 off[4] // 46 name[namelen] // 46+namelen+xlen+fclen - next header - // + // if get4(buf) != zcheader { break } diff --git a/src/pkg/time/zoneinfo_windows.go b/src/pkg/time/zoneinfo_windows.go index d596fab93..a8d3dcbfe 100644 --- a/src/pkg/time/zoneinfo_windows.go +++ b/src/pkg/time/zoneinfo_windows.go @@ -15,9 +15,9 @@ import ( // BUG(brainman,rsc): On Windows, the operating system does not provide complete // time zone information. // The implementation assumes that this year's rules for daylight savings -// time apply to all previous and future years as well. +// time apply to all previous and future years as well. // Also, time zone abbreviations are unavailable. The implementation constructs -// them using the capital letters from a longer time zone description. +// them using the capital letters from a longer time zone description. // abbrev returns the abbreviation to use for the given zone name. func abbrev(name []uint16) string { diff --git a/src/pkg/unicode/digit.go b/src/pkg/unicode/digit.go index 4800bd6ea..53171b396 100644 --- a/src/pkg/unicode/digit.go +++ b/src/pkg/unicode/digit.go @@ -9,5 +9,5 @@ func IsDigit(r rune) bool { if r <= MaxLatin1 { return '0' <= r && r <= '9' } - return Is(Digit, r) + return isExcludingLatin(Digit, r) } diff --git a/src/pkg/unicode/graphic.go b/src/pkg/unicode/graphic.go index 0de90ebd8..5b995fcd0 100644 --- a/src/pkg/unicode/graphic.go +++ b/src/pkg/unicode/graphic.go @@ -6,15 +6,17 @@ package unicode // Bit masks for each code point under U+0100, for fast lookup. const ( - pC = 1 << iota // a control character. - pP // a punctuation character. - pN // a numeral. - pS // a symbolic character. - pZ // a spacing character. - pLu // an upper-case letter. - pLl // a lower-case letter. - pp // a printable character according to Go's definition. - pg = pp | pZ // a graphical character according to the Unicode definition. + pC = 1 << iota // a control character. + pP // a punctuation character. + pN // a numeral. + pS // a symbolic character. + pZ // a spacing character. + pLu // an upper-case letter. + pLl // a lower-case letter. + pp // a printable character according to Go's definition. + pg = pp | pZ // a graphical character according to the Unicode definition. + pLo = pLl | pLu // a letter that is neither upper nor lower case. + pLmask = pLo ) // GraphicRanges defines the set of graphic characters according to Unicode. @@ -76,15 +78,15 @@ func IsControl(r rune) bool { // IsLetter reports whether the rune is a letter (category L). func IsLetter(r rune) bool { if uint32(r) <= MaxLatin1 { - return properties[uint8(r)]&(pLu|pLl) != 0 + return properties[uint8(r)]&(pLmask) != 0 } - return Is(Letter, r) + return isExcludingLatin(Letter, r) } // IsMark reports whether the rune is a mark character (category M). func IsMark(r rune) bool { // There are no mark characters in Latin-1. - return Is(Mark, r) + return isExcludingLatin(Mark, r) } // IsNumber reports whether the rune is a number (category N). @@ -92,7 +94,7 @@ func IsNumber(r rune) bool { if uint32(r) <= MaxLatin1 { return properties[uint8(r)]&pN != 0 } - return Is(Number, r) + return isExcludingLatin(Number, r) } // IsPunct reports whether the rune is a Unicode punctuation character @@ -119,7 +121,7 @@ func IsSpace(r rune) bool { } return false } - return Is(White_Space, r) + return isExcludingLatin(White_Space, r) } // IsSymbol reports whether the rune is a symbolic character. @@ -127,5 +129,5 @@ func IsSymbol(r rune) bool { if uint32(r) <= MaxLatin1 { return properties[uint8(r)]&pS != 0 } - return Is(Symbol, r) + return isExcludingLatin(Symbol, r) } diff --git a/src/pkg/unicode/letter.go b/src/pkg/unicode/letter.go index be484553d..8d56363df 100644 --- a/src/pkg/unicode/letter.go +++ b/src/pkg/unicode/letter.go @@ -19,8 +19,9 @@ const ( // The two slices must be in sorted order and non-overlapping. // Also, R32 should contain only values >= 0x10000 (1<<16). type RangeTable struct { - R16 []Range16 - R32 []Range32 + R16 []Range16 + R32 []Range32 + LatinOffset int // number of entries in R16 with Hi <= MaxLatin1 } // Range16 represents of a range of 16-bit Unicode code points. The range runs from Lo to Hi @@ -80,14 +81,31 @@ const ( UpperLower = MaxRune + 1 // (Cannot be a valid delta.) ) -// is16 uses binary search to test whether rune is in the specified slice of 16-bit ranges. +// linearMax is the maximum size table for linear search for non-Latin1 rune. +// Derived by running 'go test -calibrate'. +const linearMax = 18 + +// is16 reports whether r is in the sorted slice of 16-bit ranges. func is16(ranges []Range16, r uint16) bool { + if len(ranges) <= linearMax || r <= MaxLatin1 { + for i := range ranges { + range_ := &ranges[i] + if r < range_.Lo { + return false + } + if r <= range_.Hi { + return (r-range_.Lo)%range_.Stride == 0 + } + } + return false + } + // binary search over ranges lo := 0 hi := len(ranges) for lo < hi { m := lo + (hi-lo)/2 - range_ := ranges[m] + range_ := &ranges[m] if range_.Lo <= r && r <= range_.Hi { return (r-range_.Lo)%range_.Stride == 0 } @@ -100,8 +118,21 @@ func is16(ranges []Range16, r uint16) bool { return false } -// is32 uses binary search to test whether rune is in the specified slice of 32-bit ranges. +// is32 reports whether r is in the sorted slice of 32-bit ranges. func is32(ranges []Range32, r uint32) bool { + if len(ranges) <= linearMax { + for i := range ranges { + range_ := &ranges[i] + if r < range_.Lo { + return false + } + if r <= range_.Hi { + return (r-range_.Lo)%range_.Stride == 0 + } + } + return false + } + // binary search over ranges lo := 0 hi := len(ranges) @@ -122,21 +153,6 @@ func is32(ranges []Range32, r uint32) bool { // Is tests whether rune is in the specified table of ranges. func Is(rangeTab *RangeTable, r rune) bool { - // common case: rune is ASCII or Latin-1. - if uint32(r) <= MaxLatin1 { - // Only need to check R16, since R32 is always >= 1<<16. - r16 := uint16(r) - for _, r := range rangeTab.R16 { - if r16 > r.Hi { - continue - } - if r16 < r.Lo { - return false - } - return (r16-r.Lo)%r.Stride == 0 - } - return false - } r16 := rangeTab.R16 if len(r16) > 0 && r <= rune(r16[len(r16)-1].Hi) { return is16(r16, uint16(r)) @@ -148,22 +164,34 @@ func Is(rangeTab *RangeTable, r rune) bool { return false } +func isExcludingLatin(rangeTab *RangeTable, r rune) bool { + r16 := rangeTab.R16 + if off := rangeTab.LatinOffset; len(r16) > off && r <= rune(r16[len(r16)-1].Hi) { + return is16(r16[off:], uint16(r)) + } + r32 := rangeTab.R32 + if len(r32) > 0 && r >= rune(r32[0].Lo) { + return is32(r32, uint32(r)) + } + return false +} + // IsUpper reports whether the rune is an upper case letter. func IsUpper(r rune) bool { // See comment in IsGraphic. if uint32(r) <= MaxLatin1 { - return properties[uint8(r)]&pLu != 0 + return properties[uint8(r)]&pLmask == pLu } - return Is(Upper, r) + return isExcludingLatin(Upper, r) } // IsLower reports whether the rune is a lower case letter. func IsLower(r rune) bool { // See comment in IsGraphic. if uint32(r) <= MaxLatin1 { - return properties[uint8(r)]&pLl != 0 + return properties[uint8(r)]&pLmask == pLl } - return Is(Lower, r) + return isExcludingLatin(Lower, r) } // IsTitle reports whether the rune is a title case letter. @@ -171,7 +199,7 @@ func IsTitle(r rune) bool { if r <= MaxLatin1 { return false } - return Is(Title, r) + return isExcludingLatin(Title, r) } // to maps the rune using the specified case mapping. @@ -288,7 +316,7 @@ type foldPair struct { // SimpleFold iterates over Unicode code points equivalent under // the Unicode-defined simple case folding. Among the code points // equivalent to rune (including rune itself), SimpleFold returns the -// smallest rune >= r if one exists, or else the smallest rune >= 0. +// smallest rune >= r if one exists, or else the smallest rune >= 0. // // For example: // SimpleFold('A') = 'a' diff --git a/src/pkg/unicode/letter_test.go b/src/pkg/unicode/letter_test.go index 2d8056218..e4d5572a0 100644 --- a/src/pkg/unicode/letter_test.go +++ b/src/pkg/unicode/letter_test.go @@ -5,6 +5,10 @@ package unicode_test import ( + "flag" + "fmt" + "runtime" + "sort" "testing" . "unicode" ) @@ -427,3 +431,117 @@ func TestSimpleFold(t *testing.T) { } } } + +// Running 'go test -calibrate' runs the calibration to find a plausible +// cutoff point for linear search of a range list vs. binary search. +// We create a fake table and then time how long it takes to do a +// sequence of searches within that table, for all possible inputs +// relative to the ranges (something before all, in each, between each, after all). +// This assumes that all possible runes are equally likely. +// In practice most runes are ASCII so this is a conservative estimate +// of an effective cutoff value. In practice we could probably set it higher +// than what this function recommends. + +var calibrate = flag.Bool("calibrate", false, "compute crossover for linear vs. binary search") + +func TestCalibrate(t *testing.T) { + if !*calibrate { + return + } + + if runtime.GOARCH == "amd64" { + fmt.Printf("warning: running calibration on %s\n", runtime.GOARCH) + } + + // Find the point where binary search wins by more than 10%. + // The 10% bias gives linear search an edge when they're close, + // because on predominantly ASCII inputs linear search is even + // better than our benchmarks measure. + n := sort.Search(64, func(n int) bool { + tab := fakeTable(n) + blinear := func(b *testing.B) { + tab := tab + max := n*5 + 20 + for i := 0; i < b.N; i++ { + for j := 0; j <= max; j++ { + linear(tab, uint16(j)) + } + } + } + bbinary := func(b *testing.B) { + tab := tab + max := n*5 + 20 + for i := 0; i < b.N; i++ { + for j := 0; j <= max; j++ { + binary(tab, uint16(j)) + } + } + } + bmlinear := testing.Benchmark(blinear) + bmbinary := testing.Benchmark(bbinary) + fmt.Printf("n=%d: linear=%d binary=%d\n", n, bmlinear.NsPerOp(), bmbinary.NsPerOp()) + return bmlinear.NsPerOp()*100 > bmbinary.NsPerOp()*110 + }) + fmt.Printf("calibration: linear cutoff = %d\n", n) +} + +func fakeTable(n int) []Range16 { + var r16 []Range16 + for i := 0; i < n; i++ { + r16 = append(r16, Range16{uint16(i*5 + 10), uint16(i*5 + 12), 1}) + } + return r16 +} + +func linear(ranges []Range16, r uint16) bool { + for i := range ranges { + range_ := &ranges[i] + if r < range_.Lo { + return false + } + if r <= range_.Hi { + return (r-range_.Lo)%range_.Stride == 0 + } + } + return false +} + +func binary(ranges []Range16, r uint16) bool { + // binary search over ranges + lo := 0 + hi := len(ranges) + for lo < hi { + m := lo + (hi-lo)/2 + range_ := &ranges[m] + if range_.Lo <= r && r <= range_.Hi { + return (r-range_.Lo)%range_.Stride == 0 + } + if r < range_.Lo { + hi = m + } else { + lo = m + 1 + } + } + return false +} + +func TestLatinOffset(t *testing.T) { + var maps = []map[string]*RangeTable{ + Categories, + FoldCategory, + FoldScript, + Properties, + Scripts, + } + for _, m := range maps { + for name, tab := range m { + i := 0 + for i < len(tab.R16) && tab.R16[i].Hi <= MaxLatin1 { + i++ + } + if tab.LatinOffset != i { + t.Errorf("%s: LatinOffset=%d, want %d", name, tab.LatinOffset, i) + } + } + } +} diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go index fcd14fc73..53d8b967e 100644 --- a/src/pkg/unicode/maketables.go +++ b/src/pkg/unicode/maketables.go @@ -13,7 +13,6 @@ import ( "bufio" "flag" "fmt" - "io" "log" "net/http" "os" @@ -41,7 +40,7 @@ func main() { var dataURL = flag.String("data", "", "full URL for UnicodeData.txt; defaults to --url/UnicodeData.txt") var casefoldingURL = flag.String("casefolding", "", "full URL for CaseFolding.txt; defaults to --url/CaseFolding.txt") var url = flag.String("url", - "http://www.unicode.org/Public/6.0.0/ucd/", + "http://www.unicode.org/Public/6.2.0/ucd/", "URL of Unicode database directory") var tablelist = flag.String("tables", "all", @@ -321,16 +320,11 @@ func loadChars() { flag.Set("data", *url+"UnicodeData.txt") } input := open(*dataURL) + defer input.close() + scanner := bufio.NewScanner(input) var first rune = 0 - for { - line, err := input.ReadString('\n') - if err != nil { - if err == io.EOF { - break - } - logger.Fatal(err) - } - switch parseCategory(line[0 : len(line)-1]) { + for scanner.Scan() { + switch parseCategory(scanner.Text()) { case SNormal: if first != 0 { logger.Fatalf("bad state normal at %U", lastChar) @@ -351,7 +345,9 @@ func loadChars() { first = 0 } } - input.close() + if scanner.Err() != nil { + logger.Fatal(scanner.Err()) + } } func loadCasefold() { @@ -359,15 +355,11 @@ func loadCasefold() { flag.Set("casefolding", *url+"CaseFolding.txt") } input := open(*casefoldingURL) - for { - line, err := input.ReadString('\n') - if err != nil { - if err == io.EOF { - break - } - logger.Fatal(err) - } - if line[0] == '#' { + defer input.close() + scanner := bufio.NewScanner(input) + for scanner.Scan() { + line := scanner.Text() + if len(line) == 0 || line[0] == '#' || len(strings.TrimSpace(line)) == 0 { continue } field := strings.Split(line, "; ") @@ -389,7 +381,9 @@ func loadCasefold() { } chars[p1].foldCase = rune(p2) } - input.close() + if scanner.Err() != nil { + logger.Fatal(scanner.Err()) + } } const progHeader = `// Generated by running @@ -503,6 +497,7 @@ const format = "\t\t{0x%04x, 0x%04x, %d},\n" func dumpRange(header string, inCategory Op) { fmt.Print(header) next := rune(0) + latinOffset := 0 fmt.Print("\tR16: []Range16{\n") // one Range for each iteration count := &range16Count @@ -546,11 +541,17 @@ func dumpRange(header string, inCategory Op) { break } } + if uint32(hi) <= unicode.MaxLatin1 { + latinOffset++ + } size, count = printRange(uint32(lo), uint32(hi), uint32(stride), size, count) // next range: start looking where this range ends next = hi + 1 } fmt.Print("\t},\n") + if latinOffset > 0 { + fmt.Printf("\tLatinOffset: %d,\n", latinOffset) + } fmt.Print("}\n\n") } @@ -703,15 +704,12 @@ func printScriptOrProperty(doProps bool) { return } input := open(*url + file) - for { - line, err := input.ReadString('\n') - if err != nil { - if err == io.EOF { - break - } - logger.Fatal(err) - } - parseScript(line[0:len(line)-1], table) + scanner := bufio.NewScanner(input) + for scanner.Scan() { + parseScript(scanner.Text(), table) + } + if scanner.Err() != nil { + logger.Fatal(scanner.Err()) } input.close() @@ -760,14 +758,17 @@ func printScriptOrProperty(doProps bool) { } ndecl++ fmt.Printf("var _%s = &RangeTable {\n", name) - fmt.Print("\tR16: []Range16{\n") ranges := foldAdjacent(table[name]) + fmt.Print("\tR16: []Range16{\n") size := 16 count := &range16Count for _, s := range ranges { size, count = printRange(s.Lo, s.Hi, s.Stride, size, count) } fmt.Print("\t},\n") + if off := findLatinOffset(ranges); off > 0 { + fmt.Printf("\tLatinOffset: %d,\n", off) + } fmt.Print("}\n\n") } decl.Sort() @@ -779,6 +780,14 @@ func printScriptOrProperty(doProps bool) { fmt.Print(")\n\n") } +func findLatinOffset(ranges []unicode.Range32) int { + i := 0 + for i < len(ranges) && ranges[i].Hi <= unicode.MaxLatin1 { + i++ + } + return i +} + const ( CaseUpper = 1 << iota CaseLower @@ -1022,6 +1031,8 @@ func printLatinProperties() { property = "0" case "Ll": property = "pLl | pp" + case "Lo": + property = "pLo | pp" case "Lu": property = "pLu | pp" case "Nd", "No": diff --git a/src/pkg/unicode/script_test.go b/src/pkg/unicode/script_test.go index 1c5b80142..395cc71a0 100644 --- a/src/pkg/unicode/script_test.go +++ b/src/pkg/unicode/script_test.go @@ -14,7 +14,7 @@ type T struct { script string } -// Hand-chosen tests from Unicode 5.1.0 & 6.0..0, mostly to discover when new +// Hand-chosen tests from Unicode 5.1.0, 6.0.0 and 6.2.0 mostly to discover when new // scripts and categories arise. var inTest = []T{ {0x06e2, "Arabic"}, @@ -31,6 +31,7 @@ var inTest = []T{ {0x11011, "Brahmi"}, {0x156d, "Canadian_Aboriginal"}, {0x102a9, "Carian"}, + {0x11111, "Chakma"}, {0xaa4d, "Cham"}, {0x13c2, "Cherokee"}, {0x0020, "Common"}, @@ -76,6 +77,9 @@ var inTest = []T{ {0x0d42, "Malayalam"}, {0x0843, "Mandaic"}, {0xabd0, "Meetei_Mayek"}, + {0x1099f, "Meroitic_Hieroglyphs"}, + {0x109a0, "Meroitic_Cursive"}, + {0x16f00, "Miao"}, {0x1822, "Mongolian"}, {0x104c, "Myanmar"}, {0x19c3, "New_Tai_Lue"}, @@ -94,8 +98,10 @@ var inTest = []T{ {0x16c0, "Runic"}, {0x081d, "Samaritan"}, {0xa892, "Saurashtra"}, + {0x111a0, "Sharada"}, {0x10463, "Shavian"}, {0x0dbd, "Sinhala"}, + {0x110d0, "Sora_Sompeng"}, {0x1ba3, "Sundanese"}, {0xa803, "Syloti_Nagri"}, {0x070f, "Syriac"}, @@ -104,6 +110,7 @@ var inTest = []T{ {0x1972, "Tai_Le"}, {0x1a62, "Tai_Tham"}, {0xaadc, "Tai_Viet"}, + {0x116c9, "Takri"}, {0x0bbf, "Tamil"}, {0x0c55, "Telugu"}, {0x07a7, "Thaana"}, @@ -121,7 +128,7 @@ var outTest = []T{ // not really worth being thorough var inCategoryTest = []T{ {0x0081, "Cc"}, - {0x17b4, "Cf"}, + {0x200B, "Cf"}, {0xf0000, "Co"}, {0xdb80, "Cs"}, {0x0236, "Ll"}, diff --git a/src/pkg/unicode/tables.go b/src/pkg/unicode/tables.go index ebd169b09..36b5a3115 100644 --- a/src/pkg/unicode/tables.go +++ b/src/pkg/unicode/tables.go @@ -1,11 +1,11 @@ // Generated by running -// maketables --tables=all --data=http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.0.0/ucd/CaseFolding.txt +// maketables --tables=all --data=http://www.unicode.org/Public/6.2.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.2.0/ucd/CaseFolding.txt // DO NOT EDIT package unicode // Version is the Unicode edition from which the tables are derived. -const Version = "6.0.0" +const Version = "6.2.0" // Categories is the set of Unicode category tables. var Categories = map[string]*RangeTable{ @@ -52,9 +52,8 @@ var _C = &RangeTable{ {0x0001, 0x001f, 1}, {0x007f, 0x009f, 1}, {0x00ad, 0x0600, 1363}, - {0x0601, 0x0603, 1}, + {0x0601, 0x0604, 1}, {0x06dd, 0x070f, 50}, - {0x17b4, 0x17b5, 1}, {0x200b, 0x200f, 1}, {0x202a, 0x202e, 1}, {0x2060, 0x2064, 1}, @@ -71,6 +70,7 @@ var _C = &RangeTable{ {0xf0000, 0xffffd, 1}, {0x100000, 0x10fffd, 1}, }, + LatinOffset: 2, } var _Cc = &RangeTable{ @@ -78,14 +78,14 @@ var _Cc = &RangeTable{ {0x0001, 0x001f, 1}, {0x007f, 0x009f, 1}, }, + LatinOffset: 2, } var _Cf = &RangeTable{ R16: []Range16{ {0x00ad, 0x0600, 1363}, - {0x0601, 0x0603, 1}, + {0x0601, 0x0604, 1}, {0x06dd, 0x070f, 50}, - {0x17b4, 0x17b5, 1}, {0x200b, 0x200f, 1}, {0x202a, 0x202e, 1}, {0x2060, 0x2064, 1}, @@ -162,6 +162,8 @@ var _L = &RangeTable{ {0x081a, 0x0824, 10}, {0x0828, 0x0840, 24}, {0x0841, 0x0858, 1}, + {0x08a0, 0x08a2, 2}, + {0x08a3, 0x08ac, 1}, {0x0904, 0x0939, 1}, {0x093d, 0x0950, 19}, {0x0958, 0x0961, 1}, @@ -259,8 +261,9 @@ var _L = &RangeTable{ {0x0ebd, 0x0ec0, 3}, {0x0ec1, 0x0ec4, 1}, {0x0ec6, 0x0edc, 22}, - {0x0edd, 0x0f00, 35}, - {0x0f40, 0x0f47, 1}, + {0x0edd, 0x0edf, 1}, + {0x0f00, 0x0f40, 64}, + {0x0f41, 0x0f47, 1}, {0x0f49, 0x0f6c, 1}, {0x0f88, 0x0f8c, 1}, {0x1000, 0x102a, 1}, @@ -273,9 +276,9 @@ var _L = &RangeTable{ {0x1075, 0x1081, 1}, {0x108e, 0x10a0, 18}, {0x10a1, 0x10c5, 1}, + {0x10c7, 0x10cd, 6}, {0x10d0, 0x10fa, 1}, - {0x10fc, 0x1100, 4}, - {0x1101, 0x1248, 1}, + {0x10fc, 0x1248, 1}, {0x124a, 0x124d, 1}, {0x1250, 0x1256, 1}, {0x1258, 0x125a, 2}, @@ -321,12 +324,13 @@ var _L = &RangeTable{ {0x1b45, 0x1b4b, 1}, {0x1b83, 0x1ba0, 1}, {0x1bae, 0x1baf, 1}, - {0x1bc0, 0x1be5, 1}, + {0x1bba, 0x1be5, 1}, {0x1c00, 0x1c23, 1}, {0x1c4d, 0x1c4f, 1}, {0x1c5a, 0x1c7d, 1}, {0x1ce9, 0x1cec, 1}, {0x1cee, 0x1cf1, 1}, + {0x1cf5, 0x1cf6, 1}, {0x1d00, 0x1dbf, 1}, {0x1e00, 0x1f15, 1}, {0x1f18, 0x1f1d, 1}, @@ -362,8 +366,10 @@ var _L = &RangeTable{ {0x2c30, 0x2c5e, 1}, {0x2c60, 0x2ce4, 1}, {0x2ceb, 0x2cee, 1}, + {0x2cf2, 0x2cf3, 1}, {0x2d00, 0x2d25, 1}, - {0x2d30, 0x2d65, 1}, + {0x2d27, 0x2d2d, 6}, + {0x2d30, 0x2d67, 1}, {0x2d6f, 0x2d80, 17}, {0x2d81, 0x2d96, 1}, {0x2da0, 0x2da6, 1}, @@ -387,7 +393,7 @@ var _L = &RangeTable{ {0x31a0, 0x31ba, 1}, {0x31f0, 0x31ff, 1}, {0x3400, 0x4db5, 1}, - {0x4e00, 0x9fcb, 1}, + {0x4e00, 0x9fcc, 1}, {0xa000, 0xa48c, 1}, {0xa4d0, 0xa4fd, 1}, {0xa500, 0xa60c, 1}, @@ -399,9 +405,9 @@ var _L = &RangeTable{ {0xa717, 0xa71f, 1}, {0xa722, 0xa788, 1}, {0xa78b, 0xa78e, 1}, - {0xa790, 0xa791, 1}, - {0xa7a0, 0xa7a9, 1}, - {0xa7fa, 0xa801, 1}, + {0xa790, 0xa793, 1}, + {0xa7a0, 0xa7aa, 1}, + {0xa7f8, 0xa801, 1}, {0xa803, 0xa805, 1}, {0xa807, 0xa80a, 1}, {0xa80c, 0xa822, 1}, @@ -425,6 +431,8 @@ var _L = &RangeTable{ {0xaaba, 0xaabd, 1}, {0xaac0, 0xaac2, 2}, {0xaadb, 0xaadd, 1}, + {0xaae0, 0xaaea, 1}, + {0xaaf2, 0xaaf4, 1}, {0xab01, 0xab06, 1}, {0xab09, 0xab0e, 1}, {0xab11, 0xab16, 1}, @@ -434,8 +442,7 @@ var _L = &RangeTable{ {0xac00, 0xd7a3, 1}, {0xd7b0, 0xd7c6, 1}, {0xd7cb, 0xd7fb, 1}, - {0xf900, 0xfa2d, 1}, - {0xfa30, 0xfa6d, 1}, + {0xf900, 0xfa6d, 1}, {0xfa70, 0xfad9, 1}, {0xfb00, 0xfb06, 1}, {0xfb13, 0xfb17, 1}, @@ -486,6 +493,8 @@ var _L = &RangeTable{ {0x10840, 0x10855, 1}, {0x10900, 0x10915, 1}, {0x10920, 0x10939, 1}, + {0x10980, 0x109b7, 1}, + {0x109be, 0x109bf, 1}, {0x10a00, 0x10a10, 16}, {0x10a11, 0x10a13, 1}, {0x10a15, 0x10a17, 1}, @@ -497,9 +506,17 @@ var _L = &RangeTable{ {0x10c00, 0x10c48, 1}, {0x11003, 0x11037, 1}, {0x11083, 0x110af, 1}, + {0x110d0, 0x110e8, 1}, + {0x11103, 0x11126, 1}, + {0x11183, 0x111b2, 1}, + {0x111c1, 0x111c4, 1}, + {0x11680, 0x116aa, 1}, {0x12000, 0x1236e, 1}, {0x13000, 0x1342e, 1}, {0x16800, 0x16a38, 1}, + {0x16f00, 0x16f44, 1}, + {0x16f50, 0x16f93, 67}, + {0x16f94, 0x16f9f, 1}, {0x1b000, 0x1b001, 1}, {0x1d400, 0x1d454, 1}, {0x1d456, 0x1d49c, 1}, @@ -531,18 +548,42 @@ var _L = &RangeTable{ {0x1d78a, 0x1d7a8, 1}, {0x1d7aa, 0x1d7c2, 1}, {0x1d7c4, 0x1d7cb, 1}, + {0x1ee00, 0x1ee03, 1}, + {0x1ee05, 0x1ee1f, 1}, + {0x1ee21, 0x1ee22, 1}, + {0x1ee24, 0x1ee27, 3}, + {0x1ee29, 0x1ee32, 1}, + {0x1ee34, 0x1ee37, 1}, + {0x1ee39, 0x1ee3b, 2}, + {0x1ee42, 0x1ee47, 5}, + {0x1ee49, 0x1ee4d, 2}, + {0x1ee4e, 0x1ee4f, 1}, + {0x1ee51, 0x1ee52, 1}, + {0x1ee54, 0x1ee57, 3}, + {0x1ee59, 0x1ee61, 2}, + {0x1ee62, 0x1ee64, 2}, + {0x1ee67, 0x1ee6a, 1}, + {0x1ee6c, 0x1ee72, 1}, + {0x1ee74, 0x1ee77, 1}, + {0x1ee79, 0x1ee7c, 1}, + {0x1ee7e, 0x1ee80, 2}, + {0x1ee81, 0x1ee89, 1}, + {0x1ee8b, 0x1ee9b, 1}, + {0x1eea1, 0x1eea3, 1}, + {0x1eea5, 0x1eea9, 1}, + {0x1eeab, 0x1eebb, 1}, {0x20000, 0x2a6d6, 1}, {0x2a700, 0x2b734, 1}, {0x2b740, 0x2b81d, 1}, {0x2f800, 0x2fa1d, 1}, }, + LatinOffset: 6, } var _Ll = &RangeTable{ R16: []Range16{ {0x0061, 0x007a, 1}, - {0x00aa, 0x00b5, 11}, - {0x00ba, 0x00df, 37}, + {0x00b5, 0x00df, 42}, {0x00e0, 0x00f6, 1}, {0x00f8, 0x00ff, 1}, {0x0101, 0x0137, 2}, @@ -593,7 +634,7 @@ var _Ll = &RangeTable{ {0x04cf, 0x0527, 2}, {0x0561, 0x0587, 1}, {0x1d00, 0x1d2b, 1}, - {0x1d62, 0x1d77, 1}, + {0x1d6b, 0x1d77, 1}, {0x1d79, 0x1d9a, 1}, {0x1e01, 0x1e95, 2}, {0x1e96, 0x1e9d, 1}, @@ -630,11 +671,12 @@ var _Ll = &RangeTable{ {0x2c66, 0x2c6c, 2}, {0x2c71, 0x2c73, 2}, {0x2c74, 0x2c76, 2}, - {0x2c77, 0x2c7c, 1}, + {0x2c77, 0x2c7b, 1}, {0x2c81, 0x2ce3, 2}, {0x2ce4, 0x2cec, 8}, - {0x2cee, 0x2d00, 18}, - {0x2d01, 0x2d25, 1}, + {0x2cee, 0x2cf3, 5}, + {0x2d00, 0x2d25, 1}, + {0x2d27, 0x2d2d, 6}, {0xa641, 0xa66d, 2}, {0xa681, 0xa697, 2}, {0xa723, 0xa72f, 2}, @@ -644,8 +686,8 @@ var _Ll = &RangeTable{ {0xa77a, 0xa77c, 2}, {0xa77f, 0xa787, 2}, {0xa78c, 0xa78e, 2}, - {0xa791, 0xa7a1, 16}, - {0xa7a3, 0xa7a9, 2}, + {0xa791, 0xa793, 2}, + {0xa7a1, 0xa7a9, 2}, {0xa7fa, 0xfb00, 21254}, {0xfb01, 0xfb06, 1}, {0xfb13, 0xfb17, 1}, @@ -682,6 +724,7 @@ var _Ll = &RangeTable{ {0x1d7c4, 0x1d7c9, 1}, {0x1d7cb, 0x1d7cb, 1}, }, + LatinOffset: 4, } var _Lm = &RangeTable{ @@ -701,14 +744,15 @@ var _Lm = &RangeTable{ {0x17d7, 0x1843, 108}, {0x1aa7, 0x1c78, 465}, {0x1c79, 0x1c7d, 1}, - {0x1d2c, 0x1d61, 1}, + {0x1d2c, 0x1d6a, 1}, {0x1d78, 0x1d9b, 35}, {0x1d9c, 0x1dbf, 1}, {0x2071, 0x207f, 14}, {0x2090, 0x209c, 1}, - {0x2c7d, 0x2d6f, 242}, - {0x2e2f, 0x3005, 470}, - {0x3031, 0x3035, 1}, + {0x2c7c, 0x2c7d, 1}, + {0x2d6f, 0x2e2f, 192}, + {0x3005, 0x3031, 44}, + {0x3032, 0x3035, 1}, {0x303b, 0x309d, 98}, {0x309e, 0x30fc, 94}, {0x30fd, 0x30fe, 1}, @@ -717,14 +761,20 @@ var _Lm = &RangeTable{ {0xa60c, 0xa67f, 115}, {0xa717, 0xa71f, 1}, {0xa770, 0xa788, 24}, + {0xa7f8, 0xa7f9, 1}, {0xa9cf, 0xaa70, 161}, - {0xaadd, 0xff70, 21651}, + {0xaadd, 0xaaf3, 22}, + {0xaaf4, 0xff70, 21628}, {0xff9e, 0xff9f, 1}, }, + R32: []Range32{ + {0x16f93, 0x16f9f, 1}, + }, } var _Lo = &RangeTable{ R16: []Range16{ + {0x00aa, 0x00ba, 16}, {0x01bb, 0x01c0, 5}, {0x01c1, 0x01c3, 1}, {0x0294, 0x05d0, 828}, @@ -744,6 +794,8 @@ var _Lo = &RangeTable{ {0x07cb, 0x07ea, 1}, {0x0800, 0x0815, 1}, {0x0840, 0x0858, 1}, + {0x08a0, 0x08a2, 2}, + {0x08a3, 0x08ac, 1}, {0x0904, 0x0939, 1}, {0x093d, 0x0950, 19}, {0x0958, 0x0961, 1}, @@ -840,7 +892,7 @@ var _Lo = &RangeTable{ {0x0eb2, 0x0eb3, 1}, {0x0ebd, 0x0ec0, 3}, {0x0ec1, 0x0ec4, 1}, - {0x0edc, 0x0edd, 1}, + {0x0edc, 0x0edf, 1}, {0x0f00, 0x0f40, 64}, {0x0f41, 0x0f47, 1}, {0x0f49, 0x0f6c, 1}, @@ -855,7 +907,7 @@ var _Lo = &RangeTable{ {0x1075, 0x1081, 1}, {0x108e, 0x10d0, 66}, {0x10d1, 0x10fa, 1}, - {0x1100, 0x1248, 1}, + {0x10fd, 0x1248, 1}, {0x124a, 0x124d, 1}, {0x1250, 0x1256, 1}, {0x1258, 0x125a, 2}, @@ -901,14 +953,15 @@ var _Lo = &RangeTable{ {0x1b45, 0x1b4b, 1}, {0x1b83, 0x1ba0, 1}, {0x1bae, 0x1baf, 1}, - {0x1bc0, 0x1be5, 1}, + {0x1bba, 0x1be5, 1}, {0x1c00, 0x1c23, 1}, {0x1c4d, 0x1c4f, 1}, {0x1c5a, 0x1c77, 1}, {0x1ce9, 0x1cec, 1}, {0x1cee, 0x1cf1, 1}, + {0x1cf5, 0x1cf6, 1}, {0x2135, 0x2138, 1}, - {0x2d30, 0x2d65, 1}, + {0x2d30, 0x2d67, 1}, {0x2d80, 0x2d96, 1}, {0x2da0, 0x2da6, 1}, {0x2da8, 0x2dae, 1}, @@ -928,7 +981,7 @@ var _Lo = &RangeTable{ {0x31a0, 0x31ba, 1}, {0x31f0, 0x31ff, 1}, {0x3400, 0x4db5, 1}, - {0x4e00, 0x9fcb, 1}, + {0x4e00, 0x9fcc, 1}, {0xa000, 0xa014, 1}, {0xa016, 0xa48c, 1}, {0xa4d0, 0xa4f7, 1}, @@ -961,7 +1014,9 @@ var _Lo = &RangeTable{ {0xaaba, 0xaabd, 1}, {0xaac0, 0xaac2, 2}, {0xaadb, 0xaadc, 1}, - {0xab01, 0xab06, 1}, + {0xaae0, 0xaaea, 1}, + {0xaaf2, 0xab01, 15}, + {0xab02, 0xab06, 1}, {0xab09, 0xab0e, 1}, {0xab11, 0xab16, 1}, {0xab20, 0xab26, 1}, @@ -970,8 +1025,7 @@ var _Lo = &RangeTable{ {0xac00, 0xd7a3, 1}, {0xd7b0, 0xd7c6, 1}, {0xd7cb, 0xd7fb, 1}, - {0xf900, 0xfa2d, 1}, - {0xfa30, 0xfa6d, 1}, + {0xf900, 0xfa6d, 1}, {0xfa70, 0xfad9, 1}, {0xfb1d, 0xfb1f, 2}, {0xfb20, 0xfb28, 1}, @@ -1020,6 +1074,8 @@ var _Lo = &RangeTable{ {0x10840, 0x10855, 1}, {0x10900, 0x10915, 1}, {0x10920, 0x10939, 1}, + {0x10980, 0x109b7, 1}, + {0x109be, 0x109bf, 1}, {0x10a00, 0x10a10, 16}, {0x10a11, 0x10a13, 1}, {0x10a15, 0x10a17, 1}, @@ -1031,15 +1087,47 @@ var _Lo = &RangeTable{ {0x10c00, 0x10c48, 1}, {0x11003, 0x11037, 1}, {0x11083, 0x110af, 1}, + {0x110d0, 0x110e8, 1}, + {0x11103, 0x11126, 1}, + {0x11183, 0x111b2, 1}, + {0x111c1, 0x111c4, 1}, + {0x11680, 0x116aa, 1}, {0x12000, 0x1236e, 1}, {0x13000, 0x1342e, 1}, {0x16800, 0x16a38, 1}, - {0x1b000, 0x1b001, 1}, + {0x16f00, 0x16f44, 1}, + {0x16f50, 0x1b000, 16560}, + {0x1b001, 0x1ee00, 15871}, + {0x1ee01, 0x1ee03, 1}, + {0x1ee05, 0x1ee1f, 1}, + {0x1ee21, 0x1ee22, 1}, + {0x1ee24, 0x1ee27, 3}, + {0x1ee29, 0x1ee32, 1}, + {0x1ee34, 0x1ee37, 1}, + {0x1ee39, 0x1ee3b, 2}, + {0x1ee42, 0x1ee47, 5}, + {0x1ee49, 0x1ee4d, 2}, + {0x1ee4e, 0x1ee4f, 1}, + {0x1ee51, 0x1ee52, 1}, + {0x1ee54, 0x1ee57, 3}, + {0x1ee59, 0x1ee61, 2}, + {0x1ee62, 0x1ee64, 2}, + {0x1ee67, 0x1ee6a, 1}, + {0x1ee6c, 0x1ee72, 1}, + {0x1ee74, 0x1ee77, 1}, + {0x1ee79, 0x1ee7c, 1}, + {0x1ee7e, 0x1ee80, 2}, + {0x1ee81, 0x1ee89, 1}, + {0x1ee8b, 0x1ee9b, 1}, + {0x1eea1, 0x1eea3, 1}, + {0x1eea5, 0x1eea9, 1}, + {0x1eeab, 0x1eebb, 1}, {0x20000, 0x2a6d6, 1}, {0x2a700, 0x2b734, 1}, {0x2b740, 0x2b81d, 1}, {0x2f800, 0x2fa1d, 1}, }, + LatinOffset: 1, } var _Lt = &RangeTable{ @@ -1109,6 +1197,7 @@ var _Lu = &RangeTable{ {0x04d0, 0x0526, 2}, {0x0531, 0x0556, 1}, {0x10a0, 0x10c5, 1}, + {0x10c7, 0x10cd, 6}, {0x1e00, 0x1e94, 2}, {0x1e9e, 0x1efe, 2}, {0x1f08, 0x1f0f, 1}, @@ -1142,15 +1231,16 @@ var _Lu = &RangeTable{ {0x2c7e, 0x2c80, 1}, {0x2c82, 0x2ce2, 2}, {0x2ceb, 0x2ced, 2}, - {0xa640, 0xa66c, 2}, + {0x2cf2, 0xa640, 31054}, + {0xa642, 0xa66c, 2}, {0xa680, 0xa696, 2}, {0xa722, 0xa72e, 2}, {0xa732, 0xa76e, 2}, {0xa779, 0xa77d, 2}, {0xa77e, 0xa786, 2}, {0xa78b, 0xa78d, 2}, - {0xa790, 0xa7a0, 16}, - {0xa7a2, 0xa7a8, 2}, + {0xa790, 0xa792, 2}, + {0xa7a0, 0xa7aa, 2}, {0xff21, 0xff3a, 1}, }, R32: []Range32{ @@ -1186,6 +1276,7 @@ var _Lu = &RangeTable{ {0x1d790, 0x1d7a8, 1}, {0x1d7ca, 0x1d7ca, 1}, }, + LatinOffset: 3, } var _M = &RangeTable{ @@ -1212,6 +1303,7 @@ var _M = &RangeTable{ {0x0825, 0x0827, 1}, {0x0829, 0x082d, 1}, {0x0859, 0x085b, 1}, + {0x08e4, 0x08fe, 1}, {0x0900, 0x0903, 1}, {0x093a, 0x093c, 1}, {0x093e, 0x094f, 1}, @@ -1302,7 +1394,7 @@ var _M = &RangeTable{ {0x1732, 0x1734, 1}, {0x1752, 0x1753, 1}, {0x1772, 0x1773, 1}, - {0x17b6, 0x17d3, 1}, + {0x17b4, 0x17d3, 1}, {0x17dd, 0x180b, 46}, {0x180c, 0x180d, 1}, {0x18a9, 0x1920, 119}, @@ -1318,12 +1410,13 @@ var _M = &RangeTable{ {0x1b34, 0x1b44, 1}, {0x1b6b, 0x1b73, 1}, {0x1b80, 0x1b82, 1}, - {0x1ba1, 0x1baa, 1}, + {0x1ba1, 0x1bad, 1}, {0x1be6, 0x1bf3, 1}, {0x1c24, 0x1c37, 1}, {0x1cd0, 0x1cd2, 1}, {0x1cd4, 0x1ce8, 1}, {0x1ced, 0x1cf2, 5}, + {0x1cf3, 0x1cf4, 1}, {0x1dc0, 0x1de6, 1}, {0x1dfc, 0x1dff, 1}, {0x20d0, 0x20f0, 1}, @@ -1333,11 +1426,11 @@ var _M = &RangeTable{ {0x302a, 0x302f, 1}, {0x3099, 0x309a, 1}, {0xa66f, 0xa672, 1}, - {0xa67c, 0xa67d, 1}, - {0xa6f0, 0xa6f1, 1}, - {0xa802, 0xa806, 4}, - {0xa80b, 0xa823, 24}, - {0xa824, 0xa827, 1}, + {0xa674, 0xa67d, 1}, + {0xa69f, 0xa6f0, 81}, + {0xa6f1, 0xa802, 273}, + {0xa806, 0xa80b, 5}, + {0xa823, 0xa827, 1}, {0xa880, 0xa881, 1}, {0xa8b4, 0xa8c4, 1}, {0xa8e0, 0xa8f1, 1}, @@ -1352,8 +1445,10 @@ var _M = &RangeTable{ {0xaab3, 0xaab4, 1}, {0xaab7, 0xaab8, 1}, {0xaabe, 0xaabf, 1}, - {0xaac1, 0xabe3, 290}, - {0xabe4, 0xabea, 1}, + {0xaac1, 0xaaeb, 42}, + {0xaaec, 0xaaef, 1}, + {0xaaf5, 0xaaf6, 1}, + {0xabe3, 0xabea, 1}, {0xabec, 0xabed, 1}, {0xfb1e, 0xfe00, 738}, {0xfe01, 0xfe0f, 1}, @@ -1370,6 +1465,13 @@ var _M = &RangeTable{ {0x11038, 0x11046, 1}, {0x11080, 0x11082, 1}, {0x110b0, 0x110ba, 1}, + {0x11100, 0x11102, 1}, + {0x11127, 0x11134, 1}, + {0x11180, 0x11182, 1}, + {0x111b3, 0x111c0, 1}, + {0x116ab, 0x116b7, 1}, + {0x16f51, 0x16f7e, 1}, + {0x16f8f, 0x16f92, 1}, {0x1d165, 0x1d169, 1}, {0x1d16d, 0x1d172, 1}, {0x1d17b, 0x1d182, 1}, @@ -1454,17 +1556,19 @@ var _Mc = &RangeTable{ {0x1b43, 0x1b44, 1}, {0x1b82, 0x1ba1, 31}, {0x1ba6, 0x1ba7, 1}, - {0x1baa, 0x1be7, 61}, + {0x1baa, 0x1bac, 2}, + {0x1bad, 0x1be7, 58}, {0x1bea, 0x1bec, 1}, {0x1bee, 0x1bf2, 4}, {0x1bf3, 0x1c24, 49}, {0x1c25, 0x1c2b, 1}, {0x1c34, 0x1c35, 1}, {0x1ce1, 0x1cf2, 17}, - {0xa823, 0xa824, 1}, - {0xa827, 0xa880, 89}, - {0xa881, 0xa8b4, 51}, - {0xa8b5, 0xa8c3, 1}, + {0x1cf3, 0x302e, 4923}, + {0x302f, 0xa823, 30708}, + {0xa824, 0xa827, 3}, + {0xa880, 0xa881, 1}, + {0xa8b4, 0xa8c3, 1}, {0xa952, 0xa953, 1}, {0xa983, 0xa9b4, 49}, {0xa9b5, 0xa9ba, 5}, @@ -1473,6 +1577,8 @@ var _Mc = &RangeTable{ {0xaa2f, 0xaa30, 1}, {0xaa33, 0xaa34, 1}, {0xaa4d, 0xaa7b, 46}, + {0xaaeb, 0xaaee, 3}, + {0xaaef, 0xaaf5, 6}, {0xabe3, 0xabe4, 1}, {0xabe6, 0xabe7, 1}, {0xabe9, 0xabea, 1}, @@ -1483,6 +1589,12 @@ var _Mc = &RangeTable{ {0x11002, 0x11082, 128}, {0x110b0, 0x110b2, 1}, {0x110b7, 0x110b8, 1}, + {0x1112c, 0x11182, 86}, + {0x111b3, 0x111b5, 1}, + {0x111bf, 0x111c0, 1}, + {0x116ac, 0x116ae, 2}, + {0x116af, 0x116b6, 7}, + {0x16f51, 0x16f7e, 1}, {0x1d165, 0x1d166, 1}, {0x1d16d, 0x1d172, 1}, }, @@ -1521,6 +1633,7 @@ var _Mn = &RangeTable{ {0x0825, 0x0827, 1}, {0x0829, 0x082d, 1}, {0x0859, 0x085b, 1}, + {0x08e4, 0x08fe, 1}, {0x0900, 0x0902, 1}, {0x093a, 0x093c, 2}, {0x0941, 0x0948, 1}, @@ -1592,6 +1705,7 @@ var _Mn = &RangeTable{ {0x1732, 0x1734, 1}, {0x1752, 0x1753, 1}, {0x1772, 0x1773, 1}, + {0x17b4, 0x17b5, 1}, {0x17b7, 0x17bd, 1}, {0x17c6, 0x17c9, 3}, {0x17ca, 0x17d3, 1}, @@ -1617,16 +1731,17 @@ var _Mn = &RangeTable{ {0x1b80, 0x1b81, 1}, {0x1ba2, 0x1ba5, 1}, {0x1ba8, 0x1ba9, 1}, - {0x1be6, 0x1be8, 2}, - {0x1be9, 0x1bed, 4}, - {0x1bef, 0x1bf1, 1}, + {0x1bab, 0x1be6, 59}, + {0x1be8, 0x1be9, 1}, + {0x1bed, 0x1bef, 2}, + {0x1bf0, 0x1bf1, 1}, {0x1c2c, 0x1c33, 1}, {0x1c36, 0x1c37, 1}, {0x1cd0, 0x1cd2, 1}, {0x1cd4, 0x1ce0, 1}, {0x1ce2, 0x1ce8, 1}, - {0x1ced, 0x1dc0, 211}, - {0x1dc1, 0x1de6, 1}, + {0x1ced, 0x1cf4, 7}, + {0x1dc0, 0x1de6, 1}, {0x1dfc, 0x1dff, 1}, {0x20d0, 0x20dc, 1}, {0x20e1, 0x20e5, 4}, @@ -1634,10 +1749,11 @@ var _Mn = &RangeTable{ {0x2cef, 0x2cf1, 1}, {0x2d7f, 0x2de0, 97}, {0x2de1, 0x2dff, 1}, - {0x302a, 0x302f, 1}, + {0x302a, 0x302d, 1}, {0x3099, 0x309a, 1}, - {0xa66f, 0xa67c, 13}, - {0xa67d, 0xa6f0, 115}, + {0xa66f, 0xa674, 5}, + {0xa675, 0xa67d, 1}, + {0xa69f, 0xa6f0, 81}, {0xa6f1, 0xa802, 273}, {0xa806, 0xa80b, 5}, {0xa825, 0xa826, 1}, @@ -1657,10 +1773,11 @@ var _Mn = &RangeTable{ {0xaab3, 0xaab4, 1}, {0xaab7, 0xaab8, 1}, {0xaabe, 0xaabf, 1}, - {0xaac1, 0xabe5, 292}, - {0xabe8, 0xabed, 5}, - {0xfb1e, 0xfe00, 738}, - {0xfe01, 0xfe0f, 1}, + {0xaac1, 0xaaec, 43}, + {0xaaed, 0xaaf6, 9}, + {0xabe5, 0xabe8, 3}, + {0xabed, 0xfb1e, 20273}, + {0xfe00, 0xfe0f, 1}, {0xfe20, 0xfe26, 1}, }, R32: []Range32{ @@ -1674,6 +1791,15 @@ var _Mn = &RangeTable{ {0x11080, 0x11081, 1}, {0x110b3, 0x110b6, 1}, {0x110b9, 0x110ba, 1}, + {0x11100, 0x11102, 1}, + {0x11127, 0x1112b, 1}, + {0x1112d, 0x11134, 1}, + {0x11180, 0x11181, 1}, + {0x111b6, 0x111be, 1}, + {0x116ab, 0x116ad, 2}, + {0x116b0, 0x116b5, 1}, + {0x116b7, 0x16f8f, 22744}, + {0x16f90, 0x16f92, 1}, {0x1d167, 0x1d169, 1}, {0x1d17b, 0x1d182, 1}, {0x1d185, 0x1d18b, 1}, @@ -1735,6 +1861,7 @@ var _N = &RangeTable{ {0x3038, 0x303a, 1}, {0x3192, 0x3195, 1}, {0x3220, 0x3229, 1}, + {0x3248, 0x324f, 1}, {0x3251, 0x325f, 1}, {0x3280, 0x3289, 1}, {0x32b1, 0x32bf, 1}, @@ -1764,11 +1891,16 @@ var _N = &RangeTable{ {0x10b78, 0x10b7f, 1}, {0x10e60, 0x10e7e, 1}, {0x11052, 0x1106f, 1}, + {0x110f0, 0x110f9, 1}, + {0x11136, 0x1113f, 1}, + {0x111d0, 0x111d9, 1}, + {0x116c0, 0x116c9, 1}, {0x12400, 0x12462, 1}, {0x1d360, 0x1d371, 1}, {0x1d7ce, 0x1d7ff, 1}, {0x1f100, 0x1f10a, 1}, }, + LatinOffset: 4, } var _Nd = &RangeTable{ @@ -1812,8 +1944,13 @@ var _Nd = &RangeTable{ R32: []Range32{ {0x104a0, 0x104a9, 1}, {0x11066, 0x1106f, 1}, + {0x110f0, 0x110f9, 1}, + {0x11136, 0x1113f, 1}, + {0x111d0, 0x111d9, 1}, + {0x116c0, 0x116c9, 1}, {0x1d7ce, 0x1d7ff, 1}, }, + LatinOffset: 1, } var _Nl = &RangeTable{ @@ -1858,6 +1995,7 @@ var _No = &RangeTable{ {0x2cfd, 0x3192, 1173}, {0x3193, 0x3195, 1}, {0x3220, 0x3229, 1}, + {0x3248, 0x324f, 1}, {0x3251, 0x325f, 1}, {0x3280, 0x3289, 1}, {0x32b1, 0x32bf, 1}, @@ -1879,6 +2017,7 @@ var _No = &RangeTable{ {0x1d360, 0x1d371, 1}, {0x1f100, 0x1f10a, 1}, }, + LatinOffset: 3, } var _P = &RangeTable{ @@ -1891,7 +2030,8 @@ var _P = &RangeTable{ {0x005b, 0x005d, 1}, {0x005f, 0x007b, 28}, {0x007d, 0x00a1, 36}, - {0x00ab, 0x00b7, 12}, + {0x00a7, 0x00ab, 4}, + {0x00b6, 0x00b7, 1}, {0x00bb, 0x00bf, 4}, {0x037e, 0x0387, 9}, {0x055a, 0x055f, 1}, @@ -1910,16 +2050,18 @@ var _P = &RangeTable{ {0x0830, 0x083e, 1}, {0x085e, 0x0964, 262}, {0x0965, 0x0970, 11}, - {0x0df4, 0x0e4f, 91}, - {0x0e5a, 0x0e5b, 1}, - {0x0f04, 0x0f12, 1}, - {0x0f3a, 0x0f3d, 1}, + {0x0af0, 0x0df4, 772}, + {0x0e4f, 0x0e5a, 11}, + {0x0e5b, 0x0f04, 169}, + {0x0f05, 0x0f12, 1}, + {0x0f14, 0x0f3a, 38}, + {0x0f3b, 0x0f3d, 1}, {0x0f85, 0x0fd0, 75}, {0x0fd1, 0x0fd4, 1}, {0x0fd9, 0x0fda, 1}, {0x104a, 0x104f, 1}, - {0x10fb, 0x1361, 614}, - {0x1362, 0x1368, 1}, + {0x10fb, 0x1360, 613}, + {0x1361, 0x1368, 1}, {0x1400, 0x166d, 621}, {0x166e, 0x169b, 45}, {0x169c, 0x16eb, 79}, @@ -1936,6 +2078,7 @@ var _P = &RangeTable{ {0x1bfc, 0x1bff, 1}, {0x1c3b, 0x1c3f, 1}, {0x1c7e, 0x1c7f, 1}, + {0x1cc0, 0x1cc7, 1}, {0x1cd3, 0x2010, 829}, {0x2011, 0x2027, 1}, {0x2030, 0x2043, 1}, @@ -1954,7 +2097,7 @@ var _P = &RangeTable{ {0x2cfe, 0x2cff, 1}, {0x2d70, 0x2e00, 144}, {0x2e01, 0x2e2e, 1}, - {0x2e30, 0x2e31, 1}, + {0x2e30, 0x2e3b, 1}, {0x3001, 0x3003, 1}, {0x3008, 0x3011, 1}, {0x3014, 0x301f, 1}, @@ -1973,6 +2116,7 @@ var _P = &RangeTable{ {0xa9de, 0xa9df, 1}, {0xaa5c, 0xaa5f, 1}, {0xaade, 0xaadf, 1}, + {0xaaf0, 0xaaf1, 1}, {0xabeb, 0xfd3e, 20819}, {0xfd3f, 0xfe10, 209}, {0xfe11, 0xfe19, 1}, @@ -1991,7 +2135,7 @@ var _P = &RangeTable{ {0xff60, 0xff65, 1}, }, R32: []Range32{ - {0x10100, 0x10101, 1}, + {0x10100, 0x10102, 1}, {0x1039f, 0x103d0, 49}, {0x10857, 0x1091f, 200}, {0x1093f, 0x10a50, 273}, @@ -2001,8 +2145,11 @@ var _P = &RangeTable{ {0x11047, 0x1104d, 1}, {0x110bb, 0x110bc, 1}, {0x110be, 0x110c1, 1}, + {0x11140, 0x11143, 1}, + {0x111c5, 0x111c8, 1}, {0x12470, 0x12473, 1}, }, + LatinOffset: 11, } var _Pc = &RangeTable{ @@ -2022,6 +2169,7 @@ var _Pd = &RangeTable{ {0x1806, 0x2010, 2058}, {0x2011, 0x2015, 1}, {0x2e17, 0x2e1a, 3}, + {0x2e3a, 0x2e3b, 1}, {0x301c, 0x3030, 20}, {0x30a0, 0xfe31, 52625}, {0xfe32, 0xfe58, 38}, @@ -2053,6 +2201,7 @@ var _Pe = &RangeTable{ {0xff09, 0xff3d, 52}, {0xff5d, 0xff63, 3}, }, + LatinOffset: 1, } var _Pf = &RangeTable{ @@ -2084,7 +2233,8 @@ var _Po = &RangeTable{ {0x002f, 0x003a, 11}, {0x003b, 0x003f, 4}, {0x0040, 0x005c, 28}, - {0x00a1, 0x00b7, 22}, + {0x00a1, 0x00a7, 6}, + {0x00b6, 0x00b7, 1}, {0x00bf, 0x037e, 703}, {0x0387, 0x055a, 467}, {0x055b, 0x055f, 1}, @@ -2102,15 +2252,16 @@ var _Po = &RangeTable{ {0x0830, 0x083e, 1}, {0x085e, 0x0964, 262}, {0x0965, 0x0970, 11}, - {0x0df4, 0x0e4f, 91}, - {0x0e5a, 0x0e5b, 1}, - {0x0f04, 0x0f12, 1}, - {0x0f85, 0x0fd0, 75}, - {0x0fd1, 0x0fd4, 1}, + {0x0af0, 0x0df4, 772}, + {0x0e4f, 0x0e5a, 11}, + {0x0e5b, 0x0f04, 169}, + {0x0f05, 0x0f12, 1}, + {0x0f14, 0x0f85, 113}, + {0x0fd0, 0x0fd4, 1}, {0x0fd9, 0x0fda, 1}, {0x104a, 0x104f, 1}, - {0x10fb, 0x1361, 614}, - {0x1362, 0x1368, 1}, + {0x10fb, 0x1360, 613}, + {0x1361, 0x1368, 1}, {0x166d, 0x166e, 1}, {0x16eb, 0x16ed, 1}, {0x1735, 0x1736, 1}, @@ -2126,6 +2277,7 @@ var _Po = &RangeTable{ {0x1bfc, 0x1bff, 1}, {0x1c3b, 0x1c3f, 1}, {0x1c7e, 0x1c7f, 1}, + {0x1cc0, 0x1cc7, 1}, {0x1cd3, 0x2016, 835}, {0x2017, 0x2020, 9}, {0x2021, 0x2027, 1}, @@ -2146,7 +2298,7 @@ var _Po = &RangeTable{ {0x2e1b, 0x2e1e, 3}, {0x2e1f, 0x2e2a, 11}, {0x2e2b, 0x2e2e, 1}, - {0x2e30, 0x2e31, 1}, + {0x2e30, 0x2e39, 1}, {0x3001, 0x3003, 1}, {0x303d, 0x30fb, 190}, {0xa4fe, 0xa4ff, 1}, @@ -2162,6 +2314,7 @@ var _Po = &RangeTable{ {0xa9de, 0xa9df, 1}, {0xaa5c, 0xaa5f, 1}, {0xaade, 0xaadf, 1}, + {0xaaf0, 0xaaf1, 1}, {0xabeb, 0xfe10, 21029}, {0xfe11, 0xfe16, 1}, {0xfe19, 0xfe30, 23}, @@ -2183,17 +2336,21 @@ var _Po = &RangeTable{ }, R32: []Range32{ {0x10100, 0x10100, 1}, - {0x10101, 0x1039f, 670}, - {0x103d0, 0x10857, 1159}, - {0x1091f, 0x1093f, 32}, - {0x10a50, 0x10a58, 1}, + {0x10101, 0x10102, 1}, + {0x1039f, 0x103d0, 49}, + {0x10857, 0x1091f, 200}, + {0x1093f, 0x10a50, 273}, + {0x10a51, 0x10a58, 1}, {0x10a7f, 0x10b39, 186}, {0x10b3a, 0x10b3f, 1}, {0x11047, 0x1104d, 1}, {0x110bb, 0x110bc, 1}, {0x110be, 0x110c1, 1}, + {0x11140, 0x11143, 1}, + {0x111c5, 0x111c8, 1}, {0x12470, 0x12473, 1}, }, + LatinOffset: 8, } var _Ps = &RangeTable{ @@ -2222,6 +2379,7 @@ var _Ps = &RangeTable{ {0xff5b, 0xff5f, 4}, {0xff62, 0xff62, 1}, }, + LatinOffset: 1, } var _S = &RangeTable{ @@ -2230,10 +2388,11 @@ var _S = &RangeTable{ {0x003c, 0x003e, 1}, {0x005e, 0x0060, 2}, {0x007c, 0x007e, 2}, - {0x00a2, 0x00a9, 1}, + {0x00a2, 0x00a6, 1}, + {0x00a8, 0x00a9, 1}, {0x00ac, 0x00ae, 2}, {0x00af, 0x00b1, 1}, - {0x00b4, 0x00b8, 2}, + {0x00b4, 0x00b8, 4}, {0x00d7, 0x00f7, 32}, {0x02c2, 0x02c5, 1}, {0x02d2, 0x02df, 1}, @@ -2242,8 +2401,8 @@ var _S = &RangeTable{ {0x02f0, 0x02ff, 1}, {0x0375, 0x0384, 15}, {0x0385, 0x03f6, 113}, - {0x0482, 0x0606, 388}, - {0x0607, 0x0608, 1}, + {0x0482, 0x058f, 269}, + {0x0606, 0x0608, 1}, {0x060b, 0x060e, 3}, {0x060f, 0x06de, 207}, {0x06e9, 0x06fd, 20}, @@ -2255,7 +2414,8 @@ var _S = &RangeTable{ {0x0c7f, 0x0d79, 250}, {0x0e3f, 0x0f01, 194}, {0x0f02, 0x0f03, 1}, - {0x0f13, 0x0f17, 1}, + {0x0f13, 0x0f15, 2}, + {0x0f16, 0x0f17, 1}, {0x0f1a, 0x0f1f, 1}, {0x0f34, 0x0f38, 2}, {0x0fbe, 0x0fc5, 1}, @@ -2263,8 +2423,7 @@ var _S = &RangeTable{ {0x0fce, 0x0fcf, 1}, {0x0fd5, 0x0fd8, 1}, {0x109e, 0x109f, 1}, - {0x1360, 0x1390, 48}, - {0x1391, 0x1399, 1}, + {0x1390, 0x1399, 1}, {0x17db, 0x1940, 357}, {0x19de, 0x19ff, 1}, {0x1b61, 0x1b6a, 1}, @@ -2278,7 +2437,7 @@ var _S = &RangeTable{ {0x2044, 0x2052, 14}, {0x207a, 0x207c, 1}, {0x208a, 0x208c, 1}, - {0x20a0, 0x20b9, 1}, + {0x20a0, 0x20ba, 1}, {0x2100, 0x2101, 1}, {0x2103, 0x2106, 1}, {0x2108, 0x2109, 1}, @@ -2299,9 +2458,7 @@ var _S = &RangeTable{ {0x2500, 0x26ff, 1}, {0x2701, 0x2767, 1}, {0x2794, 0x27c4, 1}, - {0x27c7, 0x27ca, 1}, - {0x27cc, 0x27ce, 2}, - {0x27cf, 0x27e5, 1}, + {0x27c7, 0x27e5, 1}, {0x27f0, 0x2982, 1}, {0x2999, 0x29d7, 1}, {0x29dc, 0x29fb, 1}, @@ -2321,8 +2478,9 @@ var _S = &RangeTable{ {0x3196, 0x319f, 1}, {0x31c0, 0x31e3, 1}, {0x3200, 0x321e, 1}, - {0x322a, 0x3250, 1}, - {0x3260, 0x327f, 1}, + {0x322a, 0x3247, 1}, + {0x3250, 0x3260, 16}, + {0x3261, 0x327f, 1}, {0x328a, 0x32b0, 1}, {0x32c0, 0x32fe, 1}, {0x3300, 0x33ff, 1}, @@ -2349,8 +2507,7 @@ var _S = &RangeTable{ {0xfffc, 0xfffd, 1}, }, R32: []Range32{ - {0x10102, 0x10137, 53}, - {0x10138, 0x1013f, 1}, + {0x10137, 0x1013f, 1}, {0x10179, 0x10189, 1}, {0x10190, 0x1019b, 1}, {0x101d0, 0x101fc, 1}, @@ -2369,6 +2526,7 @@ var _S = &RangeTable{ {0x1d735, 0x1d74f, 26}, {0x1d76f, 0x1d789, 26}, {0x1d7a9, 0x1d7c3, 26}, + {0x1eef0, 0x1eef1, 1}, {0x1f000, 0x1f02b, 1}, {0x1f030, 0x1f093, 1}, {0x1f0a0, 0x1f0ae, 1}, @@ -2376,7 +2534,7 @@ var _S = &RangeTable{ {0x1f0c1, 0x1f0cf, 1}, {0x1f0d1, 0x1f0df, 1}, {0x1f110, 0x1f12e, 1}, - {0x1f130, 0x1f169, 1}, + {0x1f130, 0x1f16b, 1}, {0x1f170, 0x1f19a, 1}, {0x1f1e6, 0x1f202, 1}, {0x1f210, 0x1f23a, 1}, @@ -2394,37 +2552,32 @@ var _S = &RangeTable{ {0x1f443, 0x1f4f7, 1}, {0x1f4f9, 0x1f4fc, 1}, {0x1f500, 0x1f53d, 1}, + {0x1f540, 0x1f543, 1}, {0x1f550, 0x1f567, 1}, - {0x1f5fb, 0x1f5ff, 1}, - {0x1f601, 0x1f610, 1}, - {0x1f612, 0x1f614, 1}, - {0x1f616, 0x1f61c, 2}, - {0x1f61d, 0x1f61e, 1}, - {0x1f620, 0x1f625, 1}, - {0x1f628, 0x1f62b, 1}, - {0x1f62d, 0x1f630, 3}, - {0x1f631, 0x1f633, 1}, - {0x1f635, 0x1f640, 1}, + {0x1f5fb, 0x1f640, 1}, {0x1f645, 0x1f64f, 1}, {0x1f680, 0x1f6c5, 1}, {0x1f700, 0x1f773, 1}, }, + LatinOffset: 10, } var _Sc = &RangeTable{ R16: []Range16{ {0x0024, 0x00a2, 126}, {0x00a3, 0x00a5, 1}, - {0x060b, 0x09f2, 999}, - {0x09f3, 0x09fb, 8}, - {0x0af1, 0x0bf9, 264}, - {0x0e3f, 0x17db, 2460}, - {0x20a0, 0x20b9, 1}, + {0x058f, 0x060b, 124}, + {0x09f2, 0x09f3, 1}, + {0x09fb, 0x0af1, 246}, + {0x0bf9, 0x0e3f, 582}, + {0x17db, 0x20a0, 2245}, + {0x20a1, 0x20ba, 1}, {0xa838, 0xfdfc, 21956}, {0xfe69, 0xff04, 155}, {0xffe0, 0xffe1, 1}, {0xffe5, 0xffe6, 1}, }, + LatinOffset: 2, } var _Sk = &RangeTable{ @@ -2452,6 +2605,7 @@ var _Sk = &RangeTable{ {0xff3e, 0xff40, 2}, {0xffe3, 0xffe3, 1}, }, + LatinOffset: 3, } var _Sm = &RangeTable{ @@ -2485,9 +2639,7 @@ var _Sm = &RangeTable{ {0x25f8, 0x25ff, 1}, {0x266f, 0x27c0, 337}, {0x27c1, 0x27c4, 1}, - {0x27c7, 0x27ca, 1}, - {0x27cc, 0x27ce, 2}, - {0x27cf, 0x27e5, 1}, + {0x27c7, 0x27e5, 1}, {0x27f0, 0x27ff, 1}, {0x2900, 0x2982, 1}, {0x2999, 0x29d7, 1}, @@ -2509,14 +2661,15 @@ var _Sm = &RangeTable{ {0x1d735, 0x1d74f, 26}, {0x1d76f, 0x1d789, 26}, {0x1d7a9, 0x1d7c3, 26}, + {0x1eef0, 0x1eef1, 1}, }, + LatinOffset: 5, } var _So = &RangeTable{ R16: []Range16{ - {0x00a6, 0x00a7, 1}, - {0x00a9, 0x00ae, 5}, - {0x00b0, 0x00b6, 6}, + {0x00a6, 0x00a9, 3}, + {0x00ae, 0x00b0, 2}, {0x0482, 0x060e, 396}, {0x060f, 0x06de, 207}, {0x06e9, 0x06fd, 20}, @@ -2526,7 +2679,8 @@ var _So = &RangeTable{ {0x0bfa, 0x0c7f, 133}, {0x0d79, 0x0f01, 392}, {0x0f02, 0x0f03, 1}, - {0x0f13, 0x0f17, 1}, + {0x0f13, 0x0f15, 2}, + {0x0f16, 0x0f17, 1}, {0x0f1a, 0x0f1f, 1}, {0x0f34, 0x0f38, 2}, {0x0fbe, 0x0fc5, 1}, @@ -2534,8 +2688,7 @@ var _So = &RangeTable{ {0x0fce, 0x0fcf, 1}, {0x0fd5, 0x0fd8, 1}, {0x109e, 0x109f, 1}, - {0x1360, 0x1390, 48}, - {0x1391, 0x1399, 1}, + {0x1390, 0x1399, 1}, {0x1940, 0x19de, 158}, {0x19df, 0x19ff, 1}, {0x1b61, 0x1b6a, 1}, @@ -2594,8 +2747,9 @@ var _So = &RangeTable{ {0x3196, 0x319f, 1}, {0x31c0, 0x31e3, 1}, {0x3200, 0x321e, 1}, - {0x322a, 0x3250, 1}, - {0x3260, 0x327f, 1}, + {0x322a, 0x3247, 1}, + {0x3250, 0x3260, 16}, + {0x3261, 0x327f, 1}, {0x328a, 0x32b0, 1}, {0x32c0, 0x32fe, 1}, {0x3300, 0x33ff, 1}, @@ -2611,8 +2765,8 @@ var _So = &RangeTable{ {0xfffd, 0xfffd, 1}, }, R32: []Range32{ - {0x10102, 0x10102, 1}, - {0x10137, 0x1013f, 1}, + {0x10137, 0x10137, 1}, + {0x10138, 0x1013f, 1}, {0x10179, 0x10189, 1}, {0x10190, 0x1019b, 1}, {0x101d0, 0x101fc, 1}, @@ -2633,7 +2787,7 @@ var _So = &RangeTable{ {0x1f0c1, 0x1f0cf, 1}, {0x1f0d1, 0x1f0df, 1}, {0x1f110, 0x1f12e, 1}, - {0x1f130, 0x1f169, 1}, + {0x1f130, 0x1f16b, 1}, {0x1f170, 0x1f19a, 1}, {0x1f1e6, 0x1f202, 1}, {0x1f210, 0x1f23a, 1}, @@ -2651,21 +2805,14 @@ var _So = &RangeTable{ {0x1f443, 0x1f4f7, 1}, {0x1f4f9, 0x1f4fc, 1}, {0x1f500, 0x1f53d, 1}, + {0x1f540, 0x1f543, 1}, {0x1f550, 0x1f567, 1}, - {0x1f5fb, 0x1f5ff, 1}, - {0x1f601, 0x1f610, 1}, - {0x1f612, 0x1f614, 1}, - {0x1f616, 0x1f61c, 2}, - {0x1f61d, 0x1f61e, 1}, - {0x1f620, 0x1f625, 1}, - {0x1f628, 0x1f62b, 1}, - {0x1f62d, 0x1f630, 3}, - {0x1f631, 0x1f633, 1}, - {0x1f635, 0x1f640, 1}, + {0x1f5fb, 0x1f640, 1}, {0x1f645, 0x1f64f, 1}, {0x1f680, 0x1f6c5, 1}, {0x1f700, 0x1f773, 1}, }, + LatinOffset: 2, } var _Z = &RangeTable{ @@ -2677,6 +2824,7 @@ var _Z = &RangeTable{ {0x202f, 0x205f, 48}, {0x3000, 0x3000, 1}, }, + LatinOffset: 1, } var _Zl = &RangeTable{ @@ -2699,6 +2847,7 @@ var _Zs = &RangeTable{ {0x202f, 0x205f, 48}, {0x3000, 0x3000, 1}, }, + LatinOffset: 1, } // These variables have type *RangeTable. @@ -2753,7 +2902,7 @@ var ( ) // Generated by running -// maketables --scripts=all --url=http://www.unicode.org/Public/6.0.0/ucd/ +// maketables --scripts=all --url=http://www.unicode.org/Public/6.2.0/ucd/ // DO NOT EDIT // Scripts is the set of Unicode script tables. @@ -2772,6 +2921,7 @@ var Scripts = map[string]*RangeTable{ "Buhid": Buhid, "Canadian_Aboriginal": Canadian_Aboriginal, "Carian": Carian, + "Chakma": Chakma, "Cham": Cham, "Cherokee": Cherokee, "Common": Common, @@ -2816,6 +2966,9 @@ var Scripts = map[string]*RangeTable{ "Malayalam": Malayalam, "Mandaic": Mandaic, "Meetei_Mayek": Meetei_Mayek, + "Meroitic_Cursive": Meroitic_Cursive, + "Meroitic_Hieroglyphs": Meroitic_Hieroglyphs, + "Miao": Miao, "Mongolian": Mongolian, "Myanmar": Myanmar, "New_Tai_Lue": New_Tai_Lue, @@ -2834,8 +2987,10 @@ var Scripts = map[string]*RangeTable{ "Runic": Runic, "Samaritan": Samaritan, "Saurashtra": Saurashtra, + "Sharada": Sharada, "Shavian": Shavian, "Sinhala": Sinhala, + "Sora_Sompeng": Sora_Sompeng, "Sundanese": Sundanese, "Syloti_Nagri": Syloti_Nagri, "Syriac": Syriac, @@ -2844,6 +2999,7 @@ var Scripts = map[string]*RangeTable{ "Tai_Le": Tai_Le, "Tai_Tham": Tai_Tham, "Tai_Viet": Tai_Viet, + "Takri": Takri, "Tamil": Tamil, "Telugu": Telugu, "Thaana": Thaana, @@ -2857,17 +3013,20 @@ var Scripts = map[string]*RangeTable{ var _Arabic = &RangeTable{ R16: []Range16{ - {0x0600, 0x0603, 1}, + {0x0600, 0x0604, 1}, {0x0606, 0x060b, 1}, {0x060d, 0x061a, 1}, {0x061e, 0x061e, 1}, {0x0620, 0x063f, 1}, {0x0641, 0x064a, 1}, - {0x0656, 0x065e, 1}, + {0x0656, 0x065f, 1}, {0x066a, 0x066f, 1}, {0x0671, 0x06dc, 1}, {0x06de, 0x06ff, 1}, {0x0750, 0x077f, 1}, + {0x08a0, 0x08a0, 1}, + {0x08a2, 0x08ac, 1}, + {0x08e4, 0x08fe, 1}, {0xfb50, 0xfbc1, 1}, {0xfbd3, 0xfd3d, 1}, {0xfd50, 0xfd8f, 1}, @@ -2878,6 +3037,40 @@ var _Arabic = &RangeTable{ }, R32: []Range32{ {0x10e60, 0x10e7e, 1}, + {0x1ee00, 0x1ee03, 1}, + {0x1ee05, 0x1ee1f, 1}, + {0x1ee21, 0x1ee22, 1}, + {0x1ee24, 0x1ee24, 1}, + {0x1ee27, 0x1ee27, 1}, + {0x1ee29, 0x1ee32, 1}, + {0x1ee34, 0x1ee37, 1}, + {0x1ee39, 0x1ee39, 1}, + {0x1ee3b, 0x1ee3b, 1}, + {0x1ee42, 0x1ee42, 1}, + {0x1ee47, 0x1ee47, 1}, + {0x1ee49, 0x1ee49, 1}, + {0x1ee4b, 0x1ee4b, 1}, + {0x1ee4d, 0x1ee4f, 1}, + {0x1ee51, 0x1ee52, 1}, + {0x1ee54, 0x1ee54, 1}, + {0x1ee57, 0x1ee57, 1}, + {0x1ee59, 0x1ee59, 1}, + {0x1ee5b, 0x1ee5b, 1}, + {0x1ee5d, 0x1ee5d, 1}, + {0x1ee5f, 0x1ee5f, 1}, + {0x1ee61, 0x1ee62, 1}, + {0x1ee64, 0x1ee64, 1}, + {0x1ee67, 0x1ee6a, 1}, + {0x1ee6c, 0x1ee72, 1}, + {0x1ee74, 0x1ee77, 1}, + {0x1ee79, 0x1ee7c, 1}, + {0x1ee7e, 0x1ee7e, 1}, + {0x1ee80, 0x1ee89, 1}, + {0x1ee8b, 0x1ee9b, 1}, + {0x1eea1, 0x1eea3, 1}, + {0x1eea5, 0x1eea9, 1}, + {0x1eeab, 0x1eebb, 1}, + {0x1eef0, 0x1eef1, 1}, }, } @@ -2887,6 +3080,7 @@ var _Armenian = &RangeTable{ {0x0559, 0x055f, 1}, {0x0561, 0x0587, 1}, {0x058a, 0x058a, 1}, + {0x058f, 0x058f, 1}, {0xfb13, 0xfb17, 1}, }, } @@ -2990,6 +3184,14 @@ var _Carian = &RangeTable{ }, } +var _Chakma = &RangeTable{ + R16: []Range16{}, + R32: []Range32{ + {0x11100, 0x11134, 1}, + {0x11136, 0x11143, 1}, + }, +} + var _Cham = &RangeTable{ R16: []Range16{ {0xaa00, 0xaa36, 1}, @@ -3029,7 +3231,6 @@ var _Common = &RangeTable{ {0x0660, 0x0669, 1}, {0x06dd, 0x06dd, 1}, {0x0964, 0x0965, 1}, - {0x0970, 0x0970, 1}, {0x0e3f, 0x0e3f, 1}, {0x0fd5, 0x0fd8, 1}, {0x10fb, 0x10fb, 1}, @@ -3040,13 +3241,14 @@ var _Common = &RangeTable{ {0x1cd3, 0x1cd3, 1}, {0x1ce1, 0x1ce1, 1}, {0x1ce9, 0x1cec, 1}, - {0x1cee, 0x1cf2, 1}, + {0x1cee, 0x1cf3, 1}, + {0x1cf5, 0x1cf6, 1}, {0x2000, 0x200b, 1}, {0x200e, 0x2064, 1}, {0x206a, 0x2070, 1}, {0x2074, 0x207e, 1}, {0x2080, 0x208e, 1}, - {0x20a0, 0x20b9, 1}, + {0x20a0, 0x20ba, 1}, {0x2100, 0x2125, 1}, {0x2127, 0x2129, 1}, {0x212c, 0x2131, 1}, @@ -3057,12 +3259,10 @@ var _Common = &RangeTable{ {0x2400, 0x2426, 1}, {0x2440, 0x244a, 1}, {0x2460, 0x26ff, 1}, - {0x2701, 0x27ca, 1}, - {0x27cc, 0x27cc, 1}, - {0x27ce, 0x27ff, 1}, + {0x2701, 0x27ff, 1}, {0x2900, 0x2b4c, 1}, {0x2b50, 0x2b59, 1}, - {0x2e00, 0x2e31, 1}, + {0x2e00, 0x2e3b, 1}, {0x2ff0, 0x2ffb, 1}, {0x3000, 0x3004, 1}, {0x3006, 0x3006, 1}, @@ -3141,7 +3341,7 @@ var _Common = &RangeTable{ {0x1f0d1, 0x1f0df, 1}, {0x1f100, 0x1f10a, 1}, {0x1f110, 0x1f12e, 1}, - {0x1f130, 0x1f169, 1}, + {0x1f130, 0x1f16b, 1}, {0x1f170, 0x1f19a, 1}, {0x1f1e6, 0x1f1ff, 1}, {0x1f201, 0x1f202, 1}, @@ -3160,31 +3360,22 @@ var _Common = &RangeTable{ {0x1f442, 0x1f4f7, 1}, {0x1f4f9, 0x1f4fc, 1}, {0x1f500, 0x1f53d, 1}, + {0x1f540, 0x1f543, 1}, {0x1f550, 0x1f567, 1}, - {0x1f5fb, 0x1f5ff, 1}, - {0x1f601, 0x1f610, 1}, - {0x1f612, 0x1f614, 1}, - {0x1f616, 0x1f616, 1}, - {0x1f618, 0x1f618, 1}, - {0x1f61a, 0x1f61a, 1}, - {0x1f61c, 0x1f61e, 1}, - {0x1f620, 0x1f625, 1}, - {0x1f628, 0x1f62b, 1}, - {0x1f62d, 0x1f62d, 1}, - {0x1f630, 0x1f633, 1}, - {0x1f635, 0x1f640, 1}, + {0x1f5fb, 0x1f640, 1}, {0x1f645, 0x1f64f, 1}, {0x1f680, 0x1f6c5, 1}, {0x1f700, 0x1f773, 1}, {0xe0001, 0xe0001, 1}, {0xe0020, 0xe007f, 1}, }, + LatinOffset: 7, } var _Coptic = &RangeTable{ R16: []Range16{ {0x03e2, 0x03ef, 1}, - {0x2c80, 0x2cf1, 1}, + {0x2c80, 0x2cf3, 1}, {0x2cf9, 0x2cff, 1}, }, } @@ -3217,8 +3408,8 @@ var _Cyrillic = &RangeTable{ {0x1d2b, 0x1d2b, 1}, {0x1d78, 0x1d78, 1}, {0x2de0, 0x2dff, 1}, - {0xa640, 0xa673, 1}, - {0xa67c, 0xa697, 1}, + {0xa640, 0xa697, 1}, + {0xa69f, 0xa69f, 1}, }, } @@ -3233,8 +3424,7 @@ var _Devanagari = &RangeTable{ R16: []Range16{ {0x0900, 0x0950, 1}, {0x0953, 0x0963, 1}, - {0x0966, 0x096f, 1}, - {0x0971, 0x0977, 1}, + {0x0966, 0x0977, 1}, {0x0979, 0x097f, 1}, {0xa8e0, 0xa8fb, 1}, }, @@ -3287,9 +3477,13 @@ var _Ethiopic = &RangeTable{ var _Georgian = &RangeTable{ R16: []Range16{ {0x10a0, 0x10c5, 1}, + {0x10c7, 0x10c7, 1}, + {0x10cd, 0x10cd, 1}, {0x10d0, 0x10fa, 1}, - {0x10fc, 0x10fc, 1}, + {0x10fc, 0x10ff, 1}, {0x2d00, 0x2d25, 1}, + {0x2d27, 0x2d27, 1}, + {0x2d2d, 0x2d2d, 1}, }, } @@ -3361,8 +3555,7 @@ var _Gujarati = &RangeTable{ {0x0acb, 0x0acd, 1}, {0x0ad0, 0x0ad0, 1}, {0x0ae0, 0x0ae3, 1}, - {0x0ae6, 0x0aef, 1}, - {0x0af1, 0x0af1, 1}, + {0x0ae6, 0x0af1, 1}, }, } @@ -3397,9 +3590,8 @@ var _Han = &RangeTable{ {0x3021, 0x3029, 1}, {0x3038, 0x303b, 1}, {0x3400, 0x4db5, 1}, - {0x4e00, 0x9fcb, 1}, - {0xf900, 0xfa2d, 1}, - {0xfa30, 0xfa6d, 1}, + {0x4e00, 0x9fcc, 1}, + {0xf900, 0xfa6d, 1}, {0xfa70, 0xfad9, 1}, }, R32: []Range32{ @@ -3473,13 +3665,13 @@ var _Inherited = &RangeTable{ {0x0300, 0x036f, 1}, {0x0485, 0x0486, 1}, {0x064b, 0x0655, 1}, - {0x065f, 0x065f, 1}, {0x0670, 0x0670, 1}, {0x0951, 0x0952, 1}, {0x1cd0, 0x1cd2, 1}, {0x1cd4, 0x1ce0, 1}, {0x1ce2, 0x1ce8, 1}, {0x1ced, 0x1ced, 1}, + {0x1cf4, 0x1cf4, 1}, {0x1dc0, 0x1de6, 1}, {0x1dfc, 0x1dff, 1}, {0x200c, 0x200d, 1}, @@ -3612,7 +3804,7 @@ var _Lao = &RangeTable{ {0x0ec6, 0x0ec6, 1}, {0x0ec8, 0x0ecd, 1}, {0x0ed0, 0x0ed9, 1}, - {0x0edc, 0x0edd, 1}, + {0x0edc, 0x0edf, 1}, }, } @@ -3642,13 +3834,14 @@ var _Latin = &RangeTable{ {0x2c60, 0x2c7f, 1}, {0xa722, 0xa787, 1}, {0xa78b, 0xa78e, 1}, - {0xa790, 0xa791, 1}, - {0xa7a0, 0xa7a9, 1}, - {0xa7fa, 0xa7ff, 1}, + {0xa790, 0xa793, 1}, + {0xa7a0, 0xa7aa, 1}, + {0xa7f8, 0xa7ff, 1}, {0xfb00, 0xfb06, 1}, {0xff21, 0xff3a, 1}, {0xff41, 0xff5a, 1}, }, + LatinOffset: 6, } var _Lepcha = &RangeTable{ @@ -3728,11 +3921,36 @@ var _Mandaic = &RangeTable{ var _Meetei_Mayek = &RangeTable{ R16: []Range16{ + {0xaae0, 0xaaf6, 1}, {0xabc0, 0xabed, 1}, {0xabf0, 0xabf9, 1}, }, } +var _Meroitic_Cursive = &RangeTable{ + R16: []Range16{}, + R32: []Range32{ + {0x109a0, 0x109b7, 1}, + {0x109be, 0x109bf, 1}, + }, +} + +var _Meroitic_Hieroglyphs = &RangeTable{ + R16: []Range16{}, + R32: []Range32{ + {0x10980, 0x1099f, 1}, + }, +} + +var _Miao = &RangeTable{ + R16: []Range16{}, + R32: []Range32{ + {0x16f00, 0x16f44, 1}, + {0x16f50, 0x16f7e, 1}, + {0x16f8f, 0x16f9f, 1}, + }, +} + var _Mongolian = &RangeTable{ R16: []Range16{ {0x1800, 0x1801, 1}, @@ -3877,6 +4095,14 @@ var _Saurashtra = &RangeTable{ }, } +var _Sharada = &RangeTable{ + R16: []Range16{}, + R32: []Range32{ + {0x11180, 0x111c8, 1}, + {0x111d0, 0x111d9, 1}, + }, +} + var _Shavian = &RangeTable{ R16: []Range16{}, R32: []Range32{ @@ -3900,10 +4126,18 @@ var _Sinhala = &RangeTable{ }, } +var _Sora_Sompeng = &RangeTable{ + R16: []Range16{}, + R32: []Range32{ + {0x110d0, 0x110e8, 1}, + {0x110f0, 0x110f9, 1}, + }, +} + var _Sundanese = &RangeTable{ R16: []Range16{ - {0x1b80, 0x1baa, 1}, - {0x1bae, 0x1bb9, 1}, + {0x1b80, 0x1bbf, 1}, + {0x1cc0, 0x1cc7, 1}, }, } @@ -3960,6 +4194,14 @@ var _Tai_Viet = &RangeTable{ }, } +var _Takri = &RangeTable{ + R16: []Range16{}, + R32: []Range32{ + {0x11680, 0x116b7, 1}, + {0x116c0, 0x116c9, 1}, + }, +} + var _Tamil = &RangeTable{ R16: []Range16{ {0x0b82, 0x0b83, 1}, @@ -4027,7 +4269,7 @@ var _Tibetan = &RangeTable{ var _Tifinagh = &RangeTable{ R16: []Range16{ - {0x2d30, 0x2d65, 1}, + {0x2d30, 0x2d67, 1}, {0x2d6f, 0x2d70, 1}, {0x2d7f, 0x2d7f, 1}, }, @@ -4070,6 +4312,7 @@ var ( Buhid = _Buhid // Buhid is the set of Unicode characters in script Buhid. Canadian_Aboriginal = _Canadian_Aboriginal // Canadian_Aboriginal is the set of Unicode characters in script Canadian_Aboriginal. Carian = _Carian // Carian is the set of Unicode characters in script Carian. + Chakma = _Chakma // Chakma is the set of Unicode characters in script Chakma. Cham = _Cham // Cham is the set of Unicode characters in script Cham. Cherokee = _Cherokee // Cherokee is the set of Unicode characters in script Cherokee. Common = _Common // Common is the set of Unicode characters in script Common. @@ -4114,6 +4357,9 @@ var ( Malayalam = _Malayalam // Malayalam is the set of Unicode characters in script Malayalam. Mandaic = _Mandaic // Mandaic is the set of Unicode characters in script Mandaic. Meetei_Mayek = _Meetei_Mayek // Meetei_Mayek is the set of Unicode characters in script Meetei_Mayek. + Meroitic_Cursive = _Meroitic_Cursive // Meroitic_Cursive is the set of Unicode characters in script Meroitic_Cursive. + Meroitic_Hieroglyphs = _Meroitic_Hieroglyphs // Meroitic_Hieroglyphs is the set of Unicode characters in script Meroitic_Hieroglyphs. + Miao = _Miao // Miao is the set of Unicode characters in script Miao. Mongolian = _Mongolian // Mongolian is the set of Unicode characters in script Mongolian. Myanmar = _Myanmar // Myanmar is the set of Unicode characters in script Myanmar. New_Tai_Lue = _New_Tai_Lue // New_Tai_Lue is the set of Unicode characters in script New_Tai_Lue. @@ -4132,8 +4378,10 @@ var ( Runic = _Runic // Runic is the set of Unicode characters in script Runic. Samaritan = _Samaritan // Samaritan is the set of Unicode characters in script Samaritan. Saurashtra = _Saurashtra // Saurashtra is the set of Unicode characters in script Saurashtra. + Sharada = _Sharada // Sharada is the set of Unicode characters in script Sharada. Shavian = _Shavian // Shavian is the set of Unicode characters in script Shavian. Sinhala = _Sinhala // Sinhala is the set of Unicode characters in script Sinhala. + Sora_Sompeng = _Sora_Sompeng // Sora_Sompeng is the set of Unicode characters in script Sora_Sompeng. Sundanese = _Sundanese // Sundanese is the set of Unicode characters in script Sundanese. Syloti_Nagri = _Syloti_Nagri // Syloti_Nagri is the set of Unicode characters in script Syloti_Nagri. Syriac = _Syriac // Syriac is the set of Unicode characters in script Syriac. @@ -4142,6 +4390,7 @@ var ( Tai_Le = _Tai_Le // Tai_Le is the set of Unicode characters in script Tai_Le. Tai_Tham = _Tai_Tham // Tai_Tham is the set of Unicode characters in script Tai_Tham. Tai_Viet = _Tai_Viet // Tai_Viet is the set of Unicode characters in script Tai_Viet. + Takri = _Takri // Takri is the set of Unicode characters in script Takri. Tamil = _Tamil // Tamil is the set of Unicode characters in script Tamil. Telugu = _Telugu // Telugu is the set of Unicode characters in script Telugu. Thaana = _Thaana // Thaana is the set of Unicode characters in script Thaana. @@ -4154,7 +4403,7 @@ var ( ) // Generated by running -// maketables --props=all --url=http://www.unicode.org/Public/6.0.0/ucd/ +// maketables --props=all --url=http://www.unicode.org/Public/6.2.0/ucd/ // DO NOT EDIT // Properties is the set of Unicode property tables. @@ -4199,6 +4448,7 @@ var _ASCII_Hex_Digit = &RangeTable{ {0x0041, 0x0046, 1}, {0x0061, 0x0066, 1}, }, + LatinOffset: 3, } var _Bidi_Control = &RangeTable{ @@ -4222,6 +4472,7 @@ var _Dash = &RangeTable{ {0x2212, 0x2212, 1}, {0x2e17, 0x2e17, 1}, {0x2e1a, 0x2e1a, 1}, + {0x2e3a, 0x2e3b, 1}, {0x301c, 0x301c, 1}, {0x3030, 0x3030, 1}, {0x30a0, 0x30a0, 1}, @@ -4230,6 +4481,7 @@ var _Dash = &RangeTable{ {0xfe63, 0xfe63, 1}, {0xff0d, 0xff0d, 1}, }, + LatinOffset: 1, } var _Deprecated = &RangeTable{ @@ -4278,6 +4530,7 @@ var _Diacritic = &RangeTable{ {0x07a6, 0x07b0, 1}, {0x07eb, 0x07f5, 1}, {0x0818, 0x0819, 1}, + {0x08e4, 0x08fe, 1}, {0x093c, 0x093c, 1}, {0x094d, 0x094d, 1}, {0x0951, 0x0954, 1}, @@ -4320,11 +4573,12 @@ var _Diacritic = &RangeTable{ {0x1b34, 0x1b34, 1}, {0x1b44, 0x1b44, 1}, {0x1b6b, 0x1b73, 1}, - {0x1baa, 0x1baa, 1}, + {0x1baa, 0x1bab, 1}, {0x1c36, 0x1c37, 1}, {0x1c78, 0x1c7d, 1}, {0x1cd0, 0x1ce8, 1}, {0x1ced, 0x1ced, 1}, + {0x1cf4, 0x1cf4, 1}, {0x1d2c, 0x1d6a, 1}, {0x1dc4, 0x1dcf, 1}, {0x1dfd, 0x1dff, 1}, @@ -4345,6 +4599,7 @@ var _Diacritic = &RangeTable{ {0xa6f0, 0xa6f1, 1}, {0xa717, 0xa721, 1}, {0xa788, 0xa788, 1}, + {0xa7f8, 0xa7f9, 1}, {0xa8c4, 0xa8c4, 1}, {0xa8e0, 0xa8f1, 1}, {0xa92b, 0xa92e, 1}, @@ -4353,6 +4608,7 @@ var _Diacritic = &RangeTable{ {0xa9c0, 0xa9c0, 1}, {0xaa7b, 0xaa7b, 1}, {0xaabf, 0xaac2, 1}, + {0xaaf6, 0xaaf6, 1}, {0xabec, 0xabed, 1}, {0xfb1e, 0xfb1e, 1}, {0xfe20, 0xfe26, 1}, @@ -4364,12 +4620,17 @@ var _Diacritic = &RangeTable{ }, R32: []Range32{ {0x110b9, 0x110ba, 1}, + {0x11133, 0x11134, 1}, + {0x111c0, 0x111c0, 1}, + {0x116b6, 0x116b7, 1}, + {0x16f8f, 0x16f9f, 1}, {0x1d167, 0x1d169, 1}, {0x1d16d, 0x1d172, 1}, {0x1d17b, 0x1d182, 1}, {0x1d185, 0x1d18b, 1}, {0x1d1aa, 0x1d1ad, 1}, }, + LatinOffset: 6, } var _Extender = &RangeTable{ @@ -4380,6 +4641,7 @@ var _Extender = &RangeTable{ {0x07fa, 0x07fa, 1}, {0x0e46, 0x0e46, 1}, {0x0ec6, 0x0ec6, 1}, + {0x180a, 0x180a, 1}, {0x1843, 0x1843, 1}, {0x1aa7, 0x1aa7, 1}, {0x1c36, 0x1c36, 1}, @@ -4393,8 +4655,10 @@ var _Extender = &RangeTable{ {0xa9cf, 0xa9cf, 1}, {0xaa70, 0xaa70, 1}, {0xaadd, 0xaadd, 1}, + {0xaaf3, 0xaaf4, 1}, {0xff70, 0xff70, 1}, }, + LatinOffset: 1, } var _Hex_Digit = &RangeTable{ @@ -4406,6 +4670,7 @@ var _Hex_Digit = &RangeTable{ {0xff21, 0xff26, 1}, {0xff41, 0xff46, 1}, }, + LatinOffset: 3, } var _Hyphen = &RangeTable{ @@ -4421,6 +4686,7 @@ var _Hyphen = &RangeTable{ {0xff0d, 0xff0d, 1}, {0xff65, 0xff65, 1}, }, + LatinOffset: 2, } var _IDS_Binary_Operator = &RangeTable{ @@ -4442,9 +4708,8 @@ var _Ideographic = &RangeTable{ {0x3021, 0x3029, 1}, {0x3038, 0x303a, 1}, {0x3400, 0x4db5, 1}, - {0x4e00, 0x9fcb, 1}, - {0xf900, 0xfa2d, 1}, - {0xfa30, 0xfa6d, 1}, + {0x4e00, 0x9fcc, 1}, + {0xf900, 0xfa6d, 1}, {0xfa70, 0xfad9, 1}, }, R32: []Range32{ @@ -4519,6 +4784,8 @@ var _Other_Alphabetic = &RangeTable{ {0x081b, 0x0823, 1}, {0x0825, 0x0827, 1}, {0x0829, 0x082c, 1}, + {0x08e4, 0x08e9, 1}, + {0x08f0, 0x08fe, 1}, {0x0900, 0x0903, 1}, {0x093a, 0x093b, 1}, {0x093e, 0x094c, 1}, @@ -4615,11 +4882,14 @@ var _Other_Alphabetic = &RangeTable{ {0x1b35, 0x1b43, 1}, {0x1b80, 0x1b82, 1}, {0x1ba1, 0x1ba9, 1}, + {0x1bac, 0x1bad, 1}, {0x1be7, 0x1bf1, 1}, {0x1c24, 0x1c35, 1}, - {0x1cf2, 0x1cf2, 1}, + {0x1cf2, 0x1cf3, 1}, {0x24b6, 0x24e9, 1}, {0x2de0, 0x2dff, 1}, + {0xa674, 0xa67b, 1}, + {0xa69f, 0xa69f, 1}, {0xa823, 0xa827, 1}, {0xa880, 0xa881, 1}, {0xa8b4, 0xa8c3, 1}, @@ -4634,6 +4904,8 @@ var _Other_Alphabetic = &RangeTable{ {0xaab2, 0xaab4, 1}, {0xaab7, 0xaab8, 1}, {0xaabe, 0xaabe, 1}, + {0xaaeb, 0xaaef, 1}, + {0xaaf5, 0xaaf5, 1}, {0xabe3, 0xabea, 1}, {0xfb1e, 0xfb1e, 1}, }, @@ -4645,6 +4917,12 @@ var _Other_Alphabetic = &RangeTable{ {0x11038, 0x11045, 1}, {0x11082, 0x11082, 1}, {0x110b0, 0x110b8, 1}, + {0x11100, 0x11102, 1}, + {0x11127, 0x11132, 1}, + {0x11180, 0x11182, 1}, + {0x111b3, 0x111bf, 1}, + {0x116ab, 0x116b5, 1}, + {0x16f51, 0x16f7e, 1}, }, } @@ -4652,6 +4930,7 @@ var _Other_Default_Ignorable_Code_Point = &RangeTable{ R16: []Range16{ {0x034f, 0x034f, 1}, {0x115f, 0x1160, 1}, + {0x17b4, 0x17b5, 1}, {0x2065, 0x2069, 1}, {0x3164, 0x3164, 1}, {0xffa0, 0xffa0, 1}, @@ -4680,6 +4959,7 @@ var _Other_Grapheme_Extend = &RangeTable{ {0x0dcf, 0x0dcf, 1}, {0x0ddf, 0x0ddf, 1}, {0x200c, 0x200d, 1}, + {0x302e, 0x302f, 1}, {0xff9e, 0xff9f, 1}, }, R32: []Range32{ @@ -4695,6 +4975,7 @@ var _Other_ID_Continue = &RangeTable{ {0x1369, 0x1371, 1}, {0x19da, 0x19da, 1}, }, + LatinOffset: 1, } var _Other_ID_Start = &RangeTable{ @@ -4707,20 +4988,26 @@ var _Other_ID_Start = &RangeTable{ var _Other_Lowercase = &RangeTable{ R16: []Range16{ + {0x00aa, 0x00aa, 1}, + {0x00ba, 0x00ba, 1}, {0x02b0, 0x02b8, 1}, {0x02c0, 0x02c1, 1}, {0x02e0, 0x02e4, 1}, {0x0345, 0x0345, 1}, {0x037a, 0x037a, 1}, - {0x1d2c, 0x1d61, 1}, + {0x1d2c, 0x1d6a, 1}, {0x1d78, 0x1d78, 1}, {0x1d9b, 0x1dbf, 1}, - {0x2090, 0x2094, 1}, + {0x2071, 0x2071, 1}, + {0x207f, 0x207f, 1}, + {0x2090, 0x209c, 1}, {0x2170, 0x217f, 1}, {0x24d0, 0x24e9, 1}, - {0x2c7d, 0x2c7d, 1}, + {0x2c7c, 0x2c7d, 1}, {0xa770, 0xa770, 1}, + {0xa7f8, 0xa7f9, 1}, }, + LatinOffset: 2, } var _Other_Math = &RangeTable{ @@ -4827,7 +5114,41 @@ var _Other_Math = &RangeTable{ {0x1d7aa, 0x1d7c2, 1}, {0x1d7c4, 0x1d7cb, 1}, {0x1d7ce, 0x1d7ff, 1}, - }, + {0x1ee00, 0x1ee03, 1}, + {0x1ee05, 0x1ee1f, 1}, + {0x1ee21, 0x1ee22, 1}, + {0x1ee24, 0x1ee24, 1}, + {0x1ee27, 0x1ee27, 1}, + {0x1ee29, 0x1ee32, 1}, + {0x1ee34, 0x1ee37, 1}, + {0x1ee39, 0x1ee39, 1}, + {0x1ee3b, 0x1ee3b, 1}, + {0x1ee42, 0x1ee42, 1}, + {0x1ee47, 0x1ee47, 1}, + {0x1ee49, 0x1ee49, 1}, + {0x1ee4b, 0x1ee4b, 1}, + {0x1ee4d, 0x1ee4f, 1}, + {0x1ee51, 0x1ee52, 1}, + {0x1ee54, 0x1ee54, 1}, + {0x1ee57, 0x1ee57, 1}, + {0x1ee59, 0x1ee59, 1}, + {0x1ee5b, 0x1ee5b, 1}, + {0x1ee5d, 0x1ee5d, 1}, + {0x1ee5f, 0x1ee5f, 1}, + {0x1ee61, 0x1ee62, 1}, + {0x1ee64, 0x1ee64, 1}, + {0x1ee67, 0x1ee6a, 1}, + {0x1ee6c, 0x1ee72, 1}, + {0x1ee74, 0x1ee77, 1}, + {0x1ee79, 0x1ee7c, 1}, + {0x1ee7e, 0x1ee7e, 1}, + {0x1ee80, 0x1ee89, 1}, + {0x1ee8b, 0x1ee9b, 1}, + {0x1eea1, 0x1eea3, 1}, + {0x1eea5, 0x1eea9, 1}, + {0x1eeab, 0x1eebb, 1}, + }, + LatinOffset: 1, } var _Other_Uppercase = &RangeTable{ @@ -4868,6 +5189,7 @@ var _Pattern_Syntax = &RangeTable{ {0xfd3e, 0xfd3f, 1}, {0xfe45, 0xfe46, 1}, }, + LatinOffset: 15, } var _Pattern_White_Space = &RangeTable{ @@ -4878,6 +5200,7 @@ var _Pattern_White_Space = &RangeTable{ {0x200e, 0x200f, 1}, {0x2028, 0x2029, 1}, }, + LatinOffset: 3, } var _Quotation_Mark = &RangeTable{ @@ -4895,6 +5218,7 @@ var _Quotation_Mark = &RangeTable{ {0xff07, 0xff07, 1}, {0xff62, 0xff63, 1}, }, + LatinOffset: 4, } var _Radical = &RangeTable{ @@ -4944,6 +5268,7 @@ var _STerm = &RangeTable{ {0xa92f, 0xa92f, 1}, {0xa9c8, 0xa9c9, 1}, {0xaa5d, 0xaa5f, 1}, + {0xaaf0, 0xaaf1, 1}, {0xabeb, 0xabeb, 1}, {0xfe52, 0xfe52, 1}, {0xfe56, 0xfe57, 1}, @@ -4956,7 +5281,10 @@ var _STerm = &RangeTable{ {0x10a56, 0x10a57, 1}, {0x11047, 0x11048, 1}, {0x110be, 0x110c1, 1}, + {0x11141, 0x11143, 1}, + {0x111c5, 0x111c6, 1}, }, + LatinOffset: 3, } var _Soft_Dotted = &RangeTable{ @@ -4995,6 +5323,7 @@ var _Soft_Dotted = &RangeTable{ {0x1d65e, 0x1d65f, 1}, {0x1d692, 0x1d693, 1}, }, + LatinOffset: 1, } var _Terminal_Punctuation = &RangeTable{ @@ -5048,6 +5377,7 @@ var _Terminal_Punctuation = &RangeTable{ {0xa9c7, 0xa9c9, 1}, {0xaa5d, 0xaa5f, 1}, {0xaadf, 0xaadf, 1}, + {0xaaf0, 0xaaf1, 1}, {0xabeb, 0xabeb, 1}, {0xfe50, 0xfe52, 1}, {0xfe54, 0xfe57, 1}, @@ -5067,14 +5397,17 @@ var _Terminal_Punctuation = &RangeTable{ {0x10b3a, 0x10b3f, 1}, {0x11047, 0x1104d, 1}, {0x110be, 0x110c1, 1}, + {0x11141, 0x11143, 1}, + {0x111c5, 0x111c6, 1}, {0x12470, 0x12473, 1}, }, + LatinOffset: 5, } var _Unified_Ideograph = &RangeTable{ R16: []Range16{ {0x3400, 0x4db5, 1}, - {0x4e00, 0x9fcb, 1}, + {0x4e00, 0x9fcc, 1}, {0xfa0e, 0xfa0f, 1}, {0xfa11, 0xfa11, 1}, {0xfa13, 0xfa14, 1}, @@ -5114,6 +5447,7 @@ var _White_Space = &RangeTable{ {0x205f, 0x205f, 1}, {0x3000, 0x3000, 1}, }, + LatinOffset: 4, } // These variables have type *RangeTable. @@ -5153,7 +5487,7 @@ var ( ) // Generated by running -// maketables --data=http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.0.0/ucd/CaseFolding.txt +// maketables --data=http://www.unicode.org/Public/6.2.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.2.0/ucd/CaseFolding.txt // DO NOT EDIT // CaseRanges is the table describing case mappings for all letters with @@ -5254,6 +5588,7 @@ var _CaseRanges = []CaseRange{ {0x0260, 0x0260, d{-205, 0, -205}}, {0x0263, 0x0263, d{-207, 0, -207}}, {0x0265, 0x0265, d{42280, 0, 42280}}, + {0x0266, 0x0266, d{42308, 0, 42308}}, {0x0268, 0x0268, d{-209, 0, -209}}, {0x0269, 0x0269, d{-211, 0, -211}}, {0x026B, 0x026B, d{10743, 0, 10743}}, @@ -5315,6 +5650,8 @@ var _CaseRanges = []CaseRange{ {0x0531, 0x0556, d{0, 48, 0}}, {0x0561, 0x0586, d{-48, 0, -48}}, {0x10A0, 0x10C5, d{0, 7264, 0}}, + {0x10C7, 0x10C7, d{0, 7264, 0}}, + {0x10CD, 0x10CD, d{0, 7264, 0}}, {0x1D79, 0x1D79, d{35332, 0, 35332}}, {0x1D7D, 0x1D7D, d{3814, 0, 3814}}, {0x1E00, 0x1E95, d{UpperLower, UpperLower, UpperLower}}, @@ -5402,7 +5739,10 @@ var _CaseRanges = []CaseRange{ {0x2C7E, 0x2C7F, d{0, -10815, 0}}, {0x2C80, 0x2CE3, d{UpperLower, UpperLower, UpperLower}}, {0x2CEB, 0x2CEE, d{UpperLower, UpperLower, UpperLower}}, + {0x2CF2, 0x2CF3, d{UpperLower, UpperLower, UpperLower}}, {0x2D00, 0x2D25, d{-7264, 0, -7264}}, + {0x2D27, 0x2D27, d{-7264, 0, -7264}}, + {0x2D2D, 0x2D2D, d{-7264, 0, -7264}}, {0xA640, 0xA66D, d{UpperLower, UpperLower, UpperLower}}, {0xA680, 0xA697, d{UpperLower, UpperLower, UpperLower}}, {0xA722, 0xA72F, d{UpperLower, UpperLower, UpperLower}}, @@ -5412,8 +5752,9 @@ var _CaseRanges = []CaseRange{ {0xA77E, 0xA787, d{UpperLower, UpperLower, UpperLower}}, {0xA78B, 0xA78C, d{UpperLower, UpperLower, UpperLower}}, {0xA78D, 0xA78D, d{0, -42280, 0}}, - {0xA790, 0xA791, d{UpperLower, UpperLower, UpperLower}}, + {0xA790, 0xA793, d{UpperLower, UpperLower, UpperLower}}, {0xA7A0, 0xA7A9, d{UpperLower, UpperLower, UpperLower}}, + {0xA7AA, 0xA7AA, d{0, -42308, 0}}, {0xFF21, 0xFF3A, d{0, 32, 0}}, {0xFF41, 0xFF5A, d{-32, 0, -32}}, {0x10400, 0x10427, d{0, 40, 0}}, @@ -5587,10 +5928,10 @@ var properties = [MaxLatin1 + 1]uint8{ 0xA4: pS | pp, // '¤' 0xA5: pS | pp, // '¥' 0xA6: pS | pp, // '¦' - 0xA7: pS | pp, // '§' + 0xA7: pP | pp, // '§' 0xA8: pS | pp, // '¨' 0xA9: pS | pp, // '©' - 0xAA: pLl | pp, // 'ª' + 0xAA: pLo | pp, // 'ª' 0xAB: pP | pp, // '«' 0xAC: pS | pp, // '¬' 0xAD: 0, // '\u00ad' @@ -5602,11 +5943,11 @@ var properties = [MaxLatin1 + 1]uint8{ 0xB3: pN | pp, // '³' 0xB4: pS | pp, // '´' 0xB5: pLl | pp, // 'µ' - 0xB6: pS | pp, // '¶' + 0xB6: pP | pp, // '¶' 0xB7: pP | pp, // '·' 0xB8: pS | pp, // '¸' 0xB9: pN | pp, // '¹' - 0xBA: pLl | pp, // 'º' + 0xBA: pLo | pp, // 'º' 0xBB: pP | pp, // '»' 0xBC: pN | pp, // '¼' 0xBD: pN | pp, // '½' @@ -5844,6 +6185,7 @@ var foldLl = &RangeTable{ {0x04d0, 0x0526, 2}, {0x0531, 0x0556, 1}, {0x10a0, 0x10c5, 1}, + {0x10c7, 0x10cd, 6}, {0x1e00, 0x1e94, 2}, {0x1e9e, 0x1efe, 2}, {0x1f08, 0x1f0f, 1}, @@ -5873,20 +6215,22 @@ var foldLl = &RangeTable{ {0x2c7e, 0x2c80, 1}, {0x2c82, 0x2ce2, 2}, {0x2ceb, 0x2ced, 2}, - {0xa640, 0xa66c, 2}, + {0x2cf2, 0xa640, 31054}, + {0xa642, 0xa66c, 2}, {0xa680, 0xa696, 2}, {0xa722, 0xa72e, 2}, {0xa732, 0xa76e, 2}, {0xa779, 0xa77d, 2}, {0xa77e, 0xa786, 2}, {0xa78b, 0xa78d, 2}, - {0xa790, 0xa7a0, 16}, - {0xa7a2, 0xa7a8, 2}, + {0xa790, 0xa792, 2}, + {0xa7a0, 0xa7aa, 2}, {0xff21, 0xff3a, 1}, }, R32: []Range32{ {0x10400, 0x10427, 1}, }, + LatinOffset: 3, } var foldLt = &RangeTable{ @@ -5941,11 +6285,12 @@ var foldLu = &RangeTable{ {0x0256, 0x0257, 1}, {0x0259, 0x025b, 2}, {0x0260, 0x0263, 3}, - {0x0265, 0x0268, 3}, - {0x0269, 0x026b, 2}, - {0x026f, 0x0271, 2}, - {0x0272, 0x0275, 3}, - {0x027d, 0x0283, 3}, + {0x0265, 0x0266, 1}, + {0x0268, 0x0269, 1}, + {0x026b, 0x026f, 4}, + {0x0271, 0x0272, 1}, + {0x0275, 0x027d, 8}, + {0x0280, 0x0283, 3}, {0x0288, 0x028c, 1}, {0x0292, 0x0345, 179}, {0x0371, 0x0373, 2}, @@ -5987,7 +6332,9 @@ var foldLu = &RangeTable{ {0x2c73, 0x2c76, 3}, {0x2c81, 0x2ce3, 2}, {0x2cec, 0x2cee, 2}, - {0x2d00, 0x2d25, 1}, + {0x2cf3, 0x2d00, 13}, + {0x2d01, 0x2d25, 1}, + {0x2d27, 0x2d2d, 6}, {0xa641, 0xa66d, 2}, {0xa681, 0xa697, 2}, {0xa723, 0xa72f, 2}, @@ -5995,12 +6342,14 @@ var foldLu = &RangeTable{ {0xa77a, 0xa77c, 2}, {0xa77f, 0xa787, 2}, {0xa78c, 0xa791, 5}, - {0xa7a1, 0xa7a9, 2}, + {0xa793, 0xa7a1, 14}, + {0xa7a3, 0xa7a9, 2}, {0xff41, 0xff5a, 1}, }, R32: []Range32{ {0x10428, 0x1044f, 1}, }, + LatinOffset: 4, } var foldM = &RangeTable{ @@ -6023,7 +6372,7 @@ var foldMn = &RangeTable{ // If there is no entry for a script name, there are no such points. var FoldScript = map[string]*RangeTable{} -// Range entries: 3391 16-bit, 659 32-bit, 4050 total. -// Range bytes: 20346 16-bit, 7908 32-bit, 28254 total. +// Range entries: 3462 16-bit, 832 32-bit, 4294 total. +// Range bytes: 20772 16-bit, 9984 32-bit, 30756 total. // Fold orbit bytes: 63 pairs, 252 bytes diff --git a/src/pkg/unicode/utf8/example_test.go b/src/pkg/unicode/utf8/example_test.go new file mode 100644 index 000000000..fe2037336 --- /dev/null +++ b/src/pkg/unicode/utf8/example_test.go @@ -0,0 +1,192 @@ +package utf8_test + +import ( + "fmt" + "unicode/utf8" +) + +func ExampleDecodeLastRune() { + b := []byte("Hello, 世界") + + for len(b) > 0 { + r, size := utf8.DecodeLastRune(b) + fmt.Printf("%c %v\n", r, size) + + b = b[:len(b)-size] + } + // Output: + // 界 3 + // 世 3 + // 1 + // , 1 + // o 1 + // l 1 + // l 1 + // e 1 + // H 1 +} + +func ExampleDecodeLastRuneInString() { + str := "Hello, 世界" + + for len(str) > 0 { + r, size := utf8.DecodeLastRuneInString(str) + fmt.Printf("%c %v\n", r, size) + + str = str[:len(str)-size] + } + // Output: + // 界 3 + // 世 3 + // 1 + // , 1 + // o 1 + // l 1 + // l 1 + // e 1 + // H 1 + +} + +func ExampleDecodeRune() { + b := []byte("Hello, 世界") + + for len(b) > 0 { + r, size := utf8.DecodeRune(b) + fmt.Printf("%c %v\n", r, size) + + b = b[size:] + } + // Output: + // H 1 + // e 1 + // l 1 + // l 1 + // o 1 + // , 1 + // 1 + // 世 3 + // 界 3 +} + +func ExampleDecodeRuneInString() { + str := "Hello, 世界" + + for len(str) > 0 { + r, size := utf8.DecodeRuneInString(str) + fmt.Printf("%c %v\n", r, size) + + str = str[size:] + } + // Output: + // H 1 + // e 1 + // l 1 + // l 1 + // o 1 + // , 1 + // 1 + // 世 3 + // 界 3 +} + +func ExampleEncodeRune() { + r := '世' + buf := make([]byte, 3) + + n := utf8.EncodeRune(buf, r) + + fmt.Println(buf) + fmt.Println(n) + // Output: + // [228 184 150] + // 3 +} + +func ExampleFullRune() { + buf := []byte{228, 184, 150} // 世 + fmt.Println(utf8.FullRune(buf)) + fmt.Println(utf8.FullRune(buf[:2])) + // Output: + // true + // false +} + +func ExampleFullRuneInString() { + str := "世" + fmt.Println(utf8.FullRuneInString(str)) + fmt.Println(utf8.FullRuneInString(str[:2])) + // Output: + // true + // false +} + +func ExampleRuneCount() { + buf := []byte("Hello, 世界") + fmt.Println("bytes =", len(buf)) + fmt.Println("runes =", utf8.RuneCount(buf)) + // Output: + // bytes = 13 + // runes = 9 +} + +func ExampleRuneCountInString() { + str := "Hello, 世界" + fmt.Println("bytes =", len(str)) + fmt.Println("runes =", utf8.RuneCountInString(str)) + // Output: + // bytes = 13 + // runes = 9 +} + +func ExampleRuneLen() { + fmt.Println(utf8.RuneLen('a')) + fmt.Println(utf8.RuneLen('界')) + // Output: + // 1 + // 3 +} + +func ExampleRuneStart() { + buf := []byte("a界") + fmt.Println(utf8.RuneStart(buf[0])) + fmt.Println(utf8.RuneStart(buf[1])) + fmt.Println(utf8.RuneStart(buf[2])) + // Output: + // true + // true + // false +} + +func ExampleValid() { + valid := []byte("Hello, 世界") + invalid := []byte{0xff, 0xfe, 0xfd} + + fmt.Println(utf8.Valid(valid)) + fmt.Println(utf8.Valid(invalid)) + // Output: + // true + // false +} + +func ExampleValidRune() { + valid := 'a' + invalid := rune(0xfffffff) + + fmt.Println(utf8.ValidRune(valid)) + fmt.Println(utf8.ValidRune(invalid)) + // Output: + // true + // false +} + +func ExampleValidString() { + valid := "Hello, 世界" + invalid := string([]byte{0xff, 0xfe, 0xfd}) + + fmt.Println(utf8.ValidString(valid)) + fmt.Println(utf8.ValidString(invalid)) + // Output: + // true + // false +} diff --git a/src/pkg/unicode/utf8/utf8.go b/src/pkg/unicode/utf8/utf8.go index 57ea19e96..93d0be5e0 100644 --- a/src/pkg/unicode/utf8/utf8.go +++ b/src/pkg/unicode/utf8/utf8.go @@ -18,6 +18,12 @@ const ( UTFMax = 4 // maximum number of bytes of a UTF-8 encoded Unicode character. ) +// Code points in the surrogate range are not valid for UTF-8. +const ( + surrogateMin = 0xD800 + surrogateMax = 0xDFFF +) + const ( t1 = 0x00 // 0000 0000 tx = 0x80 // 1000 0000 @@ -34,7 +40,6 @@ const ( rune1Max = 1<<7 - 1 rune2Max = 1<<11 - 1 rune3Max = 1<<16 - 1 - rune4Max = 1<<21 - 1 ) func decodeRuneInternal(p []byte) (r rune, size int, short bool) { @@ -87,6 +92,9 @@ func decodeRuneInternal(p []byte) (r rune, size int, short bool) { if r <= rune2Max { return RuneError, 1, false } + if surrogateMin <= r && r <= surrogateMax { + return RuneError, 1, false + } return r, 3, false } @@ -102,7 +110,7 @@ func decodeRuneInternal(p []byte) (r rune, size int, short bool) { // 4-byte, 21-bit sequence? if c0 < t5 { r = rune(c0&mask4)<<18 | rune(c1&maskx)<<12 | rune(c2&maskx)<<6 | rune(c3&maskx) - if r <= rune3Max { + if r <= rune3Max || MaxRune < r { return RuneError, 1, false } return r, 4, false @@ -162,6 +170,9 @@ func decodeRuneInStringInternal(s string) (r rune, size int, short bool) { if r <= rune2Max { return RuneError, 1, false } + if surrogateMin <= r && r <= surrogateMax { + return RuneError, 1, false + } return r, 3, false } @@ -177,7 +188,7 @@ func decodeRuneInStringInternal(s string) (r rune, size int, short bool) { // 4-byte, 21-bit sequence? if c0 < t5 { r = rune(c0&mask4)<<18 | rune(c1&maskx)<<12 | rune(c2&maskx)<<6 | rune(c3&maskx) - if r <= rune3Max { + if r <= rune3Max || MaxRune < r { return RuneError, 1, false } return r, 4, false @@ -202,6 +213,9 @@ func FullRuneInString(s string) bool { // DecodeRune unpacks the first UTF-8 encoding in p and returns the rune and its width in bytes. // If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8. +// An encoding is invalid if it is incorrect UTF-8, encodes a rune that is +// out of range, or is not the shortest possible UTF-8 encoding for the +// value. No other validation is performed. func DecodeRune(p []byte) (r rune, size int) { r, size, _ = decodeRuneInternal(p) return @@ -209,6 +223,9 @@ func DecodeRune(p []byte) (r rune, size int) { // DecodeRuneInString is like DecodeRune but its input is a string. // If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8. +// An encoding is invalid if it is incorrect UTF-8, encodes a rune that is +// out of range, or is not the shortest possible UTF-8 encoding for the +// value. No other validation is performed. func DecodeRuneInString(s string) (r rune, size int) { r, size, _ = decodeRuneInStringInternal(s) return @@ -216,6 +233,9 @@ func DecodeRuneInString(s string) (r rune, size int) { // DecodeLastRune unpacks the last UTF-8 encoding in p and returns the rune and its width in bytes. // If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8. +// An encoding is invalid if it is incorrect UTF-8, encodes a rune that is +// out of range, or is not the shortest possible UTF-8 encoding for the +// value. No other validation is performed. func DecodeLastRune(p []byte) (r rune, size int) { end := len(p) if end == 0 { @@ -250,6 +270,9 @@ func DecodeLastRune(p []byte) (r rune, size int) { // DecodeLastRuneInString is like DecodeLastRune but its input is a string. // If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8. +// An encoding is invalid if it is incorrect UTF-8, encodes a rune that is +// out of range, or is not the shortest possible UTF-8 encoding for the +// value. No other validation is performed. func DecodeLastRuneInString(s string) (r rune, size int) { end := len(s) if end == 0 { @@ -283,15 +306,20 @@ func DecodeLastRuneInString(s string) (r rune, size int) { } // RuneLen returns the number of bytes required to encode the rune. +// It returns -1 if the rune is not a valid value to encode in UTF-8. func RuneLen(r rune) int { switch { + case r < 0: + return -1 case r <= rune1Max: return 1 case r <= rune2Max: return 2 + case surrogateMin <= r && r <= surrogateMax: + return -1 case r <= rune3Max: return 3 - case r <= rune4Max: + case r <= MaxRune: return 4 } return -1 @@ -316,6 +344,10 @@ func EncodeRune(p []byte, r rune) int { r = RuneError } + if surrogateMin <= r && r <= surrogateMax { + r = RuneError + } + if uint32(r) <= rune3Max { p[0] = t3 | byte(r>>12) p[1] = tx | byte(r>>6)&maskx @@ -368,7 +400,7 @@ func Valid(p []byte) bool { } else { _, size := DecodeRune(p[i:]) if size == 1 { - // All valid runes of size of 1 (those + // All valid runes of size 1 (those // below RuneSelf) were handled above. // This must be a RuneError. return false @@ -395,3 +427,17 @@ func ValidString(s string) bool { } return true } + +// ValidRune reports whether r can be legally encoded as UTF-8. +// Code points that are out of range or a surrogate half are illegal. +func ValidRune(r rune) bool { + switch { + case r < 0: + return false + case surrogateMin <= r && r <= surrogateMax: + return false + case r > MaxRune: + return false + } + return true +} diff --git a/src/pkg/unicode/utf8/utf8_test.go b/src/pkg/unicode/utf8/utf8_test.go index 4f73c8fb8..c516871c9 100644 --- a/src/pkg/unicode/utf8/utf8_test.go +++ b/src/pkg/unicode/utf8/utf8_test.go @@ -56,6 +56,8 @@ var utf8map = []Utf8Map{ {0x07ff, "\xdf\xbf"}, {0x0800, "\xe0\xa0\x80"}, {0x0801, "\xe0\xa0\x81"}, + {0xd7ff, "\xed\x9f\xbf"}, // last code point before surrogate half. + {0xe000, "\xee\x80\x80"}, // first code point after surrogate half. {0xfffe, "\xef\xbf\xbe"}, {0xffff, "\xef\xbf\xbf"}, {0x10000, "\xf0\x90\x80\x80"}, @@ -65,6 +67,11 @@ var utf8map = []Utf8Map{ {0xFFFD, "\xef\xbf\xbd"}, } +var surrogateMap = []Utf8Map{ + {0xd800, "\xed\xa0\x80"}, // surrogate min decodes to (RuneError, 1) + {0xdfff, "\xed\xbf\xbf"}, // surrogate max decodes to (RuneError, 1) +} + var testStrings = []string{ "", "abcd", @@ -75,8 +82,7 @@ var testStrings = []string{ } func TestFullRune(t *testing.T) { - for i := 0; i < len(utf8map); i++ { - m := utf8map[i] + for _, m := range utf8map { b := []byte(m.str) if !FullRune(b) { t.Errorf("FullRune(%q) (%U) = false, want true", b, m.r) @@ -97,8 +103,7 @@ func TestFullRune(t *testing.T) { } func TestEncodeRune(t *testing.T) { - for i := 0; i < len(utf8map); i++ { - m := utf8map[i] + for _, m := range utf8map { b := []byte(m.str) var buf [10]byte n := EncodeRune(buf[0:], m.r) @@ -110,8 +115,7 @@ func TestEncodeRune(t *testing.T) { } func TestDecodeRune(t *testing.T) { - for i := 0; i < len(utf8map); i++ { - m := utf8map[i] + for _, m := range utf8map { b := []byte(m.str) r, size := DecodeRune(b) if r != m.r || size != len(b) { @@ -168,6 +172,21 @@ func TestDecodeRune(t *testing.T) { } } +func TestDecodeSurrogateRune(t *testing.T) { + for _, m := range surrogateMap { + b := []byte(m.str) + r, size := DecodeRune(b) + if r != RuneError || size != 1 { + t.Errorf("DecodeRune(%q) = %x, %d want %x, %d", b, r, size, RuneError, 1) + } + s := m.str + r, size = DecodeRuneInString(s) + if r != RuneError || size != 1 { + t.Errorf("DecodeRune(%q) = %x, %d want %x, %d", b, r, size, RuneError, 1) + } + } +} + // Check that DecodeRune and DecodeLastRune correspond to // the equivalent range loop. func TestSequencing(t *testing.T) { @@ -284,8 +303,7 @@ var runecounttests = []RuneCountTest{ } func TestRuneCount(t *testing.T) { - for i := 0; i < len(runecounttests); i++ { - tt := runecounttests[i] + for _, tt := range runecounttests { if out := RuneCountInString(tt.in); out != tt.out { t.Errorf("RuneCountInString(%q) = %d, want %d", tt.in, out, tt.out) } @@ -295,6 +313,32 @@ func TestRuneCount(t *testing.T) { } } +type RuneLenTest struct { + r rune + size int +} + +var runelentests = []RuneLenTest{ + {0, 1}, + {'e', 1}, + {'é', 2}, + {'☺', 3}, + {RuneError, 3}, + {MaxRune, 4}, + {0xD800, -1}, + {0xDFFF, -1}, + {MaxRune + 1, -1}, + {-1, -1}, +} + +func TestRuneLen(t *testing.T) { + for _, tt := range runelentests { + if size := RuneLen(tt.r); size != tt.size { + t.Errorf("RuneLen(%#U) = %d, want %d", tt.r, size, tt.size) + } + } +} + type ValidTest struct { in string out bool @@ -311,15 +355,50 @@ var validTests = []ValidTest{ {string([]byte{66, 250}), false}, {string([]byte{66, 250, 67}), false}, {"a\uFFFDb", true}, + {string("\xF4\x8F\xBF\xBF"), true}, // U+10FFFF + {string("\xF4\x90\x80\x80"), false}, // U+10FFFF+1; out of range + {string("\xF7\xBF\xBF\xBF"), false}, // 0x1FFFFF; out of range + {string("\xFB\xBF\xBF\xBF\xBF"), false}, // 0x3FFFFFF; out of range + {string("\xc0\x80"), false}, // U+0000 encoded in two bytes: incorrect + {string("\xed\xa0\x80"), false}, // U+D800 high surrogate (sic) + {string("\xed\xbf\xbf"), false}, // U+DFFF low surrogate (sic) } func TestValid(t *testing.T) { - for i, tt := range validTests { + for _, tt := range validTests { if Valid([]byte(tt.in)) != tt.out { - t.Errorf("%d. Valid(%q) = %v; want %v", i, tt.in, !tt.out, tt.out) + t.Errorf("Valid(%q) = %v; want %v", tt.in, !tt.out, tt.out) } if ValidString(tt.in) != tt.out { - t.Errorf("%d. ValidString(%q) = %v; want %v", i, tt.in, !tt.out, tt.out) + t.Errorf("ValidString(%q) = %v; want %v", tt.in, !tt.out, tt.out) + } + } +} + +type ValidRuneTest struct { + r rune + ok bool +} + +var validrunetests = []ValidRuneTest{ + {0, true}, + {'e', true}, + {'é', true}, + {'☺', true}, + {RuneError, true}, + {MaxRune, true}, + {0xD7FF, true}, + {0xD800, false}, + {0xDFFF, false}, + {0xE000, true}, + {MaxRune + 1, false}, + {-1, false}, +} + +func TestValidRune(t *testing.T) { + for _, tt := range validrunetests { + if ok := ValidRune(tt.r); ok != tt.ok { + t.Errorf("ValidRune(%#U) = %t, want %t", tt.r, ok, tt.ok) } } } diff --git a/src/pkg/unsafe/unsafe.go b/src/pkg/unsafe/unsafe.go index 024969b42..83b2e1405 100644 --- a/src/pkg/unsafe/unsafe.go +++ b/src/pkg/unsafe/unsafe.go @@ -11,7 +11,7 @@ package unsafe // part of the unsafe package. It represents the type of an arbitrary Go expression. type ArbitraryType int -// Pointer represents a pointer to an arbitrary type. There are three special operations +// Pointer represents a pointer to an arbitrary type. There are four special operations // available for type Pointer that are not available for other types. // 1) A pointer value of any type can be converted to a Pointer. // 2) A Pointer can be converted to a pointer value of any type. @@ -32,6 +32,6 @@ func Sizeof(v ArbitraryType) uintptr func Offsetof(v ArbitraryType) uintptr // Alignof returns the alignment of the value v. It is the maximum value m such -// that the address of a variable with the type of v will always always be zero mod m. +// that the address of a variable with the type of v will always be zero mod m. // If v is of the form structValue.field, it returns the alignment of field f within struct object obj. func Alignof(v ArbitraryType) uintptr diff --git a/src/race.bash b/src/race.bash new file mode 100755 index 000000000..3cdc5e10c --- /dev/null +++ b/src/race.bash @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Copyright 2013 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# race.bash tests the standard library under the race detector. +# http://golang.org/doc/articles/race_detector.html + +set -e + +function usage { + echo 'race detector is only supported on linux/amd64 and darwin/amd64' 1>&2 + exit 1 +} + +case $(uname) in +"Darwin") + # why Apple? why? + if sysctl machdep.cpu.extfeatures | grep -qv EM64T; then + usage + fi + ;; +"Linux") + if [ $(uname -m) != "x86_64" ]; then + usage + fi + ;; +*) + usage + ;; +esac + +if [ ! -f make.bash ]; then + echo 'race.bash must be run from $GOROOT/src' 1>&2 + exit 1 +fi +. ./make.bash --no-banner +go install -race std +go test -race -short std +go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std diff --git a/src/race.bat b/src/race.bat new file mode 100644 index 000000000..989a2e7d8 --- /dev/null +++ b/src/race.bat @@ -0,0 +1,52 @@ +:: Copyright 2013 The Go Authors. All rights reserved. +:: Use of this source code is governed by a BSD-style +:: license that can be found in the LICENSE file. + +:: race.bash tests the standard library under the race detector. +:: http://golang.org/doc/articles/race_detector.html + +@echo off + +setlocal + +if exist make.bat goto ok +echo race.bat must be run from go\src +:: cannot exit: would kill parent command interpreter +goto end +:ok + +call make.bat --dist-tool >NUL +if errorlevel 1 goto fail +.\cmd\dist\dist env -wp >env.bat +if errorlevel 1 goto fail +call env.bat +del env.bat + +if %GOHOSTARCH% == amd64 goto continue +echo Race detector is only supported on windows/amd64. +goto fail + +:continue +call make.bat --no-banner --no-local +if %GOBUILDFAIL%==1 goto end +echo # go install -race std +go install -race std +if errorlevel 1 goto fail +echo # go test -race -short -std +go test -race -short std +if errorlevel 1 goto fail +echo # go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std +go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std +if errorlevel 1 goto fail +goto succ + +:fail +set GOBUILDFAIL=1 +echo Fail. +goto end + +:succ +echo All tests passed. + +:end + diff --git a/src/run.bash b/src/run.bash index ca84b7034..a026b459c 100755 --- a/src/run.bash +++ b/src/run.bash @@ -14,6 +14,12 @@ unset GOPATH # we disallow local import for non-local packages, if $GOROOT ha # no core files, please ulimit -c 0 +# Raise soft limits to hard limits for NetBSD/OpenBSD. +# We need at least 256 files and ~300 MB of bss. +# On OS X ulimit -S -n rejects 'unlimited'. +[ "$(ulimit -H -n)" == "unlimited" ] || ulimit -S -n $(ulimit -H -n) +[ "$(ulimit -H -d)" == "unlimited" ] || ulimit -S -d $(ulimit -H -d) + # allow all.bash to avoid double-build of everything rebuild=true if [ "$1" = "--no-rebuild" ]; then @@ -40,6 +46,16 @@ echo echo '# sync -cpu=10' go test sync -short -timeout=120s -cpu=10 +# Race detector only supported on Linux and OS X, +# and only on amd64, and only when cgo is enabled. +case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED" in +linux-linux-amd64-1 | darwin-darwin-amd64-1) + echo + echo '# Testing race detector.' + go test -race -i flag + go test -race -short flag +esac + xcd() { echo echo '#' $1 @@ -49,15 +65,16 @@ xcd() { [ "$CGO_ENABLED" != 1 ] || [ "$GOHOSTOS" == windows ] || (xcd ../misc/cgo/stdio -./test.bash +go run $GOROOT/test/run.go - . ) || exit $? [ "$CGO_ENABLED" != 1 ] || (xcd ../misc/cgo/life -./test.bash +go run $GOROOT/test/run.go - . ) || exit $? [ "$CGO_ENABLED" != 1 ] || +[ "$GOHOSTOS" == openbsd ] || # issue 4878 (xcd ../misc/cgo/test go test ) || exit $? @@ -101,12 +118,13 @@ echo '#' ../test/bench/go1 go test ../test/bench/go1 (xcd ../test +unset GOMAXPROCS time go run run.go ) || exit $? echo echo '# Checking API compatibility.' -go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt +go tool api -c $GOROOT/api/go1.txt -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt echo echo ALL TESTS PASSED diff --git a/src/run.bat b/src/run.bat index 9a09d435c..5f7164a0c 100644 --- a/src/run.bat +++ b/src/run.bat @@ -30,6 +30,13 @@ echo. :: at least runtime/debug test will fail. set GOROOT_FINAL= +:: get CGO_ENABLED +go env > env.bat +if errorlevel 1 goto fail +call env.bat +del env.bat +echo. + echo # Testing packages. go test std -short -timeout=120s if errorlevel 1 goto fail @@ -45,30 +52,70 @@ go test sync -short -timeout=120s -cpu=10 if errorlevel 1 goto fail echo. +if not "%GOHOSTOS%-%GOOS%-%GOARCH%-%CGO_ENABLED%" == "windows-windows-amd64-1" goto norace +echo # Testing race detector. +go test -race -i flag +if errorlevel 1 goto fail +go test -race -short flag +if errorlevel 1 goto fail +echo. +:norace + echo # ..\misc\dashboard\builder ..\misc\goplay go build ..\misc\dashboard\builder ..\misc\goplay if errorlevel 1 goto fail echo. -:: TODO(brainman): disabled, because it fails with: mkdir C:\Users\ADMINI~1\AppData\Local\Temp\2.....\test\bench\: The filename or extension is too long. -::echo # ..\test\bench\go1 -::go test ..\test\bench\go1 -::if errorlevel 1 goto fail -::echo. +echo # ..\test\bench\go1 +go test ..\test\bench\go1 +if errorlevel 1 goto fail +echo. + +:: cgo tests +:: issue 4955 - cgo is broken +goto nocgo +if x%CGO_ENABLED% == x0 goto nocgo +echo # ..\misc\cgo\life +go run %GOROOT%\test\run.go - ..\misc\cgo\life +if errorlevel 1 goto fail +echo. + +echo # ..\misc\cgo\stdio +go run %GOROOT%\test\run.go - ..\misc\cgo\stdio +if errorlevel 1 goto fail +echo. + +echo # ..\misc\cgo\test +go test ..\misc\cgo\test +if errorlevel 1 goto fail +echo. +:nocgo + +echo # ..\doc\progs +go run %GOROOT%\test\run.go - ..\doc\progs +if errorlevel 1 goto fail +echo. :: TODO: The other tests in run.bash. -echo # test + +set OLDGOMAXPROCS=%GOMAXPROCS% + +echo # ..\test cd ..\test set FAIL=0 +set GOMAXPROCS= go run run.go if errorlevel 1 set FAIL=1 cd ..\src echo. if %FAIL%==1 goto fail +set GOMAXPROCS=%OLDGOMAXPROCS% +set OLDGOMAXPROCS= + echo # Checking API compatibility. -go tool api -c ..\api\go1.txt -next ..\api\next.txt +go tool api -c ..\api\go1.txt -next ..\api\next.txt -except ..\api\except.txt if errorlevel 1 goto fail echo. diff --git a/src/run.rc b/src/run.rc new file mode 100755 index 000000000..e493eae36 --- /dev/null +++ b/src/run.rc @@ -0,0 +1,63 @@ +#!/bin/rc -e +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +rfork e + +eval `{go env} + +GOPATH = () # we disallow local import for non-local packges, if $GOROOT happens + # to be under $GOPATH, then some tests below will fail + +# allow all.rc to avoid double-build of everything +rebuild = true +if(~ $1 --no-rebuild) + shift +if not { + # Run only one process at a time on 9vx. + if(~ $sysname vx32) + pflag = (-p 1) + echo '# Building packages and commands.' + time go install -a -v $pflag std + echo +} + +# we must unset GOROOT_FINAL before tests, because runtime/debug requires +# correct access to source code, so if we have GOROOT_FINAL in effect, +# at least runtime/debug test will fail. +GOROOT_FINAL = () + +echo '# Testing packages.' +time go test std -short -timeout 120s +echo + +echo '# GOMAXPROCS=2 runtime -cpu=1,2,4' +GOMAXPROCS=2 go test runtime -short -timeout 240s -cpu 1,2,4 +echo + +echo '# sync -cpu=10' +go test sync -short -timeout 120s -cpu 10 +echo + +fn xcd { + echo + echo '#' $1 + cd $"GOROOT/src/$1 +} + +echo +echo '#' ../misc/dashboard/builder ../misc/goplay +go build ../misc/dashboard/builder ../misc/gplay + +echo +echo '#' ../test/bench/go1 +go test ../test/bench/go1 + +@{ + xcd ../test + GOMAXPROCS='' time go run run.go +} + +echo +echo ALL TESTS PASSED diff --git a/src/sudo.bash b/src/sudo.bash index 40f8d1aa1..7b7d4f1cd 100755 --- a/src/sudo.bash +++ b/src/sudo.bash @@ -12,6 +12,12 @@ Darwin) exit 0 esac +# Check that the go command exists +if ! go help >/dev/null 2>&1; then + echo "The go command is not in your PATH." >&2 + exit 2 +fi + eval $(go env) if ! [ -x $GOTOOLDIR/cov -a -x $GOTOOLDIR/prof ]; then echo "You don't need to run sudo.bash." >&2 -- cgit v1.2.3